Blender V4.3
node_geo_mesh_to_points.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_array_utils.hh"
6#include "BLI_task.hh"
7
8#include "DNA_mesh_types.h"
10
11#include "BKE_attribute_math.hh"
12#include "BKE_customdata.hh"
13#include "BKE_pointcloud.hh"
14
15#include "NOD_rna_define.hh"
16
17#include "UI_interface.hh"
18#include "UI_resources.hh"
19
20#include "node_geometry_util.hh"
21
23
25
27{
28 b.add_input<decl::Geometry>("Mesh").supported_type(GeometryComponent::Type::Mesh);
29 b.add_input<decl::Bool>("Selection").default_value(true).field_on_all().hide_value();
30 b.add_input<decl::Vector>("Position").implicit_field_on_all(implicit_field_inputs::position);
31 b.add_input<decl::Float>("Radius")
32 .default_value(0.05f)
33 .min(0.0f)
35 .field_on_all();
36 b.add_output<decl::Geometry>("Points").propagate_all();
37}
38
39static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
40{
41 uiItemR(layout, ptr, "mode", UI_ITEM_NONE, "", ICON_NONE);
42}
43
44static void node_init(bNodeTree * /*tree*/, bNode *node)
45{
46 NodeGeometryMeshToPoints *data = MEM_cnew<NodeGeometryMeshToPoints>(__func__);
48 node->storage = data;
49}
50
51static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
52 const Field<float3> &position_field,
53 const Field<float> &radius_field,
54 const Field<bool> &selection_field,
55 const AttrDomain domain,
56 const AttributeFilter &attribute_filter)
57{
58 const Mesh *mesh = geometry_set.get_mesh();
59 if (mesh == nullptr) {
60 geometry_set.remove_geometry_during_modify();
61 return;
62 }
63 const int domain_size = mesh->attributes().domain_size(domain);
64 if (domain_size == 0) {
65 geometry_set.remove_geometry_during_modify();
66 return;
67 }
68 const AttributeAccessor src_attributes = mesh->attributes();
69 const bke::MeshFieldContext field_context{*mesh, domain};
70 fn::FieldEvaluator evaluator{field_context, domain_size};
71 evaluator.set_selection(selection_field);
72 /* Evaluating directly into the point cloud doesn't work because we are not using the full
73 * "min_array_size" array but compressing the selected elements into the final array with no
74 * gaps. */
75 evaluator.add(position_field);
76 evaluator.add(radius_field);
77 evaluator.evaluate();
78 const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
79 const VArray<float3> positions_eval = evaluator.get_evaluated<float3>(0);
80 const VArray<float> radii_eval = evaluator.get_evaluated<float>(1);
81
82 const bool share_arrays = selection.size() == domain_size;
83 const bool share_position = share_arrays && positions_eval.is_span() &&
84 positions_eval.get_internal_span().data() ==
85 mesh->vert_positions().data();
86
87 PointCloud *pointcloud;
88 if (share_position) {
89 /* Create an empty point cloud so the positions can be shared. */
90 pointcloud = BKE_pointcloud_new_nomain(0);
91 CustomData_free_layer_named(&pointcloud->pdata, "position", pointcloud->totpoint);
92 pointcloud->totpoint = mesh->verts_num;
93 const bke::AttributeReader src = src_attributes.lookup<float3>("position");
94 const bke::AttributeInitShared init(src.varray.get_internal_span().data(), *src.sharing_info);
95 pointcloud->attributes_for_write().add<float3>("position", AttrDomain::Point, init);
96 }
97 else {
98 pointcloud = BKE_pointcloud_new_nomain(selection.size());
99 array_utils::gather(positions_eval, selection, pointcloud->positions_for_write());
100 }
101
102 MutableAttributeAccessor dst_attributes = pointcloud->attributes_for_write();
104 "radius", AttrDomain::Point, CD_PROP_FLOAT);
105 array_utils::gather(evaluator.get_evaluated(1), selection, radius.span);
106 radius.finish();
107
111 false,
112 attribute_filter,
113 attributes);
114 attributes.remove("radius");
115 attributes.remove("position");
116
117 for (MapItem<StringRef, AttributeKind> entry : attributes.items()) {
118 const StringRef attribute_id = entry.key;
119 const eCustomDataType data_type = entry.value.data_type;
120 const bke::GAttributeReader src = src_attributes.lookup(attribute_id, domain, data_type);
121 if (!src) {
122 /* Domain interpolation can fail if the source domain is empty. */
123 continue;
124 }
125
126 if (share_arrays && src.domain == domain && src.sharing_info && src.varray.is_span()) {
128 *src.sharing_info);
129 dst_attributes.add(attribute_id, AttrDomain::Point, data_type, init);
130 }
131 else {
133 attribute_id, AttrDomain::Point, data_type);
134 array_utils::gather(src.varray, selection, dst.span);
135 dst.finish();
136 }
137 }
138
139 geometry_set.replace_pointcloud(pointcloud);
141}
142
144{
145 GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
146 Field<float3> position = params.extract_input<Field<float3>>("Position");
147 Field<float> radius = params.extract_input<Field<float>>("Radius");
148 Field<bool> selection = params.extract_input<Field<bool>>("Selection");
149
150 /* Use another multi-function operation to make sure the input radius is greater than zero.
151 * TODO: Use mutable multi-function once that is supported. */
152 static auto max_zero_fn = mf::build::SI1_SO<float, float>(
153 __func__,
154 [](float value) { return std::max(0.0f, value); },
155 mf::build::exec_presets::AllSpanOrSingle());
156 const Field<float> positive_radius(FieldOperation::Create(max_zero_fn, {std::move(radius)}), 0);
157
158 const NodeGeometryMeshToPoints &storage = node_storage(params.node());
160
161 const NodeAttributeFilter &attribute_filter = params.get_attribute_filter("Points");
162
163 geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
164 switch (mode) {
166 geometry_set_mesh_to_points(geometry_set,
167 position,
168 positive_radius,
169 selection,
170 AttrDomain::Point,
171 attribute_filter);
172 break;
174 geometry_set_mesh_to_points(geometry_set,
175 position,
176 positive_radius,
177 selection,
178 AttrDomain::Edge,
179 attribute_filter);
180 break;
182 geometry_set_mesh_to_points(geometry_set,
183 position,
184 positive_radius,
185 selection,
186 AttrDomain::Face,
187 attribute_filter);
188 break;
190 geometry_set_mesh_to_points(geometry_set,
191 position,
192 positive_radius,
193 selection,
194 AttrDomain::Corner,
195 attribute_filter);
196 break;
197 }
198 });
199
200 params.set_output("Points", std::move(geometry_set));
201}
202
203static void node_rna(StructRNA *srna)
204{
205 static EnumPropertyItem mode_items[] = {
207 "VERTICES",
208 0,
209 "Vertices",
210 "Create a point in the point cloud for each selected vertex"},
212 "EDGES",
213 0,
214 "Edges",
215 "Create a point in the point cloud for each selected edge"},
217 "FACES",
218 0,
219 "Faces",
220 "Create a point in the point cloud for each selected face"},
222 "CORNERS",
223 0,
224 "Corners",
225 "Create a point in the point cloud for each selected face corner"},
226 {0, nullptr, 0, nullptr, nullptr},
227 };
228
230 "mode",
231 "Mode",
232 "",
233 mode_items,
236 nullptr,
237 true);
238}
239
240static void node_register()
241{
242 static blender::bke::bNodeType ntype;
243
244 geo_node_type_base(&ntype, GEO_NODE_MESH_TO_POINTS, "Mesh to Points", NODE_CLASS_GEOMETRY);
245 ntype.declare = node_declare;
247 ntype.initfunc = node_init;
250 &ntype, "NodeGeometryMeshToPoints", node_free_standard_storage, node_copy_standard_storage);
252
253 node_rna(ntype.rna_ext.srna);
254}
256
257} // namespace blender::nodes::node_geo_mesh_to_points_cc
CustomData interface, see also DNA_customdata_types.h.
bool CustomData_free_layer_named(CustomData *data, blender::StringRef name, const int totelem)
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1799
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
General operations for point clouds.
PointCloud * BKE_pointcloud_new_nomain(int totpoint)
@ CD_PROP_FLOAT
GeometryNodeMeshToPointsMode
@ GEO_NODE_MESH_TO_POINTS_FACES
@ GEO_NODE_MESH_TO_POINTS_VERTICES
@ GEO_NODE_MESH_TO_POINTS_CORNERS
@ GEO_NODE_MESH_TO_POINTS_EDGES
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define NOD_storage_enum_accessors(member)
@ PROP_DISTANCE
Definition RNA_types.hh:159
#define UI_ITEM_NONE
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
void init()
const void * data() const
Span< T > get_internal_span() const
GAttributeReader lookup(const StringRef attribute_id) const
bool add(const StringRef attribute_id, const AttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer)
GSpanAttributeWriter lookup_or_add_for_write_only_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type)
void set_selection(Field< bool > selection)
Definition FN_field.hh:385
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:244
local_group_size(16, 16) .push_constant(Type b
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void gather(const GVArray &src, const IndexMask &indices, GMutableSpan dst, int64_t grain_size=4096)
void node_type_storage(bNodeType *ntype, const char *storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:4632
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
void position(const bNode &, void *r_value)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void node_init(bNodeTree *, bNode *node)
static void node_declare(NodeDeclarationBuilder &b)
static void geometry_set_mesh_to_points(GeometrySet &geometry_set, const Field< float3 > &position_field, const Field< float > &radius_field, const Field< bool > &selection_field, const AttrDomain domain, const AttributeFilter &attribute_filter)
static void node_geo_exec(GeoNodeExecParams params)
PropertyRNA * RNA_def_node_enum(StructRNA *srna, const char *identifier, const char *ui_name, const char *ui_description, const EnumPropertyItem *static_items, const EnumRNAAccessors accessors, std::optional< int > default_value, const EnumPropertyItemFunc item_func, const bool allow_animation)
void geo_node_type_base(blender::bke::bNodeType *ntype, int type, const char *name, short nclass)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:46
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:58
StructRNA * srna
Definition RNA_types.hh:780
struct CustomData pdata
const ImplicitSharingInfo * sharing_info
const ImplicitSharingInfo * sharing_info
void gather_attributes_for_propagation(Span< GeometryComponent::Type > component_types, GeometryComponent::Type dst_component_type, bool include_instances, const AttributeFilter &attribute_filter, Map< StringRef, AttributeKind > &r_attributes) const
void replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
void keep_only_during_modify(Span< GeometryComponent::Type > component_types)
const Mesh * get_mesh() const
void modify_geometry_sets(ForeachSubGeometryCallback callback)
Defines a node type.
Definition BKE_node.hh:218
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:267
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:238
NodeDeclareFunction declare
Definition BKE_node.hh:347
PointerRNA * ptr
Definition wm_files.cc:4126