Blender  V3.3
node_geo_curve_to_points.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include "BLI_array.hh"
4 #include "BLI_task.hh"
5 #include "BLI_timeit.hh"
6 
7 #include "BKE_pointcloud.h"
8 #include "BKE_spline.hh"
9 
10 #include "UI_interface.h"
11 #include "UI_resources.h"
12 
13 #include "node_geometry_util.hh"
14 
16 
18 
20 {
21  b.add_input<decl::Geometry>(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE);
22  b.add_input<decl::Int>(N_("Count"))
23  .default_value(10)
24  .min(2)
25  .max(100000)
27  [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_RESAMPLE_COUNT; });
28  b.add_input<decl::Float>(N_("Length"))
29  .default_value(0.1f)
30  .min(0.001f)
31  .subtype(PROP_DISTANCE)
33  [](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_RESAMPLE_LENGTH; });
34  b.add_output<decl::Geometry>(N_("Points"));
35  b.add_output<decl::Vector>(N_("Tangent")).field_source();
36  b.add_output<decl::Vector>(N_("Normal")).field_source();
37  b.add_output<decl::Vector>(N_("Rotation")).field_source();
38 }
39 
40 static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
41 {
42  uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
43 }
44 
46 {
47  NodeGeometryCurveToPoints *data = MEM_cnew<NodeGeometryCurveToPoints>(__func__);
48 
50  node->storage = data;
51 }
52 
54 {
55  const NodeGeometryCurveToPoints &storage = node_storage(*node);
57 
58  bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next;
59  bNodeSocket *length_socket = count_socket->next;
60 
63 }
64 
68 {
69  threading::parallel_for(IndexRange(rotations.size()), 512, [&](IndexRange range) {
70  for (const int i : range) {
71  rotations[i] =
72  float4x4::from_normalized_axis_data({0, 0, 0}, normals[i], tangents[i]).to_euler();
73  }
74  });
75 }
76 
79  const CurveEval &curve,
81 {
82  const int size = curve.splines().size();
83  switch (mode) {
85  const int count = params.get_input<int>("Count");
86  if (count < 1) {
87  return {0};
88  }
89  Array<int> offsets(size + 1);
90  int offset = 0;
91  for (const int i : IndexRange(size)) {
92  offsets[i] = offset;
93  if (splines[i]->evaluated_points_num() > 0) {
94  offset += count;
95  }
96  }
97  offsets.last() = offset;
98  return offsets;
99  }
101  /* Don't allow asymptotic count increase for low resolution values. */
102  const float resolution = std::max(params.get_input<float>("Length"), 0.0001f);
103  Array<int> offsets(size + 1);
104  int offset = 0;
105  for (const int i : IndexRange(size)) {
106  offsets[i] = offset;
107  if (splines[i]->evaluated_points_num() > 0) {
108  offset += splines[i]->length() / resolution + 1;
109  }
110  }
111  offsets.last() = offset;
112  return offsets;
113  }
115  return curve.evaluated_point_offsets();
116  }
117  }
119  return {0};
120 }
121 
126  const AttributeIDRef &attribute_id,
127  const eCustomDataType data_type)
128 {
129  GAttributeWriter attribute = points.attributes_for_write()->lookup_or_add_for_write(
130  attribute_id, ATTR_DOMAIN_POINT, data_type);
131  GMutableSpan span = attribute.varray.get_internal_span();
132  attribute.finish();
133  return span;
134 }
135 
136 template<typename T>
138  const AttributeIDRef &attribute_id)
139 {
140  AttributeWriter<T> attribute = points.attributes_for_write()->lookup_or_add_for_write<T>(
141  attribute_id, ATTR_DOMAIN_POINT);
142  MutableSpan<T> span = attribute.varray.get_internal_span();
143  attribute.finish();
144  return span;
145 }
146 
147 namespace {
148 struct AnonymousAttributeIDs {
152 };
153 
154 struct ResultAttributes {
155  MutableSpan<float3> positions;
156  MutableSpan<float> radii;
157 
158  Map<AttributeIDRef, GMutableSpan> point_attributes;
159 
160  MutableSpan<float3> tangents;
161  MutableSpan<float3> normals;
162  MutableSpan<float3> rotations;
163 };
164 } // namespace
165 
166 static ResultAttributes create_attributes_for_transfer(PointCloudComponent &points,
167  const CurveEval &curve,
168  const AnonymousAttributeIDs &attributes)
169 {
170  ResultAttributes outputs;
171 
172  outputs.positions = ensure_point_attribute<float3>(points, "position");
173  outputs.radii = ensure_point_attribute<float>(points, "radius");
174 
175  if (attributes.tangent_id) {
176  outputs.tangents = ensure_point_attribute<float3>(points, attributes.tangent_id.get());
177  }
178  if (attributes.normal_id) {
179  outputs.normals = ensure_point_attribute<float3>(points, attributes.normal_id.get());
180  }
181  if (attributes.rotation_id) {
182  outputs.rotations = ensure_point_attribute<float3>(points, attributes.rotation_id.get());
183  }
184 
185  /* Because of the invariants of the curve component, we use the attributes of the first spline
186  * as a representative for the attribute meta data all splines. Attributes from the spline domain
187  * are handled separately. */
188  curve.splines().first()->attributes.foreach_attribute(
189  [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) {
190  if (id.should_be_kept()) {
191  outputs.point_attributes.add_new(
192  id, ensure_point_attribute(points, id, meta_data.data_type));
193  }
194  return true;
195  },
197 
198  return outputs;
199 }
200 
206  const Span<int> offsets,
207  ResultAttributes &data)
208 {
209  threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) {
210  for (const int i : range) {
211  const Spline &spline = *splines[i];
212  const int offset = offsets[i];
213  const int size = offsets[i + 1] - offsets[i];
214 
215  data.positions.slice(offset, size).copy_from(spline.evaluated_positions());
216  spline.interpolate_to_evaluated(spline.radii()).materialize(data.radii.slice(offset, size));
217 
218  for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) {
219  const AttributeIDRef attribute_id = item.key;
220  const GMutableSpan dst = item.value;
221 
222  BLI_assert(spline.attributes.get_for_read(attribute_id));
223  GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
224 
225  spline.interpolate_to_evaluated(spline_span).materialize(dst.slice(offset, size).data());
226  }
227 
228  if (!data.tangents.is_empty()) {
229  data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents());
230  }
231  if (!data.normals.is_empty()) {
232  data.normals.slice(offset, size).copy_from(spline.evaluated_normals());
233  }
234  }
235  });
236 }
237 
239  const Span<int> offsets,
240  ResultAttributes &data)
241 {
242  threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) {
243  for (const int i : range) {
244  const Spline &spline = *splines[i];
245  const int offset = offsets[i];
246  const int num = offsets[i + 1] - offsets[i];
247  if (num == 0) {
248  continue;
249  }
250 
251  const Array<float> uniform_samples = spline.sample_uniform_index_factors(num);
252 
253  spline.sample_with_index_factors<float3>(
254  spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, num));
255  spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()),
256  uniform_samples,
257  data.radii.slice(offset, num));
258 
259  for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) {
260  const AttributeIDRef attribute_id = item.key;
261  const GMutableSpan dst = item.value;
262 
263  BLI_assert(spline.attributes.get_for_read(attribute_id));
264  GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
265 
266  spline.sample_with_index_factors(
267  spline.interpolate_to_evaluated(spline_span), uniform_samples, dst.slice(offset, num));
268  }
269 
270  if (!data.tangents.is_empty()) {
271  Span<float3> src_tangents = spline.evaluated_tangents();
272  MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, num);
273  spline.sample_with_index_factors<float3>(src_tangents, uniform_samples, sampled_tangents);
274  for (float3 &vector : sampled_tangents) {
275  vector = math::normalize(vector);
276  }
277  }
278 
279  if (!data.normals.is_empty()) {
280  Span<float3> src_normals = spline.evaluated_normals();
281  MutableSpan<float3> sampled_normals = data.normals.slice(offset, num);
282  spline.sample_with_index_factors<float3>(src_normals, uniform_samples, sampled_normals);
283  for (float3 &vector : sampled_normals) {
284  vector = math::normalize(vector);
285  }
286  }
287  }
288  });
289 }
290 
292  const Span<int> offsets,
293  PointCloudComponent &points)
294 {
295  curve.attributes.foreach_attribute(
296  [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
297  const GSpan curve_attribute = *curve.attributes.get_for_read(attribute_id);
298  const CPPType &type = curve_attribute.type();
299  const GMutableSpan dst = ensure_point_attribute(points, attribute_id, meta_data.data_type);
300 
301  for (const int i : curve.splines().index_range()) {
302  const int offset = offsets[i];
303  const int num = offsets[i + 1] - offsets[i];
304  type.fill_assign_n(curve_attribute[i], dst[offset], num);
305  }
306 
307  return true;
308  },
310 }
311 
313 {
314  const NodeGeometryCurveToPoints &storage = node_storage(params.node());
316  GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
317 
318  AnonymousAttributeIDs attribute_outputs;
319  attribute_outputs.tangent_id = StrongAnonymousAttributeID("Tangent");
320  attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal");
321  attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation");
322 
324 
325  geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
326  if (!geometry_set.has_curves()) {
327  geometry_set.remove_geometry_during_modify();
328  return;
329  }
330  const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
331  *geometry_set.get_curves_for_read());
332  const Span<SplinePtr> splines = curve->splines();
333  curve->assert_valid_point_attributes();
334 
336  const int total_num = offsets.last();
337  if (total_num == 0) {
338  geometry_set.remove_geometry_during_modify();
339  return;
340  }
341 
342  geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_num));
345  points, *curve, attribute_outputs);
346 
347  switch (mode) {
351  break;
354  break;
355  }
356 
357  copy_spline_domain_attributes(*curve, offsets, points);
358 
359  if (!point_attributes.rotations.is_empty()) {
361  point_attributes.tangents, point_attributes.normals, point_attributes.rotations);
362  }
363 
365  });
366 
367  params.set_output("Points", std::move(geometry_set));
368  if (attribute_outputs.tangent_id) {
369  params.set_output(
370  "Tangent",
371  AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.tangent_id),
372  params.attribute_producer_name()));
373  }
374  if (attribute_outputs.normal_id) {
375  params.set_output(
376  "Normal",
377  AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id),
378  params.attribute_producer_name()));
379  }
380  if (attribute_outputs.rotation_id) {
381  params.set_output(
382  "Rotation",
383  AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id),
384  params.attribute_producer_name()));
385  }
386 }
387 
388 } // namespace blender::nodes::node_geo_curve_to_points_cc
389 
391 {
393 
394  static bNodeType ntype;
395 
401  &ntype, "NodeGeometryCurveToPoints", node_free_standard_storage, node_copy_standard_storage);
404  nodeRegisterType(&ntype);
405 }
@ ATTR_DOMAIN_CURVE
Definition: BKE_attribute.h:31
@ ATTR_DOMAIN_POINT
Definition: BKE_attribute.h:27
@ GEO_COMPONENT_TYPE_POINT_CLOUD
@ GEO_COMPONENT_TYPE_CURVE
void node_type_update(struct bNodeType *ntype, void(*updatefunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4443
#define NODE_STORAGE_FUNCS(StorageT)
Definition: BKE_node.h:1563
void nodeSetSocketAvailability(struct bNodeTree *ntree, struct bNodeSocket *sock, bool is_available)
Definition: node.cc:3664
void node_type_init(struct bNodeType *ntype, void(*initfunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4390
#define NODE_CLASS_GEOMETRY
Definition: BKE_node.h:359
void node_type_storage(struct bNodeType *ntype, const char *storagename, void(*freefunc)(struct bNode *node), void(*copyfunc)(struct bNodeTree *dest_ntree, struct bNode *dest_node, const struct bNode *src_node))
Definition: node.cc:4426
#define GEO_NODE_CURVE_TO_POINTS
Definition: BKE_node.h:1471
void nodeRegisterType(struct bNodeType *ntype)
Definition: node.cc:1357
General operations for point clouds.
struct PointCloud * BKE_pointcloud_new_nomain(int totpoint)
Definition: pointcloud.cc:243
std::unique_ptr< CurveEval > curves_to_curve_eval(const Curves &curves)
Definition: curve_eval.cc:373
#define BLI_assert_unreachable()
Definition: BLI_assert.h:93
#define UNUSED(x)
eCustomDataType
GeometryNodeCurveResampleMode
@ GEO_NODE_CURVE_RESAMPLE_LENGTH
@ GEO_NODE_CURVE_RESAMPLE_EVALUATED
@ GEO_NODE_CURVE_RESAMPLE_COUNT
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Sky Generate a procedural sky texture Noise Generate fractal Perlin noise Wave Generate procedural bands or rings with noise Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color attribute
@ PROP_DISTANCE
Definition: RNA_types.h:149
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
static void remember_deformed_curve_positions_if_necessary(GeometrySet &geometry)
std::optional< blender::bke::MutableAttributeAccessor > attributes_for_write() final
const T & last(const int64_t n=0) const
Definition: BLI_array.hh:284
const CPPType & type() const
OperationNode * node
Curve curve
void * tree
bNodeTree * ntree
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int count
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
ListBase splines
Definition: mask.c:265
static ulong * next
#define T
OwnedAnonymousAttributeID< true > StrongAnonymousAttributeID
static void node_geo_exec(GeoNodeExecParams params)
static void node_declare(NodeDeclarationBuilder &b)
static void copy_evaluated_point_attributes(const Span< SplinePtr > splines, const Span< int > offsets, ResultAttributes &data)
static MutableSpan< T > ensure_point_attribute(PointCloudComponent &points, const AttributeIDRef &attribute_id)
static void curve_create_default_rotation_attribute(Span< float3 > tangents, Span< float3 > normals, MutableSpan< float3 > rotations)
static void copy_spline_domain_attributes(const CurveEval &curve, const Span< int > offsets, PointCloudComponent &points)
static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
static void node_init(bNodeTree *UNUSED(tree), bNode *node)
static void copy_uniform_sample_point_attributes(const Span< SplinePtr > splines, const Span< int > offsets, ResultAttributes &data)
static ResultAttributes create_attributes_for_transfer(PointCloudComponent &points, const CurveEval &curve, const AnonymousAttributeIDs &attributes)
static Array< int > calculate_spline_point_offsets(GeoNodeExecParams &params, const GeometryNodeCurveResampleMode mode, const CurveEval &curve, const Span< SplinePtr > splines)
static void node_update(bNodeTree *ntree, bNode *node)
void parallel_for(IndexRange range, int64_t grain_size, const Function &function)
Definition: BLI_task.hh:51
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
static void node_init(const struct bContext *C, bNodeTree *ntree, bNode *node)
Definition: node.cc:1082
MutableSpan< float3 > positions
void register_node_type_geo_curve_to_points()
Map< AttributeIDRef, GMutableSpan > point_attributes
StrongAnonymousAttributeID normal_id
StrongAnonymousAttributeID tangent_id
MutableSpan< float > radii
MutableSpan< float3 > tangents
StrongAnonymousAttributeID rotation_id
MutableSpan< float3 > rotations
MutableSpan< float3 > normals
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass)
static bNodeSocketTemplate outputs[]
void node_copy_standard_storage(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, const bNode *src_node)
Definition: node_util.c:55
void node_free_standard_storage(bNode *node)
Definition: node_util.c:43
float size[3]
GeometryComponent & get_component_for_write(GeometryComponentType component_type)
void keep_only_during_modify(const blender::Span< GeometryComponentType > component_types)
const Curves * get_curves_for_read() const
bool has_curves() const
void remove_geometry_during_modify()
void replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
struct bNodeSocket * next
Defines a node type.
Definition: BKE_node.h:226
NodeGeometryExecFunction geometry_node_execute
Definition: BKE_node.h:316
void(* draw_buttons)(struct uiLayout *, struct bContext *C, struct PointerRNA *ptr)
Definition: BKE_node.h:244
NodeDeclareFunction declare
Definition: BKE_node.h:324
float max
#define N_(msgid)
PointerRNA * ptr
Definition: wm_files.c:3480