Files
test/source/blender/geometry/intern/extract_elements.cc
Hans Goudey 1f92fd7577 Refactor: Use AttrType instead of CustomData type in attribute API
Change `eCustomDataType` to `bke::AttrType` for uses of the attribute
API (the `AttributeAccessor` one anyway). I didn't touch any values that
might be saved in files; those should be handled on a case by case basis.

Part of #122398

Pull Request: https://projects.blender.org/blender/blender/pulls/141301
2025-07-01 22:14:26 +02:00

533 lines
20 KiB
C++

/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "GEO_extract_elements.hh"
#include "BLI_index_mask.hh"
#include "BKE_attribute.hh"
#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_instances.hh"
#include "BKE_mesh.hh"
#include "BKE_pointcloud.hh"
namespace blender::geometry {
using bke::AttrDomain;
struct PropagationAttribute {
StringRef name;
bke::AttrType data_type;
AttrDomain domain;
GVArray data;
};
Array<Mesh *> extract_mesh_vertices(const Mesh &mesh,
const IndexMask &mask,
const bke::AttributeFilter &attribute_filter)
{
BLI_assert(mask.min_array_size() <= mesh.verts_num);
Array<Mesh *> elements(mask.size(), nullptr);
const bke::AttributeAccessor src_attributes = mesh.attributes();
Vector<PropagationAttribute> propagation_attributes;
src_attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
if (iter.data_type == bke::AttrType::String) {
return;
}
if (attribute_filter.allow_skip(iter.name)) {
return;
}
const bke::GAttributeReader src_attribute = iter.get(AttrDomain::Point);
if (!src_attribute) {
return;
}
propagation_attributes.append({iter.name, iter.data_type, AttrDomain::Point, *src_attribute});
});
mask.foreach_index(GrainSize(32), [&](const int vert_i, const int element_i) {
Mesh *element = BKE_mesh_new_nomain(1, 0, 0, 0);
BKE_mesh_copy_parameters_for_eval(element, &mesh);
bke::MutableAttributeAccessor element_attributes = element->attributes_for_write();
for (const PropagationAttribute &src_attribute : propagation_attributes) {
bke::GSpanAttributeWriter dst = element_attributes.lookup_or_add_for_write_only_span(
src_attribute.name, AttrDomain::Point, src_attribute.data_type);
if (!dst) {
continue;
}
src_attribute.data.get(vert_i, dst.span[0]);
dst.finish();
}
elements[element_i] = element;
});
return elements;
}
Array<Mesh *> extract_mesh_edges(const Mesh &mesh,
const IndexMask &mask,
const bke::AttributeFilter &attribute_filter)
{
BLI_assert(mask.min_array_size() <= mesh.edges_num);
Array<Mesh *> elements(mask.size(), nullptr);
const Span<int2> src_edges = mesh.edges();
const bke::AttributeAccessor src_attributes = mesh.attributes();
Vector<PropagationAttribute> propagation_attributes;
src_attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
if (iter.data_type == bke::AttrType::String) {
return;
}
if (iter.name == ".edge_verts") {
return;
}
if (attribute_filter.allow_skip(iter.name)) {
return;
}
const bke::GAttributeReader src_attribute = iter.get();
if (ELEM(src_attribute.domain, AttrDomain::Point, AttrDomain::Edge)) {
propagation_attributes.append(
{iter.name, iter.data_type, src_attribute.domain, *src_attribute});
}
else if (src_attribute.domain == AttrDomain::Corner) {
if (GVArray adapted_attribute = src_attributes.adapt_domain(
*src_attribute, src_attribute.domain, AttrDomain::Point))
{
propagation_attributes.append(
{iter.name, iter.data_type, AttrDomain::Point, adapted_attribute});
}
}
else if (src_attribute.domain == AttrDomain::Face) {
if (GVArray adapted_attribute = src_attributes.adapt_domain(
*src_attribute, src_attribute.domain, AttrDomain::Edge))
{
propagation_attributes.append(
{iter.name, iter.data_type, AttrDomain::Edge, adapted_attribute});
}
}
});
mask.foreach_index(GrainSize(32), [&](const int edge_i, const int element_i) {
Mesh *element = BKE_mesh_new_nomain(2, 1, 0, 0);
BKE_mesh_copy_parameters_for_eval(element, &mesh);
MutableSpan<int2> element_edges = element->edges_for_write();
element_edges[0] = {0, 1};
const int2 &src_edge = src_edges[edge_i];
bke::MutableAttributeAccessor element_attributes = element->attributes_for_write();
for (const PropagationAttribute &src_attribute : propagation_attributes) {
bke::GSpanAttributeWriter dst = element_attributes.lookup_or_add_for_write_only_span(
src_attribute.name, src_attribute.domain, src_attribute.data_type);
if (!dst) {
continue;
}
if (src_attribute.domain == AttrDomain::Point) {
src_attribute.data.get(src_edge[0], dst.span[0]);
src_attribute.data.get(src_edge[1], dst.span[1]);
}
else {
src_attribute.data.get(edge_i, dst.span[0]);
}
dst.finish();
}
elements[element_i] = element;
});
return elements;
}
Array<Mesh *> extract_mesh_faces(const Mesh &mesh,
const IndexMask &mask,
const bke::AttributeFilter &attribute_filter)
{
BLI_assert(mask.min_array_size() <= mesh.faces_num);
Array<Mesh *> elements(mask.size(), nullptr);
const Span<int> src_corner_verts = mesh.corner_verts();
const Span<int> src_corner_edges = mesh.corner_edges();
const OffsetIndices<int> src_faces = mesh.faces();
const bke::AttributeAccessor src_attributes = mesh.attributes();
Vector<PropagationAttribute> propagation_attributes;
src_attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
if (iter.data_type == bke::AttrType::String) {
return;
}
if (ELEM(iter.name, ".edge_verts", ".corner_edge", ".corner_vert")) {
return;
}
if (attribute_filter.allow_skip(iter.name)) {
return;
}
const bke::GAttributeReader src_attribute = iter.get();
if (!src_attribute) {
return;
}
propagation_attributes.append(
{iter.name, iter.data_type, src_attribute.domain, *src_attribute});
});
mask.foreach_index(GrainSize(32), [&](const int face_i, const int element_i) {
const IndexRange src_face = src_faces[face_i];
const int verts_num = src_face.size();
Mesh *element = BKE_mesh_new_nomain(verts_num, verts_num, 1, verts_num);
BKE_mesh_copy_parameters_for_eval(element, &mesh);
MutableSpan<int2> element_edges = element->edges_for_write();
MutableSpan<int> element_corner_verts = element->corner_verts_for_write();
MutableSpan<int> element_corner_edges = element->corner_edges_for_write();
MutableSpan<int> element_face_offsets = element->face_offsets_for_write();
for (const int i : IndexRange(verts_num)) {
element_edges[i] = {i, i + 1};
element_corner_verts[i] = i;
element_corner_edges[i] = i;
}
element_edges.last()[1] = 0;
element_face_offsets[0] = 0;
element_face_offsets[1] = verts_num;
bke::MutableAttributeAccessor element_attributes = element->attributes_for_write();
for (const PropagationAttribute &src_attribute : propagation_attributes) {
bke::GSpanAttributeWriter dst = element_attributes.lookup_or_add_for_write_only_span(
src_attribute.name, src_attribute.domain, src_attribute.data_type);
if (!dst) {
continue;
}
switch (src_attribute.domain) {
case AttrDomain::Point: {
for (const int i : IndexRange(verts_num)) {
const int src_corner_i = src_face[i];
const int src_vert_i = src_corner_verts[src_corner_i];
src_attribute.data.get(src_vert_i, dst.span[i]);
}
break;
}
case AttrDomain::Edge: {
for (const int i : IndexRange(verts_num)) {
const int src_corner_i = src_face[i];
const int src_edge_i = src_corner_edges[src_corner_i];
src_attribute.data.get(src_edge_i, dst.span[i]);
}
break;
}
case AttrDomain::Corner: {
src_attribute.data.materialize_compressed(src_face, dst.span.data());
break;
}
case AttrDomain::Face: {
src_attribute.data.get(face_i, dst.span[0]);
break;
}
default:
BLI_assert_unreachable();
break;
}
dst.finish();
}
elements[element_i] = element;
});
return elements;
}
Array<PointCloud *> extract_pointcloud_points(const PointCloud &pointcloud,
const IndexMask &mask,
const bke::AttributeFilter &attribute_filter)
{
BLI_assert(mask.min_array_size() <= pointcloud.totpoint);
Array<PointCloud *> elements(mask.size(), nullptr);
const bke::AttributeAccessor src_attributes = pointcloud.attributes();
mask.foreach_index(GrainSize(32), [&](const int point_i, const int element_i) {
PointCloud *element = BKE_pointcloud_new_nomain(1);
element->totcol = pointcloud.totcol;
element->mat = static_cast<Material **>(MEM_dupallocN(pointcloud.mat));
bke::gather_attributes(src_attributes,
AttrDomain::Point,
AttrDomain::Point,
attribute_filter,
Span<int>{point_i},
element->attributes_for_write());
elements[element_i] = element;
});
return elements;
}
Array<Curves *> extract_curves_points(const Curves &curves,
const IndexMask &mask,
const bke::AttributeFilter &attribute_filter)
{
BLI_assert(mask.min_array_size() <= curves.geometry.point_num);
Array<Curves *> elements(mask.size(), nullptr);
const bke::CurvesGeometry &src_curves = curves.geometry.wrap();
const bke::AttributeAccessor src_attributes = src_curves.attributes();
const Array<int> point_to_curve_map = src_curves.point_to_curve_map();
mask.foreach_index(GrainSize(32), [&](const int point_i, const int element_i) {
const int curve_i = point_to_curve_map[point_i];
/* Actual curve type is propagated below. */
Curves *element = bke::curves_new_nomain_single(1, CURVE_TYPE_POLY);
bke::curves_copy_parameters(curves, *element);
bke::MutableAttributeAccessor element_attributes =
element->geometry.wrap().attributes_for_write();
bke::gather_attributes(src_attributes,
AttrDomain::Point,
AttrDomain::Point,
attribute_filter,
Span<int>{point_i},
element_attributes);
bke::gather_attributes(src_attributes,
AttrDomain::Curve,
AttrDomain::Curve,
attribute_filter,
Span<int>{curve_i},
element_attributes);
elements[element_i] = element;
});
return elements;
}
Array<Curves *> extract_curves(const Curves &curves,
const IndexMask &mask,
const bke::AttributeFilter &attribute_filter)
{
BLI_assert(mask.min_array_size() <= curves.geometry.curve_num);
Array<Curves *> elements(mask.size(), nullptr);
const bke::CurvesGeometry &src_curves = curves.geometry.wrap();
const bke::AttributeAccessor src_attributes = src_curves.attributes();
const OffsetIndices<int> src_points_by_curve = src_curves.points_by_curve();
mask.foreach_index(GrainSize(32), [&](const int curve_i, const int element_i) {
const IndexRange src_points = src_points_by_curve[curve_i];
const int points_num = src_points.size();
Curves *element = bke::curves_new_nomain(points_num, 1);
bke::MutableAttributeAccessor element_attributes =
element->geometry.wrap().attributes_for_write();
bke::curves_copy_parameters(curves, *element);
bke::gather_attributes(src_attributes,
AttrDomain::Point,
AttrDomain::Point,
attribute_filter,
src_points,
element_attributes);
bke::gather_attributes(src_attributes,
AttrDomain::Curve,
AttrDomain::Curve,
attribute_filter,
Span<int>{curve_i},
element_attributes);
element->geometry.wrap().update_curve_types();
elements[element_i] = element;
});
return elements;
}
Array<bke::Instances *> extract_instances(const bke::Instances &instances,
const IndexMask &mask,
const bke::AttributeFilter &attribute_filter)
{
using bke::Instances;
BLI_assert(mask.min_array_size() <= instances.instances_num());
Array<Instances *> elements(mask.size(), nullptr);
const bke::AttributeAccessor src_attributes = instances.attributes();
const Span<bke::InstanceReference> src_references = instances.references();
const Span<int> src_reference_handles = instances.reference_handles();
const Span<float4x4> src_transforms = instances.transforms();
mask.foreach_index(GrainSize(32), [&](const int instance_i, const int element_i) {
const int old_handle = src_reference_handles[instance_i];
const bke::InstanceReference &old_reference = src_references[old_handle];
const float4x4 &old_transform = src_transforms[instance_i];
Instances *element = new Instances();
const int new_handle = element->add_new_reference(old_reference);
element->add_instance(new_handle, old_transform);
bke::gather_attributes(src_attributes,
AttrDomain::Instance,
AttrDomain::Instance,
bke::attribute_filter_with_skip_ref(
attribute_filter, {".reference_index", "instance_transform"}),
Span<int>{instance_i},
element->attributes_for_write());
elements[element_i] = element;
});
return elements;
}
Array<GreasePencil *> extract_greasepencil_layers(const GreasePencil &grease_pencil,
const IndexMask &mask,
const bke::AttributeFilter &attribute_filter)
{
using namespace bke::greasepencil;
BLI_assert(mask.min_array_size() <= grease_pencil.layers().size());
Array<GreasePencil *> elements(mask.size(), nullptr);
const bke::AttributeAccessor src_attributes = grease_pencil.attributes();
const Span<const Layer *> src_layers = grease_pencil.layers();
mask.foreach_index(GrainSize(32), [&](const int layer_i, const int element_i) {
GreasePencil *element = BKE_grease_pencil_new_nomain();
element->material_array = static_cast<Material **>(
MEM_dupallocN(grease_pencil.material_array));
element->material_array_num = grease_pencil.material_array_num;
const Layer &src_layer = *src_layers[layer_i];
const Drawing *src_drawing = grease_pencil.get_eval_drawing(src_layer);
if (src_drawing) {
Layer &new_layer = element->add_layer(src_layer.name());
Drawing &drawing = *element->insert_frame(new_layer, element->runtime->eval_frame);
drawing.strokes_for_write() = src_drawing->strokes();
bke::gather_attributes(src_attributes,
AttrDomain::Layer,
AttrDomain::Layer,
attribute_filter,
Span<int>{layer_i},
element->attributes_for_write());
}
elements[element_i] = element;
});
return elements;
}
Array<GreasePencil *> extract_greasepencil_layer_points(
const GreasePencil &grease_pencil,
int layer_i,
const IndexMask &mask,
const bke::AttributeFilter &attribute_filter)
{
using namespace bke::greasepencil;
const Layer &src_layer = grease_pencil.layer(layer_i);
const Drawing &src_drawing = *grease_pencil.get_eval_drawing(src_layer);
const bke::CurvesGeometry &src_curves = src_drawing.strokes();
const bke::AttributeAccessor src_layer_attributes = grease_pencil.attributes();
const bke::AttributeAccessor src_curves_attributes = src_curves.attributes();
const Array<int> point_to_curve_map = src_curves.point_to_curve_map();
Array<GreasePencil *> elements(mask.size(), nullptr);
mask.foreach_index(GrainSize(32), [&](const int point_i, const int element_i) {
const int curve_i = point_to_curve_map[point_i];
GreasePencil *element = BKE_grease_pencil_new_nomain();
element->material_array = static_cast<Material **>(
MEM_dupallocN(grease_pencil.material_array));
element->material_array_num = grease_pencil.material_array_num;
Layer &new_layer = element->add_layer(src_layer.name());
Drawing &drawing = *element->insert_frame(new_layer, element->runtime->eval_frame);
bke::CurvesGeometry &new_curves = drawing.strokes_for_write();
new_curves.resize(1, 1);
new_curves.offsets_for_write().last() = 1;
bke::gather_attributes(src_curves_attributes,
AttrDomain::Point,
AttrDomain::Point,
attribute_filter,
Span<int>{point_i},
new_curves.attributes_for_write());
bke::gather_attributes(src_curves_attributes,
AttrDomain::Curve,
AttrDomain::Curve,
attribute_filter,
Span<int>{curve_i},
new_curves.attributes_for_write());
bke::gather_attributes(src_layer_attributes,
AttrDomain::Layer,
AttrDomain::Layer,
attribute_filter,
Span<int>{layer_i},
element->attributes_for_write());
new_curves.update_curve_types();
elements[element_i] = element;
});
return elements;
}
Array<GreasePencil *> extract_greasepencil_layer_curves(
const GreasePencil &grease_pencil,
const int layer_i,
const IndexMask &mask,
const bke::AttributeFilter &attribute_filter)
{
using namespace bke::greasepencil;
const Layer &src_layer = grease_pencil.layer(layer_i);
const Drawing &src_drawing = *grease_pencil.get_eval_drawing(src_layer);
const bke::CurvesGeometry &src_curves = src_drawing.strokes();
const bke::AttributeAccessor src_layer_attributes = grease_pencil.attributes();
const bke::AttributeAccessor src_curves_attributes = src_curves.attributes();
const OffsetIndices<int> src_points_by_curve = src_curves.points_by_curve();
Array<GreasePencil *> elements(mask.size(), nullptr);
mask.foreach_index(GrainSize(32), [&](const int curve_i, const int element_i) {
const IndexRange src_points = src_points_by_curve[curve_i];
const int points_num = src_points.size();
GreasePencil *element = BKE_grease_pencil_new_nomain();
element->material_array = static_cast<Material **>(
MEM_dupallocN(grease_pencil.material_array));
element->material_array_num = grease_pencil.material_array_num;
Layer &new_layer = element->add_layer(src_layer.name());
Drawing &drawing = *element->insert_frame(new_layer, element->runtime->eval_frame);
bke::CurvesGeometry &new_curves = drawing.strokes_for_write();
new_curves.resize(points_num, 1);
bke::gather_attributes(src_curves_attributes,
AttrDomain::Point,
AttrDomain::Point,
attribute_filter,
src_points,
new_curves.attributes_for_write());
bke::gather_attributes(src_curves_attributes,
AttrDomain::Curve,
AttrDomain::Curve,
attribute_filter,
Span<int>{curve_i},
new_curves.attributes_for_write());
bke::gather_attributes(src_layer_attributes,
AttrDomain::Layer,
AttrDomain::Layer,
attribute_filter,
Span<int>{layer_i},
element->attributes_for_write());
new_curves.update_curve_types();
elements[element_i] = element;
});
return elements;
}
} // namespace blender::geometry