Blender  V2.93
node_geo_attribute_compare.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
17 #include "UI_interface.h"
18 #include "UI_resources.h"
19 
20 #include "NOD_math_functions.hh"
21 
22 #include "node_geometry_util.hh"
23 
25  {SOCK_GEOMETRY, N_("Geometry")},
26  {SOCK_STRING, N_("A")},
27  {SOCK_FLOAT, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
28  {SOCK_VECTOR, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
29  {SOCK_RGBA, N_("A"), 0.5, 0.5, 0.5, 1.0},
30  {SOCK_STRING, N_("B")},
31  {SOCK_FLOAT, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
32  {SOCK_VECTOR, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX},
33  {SOCK_RGBA, N_("B"), 0.5, 0.5, 0.5, 1.0},
34  {SOCK_FLOAT, N_("Threshold"), 0.01f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX},
35  {SOCK_STRING, N_("Result")},
36  {-1, ""},
37 };
38 
40  {SOCK_GEOMETRY, N_("Geometry")},
41  {-1, ""},
42 };
43 
45  bContext *UNUSED(C),
46  PointerRNA *ptr)
47 {
48  uiItemR(layout, ptr, "operation", 0, "", ICON_NONE);
49  uiLayoutSetPropSep(layout, true);
50  uiLayoutSetPropDecorate(layout, false);
51  uiItemR(layout, ptr, "input_type_a", 0, IFACE_("A"), ICON_NONE);
52  uiItemR(layout, ptr, "input_type_b", 0, IFACE_("B"), ICON_NONE);
53 }
54 
56 {
58  __func__);
62  node->storage = data;
63 }
64 
65 static bool operation_tests_equality(const NodeAttributeCompare &node_storage)
66 {
68 }
69 
70 namespace blender::nodes {
71 
73 {
74  NodeAttributeCompare *node_storage = (NodeAttributeCompare *)node->storage;
76  *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a);
78  *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b);
79 
80  bNodeSocket *socket_threshold = (bNodeSocket *)BLI_findlink(&node->inputs, 9);
81  nodeSetSocketAvailability(socket_threshold, operation_tests_equality(*node_storage));
82 }
83 
84 static void do_math_operation(const FloatReadAttribute &input_a,
85  const FloatReadAttribute &input_b,
86  const FloatCompareOperation operation,
87  MutableSpan<bool> span_result)
88 {
89  const int size = input_a.size();
90 
91  Span<float> span_a = input_a.get_span();
92  Span<float> span_b = input_b.get_span();
93 
95  operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
96  for (const int i : IndexRange(size)) {
97  const float a = span_a[i];
98  const float b = span_b[i];
99  const bool out = math_function(a, b);
100  span_result[i] = out;
101  }
102  })) {
103  return;
104  }
105 
106  /* The operation is not supported by this node currently. */
107  BLI_assert(false);
108 }
109 
110 static void do_equal_operation_float(const FloatReadAttribute &input_a,
111  const FloatReadAttribute &input_b,
112  const float threshold,
113  MutableSpan<bool> span_result)
114 {
115  const int size = input_a.size();
116  for (const int i : IndexRange(size)) {
117  const float a = input_a[i];
118  const float b = input_b[i];
119  span_result[i] = compare_ff(a, b, threshold);
120  }
121 }
122 
124  const Float3ReadAttribute &input_b,
125  const float threshold,
126  MutableSpan<bool> span_result)
127 {
128  const float threshold_squared = pow2f(threshold);
129  const int size = input_a.size();
130  for (const int i : IndexRange(size)) {
131  const float3 a = input_a[i];
132  const float3 b = input_b[i];
133  span_result[i] = len_squared_v3v3(a, b) < threshold_squared;
134  }
135 }
136 
138  const Color4fReadAttribute &input_b,
139  const float threshold,
140  MutableSpan<bool> span_result)
141 {
142  const float threshold_squared = pow2f(threshold);
143  const int size = input_a.size();
144  for (const int i : IndexRange(size)) {
145  const Color4f a = input_a[i];
146  const Color4f b = input_b[i];
147  span_result[i] = len_squared_v4v4(a, b) < threshold_squared;
148  }
149 }
150 
152  const BooleanReadAttribute &input_b,
153  const float UNUSED(threshold),
154  MutableSpan<bool> span_result)
155 {
156  const int size = input_a.size();
157  for (const int i : IndexRange(size)) {
158  const bool a = input_a[i];
159  const bool b = input_b[i];
160  span_result[i] = a == b;
161  }
162 }
163 
165  const FloatReadAttribute &input_b,
166  const float threshold,
167  MutableSpan<bool> span_result)
168 {
169  const int size = input_a.size();
170  for (const int i : IndexRange(size)) {
171  const float a = input_a[i];
172  const float b = input_b[i];
173  span_result[i] = !compare_ff(a, b, threshold);
174  }
175 }
176 
178  const Float3ReadAttribute &input_b,
179  const float threshold,
180  MutableSpan<bool> span_result)
181 {
182  const float threshold_squared = pow2f(threshold);
183  const int size = input_a.size();
184  for (const int i : IndexRange(size)) {
185  const float3 a = input_a[i];
186  const float3 b = input_b[i];
187  span_result[i] = len_squared_v3v3(a, b) >= threshold_squared;
188  }
189 }
190 
192  const Color4fReadAttribute &input_b,
193  const float threshold,
194  MutableSpan<bool> span_result)
195 {
196  const float threshold_squared = pow2f(threshold);
197  const int size = input_a.size();
198  for (const int i : IndexRange(size)) {
199  const Color4f a = input_a[i];
200  const Color4f b = input_b[i];
201  span_result[i] = len_squared_v4v4(a, b) >= threshold_squared;
202  }
203 }
204 
206  const BooleanReadAttribute &input_b,
207  const float UNUSED(threshold),
208  MutableSpan<bool> span_result)
209 {
210  const int size = input_a.size();
211  for (const int i : IndexRange(size)) {
212  const bool a = input_a[i];
213  const bool b = input_b[i];
214  span_result[i] = a != b;
215  }
216 }
217 
219  const GeoNodeExecParams &params,
220  const NodeAttributeCompare &node_storage)
221 {
222  if (operation_tests_equality(node_storage)) {
223  /* Convert the input attributes to the same data type for the equality tests. Use the higher
224  * complexity attribute type, otherwise information necessary to the comparison may be lost. */
226  params.get_input_attribute_data_type("A", component, CD_PROP_FLOAT),
227  params.get_input_attribute_data_type("B", component, CD_PROP_FLOAT),
228  });
229  }
230 
231  /* Use float compare for every operation besides equality. */
232  return CD_PROP_FLOAT;
233 }
234 
236  const GeoNodeExecParams &params,
237  StringRef result_name)
238 {
239  /* Use the domain of the result attribute if it already exists. */
240  ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name);
241  if (result_attribute) {
242  return result_attribute->domain();
243  }
244 
245  /* Otherwise use the highest priority domain from existing input attributes, or the default. */
246  return params.get_highest_priority_input_domain({"A", "B"}, component, ATTR_DOMAIN_POINT);
247 }
248 
250 {
251  const bNode &node = params.node();
252  NodeAttributeCompare *node_storage = (NodeAttributeCompare *)node.storage;
253  const FloatCompareOperation operation = static_cast<FloatCompareOperation>(
254  node_storage->operation);
255  const std::string result_name = params.get_input<std::string>("Result");
256 
257  const CustomDataType result_type = CD_PROP_BOOL;
258  const AttributeDomain result_domain = get_result_domain(component, params, result_name);
259 
260  OutputAttributePtr attribute_result = component.attribute_try_get_for_output(
261  result_name, result_domain, result_type);
262  if (!attribute_result) {
263  return;
264  }
265 
266  const CustomDataType input_data_type = get_data_type(component, params, *node_storage);
267 
268  ReadAttributePtr attribute_a = params.get_input_attribute(
269  "A", component, result_domain, input_data_type, nullptr);
270  ReadAttributePtr attribute_b = params.get_input_attribute(
271  "B", component, result_domain, input_data_type, nullptr);
272 
273  if (!attribute_a || !attribute_b) {
274  /* Attribute wasn't found. */
275  return;
276  }
277 
278  MutableSpan<bool> result_span = attribute_result->get_span_for_write_only<bool>();
279 
280  /* Use specific types for correct equality operations, but for other operations we use implicit
281  * conversions and float comparison. In other words, the comparison is not element-wise. */
282  if (operation_tests_equality(*node_storage)) {
283  const float threshold = params.get_input<float>("Threshold");
284  if (operation == NODE_FLOAT_COMPARE_EQUAL) {
285  if (input_data_type == CD_PROP_FLOAT) {
286  do_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span);
287  }
288  else if (input_data_type == CD_PROP_FLOAT3) {
289  do_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span);
290  }
291  else if (input_data_type == CD_PROP_COLOR) {
292  do_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span);
293  }
294  else if (input_data_type == CD_PROP_BOOL) {
295  do_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span);
296  }
297  }
298  else if (operation == NODE_FLOAT_COMPARE_NOT_EQUAL) {
299  if (input_data_type == CD_PROP_FLOAT) {
300  do_not_equal_operation_float(*attribute_a, *attribute_b, threshold, result_span);
301  }
302  else if (input_data_type == CD_PROP_FLOAT3) {
303  do_not_equal_operation_float3(*attribute_a, *attribute_b, threshold, result_span);
304  }
305  else if (input_data_type == CD_PROP_COLOR) {
306  do_not_equal_operation_color4f(*attribute_a, *attribute_b, threshold, result_span);
307  }
308  else if (input_data_type == CD_PROP_BOOL) {
309  do_not_equal_operation_bool(*attribute_a, *attribute_b, threshold, result_span);
310  }
311  }
312  }
313  else {
314  do_math_operation(*attribute_a, *attribute_b, operation, result_span);
315  }
316 
317  attribute_result.apply_span_and_save();
318 }
319 
321 {
322  GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
323 
324  geometry_set = geometry_set_realize_instances(geometry_set);
325 
326  if (geometry_set.has<MeshComponent>()) {
328  }
329  if (geometry_set.has<PointCloudComponent>()) {
331  }
332 
333  params.set_output("Geometry", geometry_set);
334 }
335 
336 } // namespace blender::nodes
337 
339 {
340  static bNodeType ntype;
341 
343  &ntype, GEO_NODE_ATTRIBUTE_COMPARE, "Attribute Compare", NODE_CLASS_ATTRIBUTE, 0);
350  &ntype, "NodeAttributeCompare", node_free_standard_storage, node_copy_standard_storage);
352  nodeRegisterType(&ntype);
353 }
AttributeDomain
Definition: BKE_attribute.h:41
@ ATTR_DOMAIN_POINT
Definition: BKE_attribute.h:43
void nodeSetSocketAvailability(struct bNodeSocket *sock, bool is_available)
Definition: node.cc:3726
void node_type_socket_templates(struct bNodeType *ntype, struct bNodeSocketTemplate *inputs, struct bNodeSocketTemplate *outputs)
Definition: node.cc:4527
void node_type_update(struct bNodeType *ntype, void(*updatefunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4623
void node_type_init(struct bNodeType *ntype, void(*initfunc)(struct bNodeTree *ntree, struct bNode *node))
Definition: node.cc:4559
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:4599
#define NODE_CLASS_ATTRIBUTE
Definition: BKE_node.h:360
void nodeRegisterType(struct bNodeType *ntype)
Definition: node.cc:1298
#define BLI_assert(a)
Definition: BLI_assert.h:58
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE int compare_ff(float a, float b, const float max_diff)
MINLINE float pow2f(float x)
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v4v4(const float a[4], const float b[4]) ATTR_WARN_UNUSED_RESULT
#define UNUSED(x)
#define ELEM(...)
#define IFACE_(msgid)
#define N_(msgid)
static uint8 component(Color32 c, uint i)
Definition: ColorBlock.cpp:126
CustomDataType
@ CD_PROP_FLOAT
@ CD_PROP_FLOAT3
@ CD_PROP_COLOR
@ CD_PROP_BOOL
@ SOCK_VECTOR
@ SOCK_FLOAT
@ SOCK_GEOMETRY
@ SOCK_STRING
@ SOCK_RGBA
FloatCompareOperation
@ NODE_FLOAT_COMPARE_GREATER_THAN
@ NODE_FLOAT_COMPARE_EQUAL
@ NODE_FLOAT_COMPARE_NOT_EQUAL
GeometryNodeAttributeInputMode
@ GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE
Group RGB to Bright Vector Camera Vector Combine Material Light Line Style Layer Add Ambient Diffuse Glossy Refraction Transparent Toon Principled Hair Volume Principled Light Particle Volume Image Sky Noise Wave Voronoi Brick Texture Vector Combine Vertex Separate Vector White RGB Map Separate Set Z Dilate Combine Combine Color Channel Split ID Combine Luminance Directional Alpha Distance Hue Movie Ellipse Bokeh View Corner Anti Mix RGB Hue Separate TEX_NODE_PROC TEX_NODE_PROC TEX_NODE_PROC TEX_NODE_PROC TEX_NODE_PROC Boolean Random Edge Subdivision Point Object Attribute Attribute Attribute Color GEO_NODE_ATTRIBUTE_COMPARE
#define C
Definition: RandGen.cpp:39
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
fn::GMutableSpan get_span_for_write_only()
OperationNode * node
void * tree
bNodeTree * ntree
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
static unsigned a[3]
Definition: RandGen.cpp:92
TypedReadAttribute< Color4f > Color4fReadAttribute
TypedReadAttribute< float > FloatReadAttribute
TypedReadAttribute< float3 > Float3ReadAttribute
GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set)
TypedReadAttribute< bool > BooleanReadAttribute
std::unique_ptr< ReadAttribute > ReadAttributePtr
CustomDataType attribute_data_type_highest_complexity(Span< CustomDataType > data_types)
static void do_not_equal_operation_color4f(const Color4fReadAttribute &input_a, const Color4fReadAttribute &input_b, const float threshold, MutableSpan< bool > span_result)
bool try_dispatch_float_math_fl_fl_to_bool(const FloatCompareOperation operation, Callback &&callback)
static void do_not_equal_operation_float3(const Float3ReadAttribute &input_a, const Float3ReadAttribute &input_b, const float threshold, MutableSpan< bool > span_result)
static void geo_node_attribute_compare_update(bNodeTree *UNUSED(ntree), bNode *node)
void update_attribute_input_socket_availabilities(bNode &node, const StringRef name, const GeometryNodeAttributeInputMode mode, const bool name_is_available)
static void attribute_compare_calc(GeometryComponent &component, const GeoNodeExecParams &params)
static void do_equal_operation_float(const FloatReadAttribute &input_a, const FloatReadAttribute &input_b, const float threshold, MutableSpan< bool > span_result)
static void geo_node_attribute_compare_exec(GeoNodeExecParams params)
static CustomDataType get_data_type(GeometryComponent &component, const GeoNodeExecParams &params, const NodeAttributeCompare &node_storage)
static AttributeDomain get_result_domain(const GeometryComponent &component, StringRef source_name, StringRef result_name)
static void do_equal_operation_float3(const Float3ReadAttribute &input_a, const Float3ReadAttribute &input_b, const float threshold, MutableSpan< bool > span_result)
static void do_equal_operation_color4f(const Color4fReadAttribute &input_a, const Color4fReadAttribute &input_b, const float threshold, MutableSpan< bool > span_result)
static void do_not_equal_operation_float(const FloatReadAttribute &input_a, const FloatReadAttribute &input_b, const float threshold, MutableSpan< bool > span_result)
static void do_equal_operation_bool(const BooleanReadAttribute &input_a, const BooleanReadAttribute &input_b, const float UNUSED(threshold), MutableSpan< bool > span_result)
static void do_math_operation(const FloatReadAttribute &input_a, const FloatReadAttribute &input_b, const FloatCompareOperation operation, MutableSpan< bool > span_result)
static void do_not_equal_operation_bool(const BooleanReadAttribute &input_a, const BooleanReadAttribute &input_b, const float UNUSED(threshold), MutableSpan< bool > span_result)
void register_node_type_geo_attribute_compare()
static void geo_node_attribute_compare_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
static bNodeSocketTemplate geo_node_attribute_compare_out[]
static bNodeSocketTemplate geo_node_attribute_compare_in[]
static bool operation_tests_equality(const NodeAttributeCompare &node_storage)
static void geo_node_attribute_compare_init(bNodeTree *UNUSED(tree), bNode *node)
void geo_node_type_base(bNodeType *ntype, int type, const char *name, short nclass, short flag)
void node_copy_standard_storage(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, const bNode *src_node)
Definition: node_util.c:67
void node_free_standard_storage(bNode *node)
Definition: node_util.c:55
GeometryComponent & get_component_for_write(GeometryComponentType component_type)
bool has(const GeometryComponentType component_type) const
Compact definition of a node socket.
Definition: BKE_node.h:95
Defines a node type.
Definition: BKE_node.h:221
NodeGeometryExecFunction geometry_node_execute
Definition: BKE_node.h:327
void(* draw_buttons)(struct uiLayout *, struct bContext *C, struct PointerRNA *ptr)
Definition: BKE_node.h:253
PointerRNA * ptr
Definition: wm_files.c:3157