Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2021-09-28 20:14:13 +0300
committerHans Goudey <h.goudey@me.com>2021-09-28 20:14:13 +0300
commit262b2118565826177133013c324212c66d882456 (patch)
tree0be55bc95b3c79c712642aa6157f194baf54785a
parent797064544ea3e176c511f7ff7c1303498a783182 (diff)
Geometry Nodes: Mesh Point Cloud Conversion Nodes
This commit adds nodes to do direct conversion between meshes and point clouds in geometry nodes. The conversion from mesh to points is helpful to instance once per face, or once per edge, which was previously only possibly with ugly work-arounds. Fields can be evaluated on the mesh to pass them to the points with the attribute capture node. The other conversion, point cloud to mesh vertices, is a bit less obvious, though it is still a common request from users. It's helpful for flexibility when passing data around, better visualization in the viewport (and in the future, cycles), and the simplicity of points. This is a step towards T91754, where point clouds are currently combined with meshes when outputing to the next modifier after geometry nodes. Since we're removing the implicit behavior for realizing instances, it feels natural to use an explicit node to convert points to vertices too. Differential Revision: https://developer.blender.org/D12657
-rw-r--r--release/scripts/startup/nodeitems_builtins.py2
-rw-r--r--source/blender/blenkernel/BKE_node.h2
-rw-r--r--source/blender/blenkernel/intern/node.cc2
-rw-r--r--source/blender/makesdna/DNA_node_types.h12
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c36
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc2
-rw-r--r--source/blender/nodes/CMakeLists.txt2
-rw-r--r--source/blender/nodes/NOD_geometry.h2
-rw-r--r--source/blender/nodes/NOD_static_types.h2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc189
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc118
11 files changed, 368 insertions, 1 deletions
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index 2c13d4eac9b..d1f1cfef682 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -582,6 +582,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeBoolean"),
NodeItem("GeometryNodeTriangulate"),
NodeItem("GeometryNodeMeshSubdivide"),
+ NodeItem("GeometryNodePointsToVertices", poll=geometry_nodes_fields_poll),
]),
GeometryNodeCategory("GEO_PRIMITIVES_MESH", "Mesh Primitives", items=[
NodeItem("GeometryNodeMeshCircle"),
@@ -594,6 +595,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeMeshUVSphere"),
]),
GeometryNodeCategory("GEO_POINT", "Point", items=[
+ NodeItem("GeometryNodeMeshToPoints", poll=geometry_nodes_fields_poll),
NodeItem("GeometryNodeInstanceOnPoints", poll=geometry_nodes_fields_poll),
NodeItem("GeometryNodeDistributePointsOnFaces", poll=geometry_nodes_fields_poll),
NodeItem("GeometryNodeLegacyPointDistribute", poll=geometry_nodes_fields_legacy_poll),
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 16d4eb51d31..70700246d29 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -1503,6 +1503,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_DISTRIBUTE_POINTS_ON_FACES 1090
#define GEO_NODE_STRING_TO_CURVES 1091
#define GEO_NODE_INSTANCE_ON_POINTS 1092
+#define GEO_NODE_MESH_TO_POINTS 1093
+#define GEO_NODE_POINTS_TO_VERTICES 1094
/** \} */
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index c8ba7991f31..8a6f1471ddb 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -5778,6 +5778,7 @@ static void registerGeometryNodes()
register_node_type_geo_mesh_primitive_uv_sphere();
register_node_type_geo_mesh_subdivide();
register_node_type_geo_mesh_to_curve();
+ register_node_type_geo_mesh_to_points();
register_node_type_geo_object_info();
register_node_type_geo_point_distribute();
register_node_type_geo_point_instance();
@@ -5785,6 +5786,7 @@ static void registerGeometryNodes()
register_node_type_geo_point_scale();
register_node_type_geo_point_separate();
register_node_type_geo_point_translate();
+ register_node_type_geo_points_to_vertices();
register_node_type_geo_points_to_volume();
register_node_type_geo_raycast();
register_node_type_geo_realize_instances();
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index f7f2ccaa7be..538a80ce3e1 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -1511,6 +1511,11 @@ typedef struct NodeGeometryCurveFill {
uint8_t mode;
} NodeGeometryCurveFill;
+typedef struct NodeGeometryMeshToPoints {
+ /* GeometryNodeMeshToPointsMode */
+ uint8_t mode;
+} NodeGeometryMeshToPoints;
+
typedef struct NodeGeometryAttributeCapture {
/* CustomDataType. */
int8_t data_type;
@@ -2124,6 +2129,13 @@ typedef enum GeometryNodeCurveFillMode {
GEO_NODE_CURVE_FILL_MODE_NGONS = 1,
} GeometryNodeCurveFillMode;
+typedef enum GeometryNodeMeshToPointsMode {
+ GEO_NODE_MESH_TO_POINTS_VERTICES = 0,
+ GEO_NODE_MESH_TO_POINTS_EDGES = 1,
+ GEO_NODE_MESH_TO_POINTS_FACES = 2,
+ GEO_NODE_MESH_TO_POINTS_CORNERS = 3,
+} GeometryNodeMeshToPointsMode;
+
typedef enum GeometryNodeStringToCurvesOverflowMode {
GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW = 0,
GEO_NODE_STRING_TO_CURVES_MODE_SCALE_TO_FIT = 1,
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 04f60c0d229..556b8e94fe6 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -10306,6 +10306,42 @@ static void def_geo_curve_to_points(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_mesh_to_points(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ static EnumPropertyItem mode_items[] = {
+ {GEO_NODE_MESH_TO_POINTS_VERTICES,
+ "VERTICES",
+ 0,
+ "Vertices",
+ "Create a point in the point cloud for each selected vertex"},
+ {GEO_NODE_MESH_TO_POINTS_EDGES,
+ "EDGES",
+ 0,
+ "Edges",
+ "Create a point in the point cloud for each selected edge"},
+ {GEO_NODE_MESH_TO_POINTS_FACES,
+ "FACES",
+ 0,
+ "Faces",
+ "Create a point in the point cloud for each selected face"},
+ {GEO_NODE_MESH_TO_POINTS_CORNERS,
+ "CORNERS",
+ 0,
+ "Corners",
+ "Create a point in the point cloud for each selected face corner"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryMeshToPoints", "storage");
+
+ prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, mode_items);
+ RNA_def_property_ui_text(prop, "Mode", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
static void def_geo_curve_trim(StructRNA *srna)
{
PropertyRNA *prop;
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
index 6f18c4d40db..d9ea5696bf5 100644
--- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
@@ -329,7 +329,7 @@ static void get_socket_value(const SocketRef &socket, void *r_value)
if (bsocket.flag & SOCK_HIDE_VALUE) {
const bNode &bnode = *socket.bnode();
if (bsocket.type == SOCK_VECTOR) {
- if (ELEM(bnode.type, GEO_NODE_SET_POSITION, SH_NODE_TEX_NOISE)) {
+ if (ELEM(bnode.type, GEO_NODE_SET_POSITION, SH_NODE_TEX_NOISE, GEO_NODE_MESH_TO_POINTS)) {
new (r_value) Field<float3>(
std::make_shared<bke::AttributeFieldInput>("position", CPPType::get<float3>()));
return;
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 9ce3d59c121..d2ae91ee4bc 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -231,7 +231,9 @@ set(SRC
geometry/nodes/node_geo_mesh_primitive_line.cc
geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc
geometry/nodes/node_geo_mesh_subdivide.cc
+ geometry/nodes/node_geo_mesh_to_points.cc
geometry/nodes/node_geo_object_info.cc
+ geometry/nodes/node_geo_points_to_vertices.cc
geometry/nodes/node_geo_realize_instances.cc
geometry/nodes/node_geo_separate_components.cc
geometry/nodes/node_geo_set_position.cc
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index bf780042600..6cbb45f80aa 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -101,6 +101,7 @@ void register_node_type_geo_mesh_primitive_line(void);
void register_node_type_geo_mesh_primitive_uv_sphere(void);
void register_node_type_geo_mesh_subdivide(void);
void register_node_type_geo_mesh_to_curve(void);
+void register_node_type_geo_mesh_to_points(void);
void register_node_type_geo_object_info(void);
void register_node_type_geo_point_distribute(void);
void register_node_type_geo_point_instance(void);
@@ -108,6 +109,7 @@ void register_node_type_geo_point_rotate(void);
void register_node_type_geo_point_scale(void);
void register_node_type_geo_point_separate(void);
void register_node_type_geo_point_translate(void);
+void register_node_type_geo_points_to_vertices(void);
void register_node_type_geo_points_to_volume(void);
void register_node_type_geo_raycast(void);
void register_node_type_geo_realize_instances(void);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index f94394d4986..d653400361a 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -358,7 +358,9 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "")
DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Mesh Subdivide", "")
+DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "")
DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
+DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "")
DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "")
DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "")
DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "")
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
new file mode 100644
index 00000000000..2f59a3c968b
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
@@ -0,0 +1,189 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_pointcloud.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+using blender::Array;
+
+namespace blender::nodes {
+
+static void geo_node_mesh_to_points_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Mesh");
+ b.add_input<decl::Vector>("Position").implicit_field();
+ b.add_input<decl::Float>("Radius")
+ .default_value(0.05f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .supports_field();
+ b.add_input<decl::Bool>("Selection").default_value(true).supports_field().hide_value();
+ b.add_output<decl::Geometry>("Points");
+}
+
+static void geo_node_mesh_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
+}
+
+static void geo_node_mesh_to_points_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeGeometryMeshToPoints *data = (NodeGeometryMeshToPoints *)MEM_callocN(
+ sizeof(NodeGeometryMeshToPoints), __func__);
+ data->mode = GEO_NODE_MESH_TO_POINTS_FACES;
+ node->storage = data;
+}
+
+template<typename T>
+static void copy_attribute_to_points(const VArray<T> &src,
+ const IndexMask mask,
+ MutableSpan<T> dst)
+{
+ for (const int i : mask.index_range()) {
+ dst[i] = src[mask[i]];
+ }
+}
+
+static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
+ Field<float3> &position_field,
+ Field<float> &radius_field,
+ Field<bool> &selection_field,
+ const AttributeDomain domain)
+{
+ const MeshComponent *mesh_component = geometry_set.get_component_for_read<MeshComponent>();
+ if (mesh_component == nullptr) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+ GeometryComponentFieldContext field_context{*mesh_component, domain};
+ const int domain_size = mesh_component->attribute_domain_size(domain);
+ if (domain_size == 0) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
+
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
+ uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f);
+ geometry_set.replace_pointcloud(pointcloud);
+ PointCloudComponent &point_component =
+ geometry_set.get_component_for_write<PointCloudComponent>();
+
+ /* Evaluating directly into the point cloud doesn't work because we are not using the full
+ * "min_array_size" array but compressing the selected elements into the final array with no
+ * gaps. */
+ fn::FieldEvaluator evaluator{field_context, &selection};
+ evaluator.add(position_field);
+ evaluator.add(radius_field);
+ evaluator.evaluate();
+ copy_attribute_to_points(evaluator.get_evaluated<float3>(0),
+ selection,
+ {(float3 *)pointcloud->co, pointcloud->totpoint});
+ copy_attribute_to_points(
+ evaluator.get_evaluated<float>(1), selection, {pointcloud->radius, pointcloud->totpoint});
+
+ Map<AttributeIDRef, AttributeKind> attributes;
+ geometry_set.gather_attributes_for_propagation(
+ {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes);
+ attributes.remove("position");
+
+ for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
+ const AttributeIDRef attribute_id = entry.key;
+ const CustomDataType data_type = entry.value.data_type;
+ GVArrayPtr src = mesh_component->attribute_get_for_read(attribute_id, domain, data_type);
+ OutputAttribute dst = point_component.attribute_try_get_for_output_only(
+ attribute_id, ATTR_DOMAIN_POINT, data_type);
+ if (dst && src) {
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ GVArray_Typed<T> src_typed{*src};
+ copy_attribute_to_points(*src_typed, selection, dst.as_span().typed<T>());
+ });
+ dst.save();
+ }
+ }
+
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES});
+}
+
+static void geo_node_mesh_to_points_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+ Field<float3> position = params.extract_input<Field<float3>>("Position");
+ Field<float> radius = params.extract_input<Field<float>>("Radius");
+ Field<bool> selection = params.extract_input<Field<bool>>("Selection");
+
+ /* Use another multi-function operation to make sure the input radius is greater than zero.
+ * TODO: Use mutable multi-function once that is supported. */
+ static fn::CustomMF_SI_SO<float, float> max_zero_fn(
+ __func__, [](float value) { return std::max(0.0f, value); });
+ auto max_zero_op = std::make_shared<FieldOperation>(
+ FieldOperation(max_zero_fn, {std::move(radius)}));
+ Field<float> positive_radius(std::move(max_zero_op), 0);
+
+ const NodeGeometryMeshToPoints &storage =
+ *(const NodeGeometryMeshToPoints *)params.node().storage;
+ const GeometryNodeMeshToPointsMode mode = (GeometryNodeMeshToPointsMode)storage.mode;
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ switch (mode) {
+ case GEO_NODE_MESH_TO_POINTS_VERTICES:
+ geometry_set_mesh_to_points(
+ geometry_set, position, positive_radius, selection, ATTR_DOMAIN_POINT);
+ break;
+ case GEO_NODE_MESH_TO_POINTS_EDGES:
+ geometry_set_mesh_to_points(
+ geometry_set, position, positive_radius, selection, ATTR_DOMAIN_EDGE);
+ break;
+ case GEO_NODE_MESH_TO_POINTS_FACES:
+ geometry_set_mesh_to_points(
+ geometry_set, position, positive_radius, selection, ATTR_DOMAIN_FACE);
+ break;
+ case GEO_NODE_MESH_TO_POINTS_CORNERS:
+ geometry_set_mesh_to_points(
+ geometry_set, position, positive_radius, selection, ATTR_DOMAIN_CORNER);
+ break;
+ }
+ });
+
+ params.set_output("Points", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_mesh_to_points()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype, GEO_NODE_MESH_TO_POINTS, "Mesh to Points", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_mesh_to_points_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_points_exec;
+ node_type_init(&ntype, blender::nodes::geo_node_mesh_to_points_init);
+ ntype.draw_buttons = blender::nodes::geo_node_mesh_to_points_layout;
+ node_type_storage(
+ &ntype, "NodeGeometryMeshToPoints", node_free_standard_storage, node_copy_standard_storage);
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
new file mode 100644
index 00000000000..afd0ced6360
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
@@ -0,0 +1,118 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "BLI_task.hh"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+using blender::Array;
+
+namespace blender::nodes {
+
+static void geo_node_points_to_vertices_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Points");
+ b.add_input<decl::Bool>("Selection").default_value(true).supports_field().hide_value();
+ b.add_output<decl::Geometry>("Mesh");
+}
+
+template<typename T>
+static void copy_attribute_to_vertices(const Span<T> src, const IndexMask mask, MutableSpan<T> dst)
+{
+ for (const int i : mask.index_range()) {
+ dst[i] = src[mask[i]];
+ }
+}
+
+/* One improvement would be to move the attribute arrays directly to the mesh when possible. */
+static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
+ Field<bool> &selection_field)
+{
+ const PointCloudComponent *point_component =
+ geometry_set.get_component_for_read<PointCloudComponent>();
+ if (point_component == nullptr) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+
+ GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT};
+ const int domain_size = point_component->attribute_domain_size(ATTR_DOMAIN_POINT);
+ if (domain_size == 0) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
+
+ Map<AttributeIDRef, AttributeKind> attributes;
+ geometry_set.gather_attributes_for_propagation(
+ {GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_MESH, false, attributes);
+
+ Mesh *mesh = BKE_mesh_new_nomain(selection.size(), 0, 0, 0, 0);
+ geometry_set.replace_mesh(mesh);
+ MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+
+ for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
+ const AttributeIDRef attribute_id = entry.key;
+ const CustomDataType data_type = entry.value.data_type;
+ GVArrayPtr src = point_component->attribute_get_for_read(
+ attribute_id, ATTR_DOMAIN_POINT, data_type);
+ OutputAttribute dst = mesh_component.attribute_try_get_for_output_only(
+ attribute_id, ATTR_DOMAIN_POINT, data_type);
+ if (dst && src) {
+ attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ GVArray_Typed<T> src_typed{*src};
+ VArray_Span<T> src_typed_span{*src_typed};
+ copy_attribute_to_vertices(src_typed_span, selection, dst.as_span().typed<T>());
+ });
+ dst.save();
+ }
+ }
+
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+}
+
+static void geo_node_points_to_vertices_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
+ Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ geometry_set_points_to_vertices(geometry_set, selection_field);
+ });
+
+ params.set_output("Mesh", std::move(geometry_set));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_points_to_vertices()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_POINTS_TO_VERTICES, "Points to Vertices", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_points_to_vertices_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_points_to_vertices_exec;
+ nodeRegisterType(&ntype);
+}