OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::TextureBake::anonymous_namespace{TextureBakeSubdivide.cpp} Namespace Reference

Classes

struct  PassResult

Functions

double edge_len_sq (const VertStore &v, int a, int b)
void normalize_store_normals (VertStore &verts)
int get_midpoint (VertStore &verts, QuantizedPointMap &cache, int a, int b, QuantizedPointMap *pos_canon_map)
PassResult subdivide_pass (VertStore &verts, const std::vector< int > &indices, double max_edge_length, int safety_cap, const std::vector< uint8_t > &face_excluded, QuantizedPointMap *pos_canon_map, const std::vector< int > &face_parent_id, const SubdivideWithinFn &within)

Class Documentation

◆ Slic3r::TextureBake::anonymous_namespace{TextureBakeSubdivide.cpp}::PassResult

struct Slic3r::TextureBake::anonymous_namespace{TextureBakeSubdivide.cpp}::PassResult
Collaboration diagram for Slic3r::TextureBake::anonymous_namespace{TextureBakeSubdivide.cpp}::PassResult:
Class Members
bool capped = false
bool changed = false
vector< uint8_t > face_excluded
vector< int > face_parent_id
vector< int > indices

Function Documentation

◆ edge_len_sq()

double Slic3r::TextureBake::anonymous_namespace{TextureBakeSubdivide.cpp}::edge_len_sq ( const VertStore & v,
int a,
int b )
25{
26 return (v.pos[size_t(a)] - v.pos[size_t(b)]).squaredNorm();
27}
std::vector< Vec3d > pos
Definition TextureBakeSubdivide.hpp:39

References edge_len_sq(), and Slic3r::TextureBake::VertStore::pos.

Referenced by edge_len_sq(), and subdivide_pass().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_midpoint()

int Slic3r::TextureBake::anonymous_namespace{TextureBakeSubdivide.cpp}::get_midpoint ( VertStore & verts,
QuantizedPointMap & cache,
int a,
int b,
QuantizedPointMap * pos_canon_map )
42{
43 const int lo = std::min(a, b), hi = std::max(a, b);
44 if (const int cached = cache.get_key(lo, hi, 0); cached != -1)
45 return cached;
46
47 const Vec3d m = (verts.pos[size_t(a)] + verts.pos[size_t(b)]) * 0.5;
48 Vec3d n = verts.nrm[size_t(a)] + verts.nrm[size_t(b)];
49 const double nl = n.norm();
50 n = (nl > 0.0) ? Vec3d(n / nl) : verts.nrm[size_t(a)];
51
52 const int idx = verts.push(m, n);
53 if (!verts.wgt.empty())
54 verts.wgt.push_back((verts.wgt[size_t(a)] + verts.wgt[size_t(b)]) * 0.5);
55 if (!verts.canon.empty() && pos_canon_map != nullptr)
56 verts.canon.push_back(pos_canon_map->get_or_set(float(m.x()), float(m.y()), float(m.z()), idx));
57
58 cache.get_or_set_key(lo, hi, 0, idx);
59 return idx;
60}
int get_key(int64_t a, int64_t b, int64_t c)
Definition TextureBakeIndex.hpp:72
int get_or_set(float x, float y, float z, int value)
Definition TextureBakeIndex.hpp:63
int get_or_set_key(int64_t a, int64_t b, int64_t c, int value)
Definition TextureBakeIndex.hpp:73
Eigen::Matrix< double, 3, 1, Eigen::DontAlign > Vec3d
Definition Point.hpp:68
std::vector< Vec3d > nrm
Definition TextureBakeSubdivide.hpp:40
std::vector< double > wgt
Definition TextureBakeSubdivide.hpp:41
int push(const Vec3d &p, const Vec3d &n)
Definition TextureBakeSubdivide.hpp:45
std::vector< int > canon
Definition TextureBakeSubdivide.hpp:42

References Slic3r::TextureBake::VertStore::canon, Slic3r::TextureBake::QuantizedPointMap::get_key(), get_midpoint(), Slic3r::TextureBake::QuantizedPointMap::get_or_set(), Slic3r::TextureBake::QuantizedPointMap::get_or_set_key(), Slic3r::TextureBake::VertStore::nrm, Slic3r::TextureBake::VertStore::pos, Slic3r::TextureBake::VertStore::push(), and Slic3r::TextureBake::VertStore::wgt.

Referenced by get_midpoint(), and subdivide_pass().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ normalize_store_normals()

void Slic3r::TextureBake::anonymous_namespace{TextureBakeSubdivide.cpp}::normalize_store_normals ( VertStore & verts)
31{
32 for (Vec3d &n : verts.nrm) {
33 const double len = n.norm();
34 n = (len > 0.0) ? Vec3d(n / len) : Vec3d(0.0, 0.0, 1.0);
35 }
36}

References normalize_store_normals(), and Slic3r::TextureBake::VertStore::nrm.

Referenced by normalize_store_normals().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ subdivide_pass()

PassResult Slic3r::TextureBake::anonymous_namespace{TextureBakeSubdivide.cpp}::subdivide_pass ( VertStore & verts,
const std::vector< int > & indices,
double max_edge_length,
int safety_cap,
const std::vector< uint8_t > & face_excluded,
QuantizedPointMap * pos_canon_map,
const std::vector< int > & face_parent_id,
const SubdivideWithinFn & within )
81{
82 PassResult out;
83 const double max_sq = max_edge_length * max_edge_length;
84 const size_t tri_count = indices.size() / 3;
85 const bool have_canon = !verts.canon.empty();
86
87 // Sized from the pass rather than grown from 64 k: a fine pass marks on the order of one edge per
88 // triangle, and growing to that by doubling rehashes the whole table a dozen times.
89 const size_t expect = std::max<size_t>(size_t(1) << 16, tri_count);
90 QuantizedPointMap mid_cache(1.0, expect);
91 QuantizedPointMap split_edges(1.0, expect);
92
93 // With canonical ids the key is the canonical *position* id, so split copies either side of a
94 // sharp edge see one another's decision; without them the vertex index serves.
95 const auto key_of = [&](int v) -> int64_t { return have_canon ? verts.canon[size_t(v)] : v; };
96 const auto mark_edge = [&](int a, int b) {
97 const int64_t u = key_of(a), v = key_of(b);
98 if (u < v) split_edges.get_or_set_key(u, v, 0, 1);
99 else split_edges.get_or_set_key(v, u, 0, 1);
100 };
101 const auto is_marked = [&](int a, int b) {
102 const int64_t u = key_of(a), v = key_of(b);
103 return (u < v ? split_edges.get_key(u, v, 0) : split_edges.get_key(v, u, 0)) != -1;
104 };
105
106 // Graded, an edge splits while it is longer than max_edge_length + SUBDIVIDE_GRADE * (d - len/2),
107 // d its midpoint's distance from the region; less half the length bounds the edge's own distance
108 // from below, so the test errs toward splitting. Solved for d, that is the region coming within the
109 // radius below. It reads the positions alone, so two triangles sharing the edge agree.
110 const auto too_long = [&](int a, int b) {
111 const double len_sq = edge_len_sq(verts, a, b);
112 if (len_sq <= max_sq)
113 return false;
114 if (!within)
115 return true;
116 const double len = std::sqrt(len_sq);
117 return within((verts.pos[size_t(a)] + verts.pos[size_t(b)]) * 0.5,
118 0.5 * len + (len - max_edge_length) / SUBDIVIDE_GRADE);
119 };
120
121 // Step 1. An excluded triangle marks none of its own edges while left alone, so its interior never
122 // refines; its boundary edges are still marked by an included neighbour, and ungraded it follows
123 // that split. Graded, one the refinement reaches marks its own too-long edges too, and so on
124 // outward. An earlier bake's relief meets the paint along edges already at the target length, so
125 // it is never reached and stays as it was.
126 std::vector<uint8_t> want(tri_count, 0);
127 tbb::parallel_for(tbb::blocked_range<size_t>(0, tri_count), [&](const tbb::blocked_range<size_t> &range) {
128 for (size_t t = range.begin(); t < range.end(); ++t) {
129 const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2];
130 want[t] = uint8_t(too_long(a, b)) | uint8_t(too_long(b, c) << 1) | uint8_t(too_long(c, a) << 2);
131 }
132 });
133 const auto mark_wanted = [&](size_t t) {
134 const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2];
135 if (want[t] & 1) mark_edge(a, b);
136 if (want[t] & 2) mark_edge(b, c);
137 if (want[t] & 4) mark_edge(c, a);
138 };
139 std::vector<size_t> unreached;
140 for (size_t t = 0; t < tri_count; ++t)
141 if (face_excluded.empty() || !face_excluded[t])
142 mark_wanted(t);
143 else if (within && want[t] != 0)
144 unreached.push_back(t);
145 // Marks only accumulate, so the order the reached triangles are taken in does not matter.
146 for (size_t n = 0; n != unreached.size();) {
147 n = unreached.size();
148 unreached.erase(std::remove_if(unreached.begin(), unreached.end(), [&](size_t t) {
149 const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2];
150 const bool reached = is_marked(a, b) || is_marked(b, c) || is_marked(c, a);
151 if (reached)
152 mark_wanted(t);
153 return reached;
154 }), unreached.end());
155 }
156 if (split_edges.size() == 0) {
157 out.indices = indices;
158 out.face_excluded = face_excluded;
159 out.face_parent_id = face_parent_id;
160 return out; // changed stays false: nothing left to refine
161 }
162
163 // Step 1.5. Read-only against the finished mark set, so it runs in parallel - and it keeps each
164 // triangle's three marks (bit 0 = ab, 1 = bc, 2 = ca), so the serial rebuild below reads a byte
165 // instead of probing the hash map three more times per triangle.
166 std::vector<uint8_t> tri_marks(tri_count);
167 const size_t predicted = tbb::parallel_reduce(
168 tbb::blocked_range<size_t>(0, tri_count), size_t(0),
169 [&](const tbb::blocked_range<size_t> &range, size_t acc) {
170 for (size_t t = range.begin(); t < range.end(); ++t) {
171 const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2];
172 const uint8_t m = uint8_t(is_marked(a, b)) | uint8_t(is_marked(b, c) << 1) |
173 uint8_t(is_marked(c, a) << 2);
174 tri_marks[t] = m;
175 const int n = (m & 1) + ((m >> 1) & 1) + ((m >> 2) & 1);
176 acc += (n == 0) ? 1 : size_t(n + 1);
177 }
178 return acc;
179 },
180 std::plus<size_t>());
181 if (predicted > size_t(safety_cap)) {
182 out.indices = indices;
183 out.face_excluded = face_excluded;
184 out.face_parent_id = face_parent_id;
185 out.capped = true;
186 return out; // coarser than asked for, but watertight
187 }
188
189 // Step 2.
190 out.indices.resize(predicted * 3);
191 if (!face_excluded.empty())
192 out.face_excluded.resize(predicted);
193 if (!face_parent_id.empty())
194 out.face_parent_id.resize(predicted);
195 size_t wi = 0, fi = 0;
196 const auto emit_face_data = [&](uint8_t excl, int pid, int times) {
197 for (int k = 0; k < times; ++k) {
198 if (!out.face_excluded.empty()) out.face_excluded[fi] = excl;
199 if (!out.face_parent_id.empty()) out.face_parent_id[fi] = pid;
200 ++fi;
201 }
202 };
203 const auto emit = [&](int x, int y, int z) {
204 out.indices[wi++] = x; out.indices[wi++] = y; out.indices[wi++] = z;
205 };
206
207 for (size_t t = 0; t < tri_count; ++t) {
208 const int a = indices[t * 3], b = indices[t * 3 + 1], c = indices[t * 3 + 2];
209 const uint8_t excl = face_excluded.empty() ? uint8_t(0) : face_excluded[t];
210 const int pid = face_parent_id.empty() ? 0 : face_parent_id[t];
211 const bool s_ab = (tri_marks[t] & 1) != 0, s_bc = (tri_marks[t] & 2) != 0, s_ca = (tri_marks[t] & 4) != 0;
212 const int n = int(s_ab) + int(s_bc) + int(s_ca);
213
214 if (n == 0) {
215 emit(a, b, c);
216 emit_face_data(excl, pid, 1);
217 } else if (n == 3) {
218 // a
219 // / \
220 // mCA-mAB
221 // / \ / \
222 // c--mBC--b
223 const int m_ab = get_midpoint(verts, mid_cache, a, b, pos_canon_map);
224 const int m_bc = get_midpoint(verts, mid_cache, b, c, pos_canon_map);
225 const int m_ca = get_midpoint(verts, mid_cache, c, a, pos_canon_map);
226 emit(a, m_ab, m_ca);
227 emit(m_ab, b, m_bc);
228 emit(m_ca, m_bc, c);
229 emit(m_ab, m_bc, m_ca);
230 emit_face_data(excl, pid, 4);
231 } else if (n == 1) {
232 if (s_ab) {
233 const int m = get_midpoint(verts, mid_cache, a, b, pos_canon_map);
234 emit(a, m, c);
235 emit(m, b, c);
236 } else if (s_bc) {
237 const int m = get_midpoint(verts, mid_cache, b, c, pos_canon_map);
238 emit(a, b, m);
239 emit(a, m, c);
240 } else {
241 const int m = get_midpoint(verts, mid_cache, c, a, pos_canon_map);
242 emit(a, b, m);
243 emit(m, b, c);
244 }
245 emit_face_data(excl, pid, 2);
246 } else {
247 // A corner triangle on the untouched-edge vertex, then the remaining quadrilateral split
248 // along the midpoint-to-midpoint diagonal, which keeps the winding consistent.
249 //
250 // A sliver parent propagates: that inner diagonal inherits half the short edge and hands
251 // the sliver to two children per pass. No better diagonal exists - one avoiding the
252 // midpoints must pass through one of them, giving a zero-area triangle. Regularization
253 // removes such slivers before the mesh reaches here.
254 if (!s_ab) { // fan from c
255 const int m_bc = get_midpoint(verts, mid_cache, b, c, pos_canon_map);
256 const int m_ca = get_midpoint(verts, mid_cache, c, a, pos_canon_map);
257 emit(a, b, m_bc);
258 emit(a, m_bc, m_ca);
259 emit(c, m_ca, m_bc);
260 } else if (!s_bc) { // fan from a
261 const int m_ab = get_midpoint(verts, mid_cache, a, b, pos_canon_map);
262 const int m_ca = get_midpoint(verts, mid_cache, c, a, pos_canon_map);
263 emit(a, m_ab, m_ca);
264 emit(m_ab, b, c);
265 emit(m_ab, c, m_ca);
266 } else { // fan from b
267 const int m_ab = get_midpoint(verts, mid_cache, a, b, pos_canon_map);
268 const int m_bc = get_midpoint(verts, mid_cache, b, c, pos_canon_map);
269 emit(b, m_bc, m_ab);
270 emit(a, m_ab, m_bc);
271 emit(a, m_bc, c);
272 }
273 emit_face_data(excl, pid, 3);
274 }
275 }
276
277 out.changed = true;
278 return out;
279}
Definition TextureBakeIndex.hpp:39
int get_midpoint(VertStore &verts, QuantizedPointMap &cache, int a, int b, QuantizedPointMap *pos_canon_map)
Definition TextureBakeSubdivide.cpp:40
std::vector< uint8_t > face_excluded
Definition TextureBakeSubdivide.cpp:65
std::vector< int > face_parent_id
Definition TextureBakeSubdivide.cpp:66
double edge_len_sq(const VertStore &v, int a, int b)
Definition TextureBakeSubdivide.cpp:24
std::vector< int > indices
Definition TextureBakeSubdivide.cpp:64
static constexpr double SUBDIVIDE_GRADE
Definition TextureBakeSubdivide.hpp:33
auto range(Cont &&cont)
Definition libslic3r.h:405

References Slic3r::TextureBake::VertStore::canon, Slic3r::TextureBake::anonymous_namespace{TextureBakeSubdivide.cpp}::PassResult::capped, Slic3r::TextureBake::anonymous_namespace{TextureBakeSubdivide.cpp}::PassResult::changed, edge_len_sq(), Slic3r::TextureBake::anonymous_namespace{TextureBakeSubdivide.cpp}::PassResult::face_excluded, Slic3r::TextureBake::anonymous_namespace{TextureBakeSubdivide.cpp}::PassResult::face_parent_id, Slic3r::TextureBake::QuantizedPointMap::get_key(), get_midpoint(), Slic3r::TextureBake::QuantizedPointMap::get_or_set_key(), Slic3r::TextureBake::anonymous_namespace{TextureBakeSubdivide.cpp}::PassResult::indices, Slic3r::TextureBake::VertStore::pos, Slic3r::range(), Slic3r::TextureBake::QuantizedPointMap::size(), Slic3r::TextureBake::SUBDIVIDE_GRADE, and subdivide_pass().

Referenced by subdivide_pass().

Here is the call graph for this function:
Here is the caller graph for this function: