Cleanup: Reduce binary size by deduplicating attribute processing #107823

Merged
Hans Goudey merged 4 commits from HooglyBoogly/blender:cleanup-remove-convert-to-static-type into main 2023-05-12 14:44:46 +02:00
9 changed files with 282 additions and 265 deletions

View File

@ -5,6 +5,8 @@
#include "BLI_array.hh"
#include "BLI_color.hh"
#include "BLI_cpp_type.hh"
#include "BLI_generic_span.hh"
#include "BLI_generic_virtual_array.hh"
#include "BLI_math_color.hh"
#include "BLI_math_vector.h"
#include "BLI_math_vector.hh"
@ -587,4 +589,16 @@ template<typename T> using DefaultMixer = typename DefaultMixerStruct<T>::type;
/** \} */
/* -------------------------------------------------------------------- */
/** \name Generic Array Utils Implementations
*
* Extra implementations of functions from #BLI_array_utils.hh for all attribute types,
* used to avoid templating the same logic for each type in many places.
* \{ */
void gather(GSpan src, Span<int> map, GMutableSpan dst);
void gather(const GVArray &src, Span<int> map, GMutableSpan dst);
/** \} */
} // namespace blender::bke::attribute_math

View File

@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_array_utils.hh"
#include "BKE_attribute_math.hh"
namespace blender::bke::attribute_math {
@ -128,4 +130,20 @@ void ColorGeometry4bMixer::finalize(const IndexMask mask)
});
}
void gather(const GSpan src, const Span<int> map, GMutableSpan dst)
{
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
array_utils::gather(src.typed<T>(), map, dst.typed<T>());
});
}
void gather(const GVArray &src, const Span<int> map, GMutableSpan dst)
{
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
array_utils::gather(src.typed<T>(), map, dst.typed<T>());
});
}
} // namespace blender::bke::attribute_math

View File

@ -1124,14 +1124,6 @@ static void copy_construct_data(const GSpan src, GMutableSpan dst)
src.type().copy_construct_n(src.data(), dst.data(), src.size());
}
static void copy_with_map(const GSpan src, const Span<int> map, GMutableSpan dst)
{
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
array_utils::gather(src.typed<T>(), map, dst.typed<T>());
});
}
static CurvesGeometry copy_with_removed_points(
const CurvesGeometry &curves,
const IndexMask points_to_delete,
@ -1216,7 +1208,7 @@ static CurvesGeometry copy_with_removed_points(
attribute.dst.span.copy_from(attribute.src);
}
else {
copy_with_map(attribute.src, new_curve_orig_indices, attribute.dst.span);
bke::attribute_math::gather(attribute.src, new_curve_orig_indices, attribute.dst.span);
}
}
});

View File

@ -18,14 +18,6 @@ static inline bool naive_edges_equal(const int2 &edge1, const int2 &edge2)
return edge1 == edge2;
}
template<typename T>
static void copy_to_new_verts(MutableSpan<T> data, const Span<int> new_to_old_verts_map)
{
const Span<T> old_data = data.drop_back(new_to_old_verts_map.size());
MutableSpan<T> new_data = data.take_back(new_to_old_verts_map.size());
array_utils::gather(old_data, new_to_old_verts_map, new_data);
}
static void add_new_vertices(Mesh &mesh, const Span<int> new_to_old_verts_map)
{
/* These types aren't supported for interpolation below. */
@ -46,22 +38,26 @@ static void add_new_vertices(Mesh &mesh, const Span<int> new_to_old_verts_map)
continue;
}
bke::attribute_math::convert_to_static_type(attribute.span.type(), [&](auto dummy) {
using T = decltype(dummy);
copy_to_new_verts(attribute.span.typed<T>(), new_to_old_verts_map);
});
bke::attribute_math::gather(attribute.span,
new_to_old_verts_map,
attribute.span.take_back(new_to_old_verts_map.size()));
attribute.finish();
}
if (float3 *orco = static_cast<float3 *>(
CustomData_get_layer_for_write(&mesh.vdata, CD_ORCO, mesh.totvert)))
{
copy_to_new_verts<float3>({orco, mesh.totvert}, new_to_old_verts_map);
array_utils::gather(Span(orco, mesh.totvert),
new_to_old_verts_map,
MutableSpan(orco, mesh.totvert).take_back(new_to_old_verts_map.size()));
}
if (int *orig_indices = static_cast<int *>(
CustomData_get_layer_for_write(&mesh.vdata, CD_ORIGINDEX, mesh.totvert)))
{
copy_to_new_verts<int>({orig_indices, mesh.totvert}, new_to_old_verts_map);
array_utils::gather(
Span(orig_indices, mesh.totvert),
new_to_old_verts_map,
MutableSpan(orig_indices, mesh.totvert).take_back(new_to_old_verts_map.size()));
}
}
@ -119,12 +115,8 @@ static void add_new_edges(Mesh &mesh,
const CPPType &type = attribute.varray.type();
void *new_data = MEM_malloc_arrayN(new_edges.size(), type.size(), __func__);
bke::attribute_math::convert_to_static_type(type, [&](auto dummy) {
using T = decltype(dummy);
const VArray<T> src = attribute.varray.typed<T>();
MutableSpan<T> dst(static_cast<T *>(new_data), new_edges.size());
array_utils::gather(src, new_to_old_edges_map, dst);
});
bke::attribute_math::gather(
attribute.varray, new_to_old_edges_map, GMutableSpan(type, new_data, new_edges.size()));
/* Free the original attribute as soon as possible to lower peak memory usage. */
attributes.remove(local_id);

View File

@ -51,21 +51,19 @@ BLI_NOINLINE bke::CurvesGeometry create_curve_from_vert_indices(
continue;
}
const GVArray mesh_attribute = *mesh_attributes.lookup(attribute_id, ATTR_DOMAIN_POINT);
const GVArray src = *mesh_attributes.lookup(attribute_id, ATTR_DOMAIN_POINT);
/* Some attributes might not exist if they were builtin attribute on domains that don't
* have any elements, i.e. a face attribute on the output of the line primitive node. */
if (!mesh_attribute) {
if (!src) {
continue;
}
const eCustomDataType type = bke::cpp_type_to_custom_data_type(src.type());
/* Copy attribute based on the map for this curve. */
bke::attribute_math::convert_to_static_type(mesh_attribute.type(), [&](auto dummy) {
using T = decltype(dummy);
bke::SpanAttributeWriter<T> attribute =
curves_attributes.lookup_or_add_for_write_only_span<T>(attribute_id, ATTR_DOMAIN_POINT);
array_utils::gather<T>(mesh_attribute.typed<T>(), vert_indices, attribute.span);
attribute.finish();
});
bke::GSpanAttributeWriter dst = curves_attributes.lookup_or_add_for_write_only_span(
attribute_id, ATTR_DOMAIN_POINT, type);
bke::attribute_math::gather(src, vert_indices, dst.span);
dst.finish();
}
return curves;

View File

@ -334,10 +334,8 @@ class SampleCurveFunction : public mf::MultiFunction {
sampled_normals.fill_indices(mask.indices(), float3(0));
}
if (!sampled_values.is_empty()) {
bke::attribute_math::convert_to_static_type(source_data_->type(), [&](auto dummy) {
using T = decltype(dummy);
sampled_values.typed<T>().fill_indices(mask.indices(), {});
});
const CPPType &type = sampled_values.type();
type.fill_construct_indices(type.default_value(), sampled_values.data(), mask);

Should use fill_construct_indices.

Should use `fill_construct_indices`.
}
};

View File

@ -149,52 +149,58 @@ static void transfer_attributes(
});
for (const AttributeIDRef &id : attribute_ids) {
GAttributeReader src_attribute = src_attributes.lookup(id);
GAttributeReader src = src_attributes.lookup(id);
eAttrDomain out_domain;
if (src_attribute.domain == ATTR_DOMAIN_FACE) {
if (src.domain == ATTR_DOMAIN_FACE) {
out_domain = ATTR_DOMAIN_POINT;
}
else if (src_attribute.domain == ATTR_DOMAIN_POINT) {
else if (src.domain == ATTR_DOMAIN_POINT) {
out_domain = ATTR_DOMAIN_FACE;
}
else {
/* Edges and Face Corners. */
out_domain = src_attribute.domain;
out_domain = src.domain;
}
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(src.varray.type());
GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
id, out_domain, data_type);
if (!dst_attribute) {
if (!dst) {
continue;
}
bke::attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
VArraySpan<T> span{src_attribute.varray.typed<T>()};
MutableSpan<T> dst_span = dst_attribute.span.typed<T>();
switch (src_attribute.domain) {
case ATTR_DOMAIN_POINT:
copy_data_based_on_vertex_types(span, dst_span, vertex_types, keep_boundaries);
break;
case ATTR_DOMAIN_EDGE:
array_utils::gather(span, new_to_old_edges_map, dst_span);
break;
case ATTR_DOMAIN_FACE:
dst_span.take_front(span.size()).copy_from(span);
if (keep_boundaries) {
copy_data_based_on_pairs(span, dst_span, boundary_vertex_to_relevant_face_map);
}
break;
case ATTR_DOMAIN_CORNER:
array_utils::gather(span, new_to_old_face_corners_map, dst_span);
break;
default:
BLI_assert_unreachable();
switch (src.domain) {
case ATTR_DOMAIN_POINT: {
const GVArraySpan src_span(*src);
bke::attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
copy_data_based_on_vertex_types(
src_span.typed<T>(), dst.span.typed<T>(), vertex_types, keep_boundaries);
});
break;
}
});
dst_attribute.finish();
case ATTR_DOMAIN_EDGE:
bke::attribute_math::gather(*src, new_to_old_edges_map, dst.span);
break;
case ATTR_DOMAIN_FACE: {
const GVArraySpan src_span(*src);
dst.span.take_front(src_span.size()).copy_from(src_span);
bke::attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
using T = decltype(dummy);
if (keep_boundaries) {
copy_data_based_on_pairs(
src_span.typed<T>(), dst.span.typed<T>(), boundary_vertex_to_relevant_face_map);
}
});
break;
}
case ATTR_DOMAIN_CORNER:
bke::attribute_math::gather(*src, new_to_old_face_corners_map, dst.span);
break;
default:
BLI_assert_unreachable();
}
dst.finish();
}
}

View File

@ -103,6 +103,17 @@ static void threaded_slice_fill(const OffsetIndices<int> offsets,
});
}
static void threaded_slice_fill(const OffsetIndices<int> offsets,
const IndexMask selection,
const GSpan src,
GMutableSpan dst)
{
bke::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
threaded_slice_fill<T>(offsets, selection, src.typed<T>(), dst.typed<T>());
});
}
static void copy_hashed_ids(const Span<int> src, const int hash, MutableSpan<int> dst)
{
for (const int i : src.index_range()) {
@ -181,12 +192,7 @@ static void copy_attributes_without_id(const OffsetIndices<int> offsets,
for (auto &attribute : bke::retrieve_attributes_for_transfer(
src_attributes, dst_attributes, ATTR_DOMAIN_AS_MASK(domain), propagation_info, {"id"}))
{
bke::attribute_math::convert_to_static_type(attribute.src.type(), [&](auto dummy) {
using T = decltype(dummy);
const Span<T> src = attribute.src.typed<T>();
MutableSpan<T> dst = attribute.dst.span.typed<T>();
threaded_slice_fill<T>(offsets, selection, src, dst);
});
threaded_slice_fill(offsets, selection, attribute.src, attribute.dst.span);
attribute.dst.finish();
}
}
@ -217,16 +223,15 @@ static void copy_curve_attributes_without_id(
propagation_info,
{"id"}))
{
bke::attribute_math::convert_to_static_type(attribute.src.type(), [&](auto dummy) {
using T = decltype(dummy);
const Span<T> src = attribute.src.typed<T>();
MutableSpan<T> dst = attribute.dst.span.typed<T>();
switch (attribute.meta_data.domain) {
case ATTR_DOMAIN_CURVE:
threaded_slice_fill<T>(curve_offsets, selection, src, dst);
break;
case ATTR_DOMAIN_POINT:
switch (attribute.meta_data.domain) {
case ATTR_DOMAIN_CURVE:
threaded_slice_fill(curve_offsets, selection, attribute.src, attribute.dst.span);
break;
case ATTR_DOMAIN_POINT:
bke::attribute_math::convert_to_static_type(attribute.src.type(), [&](auto dummy) {
using T = decltype(dummy);
const Span<T> src = attribute.src.typed<T>();
MutableSpan<T> dst = attribute.dst.span.typed<T>();
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i_selection : range) {
const int i_src_curve = selection[i_selection];
@ -236,12 +241,12 @@ static void copy_curve_attributes_without_id(
}
}
});
break;
default:
BLI_assert_unreachable();
break;
}
});
});
break;
default:
BLI_assert_unreachable();
break;
}
attribute.dst.finish();
}
}
@ -395,29 +400,23 @@ static void copy_face_attributes_without_id(
propagation_info,
{"id", ".corner_vert", ".corner_edge", ".edge_verts"}))
{
bke::attribute_math::convert_to_static_type(attribute.src.type(), [&](auto dummy) {
using T = decltype(dummy);
const Span<T> src = attribute.src.typed<T>();
MutableSpan<T> dst = attribute.dst.span.typed<T>();
switch (attribute.meta_data.domain) {
case ATTR_DOMAIN_POINT:
array_utils::gather(src, vert_mapping, dst);
break;
case ATTR_DOMAIN_EDGE:
array_utils::gather(src, edge_mapping, dst);
break;
case ATTR_DOMAIN_FACE:
threaded_slice_fill<T>(offsets, selection, src, dst);
break;
case ATTR_DOMAIN_CORNER:
array_utils::gather(src, loop_mapping, dst);
break;
default:
BLI_assert_unreachable();
break;
}
});
switch (attribute.meta_data.domain) {
case ATTR_DOMAIN_POINT:
bke::attribute_math::gather(attribute.src, vert_mapping, attribute.dst.span);
break;
case ATTR_DOMAIN_EDGE:
bke::attribute_math::gather(attribute.src, edge_mapping, attribute.dst.span);
break;
case ATTR_DOMAIN_FACE:
threaded_slice_fill(offsets, selection, attribute.src, attribute.dst.span);
break;
case ATTR_DOMAIN_CORNER:
bke::attribute_math::gather(attribute.src, loop_mapping, attribute.dst.span);
break;
default:
BLI_assert_unreachable();
break;
}
attribute.dst.finish();
}
}
@ -606,23 +605,17 @@ static void copy_edge_attributes_without_id(
propagation_info,
{"id", ".edge_verts"}))
{
bke::attribute_math::convert_to_static_type(attribute.src.type(), [&](auto dummy) {
using T = decltype(dummy);
const Span<T> src = attribute.src.typed<T>();
MutableSpan<T> dst = attribute.dst.span.typed<T>();
switch (attribute.meta_data.domain) {
case ATTR_DOMAIN_EDGE:
threaded_slice_fill<T>(offsets, selection, src, dst);
break;
case ATTR_DOMAIN_POINT:
array_utils::gather(src, point_mapping, dst);
break;
default:
BLI_assert_unreachable();
break;
}
});
switch (attribute.meta_data.domain) {
case ATTR_DOMAIN_EDGE:
threaded_slice_fill(offsets, selection, attribute.src, attribute.dst.span);
break;
case ATTR_DOMAIN_POINT:
bke::attribute_math::gather(attribute.src, point_mapping, attribute.dst.span);
break;
default:
BLI_assert_unreachable();
break;
}
attribute.dst.finish();
}
}
@ -784,10 +777,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
bke::curves_copy_parameters(src_curves_id, *new_curves_id);
bke::CurvesGeometry &new_curves = new_curves_id->geometry.wrap();
MutableSpan<int> new_curve_offsets = new_curves.offsets_for_write();
for (const int i : new_curves.curves_range()) {
new_curve_offsets[i] = i;
}
new_curve_offsets.last() = dst_num;
std::iota(new_curve_offsets.begin(), new_curve_offsets.end(), 0);
for (auto &attribute : bke::retrieve_attributes_for_transfer(src_curves.attributes(),
new_curves.attributes_for_write(),
@ -795,27 +785,27 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
propagation_info,
{"id"}))
{
bke::attribute_math::convert_to_static_type(attribute.src.type(), [&](auto dummy) {
using T = decltype(dummy);
const Span<T> src = attribute.src.typed<T>();
MutableSpan<T> dst = attribute.dst.span.typed<T>();
switch (attribute.meta_data.domain) {
case ATTR_DOMAIN_CURVE:
switch (attribute.meta_data.domain) {
case ATTR_DOMAIN_CURVE:
bke::attribute_math::convert_to_static_type(attribute.src.type(), [&](auto dummy) {
using T = decltype(dummy);
const Span<T> src = attribute.src.typed<T>();
MutableSpan<T> dst = attribute.dst.span.typed<T>();
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i_selection : range) {
const T &src_value = src[point_to_curve_map[selection[i_selection]]];
dst.slice(duplicates[i_selection]).fill(src_value);
}
});
break;
case ATTR_DOMAIN_POINT:
threaded_slice_fill(duplicates, selection, src, dst);
break;
default:
BLI_assert_unreachable();
break;
}
});
});
break;
case ATTR_DOMAIN_POINT:
threaded_slice_fill(duplicates, selection, attribute.src, attribute.dst.span);
break;
default:
BLI_assert_unreachable();
break;
}
attribute.dst.finish();
}

View File

@ -199,15 +199,16 @@ static MutableSpan<int> get_orig_index_layer(Mesh &mesh, const eAttrDomain domai
* \param get_mix_indices_fn: Returns a Span of indices of the source points to mix for every
* result point.
*/
template<typename T, typename GetMixIndicesFn>
template<typename T>
void copy_with_mixing(const Span<T> src,
const GetMixIndicesFn &get_mix_indices_fn,
const FunctionRef<Span<int>(int)> get_mix_indices_fn,
MutableSpan<T> dst)
{
threading::parallel_for(dst.index_range(), 512, [&](const IndexRange range) {
bke::attribute_math::DefaultPropagationMixer<T> mixer{dst.slice(range)};
for (const int i_dst : IndexRange(range.size())) {
for (const int i_src : get_mix_indices_fn(range[i_dst])) {
const Span<int> indices = get_mix_indices_fn(range[i_dst]);
for (const int i_src : indices) {
mixer.mix_in(i_dst, src[i_src]);
}
}
@ -215,8 +216,9 @@ void copy_with_mixing(const Span<T> src,
});
}
template<typename GetMixIndicesFn>
void copy_with_mixing(const GSpan src, const GetMixIndicesFn &get_mix_indices_fn, GMutableSpan dst)
static void copy_with_mixing(const GSpan src,
const FunctionRef<Span<int>(int)> get_mix_indices_fn,
GMutableSpan dst)
{
bke::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
@ -535,41 +537,40 @@ static void extrude_mesh_edges(Mesh &mesh,
}
GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
bke::attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
MutableSpan<T> data = attribute.span.typed<T>();
switch (attribute.domain) {
case ATTR_DOMAIN_POINT: {
/* New vertices copy the attribute values from their source vertex. */
array_utils::gather(
data.as_span(), new_vert_indices.as_span(), data.slice(new_vert_range));
break;
}
case ATTR_DOMAIN_EDGE: {
/* Edges parallel to original edges copy the edge attributes from the original edges. */
MutableSpan<T> duplicate_data = data.slice(duplicate_edge_range);
array_utils::gather(data.as_span(), edge_selection, duplicate_data);
switch (attribute.domain) {
case ATTR_DOMAIN_POINT: {
/* New vertices copy the attribute values from their source vertex. */
bke::attribute_math::gather(
attribute.span, new_vert_indices, attribute.span.slice(new_vert_range));
break;
}
case ATTR_DOMAIN_EDGE: {
/* Edges parallel to original edges copy the edge attributes from the original edges. */
GMutableSpan duplicate_data = attribute.span.slice(duplicate_edge_range);
array_utils::gather(attribute.span, edge_selection, duplicate_data);
/* Edges connected to original vertices mix values of selected connected edges. */
copy_with_mixing(
duplicate_data.as_span(),
[&](const int i) { return new_vert_to_duplicate_edge_map[i].as_span(); },
data.slice(connect_edge_range));
break;
}
case ATTR_DOMAIN_FACE: {
/* Attribute values for new faces are a mix of the values of faces connected to the its
* original edge. */
copy_with_mixing(
data.as_span(),
[&](const int i) { return edge_to_poly_map[edge_selection[i]].as_span(); },
data.slice(new_poly_range));
break;
}
case ATTR_DOMAIN_CORNER: {
/* New corners get the average value of all adjacent corners on original faces connected
* to the original edge of their face. */
/* Edges connected to original vertices mix values of selected connected edges. */
copy_with_mixing(
duplicate_data,
[&](const int i) { return new_vert_to_duplicate_edge_map[i].as_span(); },
attribute.span.slice(connect_edge_range));
break;
}
case ATTR_DOMAIN_FACE: {
/* Attribute values for new faces are a mix of the values of faces connected to the its
* original edge. */
copy_with_mixing(
attribute.span,
[&](const int i) { return edge_to_poly_map[edge_selection[i]].as_span(); },
attribute.span.slice(new_poly_range));
break;
}
case ATTR_DOMAIN_CORNER: {
/* New corners get the average value of all adjacent corners on original faces connected
* to the original edge of their face. */
bke::attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
MutableSpan<T> data = attribute.span.typed<T>();
MutableSpan<T> new_data = data.slice(new_loop_range);
threading::parallel_for(edge_selection.index_range(), 256, [&](const IndexRange range) {
for (const int i_edge_selection : range) {
@ -623,12 +624,12 @@ static void extrude_mesh_edges(Mesh &mesh,
}
}
});
break;
}
default:
BLI_assert_unreachable();
});
break;
}
});
default:
BLI_assert_unreachable();
}
attribute.finish();
return true;
@ -935,41 +936,41 @@ static void extrude_mesh_face_regions(Mesh &mesh,
}
GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
bke::attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
MutableSpan<T> data = attribute.span.typed<T>();
switch (attribute.domain) {
case ATTR_DOMAIN_POINT: {
/* New vertices copy the attributes from their original vertices. */
array_utils::gather(
data.as_span(), new_vert_indices.as_span(), data.slice(new_vert_range));
break;
}
case ATTR_DOMAIN_EDGE: {
/* Edges parallel to original edges copy the edge attributes from the original edges. */
MutableSpan<T> boundary_data = data.slice(boundary_edge_range);
array_utils::gather(data.as_span(), boundary_edge_indices.as_span(), boundary_data);
switch (attribute.domain) {
case ATTR_DOMAIN_POINT: {
/* New vertices copy the attributes from their original vertices. */
bke::attribute_math::gather(
attribute.span, new_vert_indices, attribute.span.slice(new_vert_range));
break;
}
case ATTR_DOMAIN_EDGE: {
/* Edges parallel to original edges copy the edge attributes from the original edges. */
GMutableSpan boundary_data = attribute.span.slice(boundary_edge_range);
bke::attribute_math::gather(attribute.span, boundary_edge_indices, boundary_data);
/* Edges inside of face regions also just duplicate their source data. */
MutableSpan<T> new_inner_data = data.slice(new_inner_edge_range);
array_utils::gather(data.as_span(), new_inner_edge_indices.as_span(), new_inner_data);
/* Edges inside of face regions also just duplicate their source data. */
GMutableSpan new_inner_data = attribute.span.slice(new_inner_edge_range);
bke::attribute_math::gather(attribute.span, new_inner_edge_indices, new_inner_data);
/* Edges connected to original vertices mix values of selected connected edges. */
copy_with_mixing(
boundary_data.as_span(),
[&](const int i) { return new_vert_to_duplicate_edge_map[i].as_span(); },
data.slice(connect_edge_range));
break;
}
case ATTR_DOMAIN_FACE: {
/* New faces on the side of extrusions get the values from the corresponding selected
* face. */
array_utils::gather(
data.as_span(), edge_extruded_face_indices.as_span(), data.slice(side_poly_range));
break;
}
case ATTR_DOMAIN_CORNER: {
/* New corners get the values from the corresponding corner on the extruded face. */
/* Edges connected to original vertices mix values of selected connected edges. */
copy_with_mixing(
boundary_data,
[&](const int i) { return new_vert_to_duplicate_edge_map[i].as_span(); },
attribute.span.slice(connect_edge_range));
break;
}
case ATTR_DOMAIN_FACE: {
/* New faces on the side of extrusions get the values from the corresponding selected
* face. */
GMutableSpan side_data = attribute.span.slice(side_poly_range);
bke::attribute_math::gather(attribute.span, edge_extruded_face_indices, side_data);
break;
}
case ATTR_DOMAIN_CORNER: {
/* New corners get the values from the corresponding corner on the extruded face. */
bke::attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
MutableSpan<T> data = attribute.span.typed<T>();
MutableSpan<T> new_data = data.slice(side_loop_range);
threading::parallel_for(
boundary_edge_indices.index_range(), 256, [&](const IndexRange range) {
@ -1009,12 +1010,12 @@ static void extrude_mesh_face_regions(Mesh &mesh,
}
}
});
break;
}
default:
BLI_assert_unreachable();
});
break;
}
});
default:
BLI_assert_unreachable();
}
attribute.finish();
return true;
@ -1227,21 +1228,21 @@ static void extrude_individual_mesh_faces(
}
GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
bke::attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
MutableSpan<T> data = attribute.span.typed<T>();
switch (attribute.domain) {
case ATTR_DOMAIN_POINT: {
/* New vertices copy the attributes from their original vertices. */
MutableSpan<T> new_data = data.slice(new_vert_range);
array_utils::gather(data.as_span(), new_vert_indices.as_span(), new_data);
break;
}
case ATTR_DOMAIN_EDGE: {
/* The data for the duplicate edge is simply a copy of the original edge's data. */
MutableSpan<T> duplicate_data = data.slice(duplicate_edge_range);
array_utils::gather(data.as_span(), duplicate_edge_indices.as_span(), duplicate_data);
switch (attribute.domain) {
case ATTR_DOMAIN_POINT: {
/* New vertices copy the attributes from their original vertices. */
GMutableSpan new_data = attribute.span.slice(new_vert_range);
bke::attribute_math::gather(attribute.span, new_vert_indices, new_data);
break;
}
case ATTR_DOMAIN_EDGE: {
/* The data for the duplicate edge is simply a copy of the original edge's data. */
GMutableSpan duplicate_data = attribute.span.slice(duplicate_edge_range);
bke::attribute_math::gather(attribute.span, duplicate_edge_indices, duplicate_data);
bke::attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
MutableSpan<T> data = attribute.span.typed<T>();
MutableSpan<T> connect_data = data.slice(connect_edge_range);
threading::parallel_for(poly_selection.index_range(), 512, [&](const IndexRange range) {
for (const int i_selection : range) {
@ -1268,10 +1269,14 @@ static void extrude_individual_mesh_faces(
}
}
});
break;
}
case ATTR_DOMAIN_FACE: {
/* Each side face gets the values from the corresponding new face. */
});
break;
}
case ATTR_DOMAIN_FACE: {
/* Each side face gets the values from the corresponding new face. */
bke::attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
MutableSpan<T> data = attribute.span.typed<T>();
MutableSpan<T> new_data = data.slice(side_poly_range);
threading::parallel_for(poly_selection.index_range(), 1024, [&](const IndexRange range) {
for (const int i_selection : range) {
@ -1280,11 +1285,15 @@ static void extrude_individual_mesh_faces(
new_data.slice(extrude_range).fill(data[poly_index]);
}
});
break;
}
case ATTR_DOMAIN_CORNER: {
/* Each corner on a side face gets its value from the matching corner on an extruded
* face. */
});
break;
}
case ATTR_DOMAIN_CORNER: {
/* Each corner on a side face gets its value from the matching corner on an extruded
* face. */
bke::attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
MutableSpan<T> data = attribute.span.typed<T>();
MutableSpan<T> new_data = data.slice(side_loop_range);
threading::parallel_for(poly_selection.index_range(), 256, [&](const IndexRange range) {
for (const int i_selection : range) {
@ -1308,12 +1317,12 @@ static void extrude_individual_mesh_faces(
}
}
});
break;
}
default:
BLI_assert_unreachable();
});
break;
}
});
default:
BLI_assert_unreachable();
}
attribute.finish();
return true;