Blender V4.3
node_geo_dual_mesh.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_array_utils.hh"
6#include "BLI_task.hh"
7
9#include "BKE_mesh.hh"
10#include "BKE_mesh_mapping.hh"
11
12#include "GEO_randomize.hh"
13
14#include "node_geometry_util.hh"
15
17
19{
20 b.add_input<decl::Geometry>("Mesh").supported_type(GeometryComponent::Type::Mesh);
21 b.add_input<decl::Bool>("Keep Boundaries")
22 .default_value(false)
24 "Keep non-manifold boundaries of the input mesh in place by avoiding the dual "
25 "transformation there");
26 b.add_output<decl::Geometry>("Dual Mesh").propagate_all();
27}
28
29enum class EdgeType : int8_t {
30 Loose = 0, /* No faces connected to it. */
31 Boundary = 1, /* An edge connected to exactly one face. */
32 Normal = 2, /* A normal edge (connected to two faces). */
33 NonManifold = 3, /* An edge connected to more than two faces. */
34};
35
37{
38 switch (old_type) {
39 case EdgeType::Loose:
40 return EdgeType::Boundary;
42 return EdgeType::Normal;
46 }
48 return EdgeType::Loose;
49}
50
51enum class VertexType : int8_t {
52 Loose = 0, /* Either no edges connected or only loose edges connected. */
53 Normal = 1, /* A normal vertex. */
54 Boundary = 2, /* A vertex on a boundary edge. */
55 NonManifold = 3, /* A vertex on a non-manifold edge. */
56};
57
72
73/* Copy only where vertex_types is 'normal'. If keep boundaries is selected, also copy from
74 * boundary vertices. */
75template<typename T>
77 MutableSpan<T> r_data,
78 const Span<VertexType> vertex_types,
79 const bool keep_boundaries)
80{
81 if (keep_boundaries) {
82 int out_i = 0;
83 for (const int i : data.index_range()) {
84 if (ELEM(vertex_types[i], VertexType::Normal, VertexType::Boundary)) {
85 r_data[out_i] = data[i];
86 out_i++;
87 }
88 }
89 }
90 else {
91 int out_i = 0;
92 for (const int i : data.index_range()) {
93 if (vertex_types[i] == VertexType::Normal) {
94 r_data[out_i] = data[i];
95 out_i++;
96 }
97 }
98 }
99}
100
101template<typename T>
103 MutableSpan<T> r_data,
104 const Span<std::pair<int, int>> new_to_old_map)
105{
106 for (const std::pair<int, int> &pair : new_to_old_map) {
107 r_data[pair.first] = data[pair.second];
108 }
109}
110
131 const Span<VertexType> vertex_types,
132 const bool keep_boundaries,
133 const Span<int> new_to_old_edges_map,
134 const Span<int> new_to_old_face_corners_map,
135 const Span<std::pair<int, int>> boundary_vertex_to_relevant_face_map,
136 const AttributeFilter &attribute_filter,
137 const AttributeAccessor src_attributes,
138 MutableAttributeAccessor dst_attributes)
139{
140 /* Retrieve all attributes except for position which is handled manually.
141 * Remove anonymous attributes that don't need to be propagated. */
142 Set<StringRefNull> attribute_ids = src_attributes.all_ids();
143 attribute_ids.remove("position");
144 attribute_ids.remove(".edge_verts");
145 attribute_ids.remove(".corner_vert");
146 attribute_ids.remove(".corner_edge");
147 attribute_ids.remove("sharp_face");
148 attribute_ids.remove_if([&](const StringRef id) { return attribute_filter.allow_skip(id); });
149
150 for (const StringRef id : attribute_ids) {
151 GAttributeReader src = src_attributes.lookup(id);
152
153 AttrDomain out_domain;
154 if (src.domain == AttrDomain::Face) {
155 out_domain = AttrDomain::Point;
156 }
157 else if (src.domain == AttrDomain::Point) {
158 out_domain = AttrDomain::Face;
159 }
160 else {
161 /* Edges and Face Corners. */
162 out_domain = src.domain;
163 }
166 id, out_domain, data_type);
167 if (!dst) {
168 continue;
169 }
170
171 switch (src.domain) {
172 case AttrDomain::Point: {
173 const GVArraySpan src_span(*src);
174 bke::attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
175 using T = decltype(dummy);
177 src_span.typed<T>(), dst.span.typed<T>(), vertex_types, keep_boundaries);
178 });
179 break;
180 }
181 case AttrDomain::Edge:
182 bke::attribute_math::gather(*src, new_to_old_edges_map, dst.span);
183 break;
184 case AttrDomain::Face: {
185 const GVArraySpan src_span(*src);
186 dst.span.take_front(src_span.size()).copy_from(src_span);
187 bke::attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
188 using T = decltype(dummy);
189 if (keep_boundaries) {
191 src_span.typed<T>(), dst.span.typed<T>(), boundary_vertex_to_relevant_face_map);
192 }
193 });
194 break;
195 }
196 case AttrDomain::Corner:
197 bke::attribute_math::gather(*src, new_to_old_face_corners_map, dst.span);
198 break;
199 default:
201 }
202 dst.finish();
203 }
204}
205
212static void calc_boundaries(const Mesh &mesh,
213 MutableSpan<VertexType> r_vertex_types,
214 MutableSpan<EdgeType> r_edge_types)
215{
216 BLI_assert(r_vertex_types.size() == mesh.verts_num);
217 BLI_assert(r_edge_types.size() == mesh.edges_num);
218 const Span<int2> edges = mesh.edges();
219 const OffsetIndices faces = mesh.faces();
220 const Span<int> corner_edges = mesh.corner_edges();
221
222 r_vertex_types.fill(VertexType::Loose);
223 r_edge_types.fill(EdgeType::Loose);
224
225 /* Add up the number of faces connected to each edge. */
226 for (const int i : IndexRange(mesh.faces_num)) {
227 for (const int edge_i : corner_edges.slice(faces[i])) {
228 r_edge_types[edge_i] = get_edge_type_with_added_neighbor(r_edge_types[edge_i]);
229 }
230 }
231
232 /* Update vertices. */
233 for (const int i : IndexRange(mesh.edges_num)) {
234 const EdgeType edge_type = r_edge_types[i];
235 if (edge_type == EdgeType::Loose) {
236 continue;
237 }
238 const int2 &edge = edges[i];
239 if (edge_type == EdgeType::Boundary) {
240 r_vertex_types[edge[0]] = get_vertex_type_with_added_neighbor(r_vertex_types[edge[0]]);
241 r_vertex_types[edge[1]] = get_vertex_type_with_added_neighbor(r_vertex_types[edge[1]]);
242 }
243 else if (edge_type >= EdgeType::NonManifold) {
244 r_vertex_types[edge[0]] = VertexType::NonManifold;
245 r_vertex_types[edge[1]] = VertexType::NonManifold;
246 }
247 }
248
249 /* Normal verts are on a normal edge, and not on boundary edges or non-manifold edges. */
250 for (const int i : IndexRange(mesh.edges_num)) {
251 const EdgeType edge_type = r_edge_types[i];
252 if (edge_type == EdgeType::Normal) {
253 const int2 &edge = edges[i];
254 if (r_vertex_types[edge[0]] == VertexType::Loose) {
255 r_vertex_types[edge[0]] = VertexType::Normal;
256 }
257 if (r_vertex_types[edge[1]] == VertexType::Loose) {
258 r_vertex_types[edge[1]] = VertexType::Normal;
259 }
260 }
261 }
262}
263
319static bool sort_vertex_faces(const Span<int2> edges,
320 const OffsetIndices<int> faces,
321 const Span<int> corner_verts,
322 const Span<int> corner_edges,
323 const int vertex_index,
324 const bool boundary_vertex,
325 const Span<EdgeType> edge_types,
326 MutableSpan<int> connected_faces,
327 MutableSpan<int> r_shared_edges,
328 MutableSpan<int> r_sorted_corners)
329{
330 if (connected_faces.size() <= 2 && (!boundary_vertex || connected_faces.is_empty())) {
331 return true;
332 }
333
334 /* For each face store the two corners whose edge contains the vertex. */
335 Array<std::pair<int, int>> face_vertex_corners(connected_faces.size());
336 for (const int i : connected_faces.index_range()) {
337 bool first_edge_done = false;
338 for (const int corner : faces[connected_faces[i]]) {
339 const int edge = corner_edges[corner];
340 if (edges[edge][0] == vertex_index || edges[edge][1] == vertex_index) {
341 if (!first_edge_done) {
342 face_vertex_corners[i].first = corner;
343 first_edge_done = true;
344 }
345 else {
346 face_vertex_corners[i].second = corner;
347 break;
348 }
349 }
350 }
351 }
352
353 int shared_edge_i = -1;
354 /* Determine first face and orientation. For now the orientation of the whole loop depends
355 * on the one face we chose as first. It's probably not worth it to check every face in
356 * the loop to determine the 'average' orientation. */
357 if (boundary_vertex) {
358 /* Our first face needs to be one which has a boundary edge. */
359 for (const int i : connected_faces.index_range()) {
360 const int corner_1 = face_vertex_corners[i].first;
361 const int corner_2 = face_vertex_corners[i].second;
362 if (edge_types[corner_edges[corner_1]] == EdgeType::Boundary &&
363 corner_verts[corner_1] == vertex_index)
364 {
365 shared_edge_i = corner_edges[corner_2];
366 r_sorted_corners[0] = face_vertex_corners[i].first;
367 std::swap(connected_faces[i], connected_faces[0]);
368 std::swap(face_vertex_corners[i], face_vertex_corners[0]);
369 break;
370 }
371 if (edge_types[corner_edges[corner_2]] == EdgeType::Boundary &&
372 corner_verts[corner_2] == vertex_index)
373 {
374 shared_edge_i = corner_edges[corner_1];
375 r_sorted_corners[0] = face_vertex_corners[i].second;
376 std::swap(connected_faces[i], connected_faces[0]);
377 std::swap(face_vertex_corners[i], face_vertex_corners[0]);
378 break;
379 }
380 }
381 if (shared_edge_i == -1) {
382 /* The rotation is inconsistent between the two faces on the boundary. Just choose one
383 * of the face's orientation. */
384 for (const int i : connected_faces.index_range()) {
385 const int corner_1 = face_vertex_corners[i].first;
386 const int corner_2 = face_vertex_corners[i].second;
387 if (edge_types[corner_edges[corner_1]] == EdgeType::Boundary) {
388 shared_edge_i = corner_edges[corner_2];
389 r_sorted_corners[0] = face_vertex_corners[i].first;
390 std::swap(connected_faces[i], connected_faces[0]);
391 std::swap(face_vertex_corners[i], face_vertex_corners[0]);
392 break;
393 }
394 if (edge_types[corner_edges[corner_2]] == EdgeType::Boundary) {
395 shared_edge_i = corner_edges[corner_1];
396 r_sorted_corners[0] = face_vertex_corners[i].second;
397 std::swap(connected_faces[i], connected_faces[0]);
398 std::swap(face_vertex_corners[i], face_vertex_corners[0]);
399 break;
400 }
401 }
402 }
403 }
404 else {
405 /* Any face can be the first. Just need to check the orientation. */
406 const int corner_1 = face_vertex_corners.first().first;
407 const int corner_2 = face_vertex_corners.first().second;
408 if (corner_verts[corner_1] == vertex_index) {
409 shared_edge_i = corner_edges[corner_2];
410 r_sorted_corners[0] = face_vertex_corners[0].first;
411 }
412 else {
413 r_sorted_corners[0] = face_vertex_corners[0].second;
414 shared_edge_i = corner_edges[corner_1];
415 }
416 }
417 BLI_assert(shared_edge_i != -1);
418
419 for (const int i : IndexRange(connected_faces.size() - 1)) {
420 r_shared_edges[i] = shared_edge_i;
421
422 /* Look at the other faces to see if it has this shared edge. */
423 int j = i + 1;
424 for (; j < connected_faces.size(); ++j) {
425 const int corner_1 = face_vertex_corners[j].first;
426 const int corner_2 = face_vertex_corners[j].second;
427
428 if (corner_edges[corner_1] == shared_edge_i) {
429 r_sorted_corners[i + 1] = face_vertex_corners[j].first;
430 shared_edge_i = corner_edges[corner_2];
431 break;
432 }
433 if (corner_edges[corner_2] == shared_edge_i) {
434 r_sorted_corners[i + 1] = face_vertex_corners[j].second;
435 shared_edge_i = corner_edges[corner_1];
436 break;
437 }
438 }
439 if (j == connected_faces.size()) {
440 /* The vertex is not manifold because the faces around the vertex don't form a loop, and
441 * hence can't be sorted. */
442 return false;
443 }
444
445 std::swap(connected_faces[i + 1], connected_faces[j]);
446 std::swap(face_vertex_corners[i + 1], face_vertex_corners[j]);
447 }
448
449 if (!boundary_vertex) {
450 /* Shared edge between first and last face. */
451 r_shared_edges.last() = shared_edge_i;
452 }
453 return true;
454}
455
459static void boundary_edge_on_face(const Span<int2> edges,
460 const Span<int> face_edges,
461 const int vertex_index,
462 const Span<EdgeType> edge_types,
463 int &r_edge)
464{
465 for (const int edge_i : face_edges) {
466 if (edge_types[edge_i] == EdgeType::Boundary) {
467 const int2 &edge = edges[edge_i];
468 if (edge[0] == vertex_index || edge[1] == vertex_index) {
469 r_edge = edge_i;
470 return;
471 }
472 }
473 }
474}
475
480static void boundary_edges_on_face(const IndexRange face,
481 const Span<int2> edges,
482 const Span<int> corner_verts,
483 const Span<int> corner_edges,
484 const int vertex_index,
485 const Span<EdgeType> edge_types,
486 int &r_edge1,
487 int &r_edge2)
488{
489 bool edge1_done = false;
490 /* This is set to true if the order in which we encounter the two edges is inconsistent with the
491 * orientation of the face. */
492 bool needs_swap = false;
493 for (const int corner : face) {
494 const int edge_i = corner_edges[corner];
495 if (edge_types[edge_i] == EdgeType::Boundary) {
496 const int2 &edge = edges[edge_i];
497 if (edge[0] == vertex_index || edge[1] == vertex_index) {
498 if (edge1_done) {
499 if (needs_swap) {
500 r_edge2 = r_edge1;
501 r_edge1 = edge_i;
502 }
503 else {
504 r_edge2 = edge_i;
505 }
506 return;
507 }
508 r_edge1 = edge_i;
509 edge1_done = true;
510 if (corner_verts[corner] == vertex_index) {
511 needs_swap = true;
512 }
513 }
514 }
515 }
516}
517
518static void add_edge(const int old_edge_i,
519 const int v1,
520 const int v2,
521 Vector<int> &new_to_old_edges_map,
522 Vector<int2> &new_edges,
523 Vector<int> &corner_edges)
524{
525 const int new_edge_i = new_edges.size();
526 new_to_old_edges_map.append(old_edge_i);
527 new_edges.append({v1, v2});
528 corner_edges.append(new_edge_i);
529}
530
531/* Returns true if the vertex is connected only to the two faces and is not on the boundary. */
532static bool vertex_needs_dissolving(const int vertex,
533 const int first_face_index,
534 const int second_face_index,
535 const Span<VertexType> vertex_types,
536 const GroupedSpan<int> vert_to_face_map)
537{
538 /* Order is guaranteed to be the same because 2face verts that are not on the boundary are
539 * ignored in `sort_vertex_faces`. */
540 return (vertex_types[vertex] != VertexType::Boundary && vert_to_face_map[vertex].size() == 2 &&
541 vert_to_face_map[vertex][0] == first_face_index &&
542 vert_to_face_map[vertex][1] == second_face_index);
543}
544
552static void dissolve_redundant_verts(const Span<int2> edges,
553 const OffsetIndices<int> faces,
554 const Span<int> corner_edges,
555 const GroupedSpan<int> vert_to_face_map,
556 MutableSpan<VertexType> vertex_types,
557 MutableSpan<int> old_to_new_edges_map,
558 Vector<int2> &new_edges,
559 Vector<int> &new_to_old_edges_map)
560{
561 const int vertex_num = vertex_types.size();
562 for (const int vert_i : IndexRange(vertex_num)) {
563 if (vert_to_face_map[vert_i].size() != 2 || vertex_types[vert_i] != VertexType::Normal) {
564 continue;
565 }
566 const int first_face_index = vert_to_face_map[vert_i][0];
567 const int second_face_index = vert_to_face_map[vert_i][1];
568 const int new_edge_index = new_edges.size();
569 bool edge_created = false;
570 for (const int edge_i : corner_edges.slice(faces[first_face_index])) {
571 const int2 &edge = edges[edge_i];
572 bool mark_edge = false;
574 edge[0], first_face_index, second_face_index, vertex_types, vert_to_face_map))
575 {
576 /* This vertex is now 'removed' and should be ignored elsewhere. */
577 vertex_types[edge[0]] = VertexType::Loose;
578 mark_edge = true;
579 }
581 edge[1], first_face_index, second_face_index, vertex_types, vert_to_face_map))
582 {
583 /* This vertex is now 'removed' and should be ignored elsewhere. */
584 vertex_types[edge[1]] = VertexType::Loose;
585 mark_edge = true;
586 }
587 if (mark_edge) {
588 if (!edge_created) {
589 /* The vertex indices in the dual mesh are the face indices of the input mesh. */
590 new_to_old_edges_map.append(edge_i);
591 new_edges.append({first_face_index, second_face_index});
592 edge_created = true;
593 }
594 old_to_new_edges_map[edge_i] = new_edge_index;
595 }
596 }
597 }
598}
599
614static Mesh *calc_dual_mesh(const Mesh &src_mesh,
615 const bool keep_boundaries,
616 const AttributeFilter &attribute_filter)
617{
618 const Span<float3> src_positions = src_mesh.vert_positions();
619 const Span<int2> src_edges = src_mesh.edges();
620 const OffsetIndices src_faces = src_mesh.faces();
621 const Span<int> src_corner_verts = src_mesh.corner_verts();
622 const Span<int> src_corner_edges = src_mesh.corner_edges();
623
624 Array<VertexType> vertex_types(src_mesh.verts_num);
625 Array<EdgeType> edge_types(src_mesh.edges_num);
626 calc_boundaries(src_mesh, vertex_types, edge_types);
627 /* Stores the indices of the faces connected to the vertex. Because the faces are looped
628 * over in order of their indices, the face's indices will be sorted in ascending order.
629 * (This can change once they are sorted using `sort_vertex_faces`). */
630 Array<int> vert_to_face_indices = src_mesh.vert_to_face_map().data;
631 const OffsetIndices<int> vert_to_face_offsets = src_mesh.vert_to_face_map().offsets;
632
633 Array<Array<int>> vertex_shared_edges(src_mesh.verts_num);
634 Array<Array<int>> vertex_corners(src_mesh.verts_num);
635 threading::parallel_for(src_positions.index_range(), 512, [&](IndexRange range) {
636 for (const int i : range) {
637 if (vertex_types[i] == VertexType::Loose || vertex_types[i] >= VertexType::NonManifold ||
638 (!keep_boundaries && vertex_types[i] == VertexType::Boundary))
639 {
640 /* Bad vertex that we can't work with. */
641 continue;
642 }
643 MutableSpan<int> corner_indices = vert_to_face_indices.as_mutable_span().slice(
644 vert_to_face_offsets[i]);
645 Array<int> sorted_corners(corner_indices.size());
646 bool vertex_ok = true;
647 if (vertex_types[i] == VertexType::Normal) {
648 Array<int> shared_edges(corner_indices.size());
649 vertex_ok = sort_vertex_faces(src_edges,
650 src_faces,
651 src_corner_verts,
652 src_corner_edges,
653 i,
654 false,
655 edge_types,
656 corner_indices,
657 shared_edges,
658 sorted_corners);
659 vertex_shared_edges[i] = std::move(shared_edges);
660 }
661 else {
662 Array<int> shared_edges(corner_indices.size() - 1);
663 vertex_ok = sort_vertex_faces(src_edges,
664 src_faces,
665 src_corner_verts,
666 src_corner_edges,
667 i,
668 true,
669 edge_types,
670 corner_indices,
671 shared_edges,
672 sorted_corners);
673 vertex_shared_edges[i] = std::move(shared_edges);
674 }
675 if (!vertex_ok) {
676 /* The sorting failed which means that the vertex is non-manifold and should be ignored
677 * further on. */
678 vertex_types[i] = VertexType::NonManifold;
679 continue;
680 }
681 vertex_corners[i] = std::move(sorted_corners);
682 }
683 });
684
685 const GroupedSpan<int> vert_to_face_map(vert_to_face_offsets, vert_to_face_indices);
686
687 Vector<float3> vert_positions(src_mesh.faces_num);
688 for (const int i : src_faces.index_range()) {
689 const IndexRange face = src_faces[i];
690 vert_positions[i] = bke::mesh::face_center_calc(src_positions, src_corner_verts.slice(face));
691 }
692
693 Array<int> boundary_edge_midpoint_index;
694 if (keep_boundaries) {
695 /* Only initialize when we actually need it. */
696 boundary_edge_midpoint_index.reinitialize(src_mesh.edges_num);
697 /* We need to add vertices at the centers of boundary edges. */
698 for (const int i : IndexRange(src_mesh.edges_num)) {
699 if (edge_types[i] == EdgeType::Boundary) {
700 const int2 &edge = src_edges[i];
701 const float3 mid = math::midpoint(src_positions[edge[0]], src_positions[edge[1]]);
702 boundary_edge_midpoint_index[i] = vert_positions.size();
703 vert_positions.append(mid);
704 }
705 }
706 }
707
708 Vector<int> face_sizes;
709 Vector<int> corner_verts;
710 Vector<int> corner_edges;
711 Vector<int2> new_edges;
712 /* These are used to transfer attributes. */
713 Vector<int> new_to_old_face_corners_map;
714 Vector<int> new_to_old_edges_map;
715 /* Stores the index of the vertex in the dual and the face it should get the attribute from. */
716 Vector<std::pair<int, int>> boundary_vertex_to_relevant_face_map;
717 /* Since each edge in the dual (except the ones created with keep boundaries) comes from
718 * exactly one edge in the original, we can use this array to keep track of whether it still
719 * needs to be created or not. If it's not -1 it gives the index in `new_edges` of the dual
720 * edge. The edges coming from preserving the boundaries only get added once anyway, so we
721 * don't need a hash-map for that. */
722 Array<int> old_to_new_edges_map(src_mesh.edges_num);
723 old_to_new_edges_map.fill(-1);
724
725 /* This is necessary to prevent duplicate edges from being created, but will likely not do
726 * anything for most meshes. */
727 dissolve_redundant_verts(src_edges,
728 src_faces,
729 src_corner_edges,
730 vert_to_face_map,
731 vertex_types,
732 old_to_new_edges_map,
733 new_edges,
734 new_to_old_edges_map);
735
736 for (const int i : IndexRange(src_mesh.verts_num)) {
737 if (vertex_types[i] == VertexType::Loose || vertex_types[i] >= VertexType::NonManifold ||
738 (!keep_boundaries && vertex_types[i] == VertexType::Boundary))
739 {
740 /* Bad vertex that we can't work with. */
741 continue;
742 }
743
744 Vector<int> corner_indices = vert_to_face_map[i];
745 Span<int> shared_edges = vertex_shared_edges[i];
746 Span<int> sorted_corners = vertex_corners[i];
747 if (vertex_types[i] == VertexType::Normal) {
748 if (corner_indices.size() <= 2) {
749 /* We can't make a face from 2 vertices. */
750 continue;
751 }
752
753 /* Add edges in the loop. */
754 for (const int i : shared_edges.index_range()) {
755 const int old_edge_i = shared_edges[i];
756 if (old_to_new_edges_map[old_edge_i] == -1) {
757 /* This edge has not been created yet. */
758 new_to_old_edges_map.append(old_edge_i);
759 old_to_new_edges_map[old_edge_i] = new_edges.size();
760 new_edges.append({corner_indices[i], corner_indices[(i + 1) % corner_indices.size()]});
761 }
762 corner_edges.append(old_to_new_edges_map[old_edge_i]);
763 }
764
765 new_to_old_face_corners_map.extend(sorted_corners);
766 }
767 else {
792 /* Add the edges in between the faces. */
793 for (const int i : shared_edges.index_range()) {
794 const int old_edge_i = shared_edges[i];
795 if (old_to_new_edges_map[old_edge_i] == -1) {
796 /* This edge has not been created yet. */
797 new_to_old_edges_map.append(old_edge_i);
798 old_to_new_edges_map[old_edge_i] = new_edges.size();
799 new_edges.append({corner_indices[i], corner_indices[i + 1]});
800 }
801 corner_edges.append(old_to_new_edges_map[old_edge_i]);
802 }
803
804 new_to_old_face_corners_map.extend(sorted_corners);
805
806 /* Add the vertex and the midpoints of the two boundary edges to the loop. */
807
808 /* Get the boundary edges. */
809 int edge1;
810 int edge2;
811 if (corner_indices.size() >= 2) {
812 /* The first boundary edge is at the end of the chain of faces. */
813 boundary_edge_on_face(src_edges,
814 src_corner_edges.slice(src_faces[corner_indices.last()]),
815 i,
816 edge_types,
817 edge1);
818 boundary_edge_on_face(src_edges,
819 src_corner_edges.slice(src_faces[corner_indices.first()]),
820 i,
821 edge_types,
822 edge2);
823 }
824 else {
825 /* If there is only one face both edges are in that face. */
826 boundary_edges_on_face(src_faces[corner_indices[0]],
827 src_edges,
828 src_corner_verts,
829 src_corner_edges,
830 i,
831 edge_types,
832 edge1,
833 edge2);
834 }
835
836 const int last_face_center = corner_indices.last();
837 corner_indices.append(boundary_edge_midpoint_index[edge1]);
838 new_to_old_face_corners_map.append(sorted_corners.last());
839 const int first_midpoint = corner_indices.last();
840 if (old_to_new_edges_map[edge1] == -1) {
841 add_edge(edge1,
842 last_face_center,
843 first_midpoint,
844 new_to_old_edges_map,
845 new_edges,
846 corner_edges);
847 old_to_new_edges_map[edge1] = new_edges.size() - 1;
848 boundary_vertex_to_relevant_face_map.append(std::pair(first_midpoint, last_face_center));
849 }
850 else {
851 corner_edges.append(old_to_new_edges_map[edge1]);
852 }
853 corner_indices.append(vert_positions.size());
854 /* This is sort of arbitrary, but interpolating would be a lot harder to do. */
855 new_to_old_face_corners_map.append(sorted_corners.first());
856 boundary_vertex_to_relevant_face_map.append(
857 std::pair(corner_indices.last(), last_face_center));
858 vert_positions.append(src_positions[i]);
859 const int boundary_vertex = corner_indices.last();
860 add_edge(
861 edge1, first_midpoint, boundary_vertex, new_to_old_edges_map, new_edges, corner_edges);
862
863 corner_indices.append(boundary_edge_midpoint_index[edge2]);
864 new_to_old_face_corners_map.append(sorted_corners.first());
865 const int second_midpoint = corner_indices.last();
866 add_edge(
867 edge2, boundary_vertex, second_midpoint, new_to_old_edges_map, new_edges, corner_edges);
868
869 if (old_to_new_edges_map[edge2] == -1) {
870 const int first_face_center = corner_indices.first();
871 add_edge(edge2,
872 second_midpoint,
873 first_face_center,
874 new_to_old_edges_map,
875 new_edges,
876 corner_edges);
877 old_to_new_edges_map[edge2] = new_edges.size() - 1;
878 boundary_vertex_to_relevant_face_map.append(std::pair(second_midpoint, first_face_center));
879 }
880 else {
881 corner_edges.append(old_to_new_edges_map[edge2]);
882 }
883 }
884
885 face_sizes.append(corner_indices.size());
886 for (const int j : corner_indices) {
887 corner_verts.append(j);
888 }
889 }
890 Mesh *mesh_out = BKE_mesh_new_nomain(
891 vert_positions.size(), new_edges.size(), face_sizes.size(), corner_verts.size());
892 bke::mesh_smooth_set(*mesh_out, false);
893
894 transfer_attributes(vertex_types,
895 keep_boundaries,
896 new_to_old_edges_map,
897 new_to_old_face_corners_map,
898 boundary_vertex_to_relevant_face_map,
899 attribute_filter,
900 src_mesh.attributes(),
901 mesh_out->attributes_for_write());
902
903 mesh_out->vert_positions_for_write().copy_from(vert_positions);
904 mesh_out->edges_for_write().copy_from(new_edges);
905
906 if (mesh_out->faces_num > 0) {
907 MutableSpan<int> dst_face_offsets = mesh_out->face_offsets_for_write();
908 dst_face_offsets.drop_back(1).copy_from(face_sizes);
910 }
911 mesh_out->corner_verts_for_write().copy_from(corner_verts);
912 mesh_out->corner_edges_for_write().copy_from(corner_edges);
913
914 return mesh_out;
915}
916
918{
919 GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
920 const bool keep_boundaries = params.extract_input<bool>("Keep Boundaries");
921 geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
922 if (const Mesh *mesh = geometry_set.get_mesh()) {
923 Mesh *new_mesh = calc_dual_mesh(
924 *mesh, keep_boundaries, params.get_attribute_filter("Dual Mesh"));
925 geometry::debug_randomize_mesh_order(new_mesh);
926 geometry_set.replace_mesh(new_mesh);
927 }
928 });
929 params.set_output("Dual Mesh", std::move(geometry_set));
930}
931
932static void node_register()
933{
934 static blender::bke::bNodeType ntype;
935 geo_node_type_base(&ntype, GEO_NODE_DUAL_MESH, "Dual Mesh", NODE_CLASS_GEOMETRY);
936 ntype.declare = node_declare;
939}
940NOD_REGISTER_NODE(node_register)
941
942} // namespace blender::nodes::node_geo_dual_mesh_cc
Mesh * BKE_mesh_new_nomain(int verts_num, int edges_num, int faces_num, int corners_num)
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define ELEM(...)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
ATTR_WARN_UNUSED_RESULT const BMVert * v2
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
int64_t size() const
Definition BLI_array.hh:245
const T & first() const
Definition BLI_array.hh:270
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:388
void copy_from(GSpan values)
GMutableSpan take_front(const int64_t n) const
MutableSpan< T > typed() const
Span< T > typed() const
int64_t size() const
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr bool is_empty() const
Definition BLI_span.hh:510
constexpr void fill(const T &value) const
Definition BLI_span.hh:518
constexpr IndexRange index_range() const
Definition BLI_span.hh:671
constexpr T & last(const int64_t n=0) const
Definition BLI_span.hh:690
int64_t remove_if(Predicate &&predicate)
Definition BLI_set.hh:496
bool remove(const Key &key)
Definition BLI_set.hh:366
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
int64_t size() const
void append(const T &value)
GAttributeReader lookup(const StringRef attribute_id) const
Set< StringRefNull > all_ids() const
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
local_group_size(16, 16) .push_constant(Type b
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void gather(GSpan src, Span< int > map, GMutableSpan dst)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
float3 face_center_calc(Span< float3 > vert_positions, Span< int > face_verts)
eCustomDataType cpp_type_to_custom_data_type(const CPPType &type)
void mesh_smooth_set(Mesh &mesh, bool use_smooth, bool keep_sharp_edges=false)
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
T midpoint(const T &a, const T &b)
static void node_geo_exec(GeoNodeExecParams params)
static void dissolve_redundant_verts(const Span< int2 > edges, const OffsetIndices< int > faces, const Span< int > corner_edges, const GroupedSpan< int > vert_to_face_map, MutableSpan< VertexType > vertex_types, MutableSpan< int > old_to_new_edges_map, Vector< int2 > &new_edges, Vector< int > &new_to_old_edges_map)
static void node_declare(NodeDeclarationBuilder &b)
static void calc_boundaries(const Mesh &mesh, MutableSpan< VertexType > r_vertex_types, MutableSpan< EdgeType > r_edge_types)
static EdgeType get_edge_type_with_added_neighbor(EdgeType old_type)
static void boundary_edge_on_face(const Span< int2 > edges, const Span< int > face_edges, const int vertex_index, const Span< EdgeType > edge_types, int &r_edge)
static void boundary_edges_on_face(const IndexRange face, const Span< int2 > edges, const Span< int > corner_verts, const Span< int > corner_edges, const int vertex_index, const Span< EdgeType > edge_types, int &r_edge1, int &r_edge2)
static void transfer_attributes(const Span< VertexType > vertex_types, const bool keep_boundaries, const Span< int > new_to_old_edges_map, const Span< int > new_to_old_face_corners_map, const Span< std::pair< int, int > > boundary_vertex_to_relevant_face_map, const AttributeFilter &attribute_filter, const AttributeAccessor src_attributes, MutableAttributeAccessor dst_attributes)
static bool sort_vertex_faces(const Span< int2 > edges, const OffsetIndices< int > faces, const Span< int > corner_verts, const Span< int > corner_edges, const int vertex_index, const bool boundary_vertex, const Span< EdgeType > edge_types, MutableSpan< int > connected_faces, MutableSpan< int > r_shared_edges, MutableSpan< int > r_sorted_corners)
static void copy_data_based_on_vertex_types(Span< T > data, MutableSpan< T > r_data, const Span< VertexType > vertex_types, const bool keep_boundaries)
static bool vertex_needs_dissolving(const int vertex, const int first_face_index, const int second_face_index, const Span< VertexType > vertex_types, const GroupedSpan< int > vert_to_face_map)
static void add_edge(const int old_edge_i, const int v1, const int v2, Vector< int > &new_to_old_edges_map, Vector< int2 > &new_edges, Vector< int > &corner_edges)
static Mesh * calc_dual_mesh(const Mesh &src_mesh, const bool keep_boundaries, const AttributeFilter &attribute_filter)
static void copy_data_based_on_pairs(Span< T > data, MutableSpan< T > r_data, const Span< std::pair< int, int > > new_to_old_map)
static VertexType get_vertex_type_with_added_neighbor(VertexType old_type)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
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:95
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
signed char int8_t
Definition stdint.h:75
int edges_num
int faces_num
int verts_num
bool allow_skip(const StringRef name) const
const Mesh * get_mesh() const
void modify_geometry_sets(ForeachSubGeometryCallback callback)
void replace_mesh(Mesh *mesh, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
Defines a node type.
Definition BKE_node.hh:218
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
NodeDeclareFunction declare
Definition BKE_node.hh:347