Blender  V3.3
node_geo_curve_sample.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
5 
6 #include "BKE_curves.hh"
7 
8 #include "UI_interface.h"
9 #include "UI_resources.h"
10 
11 #include "node_geometry_util.hh"
12 
14 
16 
18 {
19  b.add_input<decl::Geometry>(N_("Curve"))
20  .only_realized_data()
21  .supported_type(GEO_COMPONENT_TYPE_CURVE);
22  b.add_input<decl::Float>(N_("Factor"))
23  .min(0.0f)
24  .max(1.0f)
25  .subtype(PROP_FACTOR)
26  .supports_field()
27  .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_FACTOR; });
28  b.add_input<decl::Float>(N_("Length"))
29  .min(0.0f)
30  .subtype(PROP_DISTANCE)
31  .supports_field()
32  .make_available([](bNode &node) { node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_LENGTH; });
33  b.add_output<decl::Vector>(N_("Position")).dependent_field();
34  b.add_output<decl::Vector>(N_("Tangent")).dependent_field();
35  b.add_output<decl::Vector>(N_("Normal")).dependent_field();
36 }
37 
38 static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
39 {
40  uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
41 }
42 
44 {
45  NodeGeometryCurveSample *data = MEM_cnew<NodeGeometryCurveSample>(__func__);
47  node->storage = data;
48 }
49 
51 {
52  const NodeGeometryCurveSample &storage = node_storage(*node);
54 
55  bNodeSocket *factor = ((bNodeSocket *)node->inputs.first)->next;
56  bNodeSocket *length = factor->next;
57 
60 }
61 
62 static void sample_indices_and_lengths(const Span<float> accumulated_lengths,
63  const Span<float> sample_lengths,
64  const IndexMask mask,
65  MutableSpan<int> r_segment_indices,
66  MutableSpan<float> r_length_in_segment)
67 {
68  const float total_length = accumulated_lengths.last();
70 
71  mask.to_best_mask_type([&](const auto mask) {
72  for (const int64_t i : mask) {
73  int segment_i;
74  float factor_in_segment;
75  length_parameterize::sample_at_length(accumulated_lengths,
76  std::clamp(sample_lengths[i], 0.0f, total_length),
77  segment_i,
78  factor_in_segment,
79  &hint);
80  const float segment_start = segment_i == 0 ? 0.0f : accumulated_lengths[segment_i - 1];
81  const float segment_end = accumulated_lengths[segment_i];
82  const float segment_length = segment_end - segment_start;
83 
84  r_segment_indices[i] = segment_i;
85  r_length_in_segment[i] = factor_in_segment * segment_length;
86  }
87  });
88 }
89 
90 static void sample_indices_and_factors_to_compressed(const Span<float> accumulated_lengths,
91  const Span<float> sample_lengths,
92  const IndexMask mask,
93  MutableSpan<int> r_segment_indices,
94  MutableSpan<float> r_factor_in_segment)
95 {
96  const float total_length = accumulated_lengths.last();
98 
99  mask.to_best_mask_type([&](const auto mask) {
100  for (const int64_t i : IndexRange(mask.size())) {
101  const float length = sample_lengths[mask[i]];
102  length_parameterize::sample_at_length(accumulated_lengths,
103  std::clamp(length, 0.0f, total_length),
104  r_segment_indices[i],
105  r_factor_in_segment[i],
106  &hint);
107  }
108  });
109 }
110 
116  private:
117  Array<float> accumulated_lengths_;
118 
119  public:
121  : accumulated_lengths_(std::move(accumulated_lengths))
122  {
124  this->set_signature(&signature);
125  }
126 
128  {
129  fn::MFSignatureBuilder signature{"Sample Curve Index"};
130  signature.single_input<float>("Length");
131 
132  signature.single_output<int>("Curve Index");
133  signature.single_output<float>("Length in Curve");
134  return signature.build();
135  }
136 
138  {
139  const VArraySpan<float> lengths = params.readonly_single_input<float>(0, "Length");
140  MutableSpan<int> indices = params.uninitialized_single_output<int>(1, "Curve Index");
141  MutableSpan<float> lengths_in_segments = params.uninitialized_single_output<float>(
142  2, "Length in Curve");
143 
144  sample_indices_and_lengths(accumulated_lengths_, lengths, mask, indices, lengths_in_segments);
145  }
146 };
147 
149  private:
155  GeometrySet geometry_set_;
156 
157  public:
158  SampleCurveFunction(GeometrySet geometry_set) : geometry_set_(std::move(geometry_set))
159  {
161  this->set_signature(&signature);
162  }
163 
165  {
167  signature.single_input<int>("Curve Index");
168  signature.single_input<float>("Length");
169  signature.single_output<float3>("Position");
170  signature.single_output<float3>("Tangent");
171  signature.single_output<float3>("Normal");
172  return signature.build();
173  }
174 
176  {
177  MutableSpan<float3> sampled_positions = params.uninitialized_single_output_if_required<float3>(
178  2, "Position");
179  MutableSpan<float3> sampled_tangents = params.uninitialized_single_output_if_required<float3>(
180  3, "Tangent");
181  MutableSpan<float3> sampled_normals = params.uninitialized_single_output_if_required<float3>(
182  4, "Normal");
183 
184  auto return_default = [&]() {
185  if (!sampled_positions.is_empty()) {
186  sampled_positions.fill_indices(mask, {0, 0, 0});
187  }
188  if (!sampled_tangents.is_empty()) {
189  sampled_tangents.fill_indices(mask, {0, 0, 0});
190  }
191  if (!sampled_normals.is_empty()) {
192  sampled_normals.fill_indices(mask, {0, 0, 0});
193  }
194  };
195 
196  if (!geometry_set_.has_curves()) {
197  return return_default();
198  }
199 
200  const Curves &curves_id = *geometry_set_.get_curves_for_read();
202  if (curves.points_num() == 0) {
203  return return_default();
204  }
205  Span<float3> evaluated_positions = curves.evaluated_positions();
206  Span<float3> evaluated_tangents;
207  Span<float3> evaluated_normals;
208  if (!sampled_tangents.is_empty()) {
209  evaluated_tangents = curves.evaluated_tangents();
210  }
211  if (!sampled_normals.is_empty()) {
212  evaluated_normals = curves.evaluated_normals();
213  }
214 
215  const VArray<int> curve_indices = params.readonly_single_input<int>(0, "Curve Index");
216  const VArraySpan<float> lengths = params.readonly_single_input<float>(1, "Length");
217  const VArray<bool> cyclic = curves.cyclic();
218 
220  Array<float> factors;
221 
222  auto sample_curve = [&](const int curve_i, const IndexMask mask) {
223  /* Store the sampled indices and factors in arrays the size of the mask.
224  * Then, during interpolation, move the results back to the masked indices. */
225  indices.reinitialize(mask.size());
226  factors.reinitialize(mask.size());
228  curves.evaluated_lengths_for_curve(curve_i, cyclic[curve_i]),
229  lengths,
230  mask,
231  indices,
232  factors);
233 
234  const IndexRange evaluated_points = curves.evaluated_points_for_curve(curve_i);
235  if (!sampled_positions.is_empty()) {
236  length_parameterize::interpolate_to_masked<float3>(
237  evaluated_positions.slice(evaluated_points),
238  indices,
239  factors,
240  mask,
241  sampled_positions);
242  }
243  if (!sampled_tangents.is_empty()) {
244  length_parameterize::interpolate_to_masked<float3>(
245  evaluated_tangents.slice(evaluated_points), indices, factors, mask, sampled_tangents);
246  for (const int64_t i : mask) {
247  sampled_tangents[i] = math::normalize(sampled_tangents[i]);
248  }
249  }
250  if (!sampled_normals.is_empty()) {
251  length_parameterize::interpolate_to_masked<float3>(
252  evaluated_normals.slice(evaluated_points), indices, factors, mask, sampled_normals);
253  for (const int64_t i : mask) {
254  sampled_normals[i] = math::normalize(sampled_normals[i]);
255  }
256  }
257  };
258 
259  if (curve_indices.is_single()) {
260  sample_curve(curve_indices.get_internal_single(), mask);
261  }
262  else {
263  MultiValueMap<int, int64_t> indices_per_curve;
264  devirtualize_varray(curve_indices, [&](const auto curve_indices) {
265  for (const int64_t i : mask) {
266  indices_per_curve.add(curve_indices[i], i);
267  }
268  });
269 
270  for (const int curve_i : indices_per_curve.keys()) {
271  sample_curve(curve_i, IndexMask(indices_per_curve.lookup(curve_i)));
272  }
273  }
274  }
275 };
276 
285  const GeometryNodeCurveSampleMode mode,
286  const float curves_total_length)
287 {
288  if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
289  return params.extract_input<Field<float>>("Length");
290  }
291 
292  /* Convert the factor to a length. */
293  Field<float> factor_field = params.get_input<Field<float>>("Factor");
294  auto clamp_fn = std::make_unique<fn::CustomMF_SI_SO<float, float>>(
295  __func__,
296  [curves_total_length](float factor) { return factor * curves_total_length; },
298 
299  return Field<float>(FieldOperation::Create(std::move(clamp_fn), {std::move(factor_field)}), 0);
300 }
301 
303 {
304  curves.ensure_evaluated_lengths();
305 
306  Array<float> curve_lengths(curves.curves_num());
307  const VArray<bool> cyclic = curves.cyclic();
308  float length = 0.0f;
309  for (const int i : curves.curves_range()) {
310  length += curves.evaluated_length_total_for_curve(i, cyclic[i]);
311  curve_lengths[i] = length;
312  }
313  return curve_lengths;
314 }
315 
317 {
318  GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
319  if (!geometry_set.has_curves()) {
320  params.set_default_remaining_outputs();
321  return;
322  }
323 
324  const Curves &curves_id = *geometry_set.get_curves_for_read();
326  if (curves.points_num() == 0) {
327  params.set_default_remaining_outputs();
328  return;
329  }
330 
332  const float total_length = curve_lengths.last();
333  if (total_length == 0.0f) {
334  params.set_default_remaining_outputs();
335  return;
336  }
337 
338  const NodeGeometryCurveSample &storage = node_storage(params.node());
340  Field<float> length_field = get_length_input_field(params, mode, total_length);
341 
342  auto sample_fn = std::make_unique<SampleCurveFunction>(std::move(geometry_set));
343 
344  std::shared_ptr<FieldOperation> sample_op;
345  if (curves.curves_num() == 1) {
346  sample_op = FieldOperation::Create(std::move(sample_fn),
347  {fn::make_constant_field<int>(0), std::move(length_field)});
348  }
349  else {
350  auto index_fn = std::make_unique<SampleFloatSegmentsFunction>(std::move(curve_lengths));
351  auto index_op = FieldOperation::Create(std::move(index_fn), {std::move(length_field)});
352  sample_op = FieldOperation::Create(std::move(sample_fn),
353  {Field<int>(index_op, 0), Field<float>(index_op, 1)});
354  }
355 
356  params.set_output("Position", Field<float3>(sample_op, 0));
357  params.set_output("Tangent", Field<float3>(sample_op, 1));
358  params.set_output("Normal", Field<float3>(sample_op, 2));
359 }
360 
361 } // namespace blender::nodes::node_geo_curve_sample_cc
362 
364 {
365  namespace file_ns = blender::nodes::node_geo_curve_sample_cc;
366 
367  static bNodeType ntype;
368 
375  &ntype, "NodeGeometryCurveSample", node_free_standard_storage, node_copy_standard_storage);
377 
378  nodeRegisterType(&ntype);
379 }
Low-level operations for curves.
@ 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
#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_SAMPLE_CURVE
Definition: BKE_node.h:1427
void nodeRegisterType(struct bNodeType *ntype)
Definition: node.cc:1357
#define UNUSED(x)
GeometryNodeCurveSampleMode
@ GEO_NODE_CURVE_SAMPLE_FACTOR
@ GEO_NODE_CURVE_SAMPLE_LENGTH
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to curves
@ PROP_DISTANCE
Definition: RNA_types.h:149
@ PROP_FACTOR
Definition: RNA_types.h:144
@ UI_ITEM_R_EXPAND
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
const T & last(const int64_t n=0) const
Definition: BLI_array.hh:284
void reinitialize(const int64_t new_size)
Definition: BLI_array.hh:387
MapType::KeyIterator keys() const
Span< Value > lookup(const Key &key) const
void add(const Key &key, const Value &value)
constexpr bool is_empty() const
Definition: BLI_span.hh:519
constexpr void fill_indices(Span< int64_t > indices, const T &value)
Definition: BLI_span.hh:536
constexpr Span slice(int64_t start, int64_t size) const
Definition: BLI_span.hh:142
constexpr const T & last(const int64_t n=0) const
Definition: BLI_span.hh:313
static CurvesGeometry & wrap(::CurvesGeometry &dna_struct)
Definition: BKE_curves.hh:138
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const MultiFunction > function, Vector< GField > inputs={})
Definition: FN_field.hh:225
void set_signature(const MFSignature *signature)
const MFSignature & signature() const
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
OperationNode * node
void * tree
bNodeTree * ntree
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_gpu_kernel_postfix int ccl_global int * indices
ccl_device_inline float4 mask(const int4 &mask, const float4 &a)
Definition: math_float4.h:513
static ulong * next
void sample_at_length(const Span< float > accumulated_segment_lengths, const float sample_length, int &r_segment_index, float &r_factor, SampleSegmentHint *hint=nullptr)
T clamp(const T &a, const T &min, const T &max)
T length(const vec_base< T, Size > &a)
vec_base< T, Size > normalize(const vec_base< T, Size > &v)
static Array< float > curve_accumulated_lengths(const bke::CurvesGeometry &curves)
static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
static void node_declare(NodeDeclarationBuilder &b)
static void sample_indices_and_lengths(const Span< float > accumulated_lengths, const Span< float > sample_lengths, const IndexMask mask, MutableSpan< int > r_segment_indices, MutableSpan< float > r_length_in_segment)
static Field< float > get_length_input_field(GeoNodeExecParams params, const GeometryNodeCurveSampleMode mode, const float curves_total_length)
static void sample_indices_and_factors_to_compressed(const Span< float > accumulated_lengths, const Span< float > sample_lengths, const IndexMask mask, MutableSpan< int > r_segment_indices, MutableSpan< float > r_factor_in_segment)
static void node_type_init(bNodeTree *UNUSED(tree), bNode *node)
static void node_geo_exec(GeoNodeExecParams params)
static void node_update(bNodeTree *ntree, bNode *node)
void devirtualize_varray(const VArray< T > &varray, const Func &func, bool enable=true)
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
void register_node_type_geo_curve_sample()
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass)
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
#define min(a, b)
Definition: sort.c:35
__int64 int64_t
Definition: stdint.h:89
CurvesGeometry geometry
const Curves * get_curves_for_read() const
bool has_curves() const
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
#define N_(msgid)
PointerRNA * ptr
Definition: wm_files.c:3480