Blender V4.3
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
16namespace blender::nodes {
17
18} // namespace blender::nodes
19
21
23
25{
26 const bNode *node = b.node_or_null();
27 b.add_input<decl::Geometry>("Geometry")
28 .supported_type({GeometryComponent::Type::Mesh,
33 if (node != nullptr) {
34 const eCustomDataType data_type = eCustomDataType(node_storage(*node).data_type);
35 b.add_input(data_type, "Value").hide_value().field_on_all();
36 }
37 b.add_input<decl::Int>("Index").supports_field().description(
38 "Which element to retrieve a value from on the geometry");
39
40 if (node != nullptr) {
41 const eCustomDataType data_type = eCustomDataType(node_storage(*node).data_type);
42 b.add_output(data_type, "Value").dependent_field({2});
43 }
44}
45
46static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
47{
48 uiItemR(layout, ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
49 uiItemR(layout, ptr, "domain", UI_ITEM_NONE, "", ICON_NONE);
50 uiItemR(layout, ptr, "clamp", UI_ITEM_NONE, nullptr, ICON_NONE);
51}
52
53static void node_init(bNodeTree * /*tree*/, bNode *node)
54{
55 NodeGeometrySampleIndex *data = MEM_cnew<NodeGeometrySampleIndex>(__func__);
56 data->data_type = CD_PROP_FLOAT;
57 data->domain = int8_t(AttrDomain::Point);
58 data->clamp = 0;
59 node->storage = data;
60}
61
63{
64 const NodeDeclaration &declaration = *params.node_type().static_declaration;
66
67 const std::optional<eCustomDataType> type = bke::socket_type_to_custom_data_type(
68 eNodeSocketDatatype(params.other_socket().type));
69 if (type && *type != CD_PROP_STRING) {
70 /* The input and output sockets have the same name. */
71 params.add_item(IFACE_("Value"), [type](LinkSearchOpParams &params) {
72 bNode &node = params.add_node("GeometryNodeSampleIndex");
73 node_storage(node).data_type = *type;
74 params.update_and_connect_available_socket(node, "Value");
75 });
76 }
77}
78
79static bool component_is_available(const GeometrySet &geometry,
80 const GeometryComponent::Type type,
81 const AttrDomain domain)
82{
83 if (!geometry.has(type)) {
84 return false;
85 }
86 const GeometryComponent &component = *geometry.get_component(type);
87 return component.attribute_domain_size(domain) != 0;
88}
89
91 const AttrDomain domain)
92{
93 /* Choose the other component based on a consistent order, rather than some more complicated
94 * heuristic. This is the same order visible in the spreadsheet and used in the ray-cast node. */
95 static const Array<GeometryComponent::Type> supported_types = {
101 for (const GeometryComponent::Type src_type : supported_types) {
102 if (component_is_available(geometry, src_type, domain)) {
103 return geometry.get_component(src_type);
104 }
105 }
106
107 return nullptr;
108}
109
110template<typename T>
112 const VArray<int> &indices,
113 const IndexMask &mask,
114 MutableSpan<T> dst)
115{
116 const int last_index = src.index_range().last();
117 devirtualize_varray2(src, indices, [&](const auto src, const auto indices) {
118 mask.foreach_index(GrainSize(4096), [&](const int i) {
119 const int index = indices[i];
120 dst[i] = src[std::clamp(index, 0, last_index)];
121 });
122 });
123}
124
131 GeometrySet src_geometry_;
132 GField src_field_;
133 AttrDomain domain_;
134 bool clamp_;
135
136 mf::Signature signature_;
137
138 std::optional<bke::GeometryFieldContext> geometry_context_;
139 std::unique_ptr<FieldEvaluator> evaluator_;
140 const GVArray *src_data_ = nullptr;
141
142 public:
144 GField src_field,
145 const AttrDomain domain,
146 const bool clamp)
147 : src_geometry_(std::move(geometry)),
148 src_field_(std::move(src_field)),
149 domain_(domain),
150 clamp_(clamp)
151 {
152 src_geometry_.ensure_owns_direct_data();
153
154 mf::SignatureBuilder builder{"Sample Index", signature_};
155 builder.single_input<int>("Index");
156 builder.single_output("Value", src_field_.cpp_type());
157 this->set_signature(&signature_);
158
159 this->evaluate_field();
160 }
161
163 {
164 const GeometryComponent *component = find_source_component(src_geometry_, domain_);
165 if (component == nullptr) {
166 return;
167 }
168 const int domain_num = component->attribute_domain_size(domain_);
169 geometry_context_.emplace(bke::GeometryFieldContext(*component, domain_));
170 evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_num);
171 evaluator_->add(src_field_);
172 evaluator_->evaluate();
173 src_data_ = &evaluator_->get_evaluated(0);
174 }
175
176 void call(const IndexMask &mask, mf::Params params, mf::Context /*context*/) const override
177 {
178 const VArray<int> &indices = params.readonly_single_input<int>(0, "Index");
179 GMutableSpan dst = params.uninitialized_single_output(1, "Value");
180
181 const CPPType &type = dst.type();
182 if (src_data_ == nullptr) {
183 type.value_initialize_indices(dst.data(), mask);
184 return;
185 }
186
187 if (clamp_) {
188 bke::attribute_math::convert_to_static_type(type, [&](auto dummy) {
189 using T = decltype(dummy);
190 copy_with_clamped_indices(src_data_->typed<T>(), indices, mask, dst.typed<T>());
191 });
192 }
193 else {
194 bke::copy_with_checked_indices(*src_data_, indices, mask, dst);
195 }
196 }
197};
198
200{
201 GeometrySet geometry = params.extract_input<GeometrySet>("Geometry");
202 const NodeGeometrySampleIndex &storage = node_storage(params.node());
203 const AttrDomain domain = AttrDomain(storage.domain);
204 const bool use_clamp = bool(storage.clamp);
205
206 GField value_field = params.extract_input<GField>("Value");
207 SocketValueVariant index_value_variant = params.extract_input<SocketValueVariant>("Index");
208 const CPPType &cpp_type = value_field.cpp_type();
209
210 if (index_value_variant.is_context_dependent_field()) {
211 /* If the index is a field, the output has to be a field that still depends on the input. */
212 auto fn = std::make_shared<SampleIndexFunction>(
213 std::move(geometry), std::move(value_field), domain, use_clamp);
214 auto op = FieldOperation::Create(std::move(fn), {index_value_variant.extract<Field<int>>()});
215 params.set_output("Value", GField(std::move(op)));
216 }
217 else if (const GeometryComponent *component = find_source_component(geometry, domain)) {
218 /* Optimization for the case when the index is a single value. Here only that one index has to
219 * be evaluated. */
220 const int domain_size = component->attribute_domain_size(domain);
221 int index = index_value_variant.extract<int>();
222 if (use_clamp) {
223 index = std::clamp(index, 0, domain_size - 1);
224 }
225 if (index >= 0 && index < domain_size) {
226 const IndexMask mask = IndexRange(index, 1);
227 const bke::GeometryFieldContext geometry_context(*component, domain);
228 FieldEvaluator evaluator(geometry_context, &mask);
229 evaluator.add(value_field);
230 evaluator.evaluate();
231 const GVArray &data = evaluator.get_evaluated(0);
232 BUFFER_FOR_CPP_TYPE_VALUE(cpp_type, buffer);
233 data.get_to_uninitialized(index, buffer);
234 params.set_output("Value", fn::make_constant_field(cpp_type, buffer));
235 cpp_type.destruct(buffer);
236 }
237 else {
238 params.set_output("Value", fn::make_constant_field(cpp_type, cpp_type.default_value()));
239 }
240 }
241 else {
242 /* Output default value if there is no geometry. */
243 params.set_output("Value", fn::make_constant_field(cpp_type, cpp_type.default_value()));
244 }
245}
246
247static void node_register()
248{
249 static blender::bke::bNodeType ntype;
250
251 geo_node_type_base(&ntype, GEO_NODE_SAMPLE_INDEX, "Sample Index", NODE_CLASS_GEOMETRY);
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:1799
#define NODE_CLASS_GEOMETRY
Definition BKE_node.hh:418
#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
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
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:756
const GVArray & get_evaluated(const int field_index) const
Definition FN_field.hh:450
static std::shared_ptr< FieldOperation > Create(std::shared_ptr< const mf::MultiFunction > function, Vector< GField > inputs={})
Definition FN_field.hh:244
const CPPType & cpp_type() const
Definition FN_field.hh:132
void set_signature(const Signature *signature)
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
local_group_size(16, 16) .push_constant(Type b
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_device_inline float4 mask(const int4 mask, const float4 a)
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_type_storage(bNodeType *ntype, const char *storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
Definition node.cc:4632
void node_register_type(bNodeType *ntype)
Definition node.cc:1708
std::optional< eCustomDataType > socket_type_to_custom_data_type(eNodeSocketDatatype type)
Definition node.cc:4379
GField make_constant_field(const CPPType &type, const void *value)
Definition field.cc:533
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, int type, const char *name, short nclass)
void node_free_standard_storage(bNode *node)
Definition node_util.cc:46
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
Definition node_util.cc:58
signed char int8_t
Definition stdint.h:75
Defines a node type.
Definition BKE_node.hh:218
void(* initfunc)(bNodeTree *ntree, bNode *node)
Definition BKE_node.hh:267
NodeGeometryExecFunction geometry_node_execute
Definition BKE_node.hh:339
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
Definition BKE_node.hh:238
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition BKE_node.hh:363
NodeDeclareFunction declare
Definition BKE_node.hh:347
PointerRNA * ptr
Definition wm_files.cc:4126