380 const int orig_vert_size = mesh.verts_num;
381 const int orig_edge_size = mesh.edges_num;
389 evaluator.set_selection(selection_field);
390 evaluator.evaluate();
391 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
392 if (selection.is_empty()) {
400 {
"position",
".edge_verts"});
405 if (!ids_by_domain[
int(AttrDomain::Edge)].is_empty()) {
406 vert_to_edge_map = bke::mesh::build_vert_to_edge_map(
407 mesh.edges(), orig_vert_size, vert_to_edge_offsets, vert_to_edge_indices);
412 expand_mesh(mesh, selection.size(), selection.size(), 0, 0);
414 const IndexRange new_vert_range{orig_vert_size, selection.
size()};
415 const IndexRange new_edge_range{orig_edge_size, selection.
size()};
418 selection.foreach_index_optimized<
int>(
419 GrainSize(4096), [&](
const int index,
const int i_selection) {
420 new_edges[i_selection] =
int2(index, new_vert_range[i_selection]);
427 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Edge)]) {
430 attribute.span, vert_to_edge_map, selection, attribute.span.slice(new_edge_range));
436 selection.foreach_index_optimized<
int>(
GrainSize(1024), [&](
const int index,
const int i) {
437 new_positions[i] = positions[index] + offsets[index];
441 array_utils::gather(indices->as_span(), selection, indices->slice(new_vert_range));
447 if (attribute_outputs.
top_id) {
449 attributes, *attribute_outputs.
top_id, AttrDomain::Point, new_vert_range);
451 if (attribute_outputs.
side_id) {
453 attributes, *attribute_outputs.
side_id, AttrDomain::Edge, new_edge_range);
456 const bool no_loose_vert_hint = mesh.runtime->loose_verts_cache.is_cached() &&
457 mesh.runtime->loose_verts_cache.data().count == 0;
458 const bool no_overlapping_hint = mesh.no_overlapping_topology();
460 if (no_loose_vert_hint) {
461 mesh.tag_loose_verts_none();
463 if (no_overlapping_hint) {
464 mesh.tag_overlapping_none();
561 const int orig_vert_size = mesh.verts_num;
564 const int orig_loop_size = mesh.corners_num;
569 edge_evaluator.add(offset_field);
570 edge_evaluator.evaluate();
571 const IndexMask edge_selection = edge_evaluator.get_evaluated_selection_as_mask();
584 const int2 edge = orig_edges[i_edge];
585 const float3 offset = edge_offsets[i_edge];
586 mixer.mix_in(edge[0], offset);
587 mixer.mix_in(edge[1], offset);
593 const IndexMask new_verts = geometry::vert_selection_from_edge(
594 orig_edges, edge_selection, orig_vert_size, memory);
596 const IndexRange new_vert_range{orig_vert_size, new_verts.
size()};
598 const IndexRange connect_edge_range{orig_edges.
size(), new_vert_range.size()};
604 const IndexRange new_loop_range{orig_loop_size, new_face_range.
size() * 4};
611 const GroupedSpan<int> edge_to_face_map = bke::mesh::build_edge_to_face_map(
612 orig_faces, mesh.corner_edges(), mesh.edges_num, edge_to_face_offsets, edge_to_face_indices);
618 vert_to_selected_edge_map = build_vert_to_edge_map(
619 orig_edges, edge_selection, orig_vert_size, vert_to_edge_offsets, vert_to_edge_indices);
627 new_vert_range.size(),
628 connect_edge_range.size() + duplicate_edge_range.
size(),
629 new_face_range.size(),
630 new_loop_range.size());
633 attributes, {
"position",
".edge_verts",
".corner_vert",
".corner_edge"});
645 offset_indices::fill_constant_group_size(4, orig_loop_size, new_face_offsets);
649 connect_edges[dst] =
int2(src, new_vert_range[dst]);
654 index_mask::build_reverse_map<int>(new_verts, vert_to_new_vert);
655 for (
const int i : duplicate_edges.
index_range()) {
656 const int2 orig_edge = edges[edge_selection[i]];
657 const int i_new_vert_1 = vert_to_new_vert[orig_edge[0]];
658 const int i_new_vert_2 = vert_to_new_vert[orig_edge[1]];
659 duplicate_edges[i] =
int2(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
664 const int2 duplicate_edge = duplicate_edges[i];
665 const int new_vert_1 = duplicate_edge[0];
666 const int new_vert_2 = duplicate_edge[1];
667 const int extrude_index_1 = new_vert_1 - orig_vert_size;
668 const int extrude_index_2 = new_vert_2 - orig_vert_size;
670 const int2 orig_edge = edges[orig_edge_index];
671 const Span<int> connected_faces = edge_to_face_map[orig_edge_index];
678 if (connected_faces.
size() == 1) {
679 const IndexRange connected_face = faces[connected_faces.first()];
680 connected_face_verts = corner_verts.slice(connected_face);
681 connected_face_edges = corner_edges.slice(connected_face);
684 connected_face_edges,
685 new_corner_verts.
slice(4 * i, 4),
686 new_corner_edges.
slice(4 * i, 4),
692 connect_edge_range[extrude_index_1],
693 duplicate_edge_range[i],
694 connect_edge_range[extrude_index_2]);
702 attributes, ids_by_domain[
int(AttrDomain::Edge)], edge_selection, duplicate_edge_range);
705 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Edge)]) {
708 vert_to_selected_edge_map,
710 attribute.span.
slice(connect_edge_range));
715 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Face)]) {
718 attribute.span, edge_to_face_map, edge_selection, attribute.span.
slice(new_face_range));
724 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Corner)]) {
726 bke::attribute_math::convert_to_static_type(attribute.span.type(), [&](
auto dummy) {
727 using T = decltype(dummy);
728 MutableSpan<T> data = attribute.span.typed<T>();
729 MutableSpan<T> new_data = data.slice(new_loop_range);
730 edge_selection.foreach_index(
731 GrainSize(256), [&](const int64_t orig_edge_index, const int64_t i_edge_selection) {
732 const Span<int> connected_faces = edge_to_face_map[orig_edge_index];
733 if (connected_faces.is_empty()) {
735 new_data.slice(4 * i_edge_selection, 4).fill(T());
741 Array<T> side_face_corner_data(2);
742 bke::attribute_math::DefaultPropagationMixer<T> mixer{side_face_corner_data};
744 const int new_vert_1 = duplicate_edges[i_edge_selection][0];
745 const int new_vert_2 = duplicate_edges[i_edge_selection][1];
746 const int orig_vert_1 = edges[orig_edge_index][0];
747 const int orig_vert_2 = edges[orig_edge_index][1];
751 for (const int connected_face : connected_faces) {
752 for (const int i_loop : faces[connected_face]) {
753 if (corner_verts[i_loop] == orig_vert_1) {
754 mixer.mix_in(0, data[i_loop]);
756 if (corner_verts[i_loop] == orig_vert_2) {
757 mixer.mix_in(1, data[i_loop]);
767 for (const int i : IndexRange(4 * i_edge_selection, 4)) {
768 if (ELEM(new_corner_verts[i], new_vert_1, orig_vert_1)) {
769 new_data[i] = side_face_corner_data.first();
771 else if (ELEM(new_corner_verts[i], new_vert_2, orig_vert_2)) {
772 new_data[i] = side_face_corner_data.last();
783 if (edge_offsets.is_single()) {
784 const float3 offset = edge_offsets.get_internal_single();
785 new_verts.foreach_index_optimized<
int>(
GrainSize(1024), [&](
const int src,
const int dst) {
786 new_positions[dst] = positions[src] + offset;
790 new_verts.foreach_index_optimized<
int>(GrainSize(1024), [&](
const int src,
const int dst) {
791 new_positions[dst] = positions[src] + vert_offsets[src];
796 array_utils::gather(indices->as_span(), new_verts, indices->slice(new_vert_range));
800 array_utils::gather(indices->as_span(), edge_selection, indices->slice(duplicate_edge_range));
806 if (attribute_outputs.top_id) {
808 attributes, *attribute_outputs.top_id, AttrDomain::Edge, duplicate_edge_range);
810 if (attribute_outputs.side_id) {
812 attributes, *attribute_outputs.side_id, AttrDomain::Face, new_face_range);
842 const int orig_vert_size = mesh.verts_num;
845 const Span<int> orig_corner_verts = mesh.corner_verts();
846 const int orig_loop_size = orig_corner_verts.
size();
851 face_evaluator.add(offset_field);
852 face_evaluator.evaluate();
853 const IndexMask face_selection = face_evaluator.get_evaluated_selection_as_mask();
860 face_selection.
to_bools(face_selection_array);
866 if (!face_position_offsets.
is_single()) {
870 const float3 offset = face_position_offsets[i_face];
871 for (
const int vert : orig_corner_verts.
slice(orig_faces[i_face])) {
872 mixer.mix_in(vert, offset);
881 const GroupedSpan<int> edge_to_face_map = bke::mesh::build_edge_to_face_map(
882 orig_faces, mesh.corner_edges(), mesh.edges_num, edge_to_face_offsets, edge_to_face_indices);
886 const IndexMask all_selected_verts = geometry::vert_selection_from_face(
887 orig_faces, face_selection, orig_corner_verts, orig_vert_size, memory);
900 for (
const int i_edge : orig_edges.
index_range()) {
901 const Span<int> faces = edge_to_face_map[i_edge];
903 int i_selected_face = -1;
904 int deselected_face_count = 0;
905 int selected_face_count = 0;
906 for (
const int i_other_face :
faces) {
907 if (face_selection_array[i_other_face]) {
908 selected_face_count++;
909 i_selected_face = i_other_face;
912 deselected_face_count++;
916 if (selected_face_count == 1) {
919 boundary_edge_indices.
add_new(i_edge);
920 edge_extruded_face_indices.
append(i_selected_face);
922 else if (selected_face_count > 1) {
924 if (deselected_face_count > 0) {
926 new_inner_edge_indices.
add_new(i_edge);
931 inner_edge_indices.
append(i_edge);
939 const int extruded_vert_size = new_vert_indices.
size();
942 for (
const int i_edge : new_inner_edge_indices) {
943 const int2 &edge = orig_edges[i_edge];
944 new_vert_indices.
add(edge[0]);
945 new_vert_indices.
add(edge[1]);
949 const IndexRange new_vert_range{orig_vert_size, new_vert_indices.
size()};
951 const IndexRange connect_edge_range{orig_edges.
size(), extruded_vert_size};
953 const IndexRange boundary_edge_range = connect_edge_range.
after(boundary_edge_indices.
size());
955 const IndexRange new_inner_edge_range = boundary_edge_range.
after(new_inner_edge_indices.
size());
959 const IndexRange side_loop_range{orig_corner_verts.
size(), side_face_range.size() * 4};
969 new_vert_range.size(),
970 connect_edge_range.size() + boundary_edge_range.
size() + new_inner_edge_range.
size(),
971 side_face_range.size(),
972 side_loop_range.size());
975 attributes, {
".corner_vert",
".corner_edge",
".edge_verts"});
990 offset_indices::fill_constant_group_size(4, orig_loop_size, new_face_offsets);
996 connect_edges[i] =
int2(new_vert_indices[i], new_vert_range[i]);
1000 for (
const int i : boundary_edges.
index_range()) {
1001 const int2 &orig_edge = edges[boundary_edge_indices[i]];
1002 const int i_new_vert_1 = new_vert_indices.
index_of(orig_edge[0]);
1003 const int i_new_vert_2 = new_vert_indices.
index_of(orig_edge[1]);
1004 boundary_edges[i] =
int2(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
1008 for (
const int i : new_inner_edge_indices.
index_range()) {
1009 const int2 &orig_edge = edges[new_inner_edge_indices[i]];
1010 const int i_new_vert_1 = new_vert_indices.
index_of(orig_edge[0]);
1011 const int i_new_vert_2 = new_vert_indices.
index_of(orig_edge[1]);
1012 new_inner_edges[i] =
int2(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
1016 for (
const int i : inner_edge_indices) {
1017 int2 &edge = edges[i];
1018 const int i_new_vert_1 = new_vert_indices.
index_of_try(edge[0]);
1019 const int i_new_vert_2 = new_vert_indices.
index_of_try(edge[1]);
1020 if (i_new_vert_1 != -1) {
1021 edge[0] = new_vert_range[i_new_vert_1];
1023 if (i_new_vert_2 != -1) {
1024 edge[1] = new_vert_range[i_new_vert_2];
1030 for (
const int corner : faces[i_face]) {
1031 const int i_new_vert = new_vert_indices.
index_of_try(corner_verts[corner]);
1032 if (i_new_vert != -1) {
1033 corner_verts[corner] = new_vert_range[i_new_vert];
1035 const int i_boundary_edge = boundary_edge_indices.
index_of_try(corner_edges[corner]);
1036 if (i_boundary_edge != -1) {
1037 corner_edges[corner] = boundary_edge_range[i_boundary_edge];
1041 const int i_new_inner_edge = new_inner_edge_indices.
index_of_try(corner_edges[corner]);
1042 if (i_new_inner_edge != -1) {
1043 corner_edges[corner] = new_inner_edge_range[i_new_inner_edge];
1049 for (
const int i : boundary_edge_indices.
index_range()) {
1050 const int2 &boundary_edge = boundary_edges[i];
1051 const int new_vert_1 = boundary_edge[0];
1052 const int new_vert_2 = boundary_edge[1];
1053 const int extrude_index_1 = new_vert_1 - orig_vert_size;
1054 const int extrude_index_2 = new_vert_2 - orig_vert_size;
1056 const IndexRange extrude_face = faces[edge_extruded_face_indices[i]];
1059 corner_edges.
slice(extrude_face),
1060 new_corner_verts.
slice(4 * i, 4),
1061 new_corner_edges.
slice(4 * i, 4),
1064 new_vert_indices[extrude_index_1],
1065 new_vert_indices[extrude_index_2],
1066 boundary_edge_range[i],
1067 connect_edge_range[extrude_index_1],
1068 boundary_edge_indices[i],
1069 connect_edge_range[extrude_index_2]);
1074 mesh, ids_by_domain[
int(AttrDomain::Point)], new_vert_indices, new_vert_range);
1077 gather_attributes(attributes,
1078 ids_by_domain[
int(AttrDomain::Face)],
1079 edge_extruded_face_indices,
1082 if (!ids_by_domain[
int(AttrDomain::Edge)].is_empty()) {
1084 const IndexMask boundary_edge_mask = IndexMask::from_indices<int>(boundary_edge_indices,
1090 edges, boundary_edge_mask, mesh.verts_num, vert_to_edge_offsets, vert_to_edge_indices);
1092 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Edge)]) {
1097 array_utils::gather(attribute.span, boundary_edge_mask, boundary_data);
1101 bke::attribute_math::gather(attribute.span, new_inner_edge_indices, new_inner_data);
1105 vert_to_boundary_edge_map,
1107 attribute.span.slice(connect_edge_range));
1113 if (!ids_by_domain[
int(AttrDomain::Corner)].is_empty()) {
1114 Array<int> orig_corners(side_loop_range.size());
1116 for (const int i_boundary_edge : range) {
1117 const int2 &boundary_edge = boundary_edges[i_boundary_edge];
1118 const int new_vert_1 = boundary_edge[0];
1119 const int new_vert_2 = boundary_edge[1];
1120 const int orig_vert_1 = new_vert_indices[new_vert_1 - orig_vert_size];
1121 const int orig_vert_2 = new_vert_indices[new_vert_2 - orig_vert_size];
1130 for (const int corner : faces[edge_extruded_face_indices[i_boundary_edge]]) {
1131 if (corner_verts[corner] == new_vert_1) {
1134 if (corner_verts[corner] == new_vert_2) {
1142 for (const int i : IndexRange(4 * i_boundary_edge, 4)) {
1143 if (ELEM(new_corner_verts[i], new_vert_1, orig_vert_1)) {
1144 orig_corners[i] = corner_1;
1146 else if (ELEM(new_corner_verts[i], new_vert_2, orig_vert_2)) {
1147 orig_corners[i] = corner_2;
1153 attributes, ids_by_domain[
int(AttrDomain::Corner)], orig_corners, side_loop_range);
1160 if (face_position_offsets.is_single()) {
1161 const float3 offset = face_position_offsets.get_internal_single();
1162 all_selected_verts.foreach_index(
GrainSize(1024), [&](
const int orig_vert) {
1163 const int i_new = new_vert_indices.index_of_try(orig_vert);
1165 positions[orig_vert] += offset;
1168 positions[new_vert_range[i_new]] += offset;
1173 all_selected_verts.foreach_index(GrainSize(1024), [&](
const int orig_vert) {
1174 const int i_new = new_vert_indices.index_of_try(orig_vert);
1175 const float3 offset = vert_offsets[orig_vert];
1177 positions[orig_vert] += offset;
1180 positions[new_vert_range[i_new]] += offset;
1186 array_utils::gather(
1187 indices->as_span(), new_vert_indices.as_span(), indices->slice(new_vert_range));
1191 array_utils::gather(indices->as_span(),
1192 new_inner_edge_indices.as_span(),
1193 indices->slice(new_inner_edge_range));
1194 array_utils::gather(
1195 indices->as_span(), boundary_edge_indices.as_span(), indices->slice(boundary_edge_range));
1198 array_utils::gather(
1199 indices->as_span(), edge_extruded_face_indices.as_span(), indices->slice(side_face_range));
1202 if (attribute_outputs.top_id) {
1204 attributes, *attribute_outputs.top_id, AttrDomain::Face, face_selection);
1206 if (attribute_outputs.side_id) {
1208 attributes, *attribute_outputs.side_id, AttrDomain::Face, side_face_range);
1220 const int orig_vert_size = mesh.verts_num;
1221 const int orig_edge_size = mesh.edges_num;
1223 const Span<int> orig_corner_verts = mesh.corner_verts();
1224 const int orig_loop_size = orig_corner_verts.
size();
1232 face_evaluator.add_with_destination(offset_field, face_offset.
as_mutable_span());
1233 face_evaluator.evaluate();
1234 const IndexMask face_selection = face_evaluator.get_evaluated_selection_as_mask();
1244 orig_faces, face_selection, group_per_face_data);
1245 const int extrude_corner_size = group_per_face.
total_size();
1247 const IndexRange new_vert_range{orig_vert_size, extrude_corner_size};
1249 const IndexRange connect_edge_range{orig_edge_size, extrude_corner_size};
1251 const IndexRange duplicate_edge_range = connect_edge_range.
after(extrude_corner_size);
1254 const IndexRange side_loop_range{orig_loop_size, side_face_range.
size() * 4};
1264 new_vert_range.size(),
1265 connect_edge_range.size() + duplicate_edge_range.
size(),
1266 side_face_range.size(),
1267 side_loop_range.size());
1270 attributes, {
"position",
".edge_verts",
".corner_vert",
".corner_edge"});
1282 offset_indices::fill_constant_group_size(4, orig_loop_size, new_face_offsets);
1291 Array<int> new_vert_indices(extrude_corner_size);
1292 Array<int> duplicate_edge_indices(extrude_corner_size);
1295 const IndexRange extrude_range = group_per_face[i_selection];
1301 for (
const int i : face.index_range()) {
1302 const int i_extrude = extrude_range[i];
1303 new_vert_indices[i_extrude] = face_verts[i];
1304 duplicate_edge_indices[i_extrude] = face_edges[i];
1306 face_verts[i] = new_vert_range[i_extrude];
1307 face_edges[i] = duplicate_edge_range[i_extrude];
1310 for (
const int i : face.index_range()) {
1311 const int i_next = (i == face.size() - 1) ? 0 : i + 1;
1312 const int i_extrude = extrude_range[i];
1313 const int i_extrude_next = extrude_range[i_next];
1315 const int i_duplicate_edge = duplicate_edge_range[i_extrude];
1316 const int new_vert = new_vert_range[i_extrude];
1317 const int new_vert_next = new_vert_range[i_extrude_next];
1319 const int orig_edge = duplicate_edge_indices[i_extrude];
1321 const int orig_vert = new_vert_indices[i_extrude];
1322 const int orig_vert_next = new_vert_indices[i_extrude_next];
1324 duplicate_edges[i_extrude] =
int2(new_vert, new_vert_next);
1328 side_face_verts[0] = new_vert_next;
1329 side_face_edges[0] = i_duplicate_edge;
1330 side_face_verts[1] = new_vert;
1331 side_face_edges[1] = connect_edge_range[i_extrude];
1332 side_face_verts[2] = orig_vert;
1333 side_face_edges[2] = orig_edge;
1334 side_face_verts[3] = orig_vert_next;
1335 side_face_edges[3] = connect_edge_range[i_extrude_next];
1337 connect_edges[i_extrude] =
int2(orig_vert, new_vert);
1343 mesh, ids_by_domain[
int(AttrDomain::Point)], new_vert_indices, new_vert_range);
1346 gather_attributes(attributes,
1347 ids_by_domain[
int(AttrDomain::Edge)],
1348 duplicate_edge_indices,
1349 duplicate_edge_range);
1352 if (!ids_by_domain[
int(AttrDomain::Edge)].is_empty()) {
1353 Array<int2> neighbor_edges(connect_edge_range.size());
1357 const IndexRange extrude_range = group_per_face[i_selection];
1359 for (
const int i : face.index_range()) {
1360 const int i_prev = (i == 0) ? face.
size() - 1 : i - 1;
1361 const int i_extrude = extrude_range[i];
1362 const int i_extrude_prev = extrude_range[i_prev];
1363 neighbor_edges[i_extrude] =
int2(duplicate_edge_indices[i_extrude],
1364 duplicate_edge_indices[i_extrude_prev]);
1368 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Edge)]) {
1370 bke::attribute_math::convert_to_static_type(attribute.span.type(), [&](
auto dummy) {
1371 using T = decltype(dummy);
1372 MutableSpan<T> data = attribute.span.typed<T>();
1373 MutableSpan<T> dst = data.slice(connect_edge_range);
1374 threading::parallel_for(dst.index_range(), 1024, [&](const IndexRange range) {
1375 for (const int i : range) {
1376 const int2 neighbors = neighbor_edges[i];
1377 if constexpr (std::is_same_v<T, bool>) {
1379 dst[i] = data[neighbors[0]] || data[neighbors[1]];
1382 dst[i] = bke::attribute_math::mix2(0.5f, data[neighbors[0]], data[neighbors[1]]);
1392 for (
const StringRef id : ids_by_domain[
int(AttrDomain::Face)]) {
1394 bke::attribute_math::gather_to_groups(
1395 group_per_face, face_selection, attribute.span, attribute.span.slice(side_face_range));
1400 if (!ids_by_domain[
int(AttrDomain::Corner)].is_empty()) {
1401 Array<int> orig_corners(side_loop_range.size());
1402 face_selection.foreach_index(
1405 const IndexRange extrude_range = group_per_face[i_selection];
1407 for (
const int i : face.index_range()) {
1408 const IndexRange side_face(extrude_range[i] * 4, 4);
1412 const int corner = face[i];
1413 const int next_corner = bke::mesh::face_corner_next(face, corner);
1414 orig_corners[side_face[0]] = next_corner;
1415 orig_corners[side_face[1]] = corner;
1416 orig_corners[side_face[2]] = corner;
1417 orig_corners[side_face[3]] = next_corner;
1421 attributes, ids_by_domain[
int(AttrDomain::Corner)], orig_corners, side_loop_range);
1425 face_selection.foreach_index(GrainSize(1025),
1427 const IndexRange extrude_range = group_per_face[i_selection];
1428 for (
const int i : extrude_range) {
1429 const int src_vert = new_vert_indices[i];
1430 new_positions[i] = positions[src_vert] + face_offset[index];
1435 array_utils::gather(
1436 indices->as_span(), new_vert_indices.as_span(), indices->slice(new_vert_range));
1440 array_utils::gather(indices->as_span(),
1441 duplicate_edge_indices.as_span(),
1442 indices->slice(duplicate_edge_range));
1445 array_utils::gather_to_groups(
1446 group_per_face, face_selection, indices->as_span(), indices->slice(side_face_range));
1449 if (attribute_outputs.top_id) {
1451 attributes, *attribute_outputs.top_id, AttrDomain::Face, face_selection);
1453 if (attribute_outputs.side_id) {
1455 attributes, *attribute_outputs.side_id, AttrDomain::Face, side_face_range);