Blender V4.5
mesh_boolean_manifold.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#ifdef WITH_MANIFOLD
6# include <algorithm>
7# include <iostream>
8
9# include "BLI_array.hh"
10# include "BLI_array_utils.hh"
11# include "BLI_map.hh"
12# include "BLI_math_geom.h"
13# include "BLI_math_matrix.h"
14# include "BLI_math_matrix.hh"
16# include "BLI_math_vector.hh"
18# include "BLI_offset_indices.hh"
19# include "BLI_span.hh"
20# include "BLI_task.hh"
21# include "BLI_vector.hh"
22
23// #define DEBUG_TIME
24# ifdef DEBUG_TIME
25# include "BLI_timeit.hh"
26# endif
27
28# include "BKE_attribute.hh"
29# include "BKE_attribute_math.hh"
30# include "BKE_deform.hh"
31# include "BKE_geometry_set.hh"
32# include "BKE_instances.hh"
33# include "BKE_lib_id.hh"
34# include "BKE_mesh.hh"
35
37
39
40# include "manifold/manifold.h"
41
42using manifold::Manifold;
43using manifold::MeshGL;
44
45/* Using this for now for debug printing of materials. Can remove later. */
46# include "DNA_material_types.h"
47
49
50/* Some debug output functions. */
51
52template<typename T> static void dump_span(Span<T> span, const std::string &name)
53{
54 std::cout << name << ":";
55 for (const int i : span.index_range()) {
56 if (i % 10 == 0) {
57 std::cout << "\n[" << i << "] ";
58 }
59 std::cout << span[i] << " ";
60 }
61 std::cout << "\n";
62}
63
64template<typename T>
65static void dump_span_with_stride(Span<T> span, int stride, const std::string &name)
66{
67 std::cout << name << ":";
68 for (const int i : span.index_range()) {
69 if (i % 10 == 0) {
70 std::cout << "\n[" << i << "] ";
71 }
72 std::cout << span[i] << " ";
73 if (stride > 1 && (i % stride) == stride - 1) {
74 std::cout << "/ ";
75 }
76 }
77 std::cout << "\n";
78}
79
80template<typename T>
81static void dump_vector(const std::vector<T> &vec, int stride, const std::string &name)
82{
83 std::cout << name << ":";
84 for (int i = 0; i < vec.size(); i++) {
85 if (i % 10 == 0) {
86 std::cout << "\n[" << i << "] ";
87 }
88 std::cout << vec[i] << " ";
89 if (stride > 1 && (i % stride) == stride - 1) {
90 std::cout << "/ ";
91 }
92 }
93 std::cout << "\n";
94}
95
96template<typename T>
97static void dump_vector_values(const std::string indent,
98 const std::string &assign_to,
99 const std::vector<T> &vec)
100{
101 std::cout << indent << assign_to << " = { ";
102 for (int i = 0; i < vec.size(); i++) {
103 if (i > 0 && (i % 10) == 0) {
104 std::cout << "\n" << indent << indent;
105 }
106 std::cout << vec[i];
107 if (i == vec.size() - 1) {
108 std::cout << " };\n";
109 }
110 else {
111 std::cout << ", ";
112 }
113 }
114}
115
116static void dump_meshgl(const MeshGL &mgl, const std::string &name)
117{
118 std::cout << "\nMeshGL " << name << ":\n"
119 << "num verts = " << mgl.NumVert() << "\nnum triangles = " << mgl.NumTri() << "\n"
120 << "\n";
121 dump_vector(mgl.vertProperties, mgl.numProp, "vertProperties");
122 dump_vector(mgl.triVerts, 3, "triVerts");
123 dump_vector(mgl.faceID, 1, "faceID");
124 if (!mgl.mergeFromVert.empty()) {
125 dump_vector(mgl.mergeFromVert, 1, "mergeFromVert");
126 dump_vector(mgl.mergeToVert, 1, "mergeToVert");
127 }
128 dump_vector(mgl.runIndex, 1, "runIndex");
129 dump_vector(mgl.runOriginalID, 1, "runOrigiinalID");
130}
131
132[[maybe_unused]] static void dump_meshgl_for_debug(const MeshGL &mgl)
133{
134 std::string indent = " ";
135 std::cout << indent << "MeshGL m;\n";
136 std::cout << indent << "m.numProp = " << mgl.numProp << ";\n";
137 dump_vector_values(indent, "m.vertProperties", mgl.vertProperties);
138 dump_vector_values(indent, "m.triVerts", mgl.triVerts);
139 if (!mgl.mergeFromVert.empty()) {
140 dump_vector_values(indent, "m.mergeFromVert", mgl.mergeFromVert);
141 dump_vector_values(indent, "m.mergeToVert", mgl.mergeToVert);
142 }
143 dump_vector_values(indent, "m.runIndex", mgl.runIndex);
144 dump_vector_values(indent, "m.runOriginalID", mgl.runOriginalID);
145 dump_vector_values(indent, "m.faceID", mgl.faceID);
146 BLI_assert(mgl.runTransform.size() == 0);
147 BLI_assert(mgl.halfedgeTangent.size() == 0);
148 if (mgl.tolerance != 0) {
149 std::cout << indent << "m.tolerance = " << mgl.tolerance << ";\n";
150 }
151}
152
153static const char *domain_names[] = {
154 "point", "edge", "face", "corner", "curve", "instance", "layer"};
155
156static void dump_mesh(const Mesh *mesh, const std::string &name)
157{
158 std::cout << "\nMesh " << name << ":\n"
159 << "verts_num = " << mesh->verts_num << "\nfaces_num = " << mesh->faces_num
160 << "\nedges_num = " << mesh->edges_num << "\ncorners_num = " << mesh->corners_num
161 << "\n";
162 dump_span(mesh->vert_positions(), "verts");
163 dump_span(mesh->edges(), "edges");
164 dump_span(mesh->corner_verts(), "corner_verts");
165 dump_span(mesh->corner_edges(), "corner_edges");
166 dump_span(mesh->face_offsets(), "face_offsets");
167 std::cout << "triangulation:\n";
168 dump_span(mesh->corner_tris(), "corner_tris");
169 dump_span(mesh->corner_tri_faces(), "corner_tri_faces");
170 std::cout << "attributes:\n";
171 bke::AttributeAccessor attrs = mesh->attributes();
172 attrs.foreach_attribute([&](const bke::AttributeIter &iter) {
173 if (ELEM(iter.name, "position", ".edge_verts", ".corner_vert", ".corner_edge")) {
174 return;
175 }
176 const int di = int8_t(iter.domain);
177 const char *domain = (di >= 0 && di < ATTR_DOMAIN_NUM) ? domain_names[di] : "?";
178 std::string label = std::string(domain) + ": " + iter.name;
179 switch (iter.data_type) {
180 case CD_PROP_FLOAT: {
181 VArraySpan<float> floatspan(*attrs.lookup<float>(iter.name));
182 dump_span(floatspan, label);
183 } break;
184 case CD_PROP_INT32:
185 case CD_PROP_BOOL: {
186 const VArraySpan<int> intspan(*attrs.lookup<int>(iter.name));
187 dump_span(intspan, label);
188 } break;
189 case CD_PROP_FLOAT3: {
190 const VArraySpan<float3> float3span(*attrs.lookup<float3>(iter.name));
191 dump_span(float3span, label);
192 } break;
193 case CD_PROP_FLOAT2: {
194 const VArraySpan<float2> float2span(*attrs.lookup<float2>(iter.name));
195 dump_span(float2span, label);
196 } break;
197 default:
198 std::cout << label << " attribute not dumped\n";
199 break;
200 }
201 });
202 std::cout << "materials:\n";
203 for (int i = 0; i < mesh->totcol; i++) {
204 std::cout << "[" << i << "]: " << (mesh->mat[i] ? mesh->mat[i]->id.name + 2 : "none") << "\n";
205 }
206}
207
214struct MeshOffsets {
215 Array<int> vert_start;
216 Array<int> face_start;
217 Array<int> edge_start;
218 Array<int> corner_start;
219 OffsetIndices<int> vert_offsets;
220 OffsetIndices<int> face_offsets;
221 OffsetIndices<int> edge_offsets;
222 OffsetIndices<int> corner_offsets;
223
224 MeshOffsets(Span<const Mesh *> meshes);
225};
226
227MeshOffsets::MeshOffsets(Span<const Mesh *> meshes)
228{
229 const int meshes_num = meshes.size();
230 this->vert_start.reinitialize(meshes_num + 1);
231 this->face_start.reinitialize(meshes_num + 1);
232 this->edge_start.reinitialize(meshes_num + 1);
233 this->corner_start.reinitialize(meshes_num + 1);
234 for (int i = 0; i <= meshes_num; i++) {
235 this->vert_start[i] = (i == 0) ? 0 : this->vert_start[i - 1] + meshes[i - 1]->verts_num;
236 this->face_start[i] = (i == 0) ? 0 : this->face_start[i - 1] + meshes[i - 1]->faces_num;
237 this->edge_start[i] = (i == 0) ? 0 : this->edge_start[i - 1] + meshes[i - 1]->edges_num;
238 this->corner_start[i] = (i == 0) ? 0 : this->corner_start[i - 1] + meshes[i - 1]->corners_num;
239 }
240 this->vert_offsets = OffsetIndices<int>(this->vert_start);
241 this->face_offsets = OffsetIndices<int>(this->face_start);
242 this->edge_offsets = OffsetIndices<int>(this->edge_start);
243 this->corner_offsets = OffsetIndices<int>(this->corner_start);
244}
245
262static void get_manifold(Manifold &manifold,
263 const Span<const Mesh *> meshes,
264 int mesh_index,
265 const MeshOffsets &mesh_offsets)
266{
267 constexpr int dbg_level = 0;
268 if (dbg_level > 0) {
269 std::cout << "get_manifold for mesh " << mesh_index << "\n";
270 }
271 /* Use the original mesh for simplicity for some things, and use the joined mesh for data that
272 * could have been affected by a transform input. A potential optimization would be retrieving
273 * the data from an original mesh instead if the corresponding transform was the identity. */
274 const Mesh &mesh = *meshes[mesh_index];
275 const OffsetIndices<int> faces = mesh.faces();
276 const Span<int> corner_verts = mesh.corner_verts();
277 const Span<int3> corner_tris = mesh.corner_tris();
278
279 MeshGL meshgl;
280
281 constexpr int props_num = 3;
282 meshgl.numProp = props_num;
283 meshgl.vertProperties.resize(size_t(mesh.verts_num) * props_num);
284 array_utils::copy(mesh.vert_positions(), MutableSpan(meshgl.vertProperties).cast<float3>());
285
286 /* Using separate a OriginalID for each input face will prevent coplanar
287 * faces from being merged. We need this until the fix introduced in
288 * Manifold at version 3.1.0. */
289 constexpr bool use_runids = false;
290 if (use_runids) {
291 meshgl.runIndex.resize(mesh.faces_num);
292 meshgl.runOriginalID.resize(mesh.faces_num);
293 }
294
295 const int face_start = mesh_offsets.face_start[mesh_index];
296
297 meshgl.faceID.resize(corner_tris.size());
298 /* Inlined copy of #corner_tris_calc_face_indices with an offset added to the face index. */
299 MutableSpan face_ids = MutableSpan(meshgl.faceID);
300 threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) {
301 for (const int i : range) {
302 const IndexRange face = faces[i];
303 const int start = poly_to_tri_count(int(i), int(face.start()));
304 const int num = bke::mesh::face_triangles_num(int(face.size()));
305 face_ids.slice(start, num).fill(uint32_t(i + face_start));
306 if (use_runids) {
307 meshgl.runOriginalID[i] = face_start + i;
308 meshgl.runIndex[i] = start * 3;
309 }
310 }
311 });
312
313 meshgl.triVerts.resize(corner_tris.size() * 3);
314 MutableSpan vert_tris = MutableSpan(meshgl.triVerts).cast<int3>();
315 bke::mesh::vert_tris_from_corner_tris(corner_verts, corner_tris, vert_tris);
316
317 if (!use_runids) {
318 meshgl.runIndex.resize(2);
319 meshgl.runOriginalID.resize(1);
320 meshgl.runIndex[0] = 0;
321 meshgl.runIndex[1] = corner_tris.size() * 3;
322 meshgl.runOriginalID[0] = mesh_index;
323 }
324 if (dbg_level > 0) {
325 dump_meshgl(meshgl, "converted result for mesh " + std::to_string(mesh_index));
326 if (dbg_level > 1) {
327 dump_meshgl_for_debug(meshgl);
328 }
329 }
330 {
331# ifdef DEBUG_TIME
332 timeit::ScopedTimer mtimer("manifold constructor from meshgl");
333# endif
334 manifold = Manifold(meshgl);
335 }
336}
337
342static void get_manifolds(MutableSpan<Manifold> manifolds,
343 const Span<const Mesh *> meshes,
344 const Span<float4x4> transforms,
345 const MeshOffsets &mesh_offsets)
346{
347 constexpr int dbg_level = 0;
348 if (dbg_level > 0) {
349 std::cout << "GET_MANIFOLDS\n";
350 std::cout << "\nMesh Offset (starts):\n";
351 dump_span(mesh_offsets.vert_start.as_span(), "vert");
352 dump_span(mesh_offsets.face_start.as_span(), "face");
353 dump_span(mesh_offsets.edge_start.as_span(), "edge");
354 dump_span(mesh_offsets.corner_start.as_span(), "corner");
355 }
356 const int meshes_num = manifolds.size();
357
358 /* Transforming the original input meshes is a simple way to reuse the Mesh::corner_tris() cache
359 * for un-transformed meshes. This should reduce memory usage and help to avoid unnecessary cache
360 * re-computations. */
361 Array<const Mesh *> transformed_meshes(meshes_num);
362 for (const int i : meshes.index_range()) {
363 if (math::is_identity(transforms[i])) {
364 transformed_meshes[i] = meshes[i];
365 }
366 else {
367 Mesh *transformed_mesh = BKE_mesh_copy_for_eval(*meshes[i]);
368 bke::mesh_transform(*transformed_mesh, transforms[i], false);
369 transformed_meshes[i] = transformed_mesh;
370 }
371 }
372
373 if (dbg_level > 0) {
374 for (const int mesh_index : IndexRange(meshes_num)) {
375 get_manifold(manifolds[mesh_index], transformed_meshes, mesh_index, mesh_offsets);
376 }
377 }
378 else {
379 threading::parallel_for_each(IndexRange(meshes_num), [&](int mesh_index) {
380 get_manifold(manifolds[mesh_index], transformed_meshes, mesh_index, mesh_offsets);
381 });
382 }
383
384 for (const int i : transformed_meshes.index_range()) {
385 if (transformed_meshes[i] != meshes[i]) {
386 BKE_id_free(nullptr, const_cast<Mesh *>(transformed_meshes[i]));
387 }
388 }
389}
390
391constexpr int inline_outface_size = 8;
392
393struct OutFace {
394 /* Vertex ids in meshgl indexing space. */
396 /* The faceID input to manifold, i.e. original face id in combined input mesh indexing space.
397 */
398 int face_id;
399
400 /* Find the first index (should be only one) of verts that contains v, else -1. */
401 int find_vert_index(int v) const
402 {
403 return verts.first_index_of_try(v);
404 }
405};
406
408struct MeshAssembly {
409 /* Vertex positions, linearized (use vertpos_stride to multiply index). */
410 MutableSpan<float> vertpos;
411 int vertpos_stride = 3;
412 /* How many vertices were in the combined input meshes. */
413 int input_verts_num;
414 /* How many vertices are in the output (i.e., in vertpos). */
415 int output_verts_num;
416 /* The new output faces. */
417 Vector<OutFace> new_faces;
418 /* If we have to delete vertices, this map will have non-zero size and
419 * will map the MeshGL vertex index to final vertex index.
420 * Also, if this mapping happens, the vertpos array will be modified
421 * accordingly.
422 */
423 Vector<int> old_to_new_vert_map;
424
425 float3 vert_position(const int v) const
426 {
427 const int start = vertpos_stride * v;
428 return float3(vertpos[start], vertpos[start + 1], vertpos[start + 2]);
429 }
430
431 int mapped_vert(const int v) const
432 {
433 if (!new_faces.is_empty()) {
434 return old_to_new_vert_map[v];
435 }
436 return v;
437 }
438};
439
446class OutToInMaps {
447 Array<int> vertex_map_;
448 Array<int> face_map_;
449 Array<int> edge_map_;
450 Array<int> corner_map_;
451
452 const MeshAssembly *mesh_assembly_;
453 const Mesh *joined_mesh_;
454 const Mesh *output_mesh_;
455
456 public:
457 OutToInMaps(const MeshAssembly *mesh_assembly, const Mesh *joined_mesh, const Mesh *output_mesh)
458 : mesh_assembly_(mesh_assembly), joined_mesh_(joined_mesh), output_mesh_(output_mesh)
459 {
460 }
461
462 Span<int> ensure_vertex_map();
463 Span<int> ensure_face_map();
464 Span<int> ensure_edge_map();
465 Span<int> ensure_corner_map();
466};
467
468Span<int> OutToInMaps::ensure_face_map()
469{
470 if (!face_map_.is_empty()) {
471 return face_map_;
472 }
473 /* The MeshAssembly's new_faces should map one to one with output faces. */
474# ifdef DEBUG_TIME
475 timeit::ScopedTimer timer("filling face map");
476# endif
477 face_map_.reinitialize(output_mesh_->faces_num);
478 BLI_assert(mesh_assembly_->new_faces.size() == face_map_.size());
479 constexpr int grain_size = 50000;
480 threading::parallel_for(
481 mesh_assembly_->new_faces.index_range(), grain_size, [&](const IndexRange range) {
482 for (const int i : range) {
483 face_map_[i] = mesh_assembly_->new_faces[i].face_id;
484 }
485 });
486 return face_map_;
487}
488
489Span<int> OutToInMaps::ensure_vertex_map()
490{
491 if (!vertex_map_.is_empty()) {
492 return vertex_map_;
493 }
494 /* There may be better ways, but for now we discover the output to input
495 * vertex mapping by going through the output faces, and for each, looking
496 * through the vertices of the corresponding input face for matches.
497 */
498 const Span<int> face_map = this->ensure_face_map();
499# ifdef DEBUG_TIME
500 timeit::ScopedTimer timer("filling vertex map");
501# endif
502 vertex_map_ = Array<int>(output_mesh_->verts_num, -1);
503 /* To parallelize this, need to deal with the fact that this will
504 * have different threads wanting to write vertex_map, and also want
505 * determinism of which one wins if there is more than one possibility.
506 */
507 const OffsetIndices<int> in_faces = joined_mesh_->faces();
508 const OffsetIndices<int> out_faces = output_mesh_->faces();
509 const Span<int> in_corner_verts = joined_mesh_->corner_verts();
510 const Span<int> out_corner_verts = output_mesh_->corner_verts();
511 const Span<float3> out_vert_positions = output_mesh_->vert_positions();
512 const Span<float3> in_vert_positions = joined_mesh_->vert_positions();
513 for (const int out_face_index : IndexRange(output_mesh_->faces_num)) {
514 const int in_face_index = face_map[out_face_index];
515 const IndexRange in_face = in_faces[in_face_index];
516 const IndexRange out_face = out_faces[out_face_index];
517 const Span<int> in_face_verts = in_corner_verts.slice(in_face);
518 for (const int out_v : out_corner_verts.slice(out_face)) {
519 if (vertex_map_[out_v] != -1) {
520 continue;
521 }
522 float3 out_pos = out_vert_positions[out_v];
523 const auto *it = std::find_if(in_face_verts.begin(), in_face_verts.end(), [&](int in_v) {
524 return out_pos == in_vert_positions[in_v];
525 });
526 if (it != in_face_verts.end()) {
527 int in_v = in_face_verts[std::distance(in_face_verts.begin(), it)];
528 vertex_map_[out_v] = in_v;
529 }
530 }
531 }
532 return vertex_map_;
533}
534
535Span<int> OutToInMaps::ensure_corner_map()
536{
537 if (!corner_map_.is_empty()) {
538 return corner_map_;
539 }
540 /* There may be better ways, but for now we discover the output to input
541 * corner mapping by going through the output faces, and for each, looking
542 * through the corners of the corresponding input face for matches of the
543 * vertex involved.
544 */
545 const Span<int> face_map = this->ensure_face_map();
546 const Span<int> vert_map = this->ensure_vertex_map();
547# ifdef DEBUG_TIME
548 timeit::ScopedTimer timer("filling corner map");
549# endif
550 corner_map_ = Array<int>(output_mesh_->corners_num, -1);
551 const OffsetIndices<int> in_faces = joined_mesh_->faces();
552 const OffsetIndices<int> out_faces = output_mesh_->faces();
553 const Span<int> in_corner_verts = joined_mesh_->corner_verts();
554 const Span<int> out_corner_verts = output_mesh_->corner_verts();
555 constexpr int grain_size = 10000;
556 threading::parallel_for(
557 IndexRange(output_mesh_->faces_num), grain_size, [&](const IndexRange range) {
558 for (const int out_face_index : range) {
559 const int in_face_index = face_map[out_face_index];
560 const IndexRange in_face = in_faces[in_face_index];
561 for (const int out_c : out_faces[out_face_index]) {
562 BLI_assert(corner_map_[out_c] == -1);
563 const int out_v = out_corner_verts[out_c];
564 const int in_v = vert_map[out_v];
565 if (in_v == -1) {
566 continue;
567 }
568 const int in_face_i = in_corner_verts.slice(in_face).first_index_try(in_v);
569 if (in_face_i != -1) {
570 const int in_c = in_face[in_face_i];
571 corner_map_[out_c] = in_c;
572 }
573 }
574 }
575 });
576 return corner_map_;
577}
578
579static bool same_dir(const float3 &p1, const float3 &p2, const float3 &q1, const float3 &q2)
580{
581 float3 p = p1 - p2;
582 float3 q = q1 - q2;
583 float pq = math::length(p) * math::length(q);
584 if (pq == 0.0f) {
585 return true;
586 }
587 float abs_cos_pq = math::abs(math::dot(p, q) / pq);
588 return (math::abs(abs_cos_pq - 1.0f) <= 1e-5f);
589}
590
591Span<int> OutToInMaps::ensure_edge_map()
592{
593 constexpr int dbg_level = 0;
594 if (!edge_map_.is_empty()) {
595 return edge_map_;
596 }
597 if (dbg_level > 0) {
598 std::cout << "\nensure_edge_map\n";
599 if (dbg_level > 1) {
600 dump_mesh(joined_mesh_, "joined_mesh");
601 dump_mesh(output_mesh_, "output_mesh");
602 }
603 }
604 /* There may be better ways to get the edge map, but for now
605 * we go through the output faces, and for each edge, see if
606 * there is an input edge in the corresponding input face that
607 * has one or the other end in common, and if only one end is
608 * in common, is in approximately the same direction.
609 * We can assume that the output and input are manifold.
610 * So if there is an edge that starts or ends at a corner in
611 * the corresponding input face, then we need only look for the
612 * "starts at" case, because if it is "ends at" in this face, it
613 * should be "starts at" in the matching face.
614 */
615 const Span<int> face_map = this->ensure_face_map();
616 const Span<int> vert_map = this->ensure_vertex_map();
617 const Span<int> corner_map = this->ensure_corner_map();
618 /* To parallelize this, would need a way to figure out that
619 * this is the "canonical" edge representative so that only
620 * one thread tries to write this. Or could use atomic operations.
621 */
622# ifdef DEBUG_TIME
623 timeit::ScopedTimer timer("filling edge map");
624# endif
625 edge_map_ = Array<int>(output_mesh_->edges_num, -1);
626 const Span<int> out_corner_edges = output_mesh_->corner_edges();
627 const Span<int> out_corner_verts = output_mesh_->corner_verts();
628 const Span<int2> out_edges = output_mesh_->edges();
629 const Span<float3> out_positions = output_mesh_->vert_positions();
630 const Span<int> in_corner_edges = joined_mesh_->corner_edges();
631 const Span<int> in_corner_verts = joined_mesh_->corner_verts();
632 const Span<int2> in_edges = joined_mesh_->edges();
633 const Span<float3> in_positions = joined_mesh_->vert_positions();
634 const OffsetIndices<int> in_faces = joined_mesh_->faces();
635 const OffsetIndices<int> out_faces = output_mesh_->faces();
636 Array<bool> done_edge(output_mesh_->edges_num, false);
637 for (const int out_face_index : IndexRange(output_mesh_->faces_num)) {
638 const int in_face_index = face_map[out_face_index];
639 const IndexRange in_face = in_faces[in_face_index];
640 if (dbg_level > 0) {
641 std::cout << "process out_face = " << out_face_index << ", in_face = " << in_face_index
642 << "\n";
643 }
644 for (const int out_c : out_faces[out_face_index]) {
645 const int in_c = corner_map[out_c];
646 if (dbg_level > 0) {
647 std::cout << " out_c = " << out_c << ", in_c = " << in_c << "\n";
648 }
649 if (in_c == -1) {
650 /* No possible "starts at" match here. */
651 continue;
652 }
653 const int out_e = out_corner_edges[out_c];
654 if (dbg_level > 0) {
655 std::cout << " out_e = " << out_e << ", done = " << done_edge[out_e] << "\n";
656 }
657 if (done_edge[out_e]) {
658 continue;
659 }
660 const int out_v = out_corner_verts[out_c];
661 const int in_e = in_corner_edges[in_c];
662 const int in_v = in_corner_verts[in_c];
663 /* Because of corner mapping, the output vertex should map to the input one. */
664 BLI_assert(vert_map[out_v] == in_v);
665 int2 out_e_v = out_edges[out_e];
666 if (out_e_v[0] != out_v) {
667 out_e_v = {out_e_v[1], out_e_v[0]};
668 }
669 int2 in_e_v = in_edges[in_e];
670 if (in_e_v[0] != in_v) {
671 in_e_v = {in_e_v[1], in_e_v[0]};
672 }
673 if (dbg_level > 0) {
674 std::cout << " out_v = " << out_v << ", in_e = " << in_e << ", in_v = " << in_v << "\n";
675 std::cout << " out_e_v = " << out_e_v << ", in_e_v = " << in_e_v << "\n";
676 std::cout << " vertex_map(out_e_v) = " << int2(vert_map[out_e_v[0]], vert_map[out_e_v[1]])
677 << "\n";
678 }
679 /* Here out_e_v should hold the output vertices in out_e, with the first
680 * one being out_v, the vertex at corner out_c.
681 * Similarly for in_e_v, with the first one being in_v.
682 */
683 BLI_assert(vert_map[out_e_v[0]] == in_e_v[0]);
684 int edge_rep = -1;
685 if (vert_map[out_e_v[1]] == in_e_v[1]) {
686 /* Here both ends of the edges match. */
687 if (dbg_level > 0) {
688 std::cout << " case 1, edge_rep = in_e = " << in_e << "\n";
689 }
690 edge_rep = in_e;
691 }
692 else if (vert_map[out_e_v[1]] == -1) {
693 /* Here the "ends at" vertex of the output edge is a new vertex.
694 * Does the edge at least go in the same direction as in_e?
695 */
696 if (same_dir(out_positions[out_e_v[0]],
697 out_positions[out_e_v[1]],
698 in_positions[in_e_v[0]],
699 in_positions[in_e_v[1]]))
700 {
701 if (dbg_level > 0) {
702 std::cout << " case 2, edge_rep = in_e = " << in_e << "\n";
703 }
704 edge_rep = in_e;
705 }
706 }
707 /* It is possible that the output face and corresponding
708 * input face have opposite winding. So do all of the previous
709 * again with the previous edge of input face but same edge of
710 * output face.
711 */
712 if (edge_rep == -1) {
713 const int in_c_prev = bke::mesh::face_corner_prev(in_face, in_c);
714 const int in_e_prev = in_corner_edges[in_c_prev];
715 const int in_v_prev = in_corner_verts[in_c_prev];
716 int2 in_e_v_prev = in_edges[in_e_prev];
717 if (in_e_v_prev[0] != in_v_prev) {
718 in_e_v_prev = {in_e_v_prev[1], in_e_v_prev[0]};
719 }
720 if (dbg_level > 0) {
721 std::cout << " in_c_prev = " << in_c_prev << ", in_e_prev = " << in_e_prev
722 << ", in_v_prev = " << in_v_prev << "\n";
723 std::cout << " in_e_v_prev = " << in_e_v_prev << "\n";
724 }
725 if (vert_map[out_e_v[0]] == in_e_v_prev[1]) {
726 if (vert_map[out_e_v[1]] == in_e_v_prev[0]) {
727 if (dbg_level > 0) {
728 std::cout << " case 3, edge_rep = in_e_prev = " << in_e_prev << "\n";
729 }
730 edge_rep = in_e_prev;
731 }
732 else if (vert_map[out_e_v[1]] == -1) {
733 if (same_dir(out_positions[out_e_v[0]],
734 out_positions[out_e_v[1]],
735 in_positions[in_e_v_prev[0]],
736 in_positions[in_e_v_prev[1]]))
737 {
738 if (dbg_level > 0) {
739 std::cout << " case 4, edge_rep = in_e_prev = " << in_e_prev << "\n";
740 }
741 edge_rep = in_e_prev;
742 }
743 }
744 }
745 }
746 if (edge_rep != -1) {
747 if (dbg_level > 0) {
748 std::cout << " found: set edge_map[" << out_e << "] = " << edge_rep << "\n";
749 }
750 edge_map_[out_e] = edge_rep;
751 done_edge[out_e] = true;
752 }
753 }
754 }
755 return edge_map_;
756}
757
759constexpr int face_group_inline = 4;
760
766static Array<Vector<int, face_group_inline>> get_face_groups(const MeshGL &mgl,
767 int input_faces_num)
768{
769# ifdef DEBUG_TIME
770 timeit::ScopedTimer timer("get_face_groups");
771# endif
772 constexpr int dbg_level = 0;
773 Array<Vector<int, face_group_inline>> fg(input_faces_num);
774 const int tris_num = mgl.NumTri();
775 BLI_assert(mgl.faceID.size() == tris_num);
776 for (const int t : IndexRange(tris_num)) {
777 const int faceid = mgl.faceID[t];
778 fg[faceid].append(t);
779 }
780 if (dbg_level > 0) {
781 std::cout << "face_groups\n";
782 for (const int i : fg.index_range()) {
783 std::cout << "orig face " << i;
784 dump_span(fg[i].as_span(), "");
785 }
786 }
787 return fg;
788}
789
790# if 0
791/* TODO: later */
798static uchar check_original_face(const Vector<int, face_group_inline> &group,
799 const MeshGL &mgl,
800 const Mesh *mesh,
801 int face_index)
802{
803 BLI_assert(0 <= face_index && face_index < mesh->faces_num);
804 const IndexRange orig_face = mesh->faces()[face_index];
805 /* The face can't be original if the number of triangles isn't equal
806 * to the original face size minus 2. */
807 int orig_face_size = orig_face.size();
808 if (orig_face_size != group.size() + 2) {
809 return 0;
810 }
811 Span<int> orig_face_verts = mesh->corner_verts().slice(mesh->faces()[face_index]);
812 /* edge_value[i] will be 1 if that edge is identical to an output edge,
813 * and -1 if it is the reverse of an output edge. */
814 Array<uchar, 20> edge_value(orig_face_size, 0);
815 int stride = mgl.numProp;
816 for (const int t : group) {
817 /* face_vert_index[i] will hold the position in input face face_index
818 * where the i'th vertex of triangle t is (assuming that no position
819 * is exactly repeated in an output face). -1 if there is no such. */
820 Array<int, 3> face_vert_index(3);
821 for (const int i : IndexRange(3)) {
822 int v = mgl.triVerts[3 * t + i];
823 int prop_offset = v * stride;
824 float3 pos(mgl.vertProperties[prop_offset],
825 mgl.vertProperties[prop_offset + 1],
826 mgl.vertProperties[prop_offset + 2]);
827 auto it = std::find_if(orig_face_verts.begin(), orig_face_verts.end(), [&](int orig_v) {
828 return pos == mesh->vert_positions()[orig_v];
829 });
830 face_vert_index[i] = it == orig_face_verts.end() ?
831 -1 :
832 std::distance(orig_face_verts.begin(), it);
833 }
834 /* Now we can tell which original edges are covered by t. */
835 for (const int i : IndexRange(3)) {
836 const int a = face_vert_index[i];
837 const int b = face_vert_index[(i + 1) % 3];
838 if (a != -1 && b != -1) {
839 if ((a + 1) % orig_face_size == b) {
840 edge_value[a] = 1;
841 }
842 else if ((b + 1) % orig_face_size == a) {
843 edge_value[b] = -1;
844 }
845 }
846 }
847 }
848 if (std::all_of(edge_value.begin(), edge_value.end(), [](int x) { return x == 1; })) {
849 return 1;
850 }
851 else if (std::all_of(edge_value.begin(), edge_value.end(), [](int x) { return x == -1; })) {
852 return 2;
853 }
854 return 0;
855}
856# endif
857
858static OutFace make_out_face(const MeshGL &mgl, int tri_index, int orig_face)
859{
860 OutFace ans;
862 const int k = 3 * tri_index;
863 ans.verts[0] = mgl.triVerts[k];
864 ans.verts[1] = mgl.triVerts[k + 1];
865 ans.verts[2] = mgl.triVerts[k + 2];
866 ans.face_id = orig_face;
867 return ans;
868}
869
877struct SharedEdge {
878 /* First shared edge ("group edge" indexing). */
879 int e1;
880 /* Second shared edge. */
881 int e2;
882 /* First vertex for e1 (second for e2). */
883 int v1;
884 /* Second vertex for e1 (first for e2). */
885 int v2;
886
887 SharedEdge(int e1, int e2, int v1, int v2) : e1(e1), e2(e2), v1(v1), v2(v2) {}
888
889 /* Return the indices (in the linearized triangle space of an OutFace group)
890 * corresponding to e1 and e2. */
891 int2 outface_group_face_indices() const
892 {
893 return int2(e1 / 3, e2 / 3);
894 }
895};
896
898static inline SharedEdge canon_shared_edge(int e1, int e2, int v1, int v2)
899{
900 if (v1 < v2) {
901 return SharedEdge(e1, e2, v1, v2);
902 }
903 return SharedEdge(e2, e1, v2, v1);
904}
905
911static SharedEdge get_shared_edge_from_pair(const OutFace &tri1, const OutFace &tri2)
912{
913 /* There should be at most one shared edge between the tri1 and tri2. Find it. */
914 SharedEdge shared_edge(-1, -1, -1, -1);
915 for (const int i1 : IndexRange(3)) {
916 for (const int i2 : IndexRange(3)) {
917 const int v1 = tri1.verts[i1];
918 const int v2 = tri2.verts[i2];
919 if (v1 == v2) {
920 const int v1_next = tri1.verts[(i1 + 1) % 3];
921 const int v2_prev = tri2.verts[(i2 + 2) % 3];
922 if (v1_next == v2_prev) {
923 shared_edge = SharedEdge(i1, 3 + ((i2 + 2) % 3), v1, v1_next);
924 break;
925 }
926 const int v1_prev = tri1.verts[(i1 + 2) % 3];
927 const int v2_next = tri2.verts[(i2 + 1) % 3];
928 if (v1_prev == v2_next) {
929 shared_edge = SharedEdge((i1 + 2) % 3, 3 + i2, v1_prev, v1);
930 break;
931 }
932 }
933 }
934 if (shared_edge.e1 != -1) {
935 break;
936 }
937 }
938 return shared_edge;
939}
940
946static Vector<SharedEdge> get_shared_edges(Span<OutFace> faces)
947{
949 /* Map from two vert indices making an edge to where that edge appears
950 * in list of group edges. */
951 Map<int2, int> edge_verts_to_tri;
952 for (const int face_index : faces.index_range()) {
953 const OutFace &f = faces[face_index];
954 for (const int i : IndexRange(3)) {
955 int v1 = f.verts[i];
956 int v2 = f.verts[(i + 1) % 3];
957 int this_e = face_index * 3 + i;
958 edge_verts_to_tri.add_new(int2(v1, v2), this_e);
959 int other_e = edge_verts_to_tri.lookup_default(int2(v2, v1), -1);
960 if (other_e != -1) {
961 ans.append(canon_shared_edge(this_e, other_e, v1, v2));
962 }
963 }
964 }
965 return ans;
966}
967
973static bool is_legal_merge(const OutFace &f1, const OutFace &f2, int v1, int v2)
974{
975 /* For now, just look for each non-splice-involved vertex of each face to see if
976 * it is in the other face.
977 * TODO: if the faces are big, sort both together and look for repeats after sorting.
978 */
979 for (const int v : f1.verts) {
980 if (!ELEM(v, v1, v2)) {
981 if (f2.find_vert_index(v) != -1) {
982 return false;
983 }
984 }
985 }
986 for (const int v : f2.verts) {
987 if (!ELEM(v, v1, v2)) {
988 if (f1.find_vert_index(v) != -1) {
989 return false;
990 }
991 }
992 }
993 return true;
994}
995
1004static bool try_merge_out_face_pair(OutFace &f1, const OutFace &f2, const SharedEdge &se)
1005{
1006
1007 constexpr int dbg_level = 0;
1008 if (dbg_level > 0) {
1009 std::cout << "try_merge_out_face_pair\n";
1010 dump_span(f1.verts.as_span(), "f1");
1011 dump_span(f2.verts.as_span(), "f2");
1012 std::cout << "shared edge: "
1013 << "(e" << se.e1 << ",e" << se.e2 << ";v" << se.v1 << ",v" << se.v2 << ")\n";
1014 }
1015 const int f1_len = f1.verts.size();
1016 const int f2_len = f2.verts.size();
1017 const int v1 = se.v1;
1018 const int v2 = se.v2;
1019 /* Find i1, the index of the earlier of v1 and v2 in f1,
1020 * and i2, the index of the earlier of v1 and v2 in f2. */
1021 const int i1 = f1.find_vert_index(v1);
1022 BLI_assert(i1 != -1);
1023 const int i1_next = (i1 + 1) % f1_len;
1024 const int i2 = f2.find_vert_index(v2);
1025 BLI_assert(i2 != -1);
1026 const int i2_next = (i2 + 1) % f2_len;
1027 BLI_assert(f1.verts[i1] == v1 && f1.verts[i1_next] == v2);
1028 BLI_assert(f2.verts[i2] == v2 && f2.verts[i2_next] == v1);
1029 const bool can_merge = is_legal_merge(f1, f2, v1, v2);
1030 if (dbg_level > 0) {
1031 std::cout << "i1 = " << i1 << ", i2 = " << i2 << ", can_merge = " << can_merge << "\n";
1032 }
1033 if (!can_merge) {
1034 return false;
1035 }
1036 /* The merged face is the concatenation of these slices
1037 * (giving inclusive indices, with implied wrap-around at end of faces):
1038 * f1 : [0, i1]
1039 * f2 : [i2_next+1, i2_prev]
1040 * f1 : [i1_next, f1_len-1]
1041 */
1042 const int i2_prev = (i2 + f2_len - 1) % f2_len;
1043 const int i2_next_next = (i2_next + 1) % f2_len;
1044 const auto *f2_start_it = f2.verts.begin() + i2_next_next;
1045 const auto *f2_end_it = f2.verts.begin() + i2_prev + 1;
1046 if (f2_end_it > f2_start_it) {
1047 f1.verts.insert(i1_next, f2_start_it, f2_end_it);
1048 }
1049 else {
1050 const int n1 = std::distance(f2_start_it, f2.verts.end());
1051 if (n1 > 0) {
1052 f1.verts.insert(i1_next, f2_start_it, f2.verts.end());
1053 }
1054 if (n1 < f2_len - 2) {
1055 f1.verts.insert(i1_next + n1, f2.verts.begin(), f2_end_it);
1056 }
1057 }
1058 if (dbg_level > 0) {
1059 dump_span(f1.verts.as_span(), "merge result");
1060 }
1061 return true;
1062}
1063
1065static void merge_out_face_pair(Vector<OutFace> &faces)
1066{
1067 constexpr int dbg_level = 0;
1068 BLI_assert(faces.size() == 2);
1069 OutFace &tri1 = faces[0];
1070 OutFace &tri2 = faces[1];
1071 if (dbg_level > 0) {
1072 std::cout << "\nmerge_out_face_pair for faceid " << faces[0].face_id << "\n";
1073 dump_span(tri1.verts.as_span(), "tri1");
1074 dump_span(tri2.verts.as_span(), "tri2");
1075 }
1076 const SharedEdge shared_edge = get_shared_edge_from_pair(tri1, tri2);
1077 if (shared_edge.e1 == -1) {
1078 /* No shared edge, so no merging possible. */
1079 return;
1080 }
1081 const int va = shared_edge.v1;
1082 const int vb = shared_edge.v2;
1083 const int e1 = shared_edge.e1;
1084 const int e2 = shared_edge.e2;
1085 if (dbg_level > 0) {
1086 std::cout << "shared_edge = e" << e1 << ", e" << e2 << "; " << va << ", " << vb << "\n";
1087 }
1088 BLI_assert(e1 < 3 && e2 >= 3);
1089 /* Say tri1 has verts starting at pos e1 called a, b, c.
1090 * Then tri2 has verts starting at pos e2-3 called b, a, d.
1091 * So the quad we want is b, c, a, d.
1092 */
1093 const int vc = tri1.verts[(e1 + 2) % 3];
1094 const int vd = tri2.verts[(e2 - 3 + 2) % 3];
1095 BLI_assert(tri1.verts[e1] == va && tri1.verts[(e1 + 1) % 3] == vb && tri2.verts[e2 - 3] == vb &&
1096 tri2.verts[(e2 - 3 + 1) % 3] == va);
1097 if (vc == vd) {
1098 /* This can't happen geometrically, but maybe in extreme cases... */
1099 return;
1100 }
1101 tri1.verts.resize(4);
1102 tri1.verts[0] = vb;
1103 tri1.verts[1] = vc;
1104 tri1.verts[2] = va;
1105 tri1.verts[3] = vd;
1106 if (dbg_level > 0) {
1107 dump_span(tri1.verts.as_span(), "merged quad");
1108 }
1109 faces.resize(1);
1110}
1111
1117static void merge_out_faces(Vector<OutFace> &faces)
1118{
1119 constexpr int dbg_level = 0;
1120 if (faces.size() <= 1) {
1121 return;
1122 }
1123 if (faces.size() == 2) {
1124 merge_out_face_pair(faces);
1125 return;
1126 }
1127 if (dbg_level > 0) {
1128 std::cout << "\nmerge_out_faces for faceid " << faces[0].face_id << "\n";
1129 for (const int i : faces.index_range()) {
1130 const OutFace &f = faces[i];
1131 dump_span(f.verts.as_span(), std::to_string(i));
1132 }
1133 }
1134 Vector<SharedEdge> shared_edges = get_shared_edges(faces);
1135 if (dbg_level > 0) {
1136 std::cout << "shared edges:\n";
1137 for (const SharedEdge &se : shared_edges) {
1138 std::cout << "(e" << se.e1 << ",e" << se.e2 << ";v" << se.v1 << ",v" << se.v2 << ")";
1139 }
1140 std::cout << "\n";
1141 // dump_span(shared_edges.as_span(), "shared edges");
1142 }
1143 if (shared_edges.is_empty()) {
1144 return;
1145 }
1146 /* `shared_edge_valid[i]` is true if both edges in shared_edges[i] are still alive. */
1147 Array<bool> shared_edge_valid(shared_edges.size(), true);
1148 /* If `merged_to_faces[i]` is not -1, then argument faces[i] has been merged to that other face.
1149 */
1150 Array<int> merged_to(faces.size(), -1);
1151 /* Local function to follow merged_to mappings as far as possible. */
1152 auto final_merged_to = [&](int f_orig) {
1153 BLI_assert(f_orig != -1);
1154 int f_mapped = f_orig;
1155 do {
1156 if (merged_to[f_mapped] != -1) {
1157 f_mapped = merged_to[f_mapped];
1158 }
1159 } while (merged_to[f_mapped] != -1);
1160 return f_mapped;
1161 };
1162 /* TODO: sort shared_edges by decreasing length. */
1163 for (const int i : shared_edges.index_range()) {
1164 if (!shared_edge_valid[i]) {
1165 continue;
1166 }
1167 const SharedEdge se = shared_edges[i];
1168 const int2 orig_faces = se.outface_group_face_indices();
1169 const int2 cur_faces = int2(final_merged_to(orig_faces[0]), final_merged_to(orig_faces[1]));
1170 const int f1 = cur_faces[0];
1171 const int f2 = cur_faces[1];
1172 if (f1 == -1 || f2 == -2) {
1173 continue;
1174 }
1175 if (dbg_level > 0) {
1176 std::cout << "try merge of faces " << f1 << " and " << f2 << "\n";
1177 }
1178 if (try_merge_out_face_pair(faces[f1], faces[f2], se)) {
1179 if (dbg_level > 0) {
1180 std::cout << "successful merge\n";
1181 dump_span(faces[f1].verts.as_span(), "new f1");
1182 }
1183 merged_to[f2] = f1;
1184 }
1185 }
1186 /* Now compress the surviving faces. */
1187 int move_from = 0;
1188 int move_to = 0;
1189 const int orig_faces_num = faces.size();
1190 while (move_from < orig_faces_num) {
1191 /* Don't move faces that have been merged elsewhere. */
1192 while (move_from < orig_faces_num && merged_to[move_from] != -1) {
1193 move_from++;
1194 }
1195 if (move_from >= orig_faces_num) {
1196 break;
1197 }
1198 if (move_to < move_from) {
1199 faces[move_to] = faces[move_from];
1200 }
1201 move_to++;
1202 move_from++;
1203 }
1204 if (move_to < orig_faces_num) {
1205 faces.resize(move_to);
1206 }
1207 if (dbg_level > 0) {
1208 std::cout << "final faces:\n";
1209 for (const int i : faces.index_range()) {
1210 dump_span(faces[i].verts.as_span(), std::to_string(i));
1211 }
1212 }
1213}
1214
1216static inline const bool approx_in_line(const float3 &p0, const float3 &p1, const float3 &p2)
1217{
1218 float cos_ang = math::dot(math::normalize(p1 - p0), math::normalize(p2 - p1));
1219 return math::abs(cos_ang - 1.0) < 1e-4;
1220}
1221
1233static void dissolve_valence2_verts(MeshAssembly &ma)
1234{
1235 const int vnum = ma.output_verts_num;
1236 Array<bool> dissolve(vnum, false);
1237 /* We'll rememeber up to two vertex neighbors for each vertex. */
1238 Array<std::pair<int, int>> neighbors(ma.output_verts_num, std::pair<int, int>(-1, -1));
1239 /* First, tentatively set dissolve based on neighbors. Alignment will be checked later. */
1240 for (const int f : ma.new_faces.index_range()) {
1241 const OutFace &face = ma.new_faces[f];
1242 const int fsize = face.verts.size();
1243 for (const int i : IndexRange(fsize)) {
1244 const int vprev = face.verts[(i - 1 + fsize) % fsize];
1245 const int v = face.verts[i];
1246 const int vnext = face.verts[(i + 1) % fsize];
1247 std::pair<int, int> &v_nbrs = neighbors[v];
1248 if (v_nbrs.first == -1) {
1249 /* First time we've seen v. Set the tentative neighbors. */
1250 v_nbrs.first = vprev;
1251 v_nbrs.second = vnext;
1252 /* Don't want to dissolve any vert of a triangle, even if triangle is degenerate. */
1253 dissolve[v] = fsize <= 3 ? false : true;
1254 }
1255 else {
1256 /* Some previous face had v. Disable dissolve unless if neighbors are the same, reversed,
1257 * or if this face is a triangle.
1258 */
1259 if (fsize == 3 || !(vprev == v_nbrs.second && vnext == v_nbrs.first)) {
1260 dissolve[v] = false;
1261 }
1262 }
1263 }
1264 }
1265 /* We can't dissolve so many verts in a face that it leaves less than a triangle.
1266 * This should be rare, since the above logic will prevent dissolving a vert from a triangle,
1267 * but it is possible that two or more verts are to be dissolved from a quad or ngon.
1268 * Do a pass to remove the possiblitiy of dissolving anything from such faces.
1269 */
1270 for (const int f : ma.new_faces.index_range()) {
1271 const OutFace &face = ma.new_faces[f];
1272 const int fsize = face.verts.size();
1273 int num_dissolved = 0;
1274 for (const int i : IndexRange(fsize)) {
1275 if (dissolve[face.verts[i]]) {
1276 num_dissolved++;
1277 }
1278 }
1279 if (fsize - num_dissolved < 3) {
1280 for (const int i : IndexRange(fsize)) {
1281 dissolve[face.verts[i]] = false;
1282 }
1283 }
1284 }
1285 /* Now, for tentative dissolves, check "in a straight line" condition. */
1286 const int grain_size = 15000;
1287 bool any_dissolve = false;
1288 threading::parallel_for(IndexRange(vnum), grain_size, [&](const IndexRange range) {
1289 bool range_any_dissolve = false;
1290 for (const int v : range) {
1291 if (dissolve[v]) {
1292 std::pair<int, int> &v_nbrs = neighbors[v];
1293 BLI_assert(v_nbrs.first != -1 && v_nbrs.second != -1);
1294 const float3 p0 = ma.vert_position(v_nbrs.first);
1295 const float3 p1 = ma.vert_position(v);
1296 const float3 p2 = ma.vert_position(v_nbrs.second);
1297 if (!approx_in_line(p0, p1, p2)) {
1298 dissolve[v] = false;
1299 }
1300 else {
1301 range_any_dissolve = true;
1302 }
1303 }
1304 }
1305 if (range_any_dissolve) {
1306 /* No need for atomics here as this is a single byte. */
1307 any_dissolve = true;
1308 }
1309 });
1310 if (!any_dissolve) {
1311 return;
1312 }
1313
1314 /* We need to compress out the disssolved vertices out of ma.vertpos,
1315 * remap all the faces to account for that compression,
1316 * and rebuild any faces containing those compressed verts.
1317 * The compressing part is a bit like #mesh_copy_selection.
1318 */
1319 IndexMaskMemory memory;
1321 dissolve.index_range(), dissolve.as_span(), memory);
1322 const int new_vnum = keep.size();
1323 ma.old_to_new_vert_map.reinitialize(vnum);
1324 ma.old_to_new_vert_map.fill(-1);
1325 index_mask::build_reverse_map<int>(keep, ma.old_to_new_vert_map);
1326
1327 /* Compress vertpos in place. Is there a parallel way to do this? */
1328 float *vpos_data = ma.vertpos.data();
1329 BLI_assert(ma.vertpos_stride == 3);
1330 for (const int old_v : IndexRange(vnum)) {
1331 const int new_v = ma.old_to_new_vert_map[old_v];
1332 BLI_assert(new_v <= old_v);
1333 if (new_v >= 0) {
1334 std::copy_n(vpos_data + 3 * old_v, 3, vpos_data + 3 * new_v);
1335 }
1336 }
1337 ma.vertpos = ma.vertpos.take_front(new_vnum * ma.vertpos_stride);
1338 ma.output_verts_num = new_vnum;
1339
1340 /* Remap verts and compress dissolved verts in output faces. */
1341 threading::parallel_for(ma.new_faces.index_range(), 10000, [&](IndexRange range) {
1342 for (const int f : range) {
1343 OutFace &face = ma.new_faces[f];
1344 int i_to = 0;
1345 for (const int i_from : face.verts.index_range()) {
1346 const int mapped_v_from = ma.mapped_vert(face.verts[i_from]);
1347 if (mapped_v_from >= 0) {
1348 face.verts[i_to++] = mapped_v_from;
1349 }
1350 }
1351 if (i_to < face.verts.size()) {
1352 BLI_assert(i_to >= 3);
1353 face.verts.resize(i_to);
1354 }
1355 }
1356 });
1357}
1358
1368static MeshAssembly assemble_mesh_from_meshgl(MeshGL &mgl, const MeshOffsets &mesh_offsets)
1369{
1370# ifdef DEBUG_TIME
1371 timeit::ScopedTimer timer("calculating assemble_mesh_from_meshgl");
1372# endif
1373 constexpr int dbg_level = 0;
1374 if (dbg_level > 0) {
1375 std::cout << "assemble_mesh_from_meshgl\n";
1376 }
1377 MeshAssembly ma;
1378 ma.vertpos = MutableSpan<float>(&*mgl.vertProperties.begin(), mgl.vertProperties.size());
1379 ma.vertpos_stride = mgl.numProp;
1380 ma.input_verts_num = mesh_offsets.vert_start.last();
1381 ma.output_verts_num = ma.vertpos.size() / ma.vertpos_stride;
1382 const int input_faces_num = mesh_offsets.face_start.last();
1383
1384 /* For each offset input mesh face, what mgl triangles have it as id? */
1385 Array<Vector<int, face_group_inline>> face_groups = get_face_groups(mgl, input_faces_num);
1386 if (dbg_level > 1) {
1387 std::cout << "groups:\n";
1388 for (const int i : face_groups.index_range()) {
1389 std::cout << "orig (offset) face " << i << ": ";
1390 dump_span(face_groups[i].as_span(), "");
1391 }
1392 }
1393 {
1394# ifdef DEBUG_TIME
1395 timeit::ScopedTimer timer("face merging");
1396# endif
1397 Vector<Vector<OutFace>> new_groups(face_groups.size());
1398 const int grain_size = 15000;
1399 threading::parallel_for(face_groups.index_range(), grain_size, [&](const IndexRange range) {
1400 for (const int gid : range) {
1401 const Span<int> group = face_groups[gid].as_span();
1402 Vector<OutFace> &group_faces = new_groups[gid] = Vector<OutFace, 4>(group.size());
1403 for (const int i : group_faces.index_range()) {
1404 int tri_index = group[i];
1405 group_faces[i] = make_out_face(mgl, tri_index, gid);
1406 }
1407 merge_out_faces(group_faces);
1408 }
1409 });
1410# ifdef DEBUG_TIME
1411 timeit::ScopedTimer xtimer("copying groups at end");
1412# endif
1413 for (const int i : new_groups.index_range()) {
1414 ma.new_faces.extend(new_groups[i].as_span());
1415 }
1416 }
1417 {
1418# ifdef DEBUG_TIME
1419 timeit::ScopedTimer timer("valence-2-vertex dissolving");
1420# endif
1421 dissolve_valence2_verts(ma);
1422 if (ma.old_to_new_vert_map.size() > 0) {
1423 /* We compressed ma.vertpos in place, but that really means
1424 * we compressed mgl.VertProperties, so we need to change its size. */
1425 mgl.vertProperties.resize(ma.vertpos.size());
1426 }
1427 }
1428 if (dbg_level > 0) {
1429 std::cout << "mesh_assembly result:\n";
1430 std::cout << "input_verts_num = " << ma.input_verts_num
1431 << ", output_verts_num = " << ma.output_verts_num << "\n";
1432 dump_span_with_stride(ma.vertpos.as_span(), ma.vertpos_stride, "vertpos");
1433 std::cout << "new_faces:\n";
1434 for (const int i : ma.new_faces.index_range()) {
1435 std::cout << i << ": face_id = " << ma.new_faces[i].face_id << "\nverts ";
1436 dump_span(ma.new_faces[i].verts.as_span(), "");
1437 }
1438 }
1439 return ma;
1440}
1441
1442static void copy_attribute_using_map(const GSpan src,
1443 const Span<int> out_to_in_map,
1444 GMutableSpan dst)
1445{
1446 const CPPType &type = dst.type();
1447 const int grain_size = 20000;
1448 threading::parallel_for(out_to_in_map.index_range(), grain_size, [&](const IndexRange range) {
1449 for (const int out_elem : range) {
1450 const int in_elem = out_to_in_map[out_elem];
1451 if (in_elem != -1) {
1452 type.copy_assign(src[in_elem], dst[out_elem]);
1453 }
1454 }
1455 });
1456}
1457
1458static void interpolate_corner_attributes(bke::MutableAttributeAccessor &output_attrs,
1459 bke::AttributeAccessor &input_attrs,
1460 Mesh *output_mesh,
1461 const Mesh *input_mesh,
1462 const Span<int> out_to_in_corner_map,
1463 const Span<int> out_to_in_face_map)
1464{
1465# ifdef DEBUG_TIME
1466 timeit::ScopedTimer timer("interpolate corner attributes");
1467# endif
1468 /* Make parallel arrays of things needed access and write all corner attributes to interpolate.
1469 */
1474 /* For each index of `srcs` and `dsts`, we need to know if it is a "normal"-like attribute. */
1475 Vector<bool> is_normal_attribute;
1476 input_attrs.foreach_attribute([&](const bke::AttributeIter &iter) {
1477 if (iter.domain != bke::AttrDomain::Corner || ELEM(iter.name, ".corner_vert", ".corner_edge"))
1478 {
1479 return;
1480 }
1481 const bke::GAttributeReader reader = input_attrs.lookup_or_default(
1482 iter.name, iter.domain, iter.data_type);
1483 if (!reader) {
1484 return;
1485 }
1486 writers.append(
1487 output_attrs.lookup_or_add_for_write_span(iter.name, iter.domain, iter.data_type));
1488 readers.append(input_attrs.lookup_or_default(iter.name, iter.domain, iter.data_type));
1489 srcs.append(*readers.last());
1490 dsts.append(writers.last().span);
1491 is_normal_attribute.append(iter.name == "custom_normal");
1492 });
1493 /* Loop per source face, as there is an expensive weight calculation that needs to be done per
1494 * face. */
1495 const OffsetIndices<int> output_faces = output_mesh->faces();
1496 const OffsetIndices<int> input_faces = input_mesh->faces();
1497 const Span<int> input_corner_verts = input_mesh->corner_verts();
1498 const Span<float3> input_vert_positions = input_mesh->vert_positions();
1499 const Span<int> output_corner_verts = output_mesh->corner_verts();
1500 const Span<float3> output_vert_positions = output_mesh->vert_positions();
1501 const int grain_size = 256;
1502 threading::parallel_for(
1503 out_to_in_face_map.index_range(), grain_size, [&](const IndexRange range) {
1504 Vector<float, 20> weights;
1505 Vector<float2, 20> cos_2d;
1506 float3x3 axis_mat;
1507 for (const int out_face_index : range) {
1508 /* Are there any corners needing interpolation in this face?
1509 * The corners needing interpolation are those whose out_to_in_corner_map entry is -1.
1510 */
1511 IndexRange out_face = output_faces[out_face_index];
1512 if (!std::any_of(out_face.begin(), out_face.end(), [&](int c) {
1513 return out_to_in_corner_map[c] == -1;
1514 }))
1515 {
1516 /* We copied the attributes using the corner map before calling this function. */
1517 continue;
1518 }
1519 /* At least one output corner did not map to an input corner. */
1520
1521 /* First get coordinates of input face projected onto 2d, and make sure that
1522 * weights has the right size. */
1523 const int in_face_index = out_to_in_face_map[out_face_index];
1524 const IndexRange in_face = input_faces[in_face_index];
1525 const Span<int> in_face_verts = input_corner_verts.slice(in_face);
1526 const int in_face_size = in_face.size();
1527 const Span<int> out_face_verts = output_corner_verts.slice(out_face);
1528 weights.resize(in_face_size);
1529 cos_2d.resize(in_face_size);
1530 float(*cos_2d_p)[2] = reinterpret_cast<float(*)[2]>(cos_2d.data());
1531 const float3 axis_dominant = bke::mesh::face_normal_calc(input_vert_positions,
1532 in_face_verts);
1533 axis_dominant_v3_to_m3(axis_mat.ptr(), axis_dominant);
1534 /* We also need to know if the output face has a flipped normal compared
1535 * to the corresponding input face (used if we have custom normals).
1536 */
1537 const float3 out_face_normal = bke::mesh::face_normal_calc(output_vert_positions,
1538 out_face_verts);
1539 const bool face_is_flipped = math::dot(axis_dominant, out_face_normal) < 0.0;
1540 for (const int i : in_face_verts.index_range()) {
1541 const float3 &co = input_vert_positions[in_face_verts[i]];
1542 cos_2d[i] = (axis_mat * co).xy();
1543 }
1544 /* Now the loop to actually interpolate attributes of the new-vertex corners of the
1545 * output face. */
1546 for (const int out_c : out_face) {
1547 const int in_c = out_to_in_corner_map[out_c];
1548 if (in_c != -1) {
1549 continue;
1550 }
1551 const int out_v = output_corner_verts[out_c];
1552 float2 co;
1553 mul_v2_m3v3(co, axis_mat.ptr(), output_vert_positions[out_v]);
1554 interp_weights_poly_v2(weights.data(), cos_2d_p, in_face_size, co);
1555
1556 for (const int attr_index : dsts.index_range()) {
1557 const GSpan src = srcs[attr_index];
1558 GMutableSpan dst = dsts[attr_index];
1559 const bool need_flip = face_is_flipped && is_normal_attribute[attr_index];
1560 const CPPType &type = dst.type();
1561 bke::attribute_math::convert_to_static_type(type, [&](auto dummy) {
1562 using T = decltype(dummy);
1563 const Span<T> src_typed = src.typed<T>();
1564 MutableSpan<T> dst_typed = dst.typed<T>();
1565 bke::attribute_math::DefaultMixer<T> mixer{MutableSpan(&dst_typed[out_c], 1)};
1566 for (const int i : in_face.index_range()) {
1567 mixer.mix_in(0, src_typed[in_face[i]], weights[i]);
1568 }
1569 mixer.finalize();
1570 if (need_flip) {
1571 /* The joined mesh has converted custom normals to float3. */
1572 if (type.is<float3>()) {
1573 dst.typed<float3>()[out_c] = -dst.typed<float3>()[out_c];
1574 }
1575 }
1576 });
1577 }
1578 }
1579 }
1580 });
1581 for (bke::GSpanAttributeWriter &writer : writers) {
1582 writer.finish();
1583 }
1584}
1585
1590static inline int mesh_id_for_face(int face_id, const MeshOffsets &mesh_offsets)
1591{
1592 for (const int mesh_id : mesh_offsets.face_offsets.index_range()) {
1593 if (mesh_offsets.face_offsets[mesh_id].contains(face_id)) {
1594 return mesh_id;
1595 }
1596 }
1597 return -1;
1598}
1599
1605static void set_material_from_map(const Span<int> out_to_in_map,
1606 const Span<Array<short>> material_remaps,
1607 const Span<const Mesh *> meshes,
1608 const MeshOffsets &mesh_offsets,
1609 const MutableSpan<int> dst)
1610{
1611 BLI_assert(material_remaps.size() > 0);
1612 Vector<VArraySpan<int>> material_varrays;
1613 for (const int i : meshes.index_range()) {
1614 bke::AttributeAccessor input_attrs = meshes[i]->attributes();
1615 material_varrays.append(
1616 *input_attrs.lookup_or_default<int>("material_index", bke::AttrDomain::Face, 0));
1617 }
1618 threading::parallel_for(out_to_in_map.index_range(), 8192, [&](const IndexRange range) {
1619 for (const int out_f : range) {
1620 const int in_f = out_to_in_map[out_f];
1621 const int mesh_id = mesh_id_for_face(in_f, mesh_offsets);
1622 const int in_f_local = in_f - mesh_offsets.face_start[mesh_id];
1623 const int orig = material_varrays[mesh_id][in_f_local];
1624 const Array<short> &map = material_remaps[mesh_id];
1625 dst[out_f] = (orig >= 0 && orig < map.size()) ? map[orig] : orig;
1626 ;
1627 }
1628 });
1629}
1630
1635static void get_intersecting_edges(Vector<int> *r_intersecting_edges,
1636 const Mesh *mesh,
1637 OutToInMaps &out_to_in,
1638 const MeshOffsets &mesh_offsets)
1639{
1640/* In a manifold mesh, every edge is adjacent to exactly two faces.
1641 * Find them, and when we have a pair, check to see if those faces came
1642 * from separate input meshes, and add the edge to r_intersecting_Edges if so. */
1643# ifdef DEBUG_TIME
1644 timeit::ScopedTimer timer("get_intersecting_edges");
1645# endif
1646 const OffsetIndices<int> faces = mesh->faces();
1647 const Span<int> corner_edges = mesh->corner_edges();
1648 const Span<int> face_map = out_to_in.ensure_face_map();
1649 Array<int> edge_first_face(mesh->edges_num, -1);
1650 for (int face_i : faces.index_range()) {
1651 for (const int edge_i : corner_edges.slice(faces[face_i])) {
1652 int face2_i = edge_first_face[edge_i];
1653 if (face2_i == -1) {
1654 edge_first_face[edge_i] = face_i;
1655 }
1656 else {
1657 int in_face_i = face_map[face_i];
1658 int in_face2_i = face_map[face2_i];
1659 int m1 = mesh_id_for_face(in_face_i, mesh_offsets);
1660 int m2 = mesh_id_for_face(in_face2_i, mesh_offsets);
1661 BLI_assert(m1 != -1 && m2 != -1);
1662 if (m1 != m2) {
1663 r_intersecting_edges->append(edge_i);
1664 }
1665 }
1666 }
1667 }
1668}
1669
1675static bool is_plane(const Mesh *mesh,
1677 float3 *r_normal,
1678 float *r_origin_offset)
1679{
1680 if (mesh->faces_num != 1 && mesh->verts_num != 4) {
1681 return false;
1682 }
1683 float3 vpos[4];
1684 const Span<float3> positions = mesh->vert_positions();
1685 const Span<int> f_corners = mesh->corner_verts().slice(mesh->faces()[0]);
1686 for (int i = 0; i < 4; i++) {
1687 mul_v3_m4v3(vpos[i], transform.ptr(), positions[f_corners[i]]);
1688 }
1689 float3 norm1 = math::normal_tri(vpos[0], vpos[1], vpos[2]);
1690 float3 norm2 = math::normal_tri(vpos[0], vpos[2], vpos[3]);
1691 if (math::almost_equal_relative(norm1, norm2, 1e-5f)) {
1692 *r_normal = norm1;
1693 *r_origin_offset = math::dot(norm1, vpos[0]);
1694 return true;
1695 }
1696 return false;
1697}
1698
1705static MeshGL mesh_trim_manifold(Manifold &manifold0,
1706 float3 normal,
1707 float origin_offset,
1708 const MeshOffsets &mesh_offsets,
1709 BooleanError *r_error)
1710{
1711 Manifold man_result = manifold0.TrimByPlane(manifold::vec3(normal[0], normal[1], normal[2]),
1712 double(origin_offset));
1713 MeshGL meshgl = man_result.GetMeshGL();
1714 if (man_result.Status() != Manifold::Error::NoError) {
1715 if (man_result.Status() == Manifold::Error::ResultTooLarge) {
1716 *r_error = BooleanError::ResultTooBig;
1717 }
1718 else if (man_result.Status() == Manifold::Error::NotManifold) {
1719 *r_error = BooleanError::NonManifold;
1720 }
1721 else {
1722 *r_error = BooleanError::UnknownError;
1723 }
1724 return meshgl;
1725 }
1726 /* This meshgl_result has a non-standard (but non-zero) original ID for the
1727 * plane faces, and faceIDs that make no sense for them. Fix this.
1728 * But only do this if the result is not empty. */
1729 if (meshgl.vertProperties.size() > 0) {
1730 BLI_assert(meshgl.runOriginalID.size() == 2 && meshgl.runOriginalID[1] > 0);
1731 meshgl.runOriginalID[1] = 1;
1732 BLI_assert(meshgl.runIndex.size() == 3);
1733 int plane_face_start = meshgl.runIndex[1] / 3;
1734 int plane_face_end = meshgl.runIndex[2] / 3;
1735 for (int i = plane_face_start; i < plane_face_end; i++) {
1736 meshgl.faceID[i] = mesh_offsets.face_offsets[1][0];
1737 }
1738 }
1739 return meshgl;
1740}
1741
1748static Mesh *meshgl_to_mesh(MeshGL &mgl,
1749 const Mesh *joined_mesh,
1750 Span<const Mesh *> meshes,
1751 const Span<Array<short>> material_remaps,
1752 const MeshOffsets &mesh_offsets,
1753 Vector<int> *r_intersecting_edges)
1754{
1755 constexpr int dbg_level = 0;
1756 if (dbg_level > 0) {
1757 std::cout << "MESHGL_TO_MESH\n";
1758 }
1759# ifdef DEBUG_TIME
1760 timeit::ScopedTimer timer("meshgl to mesh from joined_mesh");
1761# endif
1762 BLI_assert(mgl.mergeFromVert.empty());
1763
1764 if (mgl.vertProperties.empty() || mgl.triVerts.empty()) {
1765 Mesh *mesh = BKE_mesh_new_nomain(0, 0, 0, 0);
1766 BKE_mesh_copy_parameters_for_eval(mesh, joined_mesh);
1767 return mesh;
1768 }
1769
1770 MeshAssembly ma = assemble_mesh_from_meshgl(mgl, mesh_offsets);
1771 const int verts_num = ma.output_verts_num;
1772 const int faces_num = ma.new_faces.size();
1773
1774 /* Make a new Mesh, now that we know the number of vertices and faces. Corners will be counted
1775 * using the mesh's face offsets, and we will use Blender's parallelized function to calculate
1776 * edges later. */
1777 Mesh *mesh = bke::mesh_new_no_attributes(verts_num, 0, faces_num, 0);
1778 BKE_mesh_copy_parameters_for_eval(mesh, joined_mesh);
1779
1780 /* First the face offsets store the size of each result face, then we accumulate them to form the
1781 * final offsets. */
1782 MutableSpan<int> face_offsets = mesh->face_offsets_for_write();
1783 threading::parallel_for(IndexRange(faces_num), 10'000, [&](const IndexRange range) {
1784 for (const int face : range) {
1785 face_offsets[face] = ma.new_faces[face].verts.size();
1786 }
1787 });
1788 const OffsetIndices<int> faces = offset_indices::accumulate_counts_to_offsets(face_offsets);
1789 mesh->corners_num = faces.total_size();
1790
1791 bke::MutableAttributeAccessor output_attrs = mesh->attributes_for_write();
1792
1793 /* Write corner vertex references. */
1794 {
1795# ifdef DEBUG_TIME
1796 timeit::ScopedTimer timer_c("calculate faces");
1797# endif
1798 output_attrs.add<int>(".corner_vert", bke::AttrDomain::Corner, bke::AttributeInitConstruct());
1799 MutableSpan<int> corner_verts = mesh->corner_verts_for_write();
1800 threading::parallel_for(IndexRange(faces_num), 10'000, [&](const IndexRange range) {
1801 for (const int face : range) {
1802 corner_verts.slice(faces[face]).copy_from(ma.new_faces[face].verts);
1803 }
1804 });
1805 }
1806
1807 {
1808# ifdef DEBUG_TIME
1809 timeit::ScopedTimer timer_e("calculating edges");
1810# endif
1811 bke::mesh_calc_edges(*mesh, false, false);
1812 }
1813
1814 /* Set the vertex positions, using implicit sharing to avoid copying any data. */
1815 {
1816# ifdef DEBUG_TIME
1817 timeit::ScopedTimer timer_c("set positions");
1818# endif
1819 BLI_assert(!output_attrs.contains("position"));
1820 BLI_assert(mgl.numProp == 3);
1821 auto *sharing_info = new ImplicitSharedValue<std::vector<float>>(
1822 std::move(mgl.vertProperties));
1823 const bke::AttributeInitShared init(sharing_info->data.data(), *sharing_info);
1824 output_attrs.add<float3>("position", bke::AttrDomain::Point, init);
1825 sharing_info->remove_user_and_delete_if_last();
1826 }
1827
1828 OutToInMaps out_to_in(&ma, joined_mesh, mesh);
1829
1830 {
1831# ifdef DEBUG_TIME
1832 timeit::ScopedTimer timer_a("copying and interpolating attributes");
1833# endif
1834
1835 /* Copy attributes from joined_mesh to elements they are mapped to
1836 * in the new mesh. For most attributes, if there is no input element
1837 * mapping to it, the attribute value is left at default.
1838 * But for corner attributes (most importantly, UV maps), missing
1839 * values are interpolated in their containing face.
1840 * We'll do corner interpolation in a separate pass so as to do
1841 * such attributes at once for a given face.
1842 */
1843 bke::AttributeAccessor join_attrs = joined_mesh->attributes();
1844
1845 bool need_corner_interpolation = false;
1846
1847 join_attrs.foreach_attribute([&](const bke::AttributeIter &iter) {
1848 if (ELEM(iter.name, "position", ".edge_verts", ".corner_vert", ".corner_edge")) {
1849 return;
1850 }
1851 Span<int> out_to_in_map;
1852 bool do_copy = true;
1853 bool do_material_remap = false;
1854 switch (iter.domain) {
1855 case bke::AttrDomain::Point: {
1856 out_to_in_map = out_to_in.ensure_vertex_map();
1857 break;
1858 }
1859 case bke::AttrDomain::Face: {
1860 out_to_in_map = out_to_in.ensure_face_map();
1861 /* If #material_remaps is non-empty, we need to use that map to set the
1862 * face "material_index" property instead of taking it from the joined mesh.
1863 * This should only happen if the user wants something other than the default
1864 * "transfer the materials" mode, which has already happened in the joined mesh.
1865 */
1866 do_material_remap = material_remaps.size() > 0 && iter.name == "material_index";
1867 break;
1868 }
1869 case bke::AttrDomain::Edge: {
1870 out_to_in_map = out_to_in.ensure_edge_map();
1871 break;
1872 }
1873 case bke::AttrDomain::Corner: {
1874 out_to_in_map = out_to_in.ensure_corner_map();
1875 need_corner_interpolation = true;
1876 break;
1877 }
1878 default: {
1880 do_copy = false;
1881 break;
1882 }
1883 }
1884 if (do_copy) {
1885 if (dbg_level > 0) {
1886 std::cout << "copy_attribute_using_map, name = " << iter.name << "\n";
1887 }
1888 bke::GSpanAttributeWriter dst = output_attrs.lookup_or_add_for_write_span(
1889 iter.name, iter.domain, iter.data_type);
1890 if (do_material_remap) {
1891 set_material_from_map(
1892 out_to_in_map, material_remaps, meshes, mesh_offsets, dst.span.typed<int>());
1893 }
1894 else {
1895 copy_attribute_using_map(GVArraySpan(*iter.get()), out_to_in_map, dst.span);
1896 }
1897 dst.finish();
1898 }
1899 });
1900 if (need_corner_interpolation) {
1901 interpolate_corner_attributes(output_attrs,
1902 join_attrs,
1903 mesh,
1904 joined_mesh,
1905 out_to_in.ensure_corner_map(),
1906 out_to_in.ensure_face_map());
1907 }
1908 if (r_intersecting_edges != nullptr) {
1909 get_intersecting_edges(r_intersecting_edges, mesh, out_to_in, mesh_offsets);
1910 }
1911 }
1912
1913 mesh->tag_loose_verts_none();
1914 mesh->tag_overlapping_none();
1915
1917
1918 return mesh;
1919}
1920
1921static bke::GeometrySet join_meshes_with_transforms(const Span<const Mesh *> meshes,
1922 const Span<float4x4> transforms)
1923{
1924# ifdef DEBUG_TIME
1925 timeit::ScopedTimer jtimer(__func__);
1926# endif
1927 bke::Instances instances;
1928 instances.resize(meshes.size());
1929 instances.transforms_for_write().copy_from(transforms);
1930 MutableSpan<int> handles = instances.reference_handles_for_write();
1931
1932 Map<const Mesh *, int> handle_by_mesh;
1933 for (const int i : meshes.index_range()) {
1934 handles[i] = handle_by_mesh.lookup_or_add_cb(meshes[i], [&]() {
1935 bke::GeometrySet geometry = bke::GeometrySet::from_mesh(
1936 const_cast<Mesh *>(meshes[i]), bke::GeometryOwnershipType::ReadOnly);
1937 return instances.add_new_reference(std::move(geometry));
1938 });
1939 }
1940 return geometry::realize_instances(
1941 bke::GeometrySet::from_instances(&instances, bke::GeometryOwnershipType::Editable),
1942 geometry::RealizeInstancesOptions());
1943}
1944
1946 const Span<float4x4> transforms,
1947 const Span<Array<short>> material_remaps,
1948 const BooleanOpParameters op_params,
1949 Vector<int> *r_intersecting_edges,
1950 BooleanError *r_error)
1951{
1952 constexpr int dbg_level = 0;
1953 if (dbg_level > 0) {
1954 std::cout << "\nMESH_BOOLEAN_MANIFOLD with " << meshes.size() << " args\n";
1955 }
1956 *r_error = BooleanError::NoError;
1957 try {
1958# ifdef DEBUG_TIME
1959 timeit::ScopedTimer timer("MANIFOLD BOOLEAN");
1960# endif
1961
1962 const int meshes_num = meshes.size();
1963
1964 bke::GeometrySet joined_meshes_set = join_meshes_with_transforms(meshes, transforms);
1965 const Mesh *joined_mesh = joined_meshes_set.get_mesh();
1966 if (joined_mesh == nullptr) {
1967 return nullptr;
1968 }
1969
1970 const MeshOffsets mesh_offsets(meshes);
1971 std::vector<Manifold> manifolds(meshes_num);
1972 get_manifolds(manifolds, meshes, transforms, mesh_offsets);
1973
1974 MeshGL meshgl_result;
1975 Operation op = op_params.boolean_mode;
1976 if (std::any_of(manifolds.begin(), manifolds.end(), [](const Manifold &m) {
1977 return m.Status() != Manifold::Error::NoError;
1978 }))
1979 {
1980 /* Check special case of subtracting a plane, which Manifold can handle. */
1981 float3 normal;
1982 float origin_offset;
1983 if (meshes_num == 2 && op == Operation::Difference &&
1984 manifolds[0].Status() == Manifold::Error::NoError &&
1985 is_plane(meshes[1], transforms[1], &normal, &origin_offset))
1986 {
1987# ifdef DEBUG_TIME
1988 timeit::ScopedTimer timer_trim("DOING BOOLEAN SLICE, GETTING MESH_GL RESULT");
1989# endif
1990 meshgl_result = mesh_trim_manifold(
1991 manifolds[0], normal, origin_offset, mesh_offsets, r_error);
1992 if (*r_error != BooleanError::NoError) {
1993 return nullptr;
1994 }
1995 }
1996 else {
1997 if (std::any_of(manifolds.begin(), manifolds.end(), [](const Manifold &m) {
1998 return m.Status() == Manifold::Error::NotManifold;
1999 }))
2000 {
2001 *r_error = BooleanError::NonManifold;
2002 }
2003 else {
2004 *r_error = BooleanError::UnknownError;
2005 }
2006 return nullptr;
2007 }
2008 }
2009 else {
2010 manifold::OpType mop = op == Operation::Intersect ?
2011 manifold::OpType::Intersect :
2012 (op == Operation::Union ? manifold::OpType::Add :
2013 manifold::OpType::Subtract);
2014# ifdef DEBUG_TIME
2015 timeit::ScopedTimer timer_bool("DOING BOOLEAN, GETTING MESH_GL RESULT");
2016# endif
2017 Manifold man_result = Manifold::BatchBoolean(manifolds, mop);
2018 meshgl_result = man_result.GetMeshGL();
2019 /* Have to wait until after converting to MeshGL to check status. */
2020 if (man_result.Status() != Manifold::Error::NoError) {
2021 if (man_result.Status() == Manifold::Error::ResultTooLarge) {
2022 *r_error = BooleanError::ResultTooBig;
2023 }
2024 else {
2025 *r_error = BooleanError::UnknownError;
2026 }
2027 if (dbg_level > 0) {
2028 std::cout << "manifold boolean returned with error status\n";
2029 }
2030 return nullptr;
2031 }
2032 }
2033 if (dbg_level > 0) {
2034 std::cout << "boolean result has " << meshgl_result.NumTri() << " tris\n";
2035 dump_meshgl(meshgl_result, "boolean result meshgl");
2036 }
2037 Mesh *mesh_result;
2038 {
2039# ifdef DEBUG_TIME
2040 timeit::ScopedTimer timer_out("MESHGL RESULT TO MESH");
2041# endif
2042 mesh_result = meshgl_to_mesh(
2043 meshgl_result, joined_mesh, meshes, material_remaps, mesh_offsets, r_intersecting_edges);
2044 }
2045 return mesh_result;
2046 }
2047 catch (const std::exception &e) {
2048 std::cout << "mesh_boolean_manifold: exception: " << e.what() << "\n";
2049 }
2050 catch (...) {
2051 std::cout << "mesh_boolean_manifold: unknown exception\n";
2052 }
2053 *r_error = BooleanError::UnknownError;
2054 return nullptr;
2055}
2056
2057} // namespace blender::geometry::boolean
2058#endif // WITH_MANIFOLD
#define ATTR_DOMAIN_NUM
support for deformation groups and hooks.
void BKE_id_free(Main *bmain, void *idv)
void BKE_mesh_copy_parameters_for_eval(Mesh *me_dst, const Mesh *me_src)
Mesh * BKE_mesh_new_nomain(int verts_num, int edges_num, int faces_num, int corners_num)
Mesh * BKE_mesh_copy_for_eval(const Mesh &source)
bool BKE_mesh_is_valid(Mesh *mesh)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
unsigned char uchar
#define ELEM(...)
float[3] Vector
@ CD_PROP_FLOAT
@ CD_PROP_FLOAT3
@ CD_PROP_INT32
@ CD_PROP_FLOAT2
struct Mesh Mesh
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
void init()
int64_t size() const
Definition BLI_array.hh:245
IndexRange index_range() const
Definition BLI_array.hh:349
const CPPType & type() const
AttributeSet attributes
int64_t size() const
static IndexMask from_bools_inverse(const VArray< bool > &bools, IndexMaskMemory &memory)
constexpr int64_t size() const
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
Value & lookup_or_add_cb(const Key &key, const CreateValueF &create_value)
Definition BLI_map.hh:620
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr T * end() const
Definition BLI_span.hh:548
constexpr T * begin() const
Definition BLI_span.hh:544
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T * end() const
Definition BLI_span.hh:224
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr const T * begin() const
Definition BLI_span.hh:220
int64_t size() const
void append(const T &value)
const T & last(const int64_t n=0) const
bool is_empty() const
IndexRange index_range() const
static float verts[][3]
uint pos
VecBase< int, 2 > int2
#define this
MatBase< 4, 4 > float4x4
VecBase< float, 3 > float3
static char faces[256]
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
void vert_tris_from_corner_tris(Span< int > corner_verts, Span< int3 > corner_tris, MutableSpan< int3 > vert_tris)
Mesh * mesh_boolean_manifold(Span< const Mesh * > meshes, Span< float4x4 > transforms, Span< Array< short > > material_remaps, BooleanOpParameters op_params, Vector< int > *r_intersecting_edges, BooleanError *r_error)
VecBase< T, 3 > normal_tri(const VecBase< T, 3 > &v1, const VecBase< T, 3 > &v2, const VecBase< T, 3 > &v3)
T length(const VecBase< T, Size > &a)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
bool almost_equal_relative(const VecBase< T, Size > &a, const VecBase< T, Size > &b, const T &epsilon_factor)
bool is_identity(const MatBase< T, NumCol, NumRow > &mat)
T abs(const T &a)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
VecBase< float, 3 > float3
static void init(bNodeTree *, bNode *node)
char name[66]
Definition DNA_ID.h:415
int corners_num
int edges_num
struct Material ** mat
short totcol
int faces_num
int verts_num
i
Definition text_draw.cc:230
wmTimer * timer
ParamHandle ** handles