diff options
-rw-r--r-- | release/scripts/startup/nodeitems_builtins.py | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_node.h | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/node.c | 2 | ||||
-rw-r--r-- | source/blender/editors/space_node/drawnode.c | 12 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_customdata_types.h | 2 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_node_types.h | 16 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_nodetree.c | 81 | ||||
-rw-r--r-- | source/blender/nodes/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/nodes/NOD_geometry.h | 2 | ||||
-rw-r--r-- | source/blender/nodes/NOD_math_functions.hh | 34 | ||||
-rw-r--r-- | source/blender/nodes/NOD_static_types.h | 2 | ||||
-rw-r--r-- | source/blender/nodes/geometry/node_geometry_util.cc | 42 | ||||
-rw-r--r-- | source/blender/nodes/geometry/node_geometry_util.hh | 2 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc | 362 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_point_separate.cc | 212 | ||||
-rw-r--r-- | source/blender/nodes/intern/math_functions.cc | 30 |
16 files changed, 790 insertions, 15 deletions
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index c3ab1b3db97..09c01ade4f1 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -483,6 +483,7 @@ geometry_node_categories = [ GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[ NodeItem("GeometryNodeAttributeRandomize"), NodeItem("GeometryNodeAttributeMath"), + NodeItem("GeometryNodeAttributeCompare"), NodeItem("GeometryNodeAttributeFill"), NodeItem("GeometryNodeAttributeMix"), NodeItem("GeometryNodeAttributeColorRamp"), @@ -510,6 +511,7 @@ geometry_node_categories = [ GeometryNodeCategory("GEO_POINT", "Point", items=[ NodeItem("GeometryNodePointDistribute"), NodeItem("GeometryNodePointInstance"), + NodeItem("GeometryNodePointSeparate"), ]), GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[ NodeItem("ShaderNodeMapRange"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 5f69fc397e8..5e360bcb074 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1353,6 +1353,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_ATTRIBUTE_FILL 1011 #define GEO_NODE_ATTRIBUTE_MIX 1012 #define GEO_NODE_ATTRIBUTE_COLOR_RAMP 1013 +#define GEO_NODE_POINT_SEPARATE 1014 +#define GEO_NODE_ATTRIBUTE_COMPARE 1015 /** \} */ diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 415eb14be66..d5cea8b6d63 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -4728,6 +4728,7 @@ static void registerGeometryNodes(void) { register_node_type_geo_group(); + register_node_type_geo_attribute_compare(); register_node_type_geo_attribute_fill(); register_node_type_geo_triangulate(); register_node_type_geo_edge_split(); @@ -4736,6 +4737,7 @@ static void registerGeometryNodes(void) register_node_type_geo_boolean(); register_node_type_geo_point_distribute(); register_node_type_geo_point_instance(); + register_node_type_geo_point_separate(); register_node_type_geo_object_info(); register_node_type_geo_attribute_randomize(); register_node_type_geo_attribute_math(); diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 45f3b6cf9c9..c2951a9d7a8 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -3149,6 +3149,15 @@ static void node_geometry_buts_boolean_math(uiLayout *layout, bContext *UNUSED(C uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE); } +static void node_geometry_buts_attribute_compare(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "input_type_a", DEFAULT_FLAGS, IFACE_("Type A"), ICON_NONE); + uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE); +} + static void node_geometry_buts_subdivision_surface(uiLayout *layout, bContext *UNUSED(C), PointerRNA *UNUSED(ptr)) @@ -3240,6 +3249,9 @@ static void node_geometry_set_butfunc(bNodeType *ntype) case GEO_NODE_ATTRIBUTE_MATH: ntype->draw_buttons = node_geometry_buts_attribute_math; break; + case GEO_NODE_ATTRIBUTE_COMPARE: + ntype->draw_buttons = node_geometry_buts_attribute_compare; + break; case GEO_NODE_POINT_INSTANCE: ntype->draw_buttons = node_geometry_buts_point_instance; break; diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 832d55ea151..e3b5ecfac04 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -220,7 +220,7 @@ typedef enum CustomDataType { /* All generic attributes. */ #define CD_MASK_PROP_ALL \ (CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 | \ - CD_MASK_PROP_COLOR | CD_MASK_PROP_STRING | CD_MASK_MLOOPCOL) + CD_MASK_PROP_COLOR | CD_MASK_PROP_STRING | CD_MASK_MLOOPCOL | CD_MASK_PROP_BOOL) typedef struct CustomData_MeshMasks { uint64_t vmask; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 7ad339c66af..f33d40b1b5d 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1074,6 +1074,17 @@ typedef struct NodeDenoise { char _pad[7]; } NodeDenoise; +typedef struct NodeAttributeCompare { + /* FloatCompareOperation. */ + uint8_t operation; + + /* GeometryNodeAttributeInputMode */ + uint8_t input_type_a; + uint8_t input_type_b; + + char _pad[5]; +} NodeAttributeCompare; + typedef struct NodeAttributeMix { /* e.g. MA_RAMP_BLEND. */ uint8_t blend_type; @@ -1365,14 +1376,14 @@ enum { }; /* Float compare node operations. */ -enum { +typedef enum FloatCompareOperation { NODE_FLOAT_COMPARE_LESS_THAN = 0, NODE_FLOAT_COMPARE_LESS_EQUAL = 1, NODE_FLOAT_COMPARE_GREATER_THAN = 2, NODE_FLOAT_COMPARE_GREATER_EQUAL = 3, NODE_FLOAT_COMPARE_EQUAL = 4, NODE_FLOAT_COMPARE_NOT_EQUAL = 5, -}; +} FloatCompareOperation; /* Clamp node types. */ enum { @@ -1503,6 +1514,7 @@ typedef enum GeometryNodeAttributeInputMode { GEO_NODE_ATTRIBUTE_INPUT_FLOAT = 1, GEO_NODE_ATTRIBUTE_INPUT_VECTOR = 2, GEO_NODE_ATTRIBUTE_INPUT_COLOR = 3, + GEO_NODE_ATTRIBUTE_INPUT_BOOLEAN = 4, } GeometryNodeAttributeInputMode; typedef enum GeometryNodePointDistributeMethod { diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index d4ac3d1b084..9a627ef6e70 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -438,20 +438,54 @@ static const EnumPropertyItem rna_node_geometry_attribute_input_b_items[] = { {0, NULL, 0, NULL, NULL}, }; -static const EnumPropertyItem rna_node_geometry_attribute_factor_input_type_items[] = { - {GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", ""}, - {GEO_NODE_ATTRIBUTE_INPUT_FLOAT, "FLOAT", 0, "Float", ""}, +# define ITEM_ATTRIBUTE \ + { \ + GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", "" \ + } +# define ITEM_FLOAT \ + { \ + GEO_NODE_ATTRIBUTE_INPUT_FLOAT, "FLOAT", 0, "Float", "" \ + } +# define ITEM_VECTOR \ + { \ + GEO_NODE_ATTRIBUTE_INPUT_VECTOR, "VECTOR", 0, "Vector", "" \ + } +# define ITEM_COLOR \ + { \ + GEO_NODE_ATTRIBUTE_INPUT_COLOR, "COLOR", 0, "Color", "" \ + } +# define ITEM_BOOLEAN \ + { \ + GEO_NODE_ATTRIBUTE_INPUT_BOOLEAN, "BOOLEAN", 0, "Boolean", "" \ + } + +static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_float[] = { + ITEM_ATTRIBUTE, + ITEM_FLOAT, {0, NULL, 0, NULL, NULL}, }; - -static const EnumPropertyItem rna_node_geometry_attribute_input_type_items[] = { - {GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", ""}, - {GEO_NODE_ATTRIBUTE_INPUT_FLOAT, "FLOAT", 0, "Float", ""}, - {GEO_NODE_ATTRIBUTE_INPUT_VECTOR, "VECTOR", 0, "Vector", ""}, - {GEO_NODE_ATTRIBUTE_INPUT_COLOR, "COLOR", 0, "Color", ""}, +static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_no_boolean[] = { + ITEM_ATTRIBUTE, + ITEM_FLOAT, + ITEM_VECTOR, + ITEM_COLOR, + {0, NULL, 0, NULL, NULL}, +}; +static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_any[] = { + ITEM_ATTRIBUTE, + ITEM_FLOAT, + ITEM_VECTOR, + ITEM_COLOR, + ITEM_BOOLEAN, {0, NULL, 0, NULL, NULL}, }; +# undef ITEM_ATTRIBUTE +# undef ITEM_FLOAT +# undef ITEM_VECTOR +# undef ITEM_COLOR +# undef ITEM_BOOLEAN + static const EnumPropertyItem rna_node_geometry_point_distribute_method_items[] = { {GEO_NODE_POINT_DISTRIBUTE_RANDOM, "RANDOM", @@ -8480,17 +8514,40 @@ static void def_geo_attribute_mix(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); prop = RNA_def_property(srna, "input_type_factor", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_factor_input_type_items); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); RNA_def_property_ui_text(prop, "Input Type Factor", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean); + RNA_def_property_ui_text(prop, "Input Type A", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "input_type_b", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_no_boolean); + RNA_def_property_ui_text(prop, "Input Type B", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + +static void def_geo_attribute_attribute_compare(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeAttributeCompare", "storage"); + + prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_node_float_compare_items); + RNA_def_property_enum_default(prop, NODE_MATH_ADD); + RNA_def_property_ui_text(prop, "Operation", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_any); RNA_def_property_ui_text(prop, "Input Type A", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); prop = RNA_def_property(srna, "input_type_b", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_any); RNA_def_property_ui_text(prop, "Input Type B", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index bc0e7972bcb..e152c50179e 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -139,6 +139,7 @@ set(SRC function/node_function_util.cc geometry/nodes/node_geo_attribute_color_ramp.cc + geometry/nodes/node_geo_attribute_compare.cc geometry/nodes/node_geo_attribute_fill.cc geometry/nodes/node_geo_attribute_math.cc geometry/nodes/node_geo_attribute_randomize.cc @@ -151,6 +152,7 @@ set(SRC geometry/nodes/node_geo_point_distribute.cc geometry/nodes/node_geo_point_distribute_poisson_disk.cc geometry/nodes/node_geo_point_instance.cc + geometry/nodes/node_geo_point_separate.cc geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_transform.cc geometry/nodes/node_geo_triangulate.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index f1cd55ce048..19a7acf2299 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -38,6 +38,8 @@ void register_node_type_geo_object_info(void); void register_node_type_geo_attribute_randomize(void); void register_node_type_geo_attribute_math(void); void register_node_type_geo_join_geometry(void); +void register_node_type_geo_point_separate(void); +void register_node_type_geo_attribute_compare(void); void register_node_type_geo_attribute_mix(void); void register_node_type_geo_attribute_color_ramp(void); diff --git a/source/blender/nodes/NOD_math_functions.hh b/source/blender/nodes/NOD_math_functions.hh index 70e4174a844..cc750f9595a 100644 --- a/source/blender/nodes/NOD_math_functions.hh +++ b/source/blender/nodes/NOD_math_functions.hh @@ -36,6 +36,7 @@ struct FloatMathOperationInfo { }; const FloatMathOperationInfo *get_float_math_operation_info(const int operation); +const FloatMathOperationInfo *get_float_compare_operation_info(const int operation); /** * This calls the `callback` with two arguments: @@ -197,4 +198,37 @@ inline bool try_dispatch_float_math_fl_fl_fl_to_fl(const int operation, Callback return false; } +/** + * This is similar to try_dispatch_float_math_fl_to_fl, just with a different callback signature. + */ +template<typename Callback> +inline bool try_dispatch_float_math_fl_fl_to_bool(const FloatCompareOperation operation, + Callback &&callback) +{ + const FloatMathOperationInfo *info = get_float_compare_operation_info(operation); + if (info == nullptr) { + return false; + } + + /* This is just an utility function to keep the individual cases smaller. */ + auto dispatch = [&](auto math_function) -> bool { + callback(math_function, *info); + return true; + }; + + switch (operation) { + case NODE_FLOAT_COMPARE_LESS_THAN: + return dispatch([](float a, float b) { return a < b; }); + case NODE_FLOAT_COMPARE_LESS_EQUAL: + return dispatch([](float a, float b) { return a <= b; }); + case NODE_FLOAT_COMPARE_GREATER_THAN: + return dispatch([](float a, float b) { return a > b; }); + case NODE_FLOAT_COMPARE_GREATER_EQUAL: + return dispatch([](float a, float b) { return a >= b; }); + default: + return false; + } + return false; +} + } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 662952abb59..1e323d23a53 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -280,6 +280,8 @@ DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "ATTRIBUTE_COLOR_RAMP", AttributeColorRamp, "Attribute Color Ramp", "") +DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "ATTRIBUTE_COMPARE", AttributeCompare, "Attribute Compare", "") /* undefine macros */ #undef DefNode diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc index 34c7d224f03..bcebaa6a37b 100644 --- a/source/blender/nodes/geometry/node_geometry_util.cc +++ b/source/blender/nodes/geometry/node_geometry_util.cc @@ -36,6 +36,48 @@ void update_attribute_input_socket_availabilities(bNode &node, } } +static int attribute_data_type_complexity(const CustomDataType data_type) +{ + switch (data_type) { + case CD_PROP_BOOL: + return 0; + case CD_PROP_INT32: + return 1; + case CD_PROP_FLOAT: + return 2; + case CD_PROP_FLOAT3: + return 4; + case CD_PROP_COLOR: + return 5; +#if 0 /* Attribute types are not supported yet. */ + case CD_MLOOPCOL: + return 3; + case CD_PROP_STRING: + return 6; +#endif + default: + /* Only accept "generic" custom data types used by the attribute system. */ + BLI_assert(false); + return 0; + } +} + +CustomDataType attribute_domain_highest_complexity(Span<CustomDataType> data_types) +{ + int highest_complexity = INT_MIN; + CustomDataType most_complex_type = CD_PROP_COLOR; + + for (const CustomDataType data_type : data_types) { + const int complexity = attribute_data_type_complexity(data_type); + if (complexity > highest_complexity) { + highest_complexity = complexity; + most_complex_type = data_type; + } + } + + return most_complex_type; +} + } // namespace blender::nodes bool geo_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree) diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index c97463cdc22..3102690d2ed 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -43,6 +43,8 @@ void update_attribute_input_socket_availabilities(bNode &node, const StringRef name, const GeometryNodeAttributeInputMode mode); +CustomDataType attribute_domain_highest_complexity(Span<CustomDataType>); + void poisson_disk_point_elimination(Vector<float3> const *input_points, Vector<float3> *output_points, float maximum_distance, diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc new file mode 100644 index 00000000000..a3d5abfb3f2 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc @@ -0,0 +1,362 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +#include "BKE_attribute.h" +#include "BKE_attribute_access.hh" + +#include "BLI_array.hh" +#include "BLI_math_base_safe.h" +#include "BLI_rand.hh" + +#include "DNA_mesh_types.h" +#include "DNA_pointcloud_types.h" + +#include "NOD_math_functions.hh" + +static bNodeSocketTemplate geo_node_attribute_compare_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("A")}, + {SOCK_FLOAT, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_VECTOR, N_("A"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_RGBA, N_("A"), 0.5, 0.5, 0.5, 1.0}, + {SOCK_STRING, N_("B")}, + {SOCK_FLOAT, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_VECTOR, N_("B"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX}, + {SOCK_RGBA, N_("B"), 0.5, 0.5, 0.5, 1.0}, + {SOCK_FLOAT, N_("Threshold"), 0.01f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX}, + {SOCK_STRING, N_("Result")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_attribute_compare_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_attribute_compare_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeAttributeCompare *data = (NodeAttributeCompare *)MEM_callocN(sizeof(NodeAttributeCompare), + "attribute mix node"); + data->operation = NODE_FLOAT_COMPARE_GREATER_THAN; + data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; + data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; + node->storage = data; +} + +static bool operation_tests_equality(const NodeAttributeCompare &node_storage) +{ + return ELEM(node_storage.operation, NODE_FLOAT_COMPARE_EQUAL, NODE_FLOAT_COMPARE_NOT_EQUAL); +} + +namespace blender::nodes { + +static void geo_node_attribute_compare_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeAttributeCompare *node_storage = (NodeAttributeCompare *)node->storage; + update_attribute_input_socket_availabilities( + *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a); + update_attribute_input_socket_availabilities( + *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b); + + bNodeSocket *socket_threshold = (bNodeSocket *)BLI_findlink(&node->inputs, 9); + nodeSetSocketAvailability(socket_threshold, operation_tests_equality(*node_storage)); +} + +static void do_math_operation(const FloatReadAttribute &input_a, + const FloatReadAttribute &input_b, + const FloatCompareOperation operation, + MutableSpan<bool> span_result) +{ + const int size = input_a.size(); + + Span<float> span_a = input_a.get_span(); + Span<float> span_b = input_b.get_span(); + + if (try_dispatch_float_math_fl_fl_to_bool( + operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { + for (const int i : IndexRange(size)) { + const float a = span_a[i]; + const float b = span_b[i]; + const bool out = math_function(a, b); + span_result[i] = out; + } + })) { + return; + } + + /* The operation is not supported by this node currently. */ + BLI_assert(false); +} + +static void do_equal_operation(const FloatReadAttribute &input_a, + const FloatReadAttribute &input_b, + const float threshold, + MutableSpan<bool> span_result) +{ + const int size = input_a.size(); + for (const int i : IndexRange(size)) { + const float a = input_a[i]; + const float b = input_b[i]; + span_result[i] = compare_ff(a, b, threshold); + } +} + +static void do_equal_operation(const Float3ReadAttribute &input_a, + const Float3ReadAttribute &input_b, + const float threshold, + MutableSpan<bool> span_result) +{ + const float threshold_squared = pow2f(threshold); + const int size = input_a.size(); + for (const int i : IndexRange(size)) { + const float3 a = input_a[i]; + const float3 b = input_b[i]; + span_result[i] = len_squared_v3v3(a, b) < threshold_squared; + } +} + +static void do_equal_operation(const Color4fReadAttribute &input_a, + const Color4fReadAttribute &input_b, + const float threshold, + MutableSpan<bool> span_result) +{ + const float threshold_squared = pow2f(threshold); + const int size = input_a.size(); + for (const int i : IndexRange(size)) { + const Color4f a = input_a[i]; + const Color4f b = input_b[i]; + span_result[i] = len_squared_v4v4(a, b) < threshold_squared; + } +} + +static void do_equal_operation(const BooleanReadAttribute &input_a, + const BooleanReadAttribute &input_b, + const float UNUSED(threshold), + MutableSpan<bool> span_result) +{ + const int size = input_a.size(); + for (const int i : IndexRange(size)) { + const bool a = input_a[i]; + const bool b = input_b[i]; + span_result[i] = a == b; + } +} + +static void do_not_equal_operation(const FloatReadAttribute &input_a, + const FloatReadAttribute &input_b, + const float threshold, + MutableSpan<bool> span_result) +{ + const int size = input_a.size(); + for (const int i : IndexRange(size)) { + const float a = input_a[i]; + const float b = input_b[i]; + span_result[i] = !compare_ff(a, b, threshold); + } +} + +static void do_not_equal_operation(const Float3ReadAttribute &input_a, + const Float3ReadAttribute &input_b, + const float threshold, + MutableSpan<bool> span_result) +{ + const float threshold_squared = pow2f(threshold); + const int size = input_a.size(); + for (const int i : IndexRange(size)) { + const float3 a = input_a[i]; + const float3 b = input_b[i]; + span_result[i] = len_squared_v3v3(a, b) >= threshold_squared; + } +} + +static void do_not_equal_operation(const Color4fReadAttribute &input_a, + const Color4fReadAttribute &input_b, + const float threshold, + MutableSpan<bool> span_result) +{ + const float threshold_squared = pow2f(threshold); + const int size = input_a.size(); + for (const int i : IndexRange(size)) { + const Color4f a = input_a[i]; + const Color4f b = input_b[i]; + span_result[i] = len_squared_v4v4(a, b) >= threshold_squared; + } +} + +static void do_not_equal_operation(const BooleanReadAttribute &input_a, + const BooleanReadAttribute &input_b, + const float UNUSED(threshold), + MutableSpan<bool> span_result) +{ + const int size = input_a.size(); + for (const int i : IndexRange(size)) { + const bool a = input_a[i]; + const bool b = input_b[i]; + span_result[i] = a != b; + } +} + +static CustomDataType get_data_type(GeometryComponent &component, + const GeoNodeExecParams ¶ms, + const NodeAttributeCompare &node_storage) +{ + if (operation_tests_equality(node_storage)) { + CustomDataType data_type_a = params.get_input_attribute_data_type( + "A", component, CD_PROP_FLOAT); + CustomDataType data_type_b = params.get_input_attribute_data_type( + "B", component, CD_PROP_FLOAT); + + /* Convert the input attributes to the same data type for the equality tests. Use the higher + * complexity attribute type, otherwise information necessary to the comparison may be lost. */ + return attribute_domain_highest_complexity({data_type_a, data_type_b}); + } + + /* Use float compare for every operation besides equality. */ + return CD_PROP_FLOAT; +} + +static void attribute_compare_calc(GeometryComponent &component, const GeoNodeExecParams ¶ms) +{ + const bNode &node = params.node(); + NodeAttributeCompare *node_storage = (NodeAttributeCompare *)node.storage; + const FloatCompareOperation operation = static_cast<FloatCompareOperation>( + node_storage->operation); + + /* The result type of this node is always float. */ + const CustomDataType result_type = CD_PROP_BOOL; + /* The result domain is always point for now. */ + const AttributeDomain result_domain = ATTR_DOMAIN_POINT; + + /* Get result attribute first, in case it has to overwrite one of the existing attributes. */ + const std::string result_name = params.get_input<std::string>("Result"); + WriteAttributePtr attribute_result = component.attribute_try_ensure_for_write( + result_name, result_domain, result_type); + if (!attribute_result) { + return; + } + + const CustomDataType input_data_type = get_data_type(component, params, *node_storage); + + ReadAttributePtr attribute_a = params.get_input_attribute( + "A", component, result_domain, input_data_type, nullptr); + ReadAttributePtr attribute_b = params.get_input_attribute( + "B", component, result_domain, input_data_type, nullptr); + + if (!attribute_a || !attribute_b) { + /* Attribute wasn't found. */ + return; + } + + BooleanWriteAttribute attribute_result_bool = std::move(attribute_result); + MutableSpan<bool> result_span = attribute_result_bool.get_span(); + + /* Use specific types for correct equality operations, but for other operations we use implicit + * conversions and float comparison. In other words, the comparison is not element-wise. */ + if (operation_tests_equality(*node_storage)) { + const float threshold = params.get_input<float>("Threshold"); + if (operation == NODE_FLOAT_COMPARE_EQUAL) { + if (input_data_type == CD_PROP_FLOAT) { + FloatReadAttribute attribute_a_float = std::move(attribute_a); + FloatReadAttribute attribute_b_float = std::move(attribute_b); + do_equal_operation( + std::move(attribute_a_float), std::move(attribute_b_float), threshold, result_span); + } + else if (input_data_type == CD_PROP_FLOAT3) { + Float3ReadAttribute attribute_a_float3 = std::move(attribute_a); + Float3ReadAttribute attribute_b_float3 = std::move(attribute_b); + do_equal_operation( + std::move(attribute_a_float3), std::move(attribute_b_float3), threshold, result_span); + } + else if (input_data_type == CD_PROP_COLOR) { + Color4fReadAttribute attribute_a_color = std::move(attribute_a); + Color4fReadAttribute attribute_b_color = std::move(attribute_b); + do_equal_operation( + std::move(attribute_a_color), std::move(attribute_b_color), threshold, result_span); + } + else if (input_data_type == CD_PROP_BOOL) { + BooleanReadAttribute attribute_a_bool = std::move(attribute_a); + BooleanReadAttribute attribute_b_bool = std::move(attribute_b); + do_equal_operation( + std::move(attribute_a_bool), std::move(attribute_b_bool), threshold, result_span); + } + } + else if (operation == NODE_FLOAT_COMPARE_NOT_EQUAL) { + if (input_data_type == CD_PROP_FLOAT) { + FloatReadAttribute attribute_a_float = std::move(attribute_a); + FloatReadAttribute attribute_b_float = std::move(attribute_b); + do_not_equal_operation( + std::move(attribute_a_float), std::move(attribute_b_float), threshold, result_span); + } + else if (input_data_type == CD_PROP_FLOAT3) { + Float3ReadAttribute attribute_a_float3 = std::move(attribute_a); + Float3ReadAttribute attribute_b_float3 = std::move(attribute_b); + do_not_equal_operation( + std::move(attribute_a_float3), std::move(attribute_b_float3), threshold, result_span); + } + else if (input_data_type == CD_PROP_COLOR) { + Color4fReadAttribute attribute_a_color = std::move(attribute_a); + Color4fReadAttribute attribute_b_color = std::move(attribute_b); + do_not_equal_operation( + std::move(attribute_a_color), std::move(attribute_b_color), threshold, result_span); + } + else if (input_data_type == CD_PROP_BOOL) { + BooleanReadAttribute attribute_a_bool = std::move(attribute_a); + BooleanReadAttribute attribute_b_bool = std::move(attribute_b); + do_not_equal_operation( + std::move(attribute_a_bool), std::move(attribute_b_bool), threshold, result_span); + } + } + } + else { + do_math_operation(std::move(attribute_a), std::move(attribute_b), operation, result_span); + } + + attribute_result_bool.apply_span(); +} + +static void geo_node_attribute_compare_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + if (geometry_set.has<MeshComponent>()) { + attribute_compare_calc(geometry_set.get_component_for_write<MeshComponent>(), params); + } + if (geometry_set.has<PointCloudComponent>()) { + attribute_compare_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params); + } + + params.set_output("Geometry", geometry_set); +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_compare() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_ATTRIBUTE_COMPARE, "Attribute Compare", NODE_CLASS_ATTRIBUTE, 0); + node_type_socket_templates( + &ntype, geo_node_attribute_compare_in, geo_node_attribute_compare_out); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_compare_exec; + node_type_update(&ntype, blender::nodes::geo_node_attribute_compare_update); + node_type_storage( + &ntype, "NodeAttributeCompare", node_free_standard_storage, node_copy_standard_storage); + node_type_init(&ntype, geo_node_attribute_compare_init); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc new file mode 100644 index 00000000000..3229d87e1b2 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc @@ -0,0 +1,212 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_mesh.h" +#include "BKE_persistent_data_handle.hh" +#include "BKE_pointcloud.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_point_instance_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Mask")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_point_instance_out[] = { + {SOCK_GEOMETRY, N_("Geometry 1")}, + {SOCK_GEOMETRY, N_("Geometry 2")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void fill_new_attribute_from_input(ReadAttributePtr input_attribute, + WriteAttributePtr out_attribute_a, + WriteAttributePtr out_attribute_b, + Span<bool> a_or_b) +{ + fn::GSpan in_span = input_attribute->get_span(); + int i_a = 0; + int i_b = 0; + for (int i_in = 0; i_in < in_span.size(); i_in++) { + const bool move_to_b = a_or_b[i_in]; + if (move_to_b) { + out_attribute_b->set(i_b, in_span[i_in]); + i_b++; + } + else { + out_attribute_a->set(i_a, in_span[i_in]); + i_a++; + } + } +} + +/** + * Move the original attribute values to the two output components. + * + * \note This assumes a consistent ordering of indices before and after the split, + * which is true for points and a simple vertex array. + */ +static void move_split_attributes(const GeometryComponent &in_component, + GeometryComponent &out_component_a, + GeometryComponent &out_component_b, + Span<bool> a_or_b) +{ + Set<std::string> attribute_names = in_component.attribute_names(); + + for (const std::string &name : attribute_names) { + ReadAttributePtr attribute = in_component.attribute_try_get_for_read(name); + BLI_assert(attribute); + + /* Since this node only creates points and vertices, don't copy other attributes. */ + if (attribute->domain() != ATTR_DOMAIN_POINT) { + continue; + } + + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute->cpp_type()); + const AttributeDomain domain = attribute->domain(); + + /* Don't try to create the attribute on the new component if it already exists. Built-in + * attributes will already exist on new components by definition. It should always be possible + * to recreate the attribute on the same component type. Also, if one of the new components + * has the attribute the other one should have it too, but check independently to be safe. */ + if (!out_component_a.attribute_exists(name)) { + if (!out_component_a.attribute_try_create(name, domain, data_type)) { + BLI_assert(false); + continue; + } + } + if (!out_component_b.attribute_exists(name)) { + if (!out_component_b.attribute_try_create(name, domain, data_type)) { + BLI_assert(false); + continue; + } + } + + WriteAttributePtr out_attribute_a = out_component_a.attribute_try_get_for_write(name); + WriteAttributePtr out_attribute_b = out_component_b.attribute_try_get_for_write(name); + if (!out_attribute_a || !out_attribute_b) { + BLI_assert(false); + continue; + } + + fill_new_attribute_from_input( + std::move(attribute), std::move(out_attribute_a), std::move(out_attribute_b), a_or_b); + } +} + +/** + * Find total in each new set and find which of the output sets each point will belong to. + */ +static Array<bool> count_point_splits(const GeometryComponent &component, + const GeoNodeExecParams ¶ms, + int *r_a_total, + int *r_b_total) +{ + const BooleanReadAttribute mask_attribute = params.get_input_attribute<bool>( + "Mask", component, ATTR_DOMAIN_POINT, false); + Array<bool> masks = mask_attribute.get_span(); + const int in_total = masks.size(); + + *r_b_total = 0; + for (const bool mask : masks) { + if (mask) { + *r_b_total += 1; + } + } + *r_a_total = in_total - *r_b_total; + + return masks; +} + +static void separate_mesh(const MeshComponent &in_component, + const GeoNodeExecParams ¶ms, + MeshComponent &out_component_a, + MeshComponent &out_component_b) +{ + const int size = in_component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (size == 0) { + return; + } + + int a_total; + int b_total; + Array<bool> a_or_b = count_point_splits(in_component, params, &a_total, &b_total); + + out_component_a.replace(BKE_mesh_new_nomain(a_total, 0, 0, 0, 0)); + out_component_b.replace(BKE_mesh_new_nomain(b_total, 0, 0, 0, 0)); + + move_split_attributes(in_component, out_component_a, out_component_b, a_or_b); +} + +static void separate_point_cloud(const PointCloudComponent &in_component, + const GeoNodeExecParams ¶ms, + PointCloudComponent &out_component_a, + PointCloudComponent &out_component_b) +{ + const int size = in_component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (size == 0) { + return; + } + + int a_total; + int b_total; + Array<bool> a_or_b = count_point_splits(in_component, params, &a_total, &b_total); + + out_component_a.replace(BKE_pointcloud_new_nomain(a_total)); + out_component_b.replace(BKE_pointcloud_new_nomain(b_total)); + + move_split_attributes(in_component, out_component_a, out_component_b, a_or_b); +} + +static void geo_node_point_separate_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + GeometrySet out_set_a(geometry_set); + GeometrySet out_set_b; + + if (geometry_set.has<PointCloudComponent>()) { + separate_point_cloud(*geometry_set.get_component_for_read<PointCloudComponent>(), + params, + out_set_a.get_component_for_write<PointCloudComponent>(), + out_set_b.get_component_for_write<PointCloudComponent>()); + } + if (geometry_set.has<MeshComponent>()) { + separate_mesh(*geometry_set.get_component_for_read<MeshComponent>(), + params, + out_set_a.get_component_for_write<MeshComponent>(), + out_set_b.get_component_for_write<MeshComponent>()); + } + + params.set_output("Geometry 1", std::move(out_set_a)); + params.set_output("Geometry 2", std::move(out_set_b)); +} +} // namespace blender::nodes + +void register_node_type_geo_point_separate() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_POINT_SEPARATE, "Point Separate", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_point_instance_in, geo_node_point_instance_out); + ntype.geometry_node_execute = blender::nodes::geo_node_point_separate_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/intern/math_functions.cc b/source/blender/nodes/intern/math_functions.cc index 5a6faa809f2..32a18f1c193 100644 --- a/source/blender/nodes/intern/math_functions.cc +++ b/source/blender/nodes/intern/math_functions.cc @@ -116,4 +116,34 @@ const FloatMathOperationInfo *get_float_math_operation_info(const int operation) return nullptr; } +const FloatMathOperationInfo *get_float_compare_operation_info(const int operation) +{ + +#define RETURN_OPERATION_INFO(title_case_name, shader_name) \ + { \ + static const FloatMathOperationInfo info{title_case_name, shader_name}; \ + return &info; \ + } \ + ((void)0) + + switch (operation) { + case NODE_FLOAT_COMPARE_LESS_THAN: + RETURN_OPERATION_INFO("Less Than", "math_less_than"); + case NODE_FLOAT_COMPARE_LESS_EQUAL: + RETURN_OPERATION_INFO("Less Than or Equal", "math_less_equal"); + case NODE_FLOAT_COMPARE_GREATER_THAN: + RETURN_OPERATION_INFO("Greater Than", "math_greater_than"); + case NODE_FLOAT_COMPARE_GREATER_EQUAL: + RETURN_OPERATION_INFO("Greater Than or Equal", "math_greater_equal"); + case NODE_FLOAT_COMPARE_EQUAL: + RETURN_OPERATION_INFO("Equal", "math_equal"); + case NODE_FLOAT_COMPARE_NOT_EQUAL: + RETURN_OPERATION_INFO("Not Equal", "math_not_equal"); + } + +#undef RETURN_OPERATION_INFO + + return nullptr; +} + } // namespace blender::nodes |