28 GeometryComponent::Type::Curve);
30 if (
const bNode *node =
b.node_or_null()) {
31 const NodeGeometryCurveSample &storage = node_storage(*node);
32 b.add_input(eCustomDataType(storage.data_type),
"Value").hide_value().field_on_all();
40 .make_available([](
bNode &node) {
41 node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_FACTOR;
47 .make_available([](
bNode &node) {
48 node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_LENGTH;
52 node_storage(node).use_all_curves = false;
55 if (
const bNode *node =
b.node_or_null()) {
56 const NodeGeometryCurveSample &storage = node_storage(*node);
57 const GeometryNodeCurveSampleMode mode = GeometryNodeCurveSampleMode(storage.mode);
58 b.add_output(eCustomDataType(storage.data_type),
"Value").dependent_field({2, 3, 4});
62 index.available(!storage.use_all_curves);
65 b.add_output<
decl::Vector>(
"Position").dependent_field({2, 3, 4});
66 b.add_output<decl::Vector>(
"Tangent").dependent_field({2, 3, 4});
67 b.add_output<decl::Vector>(
"Normal").dependent_field({2, 3, 4});
81 data->use_all_curves =
false;
98 bNode &node =
params.add_node(
"GeometryNodeSampleCurve");
99 node_storage(node).data_type = *type;
100 params.update_and_connect_available_socket(node,
"Value");
112 const float total_length = accumulated_lengths.
last();
115 mask.foreach_index_optimized<
int>([&](
const int i) {
117 sample_lengths[
i] * total_length :
120 float factor_in_segment;
122 std::clamp(sample_length, 0.0f, total_length),
126 const float segment_start = segment_i == 0 ? 0.0f : accumulated_lengths[segment_i - 1];
127 const float segment_end = accumulated_lengths[segment_i];
128 const float segment_length = segment_end - segment_start;
130 r_segment_indices[
i] = segment_i;
131 r_length_in_segment[
i] = factor_in_segment * segment_length;
142 const float total_length = accumulated_lengths.
last();
145 switch (length_mode) {
147 mask.foreach_index_optimized<
int>([&](
const int i,
const int pos) {
148 const float length = sample_lengths[
i] * total_length;
150 std::clamp(
length, 0.0f, total_length),
151 r_segment_indices[
pos],
152 r_factor_in_segment[
pos],
157 mask.foreach_index_optimized<
int>([&](
const int i,
const int pos) {
158 const float length = sample_lengths[
i];
160 std::clamp(
length, 0.0f, total_length),
161 r_segment_indices[
pos],
162 r_factor_in_segment[
pos],
181 : accumulated_lengths_(std::move(accumulated_lengths)), length_mode_(length_mode)
183 static const mf::Signature
signature = []() {
185 mf::SignatureBuilder builder{
"Sample Curve Index",
signature};
186 builder.single_input<
float>(
"Length");
188 builder.single_output<
int>(
"Curve Index");
189 builder.single_output<
float>(
"Length in Curve");
200 2,
"Length in Curve");
203 accumulated_lengths_, lengths, length_mode_,
mask,
indices, lengths_in_segments);
218 mf::Signature signature_;
220 std::optional<bke::CurvesFieldContext> source_context_;
221 std::unique_ptr<FieldEvaluator> source_evaluator_;
228 : geometry_set_(std::move(geometry_set)), src_field_(src_field), length_mode_(length_mode)
230 mf::SignatureBuilder builder{
"Sample Curve", signature_};
231 builder.single_input<
int>(
"Curve Index");
232 builder.single_input<
float>(
"Length");
233 builder.single_output<
float3>(
"Position", mf::ParamFlag::SupportsUnusedOutput);
234 builder.single_output<
float3>(
"Tangent", mf::ParamFlag::SupportsUnusedOutput);
235 builder.single_output<
float3>(
"Normal", mf::ParamFlag::SupportsUnusedOutput);
236 builder.single_output(
"Value", src_field_.cpp_type(), mf::ParamFlag::SupportsUnusedOutput);
237 this->set_signature(&signature_);
239 this->evaluate_source();
250 GMutableSpan sampled_values =
params.uninitialized_single_output_if_required(5,
"Value");
252 auto return_default = [&]() {
253 if (!sampled_positions.
is_empty()) {
256 if (!sampled_tangents.is_empty()) {
259 if (!sampled_normals.is_empty()) {
264 if (!geometry_set_.has_curves()) {
269 const Curves &curves_id = *geometry_set_.get_curves();
279 if (!sampled_tangents.is_empty()) {
282 if (!sampled_normals.is_empty()) {
288 const VArray<int> curve_indices =
params.readonly_single_input<
int>(0,
"Curve Index");
294 GArray<> src_original_values(source_data_->type());
295 GArray<> src_evaluated_values(source_data_->type());
298 if (!sampled_positions.
is_empty()) {
301 if (!sampled_tangents.is_empty()) {
304 if (!sampled_normals.is_empty()) {
307 if (!sampled_values.is_empty()) {
309 using T = decltype(dummy);
310 index_mask::masked_fill<T>(sampled_values.typed<T>(), {},
mask);
315 auto sample_curve = [&](
const int curve_i,
const IndexMask &
mask) {
316 const IndexRange evaluated_points = evaluated_points_by_curve[curve_i];
317 if (evaluated_points.
size() == 1) {
318 if (!sampled_positions.is_empty()) {
320 sampled_positions, evaluated_positions[evaluated_points.
first()],
mask);
322 if (!sampled_tangents.is_empty()) {
324 sampled_tangents, evaluated_tangents[evaluated_points.
first()],
mask);
326 if (!sampled_normals.is_empty()) {
328 sampled_normals, evaluated_normals[evaluated_points.
first()],
mask);
330 if (!sampled_values.is_empty()) {
331 bke::attribute_math::convert_to_static_type(source_data_->
type(), [&](
auto dummy) {
332 using T = decltype(dummy);
333 const T &value = source_data_->typed<T>()[points_by_curve[curve_i].first()];
334 index_mask::masked_fill<T>(sampled_values.typed<T>(), value, mask);
340 const Span<float> accumulated_lengths = curves.evaluated_lengths_for_curve(curve_i,
342 if (accumulated_lengths.
is_empty()) {
351 factors.reinitialize(
mask.size());
353 accumulated_lengths, lengths, length_mode_,
mask,
indices, factors);
355 if (!sampled_positions.is_empty()) {
356 length_parameterize::interpolate_to_masked<float3>(
357 evaluated_positions.slice(evaluated_points),
363 if (!sampled_tangents.is_empty()) {
364 length_parameterize::interpolate_to_masked<float3>(
365 evaluated_tangents.slice(evaluated_points),
indices, factors,
mask, sampled_tangents);
369 if (!sampled_normals.is_empty()) {
370 length_parameterize::interpolate_to_masked<float3>(
371 evaluated_normals.slice(evaluated_points),
indices, factors,
mask, sampled_normals);
375 if (!sampled_values.is_empty()) {
376 const IndexRange points = points_by_curve[curve_i];
377 src_original_values.reinitialize(points.
size());
379 src_evaluated_values.reinitialize(evaluated_points.
size());
380 curves.interpolate_to_evaluated(curve_i, src_original_values, src_evaluated_values);
381 bke::attribute_math::convert_to_static_type(source_data_->
type(), [&](
auto dummy) {
382 using T = decltype(dummy);
383 const Span<T> src_evaluated_values_typed = src_evaluated_values.as_span().typed<T>();
384 MutableSpan<T> sampled_values_typed = sampled_values.typed<T>();
385 length_parameterize::interpolate_to_masked<T>(
386 src_evaluated_values_typed, indices, factors, mask, sampled_values_typed);
391 if (
const std::optional<int> curve_i = curve_indices.get_if_single()) {
392 if (curves.curves_range().contains(*curve_i)) {
393 sample_curve(*curve_i,
mask);
400 Vector<int> valid_indices;
401 Vector<int> invalid_indices;
402 VectorSet<int> used_curves;
404 mask.foreach_index([&](
const int i) {
405 const int curve_i = curve_indices[
i];
406 if (curves.curves_range().contains(curve_i)) {
407 used_curves.
add(curve_i);
416 IndexMaskMemory memory;
417 const IndexMask valid_indices_mask = valid_indices.
size() ==
mask.size() ?
421 Array<IndexMask> mask_by_curve(used_curves.
size());
425 [&](
const int i) {
return used_curves.
index_of(curve_indices[
i]); },
428 for (
const int i : mask_by_curve.index_range()) {
429 sample_curve(used_curves[
i], mask_by_curve[
i]);
436 void evaluate_source()
439 const bke::CurvesGeometry &curves = curves_id.
geometry.wrap();
440 source_context_.emplace(bke::CurvesFieldContext{curves_id, AttrDomain::Point});
441 source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, curves.points_num());
442 source_evaluator_->add(src_field_);
443 source_evaluator_->evaluate();
444 source_data_ = &source_evaluator_->get_evaluated(0);
458 return curve_lengths;
465 params.set_default_remaining_outputs();
472 params.set_default_remaining_outputs();
485 std::shared_ptr<FieldOperation> sample_op;
488 std::make_unique<SampleCurveFunction>(
489 std::move(geometry_set), mode, std::move(src_values_field)),
494 auto index_fn = std::make_unique<SampleFloatSegmentsFunction>(
500 std::make_unique<SampleCurveFunction>(
502 {std::move(curve_index), std::move(length_in_curve)});
508 std::make_unique<SampleCurveFunction>(
509 std::move(geometry_set), mode, std::move(src_values_field)),
510 {std::move(curve_index), std::move(length_in_curve)});
525 ntype.
ui_name =
"Sample Curve";
527 "Retrieve data from a point on a curve at a certain distance from its start";
Low-level operations for curves.
#define NODE_STORAGE_FUNCS(StorageT)
#define NODE_CLASS_GEOMETRY
#define GEO_NODE_SAMPLE_CURVE
GeometryNodeCurveSampleMode
@ GEO_NODE_CURVE_SAMPLE_FACTOR
@ GEO_NODE_CURVE_SAMPLE_LENGTH
#define NOD_REGISTER_NODE(REGISTER_FUNC)
BMesh const char void * data
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
static void from_groups(const IndexMask &universe, IndexMaskMemory &memory, Fn &&get_group_index, MutableSpan< IndexMask > r_masks)
constexpr int64_t size() const
constexpr bool is_empty() const
int64_t index_of(const Key &key) const
void append(const T &value)
Span< T > as_span() const
void materialize_compressed_to_uninitialized(const IndexMask &mask, void *dst) const
const CPPType & type() const
constexpr int64_t first() const
constexpr int64_t size() const
constexpr bool is_empty() const
constexpr const T & last(const int64_t n=0) const
OffsetIndices< int > points_by_curve() const
void ensure_can_interpolate_to_evaluated() const
IndexRange curves_range() const
Span< float3 > evaluated_tangents() const
Span< float3 > evaluated_normals() const
OffsetIndices< int > evaluated_points_by_curve() const
float evaluated_length_total_for_curve(int curve_index, bool cyclic) const
void ensure_evaluated_lengths() const
Span< float3 > evaluated_positions() const
VArray< bool > cyclic() const
const Signature & signature() const
void set_signature(const Signature *signature)
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Vector< SocketDeclaration * > inputs
Vector< SocketDeclaration * > outputs
void make_available(bNode &node) const
bool only_realized_data() const
SampleCurveFunction(GeometrySet geometry_set, const GeometryNodeCurveSampleMode length_mode, const GField &src_field)
void call(const IndexMask &mask, mf::Params params, mf::Context) const override
void call(const IndexMask &mask, mf::Params params, mf::Context) const override
SampleFloatSegmentsFunction(Array< float > accumulated_lengths, const GeometryNodeCurveSampleMode length_mode)
VecBase< float, 3 > float3
float length(VecOp< float, D >) RET
void * MEM_callocN(size_t len, const char *str)
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
void node_register_type(bNodeType &ntype)
void node_type_storage(bNodeType &ntype, std::optional< StringRefNull > storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
std::optional< eCustomDataType > socket_type_to_custom_data_type(eNodeSocketDatatype type)
GField make_constant_field(const CPPType &type, const void *value)
void masked_fill(MutableSpan< T > data, const T &value, const IndexMask &mask)
void sample_at_length(const Span< float > accumulated_segment_lengths, const float sample_length, int &r_segment_index, float &r_factor, SampleSegmentHint *hint=nullptr)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
static void sample_indices_and_factors_to_compressed(const Span< float > accumulated_lengths, const Span< float > sample_lengths, const GeometryNodeCurveSampleMode length_mode, const IndexMask &mask, MutableSpan< int > r_segment_indices, MutableSpan< float > r_factor_in_segment)
static Array< float > curve_accumulated_lengths(const bke::CurvesGeometry &curves)
static void node_declare(NodeDeclarationBuilder &b)
static void node_register()
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static void node_geo_exec(GeoNodeExecParams params)
static void node_gather_link_searches(GatherLinkSearchOpParams ¶ms)
static void sample_indices_and_lengths(const Span< float > accumulated_lengths, const Span< float > sample_lengths, const GeometryNodeCurveSampleMode length_mode, const IndexMask &mask, MutableSpan< int > r_segment_indices, MutableSpan< float > r_length_in_segment)
static void node_init(bNodeTree *, bNode *node)
void search_link_ops_for_declarations(GatherLinkSearchOpParams ¶ms, Span< SocketDeclaration * > declarations)
void devirtualize_varray(const VArray< T > &varray, const Func &func, bool enable=true)
VecBase< float, 3 > float3
void geo_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
void node_free_standard_storage(bNode *node)
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
const Curves * get_curves() const
std::string ui_description
void(* initfunc)(bNodeTree *ntree, bNode *node)
NodeGeometryExecFunction geometry_node_execute
const char * enum_name_legacy
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
NodeDeclareFunction declare
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)