35 b.add_input<
decl::Geometry>(
"Mesh").supported_type(GeometryComponent::Type::Mesh);
50 const bNode *node =
b.node_or_null();
51 if (node !=
nullptr) {
96 names_to_remove.
append(name);
99 for (
const StringRef id : names_to_remove) {
132 const int vert_expand,
133 const int edge_expand,
134 const int face_expand,
135 const int loop_expand)
138 if (vert_expand != 0) {
139 const int old_verts_num = mesh.
verts_num;
143 if (edge_expand != 0) {
145 mesh.attributes_for_write().add(
148 const int old_edges_num = mesh.
edges_num;
152 if (face_expand != 0) {
153 const int old_faces_num = mesh.
faces_num;
157 &mesh.
runtime->face_offsets_sharing_info,
158 old_faces_num == 0 ? 0 : (old_faces_num + 1),
164 if (loop_expand != 0) {
166 mesh.attributes_for_write().add(
168 mesh.attributes_for_write().add(
180 case AttrDomain::Point:
182 case AttrDomain::Edge:
184 case AttrDomain::Face:
186 case AttrDomain::Corner:
214 const IndexRange dst_range(segment_pos, segment.size());
216 for (
const int i : segment.index_range()) {
217 for (
const int src_i : src_groups[segment[i]]) {
218 mixer.mix_in(i, src[src_i]);
232 using T = decltype(dummy);
233 copy_with_mixing(src.typed<T>(), src_groups, selection, dst.typed<T>());
244 bke::attribute_math::DefaultPropagationMixer<T> mixer{dst.slice(range)};
246 const int group_i = selection[i];
247 for (const int i_src : src_groups[group_i]) {
248 mixer.mix_in(i, src[i_src]);
261 using T = decltype(dummy);
262 copy_with_mixing(src.typed<T>(), src_groups, selection, dst.typed<T>());
281 return ids_by_domain;
288 bool is_empty =
true;
293 if (iter.
domain != domain) {
336 vertex_group_names.
add(group->name);
339 if (!vertex_group_names.
is_empty() && !mesh.deform_verts().is_empty()) {
346 if (!vertex_group_names.
contains(
id)) {
361 vertex_group_names.
add(group->name);
364 if (!vertex_group_names.
is_empty() && !mesh.deform_verts().is_empty()) {
371 if (!vertex_group_names.
contains(
id)) {
385 const int orig_vert_size = mesh.
verts_num;
386 const int orig_edge_size = mesh.
edges_num;
405 {
"position",
".edge_verts"});
410 if (!ids_by_domain[
int(AttrDomain::Edge)].is_empty()) {
412 mesh.edges(), orig_vert_size, vert_to_edge_offsets, vert_to_edge_indices);
424 GrainSize(4096), [&](
const int index,
const int i_selection) {
425 new_edges[i_selection] =
int2(index, new_vert_range[i_selection]);
432 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Edge)]) {
441 selection.foreach_index_optimized<
int>(
GrainSize(1024), [&](
const int index,
const int i) {
442 new_positions[i] = positions[index] + offsets[index];
452 if (attribute_outputs.
top_id) {
454 attributes, *attribute_outputs.
top_id, AttrDomain::Point, new_vert_range);
456 if (attribute_outputs.
side_id) {
458 attributes, *attribute_outputs.
side_id, AttrDomain::Edge, new_edge_range);
461 const bool no_loose_vert_hint = mesh.
runtime->loose_verts_cache.is_cached() &&
462 mesh.
runtime->loose_verts_cache.data().count == 0;
463 const bool no_overlapping_hint = mesh.no_overlapping_topology();
465 if (no_loose_vert_hint) {
466 mesh.tag_loose_verts_none();
468 if (no_overlapping_hint) {
469 mesh.tag_overlapping_none();
477 const int vert_connected_to_face_1,
478 const int vert_connected_to_face_2,
479 const int vert_across_from_face_1,
480 const int vert_across_from_face_2,
481 const int edge_connected_to_face,
482 const int connecting_edge_1,
483 const int edge_across_from_face,
484 const int connecting_edge_2)
487 bool start_with_connecting_edge =
true;
488 for (
const int i : other_face_edges.
index_range()) {
489 if (other_face_edges[i] == edge_connected_to_face) {
490 start_with_connecting_edge = other_face_verts[i] == vert_connected_to_face_1;
494 if (start_with_connecting_edge) {
495 new_corner_verts[0] = vert_connected_to_face_1;
496 new_corner_edges[0] = connecting_edge_1;
497 new_corner_verts[1] = vert_across_from_face_1;
498 new_corner_edges[1] = edge_across_from_face;
499 new_corner_verts[2] = vert_across_from_face_2;
500 new_corner_edges[2] = connecting_edge_2;
501 new_corner_verts[3] = vert_connected_to_face_2;
502 new_corner_edges[3] = edge_connected_to_face;
505 new_corner_verts[0] = vert_connected_to_face_1;
506 new_corner_edges[0] = edge_connected_to_face;
507 new_corner_verts[1] = vert_connected_to_face_2;
508 new_corner_edges[1] = connecting_edge_2;
509 new_corner_verts[2] = vert_across_from_face_2;
510 new_corner_edges[2] = edge_across_from_face;
511 new_corner_verts[3] = vert_across_from_face_1;
512 new_corner_edges[3] = connecting_edge_1;
522 if (edge_mask.
size() == edges.
size()) {
531 edge_mask.
to_indices<
int>(masked_edge_to_edge);
534 for (const int i : range) {
535 r_indices[i] = masked_edge_to_edge[r_indices[i]];
539 return {r_offsets.as_span(), r_indices.as_span()};
543 const bool no_loose_vert_hint = mesh.
runtime->loose_verts_cache.is_cached() &&
544 mesh.
runtime->loose_verts_cache.data().count == 0;
545 const bool no_loose_edge_hint = mesh.
runtime->loose_edges_cache.is_cached() &&
546 mesh.
runtime->loose_edges_cache.data().count == 0;
547 const bool no_overlapping_hint = mesh.no_overlapping_topology();
549 if (no_loose_vert_hint) {
550 mesh.tag_loose_verts_none();
552 if (no_loose_edge_hint) {
553 mesh.tag_loose_edges_none();
555 if (no_overlapping_hint) {
556 mesh.tag_overlapping_none();
566 const int orig_vert_size = mesh.
verts_num;
574 edge_evaluator.
add(offset_field);
589 const int2 edge = orig_edges[i_edge];
590 const float3 offset = edge_offsets[i_edge];
591 mixer.mix_in(edge[0], offset);
592 mixer.mix_in(edge[1], offset);
599 orig_edges, edge_selection, orig_vert_size, memory);
601 const IndexRange new_vert_range{orig_vert_size, new_verts.
size()};
609 const IndexRange new_loop_range{orig_loop_size, new_face_range.
size() * 4};
617 orig_faces, mesh.corner_edges(), mesh.
edges_num, edge_to_face_offsets, edge_to_face_indices);
624 orig_edges, edge_selection, orig_vert_size, vert_to_edge_offsets, vert_to_edge_indices);
632 new_vert_range.
size(),
633 connect_edge_range.
size() + duplicate_edge_range.
size(),
634 new_face_range.
size(),
635 new_loop_range.
size());
638 attributes, {
"position",
".edge_verts",
".corner_vert",
".corner_edge"});
654 connect_edges[dst] =
int2(src, new_vert_range[dst]);
660 for (
const int i : duplicate_edges.
index_range()) {
661 const int2 orig_edge = edges[edge_selection[i]];
662 const int i_new_vert_1 = vert_to_new_vert[orig_edge[0]];
663 const int i_new_vert_2 = vert_to_new_vert[orig_edge[1]];
664 duplicate_edges[i] =
int2(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
669 const int2 duplicate_edge = duplicate_edges[i];
670 const int new_vert_1 = duplicate_edge[0];
671 const int new_vert_2 = duplicate_edge[1];
672 const int extrude_index_1 = new_vert_1 - orig_vert_size;
673 const int extrude_index_2 = new_vert_2 - orig_vert_size;
675 const int2 orig_edge = edges[orig_edge_index];
676 const Span<int> connected_faces = edge_to_face_map[orig_edge_index];
683 if (connected_faces.
size() == 1) {
685 connected_face_verts = corner_verts.
slice(connected_face);
686 connected_face_edges = corner_edges.
slice(connected_face);
689 connected_face_edges,
690 new_corner_verts.
slice(4 * i, 4),
691 new_corner_edges.
slice(4 * i, 4),
697 connect_edge_range[extrude_index_1],
698 duplicate_edge_range[i],
699 connect_edge_range[extrude_index_2]);
707 attributes, ids_by_domain[
int(AttrDomain::Edge)], edge_selection, duplicate_edge_range);
710 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Edge)]) {
713 vert_to_selected_edge_map,
715 attribute.span.slice(connect_edge_range));
720 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Face)]) {
723 attribute.span, edge_to_face_map, edge_selection,
attribute.span.slice(new_face_range));
729 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Corner)]) {
732 using T = decltype(dummy);
733 MutableSpan<T> data = attribute.span.typed<T>();
734 MutableSpan<T> new_data = data.slice(new_loop_range);
735 edge_selection.foreach_index(
736 GrainSize(256), [&](const int64_t orig_edge_index, const int64_t i_edge_selection) {
737 const Span<int> connected_faces = edge_to_face_map[orig_edge_index];
738 if (connected_faces.is_empty()) {
740 new_data.slice(4 * i_edge_selection, 4).fill(T());
746 Array<T> side_face_corner_data(2);
747 bke::attribute_math::DefaultPropagationMixer<T> mixer{side_face_corner_data};
749 const int new_vert_1 = duplicate_edges[i_edge_selection][0];
750 const int new_vert_2 = duplicate_edges[i_edge_selection][1];
751 const int orig_vert_1 = edges[orig_edge_index][0];
752 const int orig_vert_2 = edges[orig_edge_index][1];
756 for (const int connected_face : connected_faces) {
757 for (const int i_loop : faces[connected_face]) {
758 if (corner_verts[i_loop] == orig_vert_1) {
759 mixer.mix_in(0, data[i_loop]);
761 if (corner_verts[i_loop] == orig_vert_2) {
762 mixer.mix_in(1, data[i_loop]);
772 for (const int i : IndexRange(4 * i_edge_selection, 4)) {
773 if (ELEM(new_corner_verts[i], new_vert_1, orig_vert_1)) {
774 new_data[i] = side_face_corner_data.first();
776 else if (ELEM(new_corner_verts[i], new_vert_2, orig_vert_2)) {
777 new_data[i] = side_face_corner_data.last();
788 if (edge_offsets.is_single()) {
789 const float3 offset = edge_offsets.get_internal_single();
790 new_verts.foreach_index_optimized<
int>(
GrainSize(1024), [&](
const int src,
const int dst) {
791 new_positions[dst] = positions[src] + offset;
795 new_verts.foreach_index_optimized<
int>(
GrainSize(1024), [&](
const int src,
const int dst) {
796 new_positions[dst] = positions[src] + vert_offsets[src];
801 array_utils::gather(
indices->as_span(), new_verts,
indices->slice(new_vert_range));
805 array_utils::gather(
indices->as_span(), edge_selection,
indices->slice(duplicate_edge_range));
811 if (attribute_outputs.top_id) {
813 attributes, *attribute_outputs.top_id, AttrDomain::Edge, duplicate_edge_range);
815 if (attribute_outputs.side_id) {
817 attributes, *attribute_outputs.side_id, AttrDomain::Face, new_face_range);
829 for (
const int i_edge : edge_indices) {
830 const int2 &edge = edges[i_edge];
831 vert_indices.
add(edge[0]);
832 vert_indices.
add(edge[1]);
847 const int orig_vert_size = mesh.
verts_num;
850 const Span<int> orig_corner_verts = mesh.corner_verts();
851 const int orig_loop_size = orig_corner_verts.
size();
856 face_evaluator.
add(offset_field);
865 face_selection.
to_bools(face_selection_array);
871 if (!face_position_offsets.
is_single()) {
875 const float3 offset = face_position_offsets[i_face];
876 for (
const int vert : orig_corner_verts.
slice(orig_faces[i_face])) {
877 mixer.mix_in(vert, offset);
887 orig_faces, mesh.corner_edges(), mesh.
edges_num, edge_to_face_offsets, edge_to_face_indices);
892 orig_faces, face_selection, orig_corner_verts, orig_vert_size, memory);
905 for (
const int i_edge : orig_edges.
index_range()) {
908 int i_selected_face = -1;
909 int deselected_face_count = 0;
910 int selected_face_count = 0;
911 for (
const int i_other_face :
faces) {
912 if (face_selection_array[i_other_face]) {
913 selected_face_count++;
914 i_selected_face = i_other_face;
917 deselected_face_count++;
921 if (selected_face_count == 1) {
924 boundary_edge_indices.
add_new(i_edge);
925 edge_extruded_face_indices.
append(i_selected_face);
927 else if (selected_face_count > 1) {
929 if (deselected_face_count > 0) {
931 new_inner_edge_indices.
add_new(i_edge);
936 inner_edge_indices.
append(i_edge);
944 const int extruded_vert_size = new_vert_indices.
size();
947 for (
const int i_edge : new_inner_edge_indices) {
948 const int2 &edge = orig_edges[i_edge];
949 new_vert_indices.
add(edge[0]);
950 new_vert_indices.
add(edge[1]);
954 const IndexRange new_vert_range{orig_vert_size, new_vert_indices.
size()};
956 const IndexRange connect_edge_range{orig_edges.
size(), extruded_vert_size};
958 const IndexRange boundary_edge_range = connect_edge_range.
after(boundary_edge_indices.
size());
960 const IndexRange new_inner_edge_range = boundary_edge_range.
after(new_inner_edge_indices.
size());
964 const IndexRange side_loop_range{orig_corner_verts.
size(), side_face_range.
size() * 4};
974 new_vert_range.
size(),
975 connect_edge_range.
size() + boundary_edge_range.
size() + new_inner_edge_range.
size(),
976 side_face_range.
size(),
977 side_loop_range.
size());
980 attributes, {
".corner_vert",
".corner_edge",
".edge_verts"});
1001 connect_edges[i] =
int2(new_vert_indices[i], new_vert_range[i]);
1005 for (
const int i : boundary_edges.
index_range()) {
1006 const int2 &orig_edge = edges[boundary_edge_indices[i]];
1007 const int i_new_vert_1 = new_vert_indices.
index_of(orig_edge[0]);
1008 const int i_new_vert_2 = new_vert_indices.
index_of(orig_edge[1]);
1009 boundary_edges[i] =
int2(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
1013 for (
const int i : new_inner_edge_indices.
index_range()) {
1014 const int2 &orig_edge = edges[new_inner_edge_indices[i]];
1015 const int i_new_vert_1 = new_vert_indices.
index_of(orig_edge[0]);
1016 const int i_new_vert_2 = new_vert_indices.
index_of(orig_edge[1]);
1017 new_inner_edges[i] =
int2(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
1021 for (
const int i : inner_edge_indices) {
1022 int2 &edge = edges[i];
1023 const int i_new_vert_1 = new_vert_indices.
index_of_try(edge[0]);
1024 const int i_new_vert_2 = new_vert_indices.
index_of_try(edge[1]);
1025 if (i_new_vert_1 != -1) {
1026 edge[0] = new_vert_range[i_new_vert_1];
1028 if (i_new_vert_2 != -1) {
1029 edge[1] = new_vert_range[i_new_vert_2];
1035 for (
const int corner :
faces[i_face]) {
1036 const int i_new_vert = new_vert_indices.
index_of_try(corner_verts[corner]);
1037 if (i_new_vert != -1) {
1038 corner_verts[corner] = new_vert_range[i_new_vert];
1040 const int i_boundary_edge = boundary_edge_indices.
index_of_try(corner_edges[corner]);
1041 if (i_boundary_edge != -1) {
1042 corner_edges[corner] = boundary_edge_range[i_boundary_edge];
1046 const int i_new_inner_edge = new_inner_edge_indices.
index_of_try(corner_edges[corner]);
1047 if (i_new_inner_edge != -1) {
1048 corner_edges[corner] = new_inner_edge_range[i_new_inner_edge];
1054 for (
const int i : boundary_edge_indices.
index_range()) {
1055 const int2 &boundary_edge = boundary_edges[i];
1056 const int new_vert_1 = boundary_edge[0];
1057 const int new_vert_2 = boundary_edge[1];
1058 const int extrude_index_1 = new_vert_1 - orig_vert_size;
1059 const int extrude_index_2 = new_vert_2 - orig_vert_size;
1064 corner_edges.
slice(extrude_face),
1065 new_corner_verts.
slice(4 * i, 4),
1066 new_corner_edges.
slice(4 * i, 4),
1069 new_vert_indices[extrude_index_1],
1070 new_vert_indices[extrude_index_2],
1071 boundary_edge_range[i],
1072 connect_edge_range[extrude_index_1],
1073 boundary_edge_indices[i],
1074 connect_edge_range[extrude_index_2]);
1079 mesh, ids_by_domain[
int(AttrDomain::Point)], new_vert_indices, new_vert_range);
1083 ids_by_domain[
int(AttrDomain::Face)],
1084 edge_extruded_face_indices,
1087 if (!ids_by_domain[
int(AttrDomain::Edge)].is_empty()) {
1095 edges, boundary_edge_mask, mesh.
verts_num, vert_to_edge_offsets, vert_to_edge_indices);
1097 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Edge)]) {
1110 vert_to_boundary_edge_map,
1112 attribute.span.slice(connect_edge_range));
1118 if (!ids_by_domain[
int(AttrDomain::Corner)].is_empty()) {
1121 for (const int i_boundary_edge : range) {
1122 const int2 &boundary_edge = boundary_edges[i_boundary_edge];
1123 const int new_vert_1 = boundary_edge[0];
1124 const int new_vert_2 = boundary_edge[1];
1125 const int orig_vert_1 = new_vert_indices[new_vert_1 - orig_vert_size];
1126 const int orig_vert_2 = new_vert_indices[new_vert_2 - orig_vert_size];
1135 for (const int corner : faces[edge_extruded_face_indices[i_boundary_edge]]) {
1136 if (corner_verts[corner] == new_vert_1) {
1139 if (corner_verts[corner] == new_vert_2) {
1147 for (const int i : IndexRange(4 * i_boundary_edge, 4)) {
1148 if (ELEM(new_corner_verts[i], new_vert_1, orig_vert_1)) {
1149 orig_corners[i] = corner_1;
1151 else if (ELEM(new_corner_verts[i], new_vert_2, orig_vert_2)) {
1152 orig_corners[i] = corner_2;
1158 attributes, ids_by_domain[
int(AttrDomain::Corner)], orig_corners, side_loop_range);
1165 if (face_position_offsets.is_single()) {
1166 const float3 offset = face_position_offsets.get_internal_single();
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);
1170 positions[orig_vert] += offset;
1173 positions[new_vert_range[i_new]] += offset;
1178 all_selected_verts.foreach_index(
GrainSize(1024), [&](
const int orig_vert) {
1179 const int i_new = new_vert_indices.index_of_try(orig_vert);
1180 const float3 offset = vert_offsets[orig_vert];
1182 positions[orig_vert] += offset;
1185 positions[new_vert_range[i_new]] += offset;
1191 array_utils::gather(
1192 indices->as_span(), new_vert_indices.as_span(),
indices->slice(new_vert_range));
1196 array_utils::gather(
indices->as_span(),
1197 new_inner_edge_indices.as_span(),
1198 indices->slice(new_inner_edge_range));
1199 array_utils::gather(
1200 indices->as_span(), boundary_edge_indices.as_span(),
indices->slice(boundary_edge_range));
1203 array_utils::gather(
1204 indices->as_span(), edge_extruded_face_indices.as_span(),
indices->slice(side_face_range));
1207 if (attribute_outputs.top_id) {
1209 attributes, *attribute_outputs.top_id, AttrDomain::Face, face_selection);
1211 if (attribute_outputs.side_id) {
1213 attributes, *attribute_outputs.side_id, AttrDomain::Face, side_face_range);
1225 const int orig_vert_size = mesh.
verts_num;
1226 const int orig_edge_size = mesh.
edges_num;
1228 const Span<int> orig_corner_verts = mesh.corner_verts();
1229 const int orig_loop_size = orig_corner_verts.
size();
1249 orig_faces, face_selection, group_per_face_data);
1250 const int extrude_corner_size = group_per_face.
total_size();
1252 const IndexRange new_vert_range{orig_vert_size, extrude_corner_size};
1254 const IndexRange connect_edge_range{orig_edge_size, extrude_corner_size};
1256 const IndexRange duplicate_edge_range = connect_edge_range.
after(extrude_corner_size);
1259 const IndexRange side_loop_range{orig_loop_size, side_face_range.
size() * 4};
1269 new_vert_range.
size(),
1270 connect_edge_range.
size() + duplicate_edge_range.
size(),
1271 side_face_range.
size(),
1272 side_loop_range.
size());
1275 attributes, {
"position",
".edge_verts",
".corner_vert",
".corner_edge"});
1296 Array<int> new_vert_indices(extrude_corner_size);
1297 Array<int> duplicate_edge_indices(extrude_corner_size);
1300 const IndexRange extrude_range = group_per_face[i_selection];
1307 const int i_extrude = extrude_range[i];
1308 new_vert_indices[i_extrude] = face_verts[i];
1309 duplicate_edge_indices[i_extrude] = face_edges[i];
1311 face_verts[i] = new_vert_range[i_extrude];
1312 face_edges[i] = duplicate_edge_range[i_extrude];
1316 const int i_next = (i == face.
size() - 1) ? 0 : i + 1;
1317 const int i_extrude = extrude_range[i];
1318 const int i_extrude_next = extrude_range[i_next];
1320 const int i_duplicate_edge = duplicate_edge_range[i_extrude];
1321 const int new_vert = new_vert_range[i_extrude];
1322 const int new_vert_next = new_vert_range[i_extrude_next];
1324 const int orig_edge = duplicate_edge_indices[i_extrude];
1326 const int orig_vert = new_vert_indices[i_extrude];
1327 const int orig_vert_next = new_vert_indices[i_extrude_next];
1329 duplicate_edges[i_extrude] =
int2(new_vert, new_vert_next);
1333 side_face_verts[0] = new_vert_next;
1334 side_face_edges[0] = i_duplicate_edge;
1335 side_face_verts[1] = new_vert;
1336 side_face_edges[1] = connect_edge_range[i_extrude];
1337 side_face_verts[2] = orig_vert;
1338 side_face_edges[2] = orig_edge;
1339 side_face_verts[3] = orig_vert_next;
1340 side_face_edges[3] = connect_edge_range[i_extrude_next];
1342 connect_edges[i_extrude] =
int2(orig_vert, new_vert);
1348 mesh, ids_by_domain[
int(AttrDomain::Point)], new_vert_indices, new_vert_range);
1352 ids_by_domain[
int(AttrDomain::Edge)],
1353 duplicate_edge_indices,
1354 duplicate_edge_range);
1357 if (!ids_by_domain[
int(AttrDomain::Edge)].is_empty()) {
1362 const IndexRange extrude_range = group_per_face[i_selection];
1365 const int i_prev = (i == 0) ? face.
size() - 1 : i - 1;
1366 const int i_extrude = extrude_range[i];
1367 const int i_extrude_prev = extrude_range[i_prev];
1368 neighbor_edges[i_extrude] =
int2(duplicate_edge_indices[i_extrude],
1369 duplicate_edge_indices[i_extrude_prev]);
1373 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Edge)]) {
1376 using T = decltype(dummy);
1377 MutableSpan<T> data = attribute.span.typed<T>();
1378 MutableSpan<T> dst = data.slice(connect_edge_range);
1379 threading::parallel_for(dst.index_range(), 1024, [&](const IndexRange range) {
1380 for (const int i : range) {
1381 const int2 neighbors = neighbor_edges[i];
1382 if constexpr (std::is_same_v<T, bool>) {
1384 dst[i] = data[neighbors[0]] || data[neighbors[1]];
1387 dst[i] = bke::attribute_math::mix2(0.5f, data[neighbors[0]], data[neighbors[1]]);
1400 group_per_face, face_selection,
attribute.span,
attribute.span.slice(side_face_range));
1405 if (!ids_by_domain[
int(AttrDomain::Corner)].is_empty()) {
1406 Array<int> orig_corners(side_loop_range.size());
1407 face_selection.foreach_index(
1410 const IndexRange extrude_range = group_per_face[i_selection];
1413 const IndexRange side_face(extrude_range[i] * 4, 4);
1417 const int corner = face[i];
1419 orig_corners[side_face[0]] = next_corner;
1420 orig_corners[side_face[1]] = corner;
1421 orig_corners[side_face[2]] = corner;
1422 orig_corners[side_face[3]] = next_corner;
1426 attributes, ids_by_domain[
int(AttrDomain::Corner)], orig_corners, side_loop_range);
1430 face_selection.foreach_index(
GrainSize(1025),
1432 const IndexRange extrude_range = group_per_face[i_selection];
1433 for (
const int i : extrude_range) {
1434 const int src_vert = new_vert_indices[i];
1435 new_positions[i] = positions[src_vert] + face_offset[
index];
1440 array_utils::gather(
1441 indices->as_span(), new_vert_indices.as_span(),
indices->slice(new_vert_range));
1445 array_utils::gather(
indices->as_span(),
1446 duplicate_edge_indices.as_span(),
1447 indices->slice(duplicate_edge_range));
1450 array_utils::gather_to_groups(
1451 group_per_face, face_selection,
indices->as_span(),
indices->slice(side_face_range));
1454 if (attribute_outputs.top_id) {
1456 attributes, *attribute_outputs.top_id, AttrDomain::Face, face_selection);
1458 if (attribute_outputs.side_id) {
1460 attributes, *attribute_outputs.side_id, AttrDomain::Face, side_face_range);
1477 static auto multiply_fn = mf::build::SI2_SO<float3, float, float3>(
1479 [](
const float3 &offset,
const float scale) {
return offset * scale; },
1480 mf::build::exec_presets::AllSpanOrSingle());
1485 attribute_outputs.
top_id =
params.get_output_anonymous_attribute_id_if_needed(
"Top");
1486 attribute_outputs.
side_id =
params.get_output_anonymous_attribute_id_if_needed(
"Side");
1489 params.extract_input<
bool>(
"Individual");
1499 *mesh,
selection, final_offset, attribute_outputs, attribute_filter);
1505 if (extrude_individual) {
1507 *mesh,
selection, final_offset, attribute_outputs, attribute_filter);
1511 *mesh,
selection, final_offset, attribute_outputs, attribute_filter);
1521 params.set_output(
"Mesh", std::move(geometry_set));
1530 {0,
nullptr, 0,
nullptr,
nullptr},
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, int totelem)
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 GEO_NODE_EXTRUDE_MESH
#define NODE_CLASS_GEOMETRY
#define BLI_assert_unreachable()
#define LISTBASE_FOREACH(type, var, list)
#define BLT_I18NCONTEXT_ID_NODETREE
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)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
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)
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(const Key &key) const
void reserve(const int64_t n)
void add_new(const Key &key)
int64_t index_of_try(const Key &key) const
IndexRange index_range() const
void append(const T &value)
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
Set< StringRefNull > all_ids() const
int domain_size(const AttrDomain domain) const
bool contains(const StringRef attribute_id) 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
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
local_group_size(16, 16) .push_constant(Type b
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
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 gather_attributes(AttributeAccessor src_attributes, AttrDomain src_domain, AttrDomain dst_domain, const AttributeFilter &attribute_filter, const IndexMask &selection, MutableAttributeAccessor dst_attributes)
void node_type_storage(bNodeType *ntype, const char *storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
void node_register_type(bNodeType *ntype)
void gather_deform_verts(Span< MDeformVert > src, Span< int > indices, MutableSpan< MDeformVert > dst)
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, int type, const char *name, short nclass)
void node_free_standard_storage(bNode *node)
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
GPU_SHADER_INTERFACE_INFO(overlay_edit_curve_handle_iface, "vert").flat(Type pos vertex_in(1, Type::UINT, "data") .vertex_out(overlay_edit_curve_handle_iface) .geometry_layout(PrimitiveIn Frequency::GEOMETRY storage_buf(1, Qualifier::READ, "uint", "data[]", Frequency::GEOMETRY) .push_constant(Type Frequency::GEOMETRY selection[]
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()
void(* initfunc)(bNodeTree *ntree, bNode *node)
NodeGeometryExecFunction geometry_node_execute
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
NodeDeclareFunction declare
std::optional< std::string > top_id
std::optional< std::string > side_id