Blender V4.5
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
9#include "UI_interface.hh"
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 if (node != nullptr) {
30 const eCustomDataType data_type = eCustomDataType(node_storage(*node).data_type);
31 b.add_input(data_type, "Value").hide_value().field_on_all();
32 }
33 b.add_input<decl::Int>("Index").supports_field().description(
34 "Which element to retrieve a value from on the geometry");
35
36 if (node != nullptr) {
37 const eCustomDataType data_type = eCustomDataType(node_storage(*node).data_type);
38 b.add_output(data_type, "Value").dependent_field({2});
39 }
40}
41
42static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
43{
44 layout->prop(ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
45 layout->prop(ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
46 layout->prop(ptr, "clamp", UI_ITEM_NONE, std::nullopt, ICON_NONE);
47}
48
49static void node_init(bNodeTree * /*tree*/, bNode *node)
50{
52 data->data_type = CD_PROP_FLOAT;
53 data->domain = int8_t(AttrDomain::Point);
54 data->clamp = 0;
55 node->storage = data;
56}
57
59{
60 const NodeDeclaration &declaration = *params.node_type().static_declaration;
62
63 const std::optional<eCustomDataType> type = bke::socket_type_to_custom_data_type(
64 eNodeSocketDatatype(params.other_socket().type));
65 if (type && *type != CD_PROP_STRING) {
66 /* The input and output sockets have the same name. */
67 params.add_item(IFACE_("Value"), [type](LinkSearchOpParams &params) {
68 bNode &node = params.add_node("GeometryNodeSampleIndex");
69 node_storage(node).data_type = *type;
70 params.update_and_connect_available_socket(node, "Value");
71 });
72 }
73}
74
76 const GeometryComponent::Type type,
77 const AttrDomain domain)
78{
79 if (!geometry.has(type)) {
80 return false;
81 }
82 const GeometryComponent &component = *geometry.get_component(type);
83 return component.attribute_domain_size(domain) != 0;
84}
85
87 const AttrDomain domain)
88{
89 /* Choose the other component based on a consistent order, rather than some more complicated
90 * heuristic. This is the same order visible in the spreadsheet and used in the ray-cast node. */
91 static const Array<GeometryComponent::Type> supported_types = {
92 GeometryComponent::Type::Mesh,
93 GeometryComponent::Type::PointCloud,
94 GeometryComponent::Type::Curve,
95 GeometryComponent::Type::Instance,
96 GeometryComponent::Type::GreasePencil};
97 for (const GeometryComponent::Type src_type : supported_types) {
98 if (component_is_available(geometry, src_type, domain)) {
99 return geometry.get_component(src_type);
100 }
101 }
102
103 return nullptr;
104}
105
106template<typename T>
108 const VArray<int> &indices,
109 const IndexMask &mask,
110 MutableSpan<T> dst)
111{
112 const int last_index = src.index_range().last();
113 devirtualize_varray2(src, indices, [&](const auto src, const auto indices) {
114 mask.foreach_index(GrainSize(4096), [&](const int i) {
115 const int index = indices[i];
116 dst[i] = src[std::clamp(index, 0, last_index)];
117 });
118 });
119}
120
126class SampleIndexFunction : public mf::MultiFunction {
127 GeometrySet src_geometry_;
128 GField src_field_;
129 AttrDomain domain_;
130 bool clamp_;
131
132 mf::Signature signature_;
133
134 std::optional<bke::GeometryFieldContext> geometry_context_;
135 std::unique_ptr<FieldEvaluator> evaluator_;
136 const GVArray *src_data_ = nullptr;
137
138 public:
140 GField src_field,
141 const AttrDomain domain,
142 const bool clamp)
143 : src_geometry_(std::move(geometry)),
144 src_field_(std::move(src_field)),
145 domain_(domain),
146 clamp_(clamp)
147 {
148 src_geometry_.ensure_owns_direct_data();
149
150 mf::SignatureBuilder builder{"Sample Index", signature_};
151 builder.single_input<int>("Index");
152 builder.single_output("Value", src_field_.cpp_type());
153 this->set_signature(&signature_);
154
155 this->evaluate_field();
156 }
157
159 {
160 const GeometryComponent *component = find_source_component(src_geometry_, domain_);
161 if (component == nullptr) {
162 return;
163 }
164 const int domain_num = component->attribute_domain_size(domain_);
165 geometry_context_.emplace(bke::GeometryFieldContext(*component, domain_));
166 evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_num);
167 evaluator_->add(src_field_);
168 evaluator_->evaluate();
169 src_data_ = &evaluator_->get_evaluated(0);
170 }
171
172 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
173 {
174 const VArray<int> &indices = params.readonly_single_input<int>(0, "Index");
175 GMutableSpan dst = params.uninitialized_single_output(1, "Value");
176
177 const CPPType &type = dst.type();
178 if (src_data_ == nullptr) {
179 type.value_initialize_indices(dst.data(), mask);
180 return;
181 }
182
183 if (clamp_) {
184 bke::attribute_math::convert_to_static_type(type, [&](auto dummy) {
185 using T = decltype(dummy);
186 copy_with_clamped_indices(src_data_->typed<T>(), indices, mask, dst.typed<T>());
187 });
188 }
189 else {
191 }
192 }
193};
194
196{
197 GeometrySet geometry = params.extract_input<GeometrySet>("Geometry");
198 const NodeGeometrySampleIndex &storage = node_storage(params.node());
199 const AttrDomain domain = AttrDomain(storage.domain);
200 const bool use_clamp = bool(storage.clamp);
201
202 GField value_field = params.extract_input<GField>("Value");
203 SocketValueVariant index_value_variant = params.extract_input<SocketValueVariant>("Index");
204 const CPPType &cpp_type = value_field.cpp_type();
205
206 if (index_value_variant.is_context_dependent_field()) {
207 /* If the index is a field, the output has to be a field that still depends on the input. */
208 auto fn = std::make_shared<SampleIndexFunction>(
209 std::move(geometry), std::move(value_field), domain, use_clamp);
210 auto op = FieldOperation::Create(std::move(fn), {index_value_variant.extract<Field<int>>()});
211 params.set_output("Value", GField(std::move(op)));
212 }
213 else if (const GeometryComponent *component = find_source_component(geometry, domain)) {
214 /* Optimization for the case when the index is a single value. Here only that one index has to
215 * be evaluated. */
216 const int domain_size = component->attribute_domain_size(domain);
217 int index = index_value_variant.extract<int>();
218 if (use_clamp) {
219 index = std::clamp(index, 0, domain_size - 1);
220 }
221 if (index >= 0 && index < domain_size) {
222 const IndexMask mask = IndexRange(index, 1);
223 const bke::GeometryFieldContext geometry_context(*component, domain);
224 FieldEvaluator evaluator(geometry_context, &mask);
225 evaluator.add(value_field);
226 evaluator.evaluate();
227 const GVArray &data = evaluator.get_evaluated(0);
228 BUFFER_FOR_CPP_TYPE_VALUE(cpp_type, buffer);
229 data.get_to_uninitialized(index, buffer);
230 params.set_output("Value", fn::make_constant_field(cpp_type, buffer));
231 cpp_type.destruct(buffer);
232 }
233 else {
234 params.set_output("Value", fn::make_constant_field(cpp_type, cpp_type.default_value()));
235 }
236 }
237 else {
238 /* Output default value if there is no geometry. */
239 params.set_output("Value", fn::make_constant_field(cpp_type, cpp_type.default_value()));
240 }
241}
242
243static void node_register()
244{
245 static blender::bke::bNodeType ntype;
246
247 geo_node_type_base(&ntype, "GeometryNodeSampleIndex", GEO_NODE_SAMPLE_INDEX);
248 ntype.ui_name = "Sample Index";
249 ntype.ui_description = "Retrieve values from specific geometry elements";
250 ntype.enum_name_legacy = "SAMPLE_INDEX";
252 ntype.initfunc = node_init;
253 ntype.declare = node_declare;
255 ntype, "NodeGeometrySampleIndex", node_free_standard_storage, node_copy_standard_storage);
260}
262
263} // namespace blender::nodes::node_geo_sample_index_cc
#define NODE_STORAGE_FUNCS(StorageT)
Definition BKE_node.hh:1215
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:447
#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
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:242
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[]
#define this
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:2748
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:5603
std::optional< eCustomDataType > socket_type_to_custom_data_type(eNodeSocketDatatype type)
Definition node.cc:5355
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)
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:226
std::string ui_description
Definition BKE_node.hh:232
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:277
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:347
const char * enum_name_legacy
Definition BKE_node.hh:235
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:247
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:371
NodeDeclareFunction declare
Definition BKE_node.hh:355
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:4226