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