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()) {
265 return return_default();
268 const Curves &curves_id = *geometry_set_.get_curves();
271 return return_default();
277 if (!sampled_tangents.is_empty()) {
280 if (!sampled_normals.is_empty()) {
286 const VArray<int> curve_indices =
params.readonly_single_input<
int>(0,
"Curve Index");
292 GArray<> src_original_values(source_data_->type());
293 GArray<> src_evaluated_values(source_data_->type());
296 if (!sampled_positions.
is_empty()) {
299 if (!sampled_tangents.is_empty()) {
302 if (!sampled_normals.is_empty()) {
305 if (!sampled_values.is_empty()) {
307 using T = decltype(dummy);
308 index_mask::masked_fill<T>(sampled_values.typed<T>(), {},
mask);
313 auto sample_curve = [&](
const int curve_i,
const IndexMask &
mask) {
314 const Span<float> accumulated_lengths = curves.evaluated_lengths_for_curve(curve_i,
316 if (accumulated_lengths.
is_empty()) {
323 factors.reinitialize(
mask.size());
324 sample_indices_and_factors_to_compressed(
325 accumulated_lengths, lengths, length_mode_,
mask,
indices, factors);
327 const IndexRange evaluated_points = evaluated_points_by_curve[curve_i];
328 if (!sampled_positions.is_empty()) {
330 evaluated_positions.slice(evaluated_points),
336 if (!sampled_tangents.is_empty()) {
338 evaluated_tangents.slice(evaluated_points),
indices, factors,
mask, sampled_tangents);
340 [&](
const int i) { sampled_tangents[i] =
math::normalize(sampled_tangents[i]); });
342 if (!sampled_normals.is_empty()) {
343 length_parameterize::interpolate_to_masked<float3>(
344 evaluated_normals.slice(evaluated_points),
indices, factors,
mask, sampled_normals);
346 [&](
const int i) { sampled_normals[i] =
math::normalize(sampled_normals[i]); });
348 if (!sampled_values.is_empty()) {
349 const IndexRange points = points_by_curve[curve_i];
350 src_original_values.reinitialize(points.
size());
352 src_evaluated_values.reinitialize(evaluated_points.
size());
353 curves.interpolate_to_evaluated(curve_i, src_original_values, src_evaluated_values);
354 bke::attribute_math::convert_to_static_type(source_data_->
type(), [&](
auto dummy) {
355 using T = decltype(dummy);
356 const Span<T> src_evaluated_values_typed = src_evaluated_values.as_span().typed<T>();
357 MutableSpan<T> sampled_values_typed = sampled_values.typed<T>();
358 length_parameterize::interpolate_to_masked<T>(
359 src_evaluated_values_typed, indices, factors, mask, sampled_values_typed);
364 if (
const std::optional<int> curve_i = curve_indices.get_if_single()) {
365 if (curves.curves_range().contains(*curve_i)) {
366 sample_curve(*curve_i,
mask);
373 Vector<int> valid_indices;
374 Vector<int> invalid_indices;
375 VectorSet<int> used_curves;
377 mask.foreach_index([&](
const int i) {
378 const int curve_i = curve_indices[i];
379 if (curves.curves_range().contains(curve_i)) {
380 used_curves.
add(curve_i);
384 invalid_indices.
append(i);
389 IndexMaskMemory memory;
390 const IndexMask valid_indices_mask = valid_indices.
size() ==
mask.size() ?
394 Array<IndexMask> mask_by_curve(used_curves.
size());
398 [&](
const int i) {
return used_curves.
index_of(curve_indices[i]); },
401 for (
const int i : mask_by_curve.index_range()) {
402 sample_curve(used_curves[i], mask_by_curve[i]);
409 void evaluate_source()
412 const bke::CurvesGeometry &curves = curves_id.
geometry.wrap();
413 source_context_.emplace(bke::CurvesFieldContext{curves_id, AttrDomain::Point});
414 source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, curves.points_num());
415 source_evaluator_->add(src_field_);
416 source_evaluator_->evaluate();
417 source_data_ = &source_evaluator_->get_evaluated(0);
429 curve_lengths[i] =
length;
431 return curve_lengths;
438 params.set_default_remaining_outputs();
445 params.set_default_remaining_outputs();
458 std::shared_ptr<FieldOperation> sample_op;
461 std::make_unique<SampleCurveFunction>(
462 std::move(geometry_set), mode, std::move(src_values_field)),
467 auto index_fn = std::make_unique<SampleFloatSegmentsFunction>(
473 std::make_unique<SampleCurveFunction>(
475 {std::move(curve_index), std::move(length_in_curve)});
481 std::make_unique<SampleCurveFunction>(
482 std::move(geometry_set), mode, std::move(src_values_field)),
483 {std::move(curve_index), std::move(length_in_curve)});
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)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
SIMD_FORCE_INLINE btScalar length(const btQuaternion &q)
Return the length of a quaternion.
VecBase< float, 3 > float3
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
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 size() const
constexpr bool is_empty() const
constexpr const T & last(const int64_t n=0) const
constexpr bool is_empty() 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)
local_group_size(16, 16) .push_constant(Type b
ccl_device_inline float4 mask(const int4 mask, const float4 a)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
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))
void node_register_type(bNodeType *ntype)
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)
void interpolate_to_masked(const Span< T > src, const Span< int > indices, const Span< float > factors, const IndexMask &dst_mask, MutableSpan< T > dst)
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, int type, const char *name, short nclass)
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
void(* initfunc)(bNodeTree *ntree, bNode *node)
NodeGeometryExecFunction geometry_node_execute
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
NodeDeclareFunction declare