Blender  V3.3
node_geo_accumulate_field.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include "BKE_attribute_math.hh"
4 
6 
7 #include "node_geometry_util.hh"
8 
9 #include "UI_interface.h"
10 #include "UI_resources.h"
11 
13 
15 
17 {
18  std::string value_in_description = "The values to be accumulated";
19  std::string leading_out_description =
20  "The running total of values in the corresponding group, starting at the first value";
21  std::string trailing_out_description =
22  "The running total of values in the corresponding group, starting at zero";
23  std::string total_out_description = "The total of all of the values in the corresponding group";
24 
25  b.add_input<decl::Vector>(N_("Value"), "Value Vector")
26  .default_value({1.0f, 1.0f, 1.0f})
27  .supports_field()
28  .description(N_(value_in_description));
29  b.add_input<decl::Float>(N_("Value"), "Value Float")
30  .default_value(1.0f)
31  .supports_field()
32  .description(N_(value_in_description));
33  b.add_input<decl::Int>(N_("Value"), "Value Int")
34  .default_value(1)
35  .supports_field()
36  .description(N_(value_in_description));
37  b.add_input<decl::Int>(N_("Group Index"))
38  .supports_field()
39  .description(
40  N_("An index used to group values together for multiple separate accumulations"));
41 
42  b.add_output<decl::Vector>(N_("Leading"), "Leading Vector")
43  .field_source()
44  .description(N_(leading_out_description));
45  b.add_output<decl::Float>(N_("Leading"), "Leading Float")
46  .field_source()
47  .description(N_(leading_out_description));
48  b.add_output<decl::Int>(N_("Leading"), "Leading Int")
49  .field_source()
50  .description(N_(leading_out_description));
51 
52  b.add_output<decl::Vector>(N_("Trailing"), "Trailing Vector")
53  .field_source()
54  .description(N_(trailing_out_description));
55  b.add_output<decl::Float>(N_("Trailing"), "Trailing Float")
56  .field_source()
57  .description(N_(trailing_out_description));
58  b.add_output<decl::Int>(N_("Trailing"), "Trailing Int")
59  .field_source()
60  .description(N_(trailing_out_description));
61 
62  b.add_output<decl::Vector>(N_("Total"), "Total Vector")
63  .field_source()
64  .description(N_(total_out_description));
65  b.add_output<decl::Float>(N_("Total"), "Total Float")
66  .field_source()
67  .description(N_(total_out_description));
68  b.add_output<decl::Int>(N_("Total"), "Total Int")
69  .field_source()
70  .description(N_(total_out_description));
71 }
72 
73 static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
74 {
75  uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
76  uiItemR(layout, ptr, "domain", 0, "", ICON_NONE);
77 }
78 
80 {
81  NodeAccumulateField *data = MEM_cnew<NodeAccumulateField>(__func__);
82  data->data_type = CD_PROP_FLOAT;
83  data->domain = ATTR_DOMAIN_POINT;
84  node->storage = data;
85 }
86 
88 {
89  const NodeAccumulateField &storage = node_storage(*node);
90  const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type);
91 
92  bNodeSocket *sock_in_vector = (bNodeSocket *)node->inputs.first;
93  bNodeSocket *sock_in_float = sock_in_vector->next;
94  bNodeSocket *sock_in_int = sock_in_float->next;
95 
96  bNodeSocket *sock_out_vector = (bNodeSocket *)node->outputs.first;
97  bNodeSocket *sock_out_float = sock_out_vector->next;
98  bNodeSocket *sock_out_int = sock_out_float->next;
99 
100  bNodeSocket *sock_out_first_vector = sock_out_int->next;
101  bNodeSocket *sock_out_first_float = sock_out_first_vector->next;
102  bNodeSocket *sock_out_first_int = sock_out_first_float->next;
103  bNodeSocket *sock_out_total_vector = sock_out_first_int->next;
104  bNodeSocket *sock_out_total_float = sock_out_total_vector->next;
105  bNodeSocket *sock_out_total_int = sock_out_total_float->next;
106 
107  nodeSetSocketAvailability(ntree, sock_in_vector, data_type == CD_PROP_FLOAT3);
108  nodeSetSocketAvailability(ntree, sock_in_float, data_type == CD_PROP_FLOAT);
109  nodeSetSocketAvailability(ntree, sock_in_int, data_type == CD_PROP_INT32);
110 
111  nodeSetSocketAvailability(ntree, sock_out_vector, data_type == CD_PROP_FLOAT3);
112  nodeSetSocketAvailability(ntree, sock_out_float, data_type == CD_PROP_FLOAT);
113  nodeSetSocketAvailability(ntree, sock_out_int, data_type == CD_PROP_INT32);
114 
115  nodeSetSocketAvailability(ntree, sock_out_first_vector, data_type == CD_PROP_FLOAT3);
116  nodeSetSocketAvailability(ntree, sock_out_first_float, data_type == CD_PROP_FLOAT);
117  nodeSetSocketAvailability(ntree, sock_out_first_int, data_type == CD_PROP_INT32);
118 
119  nodeSetSocketAvailability(ntree, sock_out_total_vector, data_type == CD_PROP_FLOAT3);
120  nodeSetSocketAvailability(ntree, sock_out_total_float, data_type == CD_PROP_FLOAT);
121  nodeSetSocketAvailability(ntree, sock_out_total_int, data_type == CD_PROP_INT32);
122 }
123 
124 enum class AccumulationMode { Leading = 0, Trailing = 1 };
125 
126 static std::optional<eCustomDataType> node_type_from_other_socket(const bNodeSocket &socket)
127 {
128  switch (socket.type) {
129  case SOCK_FLOAT:
130  return CD_PROP_FLOAT;
131  case SOCK_BOOLEAN:
132  case SOCK_INT:
133  return CD_PROP_INT32;
134  case SOCK_VECTOR:
135  case SOCK_RGBA:
136  return CD_PROP_FLOAT3;
137  default:
138  return {};
139  }
140 }
141 
143 {
144  const std::optional<eCustomDataType> type = node_type_from_other_socket(params.other_socket());
145  if (!type) {
146  return;
147  }
148  if (params.in_out() == SOCK_OUT) {
149  params.add_item(
150  IFACE_("Leading"),
152  bNode &node = params.add_node("GeometryNodeAccumulateField");
153  node_storage(node).data_type = *type;
154  params.update_and_connect_available_socket(node, "Leading");
155  },
156  0);
157  params.add_item(
158  IFACE_("Trailing"),
160  bNode &node = params.add_node("GeometryNodeAccumulateField");
161  node_storage(node).data_type = *type;
162  params.update_and_connect_available_socket(node, "Trailing");
163  },
164  -1);
165  params.add_item(
166  IFACE_("Total"),
168  bNode &node = params.add_node("GeometryNodeAccumulateField");
169  node_storage(node).data_type = *type;
170  params.update_and_connect_available_socket(node, "Total");
171  },
172  -2);
173  }
174  else {
175  params.add_item(
176  IFACE_("Value"),
178  bNode &node = params.add_node("GeometryNodeAccumulateField");
179  node_storage(node).data_type = *type;
180  params.update_and_connect_available_socket(node, "Value");
181  },
182  0);
183 
184  params.add_item(
185  IFACE_("Group Index"),
187  bNode &node = params.add_node("GeometryNodeAccumulateField");
188  node_storage(node).data_type = *type;
189  params.update_and_connect_available_socket(node, "Group Index");
190  },
191  -1);
192  }
193 }
194 
195 template<typename T> class AccumulateFieldInput final : public GeometryFieldInput {
196  private:
197  Field<T> input_;
198  Field<int> group_index_;
199  eAttrDomain source_domain_;
200  AccumulationMode accumulation_mode_;
201 
202  public:
203  AccumulateFieldInput(const eAttrDomain source_domain,
204  Field<T> input,
205  Field<int> group_index,
206  AccumulationMode accumulation_mode)
207  : GeometryFieldInput(CPPType::get<T>(), "Accumulation"),
208  input_(input),
209  group_index_(group_index),
210  source_domain_(source_domain),
211  accumulation_mode_(accumulation_mode)
212  {
213  }
214 
216  const eAttrDomain domain,
217  IndexMask UNUSED(mask)) const final
218  {
219  const GeometryComponentFieldContext field_context{component, source_domain_};
220  const int domain_size = component.attribute_domain_size(field_context.domain());
221  if (domain_size == 0) {
222  return {};
223  }
224  const AttributeAccessor attributes = *component.attributes();
225 
226  fn::FieldEvaluator evaluator{field_context, domain_size};
227  evaluator.add(input_);
228  evaluator.add(group_index_);
229  evaluator.evaluate();
230  const VArray<T> values = evaluator.get_evaluated<T>(0);
231  const VArray<int> group_indices = evaluator.get_evaluated<int>(1);
232 
233  Array<T> accumulations_out(domain_size);
234 
235  if (group_indices.is_single()) {
236  T accumulation = T();
237  if (accumulation_mode_ == AccumulationMode::Leading) {
238  for (const int i : values.index_range()) {
239  accumulation = values[i] + accumulation;
240  accumulations_out[i] = accumulation;
241  }
242  }
243  else {
244  for (const int i : values.index_range()) {
245  accumulations_out[i] = accumulation;
246  accumulation = values[i] + accumulation;
247  }
248  }
249  }
250  else {
251  Map<int, T> accumulations;
252  if (accumulation_mode_ == AccumulationMode::Leading) {
253  for (const int i : values.index_range()) {
254  T &accumulation_value = accumulations.lookup_or_add_default(group_indices[i]);
255  accumulation_value += values[i];
256  accumulations_out[i] = accumulation_value;
257  }
258  }
259  else {
260  for (const int i : values.index_range()) {
261  T &accumulation_value = accumulations.lookup_or_add_default(group_indices[i]);
262  accumulations_out[i] = accumulation_value;
263  accumulation_value += values[i];
264  }
265  }
266  }
267 
268  return attributes.adapt_domain<T>(
269  VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain);
270  }
271 
272  uint64_t hash() const override
273  {
274  return get_default_hash_4(input_, group_index_, source_domain_, accumulation_mode_);
275  }
276 
277  bool is_equal_to(const fn::FieldNode &other) const override
278  {
279  if (const AccumulateFieldInput *other_accumulate = dynamic_cast<const AccumulateFieldInput *>(
280  &other)) {
281  return input_ == other_accumulate->input_ &&
282  group_index_ == other_accumulate->group_index_ &&
283  source_domain_ == other_accumulate->source_domain_ &&
284  accumulation_mode_ == other_accumulate->accumulation_mode_;
285  }
286  return false;
287  }
288 };
289 
290 template<typename T> class TotalFieldInput final : public GeometryFieldInput {
291  private:
292  Field<T> input_;
293  Field<int> group_index_;
294  eAttrDomain source_domain_;
295 
296  public:
297  TotalFieldInput(const eAttrDomain source_domain, Field<T> input, Field<int> group_index)
298  : GeometryFieldInput(CPPType::get<T>(), "Total Value"),
299  input_(input),
300  group_index_(group_index),
301  source_domain_(source_domain)
302  {
303  }
304 
306  const eAttrDomain domain,
307  IndexMask UNUSED(mask)) const final
308  {
309  const GeometryComponentFieldContext field_context{component, source_domain_};
310  const int domain_size = component.attribute_domain_size(field_context.domain());
311  if (domain_size == 0) {
312  return {};
313  }
314  const AttributeAccessor attributes = *component.attributes();
315 
316  fn::FieldEvaluator evaluator{field_context, domain_size};
317  evaluator.add(input_);
318  evaluator.add(group_index_);
319  evaluator.evaluate();
320  const VArray<T> values = evaluator.get_evaluated<T>(0);
321  const VArray<int> group_indices = evaluator.get_evaluated<int>(1);
322 
323  if (group_indices.is_single()) {
324  T accumulation = T();
325  for (const int i : values.index_range()) {
326  accumulation = values[i] + accumulation;
327  }
328  return VArray<T>::ForSingle(accumulation, domain_size);
329  }
330 
331  Array<T> accumulations_out(domain_size);
332  Map<int, T> accumulations;
333  for (const int i : values.index_range()) {
334  T &value = accumulations.lookup_or_add_default(group_indices[i]);
335  value = value + values[i];
336  }
337  for (const int i : values.index_range()) {
338  accumulations_out[i] = accumulations.lookup(group_indices[i]);
339  }
340 
341  return attributes.adapt_domain<T>(
342  VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain);
343  }
344 
345  uint64_t hash() const override
346  {
347  return get_default_hash_3(input_, group_index_, source_domain_);
348  }
349 
350  bool is_equal_to(const fn::FieldNode &other) const override
351  {
352  if (const TotalFieldInput *other_field = dynamic_cast<const TotalFieldInput *>(&other)) {
353  return input_ == other_field->input_ && group_index_ == other_field->group_index_ &&
354  source_domain_ == other_field->source_domain_;
355  }
356  return false;
357  }
358 };
359 
360 template<typename T> std::string identifier_suffix()
361 {
362  if constexpr (std::is_same_v<T, int>) {
363  return "Int";
364  }
365  if constexpr (std::is_same_v<T, float>) {
366  return "Float";
367  }
368  if constexpr (std::is_same_v<T, float3>) {
369  return "Vector";
370  }
371 }
372 
374 {
375  const NodeAccumulateField &storage = node_storage(params.node());
376  const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type);
377  const eAttrDomain source_domain = static_cast<eAttrDomain>(storage.domain);
378 
379  Field<int> group_index_field = params.extract_input<Field<int>>("Group Index");
380  attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
381  using T = decltype(dummy);
382  if constexpr (std::is_same_v<T, int> || std::is_same_v<T, float> ||
383  std::is_same_v<T, float3>) {
384  const std::string suffix = " " + identifier_suffix<T>();
385  Field<T> input_field = params.extract_input<Field<T>>("Value" + suffix);
386  if (params.output_is_required("Leading" + suffix)) {
387  params.set_output(
388  "Leading" + suffix,
389  Field<T>{std::make_shared<AccumulateFieldInput<T>>(
390  source_domain, input_field, group_index_field, AccumulationMode::Leading)});
391  }
392  if (params.output_is_required("Trailing" + suffix)) {
393  params.set_output(
394  "Trailing" + suffix,
395  Field<T>{std::make_shared<AccumulateFieldInput<T>>(
396  source_domain, input_field, group_index_field, AccumulationMode::Trailing)});
397  }
398  if (params.output_is_required("Total" + suffix)) {
399  params.set_output("Total" + suffix,
400  Field<T>{std::make_shared<TotalFieldInput<T>>(
401  source_domain, input_field, group_index_field)});
402  }
403  }
404  });
405 }
406 } // namespace blender::nodes::node_geo_accumulate_field_cc
407 
409 {
411 
412  static bNodeType ntype;
413 
422  &ntype, "NodeAccumulateField", node_free_standard_storage, node_copy_standard_storage);
423  nodeRegisterType(&ntype);
424 }
eAttrDomain
Definition: BKE_attribute.h:25
@ ATTR_DOMAIN_POINT
Definition: BKE_attribute.h:27
void node_type_update(struct bNodeType *ntype, void(*updatefunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4443
#define NODE_CLASS_CONVERTER
Definition: BKE_node.h:351
#define NODE_STORAGE_FUNCS(StorageT)
Definition: BKE_node.h:1563
void nodeSetSocketAvailability(struct bNodeTree *ntree, struct bNodeSocket *sock, bool is_available)
Definition: node.cc:3664
void node_type_init(struct bNodeType *ntype, void(*initfunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4390
void node_type_storage(struct bNodeType *ntype, const char *storagename, void(*freefunc)(struct bNode *node), void(*copyfunc)(struct bNodeTree *dest_ntree, struct bNode *dest_node, const struct bNode *src_node))
Definition: node.cc:4426
#define GEO_NODE_ACCUMULATE_FIELD
Definition: BKE_node.h:1487
void nodeRegisterType(struct bNodeType *ntype)
Definition: node.cc:1357
#define final(a, b, c)
Definition: BLI_hash.h:21
#define UNUSED(x)
#define IFACE_(msgid)
static uint8 component(Color32 c, uint i)
Definition: ColorBlock.cpp:108
eCustomDataType
@ CD_PROP_FLOAT
@ CD_PROP_FLOAT3
@ CD_PROP_INT32
@ SOCK_OUT
@ SOCK_INT
@ SOCK_VECTOR
@ SOCK_BOOLEAN
@ SOCK_FLOAT
@ SOCK_RGBA
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
const Value & lookup(const Key &key) const
Definition: BLI_map.hh:485
Value & lookup_or_add_default(const Key &key)
Definition: BLI_map.hh:580
IndexRange index_range() const
static VArray ForContainer(ContainerT container)
static VArray ForSingle(T value, const int64_t size)
GVArray adapt_domain(const GVArray &varray, const eAttrDomain from_domain, const eAttrDomain to_domain) const
int add(GField field, GVArray *varray_ptr)
Definition: field.cc:731
AccumulateFieldInput(const eAttrDomain source_domain, Field< T > input, Field< int > group_index, AccumulationMode accumulation_mode)
GVArray get_varray_for_context(const GeometryComponent &component, const eAttrDomain domain, IndexMask UNUSED(mask)) const final
GVArray get_varray_for_context(const GeometryComponent &component, const eAttrDomain domain, IndexMask UNUSED(mask)) const final
TotalFieldInput(const eAttrDomain source_domain, Field< T > input, Field< int > group_index)
bool is_equal_to(const fn::FieldNode &other) const override
OperationNode * node
void * tree
bNodeTree * ntree
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_global KernelShaderEvalInput * input
ccl_device_inline float4 mask(const int4 &mask, const float4 &a)
Definition: math_float4.h:513
#define T
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
static void node_update(bNodeTree *ntree, bNode *node)
static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
static void node_declare(NodeDeclarationBuilder &b)
static std::optional< eCustomDataType > node_type_from_other_socket(const bNodeSocket &socket)
static void node_init(bNodeTree *UNUSED(tree), bNode *node)
static void node_gather_link_searches(GatherLinkSearchOpParams &params)
static void node_geo_exec(GeoNodeExecParams params)
uint64_t get_default_hash_4(const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4)
Definition: BLI_hash.hh:240
uint64_t get_default_hash_3(const T1 &v1, const T2 &v2, const T3 &v3)
Definition: BLI_hash.hh:231
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
static void node_init(const struct bContext *C, bNodeTree *ntree, bNode *node)
Definition: node.cc:1082
void register_node_type_geo_accumulate_field()
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass)
void node_copy_standard_storage(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, const bNode *src_node)
Definition: node_util.c:55
void node_free_standard_storage(bNode *node)
Definition: node_util.c:43
unsigned __int64 uint64_t
Definition: stdint.h:90
struct bNodeSocket * next
Defines a node type.
Definition: BKE_node.h:226
NodeGeometryExecFunction geometry_node_execute
Definition: BKE_node.h:316
NodeGatherSocketLinkOperationsFunction gather_link_search_ops
Definition: BKE_node.h:335
void(* draw_buttons)(struct uiLayout *, struct bContext *C, struct PointerRNA *ptr)
Definition: BKE_node.h:244
NodeDeclareFunction declare
Definition: BKE_node.h:324
#define N_(msgid)
PointerRNA * ptr
Definition: wm_files.c:3480