Blender V5.0
node_geo_sample_index.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BLI_task.hh"
6
8
10#include "UI_resources.hh"
11
13
14#include "node_geometry_util.hh"
15
17
19
21{
22 const bNode *node = b.node_or_null();
23 b.add_input<decl::Geometry>("Geometry")
24 .supported_type({GeometryComponent::Type::Mesh,
25 GeometryComponent::Type::PointCloud,
26 GeometryComponent::Type::Curve,
27 GeometryComponent::Type::Instance,
28 GeometryComponent::Type::GreasePencil})
29 .description("Geometry to sample a value on");
30 if (node != nullptr) {
31 const eCustomDataType data_type = eCustomDataType(node_storage(*node).data_type);
32 b.add_input(data_type, "Value").hide_value().field_on_all();
33 }
34 b.add_input<decl::Int>("Index")
35 .supports_field()
36 .description("Which element to retrieve a value from on the geometry")
37 .structure_type(StructureType::Dynamic);
38
39 if (node != nullptr) {
40 const eCustomDataType data_type = eCustomDataType(node_storage(*node).data_type);
41 b.add_output(data_type, "Value").dependent_field({2});
42 }
43}
44
45static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
46{
47 layout->prop(ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
48 layout->prop(ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
49 layout->prop(ptr, "clamp", UI_ITEM_NONE, std::nullopt, ICON_NONE);
50}
51
52static void node_init(bNodeTree * /*tree*/, bNode *node)
53{
55 data->data_type = CD_PROP_FLOAT;
56 data->domain = int8_t(AttrDomain::Point);
57 data->clamp = 0;
58 node->storage = data;
59}
60
62{
63 const NodeDeclaration &declaration = *params.node_type().static_declaration;
65
66 const std::optional<eCustomDataType> type = bke::socket_type_to_custom_data_type(
67 eNodeSocketDatatype(params.other_socket().type));
68 if (type && *type != CD_PROP_STRING) {
69 /* The input and output sockets have the same name. */
70 params.add_item(IFACE_("Value"), [type](LinkSearchOpParams &params) {
71 bNode &node = params.add_node("GeometryNodeSampleIndex");
72 node_storage(node).data_type = *type;
73 params.update_and_connect_available_socket(node, "Value");
74 });
75 }
76}
77
79 const GeometryComponent::Type type,
80 const AttrDomain domain)
81{
82 if (!geometry.has(type)) {
83 return false;
84 }
85 const GeometryComponent &component = *geometry.get_component(type);
86 return component.attribute_domain_size(domain) != 0;
87}
88
90 const AttrDomain domain)
91{
92 /* Choose the other component based on a consistent order, rather than some more complicated
93 * heuristic. This is the same order visible in the spreadsheet and used in the ray-cast node. */
94 static const Array<GeometryComponent::Type> supported_types = {
95 GeometryComponent::Type::Mesh,
96 GeometryComponent::Type::PointCloud,
97 GeometryComponent::Type::Curve,
98 GeometryComponent::Type::Instance,
99 GeometryComponent::Type::GreasePencil};
100 for (const GeometryComponent::Type src_type : supported_types) {
101 if (component_is_available(geometry, src_type, domain)) {
102 return geometry.get_component(src_type);
103 }
104 }
105
106 return nullptr;
107}
108
109template<typename T>
111 const VArray<int> &indices,
112 const IndexMask &mask,
113 MutableSpan<T> dst)
114{
115 const int last_index = src.index_range().last();
116 devirtualize_varray2(src, indices, [&](const auto src, const auto indices) {
117 mask.foreach_index(GrainSize(4096), [&](const int i) {
118 const int index = indices[i];
119 dst[i] = src[std::clamp(index, 0, last_index)];
120 });
121 });
122}
123
129class SampleIndexFunction : public mf::MultiFunction {
130 GeometrySet src_geometry_;
131 GField src_field_;
132 AttrDomain domain_;
133 bool clamp_;
134
135 mf::Signature signature_;
136
137 std::optional<bke::GeometryFieldContext> geometry_context_;
138 std::unique_ptr<FieldEvaluator> evaluator_;
139 const GVArray *src_data_ = nullptr;
140
141 public:
143 GField src_field,
144 const AttrDomain domain,
145 const bool clamp)
146 : src_geometry_(std::move(geometry)),
147 src_field_(std::move(src_field)),
148 domain_(domain),
149 clamp_(clamp)
150 {
151 src_geometry_.ensure_owns_direct_data();
152
153 mf::SignatureBuilder builder{"Sample Index", signature_};
154 builder.single_input<int>("Index");
155 builder.single_output("Value", src_field_.cpp_type());
156 this->set_signature(&signature_);
157
158 this->evaluate_field();
159 }
160
162 {
163 const GeometryComponent *component = find_source_component(src_geometry_, domain_);
164 if (component == nullptr) {
165 return;
166 }
167 const int domain_num = component->attribute_domain_size(domain_);
168 geometry_context_.emplace(bke::GeometryFieldContext(*component, domain_));
169 evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_num);
170 evaluator_->add(src_field_);
171 evaluator_->evaluate();
172 src_data_ = &evaluator_->get_evaluated(0);
173 }
174
175 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
176 {
177 const VArray<int> &indices = params.readonly_single_input<int>(0, "Index");
178 GMutableSpan dst = params.uninitialized_single_output(1, "Value");
179
180 const CPPType &type = dst.type();
181 if (src_data_ == nullptr) {
182 type.value_initialize_indices(dst.data(), mask);
183 return;
184 }
185
186 if (clamp_) {
187 bke::attribute_math::convert_to_static_type(type, [&](auto dummy) {
188 using T = decltype(dummy);
189 copy_with_clamped_indices(src_data_->typed<T>(), indices, mask, dst.typed<T>());
190 });
191 }
192 else {
194 }
195 }
196};
197
199{
200 GeometrySet geometry = params.extract_input<GeometrySet>("Geometry");
201 const NodeGeometrySampleIndex &storage = node_storage(params.node());
202 const AttrDomain domain = AttrDomain(storage.domain);
203 const bool use_clamp = bool(storage.clamp);
204
205 GField value_field = params.extract_input<GField>("Value");
206 SocketValueVariant index_value_variant = params.extract_input<SocketValueVariant>("Index");
207 const CPPType &cpp_type = value_field.cpp_type();
208
209 if (index_value_variant.is_single()) {
210 const GeometryComponent *component = find_source_component(geometry, domain);
211 if (!component) {
212 params.set_default_remaining_outputs();
213 return;
214 }
215 /* Optimization for the case when the index is a single value. Here only that one index has to
216 * be evaluated. */
217 const int domain_size = component->attribute_domain_size(domain);
218 int index = index_value_variant.extract<int>();
219 if (use_clamp) {
220 index = std::clamp(index, 0, domain_size - 1);
221 }
222 if (index >= 0 && index < domain_size) {
223 const IndexMask mask = IndexRange(index, 1);
224 const bke::GeometryFieldContext geometry_context(*component, domain);
225 FieldEvaluator evaluator(geometry_context, &mask);
226 evaluator.add(value_field);
227 evaluator.evaluate();
228 const GVArray &data = evaluator.get_evaluated(0);
229 BUFFER_FOR_CPP_TYPE_VALUE(cpp_type, buffer);
230 data.get_to_uninitialized(index, buffer);
231 params.set_output("Value", fn::make_constant_field(cpp_type, buffer));
232 cpp_type.destruct(buffer);
233 }
234 else {
235 params.set_output("Value", fn::make_constant_field(cpp_type, cpp_type.default_value()));
236 }
237 return;
238 }
239
240 bke::SocketValueVariant output_value;
241 std::string error_message;
243 std::make_shared<SampleIndexFunction>(
244 std::move(geometry), std::move(value_field), domain, use_clamp),
245 {&index_value_variant},
246 {&output_value},
247 params.user_data(),
248 error_message))
249 {
250 params.set_default_remaining_outputs();
251 params.error_message_add(NodeWarningType::Error, std::move(error_message));
252 return;
253 }
254
255 params.set_output("Value", std::move(output_value));
256}
257
258static void node_register()
259{
260 static blender::bke::bNodeType ntype;
261
262 geo_node_type_base(&ntype, "GeometryNodeSampleIndex", GEO_NODE_SAMPLE_INDEX);
263 ntype.ui_name = "Sample Index";
264 ntype.ui_description = "Retrieve values from specific geometry elements";
265 ntype.enum_name_legacy = "SAMPLE_INDEX";
267 ntype.initfunc = node_init;
268 ntype.declare = node_declare;
270 ntype, "NodeGeometrySampleIndex", node_free_standard_storage, node_copy_standard_storage);
275}
277
278} // namespace blender::nodes::node_geo_sample_index_cc
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1240
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:461
#define GEO_NODE_SAMPLE_INDEX
#define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name)
#define IFACE_(msgid)
@ CD_PROP_FLOAT
@ CD_PROP_STRING
eNodeSocketDatatype
#define NOD_REGISTER_NODE(REGISTER_FUNC)
#define UI_ITEM_NONE
BMesh const char void * data
void value_initialize_indices(void *ptr, const IndexMask &mask) const
constexpr int64_t last(const int64_t n=0) const
IndexRange index_range() const
int attribute_domain_size(AttrDomain domain) const
int add(GField field, GVArray *varray_ptr)
Definition field.cc:751
const GVArray & get_evaluated(const int field_index) const
Definition FN_field.hh:448
const CPPType & cpp_type() const
Definition FN_field.hh:130
Vector< SocketDeclaration * > inputs
SampleIndexFunction(GeometrySet geometry, GField src_field, const AttrDomain domain, const bool clamp)
void call(const IndexMask &mask, mf::Params params, mf::Context) const override
static ushort indices[]
constexpr T clamp(T, U, U) RET
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
#define T
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
void copy_with_checked_indices(const GVArray &src, const VArray< int > &indices, const IndexMask &mask, GMutableSpan dst)
void node_register_type(bNodeType &ntype)
Definition node.cc:2416
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))
Definition node.cc:5414
std::optional< eCustomDataType > socket_type_to_custom_data_type(eNodeSocketDatatype type)
Definition node.cc:5144
GField make_constant_field(const CPPType &type, const void *value)
Definition field.cc:528
static void node_layout(uiLayout *layout, bContext *, PointerRNA *ptr)
static const GeometryComponent * find_source_component(const GeometrySet &geometry, const AttrDomain domain)
static void node_geo_exec(GeoNodeExecParams params)
static void node_declare(NodeDeclarationBuilder &b)
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
void copy_with_clamped_indices(const VArray< T > &src, const VArray< int > &indices, const IndexMask &mask, MutableSpan< T > dst)
static bool component_is_available(const GeometrySet &geometry, const GeometryComponent::Type type, const AttrDomain domain)
static void node_init(bNodeTree *, bNode *node)
void search_link_ops_for_declarations(GatherLinkSearchOpParams &params, 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_varray2(const VArray< T1 > &varray1, const VArray< T2 > &varray2, const Func &func, bool enable=true)
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)
Definition node_util.cc:42
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:54
void * storage
Defines a node type.
Definition BKE_node.hh:238
std::string ui_description
Definition BKE_node.hh:244
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:289
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:354
const char * enum_name_legacy
Definition BKE_node.hh:247
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:259
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:378
NodeDeclareFunction declare
Definition BKE_node.hh:362
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)
i
Definition text_draw.cc:230
PointerRNA * ptr
Definition wm_files.cc:4238