Blender V5.0
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) {
181 VArraySpan<float> floatspan(*attrs.lookup<float>(iter.name));
182 dump_span(floatspan, label);
183 } break;
185 case bke::AttrType::Bool: {
186 const VArraySpan<int> intspan(*attrs.lookup<int>(iter.name));
187 dump_span(intspan, label);
188 } break;
190 const VArraySpan<float3> float3span(*attrs.lookup<float3>(iter.name));
191 dump_span(float3span, label);
192 } break;
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 co-planar
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. */
395 Vector<int, inline_outface_size> verts;
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 const MeshOffsets *mesh_offsets_;
456
457 public:
458 OutToInMaps(const MeshAssembly *mesh_assembly,
459 const Mesh *joined_mesh,
460 const Mesh *output_mesh,
461 const MeshOffsets *mesh_offsets)
462 : mesh_assembly_(mesh_assembly),
463 joined_mesh_(joined_mesh),
464 output_mesh_(output_mesh),
465 mesh_offsets_(mesh_offsets)
466 {
467 }
468
469 Span<int> ensure_vertex_map();
470 Span<int> ensure_face_map();
471 Span<int> ensure_edge_map();
472 Span<int> ensure_corner_map();
473};
474
475Span<int> OutToInMaps::ensure_face_map()
476{
477 if (!face_map_.is_empty()) {
478 return face_map_;
479 }
480 /* The MeshAssembly's new_faces should map one to one with output faces. */
481# ifdef DEBUG_TIME
482 timeit::ScopedTimer timer("filling face map");
483# endif
484 face_map_.reinitialize(output_mesh_->faces_num);
485 BLI_assert(mesh_assembly_->new_faces.size() == face_map_.size());
486 constexpr int grain_size = 50000;
487 threading::parallel_for(
488 mesh_assembly_->new_faces.index_range(), grain_size, [&](const IndexRange range) {
489 for (const int i : range) {
490 face_map_[i] = mesh_assembly_->new_faces[i].face_id;
491 }
492 });
493 return face_map_;
494}
495
496Span<int> OutToInMaps::ensure_vertex_map()
497{
498 if (!vertex_map_.is_empty()) {
499 return vertex_map_;
500 }
501 /* There may be better ways, but for now we discover the output to input
502 * vertex mapping by going through the output faces, and for each, looking
503 * through the vertices of the corresponding input face for matches.
504 */
505 const Span<int> face_map = this->ensure_face_map();
506# ifdef DEBUG_TIME
507 timeit::ScopedTimer timer("filling vertex map");
508# endif
509 vertex_map_ = Array<int>(output_mesh_->verts_num, -1);
510 /* To parallelize this, need to deal with the fact that this will
511 * have different threads wanting to write vertex_map, and also want
512 * determinism of which one wins if there is more than one possibility.
513 */
514 const OffsetIndices<int> in_faces = joined_mesh_->faces();
515 const OffsetIndices<int> out_faces = output_mesh_->faces();
516 const Span<int> in_corner_verts = joined_mesh_->corner_verts();
517 const Span<int> out_corner_verts = output_mesh_->corner_verts();
518 const Span<float3> out_vert_positions = output_mesh_->vert_positions();
519 const Span<float3> in_vert_positions = joined_mesh_->vert_positions();
520 for (const int out_face_index : IndexRange(output_mesh_->faces_num)) {
521 const int in_face_index = face_map[out_face_index];
522 const IndexRange in_face = in_faces[in_face_index];
523 const IndexRange out_face = out_faces[out_face_index];
524 const Span<int> in_face_verts = in_corner_verts.slice(in_face);
525 for (const int out_v : out_corner_verts.slice(out_face)) {
526 if (vertex_map_[out_v] != -1) {
527 continue;
528 }
529 float3 out_pos = out_vert_positions[out_v];
530 const auto *it = std::find_if(in_face_verts.begin(), in_face_verts.end(), [&](int in_v) {
531 return out_pos == in_vert_positions[in_v];
532 });
533 if (it != in_face_verts.end()) {
534 int in_v = in_face_verts[std::distance(in_face_verts.begin(), it)];
535 vertex_map_[out_v] = in_v;
536 }
537 }
538 }
539 return vertex_map_;
540}
541
542Span<int> OutToInMaps::ensure_corner_map()
543{
544 if (!corner_map_.is_empty()) {
545 return corner_map_;
546 }
547 /* There may be better ways, but for now we discover the output to input
548 * corner mapping by going through the output faces, and for each, looking
549 * through the corners of the corresponding input face for matches of the
550 * vertex involved.
551 */
552 const Span<int> face_map = this->ensure_face_map();
553 const Span<int> vert_map = this->ensure_vertex_map();
554# ifdef DEBUG_TIME
555 timeit::ScopedTimer timer("filling corner map");
556# endif
557 corner_map_ = Array<int>(output_mesh_->corners_num, -1);
558 const OffsetIndices<int> in_faces = joined_mesh_->faces();
559 const OffsetIndices<int> out_faces = output_mesh_->faces();
560 const Span<int> in_corner_verts = joined_mesh_->corner_verts();
561 const Span<int> out_corner_verts = output_mesh_->corner_verts();
562 constexpr int grain_size = 10000;
563 threading::parallel_for(
564 IndexRange(output_mesh_->faces_num), grain_size, [&](const IndexRange range) {
565 for (const int out_face_index : range) {
566 const int in_face_index = face_map[out_face_index];
567 const IndexRange in_face = in_faces[in_face_index];
568 for (const int out_c : out_faces[out_face_index]) {
569 BLI_assert(corner_map_[out_c] == -1);
570 const int out_v = out_corner_verts[out_c];
571 const int in_v = vert_map[out_v];
572 if (in_v == -1) {
573 continue;
574 }
575 const int in_face_i = in_corner_verts.slice(in_face).first_index_try(in_v);
576 if (in_face_i != -1) {
577 const int in_c = in_face[in_face_i];
578 corner_map_[out_c] = in_c;
579 }
580 }
581 }
582 });
583 return corner_map_;
584}
585
586static bool same_dir(const float3 &p1, const float3 &p2, const float3 &q1, const float3 &q2)
587{
588 float3 p = p1 - p2;
589 float3 q = q1 - q2;
590 float pq = math::length(p) * math::length(q);
591 if (pq == 0.0f) {
592 return true;
593 }
594 float abs_cos_pq = math::abs(math::dot(p, q) / pq);
595 return (math::abs(abs_cos_pq - 1.0f) <= 1e-5f);
596}
597
602static inline int mesh_id_for_face(const int face_id, const MeshOffsets &mesh_offsets)
603{
604 for (const int mesh_id : mesh_offsets.face_offsets.index_range()) {
605 if (mesh_offsets.face_offsets[mesh_id].contains(face_id)) {
606 return mesh_id;
607 }
608 }
609 return -1;
610}
611
616static IndexRange vertex_range_for_face(const int face_id, const MeshOffsets &mesh_offsets)
617{
618 const int mesh_id = mesh_id_for_face(face_id, mesh_offsets);
619 if (mesh_id == -1) {
620 return IndexRange();
621 }
622 return IndexRange::from_begin_end(mesh_offsets.vert_start[mesh_id],
623 mesh_offsets.vert_start[mesh_id + 1]);
624}
625
626Span<int> OutToInMaps::ensure_edge_map()
627{
628 constexpr int dbg_level = 0;
629 if (!edge_map_.is_empty()) {
630 return edge_map_;
631 }
632 if (dbg_level > 0) {
633 std::cout << "\nensure_edge_map\n";
634 if (dbg_level > 1) {
635 dump_mesh(joined_mesh_, "joined_mesh");
636 dump_mesh(output_mesh_, "output_mesh");
637 }
638 }
639 /* There may be better ways to get the edge map, but for now
640 * we go through the output faces, and for each edge, see if
641 * there is an input edge in the corresponding input face that
642 * has one or the other end in common, and if only one end is
643 * in common, is in approximately the same direction.
644 * We can assume that the output and input are manifold.
645 * So if there is an edge that starts or ends at a corner in
646 * the corresponding input face, then we need only look for the
647 * "starts at" case, because if it is "ends at" in this face, it
648 * should be "starts at" in the matching face.
649 */
650 const Span<int> face_map = this->ensure_face_map();
651 const Span<int> vert_map = this->ensure_vertex_map();
652 const Span<int> corner_map = this->ensure_corner_map();
653 /* To parallelize this, would need a way to figure out that
654 * this is the "canonical" edge representative so that only
655 * one thread tries to write this. Or could use atomic operations.
656 */
657# ifdef DEBUG_TIME
658 timeit::ScopedTimer timer("filling edge map");
659# endif
660 edge_map_ = Array<int>(output_mesh_->edges_num, -1);
661 const Span<int> out_corner_edges = output_mesh_->corner_edges();
662 const Span<int> out_corner_verts = output_mesh_->corner_verts();
663 const Span<int2> out_edges = output_mesh_->edges();
664 const Span<float3> out_positions = output_mesh_->vert_positions();
665 const Span<int> in_corner_edges = joined_mesh_->corner_edges();
666 const Span<int> in_corner_verts = joined_mesh_->corner_verts();
667 const Span<int2> in_edges = joined_mesh_->edges();
668 const Span<float3> in_positions = joined_mesh_->vert_positions();
669 const OffsetIndices<int> in_faces = joined_mesh_->faces();
670 const OffsetIndices<int> out_faces = output_mesh_->faces();
671 Array<bool> done_edge(output_mesh_->edges_num, false);
672 for (const int out_face_index : IndexRange(output_mesh_->faces_num)) {
673 const int in_face_index = face_map[out_face_index];
674 const IndexRange in_face = in_faces[in_face_index];
675 const IndexRange in_face_vert_range = vertex_range_for_face(in_face_index, *mesh_offsets_);
676 if (dbg_level > 0) {
677 std::cout << "process out_face = " << out_face_index << ", in_face = " << in_face_index
678 << "\n";
679 }
680 for (const int out_c : out_faces[out_face_index]) {
681 const int in_c = corner_map[out_c];
682 if (dbg_level > 0) {
683 std::cout << " out_c = " << out_c << ", in_c = " << in_c << "\n";
684 }
685 if (in_c == -1) {
686 /* No possible "starts at" match here. */
687 continue;
688 }
689 const int out_e = out_corner_edges[out_c];
690 if (dbg_level > 0) {
691 std::cout << " out_e = " << out_e << ", done = " << done_edge[out_e] << "\n";
692 }
693 if (done_edge[out_e]) {
694 continue;
695 }
696 const int out_v = out_corner_verts[out_c];
697 const int in_e = in_corner_edges[in_c];
698 const int in_v = in_corner_verts[in_c];
699 /* Because of corner mapping, the output vertex should map to the input one. */
700 BLI_assert(vert_map[out_v] == in_v);
701 int2 out_e_v = out_edges[out_e];
702 if (out_e_v[0] != out_v) {
703 out_e_v = {out_e_v[1], out_e_v[0]};
704 }
705 int2 in_e_v = in_edges[in_e];
706 if (in_e_v[0] != in_v) {
707 in_e_v = {in_e_v[1], in_e_v[0]};
708 }
709 if (dbg_level > 0) {
710 std::cout << " out_v = " << out_v << ", in_e = " << in_e << ", in_v = " << in_v << "\n";
711 std::cout << " out_e_v = " << out_e_v << ", in_e_v = " << in_e_v << "\n";
712 std::cout << " vertex_map(out_e_v) = " << int2(vert_map[out_e_v[0]], vert_map[out_e_v[1]])
713 << "\n";
714 }
715 /* Here out_e_v should hold the output vertices in out_e, with the first
716 * one being out_v, the vertex at corner out_c.
717 * Similarly for in_e_v, with the first one being in_v.
718 */
719 BLI_assert(vert_map[out_e_v[0]] == in_e_v[0]);
720 int edge_rep = -1;
721 if (vert_map[out_e_v[1]] == in_e_v[1]) {
722 /* Here both ends of the edges match. */
723 if (dbg_level > 0) {
724 std::cout << " case 1, edge_rep = in_e = " << in_e << "\n";
725 }
726 edge_rep = in_e;
727 }
728 else if (!in_face_vert_range.contains(vert_map[out_e_v[1]])) {
729 /* Here the "ends at" vertex of the output edge is a new vertex or in a different mesh.
730 * Does the edge at least go in the same direction as in_e?
731 */
732 if (same_dir(out_positions[out_e_v[0]],
733 out_positions[out_e_v[1]],
734 in_positions[in_e_v[0]],
735 in_positions[in_e_v[1]]))
736 {
737 if (dbg_level > 0) {
738 std::cout << " case 2, edge_rep = in_e = " << in_e << "\n";
739 }
740 edge_rep = in_e;
741 }
742 }
743 /* It is possible that the output face and corresponding
744 * input face have opposite winding. So do all of the previous
745 * again with the previous edge of input face but same edge of
746 * output face.
747 */
748 if (edge_rep == -1) {
749 const int in_c_prev = bke::mesh::face_corner_prev(in_face, in_c);
750 const int in_e_prev = in_corner_edges[in_c_prev];
751 const int in_v_prev = in_corner_verts[in_c_prev];
752 int2 in_e_v_prev = in_edges[in_e_prev];
753 if (in_e_v_prev[0] != in_v_prev) {
754 in_e_v_prev = {in_e_v_prev[1], in_e_v_prev[0]};
755 }
756 if (dbg_level > 0) {
757 std::cout << " in_c_prev = " << in_c_prev << ", in_e_prev = " << in_e_prev
758 << ", in_v_prev = " << in_v_prev << "\n";
759 std::cout << " in_e_v_prev = " << in_e_v_prev << "\n";
760 }
761 if (vert_map[out_e_v[0]] == in_e_v_prev[1]) {
762 if (vert_map[out_e_v[1]] == in_e_v_prev[0]) {
763 if (dbg_level > 0) {
764 std::cout << " case 3, edge_rep = in_e_prev = " << in_e_prev << "\n";
765 }
766 edge_rep = in_e_prev;
767 }
768 else if (vert_map[out_e_v[1]] == -1) {
769 if (same_dir(out_positions[out_e_v[0]],
770 out_positions[out_e_v[1]],
771 in_positions[in_e_v_prev[0]],
772 in_positions[in_e_v_prev[1]]))
773 {
774 if (dbg_level > 0) {
775 std::cout << " case 4, edge_rep = in_e_prev = " << in_e_prev << "\n";
776 }
777 edge_rep = in_e_prev;
778 }
779 }
780 }
781 }
782 if (edge_rep != -1) {
783 if (dbg_level > 0) {
784 std::cout << " found: set edge_map[" << out_e << "] = " << edge_rep << "\n";
785 }
786 edge_map_[out_e] = edge_rep;
787 done_edge[out_e] = true;
788 }
789 }
790 }
791 return edge_map_;
792}
793
795constexpr int face_group_inline = 4;
796
802static Array<Vector<int, face_group_inline>> get_face_groups(const MeshGL &mgl,
803 int input_faces_num)
804{
805# ifdef DEBUG_TIME
806 timeit::ScopedTimer timer("get_face_groups");
807# endif
808 constexpr int dbg_level = 0;
809 Array<Vector<int, face_group_inline>> fg(input_faces_num);
810 const int tris_num = mgl.NumTri();
811 BLI_assert(mgl.faceID.size() == tris_num);
812 for (const int t : IndexRange(tris_num)) {
813 const int faceid = mgl.faceID[t];
814 fg[faceid].append(t);
815 }
816 if (dbg_level > 0) {
817 std::cout << "face_groups\n";
818 for (const int i : fg.index_range()) {
819 std::cout << "orig face " << i;
820 dump_span(fg[i].as_span(), "");
821 }
822 }
823 return fg;
824}
825
826# if 0
827/* TODO: later */
834static uchar check_original_face(const Vector<int, face_group_inline> &group,
835 const MeshGL &mgl,
836 const Mesh *mesh,
837 int face_index)
838{
839 BLI_assert(0 <= face_index && face_index < mesh->faces_num);
840 const IndexRange orig_face = mesh->faces()[face_index];
841 /* The face can't be original if the number of triangles isn't equal
842 * to the original face size minus 2. */
843 int orig_face_size = orig_face.size();
844 if (orig_face_size != group.size() + 2) {
845 return 0;
846 }
847 Span<int> orig_face_verts = mesh->corner_verts().slice(mesh->faces()[face_index]);
848 /* edge_value[i] will be 1 if that edge is identical to an output edge,
849 * and -1 if it is the reverse of an output edge. */
850 Array<uchar, 20> edge_value(orig_face_size, 0);
851 int stride = mgl.numProp;
852 for (const int t : group) {
853 /* face_vert_index[i] will hold the position in input face face_index
854 * where the i'th vertex of triangle t is (assuming that no position
855 * is exactly repeated in an output face). -1 if there is no such. */
856 Array<int, 3> face_vert_index(3);
857 for (const int i : IndexRange(3)) {
858 int v = mgl.triVerts[3 * t + i];
859 int prop_offset = v * stride;
860 float3 pos(mgl.vertProperties[prop_offset],
861 mgl.vertProperties[prop_offset + 1],
862 mgl.vertProperties[prop_offset + 2]);
863 auto it = std::find_if(orig_face_verts.begin(), orig_face_verts.end(), [&](int orig_v) {
864 return pos == mesh->vert_positions()[orig_v];
865 });
866 face_vert_index[i] = it == orig_face_verts.end() ?
867 -1 :
868 std::distance(orig_face_verts.begin(), it);
869 }
870 /* Now we can tell which original edges are covered by t. */
871 for (const int i : IndexRange(3)) {
872 const int a = face_vert_index[i];
873 const int b = face_vert_index[(i + 1) % 3];
874 if (a != -1 && b != -1) {
875 if ((a + 1) % orig_face_size == b) {
876 edge_value[a] = 1;
877 }
878 else if ((b + 1) % orig_face_size == a) {
879 edge_value[b] = -1;
880 }
881 }
882 }
883 }
884 if (std::all_of(edge_value.begin(), edge_value.end(), [](int x) { return x == 1; })) {
885 return 1;
886 }
887 else if (std::all_of(edge_value.begin(), edge_value.end(), [](int x) { return x == -1; })) {
888 return 2;
889 }
890 return 0;
891}
892# endif
893
894static OutFace make_out_face(const MeshGL &mgl, int tri_index, int orig_face)
895{
896 OutFace ans;
898 const int k = 3 * tri_index;
899 ans.verts[0] = mgl.triVerts[k];
900 ans.verts[1] = mgl.triVerts[k + 1];
901 ans.verts[2] = mgl.triVerts[k + 2];
902 ans.face_id = orig_face;
903 return ans;
904}
905
913struct SharedEdge {
914 /* First shared edge ("group edge" indexing). */
915 int e1;
916 /* Second shared edge. */
917 int e2;
918 /* First vertex for e1 (second for e2). */
919 int v1;
920 /* Second vertex for e1 (first for e2). */
921 int v2;
922
923 SharedEdge(int e1, int e2, int v1, int v2) : e1(e1), e2(e2), v1(v1), v2(v2) {}
924
925 /* Return the indices (in the linearized triangle space of an OutFace group)
926 * corresponding to e1 and e2. */
927 int2 outface_group_face_indices() const
928 {
929 return int2(e1 / 3, e2 / 3);
930 }
931};
932
934static inline SharedEdge canon_shared_edge(int e1, int e2, int v1, int v2)
935{
936 if (v1 < v2) {
937 return SharedEdge(e1, e2, v1, v2);
938 }
939 return SharedEdge(e2, e1, v2, v1);
940}
941
947static SharedEdge get_shared_edge_from_pair(const OutFace &tri1, const OutFace &tri2)
948{
949 /* There should be at most one shared edge between the tri1 and tri2. Find it. */
950 SharedEdge shared_edge(-1, -1, -1, -1);
951 for (const int i1 : IndexRange(3)) {
952 for (const int i2 : IndexRange(3)) {
953 const int v1 = tri1.verts[i1];
954 const int v2 = tri2.verts[i2];
955 if (v1 == v2) {
956 const int v1_next = tri1.verts[(i1 + 1) % 3];
957 const int v2_prev = tri2.verts[(i2 + 2) % 3];
958 if (v1_next == v2_prev) {
959 shared_edge = SharedEdge(i1, 3 + ((i2 + 2) % 3), v1, v1_next);
960 break;
961 }
962 const int v1_prev = tri1.verts[(i1 + 2) % 3];
963 const int v2_next = tri2.verts[(i2 + 1) % 3];
964 if (v1_prev == v2_next) {
965 shared_edge = SharedEdge((i1 + 2) % 3, 3 + i2, v1_prev, v1);
966 break;
967 }
968 }
969 }
970 if (shared_edge.e1 != -1) {
971 break;
972 }
973 }
974 return shared_edge;
975}
976
982static Vector<SharedEdge> get_shared_edges(Span<OutFace> faces)
983{
985 /* Map from two vert indices making an edge to where that edge appears
986 * in list of group edges. */
987 Map<int2, int> edge_verts_to_tri;
988 for (const int face_index : faces.index_range()) {
989 const OutFace &f = faces[face_index];
990 for (const int i : IndexRange(3)) {
991 int v1 = f.verts[i];
992 int v2 = f.verts[(i + 1) % 3];
993 int this_e = face_index * 3 + i;
994 edge_verts_to_tri.add_new(int2(v1, v2), this_e);
995 int other_e = edge_verts_to_tri.lookup_default(int2(v2, v1), -1);
996 if (other_e != -1) {
997 ans.append(canon_shared_edge(this_e, other_e, v1, v2));
998 }
999 }
1000 }
1001 return ans;
1002}
1003
1009static bool is_legal_merge(const OutFace &f1, const OutFace &f2, int v1, int v2)
1010{
1011 /* For now, just look for each non-splice-involved vertex of each face to see if
1012 * it is in the other face.
1013 * TODO: if the faces are big, sort both together and look for repeats after sorting.
1014 */
1015 for (const int v : f1.verts) {
1016 if (!ELEM(v, v1, v2)) {
1017 if (f2.find_vert_index(v) != -1) {
1018 return false;
1019 }
1020 }
1021 }
1022 for (const int v : f2.verts) {
1023 if (!ELEM(v, v1, v2)) {
1024 if (f1.find_vert_index(v) != -1) {
1025 return false;
1026 }
1027 }
1028 }
1029 return true;
1030}
1031
1040static bool try_merge_out_face_pair(OutFace &f1, const OutFace &f2, const SharedEdge &se)
1041{
1042
1043 constexpr int dbg_level = 0;
1044 if (dbg_level > 0) {
1045 std::cout << "try_merge_out_face_pair\n";
1046 dump_span(f1.verts.as_span(), "f1");
1047 dump_span(f2.verts.as_span(), "f2");
1048 std::cout << "shared edge: "
1049 << "(e" << se.e1 << ",e" << se.e2 << ";v" << se.v1 << ",v" << se.v2 << ")\n";
1050 }
1051 const int f1_len = f1.verts.size();
1052 const int f2_len = f2.verts.size();
1053 const int v1 = se.v1;
1054 const int v2 = se.v2;
1055 /* Find i1, the index of the earlier of v1 and v2 in f1,
1056 * and i2, the index of the earlier of v1 and v2 in f2. */
1057 const int i1 = f1.find_vert_index(v1);
1058 BLI_assert(i1 != -1);
1059 const int i1_next = (i1 + 1) % f1_len;
1060 const int i2 = f2.find_vert_index(v2);
1061 BLI_assert(i2 != -1);
1062 const int i2_next = (i2 + 1) % f2_len;
1063 BLI_assert(f1.verts[i1] == v1 && f1.verts[i1_next] == v2);
1064 BLI_assert(f2.verts[i2] == v2 && f2.verts[i2_next] == v1);
1065 const bool can_merge = is_legal_merge(f1, f2, v1, v2);
1066 if (dbg_level > 0) {
1067 std::cout << "i1 = " << i1 << ", i2 = " << i2 << ", can_merge = " << can_merge << "\n";
1068 }
1069 if (!can_merge) {
1070 return false;
1071 }
1072 /* The merged face is the concatenation of these slices
1073 * (giving inclusive indices, with implied wrap-around at end of faces):
1074 * f1 : [0, i1]
1075 * f2 : [i2_next+1, i2_prev]
1076 * f1 : [i1_next, f1_len-1]
1077 */
1078 const int i2_prev = (i2 + f2_len - 1) % f2_len;
1079 const int i2_next_next = (i2_next + 1) % f2_len;
1080 const auto *f2_start_it = f2.verts.begin() + i2_next_next;
1081 const auto *f2_end_it = f2.verts.begin() + i2_prev + 1;
1082 if (f2_end_it > f2_start_it) {
1083 f1.verts.insert(i1_next, f2_start_it, f2_end_it);
1084 }
1085 else {
1086 const int n1 = std::distance(f2_start_it, f2.verts.end());
1087 if (n1 > 0) {
1088 f1.verts.insert(i1_next, f2_start_it, f2.verts.end());
1089 }
1090 if (n1 < f2_len - 2) {
1091 f1.verts.insert(i1_next + n1, f2.verts.begin(), f2_end_it);
1092 }
1093 }
1094 if (dbg_level > 0) {
1095 dump_span(f1.verts.as_span(), "merge result");
1096 }
1097 return true;
1098}
1099
1101static void merge_out_face_pair(Vector<OutFace> &faces)
1102{
1103 constexpr int dbg_level = 0;
1104 BLI_assert(faces.size() == 2);
1105 OutFace &tri1 = faces[0];
1106 OutFace &tri2 = faces[1];
1107 if (dbg_level > 0) {
1108 std::cout << "\nmerge_out_face_pair for faceid " << faces[0].face_id << "\n";
1109 dump_span(tri1.verts.as_span(), "tri1");
1110 dump_span(tri2.verts.as_span(), "tri2");
1111 }
1112 const SharedEdge shared_edge = get_shared_edge_from_pair(tri1, tri2);
1113 if (shared_edge.e1 == -1) {
1114 /* No shared edge, so no merging possible. */
1115 return;
1116 }
1117 const int va = shared_edge.v1;
1118 const int vb = shared_edge.v2;
1119 const int e1 = shared_edge.e1;
1120 const int e2 = shared_edge.e2;
1121 if (dbg_level > 0) {
1122 std::cout << "shared_edge = e" << e1 << ", e" << e2 << "; " << va << ", " << vb << "\n";
1123 }
1124 BLI_assert(e1 < 3 && e2 >= 3);
1125 /* Say tri1 has verts starting at pos e1 called a, b, c.
1126 * Then tri2 has verts starting at pos e2-3 called b, a, d.
1127 * So the quad we want is b, c, a, d.
1128 */
1129 const int vc = tri1.verts[(e1 + 2) % 3];
1130 const int vd = tri2.verts[(e2 - 3 + 2) % 3];
1131 BLI_assert(tri1.verts[e1] == va && tri1.verts[(e1 + 1) % 3] == vb && tri2.verts[e2 - 3] == vb &&
1132 tri2.verts[(e2 - 3 + 1) % 3] == va);
1133 if (vc == vd) {
1134 /* This can't happen geometrically, but maybe in extreme cases... */
1135 return;
1136 }
1137 tri1.verts.resize(4);
1138 tri1.verts[0] = vb;
1139 tri1.verts[1] = vc;
1140 tri1.verts[2] = va;
1141 tri1.verts[3] = vd;
1142 if (dbg_level > 0) {
1143 dump_span(tri1.verts.as_span(), "merged quad");
1144 }
1145 faces.resize(1);
1146}
1147
1153static void merge_out_faces(Vector<OutFace> &faces)
1154{
1155 constexpr int dbg_level = 0;
1156 if (faces.size() <= 1) {
1157 return;
1158 }
1159 if (faces.size() == 2) {
1160 merge_out_face_pair(faces);
1161 return;
1162 }
1163 if (dbg_level > 0) {
1164 std::cout << "\nmerge_out_faces for faceid " << faces[0].face_id << "\n";
1165 for (const int i : faces.index_range()) {
1166 const OutFace &f = faces[i];
1167 dump_span(f.verts.as_span(), std::to_string(i));
1168 }
1169 }
1170 Vector<SharedEdge> shared_edges = get_shared_edges(faces);
1171 if (dbg_level > 0) {
1172 std::cout << "shared edges:\n";
1173 for (const SharedEdge &se : shared_edges) {
1174 std::cout << "(e" << se.e1 << ",e" << se.e2 << ";v" << se.v1 << ",v" << se.v2 << ")";
1175 }
1176 std::cout << "\n";
1177 // dump_span(shared_edges.as_span(), "shared edges");
1178 }
1179 if (shared_edges.is_empty()) {
1180 return;
1181 }
1182 /* `shared_edge_valid[i]` is true if both edges in shared_edges[i] are still alive. */
1183 Array<bool> shared_edge_valid(shared_edges.size(), true);
1184 /* If `merged_to_faces[i]` is not -1, then argument faces[i] has been merged to that other face.
1185 */
1186 Array<int> merged_to(faces.size(), -1);
1187 /* Local function to follow merged_to mappings as far as possible. */
1188 auto final_merged_to = [&](int f_orig) {
1189 BLI_assert(f_orig != -1);
1190 int f_mapped = f_orig;
1191 do {
1192 if (merged_to[f_mapped] != -1) {
1193 f_mapped = merged_to[f_mapped];
1194 }
1195 } while (merged_to[f_mapped] != -1);
1196 return f_mapped;
1197 };
1198 /* TODO: sort shared_edges by decreasing length. */
1199 for (const int i : shared_edges.index_range()) {
1200 if (!shared_edge_valid[i]) {
1201 continue;
1202 }
1203 const SharedEdge se = shared_edges[i];
1204 const int2 orig_faces = se.outface_group_face_indices();
1205 const int2 cur_faces = int2(final_merged_to(orig_faces[0]), final_merged_to(orig_faces[1]));
1206 const int f1 = cur_faces[0];
1207 const int f2 = cur_faces[1];
1208 if (f1 == -1 || f2 == -2) {
1209 continue;
1210 }
1211 if (dbg_level > 0) {
1212 std::cout << "try merge of faces " << f1 << " and " << f2 << "\n";
1213 }
1214 if (try_merge_out_face_pair(faces[f1], faces[f2], se)) {
1215 if (dbg_level > 0) {
1216 std::cout << "successful merge\n";
1217 dump_span(faces[f1].verts.as_span(), "new f1");
1218 }
1219 merged_to[f2] = f1;
1220 }
1221 }
1222 /* Now compress the surviving faces. */
1223 int move_from = 0;
1224 int move_to = 0;
1225 const int orig_faces_num = faces.size();
1226 while (move_from < orig_faces_num) {
1227 /* Don't move faces that have been merged elsewhere. */
1228 while (move_from < orig_faces_num && merged_to[move_from] != -1) {
1229 move_from++;
1230 }
1231 if (move_from >= orig_faces_num) {
1232 break;
1233 }
1234 if (move_to < move_from) {
1235 faces[move_to] = faces[move_from];
1236 }
1237 move_to++;
1238 move_from++;
1239 }
1240 if (move_to < orig_faces_num) {
1241 faces.resize(move_to);
1242 }
1243 if (dbg_level > 0) {
1244 std::cout << "final faces:\n";
1245 for (const int i : faces.index_range()) {
1246 dump_span(faces[i].verts.as_span(), std::to_string(i));
1247 }
1248 }
1249}
1250
1252static inline bool approx_in_line(const float3 &p0, const float3 &p1, const float3 &p2)
1253{
1254 float cos_ang = math::dot(math::normalize(p1 - p0), math::normalize(p2 - p1));
1255 return math::abs(cos_ang - 1.0) < 1e-4;
1256}
1257
1269static void dissolve_valence2_verts(MeshAssembly &ma)
1270{
1271 const int vnum = ma.output_verts_num;
1272 Array<bool> dissolve(vnum, false);
1273 /* We'll remember up to two vertex neighbors for each vertex. */
1274 Array<std::pair<int, int>> neighbors(ma.output_verts_num, std::pair<int, int>(-1, -1));
1275 /* First, tentatively set dissolve based on neighbors. Alignment will be checked later. */
1276 for (const int f : ma.new_faces.index_range()) {
1277 const OutFace &face = ma.new_faces[f];
1278 const int fsize = face.verts.size();
1279 for (const int i : IndexRange(fsize)) {
1280 const int vprev = face.verts[(i - 1 + fsize) % fsize];
1281 const int v = face.verts[i];
1282 const int vnext = face.verts[(i + 1) % fsize];
1283 std::pair<int, int> &v_nbrs = neighbors[v];
1284 if (v_nbrs.first == -1) {
1285 /* First time we've seen v. Set the tentative neighbors. */
1286 v_nbrs.first = vprev;
1287 v_nbrs.second = vnext;
1288 /* Don't want to dissolve any vert of a triangle, even if triangle is degenerate. */
1289 dissolve[v] = fsize <= 3 ? false : true;
1290 }
1291 else {
1292 /* Some previous face had v. Disable dissolve unless if neighbors are the same, reversed,
1293 * or if this face is a triangle.
1294 */
1295 if (fsize == 3 || !(vprev == v_nbrs.second && vnext == v_nbrs.first)) {
1296 dissolve[v] = false;
1297 }
1298 }
1299 }
1300 }
1301 /* We can't dissolve so many verts in a face that it leaves less than a triangle.
1302 * This should be rare, since the above logic will prevent dissolving a vert from a triangle,
1303 * but it is possible that two or more verts are to be dissolved from a quad or ngon.
1304 * Do a pass to remove the possibility of dissolving anything from such faces. */
1305 for (const int f : ma.new_faces.index_range()) {
1306 const OutFace &face = ma.new_faces[f];
1307 const int fsize = face.verts.size();
1308 int num_dissolved = 0;
1309 for (const int i : IndexRange(fsize)) {
1310 if (dissolve[face.verts[i]]) {
1311 num_dissolved++;
1312 }
1313 }
1314 if (fsize - num_dissolved < 3) {
1315 for (const int i : IndexRange(fsize)) {
1316 dissolve[face.verts[i]] = false;
1317 }
1318 }
1319 }
1320 /* Now, for tentative dissolves, check "in a straight line" condition. */
1321 const int grain_size = 15000;
1322 bool any_dissolve = false;
1323 threading::parallel_for(IndexRange(vnum), grain_size, [&](const IndexRange range) {
1324 bool range_any_dissolve = false;
1325 for (const int v : range) {
1326 if (dissolve[v]) {
1327 std::pair<int, int> &v_nbrs = neighbors[v];
1328 BLI_assert(v_nbrs.first != -1 && v_nbrs.second != -1);
1329 const float3 p0 = ma.vert_position(v_nbrs.first);
1330 const float3 p1 = ma.vert_position(v);
1331 const float3 p2 = ma.vert_position(v_nbrs.second);
1332 if (!approx_in_line(p0, p1, p2)) {
1333 dissolve[v] = false;
1334 }
1335 else {
1336 range_any_dissolve = true;
1337 }
1338 }
1339 }
1340 if (range_any_dissolve) {
1341 /* No need for atomics here as this is a single byte. */
1342 any_dissolve = true;
1343 }
1344 });
1345 if (!any_dissolve) {
1346 return;
1347 }
1348
1349 /* We need to compress out the dissolved vertices out of `ma.vertpos`,
1350 * remap all the faces to account for that compression,
1351 * and rebuild any faces containing those compressed verts.
1352 * The compressing part is a bit like #mesh_copy_selection. */
1353 IndexMaskMemory memory;
1355 dissolve.index_range(), dissolve.as_span(), memory);
1356 const int new_vnum = keep.size();
1357 ma.old_to_new_vert_map.reinitialize(vnum);
1358 ma.old_to_new_vert_map.fill(-1);
1359 index_mask::build_reverse_map<int>(keep, ma.old_to_new_vert_map);
1360
1361 /* Compress `vertpos` in place. Is there a parallel way to do this? */
1362 float *vpos_data = ma.vertpos.data();
1363 BLI_assert(ma.vertpos_stride == 3);
1364 for (const int old_v : IndexRange(vnum)) {
1365 const int new_v = ma.old_to_new_vert_map[old_v];
1366 BLI_assert(new_v <= old_v);
1367 if (new_v >= 0) {
1368 std::copy_n(vpos_data + 3 * old_v, 3, vpos_data + 3 * new_v);
1369 }
1370 }
1371 ma.vertpos = ma.vertpos.take_front(new_vnum * ma.vertpos_stride);
1372 ma.output_verts_num = new_vnum;
1373
1374 /* Remap verts and compress dissolved verts in output faces. */
1375 threading::parallel_for(ma.new_faces.index_range(), 10000, [&](IndexRange range) {
1376 for (const int f : range) {
1377 OutFace &face = ma.new_faces[f];
1378 int i_to = 0;
1379 for (const int i_from : face.verts.index_range()) {
1380 const int mapped_v_from = ma.mapped_vert(face.verts[i_from]);
1381 if (mapped_v_from >= 0) {
1382 face.verts[i_to++] = mapped_v_from;
1383 }
1384 }
1385 if (i_to < face.verts.size()) {
1386 BLI_assert(i_to >= 3);
1387 face.verts.resize(i_to);
1388 }
1389 }
1390 });
1391}
1392
1402static MeshAssembly assemble_mesh_from_meshgl(MeshGL &mgl, const MeshOffsets &mesh_offsets)
1403{
1404# ifdef DEBUG_TIME
1405 timeit::ScopedTimer timer("calculating assemble_mesh_from_meshgl");
1406# endif
1407 constexpr int dbg_level = 0;
1408 if (dbg_level > 0) {
1409 std::cout << "assemble_mesh_from_meshgl\n";
1410 }
1411 MeshAssembly ma;
1412 ma.vertpos = MutableSpan<float>(&*mgl.vertProperties.begin(), mgl.vertProperties.size());
1413 ma.vertpos_stride = mgl.numProp;
1414 ma.input_verts_num = mesh_offsets.vert_start.last();
1415 ma.output_verts_num = ma.vertpos.size() / ma.vertpos_stride;
1416 const int input_faces_num = mesh_offsets.face_start.last();
1417
1418 /* For each offset input mesh face, what mgl triangles have it as id? */
1419 Array<Vector<int, face_group_inline>> face_groups = get_face_groups(mgl, input_faces_num);
1420 if (dbg_level > 1) {
1421 std::cout << "groups:\n";
1422 for (const int i : face_groups.index_range()) {
1423 std::cout << "orig (offset) face " << i << ": ";
1424 dump_span(face_groups[i].as_span(), "");
1425 }
1426 }
1427 {
1428# ifdef DEBUG_TIME
1429 timeit::ScopedTimer timer("face merging");
1430# endif
1431 Vector<Vector<OutFace>> new_groups(face_groups.size());
1432 const int grain_size = 15000;
1433 threading::parallel_for(face_groups.index_range(), grain_size, [&](const IndexRange range) {
1434 for (const int gid : range) {
1435 const Span<int> group = face_groups[gid].as_span();
1436 Vector<OutFace> &group_faces = new_groups[gid] = Vector<OutFace, 4>(group.size());
1437 for (const int i : group_faces.index_range()) {
1438 int tri_index = group[i];
1439 group_faces[i] = make_out_face(mgl, tri_index, gid);
1440 }
1441 merge_out_faces(group_faces);
1442 }
1443 });
1444# ifdef DEBUG_TIME
1445 timeit::ScopedTimer xtimer("copying groups at end");
1446# endif
1447 for (const int i : new_groups.index_range()) {
1448 ma.new_faces.extend(new_groups[i].as_span());
1449 }
1450 }
1451 {
1452# ifdef DEBUG_TIME
1453 timeit::ScopedTimer timer("valence-2-vertex dissolving");
1454# endif
1455 dissolve_valence2_verts(ma);
1456 if (ma.old_to_new_vert_map.size() > 0) {
1457 /* We compressed ma.vertpos in place, but that really means
1458 * we compressed mgl.VertProperties, so we need to change its size. */
1459 mgl.vertProperties.resize(ma.vertpos.size());
1460 }
1461 }
1462 if (dbg_level > 0) {
1463 std::cout << "mesh_assembly result:\n";
1464 std::cout << "input_verts_num = " << ma.input_verts_num
1465 << ", output_verts_num = " << ma.output_verts_num << "\n";
1466 dump_span_with_stride(ma.vertpos.as_span(), ma.vertpos_stride, "vertpos");
1467 std::cout << "new_faces:\n";
1468 for (const int i : ma.new_faces.index_range()) {
1469 std::cout << i << ": face_id = " << ma.new_faces[i].face_id << "\nverts ";
1470 dump_span(ma.new_faces[i].verts.as_span(), "");
1471 }
1472 }
1473 return ma;
1474}
1475
1476static void copy_attribute_using_map(const GSpan src,
1477 const Span<int> out_to_in_map,
1478 GMutableSpan dst)
1479{
1480 const CPPType &type = dst.type();
1481 const int grain_size = 20000;
1482 threading::parallel_for(out_to_in_map.index_range(), grain_size, [&](const IndexRange range) {
1483 for (const int out_elem : range) {
1484 const int in_elem = out_to_in_map[out_elem];
1485 if (in_elem != -1) {
1486 type.copy_assign(src[in_elem], dst[out_elem]);
1487 }
1488 }
1489 });
1490}
1491
1492static void interpolate_corner_attributes(bke::MutableAttributeAccessor &output_attrs,
1493 bke::AttributeAccessor &input_attrs,
1494 Mesh *output_mesh,
1495 const Mesh *input_mesh,
1496 const Span<int> out_to_in_corner_map,
1497 const Span<int> out_to_in_face_map)
1498{
1499# ifdef DEBUG_TIME
1500 timeit::ScopedTimer timer("interpolate corner attributes");
1501# endif
1502 /* Make parallel arrays of things needed access and write all corner attributes to interpolate.
1503 */
1508 /* For each index of `srcs` and `dsts`, we need to know if it is a "normal"-like attribute. */
1509 Vector<bool> is_normal_attribute;
1510 input_attrs.foreach_attribute([&](const bke::AttributeIter &iter) {
1511 if (iter.domain != bke::AttrDomain::Corner || ELEM(iter.name, ".corner_vert", ".corner_edge"))
1512 {
1513 return;
1514 }
1515 const bke::GAttributeReader reader = input_attrs.lookup_or_default(
1516 iter.name, iter.domain, iter.data_type);
1517 if (!reader) {
1518 return;
1519 }
1520 writers.append(
1521 output_attrs.lookup_or_add_for_write_span(iter.name, iter.domain, iter.data_type));
1522 readers.append(input_attrs.lookup_or_default(iter.name, iter.domain, iter.data_type));
1523 srcs.append(*readers.last());
1524 dsts.append(writers.last().span);
1525 is_normal_attribute.append(iter.name == "custom_normal");
1526 });
1527 /* Loop per source face, as there is an expensive weight calculation that needs to be done per
1528 * face. */
1529 const OffsetIndices<int> output_faces = output_mesh->faces();
1530 const OffsetIndices<int> input_faces = input_mesh->faces();
1531 const Span<int> input_corner_verts = input_mesh->corner_verts();
1532 const Span<float3> input_vert_positions = input_mesh->vert_positions();
1533 const Span<int> output_corner_verts = output_mesh->corner_verts();
1534 const Span<float3> output_vert_positions = output_mesh->vert_positions();
1535 const int grain_size = 256;
1536 threading::parallel_for(
1537 out_to_in_face_map.index_range(), grain_size, [&](const IndexRange range) {
1538 Vector<float, 20> weights;
1539 Vector<float2, 20> cos_2d;
1540 float3x3 axis_mat;
1541 for (const int out_face_index : range) {
1542 /* Are there any corners needing interpolation in this face?
1543 * The corners needing interpolation are those whose out_to_in_corner_map entry is -1.
1544 */
1545 IndexRange out_face = output_faces[out_face_index];
1546 if (!std::any_of(out_face.begin(), out_face.end(), [&](int c) {
1547 return out_to_in_corner_map[c] == -1;
1548 }))
1549 {
1550 /* We copied the attributes using the corner map before calling this function. */
1551 continue;
1552 }
1553 /* At least one output corner did not map to an input corner. */
1554
1555 /* First get coordinates of input face projected onto 2d, and make sure that
1556 * weights has the right size. */
1557 const int in_face_index = out_to_in_face_map[out_face_index];
1558 const IndexRange in_face = input_faces[in_face_index];
1559 const Span<int> in_face_verts = input_corner_verts.slice(in_face);
1560 const int in_face_size = in_face.size();
1561 const Span<int> out_face_verts = output_corner_verts.slice(out_face);
1562 weights.resize(in_face_size);
1563 cos_2d.resize(in_face_size);
1564 float (*cos_2d_p)[2] = reinterpret_cast<float (*)[2]>(cos_2d.data());
1565 const float3 axis_dominant = bke::mesh::face_normal_calc(input_vert_positions,
1566 in_face_verts);
1567 axis_dominant_v3_to_m3(axis_mat.ptr(), axis_dominant);
1568 /* We also need to know if the output face has a flipped normal compared
1569 * to the corresponding input face (used if we have custom normals).
1570 */
1571 const float3 out_face_normal = bke::mesh::face_normal_calc(output_vert_positions,
1572 out_face_verts);
1573 const bool face_is_flipped = math::dot(axis_dominant, out_face_normal) < 0.0;
1574 for (const int i : in_face_verts.index_range()) {
1575 const float3 &co = input_vert_positions[in_face_verts[i]];
1576 cos_2d[i] = (axis_mat * co).xy();
1577 }
1578 /* Now the loop to actually interpolate attributes of the new-vertex corners of the
1579 * output face. */
1580 for (const int out_c : out_face) {
1581 const int in_c = out_to_in_corner_map[out_c];
1582 if (in_c != -1) {
1583 continue;
1584 }
1585 const int out_v = output_corner_verts[out_c];
1586 float2 co;
1587 mul_v2_m3v3(co, axis_mat.ptr(), output_vert_positions[out_v]);
1588 interp_weights_poly_v2(weights.data(), cos_2d_p, in_face_size, co);
1589
1590 for (const int attr_index : dsts.index_range()) {
1591 const GSpan src = srcs[attr_index];
1592 GMutableSpan dst = dsts[attr_index];
1593 const bool need_flip = face_is_flipped && is_normal_attribute[attr_index];
1594 const CPPType &type = dst.type();
1595 bke::attribute_math::convert_to_static_type(type, [&](auto dummy) {
1596 using T = decltype(dummy);
1597 const Span<T> src_typed = src.typed<T>();
1598 MutableSpan<T> dst_typed = dst.typed<T>();
1599 bke::attribute_math::DefaultMixer<T> mixer{MutableSpan(&dst_typed[out_c], 1)};
1600 for (const int i : in_face.index_range()) {
1601 mixer.mix_in(0, src_typed[in_face[i]], weights[i]);
1602 }
1603 mixer.finalize();
1604 if (need_flip) {
1605 /* The joined mesh has converted custom normals to float3. */
1606 if (type.is<float3>()) {
1607 dst.typed<float3>()[out_c] = -dst.typed<float3>()[out_c];
1608 }
1609 }
1610 });
1611 }
1612 }
1613 }
1614 });
1615 for (bke::GSpanAttributeWriter &writer : writers) {
1616 writer.finish();
1617 }
1618}
1619
1625static void set_material_from_map(const Span<int> out_to_in_map,
1626 const Span<Array<short>> material_remaps,
1627 const Span<const Mesh *> meshes,
1628 const MeshOffsets &mesh_offsets,
1629 const MutableSpan<int> dst)
1630{
1631 BLI_assert(material_remaps.size() > 0);
1632 Vector<VArraySpan<int>> material_varrays;
1633 for (const int i : meshes.index_range()) {
1634 bke::AttributeAccessor input_attrs = meshes[i]->attributes();
1635 material_varrays.append(
1636 *input_attrs.lookup_or_default<int>("material_index", bke::AttrDomain::Face, 0));
1637 }
1638 threading::parallel_for(out_to_in_map.index_range(), 8192, [&](const IndexRange range) {
1639 for (const int out_f : range) {
1640 const int in_f = out_to_in_map[out_f];
1641 const int mesh_id = mesh_id_for_face(in_f, mesh_offsets);
1642 const int in_f_local = in_f - mesh_offsets.face_start[mesh_id];
1643 const int orig = material_varrays[mesh_id][in_f_local];
1644 const Array<short> &map = material_remaps[mesh_id];
1645 dst[out_f] = (orig >= 0 && orig < map.size()) ? map[orig] : orig;
1646 ;
1647 }
1648 });
1649}
1650
1655static void get_intersecting_edges(Vector<int> *r_intersecting_edges,
1656 const Mesh *mesh,
1657 OutToInMaps &out_to_in,
1658 const MeshOffsets &mesh_offsets)
1659{
1660/* In a manifold mesh, every edge is adjacent to exactly two faces.
1661 * Find them, and when we have a pair, check to see if those faces came
1662 * from separate input meshes, and add the edge to r_intersecting_Edges if so. */
1663# ifdef DEBUG_TIME
1664 timeit::ScopedTimer timer("get_intersecting_edges");
1665# endif
1666 const OffsetIndices<int> faces = mesh->faces();
1667 const Span<int> corner_edges = mesh->corner_edges();
1668 const Span<int> face_map = out_to_in.ensure_face_map();
1669 Array<int> edge_first_face(mesh->edges_num, -1);
1670 for (int face_i : faces.index_range()) {
1671 for (const int edge_i : corner_edges.slice(faces[face_i])) {
1672 int face2_i = edge_first_face[edge_i];
1673 if (face2_i == -1) {
1674 edge_first_face[edge_i] = face_i;
1675 }
1676 else {
1677 int in_face_i = face_map[face_i];
1678 int in_face2_i = face_map[face2_i];
1679 int m1 = mesh_id_for_face(in_face_i, mesh_offsets);
1680 int m2 = mesh_id_for_face(in_face2_i, mesh_offsets);
1681 BLI_assert(m1 != -1 && m2 != -1);
1682 if (m1 != m2) {
1683 r_intersecting_edges->append(edge_i);
1684 }
1685 }
1686 }
1687 }
1688}
1689
1695static bool is_plane(const Mesh *mesh,
1696 const float4x4 &transform,
1697 float3 *r_normal,
1698 float *r_origin_offset)
1699{
1700 if (mesh->faces_num != 1 && mesh->verts_num != 4) {
1701 return false;
1702 }
1703 float3 vpos[4];
1704 const Span<float3> positions = mesh->vert_positions();
1705 const Span<int> f_corners = mesh->corner_verts().slice(mesh->faces()[0]);
1706 for (int i = 0; i < 4; i++) {
1707 mul_v3_m4v3(vpos[i], transform.ptr(), positions[f_corners[i]]);
1708 }
1709 float3 norm1 = math::normal_tri(vpos[0], vpos[1], vpos[2]);
1710 float3 norm2 = math::normal_tri(vpos[0], vpos[2], vpos[3]);
1711 if (math::almost_equal_relative(norm1, norm2, 1e-5f)) {
1712 *r_normal = norm1;
1713 *r_origin_offset = math::dot(norm1, vpos[0]);
1714 return true;
1715 }
1716 return false;
1717}
1718
1725static MeshGL mesh_trim_manifold(Manifold &manifold0,
1726 float3 normal,
1727 float origin_offset,
1728 const MeshOffsets &mesh_offsets,
1729 BooleanError *r_error)
1730{
1731 Manifold man_result = manifold0.TrimByPlane(manifold::vec3(normal[0], normal[1], normal[2]),
1732 double(origin_offset));
1733 MeshGL meshgl = man_result.GetMeshGL();
1734 if (man_result.Status() != Manifold::Error::NoError) {
1735 if (man_result.Status() == Manifold::Error::ResultTooLarge) {
1736 *r_error = BooleanError::ResultTooBig;
1737 }
1738 else if (man_result.Status() == Manifold::Error::NotManifold) {
1739 *r_error = BooleanError::NonManifold;
1740 }
1741 else {
1742 *r_error = BooleanError::UnknownError;
1743 }
1744 return meshgl;
1745 }
1746 /* This meshgl_result has a non-standard (but non-zero) original ID for the
1747 * plane faces, and faceIDs that make no sense for them. Fix this.
1748 * But only do this if the result is not empty. */
1749 if (meshgl.vertProperties.size() > 0) {
1750 BLI_assert(meshgl.runOriginalID.size() == 2 && meshgl.runOriginalID[1] > 0);
1751 meshgl.runOriginalID[1] = 1;
1752 BLI_assert(meshgl.runIndex.size() == 3);
1753 int plane_face_start = meshgl.runIndex[1] / 3;
1754 int plane_face_end = meshgl.runIndex[2] / 3;
1755 for (int i = plane_face_start; i < plane_face_end; i++) {
1756 meshgl.faceID[i] = mesh_offsets.face_offsets[1][0];
1757 }
1758 }
1759 return meshgl;
1760}
1761
1768static Mesh *meshgl_to_mesh(MeshGL &mgl,
1769 const Mesh *joined_mesh,
1770 Span<const Mesh *> meshes,
1771 const Span<Array<short>> material_remaps,
1772 const MeshOffsets &mesh_offsets,
1773 Vector<int> *r_intersecting_edges)
1774{
1775 constexpr int dbg_level = 0;
1776 if (dbg_level > 0) {
1777 std::cout << "MESHGL_TO_MESH\n";
1778 }
1779# ifdef DEBUG_TIME
1780 timeit::ScopedTimer timer("meshgl to mesh from joined_mesh");
1781# endif
1782 BLI_assert(mgl.mergeFromVert.empty());
1783
1784 if (mgl.vertProperties.empty() || mgl.triVerts.empty()) {
1785 Mesh *mesh = BKE_mesh_new_nomain(0, 0, 0, 0);
1786 BKE_mesh_copy_parameters_for_eval(mesh, joined_mesh);
1787 return mesh;
1788 }
1789
1790 MeshAssembly ma = assemble_mesh_from_meshgl(mgl, mesh_offsets);
1791 const int verts_num = ma.output_verts_num;
1792 const int faces_num = ma.new_faces.size();
1793
1794 /* Make a new Mesh, now that we know the number of vertices and faces. Corners will be counted
1795 * using the mesh's face offsets, and we will use Blender's parallelized function to calculate
1796 * edges later. */
1797 Mesh *mesh = bke::mesh_new_no_attributes(verts_num, 0, faces_num, 0);
1798 BKE_mesh_copy_parameters_for_eval(mesh, joined_mesh);
1799
1800 /* First the face offsets store the size of each result face, then we accumulate them to form the
1801 * final offsets. */
1802 MutableSpan<int> face_offsets = mesh->face_offsets_for_write();
1803 threading::parallel_for(IndexRange(faces_num), 10'000, [&](const IndexRange range) {
1804 for (const int face : range) {
1805 face_offsets[face] = ma.new_faces[face].verts.size();
1806 }
1807 });
1808 const OffsetIndices<int> faces = offset_indices::accumulate_counts_to_offsets(face_offsets);
1809 mesh->corners_num = faces.total_size();
1810
1811 bke::MutableAttributeAccessor output_attrs = mesh->attributes_for_write();
1812
1813 /* Write corner vertex references. */
1814 {
1815# ifdef DEBUG_TIME
1816 timeit::ScopedTimer timer_c("calculate faces");
1817# endif
1818 output_attrs.add<int>(".corner_vert", bke::AttrDomain::Corner, bke::AttributeInitConstruct());
1819 MutableSpan<int> corner_verts = mesh->corner_verts_for_write();
1820 threading::parallel_for(IndexRange(faces_num), 10'000, [&](const IndexRange range) {
1821 for (const int face : range) {
1822 corner_verts.slice(faces[face]).copy_from(ma.new_faces[face].verts);
1823 }
1824 });
1825 }
1826
1827 /* Set the vertex positions, using implicit sharing to avoid copying any data. */
1828 {
1829# ifdef DEBUG_TIME
1830 timeit::ScopedTimer timer_c("set positions");
1831# endif
1832 BLI_assert(!output_attrs.contains("position"));
1833 BLI_assert(mgl.numProp == 3);
1834 auto *sharing_info = new ImplicitSharedValue<std::vector<float>>(
1835 std::move(mgl.vertProperties));
1836 const bke::AttributeInitShared init(sharing_info->data.data(), *sharing_info);
1837 output_attrs.add<float3>("position", bke::AttrDomain::Point, init);
1838 sharing_info->remove_user_and_delete_if_last();
1839 }
1840
1841 {
1842# ifdef DEBUG_TIME
1843 timeit::ScopedTimer timer_e("calculating edges");
1844# endif
1845 bke::mesh_calc_edges(*mesh, false, false);
1846 }
1847
1849
1850 OutToInMaps out_to_in(&ma, joined_mesh, mesh, &mesh_offsets);
1851
1852 {
1853# ifdef DEBUG_TIME
1854 timeit::ScopedTimer timer_a("copying and interpolating attributes");
1855# endif
1856
1857 /* Copy attributes from joined_mesh to elements they are mapped to
1858 * in the new mesh. For most attributes, if there is no input element
1859 * mapping to it, the attribute value is left at default.
1860 * But for corner attributes (most importantly, UV maps), missing
1861 * values are interpolated in their containing face.
1862 * We'll do corner interpolation in a separate pass so as to do
1863 * such attributes at once for a given face.
1864 */
1865 bke::AttributeAccessor join_attrs = joined_mesh->attributes();
1866
1867 bool need_corner_interpolation = false;
1868
1869 join_attrs.foreach_attribute([&](const bke::AttributeIter &iter) {
1870 if (ELEM(iter.name, "position", ".edge_verts", ".corner_vert", ".corner_edge")) {
1871 return;
1872 }
1873 Span<int> out_to_in_map;
1874 bool do_copy = true;
1875 bool do_material_remap = false;
1876 switch (iter.domain) {
1877 case bke::AttrDomain::Point: {
1878 out_to_in_map = out_to_in.ensure_vertex_map();
1879 break;
1880 }
1881 case bke::AttrDomain::Face: {
1882 out_to_in_map = out_to_in.ensure_face_map();
1883 /* If #material_remaps is non-empty, we need to use that map to set the
1884 * face "material_index" property instead of taking it from the joined mesh.
1885 * This should only happen if the user wants something other than the default
1886 * "transfer the materials" mode, which has already happened in the joined mesh.
1887 */
1888 do_material_remap = material_remaps.size() > 0 && iter.name == "material_index";
1889 break;
1890 }
1891 case bke::AttrDomain::Edge: {
1892 out_to_in_map = out_to_in.ensure_edge_map();
1893 break;
1894 }
1895 case bke::AttrDomain::Corner: {
1896 out_to_in_map = out_to_in.ensure_corner_map();
1897 need_corner_interpolation = true;
1898 break;
1899 }
1900 default: {
1902 do_copy = false;
1903 break;
1904 }
1905 }
1906 if (do_copy) {
1907 if (dbg_level > 0) {
1908 std::cout << "copy_attribute_using_map, name = " << iter.name << "\n";
1909 }
1910 bke::GSpanAttributeWriter dst = output_attrs.lookup_or_add_for_write_span(
1911 iter.name, iter.domain, iter.data_type);
1912 if (do_material_remap) {
1913 set_material_from_map(
1914 out_to_in_map, material_remaps, meshes, mesh_offsets, dst.span.typed<int>());
1915 }
1916 else {
1917 copy_attribute_using_map(GVArraySpan(*iter.get()), out_to_in_map, dst.span);
1918 }
1919 dst.finish();
1920 }
1921 });
1922 if (need_corner_interpolation) {
1923 interpolate_corner_attributes(output_attrs,
1924 join_attrs,
1925 mesh,
1926 joined_mesh,
1927 out_to_in.ensure_corner_map(),
1928 out_to_in.ensure_face_map());
1929 }
1930 if (r_intersecting_edges != nullptr) {
1931 get_intersecting_edges(r_intersecting_edges, mesh, out_to_in, mesh_offsets);
1932 }
1933 }
1934
1935 mesh->tag_loose_verts_none();
1936 mesh->tag_overlapping_none();
1937
1939
1940 return mesh;
1941}
1942
1943static bke::GeometrySet join_meshes_with_transforms(const Span<const Mesh *> meshes,
1944 const Span<float4x4> transforms)
1945{
1946# ifdef DEBUG_TIME
1947 timeit::ScopedTimer jtimer(__func__);
1948# endif
1949 bke::Instances instances;
1950 instances.resize(meshes.size());
1951 instances.transforms_for_write().copy_from(transforms);
1952 MutableSpan<int> handles = instances.reference_handles_for_write();
1953
1954 Map<const Mesh *, int> handle_by_mesh;
1955 for (const int i : meshes.index_range()) {
1956 handles[i] = handle_by_mesh.lookup_or_add_cb(meshes[i], [&]() {
1957 bke::GeometrySet geometry = bke::GeometrySet::from_mesh(
1958 const_cast<Mesh *>(meshes[i]), bke::GeometryOwnershipType::ReadOnly);
1959 return instances.add_new_reference(std::move(geometry));
1960 });
1961 }
1962 return geometry::realize_instances(
1963 bke::GeometrySet::from_instances(&instances, bke::GeometryOwnershipType::Editable),
1964 geometry::RealizeInstancesOptions())
1965 .geometry;
1966}
1967
1969 const Span<float4x4> transforms,
1970 const Span<Array<short>> material_remaps,
1971 const BooleanOpParameters op_params,
1972 Vector<int> *r_intersecting_edges,
1973 BooleanError *r_error)
1974{
1975 constexpr int dbg_level = 0;
1976 if (dbg_level > 0) {
1977 std::cout << "\nMESH_BOOLEAN_MANIFOLD with " << meshes.size() << " args\n";
1978 }
1979 *r_error = BooleanError::NoError;
1980 try {
1981# ifdef DEBUG_TIME
1982 timeit::ScopedTimer timer("MANIFOLD BOOLEAN");
1983# endif
1984
1985 const int meshes_num = meshes.size();
1986
1987 bke::GeometrySet joined_meshes_set = join_meshes_with_transforms(meshes, transforms);
1988 const Mesh *joined_mesh = joined_meshes_set.get_mesh();
1989 if (joined_mesh == nullptr) {
1990 return nullptr;
1991 }
1992
1993 const MeshOffsets mesh_offsets(meshes);
1994 std::vector<Manifold> manifolds(meshes_num);
1995 get_manifolds(manifolds, meshes, transforms, mesh_offsets);
1996
1997 MeshGL meshgl_result;
1998 Operation op = op_params.boolean_mode;
1999 if (std::any_of(manifolds.begin(), manifolds.end(), [](const Manifold &m) {
2000 return m.Status() != Manifold::Error::NoError;
2001 }))
2002 {
2003 /* Check special case of subtracting a plane, which Manifold can handle. */
2004 float3 normal;
2005 float origin_offset;
2006 if (meshes_num == 2 && op == Operation::Difference &&
2007 manifolds[0].Status() == Manifold::Error::NoError &&
2008 is_plane(meshes[1], transforms[1], &normal, &origin_offset))
2009 {
2010# ifdef DEBUG_TIME
2011 timeit::ScopedTimer timer_trim("DOING BOOLEAN SLICE, GETTING MESH_GL RESULT");
2012# endif
2013 meshgl_result = mesh_trim_manifold(
2014 manifolds[0], normal, origin_offset, mesh_offsets, r_error);
2015 if (*r_error != BooleanError::NoError) {
2016 return nullptr;
2017 }
2018 }
2019 else {
2020 if (std::any_of(manifolds.begin(), manifolds.end(), [](const Manifold &m) {
2021 return m.Status() == Manifold::Error::NotManifold;
2022 }))
2023 {
2024 *r_error = BooleanError::NonManifold;
2025 }
2026 else {
2027 *r_error = BooleanError::UnknownError;
2028 }
2029 return nullptr;
2030 }
2031 }
2032 else {
2033 manifold::OpType mop = op == Operation::Intersect ?
2034 manifold::OpType::Intersect :
2035 (op == Operation::Union ? manifold::OpType::Add :
2036 manifold::OpType::Subtract);
2037# ifdef DEBUG_TIME
2038 timeit::ScopedTimer timer_bool("DOING BOOLEAN, GETTING MESH_GL RESULT");
2039# endif
2040 Manifold man_result = Manifold::BatchBoolean(manifolds, mop);
2041 meshgl_result = man_result.GetMeshGL();
2042 /* Have to wait until after converting to MeshGL to check status. */
2043 if (man_result.Status() != Manifold::Error::NoError) {
2044 if (man_result.Status() == Manifold::Error::ResultTooLarge) {
2045 *r_error = BooleanError::ResultTooBig;
2046 }
2047 else {
2048 *r_error = BooleanError::UnknownError;
2049 }
2050 if (dbg_level > 0) {
2051 std::cout << "manifold boolean returned with error status\n";
2052 }
2053 return nullptr;
2054 }
2055 }
2056 if (dbg_level > 0) {
2057 std::cout << "boolean result has " << meshgl_result.NumTri() << " tris\n";
2058 dump_meshgl(meshgl_result, "boolean result meshgl");
2059 }
2060 Mesh *mesh_result;
2061 {
2062# ifdef DEBUG_TIME
2063 timeit::ScopedTimer timer_out("MESHGL RESULT TO MESH");
2064# endif
2065 mesh_result = meshgl_to_mesh(
2066 meshgl_result, joined_mesh, meshes, material_remaps, mesh_offsets, r_intersecting_edges);
2067 }
2068 return mesh_result;
2069 }
2070 catch (const std::exception &e) {
2071 std::cout << "mesh_boolean_manifold: exception: " << e.what() << "\n";
2072 }
2073 catch (...) {
2074 std::cout << "mesh_boolean_manifold: unknown exception\n";
2075 }
2076 *r_error = BooleanError::UnknownError;
2077 return nullptr;
2078}
2079
2080} // namespace blender::geometry::boolean
2081#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(...)
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:256
IndexRange index_range() const
Definition BLI_array.hh:360
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
static constexpr IndexRange from_begin_end(const int64_t begin, const int64_t end)
constexpr bool contains(int64_t value) 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
MatBase< 4, 4 > float4x4
VecBase< int, 2 > int2
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)
const char * name
char name[258]
Definition DNA_ID.h:432
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