Blender V4.3
node_geo_curve_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.hh"
6#include "BLI_math_matrix.hh"
7#include "BLI_task.hh"
8
10
11#include "BKE_customdata.hh"
12#include "BKE_grease_pencil.hh"
13#include "BKE_instances.hh"
14#include "BKE_pointcloud.hh"
15
18
19#include "NOD_rna_define.hh"
20
21#include "UI_interface.hh"
22#include "UI_resources.hh"
23
24#include "node_geometry_util.hh"
25
27
29
31{
32 b.add_input<decl::Geometry>("Curve").supported_type(
34 auto &count = b.add_input<decl::Int>("Count")
35 .default_value(10)
36 .min(2)
37 .max(100000)
38 .field_on_all()
39 .make_available([](bNode &node) {
40 node_storage(node).mode = GEO_NODE_CURVE_RESAMPLE_COUNT;
41 });
42 auto &length = b.add_input<decl::Float>("Length")
43 .default_value(0.1f)
44 .min(0.001f)
46 .make_available([](bNode &node) {
47 node_storage(node).mode = GEO_NODE_CURVE_RESAMPLE_LENGTH;
48 });
49 b.add_output<decl::Geometry>("Points").propagate_all();
50 b.add_output<decl::Vector>("Tangent").field_on_all();
51 b.add_output<decl::Vector>("Normal").field_on_all();
52 b.add_output<decl::Rotation>("Rotation").field_on_all();
53
54 const bNode *node = b.node_or_null();
55 if (node != nullptr) {
56 const NodeGeometryCurveToPoints &storage = node_storage(*node);
58
59 count.available(mode == GEO_NODE_CURVE_RESAMPLE_COUNT);
60 length.available(mode == GEO_NODE_CURVE_RESAMPLE_LENGTH);
61 }
62}
63
64static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
65{
66 uiItemR(layout, ptr, "mode", UI_ITEM_NONE, "", ICON_NONE);
67}
68
69static void node_init(bNodeTree * /*tree*/, bNode *node)
70{
71 NodeGeometryCurveToPoints *data = MEM_cnew<NodeGeometryCurveToPoints>(__func__);
72
74 node->storage = data;
75}
76
77static void fill_rotation_attribute(const Span<float3> tangents,
78 const Span<float3> normals,
80{
81 threading::parallel_for(IndexRange(rotations.size()), 512, [&](IndexRange range) {
82 for (const int i : range) {
83 rotations[i] = math::to_quaternion(
84 math::from_orthonormal_axes<float4x4>(normals[i], tangents[i]));
85 }
86 });
87}
88
89static void copy_curve_domain_attributes(const AttributeAccessor curve_attributes,
90 MutableAttributeAccessor point_attributes)
91{
92 curve_attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
93 if (iter.is_builtin) {
94 return;
95 }
96 if (iter.domain != AttrDomain::Curve) {
97 return;
98 }
99 if (iter.data_type == CD_PROP_STRING) {
100 return;
101 }
102 point_attributes.add(iter.name,
103 AttrDomain::Point,
104 iter.data_type,
105 bke::AttributeInitVArray(*iter.get(AttrDomain::Point)));
106 });
107}
108
110 const std::optional<StringRef> &tangent_id,
111 const std::optional<StringRef> &normal_id,
112 const std::optional<StringRef> &rotation_id)
113{
114 PointCloud *pointcloud = BKE_pointcloud_new_nomain(0);
115 pointcloud->totpoint = curves.points_num();
116
117 if (rotation_id) {
118 MutableAttributeAccessor attributes = curves.attributes_for_write();
119 const VArraySpan tangents = *attributes.lookup<float3>(*tangent_id, AttrDomain::Point);
120 const VArraySpan normals = *attributes.lookup<float3>(*normal_id, AttrDomain::Point);
122 attributes.lookup_or_add_for_write_only_span<math::Quaternion>(*rotation_id,
123 AttrDomain::Point);
124 fill_rotation_attribute(tangents, normals, rotations.span);
125 rotations.finish();
126 }
127
128 /* Move the curve point custom data to the pointcloud, to avoid any copying. */
129 CustomData_free(&pointcloud->pdata, pointcloud->totpoint);
130 pointcloud->pdata = curves.point_data;
131 CustomData_reset(&curves.point_data);
132
133 copy_curve_domain_attributes(curves.attributes(), pointcloud->attributes_for_write());
134
135 return pointcloud;
136}
137
138static void curve_to_points(GeometrySet &geometry_set,
142 const std::optional<StringRef> &rotation_anonymous_id)
143{
144 switch (mode) {
146 Field<int> count = params.extract_input<Field<int>>("Count");
147 geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
148 if (const Curves *src_curves_id = geometry.get_curves()) {
149 const bke::CurvesGeometry &src_curves = src_curves_id->geometry.wrap();
150 const bke::CurvesFieldContext field_context{*src_curves_id, AttrDomain::Curve};
151 bke::CurvesGeometry dst_curves = geometry::resample_to_count(
152 src_curves,
153 field_context,
154 fn::make_constant_field<bool>(true),
155 count,
156 resample_attributes);
157 PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves),
158 resample_attributes.tangent_id,
159 resample_attributes.normal_id,
160 rotation_anonymous_id);
161 geometry.remove_geometry_during_modify();
162 geometry.replace_pointcloud(pointcloud);
163 }
164 });
165 break;
166 }
168 Field<float> length = params.extract_input<Field<float>>("Length");
169 geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
170 if (const Curves *src_curves_id = geometry.get_curves()) {
171 const bke::CurvesGeometry &src_curves = src_curves_id->geometry.wrap();
172 const bke::CurvesFieldContext field_context{*src_curves_id, AttrDomain::Curve};
173 bke::CurvesGeometry dst_curves = geometry::resample_to_length(
174 src_curves,
175 field_context,
176 fn::make_constant_field<bool>(true),
177 length,
178 resample_attributes);
179 PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves),
180 resample_attributes.tangent_id,
181 resample_attributes.normal_id,
182 rotation_anonymous_id);
183 geometry.remove_geometry_during_modify();
184 geometry.replace_pointcloud(pointcloud);
185 }
186 });
187 break;
188 }
190 geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
191 if (const Curves *src_curves_id = geometry.get_curves()) {
192 const bke::CurvesGeometry &src_curves = src_curves_id->geometry.wrap();
193 const bke::CurvesFieldContext field_context{*src_curves_id, AttrDomain::Curve};
194 bke::CurvesGeometry dst_curves = geometry::resample_to_evaluated(
195 src_curves, field_context, fn::make_constant_field<bool>(true), resample_attributes);
196 PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves),
197 resample_attributes.tangent_id,
198 resample_attributes.normal_id,
199 rotation_anonymous_id);
200 geometry.remove_geometry_during_modify();
201 geometry.replace_pointcloud(pointcloud);
202 }
203 });
204 break;
205 }
206 }
207}
208
209static void grease_pencil_to_points(GeometrySet &geometry_set,
213 const std::optional<StringRef> &rotation_anonymous_id,
214 const AttributeFilter &attribute_filter)
215{
218
219 switch (mode) {
221 count = params.extract_input<Field<int>>("Count");
222 break;
224 length = params.extract_input<Field<float>>("Length");
225 break;
227 break;
228 }
229
230 geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
231 using namespace blender::bke::greasepencil;
232 if (geometry.has_grease_pencil()) {
233 const GreasePencil &grease_pencil = *geometry.get_grease_pencil();
234 Vector<PointCloud *> pointcloud_by_layer(grease_pencil.layers().size(), nullptr);
235 for (const int layer_index : grease_pencil.layers().index_range()) {
236 const Drawing *drawing = grease_pencil.get_eval_drawing(grease_pencil.layer(layer_index));
237 if (drawing == nullptr) {
238 continue;
239 }
240 const bke::CurvesGeometry &src_curves = drawing->strokes();
242 grease_pencil, AttrDomain::Curve, layer_index);
243
244 bke::CurvesGeometry dst_curves;
245 switch (mode) {
247 dst_curves = geometry::resample_to_count(src_curves,
248 field_context,
249 fn::make_constant_field<bool>(true),
250 count,
251 resample_attributes);
252 break;
253 }
255 dst_curves = geometry::resample_to_length(src_curves,
256 field_context,
257 fn::make_constant_field<bool>(true),
258 length,
259 resample_attributes);
260 break;
261 }
263 dst_curves = geometry::resample_to_evaluated(src_curves,
264 field_context,
265 fn::make_constant_field<bool>(true),
266 resample_attributes);
267 break;
268 }
269 }
270 pointcloud_by_layer[layer_index] = pointcloud_from_curves(std::move(dst_curves),
271 resample_attributes.tangent_id,
272 resample_attributes.normal_id,
273 rotation_anonymous_id);
274 }
275 if (!pointcloud_by_layer.is_empty()) {
276 bke::Instances *instances = new bke::Instances();
277 for (PointCloud *pointcloud : pointcloud_by_layer) {
278 if (!pointcloud) {
279 /* Add an empty reference so the number of layers and instances match.
280 * This makes it easy to reconstruct the layers afterwards and keep their
281 * attributes. */
282 const int handle = instances->add_reference(bke::InstanceReference());
283 instances->add_instance(handle, float4x4::identity());
284 continue;
285 }
286 GeometrySet temp_set = GeometrySet::from_pointcloud(pointcloud);
287 const int handle = instances->add_reference(bke::InstanceReference{temp_set});
288 instances->add_instance(handle, float4x4::identity());
289 }
290 GeometrySet::propagate_attributes_from_layer_to_instances(
291 geometry.get_grease_pencil()->attributes(),
292 instances->attributes_for_write(),
293 attribute_filter);
294 InstancesComponent &dst_component = geometry.get_component_for_write<InstancesComponent>();
295 GeometrySet new_instances = geometry::join_geometries(
296 {GeometrySet::from_instances(dst_component.release()),
297 GeometrySet::from_instances(instances)},
298 attribute_filter);
299 dst_component.replace(
300 new_instances.get_component_for_write<InstancesComponent>().release());
301 }
302 }
303 });
304 geometry_set.replace_grease_pencil(nullptr);
305}
306
308{
309 const NodeGeometryCurveToPoints &storage = node_storage(params.node());
311 GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
312
313 GeometryComponentEditData::remember_deformed_positions_if_necessary(geometry_set);
314
315 std::optional<std::string> rotation_anonymous_id =
316 params.get_output_anonymous_attribute_id_if_needed("Rotation");
317 const bool need_tangent_and_normal = bool(rotation_anonymous_id);
318 std::optional<std::string> tangent_anonymous_id =
319 params.get_output_anonymous_attribute_id_if_needed("Tangent", need_tangent_and_normal);
320 std::optional<std::string> normal_anonymous_id =
321 params.get_output_anonymous_attribute_id_if_needed("Normal", need_tangent_and_normal);
322
324 resample_attributes.tangent_id = tangent_anonymous_id;
325 resample_attributes.normal_id = normal_anonymous_id;
326 const NodeAttributeFilter &attribute_filter = params.get_attribute_filter("Points");
327
328 if (geometry_set.has_curves()) {
329 curve_to_points(geometry_set, params, mode, resample_attributes, rotation_anonymous_id);
330 }
331 if (geometry_set.has_grease_pencil()) {
333 geometry_set, params, mode, resample_attributes, rotation_anonymous_id, attribute_filter);
334 }
335
336 params.set_output("Points", std::move(geometry_set));
337}
338
339static void node_rna(StructRNA *srna)
340{
341 static EnumPropertyItem mode_items[] = {
343 "EVALUATED",
344 0,
345 "Evaluated",
346 "Create points from the curve's evaluated points, based on the resolution attribute for "
347 "NURBS and Bézier splines"},
349 "COUNT",
350 0,
351 "Count",
352 "Sample each spline by evenly distributing the specified number of points"},
354 "LENGTH",
355 0,
356 "Length",
357 "Sample each spline by splitting it into segments with the specified length"},
358 {0, nullptr, 0, nullptr, nullptr},
359 };
360
362 "mode",
363 "Mode",
364 "How to generate points from the input curve",
365 mode_items,
368}
369
370static void node_register()
371{
372 static blender::bke::bNodeType ntype;
373
374 geo_node_type_base(&ntype, GEO_NODE_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY);
375 ntype.declare = node_declare;
379 &ntype, "NodeGeometryCurveToPoints", node_free_standard_storage, node_copy_standard_storage);
380 ntype.initfunc = node_init;
382
383 node_rna(ntype.rna_ext.srna);
384}
385NOD_REGISTER_NODE(node_register)
386
387} // namespace blender::nodes::node_geo_curve_to_points_cc
CustomData interface, see also DNA_customdata_types.h.
void CustomData_reset(CustomData *data)
void CustomData_free(CustomData *data, int totelem)
Low-level operations for grease pencil.
#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_STRING
GeometryNodeCurveResampleMode
@ GEO_NODE_CURVE_RESAMPLE_LENGTH
@ GEO_NODE_CURVE_RESAMPLE_EVALUATED
@ GEO_NODE_CURVE_RESAMPLE_COUNT
#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)
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
constexpr int64_t size() const
Definition BLI_span.hh:494
bool is_empty() const
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
GAttributeReader get() const
void replace(Instances *instances, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
bool add(const StringRef attribute_id, const AttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer)
const bke::CurvesGeometry & strokes() const
void make_available(bNode &node) const
local_group_size(16, 16) .push_constant(Type b
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int count
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
static void node_geo_exec(GeoNodeExecParams params)
static void node_declare(NodeDeclarationBuilder &b)
static void copy_curve_domain_attributes(const AttributeAccessor curve_attributes, MutableAttributeAccessor point_attributes)
static void node_init(bNodeTree *, bNode *node)
static void grease_pencil_to_points(GeometrySet &geometry_set, GeoNodeExecParams params, const GeometryNodeCurveResampleMode mode, geometry::ResampleCurvesOutputAttributeIDs resample_attributes, const std::optional< StringRef > &rotation_anonymous_id, const AttributeFilter &attribute_filter)
static void fill_rotation_attribute(const Span< float3 > tangents, const Span< float3 > normals, MutableSpan< math::Quaternion > rotations)
static void curve_to_points(GeometrySet &geometry_set, GeoNodeExecParams params, const GeometryNodeCurveResampleMode mode, geometry::ResampleCurvesOutputAttributeIDs resample_attributes, const std::optional< StringRef > &rotation_anonymous_id)
static PointCloud * pointcloud_from_curves(bke::CurvesGeometry curves, const std::optional< StringRef > &tangent_id, const std::optional< StringRef > &normal_id, const std::optional< StringRef > &rotation_id)
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
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 parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
std::optional< std::string > rotation_id
std::optional< std::string > normal_id
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
void modify_geometry_sets(ForeachSubGeometryCallback callback)
void replace_grease_pencil(GreasePencil *grease_pencil, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
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