38 b.add_input<
decl::Geometry>(
"Mesh").supported_type(GeometryComponent::Type::Mesh);
53 const bNode *node =
b.node_or_null();
54 if (node !=
nullptr) {
99 names_to_remove.
append(name);
102 for (
const StringRef id : names_to_remove) {
134 const int vert_expand,
135 const int edge_expand,
136 const int face_expand,
137 const int loop_expand)
140 if (vert_expand != 0) {
141 const int old_verts_num = mesh.
verts_num;
145 if (edge_expand != 0) {
147 mesh.attributes_for_write().add(
150 const int old_edges_num = mesh.
edges_num;
154 if (face_expand != 0) {
155 const int old_faces_num = mesh.
faces_num;
159 &mesh.
runtime->face_offsets_sharing_info,
160 old_faces_num == 0 ? 0 : (old_faces_num + 1),
166 if (loop_expand != 0) {
168 mesh.attributes_for_write().add(
170 mesh.attributes_for_write().add(
182 case AttrDomain::Point:
184 case AttrDomain::Edge:
186 case AttrDomain::Face:
188 case AttrDomain::Corner:
216 const IndexRange dst_range(segment_pos, segment.size());
218 for (
const int i : segment.index_range()) {
219 for (
const int src_i : src_groups[segment[
i]]) {
220 mixer.mix_in(
i, src[src_i]);
234 using T = decltype(dummy);
235 copy_with_mixing(src.typed<T>(), src_groups, selection, dst.typed<T>());
246 bke::attribute_math::DefaultPropagationMixer<T> mixer{dst.slice(range)};
248 const int group_i = selection[i];
249 for (const int i_src : src_groups[group_i]) {
250 mixer.mix_in(i, src[i_src]);
263 using T = decltype(dummy);
264 copy_with_mixing(src.typed<T>(), src_groups, selection, dst.typed<T>());
281 ids_by_domain[int(iter.
domain)].append(iter.
name);
283 return ids_by_domain;
290 bool is_empty =
true;
295 if (iter.
domain != domain) {
338 vertex_group_names.
add(group->name);
341 if (!vertex_group_names.
is_empty() && !mesh.deform_verts().is_empty()) {
348 if (!vertex_group_names.
contains(
id)) {
363 vertex_group_names.
add(group->name);
366 if (!vertex_group_names.
is_empty() && !mesh.deform_verts().is_empty()) {
373 if (!vertex_group_names.
contains(
id)) {
387 const int orig_vert_size = mesh.
verts_num;
388 const int orig_edge_size = mesh.
edges_num;
407 {
"position",
".edge_verts"});
412 if (!ids_by_domain[
int(AttrDomain::Edge)].is_empty()) {
414 mesh.edges(), orig_vert_size, vert_to_edge_offsets, vert_to_edge_indices);
421 const IndexRange new_vert_range{orig_vert_size, selection.
size()};
422 const IndexRange new_edge_range{orig_edge_size, selection.
size()};
426 GrainSize(4096), [&](
const int index,
const int i_selection) {
427 new_edges[i_selection] =
int2(index, new_vert_range[i_selection]);
434 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Edge)]) {
437 attribute.
span, vert_to_edge_map, selection, attribute.
span.
slice(new_edge_range));
444 new_positions[
i] = positions[index] + offsets[index];
454 if (attribute_outputs.
top_id) {
456 attributes, *attribute_outputs.
top_id, AttrDomain::Point, new_vert_range);
458 if (attribute_outputs.
side_id) {
460 attributes, *attribute_outputs.
side_id, AttrDomain::Edge, new_edge_range);
463 const bool no_loose_vert_hint = mesh.
runtime->loose_verts_cache.is_cached() &&
464 mesh.
runtime->loose_verts_cache.data().count == 0;
465 const bool no_overlapping_hint = mesh.no_overlapping_topology();
467 if (no_loose_vert_hint) {
468 mesh.tag_loose_verts_none();
470 if (no_overlapping_hint) {
471 mesh.tag_overlapping_none();
479 const int vert_connected_to_face_1,
480 const int vert_connected_to_face_2,
481 const int vert_across_from_face_1,
482 const int vert_across_from_face_2,
483 const int edge_connected_to_face,
484 const int connecting_edge_1,
485 const int edge_across_from_face,
486 const int connecting_edge_2)
489 bool start_with_connecting_edge =
true;
491 if (other_face_edges[
i] == edge_connected_to_face) {
492 start_with_connecting_edge = other_face_verts[
i] == vert_connected_to_face_1;
496 if (start_with_connecting_edge) {
497 new_corner_verts[0] = vert_connected_to_face_1;
498 new_corner_edges[0] = connecting_edge_1;
499 new_corner_verts[1] = vert_across_from_face_1;
500 new_corner_edges[1] = edge_across_from_face;
501 new_corner_verts[2] = vert_across_from_face_2;
502 new_corner_edges[2] = connecting_edge_2;
503 new_corner_verts[3] = vert_connected_to_face_2;
504 new_corner_edges[3] = edge_connected_to_face;
507 new_corner_verts[0] = vert_connected_to_face_1;
508 new_corner_edges[0] = edge_connected_to_face;
509 new_corner_verts[1] = vert_connected_to_face_2;
510 new_corner_edges[1] = connecting_edge_2;
511 new_corner_verts[2] = vert_across_from_face_2;
512 new_corner_edges[2] = edge_across_from_face;
513 new_corner_verts[3] = vert_across_from_face_1;
514 new_corner_edges[3] = connecting_edge_1;
524 if (edge_mask.
size() == edges.
size()) {
533 edge_mask.
to_indices<
int>(masked_edge_to_edge);
536 for (const int i : range) {
537 r_indices[i] = masked_edge_to_edge[r_indices[i]];
541 return {r_offsets.as_span(), r_indices.as_span()};
545 const bool no_loose_vert_hint = mesh.
runtime->loose_verts_cache.is_cached() &&
546 mesh.
runtime->loose_verts_cache.data().count == 0;
547 const bool no_loose_edge_hint = mesh.
runtime->loose_edges_cache.is_cached() &&
548 mesh.
runtime->loose_edges_cache.data().count == 0;
549 const bool no_overlapping_hint = mesh.no_overlapping_topology();
551 if (no_loose_vert_hint) {
552 mesh.tag_loose_verts_none();
554 if (no_loose_edge_hint) {
555 mesh.tag_loose_edges_none();
557 if (no_overlapping_hint) {
558 mesh.tag_overlapping_none();
568 const int orig_vert_size = mesh.
verts_num;
576 edge_evaluator.
add(offset_field);
591 const int2 edge = orig_edges[i_edge];
592 const float3 offset = edge_offsets[i_edge];
593 mixer.mix_in(edge[0], offset);
594 mixer.mix_in(edge[1], offset);
601 orig_edges, edge_selection, orig_vert_size, memory);
603 const IndexRange new_vert_range{orig_vert_size, new_verts.
size()};
611 const IndexRange new_loop_range{orig_loop_size, new_face_range.
size() * 4};
619 orig_faces, mesh.corner_edges(), mesh.
edges_num, edge_to_face_offsets, edge_to_face_indices);
626 orig_edges, edge_selection, orig_vert_size, vert_to_edge_offsets, vert_to_edge_indices);
634 new_vert_range.
size(),
635 connect_edge_range.
size() + duplicate_edge_range.
size(),
636 new_face_range.
size(),
637 new_loop_range.
size());
640 attributes, {
"position",
".edge_verts",
".corner_vert",
".corner_edge"});
656 connect_edges[dst] =
int2(src, new_vert_range[dst]);
663 const int2 orig_edge = edges[edge_selection[
i]];
664 const int i_new_vert_1 = vert_to_new_vert[orig_edge[0]];
665 const int i_new_vert_2 = vert_to_new_vert[orig_edge[1]];
666 duplicate_edges[
i] =
int2(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
671 const int2 duplicate_edge = duplicate_edges[
i];
672 const int new_vert_1 = duplicate_edge[0];
673 const int new_vert_2 = duplicate_edge[1];
674 const int extrude_index_1 = new_vert_1 - orig_vert_size;
675 const int extrude_index_2 = new_vert_2 - orig_vert_size;
677 const int2 orig_edge = edges[orig_edge_index];
678 const Span<int> connected_faces = edge_to_face_map[orig_edge_index];
685 if (connected_faces.
size() == 1) {
687 connected_face_verts = corner_verts.
slice(connected_face);
688 connected_face_edges = corner_edges.
slice(connected_face);
691 connected_face_edges,
692 new_corner_verts.
slice(4 *
i, 4),
693 new_corner_edges.
slice(4 *
i, 4),
699 connect_edge_range[extrude_index_1],
700 duplicate_edge_range[
i],
701 connect_edge_range[extrude_index_2]);
709 attributes, ids_by_domain[
int(AttrDomain::Edge)], edge_selection, duplicate_edge_range);
712 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Edge)]) {
715 vert_to_selected_edge_map,
717 attribute.
span.
slice(connect_edge_range));
722 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Face)]) {
725 attribute.
span, edge_to_face_map, edge_selection, attribute.
span.
slice(new_face_range));
731 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Corner)]) {
734 using T = decltype(dummy);
735 MutableSpan<T> data = attribute.span.typed<T>();
736 MutableSpan<T> new_data = data.slice(new_loop_range);
737 edge_selection.foreach_index(
738 GrainSize(256), [&](const int64_t orig_edge_index, const int64_t i_edge_selection) {
739 const Span<int> connected_faces = edge_to_face_map[orig_edge_index];
740 if (connected_faces.is_empty()) {
742 new_data.slice(4 * i_edge_selection, 4).fill(T());
748 Array<T> side_face_corner_data(2);
749 bke::attribute_math::DefaultPropagationMixer<T> mixer{side_face_corner_data};
751 const int new_vert_1 = duplicate_edges[i_edge_selection][0];
752 const int new_vert_2 = duplicate_edges[i_edge_selection][1];
753 const int orig_vert_1 = edges[orig_edge_index][0];
754 const int orig_vert_2 = edges[orig_edge_index][1];
758 for (const int connected_face : connected_faces) {
759 for (const int i_loop : faces[connected_face]) {
760 if (corner_verts[i_loop] == orig_vert_1) {
761 mixer.mix_in(0, data[i_loop]);
763 if (corner_verts[i_loop] == orig_vert_2) {
764 mixer.mix_in(1, data[i_loop]);
774 for (const int i : IndexRange(4 * i_edge_selection, 4)) {
775 if (ELEM(new_corner_verts[i], new_vert_1, orig_vert_1)) {
776 new_data[i] = side_face_corner_data.first();
778 else if (ELEM(new_corner_verts[i], new_vert_2, orig_vert_2)) {
779 new_data[i] = side_face_corner_data.last();
790 if (edge_offsets.is_single()) {
791 const float3 offset = edge_offsets.get_internal_single();
792 new_verts.foreach_index_optimized<
int>(
GrainSize(1024), [&](
const int src,
const int dst) {
793 new_positions[dst] = positions[src] + offset;
797 new_verts.foreach_index_optimized<
int>(
GrainSize(1024), [&](
const int src,
const int dst) {
798 new_positions[dst] = positions[src] + vert_offsets[src];
803 array_utils::gather(
indices->as_span(), new_verts,
indices->slice(new_vert_range));
807 array_utils::gather(
indices->as_span(), edge_selection,
indices->slice(duplicate_edge_range));
813 if (attribute_outputs.top_id) {
815 attributes, *attribute_outputs.top_id, AttrDomain::Edge, duplicate_edge_range);
817 if (attribute_outputs.side_id) {
819 attributes, *attribute_outputs.side_id, AttrDomain::Face, new_face_range);
831 for (
const int i_edge : edge_indices) {
832 const int2 &edge = edges[i_edge];
833 vert_indices.
add(edge[0]);
834 vert_indices.
add(edge[1]);
849 const int orig_vert_size = mesh.
verts_num;
852 const Span<int> orig_corner_verts = mesh.corner_verts();
853 const int orig_loop_size = orig_corner_verts.
size();
858 face_evaluator.
add(offset_field);
867 face_selection.
to_bools(face_selection_array);
873 if (!face_position_offsets.
is_single()) {
877 const float3 offset = face_position_offsets[i_face];
878 for (
const int vert : orig_corner_verts.
slice(orig_faces[i_face])) {
879 mixer.mix_in(vert, offset);
889 orig_faces, mesh.corner_edges(), mesh.
edges_num, edge_to_face_offsets, edge_to_face_indices);
894 orig_faces, face_selection, orig_corner_verts, orig_vert_size, memory);
907 for (
const int i_edge : orig_edges.
index_range()) {
910 int i_selected_face = -1;
911 int deselected_face_count = 0;
912 int selected_face_count = 0;
913 for (
const int i_other_face :
faces) {
914 if (face_selection_array[i_other_face]) {
915 selected_face_count++;
916 i_selected_face = i_other_face;
919 deselected_face_count++;
923 if (selected_face_count == 1) {
926 boundary_edge_indices.
add_new(i_edge);
927 edge_extruded_face_indices.
append(i_selected_face);
929 else if (selected_face_count > 1) {
931 if (deselected_face_count > 0) {
933 new_inner_edge_indices.
add_new(i_edge);
938 inner_edge_indices.
append(i_edge);
946 const int extruded_vert_size = new_vert_indices.
size();
949 for (
const int i_edge : new_inner_edge_indices) {
950 const int2 &edge = orig_edges[i_edge];
951 new_vert_indices.
add(edge[0]);
952 new_vert_indices.
add(edge[1]);
956 const IndexRange new_vert_range{orig_vert_size, new_vert_indices.
size()};
958 const IndexRange connect_edge_range{orig_edges.
size(), extruded_vert_size};
960 const IndexRange boundary_edge_range = connect_edge_range.
after(boundary_edge_indices.
size());
962 const IndexRange new_inner_edge_range = boundary_edge_range.
after(new_inner_edge_indices.
size());
966 const IndexRange side_loop_range{orig_corner_verts.
size(), side_face_range.
size() * 4};
976 new_vert_range.
size(),
977 connect_edge_range.
size() + boundary_edge_range.
size() + new_inner_edge_range.
size(),
978 side_face_range.
size(),
979 side_loop_range.
size());
982 attributes, {
".corner_vert",
".corner_edge",
".edge_verts"});
1003 connect_edges[
i] =
int2(new_vert_indices[
i], new_vert_range[
i]);
1008 const int2 &orig_edge = edges[boundary_edge_indices[
i]];
1009 const int i_new_vert_1 = new_vert_indices.
index_of(orig_edge[0]);
1010 const int i_new_vert_2 = new_vert_indices.
index_of(orig_edge[1]);
1011 boundary_edges[
i] =
int2(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
1015 for (
const int i : new_inner_edge_indices.
index_range()) {
1016 const int2 &orig_edge = edges[new_inner_edge_indices[
i]];
1017 const int i_new_vert_1 = new_vert_indices.
index_of(orig_edge[0]);
1018 const int i_new_vert_2 = new_vert_indices.
index_of(orig_edge[1]);
1019 new_inner_edges[
i] =
int2(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
1023 for (
const int i : inner_edge_indices) {
1024 int2 &edge = edges[
i];
1025 const int i_new_vert_1 = new_vert_indices.
index_of_try(edge[0]);
1026 const int i_new_vert_2 = new_vert_indices.
index_of_try(edge[1]);
1027 if (i_new_vert_1 != -1) {
1028 edge[0] = new_vert_range[i_new_vert_1];
1030 if (i_new_vert_2 != -1) {
1031 edge[1] = new_vert_range[i_new_vert_2];
1037 for (
const int corner :
faces[i_face]) {
1038 const int i_new_vert = new_vert_indices.
index_of_try(corner_verts[corner]);
1039 if (i_new_vert != -1) {
1040 corner_verts[corner] = new_vert_range[i_new_vert];
1042 const int i_boundary_edge = boundary_edge_indices.
index_of_try(corner_edges[corner]);
1043 if (i_boundary_edge != -1) {
1044 corner_edges[corner] = boundary_edge_range[i_boundary_edge];
1048 const int i_new_inner_edge = new_inner_edge_indices.
index_of_try(corner_edges[corner]);
1049 if (i_new_inner_edge != -1) {
1050 corner_edges[corner] = new_inner_edge_range[i_new_inner_edge];
1056 for (
const int i : boundary_edge_indices.
index_range()) {
1057 const int2 &boundary_edge = boundary_edges[
i];
1058 const int new_vert_1 = boundary_edge[0];
1059 const int new_vert_2 = boundary_edge[1];
1060 const int extrude_index_1 = new_vert_1 - orig_vert_size;
1061 const int extrude_index_2 = new_vert_2 - orig_vert_size;
1066 corner_edges.
slice(extrude_face),
1067 new_corner_verts.
slice(4 *
i, 4),
1068 new_corner_edges.
slice(4 *
i, 4),
1071 new_vert_indices[extrude_index_1],
1072 new_vert_indices[extrude_index_2],
1073 boundary_edge_range[
i],
1074 connect_edge_range[extrude_index_1],
1075 boundary_edge_indices[
i],
1076 connect_edge_range[extrude_index_2]);
1081 mesh, ids_by_domain[
int(AttrDomain::Point)], new_vert_indices, new_vert_range);
1085 ids_by_domain[
int(AttrDomain::Face)],
1086 edge_extruded_face_indices,
1089 if (!ids_by_domain[
int(AttrDomain::Edge)].is_empty()) {
1097 edges, boundary_edge_mask, mesh.
verts_num, vert_to_edge_offsets, vert_to_edge_indices);
1099 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Edge)]) {
1112 vert_to_boundary_edge_map,
1114 attribute.
span.
slice(connect_edge_range));
1120 if (!ids_by_domain[
int(AttrDomain::Corner)].is_empty()) {
1123 for (const int i_boundary_edge : range) {
1124 const int2 &boundary_edge = boundary_edges[i_boundary_edge];
1125 const int new_vert_1 = boundary_edge[0];
1126 const int new_vert_2 = boundary_edge[1];
1127 const int orig_vert_1 = new_vert_indices[new_vert_1 - orig_vert_size];
1128 const int orig_vert_2 = new_vert_indices[new_vert_2 - orig_vert_size];
1137 for (const int corner : faces[edge_extruded_face_indices[i_boundary_edge]]) {
1138 if (corner_verts[corner] == new_vert_1) {
1141 if (corner_verts[corner] == new_vert_2) {
1149 for (const int i : IndexRange(4 * i_boundary_edge, 4)) {
1150 if (ELEM(new_corner_verts[i], new_vert_1, orig_vert_1)) {
1151 orig_corners[i] = corner_1;
1153 else if (ELEM(new_corner_verts[i], new_vert_2, orig_vert_2)) {
1154 orig_corners[i] = corner_2;
1160 attributes, ids_by_domain[
int(AttrDomain::Corner)], orig_corners, side_loop_range);
1167 if (face_position_offsets.is_single()) {
1168 const float3 offset = face_position_offsets.get_internal_single();
1169 all_selected_verts.foreach_index(
GrainSize(1024), [&](
const int orig_vert) {
1170 const int i_new = new_vert_indices.index_of_try(orig_vert);
1172 positions[orig_vert] += offset;
1175 positions[new_vert_range[i_new]] += offset;
1180 all_selected_verts.foreach_index(
GrainSize(1024), [&](
const int orig_vert) {
1181 const int i_new = new_vert_indices.index_of_try(orig_vert);
1182 const float3 offset = vert_offsets[orig_vert];
1184 positions[orig_vert] += offset;
1187 positions[new_vert_range[i_new]] += offset;
1193 array_utils::gather(
1194 indices->as_span(), new_vert_indices.as_span(),
indices->slice(new_vert_range));
1198 array_utils::gather(
indices->as_span(),
1199 new_inner_edge_indices.as_span(),
1200 indices->slice(new_inner_edge_range));
1201 array_utils::gather(
1202 indices->as_span(), boundary_edge_indices.as_span(),
indices->slice(boundary_edge_range));
1205 array_utils::gather(
1206 indices->as_span(), edge_extruded_face_indices.as_span(),
indices->slice(side_face_range));
1209 if (attribute_outputs.top_id) {
1211 attributes, *attribute_outputs.top_id, AttrDomain::Face, face_selection);
1213 if (attribute_outputs.side_id) {
1215 attributes, *attribute_outputs.side_id, AttrDomain::Face, side_face_range);
1227 const int orig_vert_size = mesh.
verts_num;
1228 const int orig_edge_size = mesh.
edges_num;
1230 const Span<int> orig_corner_verts = mesh.corner_verts();
1231 const int orig_loop_size = orig_corner_verts.
size();
1251 orig_faces, face_selection, group_per_face_data);
1252 const int extrude_corner_size = group_per_face.
total_size();
1254 const IndexRange new_vert_range{orig_vert_size, extrude_corner_size};
1256 const IndexRange connect_edge_range{orig_edge_size, extrude_corner_size};
1258 const IndexRange duplicate_edge_range = connect_edge_range.
after(extrude_corner_size);
1261 const IndexRange side_loop_range{orig_loop_size, side_face_range.
size() * 4};
1271 new_vert_range.
size(),
1272 connect_edge_range.
size() + duplicate_edge_range.
size(),
1273 side_face_range.
size(),
1274 side_loop_range.
size());
1277 attributes, {
"position",
".edge_verts",
".corner_vert",
".corner_edge"});
1298 Array<int> new_vert_indices(extrude_corner_size);
1299 Array<int> duplicate_edge_indices(extrude_corner_size);
1302 const IndexRange extrude_range = group_per_face[i_selection];
1309 const int i_extrude = extrude_range[
i];
1310 new_vert_indices[i_extrude] = face_verts[
i];
1311 duplicate_edge_indices[i_extrude] = face_edges[
i];
1313 face_verts[
i] = new_vert_range[i_extrude];
1314 face_edges[
i] = duplicate_edge_range[i_extrude];
1318 const int i_next = (
i == face.
size() - 1) ? 0 :
i + 1;
1319 const int i_extrude = extrude_range[
i];
1320 const int i_extrude_next = extrude_range[i_next];
1322 const int i_duplicate_edge = duplicate_edge_range[i_extrude];
1323 const int new_vert = new_vert_range[i_extrude];
1324 const int new_vert_next = new_vert_range[i_extrude_next];
1326 const int orig_edge = duplicate_edge_indices[i_extrude];
1328 const int orig_vert = new_vert_indices[i_extrude];
1329 const int orig_vert_next = new_vert_indices[i_extrude_next];
1331 duplicate_edges[i_extrude] =
int2(new_vert, new_vert_next);
1335 side_face_verts[0] = new_vert_next;
1336 side_face_edges[0] = i_duplicate_edge;
1337 side_face_verts[1] = new_vert;
1338 side_face_edges[1] = connect_edge_range[i_extrude];
1339 side_face_verts[2] = orig_vert;
1340 side_face_edges[2] = orig_edge;
1341 side_face_verts[3] = orig_vert_next;
1342 side_face_edges[3] = connect_edge_range[i_extrude_next];
1344 connect_edges[i_extrude] =
int2(orig_vert, new_vert);
1350 mesh, ids_by_domain[
int(AttrDomain::Point)], new_vert_indices, new_vert_range);
1354 ids_by_domain[
int(AttrDomain::Edge)],
1355 duplicate_edge_indices,
1356 duplicate_edge_range);
1359 if (!ids_by_domain[
int(AttrDomain::Edge)].is_empty()) {
1364 const IndexRange extrude_range = group_per_face[i_selection];
1367 const int i_prev = (
i == 0) ? face.
size() - 1 :
i - 1;
1368 const int i_extrude = extrude_range[
i];
1369 const int i_extrude_prev = extrude_range[i_prev];
1370 neighbor_edges[i_extrude] =
int2(duplicate_edge_indices[i_extrude],
1371 duplicate_edge_indices[i_extrude_prev]);
1375 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Edge)]) {
1378 using T = decltype(dummy);
1379 MutableSpan<T> data = attribute.span.typed<T>();
1380 MutableSpan<T> dst = data.slice(connect_edge_range);
1381 threading::parallel_for(dst.index_range(), 1024, [&](const IndexRange range) {
1382 for (const int i : range) {
1383 const int2 neighbors = neighbor_edges[i];
1384 if constexpr (std::is_same_v<T, bool>) {
1386 dst[i] = data[neighbors[0]] || data[neighbors[1]];
1389 dst[i] = bke::attribute_math::mix2(0.5f, data[neighbors[0]], data[neighbors[1]]);
1402 group_per_face, face_selection, attribute.
span, attribute.
span.
slice(side_face_range));
1407 if (!ids_by_domain[
int(AttrDomain::Corner)].is_empty()) {
1408 Array<int> orig_corners(side_loop_range.size());
1409 face_selection.foreach_index(
1412 const IndexRange extrude_range = group_per_face[i_selection];
1415 const IndexRange side_face(extrude_range[
i] * 4, 4);
1419 const int corner = face[
i];
1421 orig_corners[side_face[0]] = next_corner;
1422 orig_corners[side_face[1]] = corner;
1423 orig_corners[side_face[2]] = corner;
1424 orig_corners[side_face[3]] = next_corner;
1428 attributes, ids_by_domain[
int(AttrDomain::Corner)], orig_corners, side_loop_range);
1432 face_selection.foreach_index(
GrainSize(1025),
1434 const IndexRange extrude_range = group_per_face[i_selection];
1435 for (
const int i : extrude_range) {
1436 const int src_vert = new_vert_indices[
i];
1437 new_positions[
i] = positions[src_vert] + face_offset[index];
1442 array_utils::gather(
1443 indices->as_span(), new_vert_indices.as_span(),
indices->slice(new_vert_range));
1447 array_utils::gather(
indices->as_span(),
1448 duplicate_edge_indices.as_span(),
1449 indices->slice(duplicate_edge_range));
1452 array_utils::gather_to_groups(
1453 group_per_face, face_selection,
indices->as_span(),
indices->slice(side_face_range));
1456 if (attribute_outputs.top_id) {
1458 attributes, *attribute_outputs.top_id, AttrDomain::Face, face_selection);
1460 if (attribute_outputs.side_id) {
1462 attributes, *attribute_outputs.side_id, AttrDomain::Face, side_face_range);
1479 static auto multiply_fn = mf::build::SI2_SO<float3, float, float3>(
1481 [](
const float3 &offset,
const float scale) {
return offset * scale; },
1482 mf::build::exec_presets::AllSpanOrSingle());
1487 attribute_outputs.
top_id =
params.get_output_anonymous_attribute_id_if_needed(
"Top");
1488 attribute_outputs.
side_id =
params.get_output_anonymous_attribute_id_if_needed(
"Side");
1491 params.extract_input<
bool>(
"Individual");
1501 *mesh, selection, final_offset, attribute_outputs, attribute_filter);
1504 extrude_mesh_edges(*mesh, selection, final_offset, attribute_outputs, attribute_filter);
1507 if (extrude_individual) {
1509 *mesh, selection, final_offset, attribute_outputs, attribute_filter);
1513 *mesh, selection, final_offset, attribute_outputs, attribute_filter);
1523 params.set_output(
"Mesh", std::move(geometry_set));
1532 {0,
nullptr, 0,
nullptr,
nullptr},
1548 ntype.
ui_name =
"Extrude Mesh";
1550 "Generate new vertices, edges, or faces from selected elements and move them based on an "
1551 "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)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
for(;discarded_id_iter !=nullptr;discarded_id_iter=static_cast< ID * >(discarded_id_iter->next))
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
eCustomDataType data_type
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType 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 > Create(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 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 remove_unsupported_face_data(Mesh &mesh)
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)
static void remove_unsupported_edge_data(Mesh &mesh)
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
void modify_geometry_sets(ForeachSubGeometryCallback callback)
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 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)