40 .supported_type(GeometryComponent::Type::Mesh)
41 .description(
"Mesh to extrude elements of");
56 const bNode *node =
b.node_or_null();
57 if (node !=
nullptr) {
105 for (
const StringRef id : names_to_remove) {
126 const int vert_expand,
127 const int edge_expand,
128 const int face_expand,
129 const int loop_expand)
132 if (vert_expand != 0) {
133 const int old_verts_num = mesh.
verts_num;
137 if (edge_expand != 0) {
139 mesh.attributes_for_write().add(
142 const int old_edges_num = mesh.
edges_num;
146 if (face_expand != 0) {
147 const int old_faces_num = mesh.
faces_num;
151 &mesh.
runtime->face_offsets_sharing_info,
152 old_faces_num == 0 ? 0 : (old_faces_num + 1),
158 if (loop_expand != 0) {
160 mesh.attributes_for_write().add(
162 mesh.attributes_for_write().add(
174 case AttrDomain::Point:
176 case AttrDomain::Edge:
178 case AttrDomain::Face:
180 case AttrDomain::Corner:
208 const IndexRange dst_range(segment_pos, segment.size());
210 for (
const int i : segment.index_range()) {
211 for (
const int src_i : src_groups[segment[
i]]) {
212 mixer.mix_in(
i, src[src_i]);
226 using T = decltype(dummy);
227 copy_with_mixing(src.typed<T>(), src_groups, selection, dst.typed<T>());
238 bke::attribute_math::DefaultPropagationMixer<T> mixer{dst.slice(range)};
240 const int group_i = selection[i];
241 for (const int i_src : src_groups[group_i]) {
242 mixer.mix_in(i, src[i_src]);
255 using T = decltype(dummy);
256 copy_with_mixing(src.typed<T>(), src_groups, selection, dst.typed<T>());
273 ids_by_domain[int(iter.
domain)].append(iter.
name);
275 return ids_by_domain;
282 bool is_empty =
true;
287 if (iter.
domain != domain) {
330 vertex_group_names.
add(group->name);
333 if (!vertex_group_names.
is_empty() && !mesh.deform_verts().is_empty()) {
340 if (!vertex_group_names.
contains(
id)) {
355 vertex_group_names.
add(group->name);
358 if (!vertex_group_names.
is_empty() && !mesh.deform_verts().is_empty()) {
365 if (!vertex_group_names.
contains(
id)) {
379 const int orig_vert_size = mesh.
verts_num;
380 const int orig_edge_size = mesh.
edges_num;
399 {
"position",
".edge_verts"});
404 if (!ids_by_domain[
int(AttrDomain::Edge)].is_empty()) {
406 mesh.edges(), orig_vert_size, vert_to_edge_offsets, vert_to_edge_indices);
412 const IndexRange new_vert_range{orig_vert_size, selection.
size()};
413 const IndexRange new_edge_range{orig_edge_size, selection.
size()};
417 GrainSize(4096), [&](
const int index,
const int i_selection) {
418 new_edges[i_selection] =
int2(index, new_vert_range[i_selection]);
425 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Edge)]) {
428 attribute.
span, vert_to_edge_map, selection, attribute.
span.
slice(new_edge_range));
435 new_positions[
i] = positions[index] + offsets[index];
445 if (attribute_outputs.
top_id) {
447 attributes, *attribute_outputs.
top_id, AttrDomain::Point, new_vert_range);
449 if (attribute_outputs.
side_id) {
451 attributes, *attribute_outputs.
side_id, AttrDomain::Edge, new_edge_range);
454 const bool no_loose_vert_hint = mesh.
runtime->loose_verts_cache.is_cached() &&
455 mesh.
runtime->loose_verts_cache.data().count == 0;
456 const bool no_overlapping_hint = mesh.no_overlapping_topology();
458 if (no_loose_vert_hint) {
459 mesh.tag_loose_verts_none();
461 if (no_overlapping_hint) {
462 mesh.tag_overlapping_none();
470 const int vert_connected_to_face_1,
471 const int vert_connected_to_face_2,
472 const int vert_across_from_face_1,
473 const int vert_across_from_face_2,
474 const int edge_connected_to_face,
475 const int connecting_edge_1,
476 const int edge_across_from_face,
477 const int connecting_edge_2)
480 bool start_with_connecting_edge =
true;
482 if (other_face_edges[
i] == edge_connected_to_face) {
483 start_with_connecting_edge = other_face_verts[
i] == vert_connected_to_face_1;
487 if (start_with_connecting_edge) {
488 new_corner_verts[0] = vert_connected_to_face_1;
489 new_corner_edges[0] = connecting_edge_1;
490 new_corner_verts[1] = vert_across_from_face_1;
491 new_corner_edges[1] = edge_across_from_face;
492 new_corner_verts[2] = vert_across_from_face_2;
493 new_corner_edges[2] = connecting_edge_2;
494 new_corner_verts[3] = vert_connected_to_face_2;
495 new_corner_edges[3] = edge_connected_to_face;
498 new_corner_verts[0] = vert_connected_to_face_1;
499 new_corner_edges[0] = edge_connected_to_face;
500 new_corner_verts[1] = vert_connected_to_face_2;
501 new_corner_edges[1] = connecting_edge_2;
502 new_corner_verts[2] = vert_across_from_face_2;
503 new_corner_edges[2] = edge_across_from_face;
504 new_corner_verts[3] = vert_across_from_face_1;
505 new_corner_edges[3] = connecting_edge_1;
515 if (edge_mask.
size() == edges.
size()) {
524 edge_mask.
to_indices<
int>(masked_edge_to_edge);
527 for (const int i : range) {
528 r_indices[i] = masked_edge_to_edge[r_indices[i]];
532 return {r_offsets.as_span(), r_indices.as_span()};
536 const bool no_loose_vert_hint = mesh.
runtime->loose_verts_cache.is_cached() &&
537 mesh.
runtime->loose_verts_cache.data().count == 0;
538 const bool no_loose_edge_hint = mesh.
runtime->loose_edges_cache.is_cached() &&
539 mesh.
runtime->loose_edges_cache.data().count == 0;
540 const bool no_overlapping_hint = mesh.no_overlapping_topology();
542 if (no_loose_vert_hint) {
543 mesh.tag_loose_verts_none();
545 if (no_loose_edge_hint) {
546 mesh.tag_loose_edges_none();
548 if (no_overlapping_hint) {
549 mesh.tag_overlapping_none();
559 const int orig_vert_size = mesh.
verts_num;
567 edge_evaluator.
add(offset_field);
582 const int2 edge = orig_edges[i_edge];
583 const float3 offset = edge_offsets[i_edge];
584 mixer.mix_in(edge[0], offset);
585 mixer.mix_in(edge[1], offset);
592 orig_edges, edge_selection, orig_vert_size, memory);
594 const IndexRange new_vert_range{orig_vert_size, new_verts.
size()};
602 const IndexRange new_loop_range{orig_loop_size, new_face_range.
size() * 4};
610 orig_faces, mesh.corner_edges(), mesh.
edges_num, edge_to_face_offsets, edge_to_face_indices);
617 orig_edges, edge_selection, orig_vert_size, vert_to_edge_offsets, vert_to_edge_indices);
623 new_vert_range.
size(),
624 connect_edge_range.
size() + duplicate_edge_range.
size(),
625 new_face_range.
size(),
626 new_loop_range.
size());
629 attributes, {
"position",
".edge_verts",
".corner_vert",
".corner_edge"});
645 connect_edges[dst] =
int2(src, new_vert_range[dst]);
652 const int2 orig_edge = edges[edge_selection[
i]];
653 const int i_new_vert_1 = vert_to_new_vert[orig_edge[0]];
654 const int i_new_vert_2 = vert_to_new_vert[orig_edge[1]];
655 duplicate_edges[
i] =
int2(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
660 const int2 duplicate_edge = duplicate_edges[
i];
661 const int new_vert_1 = duplicate_edge[0];
662 const int new_vert_2 = duplicate_edge[1];
663 const int extrude_index_1 = new_vert_1 - orig_vert_size;
664 const int extrude_index_2 = new_vert_2 - orig_vert_size;
666 const int2 orig_edge = edges[orig_edge_index];
667 const Span<int> connected_faces = edge_to_face_map[orig_edge_index];
674 if (connected_faces.
size() == 1) {
676 connected_face_verts = corner_verts.
slice(connected_face);
677 connected_face_edges = corner_edges.
slice(connected_face);
680 connected_face_edges,
681 new_corner_verts.
slice(4 *
i, 4),
682 new_corner_edges.
slice(4 *
i, 4),
688 connect_edge_range[extrude_index_1],
689 duplicate_edge_range[
i],
690 connect_edge_range[extrude_index_2]);
698 attributes, ids_by_domain[
int(AttrDomain::Edge)], edge_selection, duplicate_edge_range);
701 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Edge)]) {
704 vert_to_selected_edge_map,
706 attribute.
span.
slice(connect_edge_range));
711 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Face)]) {
714 attribute.
span, edge_to_face_map, edge_selection, attribute.
span.
slice(new_face_range));
720 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Corner)]) {
723 using T = decltype(dummy);
724 MutableSpan<T> data = attribute.span.typed<T>();
725 MutableSpan<T> new_data = data.slice(new_loop_range);
726 edge_selection.foreach_index(
727 GrainSize(256), [&](const int64_t orig_edge_index, const int64_t i_edge_selection) {
728 const Span<int> connected_faces = edge_to_face_map[orig_edge_index];
729 if (connected_faces.is_empty()) {
731 new_data.slice(4 * i_edge_selection, 4).fill(T());
737 Array<T> side_face_corner_data(2);
738 bke::attribute_math::DefaultPropagationMixer<T> mixer{side_face_corner_data};
740 const int new_vert_1 = duplicate_edges[i_edge_selection][0];
741 const int new_vert_2 = duplicate_edges[i_edge_selection][1];
742 const int orig_vert_1 = edges[orig_edge_index][0];
743 const int orig_vert_2 = edges[orig_edge_index][1];
747 for (const int connected_face : connected_faces) {
748 for (const int i_loop : faces[connected_face]) {
749 if (corner_verts[i_loop] == orig_vert_1) {
750 mixer.mix_in(0, data[i_loop]);
752 if (corner_verts[i_loop] == orig_vert_2) {
753 mixer.mix_in(1, data[i_loop]);
763 for (const int i : IndexRange(4 * i_edge_selection, 4)) {
764 if (ELEM(new_corner_verts[i], new_vert_1, orig_vert_1)) {
765 new_data[i] = side_face_corner_data.first();
767 else if (ELEM(new_corner_verts[i], new_vert_2, orig_vert_2)) {
768 new_data[i] = side_face_corner_data.last();
779 if (edge_offsets.is_single()) {
780 const float3 offset = edge_offsets.get_internal_single();
781 new_verts.foreach_index_optimized<
int>(
GrainSize(1024), [&](
const int src,
const int dst) {
782 new_positions[dst] = positions[src] + offset;
786 new_verts.foreach_index_optimized<
int>(
GrainSize(1024), [&](
const int src,
const int dst) {
787 new_positions[dst] = positions[src] + vert_offsets[src];
792 array_utils::gather(
indices->as_span(), new_verts,
indices->slice(new_vert_range));
796 array_utils::gather(
indices->as_span(), edge_selection,
indices->slice(duplicate_edge_range));
802 if (attribute_outputs.top_id) {
804 attributes, *attribute_outputs.top_id, AttrDomain::Edge, duplicate_edge_range);
806 if (attribute_outputs.side_id) {
808 attributes, *attribute_outputs.side_id, AttrDomain::Face, new_face_range);
820 for (
const int i_edge : edge_indices) {
821 const int2 &edge = edges[i_edge];
822 vert_indices.
add(edge[0]);
823 vert_indices.
add(edge[1]);
838 const int orig_vert_size = mesh.
verts_num;
841 const Span<int> orig_corner_verts = mesh.corner_verts();
842 const int orig_loop_size = orig_corner_verts.
size();
847 face_evaluator.
add(offset_field);
856 face_selection.
to_bools(face_selection_array);
862 if (!face_position_offsets.
is_single()) {
866 const float3 offset = face_position_offsets[i_face];
867 for (
const int vert : orig_corner_verts.
slice(orig_faces[i_face])) {
868 mixer.mix_in(vert, offset);
878 orig_faces, mesh.corner_edges(), mesh.
edges_num, edge_to_face_offsets, edge_to_face_indices);
883 orig_faces, face_selection, orig_corner_verts, orig_vert_size, memory);
896 for (
const int i_edge : orig_edges.
index_range()) {
899 int i_selected_face = -1;
900 int deselected_face_count = 0;
901 int selected_face_count = 0;
902 for (
const int i_other_face :
faces) {
903 if (face_selection_array[i_other_face]) {
904 selected_face_count++;
905 i_selected_face = i_other_face;
908 deselected_face_count++;
912 if (selected_face_count == 1) {
915 boundary_edge_indices.
add_new(i_edge);
916 edge_extruded_face_indices.
append(i_selected_face);
918 else if (selected_face_count > 1) {
920 if (deselected_face_count > 0) {
922 new_inner_edge_indices.
add_new(i_edge);
927 inner_edge_indices.
append(i_edge);
935 const int extruded_vert_size = new_vert_indices.
size();
938 for (
const int i_edge : new_inner_edge_indices) {
939 const int2 &edge = orig_edges[i_edge];
940 new_vert_indices.
add(edge[0]);
941 new_vert_indices.
add(edge[1]);
945 const IndexRange new_vert_range{orig_vert_size, new_vert_indices.
size()};
947 const IndexRange connect_edge_range{orig_edges.
size(), extruded_vert_size};
949 const IndexRange boundary_edge_range = connect_edge_range.
after(boundary_edge_indices.
size());
951 const IndexRange new_inner_edge_range = boundary_edge_range.
after(new_inner_edge_indices.
size());
955 const IndexRange side_loop_range{orig_corner_verts.
size(), side_face_range.
size() * 4};
963 new_vert_range.
size(),
964 connect_edge_range.
size() + boundary_edge_range.
size() + new_inner_edge_range.
size(),
965 side_face_range.
size(),
966 side_loop_range.
size());
969 attributes, {
".corner_vert",
".corner_edge",
".edge_verts"});
990 connect_edges[
i] =
int2(new_vert_indices[
i], new_vert_range[
i]);
995 const int2 &orig_edge = edges[boundary_edge_indices[
i]];
996 const int i_new_vert_1 = new_vert_indices.
index_of(orig_edge[0]);
997 const int i_new_vert_2 = new_vert_indices.
index_of(orig_edge[1]);
998 boundary_edges[
i] =
int2(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
1002 for (
const int i : new_inner_edge_indices.
index_range()) {
1003 const int2 &orig_edge = edges[new_inner_edge_indices[
i]];
1004 const int i_new_vert_1 = new_vert_indices.
index_of(orig_edge[0]);
1005 const int i_new_vert_2 = new_vert_indices.
index_of(orig_edge[1]);
1006 new_inner_edges[
i] =
int2(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
1010 for (
const int i : inner_edge_indices) {
1011 int2 &edge = edges[
i];
1012 const int i_new_vert_1 = new_vert_indices.
index_of_try(edge[0]);
1013 const int i_new_vert_2 = new_vert_indices.
index_of_try(edge[1]);
1014 if (i_new_vert_1 != -1) {
1015 edge[0] = new_vert_range[i_new_vert_1];
1017 if (i_new_vert_2 != -1) {
1018 edge[1] = new_vert_range[i_new_vert_2];
1024 for (
const int corner :
faces[i_face]) {
1025 const int i_new_vert = new_vert_indices.
index_of_try(corner_verts[corner]);
1026 if (i_new_vert != -1) {
1027 corner_verts[corner] = new_vert_range[i_new_vert];
1029 const int i_boundary_edge = boundary_edge_indices.
index_of_try(corner_edges[corner]);
1030 if (i_boundary_edge != -1) {
1031 corner_edges[corner] = boundary_edge_range[i_boundary_edge];
1035 const int i_new_inner_edge = new_inner_edge_indices.
index_of_try(corner_edges[corner]);
1036 if (i_new_inner_edge != -1) {
1037 corner_edges[corner] = new_inner_edge_range[i_new_inner_edge];
1043 for (
const int i : boundary_edge_indices.
index_range()) {
1044 const int2 &boundary_edge = boundary_edges[
i];
1045 const int new_vert_1 = boundary_edge[0];
1046 const int new_vert_2 = boundary_edge[1];
1047 const int extrude_index_1 = new_vert_1 - orig_vert_size;
1048 const int extrude_index_2 = new_vert_2 - orig_vert_size;
1053 corner_edges.
slice(extrude_face),
1054 new_corner_verts.
slice(4 *
i, 4),
1055 new_corner_edges.
slice(4 *
i, 4),
1058 new_vert_indices[extrude_index_1],
1059 new_vert_indices[extrude_index_2],
1060 boundary_edge_range[
i],
1061 connect_edge_range[extrude_index_1],
1062 boundary_edge_indices[
i],
1063 connect_edge_range[extrude_index_2]);
1068 mesh, ids_by_domain[
int(AttrDomain::Point)], new_vert_indices, new_vert_range);
1072 ids_by_domain[
int(AttrDomain::Face)],
1073 edge_extruded_face_indices,
1076 if (!ids_by_domain[
int(AttrDomain::Edge)].is_empty()) {
1084 edges, boundary_edge_mask, mesh.
verts_num, vert_to_edge_offsets, vert_to_edge_indices);
1086 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Edge)]) {
1099 vert_to_boundary_edge_map,
1101 attribute.
span.
slice(connect_edge_range));
1107 if (!ids_by_domain[
int(AttrDomain::Corner)].is_empty()) {
1110 for (const int i_boundary_edge : range) {
1111 const int2 &boundary_edge = boundary_edges[i_boundary_edge];
1112 const int new_vert_1 = boundary_edge[0];
1113 const int new_vert_2 = boundary_edge[1];
1114 const int orig_vert_1 = new_vert_indices[new_vert_1 - orig_vert_size];
1115 const int orig_vert_2 = new_vert_indices[new_vert_2 - orig_vert_size];
1124 for (const int corner : faces[edge_extruded_face_indices[i_boundary_edge]]) {
1125 if (corner_verts[corner] == new_vert_1) {
1128 if (corner_verts[corner] == new_vert_2) {
1136 for (const int i : IndexRange(4 * i_boundary_edge, 4)) {
1137 if (ELEM(new_corner_verts[i], new_vert_1, orig_vert_1)) {
1138 orig_corners[i] = corner_1;
1140 else if (ELEM(new_corner_verts[i], new_vert_2, orig_vert_2)) {
1141 orig_corners[i] = corner_2;
1147 attributes, ids_by_domain[
int(AttrDomain::Corner)], orig_corners, side_loop_range);
1154 if (face_position_offsets.is_single()) {
1155 const float3 offset = face_position_offsets.get_internal_single();
1156 all_selected_verts.foreach_index(
GrainSize(1024), [&](
const int orig_vert) {
1157 const int i_new = new_vert_indices.index_of_try(orig_vert);
1159 positions[orig_vert] += offset;
1162 positions[new_vert_range[i_new]] += offset;
1167 all_selected_verts.foreach_index(
GrainSize(1024), [&](
const int orig_vert) {
1168 const int i_new = new_vert_indices.index_of_try(orig_vert);
1169 const float3 offset = vert_offsets[orig_vert];
1171 positions[orig_vert] += offset;
1174 positions[new_vert_range[i_new]] += offset;
1180 array_utils::gather(
1181 indices->as_span(), new_vert_indices.as_span(),
indices->slice(new_vert_range));
1185 array_utils::gather(
indices->as_span(),
1186 new_inner_edge_indices.as_span(),
1187 indices->slice(new_inner_edge_range));
1188 array_utils::gather(
1189 indices->as_span(), boundary_edge_indices.as_span(),
indices->slice(boundary_edge_range));
1192 array_utils::gather(
1193 indices->as_span(), edge_extruded_face_indices.as_span(),
indices->slice(side_face_range));
1196 if (attribute_outputs.top_id) {
1198 attributes, *attribute_outputs.top_id, AttrDomain::Face, face_selection);
1200 if (attribute_outputs.side_id) {
1202 attributes, *attribute_outputs.side_id, AttrDomain::Face, side_face_range);
1214 const int orig_vert_size = mesh.
verts_num;
1215 const int orig_edge_size = mesh.
edges_num;
1217 const Span<int> orig_corner_verts = mesh.corner_verts();
1218 const int orig_loop_size = orig_corner_verts.
size();
1238 orig_faces, face_selection, group_per_face_data);
1239 const int extrude_corner_size = group_per_face.
total_size();
1241 const IndexRange new_vert_range{orig_vert_size, extrude_corner_size};
1243 const IndexRange connect_edge_range{orig_edge_size, extrude_corner_size};
1245 const IndexRange duplicate_edge_range = connect_edge_range.
after(extrude_corner_size);
1248 const IndexRange side_loop_range{orig_loop_size, side_face_range.
size() * 4};
1256 new_vert_range.
size(),
1257 connect_edge_range.
size() + duplicate_edge_range.
size(),
1258 side_face_range.
size(),
1259 side_loop_range.
size());
1262 attributes, {
"position",
".edge_verts",
".corner_vert",
".corner_edge"});
1283 Array<int> new_vert_indices(extrude_corner_size);
1284 Array<int> duplicate_edge_indices(extrude_corner_size);
1287 const IndexRange extrude_range = group_per_face[i_selection];
1294 const int i_extrude = extrude_range[
i];
1295 new_vert_indices[i_extrude] = face_verts[
i];
1296 duplicate_edge_indices[i_extrude] = face_edges[
i];
1298 face_verts[
i] = new_vert_range[i_extrude];
1299 face_edges[
i] = duplicate_edge_range[i_extrude];
1303 const int i_next = (
i == face.
size() - 1) ? 0 :
i + 1;
1304 const int i_extrude = extrude_range[
i];
1305 const int i_extrude_next = extrude_range[i_next];
1307 const int i_duplicate_edge = duplicate_edge_range[i_extrude];
1308 const int new_vert = new_vert_range[i_extrude];
1309 const int new_vert_next = new_vert_range[i_extrude_next];
1311 const int orig_edge = duplicate_edge_indices[i_extrude];
1313 const int orig_vert = new_vert_indices[i_extrude];
1314 const int orig_vert_next = new_vert_indices[i_extrude_next];
1316 duplicate_edges[i_extrude] =
int2(new_vert, new_vert_next);
1320 side_face_verts[0] = new_vert_next;
1321 side_face_edges[0] = i_duplicate_edge;
1322 side_face_verts[1] = new_vert;
1323 side_face_edges[1] = connect_edge_range[i_extrude];
1324 side_face_verts[2] = orig_vert;
1325 side_face_edges[2] = orig_edge;
1326 side_face_verts[3] = orig_vert_next;
1327 side_face_edges[3] = connect_edge_range[i_extrude_next];
1329 connect_edges[i_extrude] =
int2(orig_vert, new_vert);
1335 mesh, ids_by_domain[
int(AttrDomain::Point)], new_vert_indices, new_vert_range);
1339 ids_by_domain[
int(AttrDomain::Edge)],
1340 duplicate_edge_indices,
1341 duplicate_edge_range);
1344 if (!ids_by_domain[
int(AttrDomain::Edge)].is_empty()) {
1349 const IndexRange extrude_range = group_per_face[i_selection];
1352 const int i_prev = (
i == 0) ? face.
size() - 1 :
i - 1;
1353 const int i_extrude = extrude_range[
i];
1354 const int i_extrude_prev = extrude_range[i_prev];
1355 neighbor_edges[i_extrude] =
int2(duplicate_edge_indices[i_extrude],
1356 duplicate_edge_indices[i_extrude_prev]);
1360 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Edge)]) {
1363 using T = decltype(dummy);
1364 MutableSpan<T> data = attribute.span.typed<T>();
1365 MutableSpan<T> dst = data.slice(connect_edge_range);
1366 threading::parallel_for(dst.index_range(), 1024, [&](const IndexRange range) {
1367 for (const int i : range) {
1368 const int2 neighbors = neighbor_edges[i];
1369 if constexpr (std::is_same_v<T, bool>) {
1371 dst[i] = data[neighbors[0]] || data[neighbors[1]];
1374 dst[i] = bke::attribute_math::mix2(0.5f, data[neighbors[0]], data[neighbors[1]]);
1387 group_per_face, face_selection, attribute.
span, attribute.
span.
slice(side_face_range));
1392 if (!ids_by_domain[
int(AttrDomain::Corner)].is_empty()) {
1393 Array<int> orig_corners(side_loop_range.size());
1394 face_selection.foreach_index(
1397 const IndexRange extrude_range = group_per_face[i_selection];
1400 const IndexRange side_face(extrude_range[
i] * 4, 4);
1404 const int corner = face[
i];
1406 orig_corners[side_face[0]] = next_corner;
1407 orig_corners[side_face[1]] = corner;
1408 orig_corners[side_face[2]] = corner;
1409 orig_corners[side_face[3]] = next_corner;
1413 attributes, ids_by_domain[
int(AttrDomain::Corner)], orig_corners, side_loop_range);
1417 face_selection.foreach_index(
GrainSize(1025),
1419 const IndexRange extrude_range = group_per_face[i_selection];
1420 for (
const int i : extrude_range) {
1421 const int src_vert = new_vert_indices[
i];
1422 new_positions[
i] = positions[src_vert] + face_offset[index];
1427 array_utils::gather(
1428 indices->as_span(), new_vert_indices.as_span(),
indices->slice(new_vert_range));
1432 array_utils::gather(
indices->as_span(),
1433 duplicate_edge_indices.as_span(),
1434 indices->slice(duplicate_edge_range));
1437 array_utils::gather_to_groups(
1438 group_per_face, face_selection,
indices->as_span(),
indices->slice(side_face_range));
1441 if (attribute_outputs.top_id) {
1443 attributes, *attribute_outputs.top_id, AttrDomain::Face, face_selection);
1445 if (attribute_outputs.side_id) {
1447 attributes, *attribute_outputs.side_id, AttrDomain::Face, side_face_range);
1464 static auto multiply_fn = mf::build::SI2_SO<float3, float, float3>(
1466 [](
const float3 &offset,
const float scale) {
return offset * scale; },
1467 mf::build::exec_presets::AllSpanOrSingle());
1472 attribute_outputs.
top_id =
params.get_output_anonymous_attribute_id_if_needed(
"Top");
1473 attribute_outputs.
side_id =
params.get_output_anonymous_attribute_id_if_needed(
"Side");
1476 params.extract_input<
bool>(
"Individual");
1486 *mesh, selection, final_offset, attribute_outputs, attribute_filter);
1489 extrude_mesh_edges(*mesh, selection, final_offset, attribute_outputs, attribute_filter);
1492 if (extrude_individual) {
1494 *mesh, selection, final_offset, attribute_outputs, attribute_filter);
1498 *mesh, selection, final_offset, attribute_outputs, attribute_filter);
1508 params.set_output(
"Mesh", std::move(geometry_set));
1517 {0,
nullptr, 0,
nullptr,
nullptr},
1533 ntype.
ui_name =
"Extrude Mesh";
1535 "Generate new vertices, edges, or faces from selected elements and move them based on an "
1536 "offset while keeping them connected by their boundary";
CustomData interface, see also DNA_customdata_types.h.
void CustomData_realloc(CustomData *data, int old_size, int new_size, eCDAllocType alloctype=CD_CONSTRUCT)
void CustomData_free_layers(CustomData *data, eCustomDataType type)
void * CustomData_get_layer_for_write(CustomData *data, eCustomDataType type, int totelem)
void BKE_mesh_runtime_clear_cache(Mesh *mesh)
#define NODE_STORAGE_FUNCS(StorageT)
#define NODE_CLASS_GEOMETRY
#define GEO_NODE_EXTRUDE_MESH
#define BLI_assert_unreachable()
#define LISTBASE_FOREACH(type, var, list)
#define BLT_I18NCONTEXT_ID_NODETREE
@ NODE_DEFAULT_INPUT_NORMAL_FIELD
GeometryNodeExtrudeMeshMode
@ GEO_NODE_EXTRUDE_MESH_FACES
@ GEO_NODE_EXTRUDE_MESH_VERTICES
@ GEO_NODE_EXTRUDE_MESH_EDGES
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_storage_enum_accessors(member)
BMesh const char void * data
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
MutableSpan< T > as_mutable_span()
IndexRange index_range() const
void reinitialize(const int64_t new_size)
GMutableSpan slice(const int64_t start, int64_t size) const
const CPPType & type() const
const CPPType & type() const
constexpr int64_t size() const
constexpr IndexRange after(int64_t n) const
constexpr IndexRange index_range() const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
constexpr bool is_empty() const
constexpr IndexRange index_range() const
bool contains(const Key &key) const
constexpr Span slice(int64_t start, int64_t size) const
constexpr const T & first() const
constexpr int64_t size() const
constexpr IndexRange index_range() const
int64_t index_of_try(const Key &key) const
void reserve(const int64_t n)
int64_t index_of(const Key &key) const
IndexRange index_range() const
void add_new(const Key &key)
void append(const T &value)
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
bool contains(StringRef attribute_id) const
Set< StringRefNull > all_ids() const
int domain_size(const AttrDomain domain) const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
bool remove(const StringRef attribute_id)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
void set_selection(Field< bool > selection)
int add(GField field, GVArray *varray_ptr)
IndexMask get_evaluated_selection_as_mask() const
int add_with_destination(GField field, GVMutableArray dst)
const GVArray & get_evaluated(const int field_index) const
void to_indices(MutableSpan< T > r_indices) const
void foreach_index_optimized(Fn &&fn) const
void to_bools(MutableSpan< bool > r_bools) const
void foreach_index(Fn &&fn) const
void foreach_segment(Fn &&fn) const
static std::shared_ptr< FieldOperation > from(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
void * MEM_callocN(size_t len, const char *str)
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void gather(GSpan src, Span< int > map, GMutableSpan dst)
void gather_to_groups(OffsetIndices< int > dst_offsets, const IndexMask &src_selection, GSpan src, GMutableSpan dst)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
typename DefaultPropagationMixerStruct< T >::type DefaultPropagationMixer
GroupedSpan< int > build_edge_to_face_map(OffsetIndices< int > faces, Span< int > corner_edges, int edges_num, Array< int > &r_offsets, Array< int > &r_indices)
int face_corner_next(const IndexRange face, const int corner)
GroupedSpan< int > build_vert_to_edge_map(Span< int2 > edges, int verts_num, Array< int > &r_offsets, Array< int > &r_indices)
void node_register_type(bNodeType &ntype)
void gather_attributes(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
void gather_deform_verts(Span< MDeformVert > src, Span< int > indices, MutableSpan< MDeformVert > dst)
void node_type_storage(bNodeType &ntype, std::optional< StringRefNull > storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
void foreach_real_geometry(bke::GeometrySet &geometry, FunctionRef< void(bke::GeometrySet &geometry_set)> fn)
void debug_randomize_mesh_order(Mesh *mesh)
IndexMask vert_selection_from_face(OffsetIndices< int > faces, const IndexMask &face_mask, Span< int > corner_verts, int verts_num, IndexMaskMemory &memory)
IndexMask vert_selection_from_edge(Span< int2 > edges, const IndexMask &edge_mask, int verts_num, IndexMaskMemory &memory)
void resize_trivial_array(T **data, const ImplicitSharingInfo **sharing_info, int64_t old_size, int64_t new_size)
void build_reverse_map(const IndexMask &mask, MutableSpan< T > r_map)
static void remove_unsupported_corner_data(Mesh &mesh)
static void node_declare(NodeDeclarationBuilder &b)
static void extrude_individual_mesh_faces(Mesh &mesh, const Field< bool > &selection_field, const Field< float3 > &offset_field, const AttributeOutputs &attribute_outputs, const AttributeFilter &attribute_filter)
static void extrude_mesh_face_regions(Mesh &mesh, const Field< bool > &selection_field, const Field< float3 > &offset_field, const AttributeOutputs &attribute_outputs, const AttributeFilter &attribute_filter)
static void extrude_mesh_edges(Mesh &mesh, const Field< bool > &selection_field, const Field< float3 > &offset_field, const AttributeOutputs &attribute_outputs, const AttributeFilter &attribute_filter)
std::array< Vector< StringRef >, ATTR_DOMAIN_NUM > IDsByDomain
static void gather_vert_attributes(Mesh &mesh, const Span< StringRef > ids, const Span< int > indices, const IndexRange new_range)
static IDsByDomain attribute_ids_by_domain(const AttributeAccessor attributes, const Set< StringRef > &skip)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static VectorSet< int > vert_indices_from_edges(const Mesh &mesh, const Span< int > edge_indices)
static void remove_unsupported_vert_data(Mesh &mesh)
static bool is_empty_domain(const AttributeAccessor attributes, const Set< StringRef > &skip, const AttrDomain domain)
static void node_register()
static CustomData & mesh_custom_data_for_domain(Mesh &mesh, const AttrDomain domain)
static void extrude_mesh_vertices(Mesh &mesh, const Field< bool > &selection_field, const Field< float3 > &offset_field, const AttributeOutputs &attribute_outputs, const AttributeFilter &attribute_filter)
static void remove_non_propagated_attributes(MutableAttributeAccessor attributes, const AttributeFilter &attribute_filter)
static void node_rna(StructRNA *srna)
static std::optional< MutableSpan< int > > get_orig_index_layer(Mesh &mesh, const AttrDomain domain)
static void save_selection_as_attribute(MutableAttributeAccessor attributes, const StringRef id, const AttrDomain domain, const IndexMask &selection)
static void fill_quad_consistent_direction(const Span< int > other_face_verts, const Span< int > other_face_edges, MutableSpan< int > new_corner_verts, MutableSpan< int > new_corner_edges, const int vert_connected_to_face_1, const int vert_connected_to_face_2, const int vert_across_from_face_1, const int vert_across_from_face_2, const int edge_connected_to_face, const int connecting_edge_1, const int edge_across_from_face, const int connecting_edge_2)
static void gather_attributes(MutableAttributeAccessor attributes, const Span< StringRef > ids, const Span< int > indices, const IndexRange new_range)
static void node_geo_exec(GeoNodeExecParams params)
void copy_with_mixing(const Span< T > src, const GroupedSpan< int > src_groups, const IndexMask &selection, MutableSpan< T > dst)
static void tag_mesh_added_faces(Mesh &mesh)
static void expand_mesh(Mesh &mesh, const int vert_expand, const int edge_expand, const int face_expand, const int loop_expand)
static GroupedSpan< int > build_vert_to_edge_map(const Span< int2 > edges, const IndexMask &edge_mask, const int verts_num, Array< int > &r_offsets, Array< int > &r_indices)
static void node_init(bNodeTree *, bNode *node)
PropertyRNA * RNA_def_node_enum(StructRNA *srna, const char *identifier, const char *ui_name, const char *ui_description, const EnumPropertyItem *static_items, const EnumRNAAccessors accessors, std::optional< int > default_value, const EnumPropertyItemFunc item_func, const bool allow_animation)
void fill_constant_group_size(int size, int start_offset, MutableSpan< int > offsets)
OffsetIndices< int > gather_selected_offsets(OffsetIndices< int > src_offsets, const IndexMask &selection, int start_offset, MutableSpan< int > dst_offsets)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
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)
void node_free_standard_storage(bNode *node)
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
MeshRuntimeHandle * runtime
ListBase vertex_group_names
int * face_offset_indices
bool allow_skip(const StringRef name) const
Mesh * get_mesh_for_write()
MutableVArraySpan< T > span
std::string ui_description
void(* initfunc)(bNodeTree *ntree, bNode *node)
NodeGeometryExecFunction geometry_node_execute
const char * enum_name_legacy
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
NodeDeclareFunction declare
std::optional< std::string > top_id
std::optional< std::string > side_id
void use_property_decorate_set(bool is_sep)
void use_property_split_set(bool value)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)