Geometry Nodes: Extrude: Skip topology map with no edge attributes
In vertex extrusion mode, when there are no edge attributes we can avoid creating a topology map meant for mixing old values. This makes simulation caching for a curl noise demo file from user Higgsas 10% faster (from 44s to 40s to calculate 230 frames).
This commit is contained in:
@@ -115,10 +115,8 @@ static void expand_mesh(Mesh &mesh,
|
||||
const int vert_expand,
|
||||
const int edge_expand,
|
||||
const int poly_expand,
|
||||
const int loop_expand,
|
||||
const AnonymousAttributePropagationInfo &propagation_info)
|
||||
const int loop_expand)
|
||||
{
|
||||
remove_non_propagated_attributes(mesh.attributes_for_write(), propagation_info);
|
||||
/* Remove types that aren't supported for interpolation in this node. */
|
||||
if (vert_expand != 0) {
|
||||
CustomData_free_layers(&mesh.vdata, CD_ORCO, mesh.totvert);
|
||||
@@ -202,7 +200,9 @@ static MutableSpan<int> get_orig_index_layer(Mesh &mesh, const eAttrDomain domai
|
||||
* result point.
|
||||
*/
|
||||
template<typename T, typename GetMixIndicesFn>
|
||||
void copy_with_mixing(MutableSpan<T> dst, Span<T> src, GetMixIndicesFn get_mix_indices_fn)
|
||||
void copy_with_mixing(const Span<T> src,
|
||||
const GetMixIndicesFn &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)};
|
||||
@@ -215,6 +215,15 @@ void copy_with_mixing(MutableSpan<T> dst, Span<T> src, GetMixIndicesFn get_mix_i
|
||||
});
|
||||
}
|
||||
|
||||
template<typename GetMixIndicesFn>
|
||||
void copy_with_mixing(const GSpan src, const GetMixIndicesFn &get_mix_indices_fn, GMutableSpan dst)
|
||||
{
|
||||
bke::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
copy_with_mixing(src.typed<T>(), get_mix_indices_fn, dst.typed<T>());
|
||||
});
|
||||
}
|
||||
|
||||
static Array<Vector<int>> create_vert_to_edge_map(const int vert_size,
|
||||
const Span<int2> edges,
|
||||
const int vert_offset = 0)
|
||||
@@ -246,61 +255,67 @@ static void extrude_mesh_vertices(Mesh &mesh,
|
||||
evaluator.evaluate();
|
||||
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
|
||||
|
||||
/* This allows parallelizing attribute mixing for new edges. */
|
||||
Array<Vector<int>> vert_to_edge_map = create_vert_to_edge_map(orig_vert_size, mesh.edges());
|
||||
remove_non_propagated_attributes(mesh.attributes_for_write(), propagation_info);
|
||||
|
||||
expand_mesh(mesh, selection.size(), selection.size(), 0, 0, propagation_info);
|
||||
Set<AttributeIDRef> point_ids;
|
||||
Set<AttributeIDRef> edge_ids;
|
||||
mesh.attributes().for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
if (meta_data.data_type == CD_PROP_STRING) {
|
||||
return true;
|
||||
}
|
||||
if (meta_data.domain == ATTR_DOMAIN_POINT) {
|
||||
if (id.name() != "position") {
|
||||
point_ids.add(id);
|
||||
}
|
||||
}
|
||||
else if (meta_data.domain == ATTR_DOMAIN_EDGE) {
|
||||
if (id.name() != ".edge_verts") {
|
||||
edge_ids.add(id);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
/* This allows parallelizing attribute mixing for new edges. */
|
||||
Array<Vector<int>> vert_to_edge_map;
|
||||
if (!edge_ids.is_empty()) {
|
||||
vert_to_edge_map = create_vert_to_edge_map(orig_vert_size, mesh.edges());
|
||||
}
|
||||
|
||||
expand_mesh(mesh, selection.size(), selection.size(), 0, 0);
|
||||
|
||||
const IndexRange new_vert_range{orig_vert_size, selection.size()};
|
||||
const IndexRange new_edge_range{orig_edge_size, selection.size()};
|
||||
|
||||
MutableSpan<float3> new_positions = mesh.vert_positions_for_write().slice(new_vert_range);
|
||||
MutableSpan<int2> new_edges = mesh.edges_for_write().slice(new_edge_range);
|
||||
|
||||
for (const int i_selection : selection.index_range()) {
|
||||
new_edges[i_selection] = int2(selection[i_selection], new_vert_range[i_selection]);
|
||||
}
|
||||
|
||||
MutableAttributeAccessor attributes = mesh.attributes_for_write();
|
||||
|
||||
attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
|
||||
if (!ELEM(meta_data.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE)) {
|
||||
return true;
|
||||
}
|
||||
if (ELEM(id.name(), ".corner_vert", ".corner_edge", ".edge_verts")) {
|
||||
return true;
|
||||
}
|
||||
if (meta_data.data_type == CD_PROP_STRING) {
|
||||
return true;
|
||||
}
|
||||
GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
|
||||
id, meta_data.domain, meta_data.data_type);
|
||||
switch (attribute.domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
/* New vertices copy the attribute values from their source vertex. */
|
||||
array_utils::gather(attribute.span, selection, attribute.span.slice(new_vert_range));
|
||||
break;
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
bke::attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
MutableSpan<T> data = attribute.span.typed<T>();
|
||||
/* New edge values are mixed from of all the edges connected to the source vertex. */
|
||||
copy_with_mixing(data.slice(new_edge_range), data.as_span(), [&](const int i) {
|
||||
return vert_to_edge_map[selection[i]].as_span();
|
||||
});
|
||||
});
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
/* New vertices copy the attribute values from their source vertex. */
|
||||
for (const AttributeIDRef &id : point_ids) {
|
||||
GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
|
||||
array_utils::gather(attribute.span, selection, attribute.span.slice(new_vert_range));
|
||||
attribute.finish();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/* New edge values are mixed from of all the edges connected to the source vertex. */
|
||||
for (const AttributeIDRef &id : edge_ids) {
|
||||
GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
|
||||
copy_with_mixing(
|
||||
attribute.span,
|
||||
[&](const int i) { return vert_to_edge_map[selection[i]].as_span(); },
|
||||
attribute.span.slice(new_edge_range));
|
||||
attribute.finish();
|
||||
}
|
||||
|
||||
MutableSpan<float3> positions = mesh.vert_positions_for_write();
|
||||
MutableSpan<float3> new_positions = positions.slice(new_vert_range);
|
||||
threading::parallel_for(selection.index_range(), 1024, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
new_positions[i] += offsets[selection[i]];
|
||||
new_positions[i] = positions[selection[i]] + offsets[selection[i]];
|
||||
}
|
||||
});
|
||||
|
||||
@@ -436,12 +451,12 @@ static void extrude_mesh_edges(Mesh &mesh,
|
||||
/* Every new polygon is a quad with four corners. */
|
||||
const IndexRange new_loop_range{orig_loop_size, new_poly_range.size() * 4};
|
||||
|
||||
remove_non_propagated_attributes(mesh.attributes_for_write(), propagation_info);
|
||||
expand_mesh(mesh,
|
||||
new_vert_range.size(),
|
||||
connect_edge_range.size() + duplicate_edge_range.size(),
|
||||
new_poly_range.size(),
|
||||
new_loop_range.size(),
|
||||
propagation_info);
|
||||
new_loop_range.size());
|
||||
|
||||
MutableSpan<int2> edges = mesh.edges_for_write();
|
||||
MutableSpan<int2> connect_edges = edges.slice(connect_edge_range);
|
||||
@@ -537,18 +552,19 @@ static void extrude_mesh_edges(Mesh &mesh,
|
||||
array_utils::gather(data.as_span(), edge_selection, duplicate_data);
|
||||
|
||||
/* Edges connected to original vertices mix values of selected connected edges. */
|
||||
MutableSpan<T> connect_data = data.slice(connect_edge_range);
|
||||
copy_with_mixing(connect_data, duplicate_data.as_span(), [&](const int i_new_vert) {
|
||||
return new_vert_to_duplicate_edge_map[i_new_vert].as_span();
|
||||
});
|
||||
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.slice(new_poly_range), data.as_span(), [&](const int i) {
|
||||
return edge_to_poly_map[edge_selection[i]].as_span();
|
||||
});
|
||||
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;
|
||||
}
|
||||
@@ -800,12 +816,12 @@ static void extrude_mesh_face_regions(Mesh &mesh,
|
||||
/* The loops that form the new side faces. */
|
||||
const IndexRange side_loop_range{orig_corner_verts.size(), side_poly_range.size() * 4};
|
||||
|
||||
remove_non_propagated_attributes(mesh.attributes_for_write(), propagation_info);
|
||||
expand_mesh(mesh,
|
||||
new_vert_range.size(),
|
||||
connect_edge_range.size() + boundary_edge_range.size() + new_inner_edge_range.size(),
|
||||
side_poly_range.size(),
|
||||
side_loop_range.size(),
|
||||
propagation_info);
|
||||
side_loop_range.size());
|
||||
|
||||
MutableSpan<int2> edges = mesh.edges_for_write();
|
||||
MutableSpan<int2> connect_edges = edges.slice(connect_edge_range);
|
||||
@@ -941,10 +957,10 @@ static void extrude_mesh_face_regions(Mesh &mesh,
|
||||
array_utils::gather(data.as_span(), new_inner_edge_indices.as_span(), new_inner_data);
|
||||
|
||||
/* Edges connected to original vertices mix values of selected connected edges. */
|
||||
MutableSpan<T> connect_data = data.slice(connect_edge_range);
|
||||
copy_with_mixing(connect_data, boundary_data.as_span(), [&](const int i) {
|
||||
return new_vert_to_duplicate_edge_map[i].as_span();
|
||||
});
|
||||
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: {
|
||||
@@ -1132,12 +1148,12 @@ static void extrude_individual_mesh_faces(
|
||||
const IndexRange side_poly_range{orig_polys.size(), duplicate_edge_range.size()};
|
||||
const IndexRange side_loop_range{orig_loop_size, side_poly_range.size() * 4};
|
||||
|
||||
remove_non_propagated_attributes(mesh.attributes_for_write(), propagation_info);
|
||||
expand_mesh(mesh,
|
||||
new_vert_range.size(),
|
||||
connect_edge_range.size() + duplicate_edge_range.size(),
|
||||
side_poly_range.size(),
|
||||
side_loop_range.size(),
|
||||
propagation_info);
|
||||
side_loop_range.size());
|
||||
|
||||
MutableSpan<float3> new_positions = mesh.vert_positions_for_write().slice(new_vert_range);
|
||||
MutableSpan<int2> edges = mesh.edges_for_write();
|
||||
|
||||
Reference in New Issue
Block a user