29 .supported_type(GeometryComponent::Type::Curve)
30 .description(
"Curves to sample positions on");
32 if (
const bNode *node =
b.node_or_null()) {
33 const NodeGeometryCurveSample &storage = node_storage(*node);
34 b.add_input(eCustomDataType(storage.data_type),
"Value").hide_value().field_on_all();
42 .structure_type(StructureType::Dynamic)
43 .make_available([](
bNode &node) {
44 node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_FACTOR;
50 .structure_type(StructureType::Dynamic)
51 .make_available([](
bNode &node) {
52 node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_LENGTH;
54 auto &index =
b.add_input<
decl::Int>(
"Curve Index")
58 [](
bNode &node) { node_storage(node).use_all_curves = false; });
60 if (
const bNode *node =
b.node_or_null()) {
61 const NodeGeometryCurveSample &storage = node_storage(*node);
62 const GeometryNodeCurveSampleMode mode = GeometryNodeCurveSampleMode(storage.mode);
63 b.add_output(eCustomDataType(storage.data_type),
"Value").dependent_field({2, 3, 4});
67 index.available(!storage.use_all_curves);
70 b.add_output<
decl::Vector>(
"Position").dependent_field({2, 3, 4});
71 b.add_output<decl::Vector>(
"Tangent").dependent_field({2, 3, 4});
72 b.add_output<decl::Vector>(
"Normal").dependent_field({2, 3, 4});
86 data->use_all_curves =
false;
103 bNode &node =
params.add_node(
"GeometryNodeSampleCurve");
104 node_storage(node).data_type = *type;
105 params.update_and_connect_available_socket(node,
"Value");
117 const float total_length = accumulated_lengths.
last();
120 mask.foreach_index_optimized<
int>([&](
const int i) {
122 sample_lengths[
i] * total_length :
125 float factor_in_segment;
127 std::clamp(sample_length, 0.0f, total_length),
131 const float segment_start = segment_i == 0 ? 0.0f : accumulated_lengths[segment_i - 1];
132 const float segment_end = accumulated_lengths[segment_i];
133 const float segment_length = segment_end - segment_start;
135 r_segment_indices[
i] = segment_i;
136 r_length_in_segment[
i] = factor_in_segment * segment_length;
147 const float total_length = accumulated_lengths.
last();
150 switch (length_mode) {
152 mask.foreach_index_optimized<
int>([&](
const int i,
const int pos) {
153 const float length = sample_lengths[
i] * total_length;
155 std::clamp(
length, 0.0f, total_length),
156 r_segment_indices[
pos],
157 r_factor_in_segment[
pos],
162 mask.foreach_index_optimized<
int>([&](
const int i,
const int pos) {
163 const float length = sample_lengths[
i];
165 std::clamp(
length, 0.0f, total_length),
166 r_segment_indices[
pos],
167 r_factor_in_segment[
pos],
186 : accumulated_lengths_(std::move(accumulated_lengths)), length_mode_(length_mode)
188 static const mf::Signature
signature = []() {
190 mf::SignatureBuilder builder{
"Sample Curve Index",
signature};
191 builder.single_input<
float>(
"Length");
193 builder.single_output<
int>(
"Curve Index");
194 builder.single_output<
float>(
"Length in Curve");
205 2,
"Length in Curve");
208 accumulated_lengths_, lengths, length_mode_,
mask,
indices, lengths_in_segments);
223 mf::Signature signature_;
225 std::optional<bke::CurvesFieldContext> source_context_;
226 std::unique_ptr<FieldEvaluator> source_evaluator_;
233 : geometry_set_(std::move(geometry_set)), src_field_(src_field), length_mode_(length_mode)
235 mf::SignatureBuilder builder{
"Sample Curve", signature_};
236 builder.single_input<
int>(
"Curve Index");
237 builder.single_input<
float>(
"Length");
238 builder.single_output<
float3>(
"Position", mf::ParamFlag::SupportsUnusedOutput);
239 builder.single_output<
float3>(
"Tangent", mf::ParamFlag::SupportsUnusedOutput);
240 builder.single_output<
float3>(
"Normal", mf::ParamFlag::SupportsUnusedOutput);
241 builder.single_output(
"Value", src_field_.cpp_type(), mf::ParamFlag::SupportsUnusedOutput);
242 this->set_signature(&signature_);
244 this->evaluate_source();
255 GMutableSpan sampled_values =
params.uninitialized_single_output_if_required(5,
"Value");
257 auto return_default = [&]() {
258 if (!sampled_positions.
is_empty()) {
261 if (!sampled_tangents.is_empty()) {
264 if (!sampled_normals.is_empty()) {
269 if (!geometry_set_.has_curves()) {
274 const Curves &curves_id = *geometry_set_.get_curves();
284 if (!sampled_tangents.is_empty()) {
287 if (!sampled_normals.is_empty()) {
293 const VArray<int> curve_indices =
params.readonly_single_input<
int>(0,
"Curve Index");
299 GArray<> src_original_values(source_data_->type());
300 GArray<> src_evaluated_values(source_data_->type());
303 if (!sampled_positions.
is_empty()) {
306 if (!sampled_tangents.is_empty()) {
309 if (!sampled_normals.is_empty()) {
312 if (!sampled_values.is_empty()) {
314 using T = decltype(dummy);
315 index_mask::masked_fill<T>(sampled_values.typed<T>(), {},
mask);
320 auto sample_curve = [&](
const int curve_i,
const IndexMask &
mask) {
321 const IndexRange evaluated_points = evaluated_points_by_curve[curve_i];
322 if (evaluated_points.
size() == 1) {
323 if (!sampled_positions.is_empty()) {
325 sampled_positions, evaluated_positions[evaluated_points.
first()],
mask);
327 if (!sampled_tangents.is_empty()) {
329 sampled_tangents, evaluated_tangents[evaluated_points.
first()],
mask);
331 if (!sampled_normals.is_empty()) {
333 sampled_normals, evaluated_normals[evaluated_points.
first()],
mask);
335 if (!sampled_values.is_empty()) {
336 bke::attribute_math::convert_to_static_type(source_data_->
type(), [&](
auto dummy) {
337 using T = decltype(dummy);
338 const T &value = source_data_->typed<T>()[points_by_curve[curve_i].first()];
339 index_mask::masked_fill<T>(sampled_values.typed<T>(), value, mask);
345 const Span<float> accumulated_lengths = curves.evaluated_lengths_for_curve(curve_i,
347 if (accumulated_lengths.
is_empty()) {
356 factors.reinitialize(
mask.size());
358 accumulated_lengths, lengths, length_mode_,
mask,
indices, factors);
360 if (!sampled_positions.is_empty()) {
361 length_parameterize::interpolate_to_masked<float3>(
362 evaluated_positions.slice(evaluated_points),
368 if (!sampled_tangents.is_empty()) {
369 length_parameterize::interpolate_to_masked<float3>(
370 evaluated_tangents.slice(evaluated_points),
indices, factors,
mask, sampled_tangents);
374 if (!sampled_normals.is_empty()) {
375 length_parameterize::interpolate_to_masked<float3>(
376 evaluated_normals.slice(evaluated_points),
indices, factors,
mask, sampled_normals);
380 if (!sampled_values.is_empty()) {
381 const IndexRange points = points_by_curve[curve_i];
382 src_original_values.reinitialize(points.
size());
384 src_evaluated_values.reinitialize(evaluated_points.
size());
385 curves.interpolate_to_evaluated(curve_i, src_original_values, src_evaluated_values);
386 bke::attribute_math::convert_to_static_type(source_data_->
type(), [&](
auto dummy) {
387 using T = decltype(dummy);
388 const Span<T> src_evaluated_values_typed = src_evaluated_values.as_span().typed<T>();
389 MutableSpan<T> sampled_values_typed = sampled_values.typed<T>();
390 length_parameterize::interpolate_to_masked<T>(
391 src_evaluated_values_typed, indices, factors, mask, sampled_values_typed);
396 if (
const std::optional<int> curve_i = curve_indices.get_if_single()) {
397 if (curves.curves_range().contains(*curve_i)) {
398 sample_curve(*curve_i,
mask);
405 Vector<int> valid_indices;
406 Vector<int> invalid_indices;
407 VectorSet<int> used_curves;
409 mask.foreach_index([&](
const int i) {
410 const int curve_i = curve_indices[
i];
411 if (curves.curves_range().contains(curve_i)) {
412 used_curves.
add(curve_i);
421 IndexMaskMemory memory;
422 const IndexMask valid_indices_mask = valid_indices.
size() ==
mask.size() ?
426 Array<IndexMask> mask_by_curve(used_curves.
size());
430 [&](
const int i) {
return used_curves.
index_of(curve_indices[
i]); },
433 for (
const int i : mask_by_curve.index_range()) {
434 sample_curve(used_curves[
i], mask_by_curve[
i]);
441 void evaluate_source()
444 const bke::CurvesGeometry &curves = curves_id.
geometry.wrap();
445 source_context_.emplace(bke::CurvesFieldContext{curves_id, AttrDomain::Point});
446 source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, curves.points_num());
447 source_evaluator_->add(src_field_);
448 source_evaluator_->evaluate();
449 source_data_ = &source_evaluator_->get_evaluated(0);
463 return curve_lengths;
470 params.set_default_remaining_outputs();
477 params.set_default_remaining_outputs();
491 std::string error_message;
497 std::shared_ptr<FieldOperation> sample_op;
501 std::make_unique<SampleCurveFunction>(
502 std::move(geometry_set), mode, std::move(src_values_field)),
503 {&curve_index, &sample_length},
504 {&position, &tangent, &normal, &value},
508 params.set_default_remaining_outputs();
520 {&curve_index, &length_in_curve},
524 params.set_default_remaining_outputs();
529 std::make_shared<SampleCurveFunction>(std::move(geometry_set),
531 std::move(src_values_field)),
532 {&curve_index, &length_in_curve},
533 {&position, &tangent, &normal, &value},
537 params.set_default_remaining_outputs();
545 std::make_shared<SampleCurveFunction>(
546 std::move(geometry_set), mode, std::move(src_values_field)),
547 {&curve_index, &sample_length},
548 {&position, &tangent, &normal, &value},
552 params.set_default_remaining_outputs();
559 params.set_output(
"Position", std::move(position));
560 params.set_output(
"Tangent", std::move(tangent));
561 params.set_output(
"Normal", std::move(normal));
562 params.set_output(
"Value", std::move(value));
570 ntype.
ui_name =
"Sample Curve";
572 "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
static SocketValueVariant From(T &&value)
const Signature & signature() const
void set_signature(const Signature *signature)
Vector< SocketDeclaration * > inputs
Vector< SocketDeclaration * > outputs
StructureType structure_type
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)
float length(VecOp< float, D >) RET
VecBase< float, 3 > float3
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)
bool execute_multi_function_on_value_variant(const MultiFunction &fn, const std::shared_ptr< MultiFunction > &owned_fn, const Span< SocketValueVariant * > input_values, const Span< SocketValueVariant * > output_values, GeoNodesUserData *user_data, std::string &r_error_message)
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)