575 edge_evaluator.set_selection(selection_field);
576 edge_evaluator.add(offset_field);
577 edge_evaluator.evaluate();
578 const IndexMask edge_selection = edge_evaluator.get_evaluated_selection_as_mask();
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);
600 const IndexMask new_verts = geometry::vert_selection_from_edge(
601 orig_edges, edge_selection, orig_vert_size, memory);
603 const IndexRange new_vert_range{orig_vert_size, new_verts.
size()};
605 const IndexRange connect_edge_range{orig_edges.
size(), new_vert_range.size()};
611 const IndexRange new_loop_range{orig_loop_size, new_face_range.
size() * 4};
618 const GroupedSpan<int> edge_to_face_map = bke::mesh::build_edge_to_face_map(
619 orig_faces,
mesh.corner_edges(),
mesh.
edges_num, edge_to_face_offsets, edge_to_face_indices);
625 vert_to_selected_edge_map = build_vert_to_edge_map(
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"});
652 offset_indices::fill_constant_group_size(4, orig_loop_size, new_face_offsets);
656 connect_edges[dst] =
int2(src, new_vert_range[dst]);
661 index_mask::build_reverse_map<int>(new_verts, vert_to_new_vert);
662 for (
const int i : duplicate_edges.
index_range()) {
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) {
686 const IndexRange connected_face = faces[connected_faces.first()];
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)]) {
733 bke::attribute_math::convert_to_static_type(attribute.
span.
type(), [&](
auto dummy) {
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);
853 const int orig_loop_size = orig_corner_verts.
size();
857 face_evaluator.set_selection(selection_field);
858 face_evaluator.add(offset_field);
859 face_evaluator.evaluate();
860 const IndexMask face_selection = face_evaluator.get_evaluated_selection_as_mask();
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);
888 const GroupedSpan<int> edge_to_face_map = bke::mesh::build_edge_to_face_map(
889 orig_faces,
mesh.corner_edges(),
mesh.
edges_num, edge_to_face_offsets, edge_to_face_indices);
893 const IndexMask all_selected_verts = geometry::vert_selection_from_face(
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"});
997 offset_indices::fill_constant_group_size(4, orig_loop_size, new_face_offsets);
1003 connect_edges[i] =
int2(new_vert_indices[i], new_vert_range[i]);
1007 for (
const int i : boundary_edges.
index_range()) {
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);
1084 gather_attributes(attributes,
1085 ids_by_domain[
int(AttrDomain::Face)],
1086 edge_extruded_face_indices,
1089 if (!ids_by_domain[
int(AttrDomain::Edge)].is_empty()) {
1091 const IndexMask boundary_edge_mask = IndexMask::from_indices<int>(boundary_edge_indices,
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)]) {
1104 array_utils::gather(attribute.
span, boundary_edge_mask, boundary_data);
1108 bke::attribute_math::gather(attribute.
span, new_inner_edge_indices, new_inner_data);
1112 vert_to_boundary_edge_map,
1114 attribute.
span.
slice(connect_edge_range));
1120 if (!ids_by_domain[
int(AttrDomain::Corner)].is_empty()) {
1121 Array<int> orig_corners(side_loop_range.size());
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);
1231 const int orig_loop_size = orig_corner_verts.
size();
1238 face_evaluator.set_selection(selection_field);
1239 face_evaluator.add_with_destination(offset_field, face_offset.
as_mutable_span());
1240 face_evaluator.evaluate();
1241 const IndexMask face_selection = face_evaluator.get_evaluated_selection_as_mask();
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"});
1289 offset_indices::fill_constant_group_size(4, orig_loop_size, new_face_offsets);
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);
1353 gather_attributes(attributes,
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()) {
1360 Array<int2> neighbor_edges(connect_edge_range.size());
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)]) {
1377 bke::attribute_math::convert_to_static_type(attribute.
span.
type(), [&](
auto dummy) {
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]]);
1399 for (
const StringRef id : ids_by_domain[int(AttrDomain::Face)]) {
1401 bke::attribute_math::gather_to_groups(
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];
1414 for (
const int i : face.index_range()) {
1415 const IndexRange side_face(extrude_range[i] * 4, 4);
1419 const int corner = face[i];
1420 const int next_corner = bke::mesh::face_corner_next(face, corner);
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);