diff options
25 files changed, 894 insertions, 24 deletions
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index df98645aee0..85f0f035a9c 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("GeometryNodeRandomAttribute"), NodeItem("GeometryNodeAttributeMath"), + NodeItem("GeometryNodeAttributeCompare"), NodeItem("GeometryNodeAttributeFill"), ]), GeometryNodeCategory("GEO_COLOR", "Color", items=[ @@ -508,6 +509,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_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index c4a704ef385..797187bacd7 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -266,9 +266,11 @@ template<typename T> class TypedWriteAttribute { } }; +using BooleanReadAttribute = TypedReadAttribute<bool>; using FloatReadAttribute = TypedReadAttribute<float>; using Float3ReadAttribute = TypedReadAttribute<float3>; using Color4fReadAttribute = TypedReadAttribute<Color4f>; +using BooleanWriteAttribute = TypedWriteAttribute<bool>; using FloatWriteAttribute = TypedWriteAttribute<float>; using Float3WriteAttribute = TypedWriteAttribute<float3>; using Color4fWriteAttribute = TypedWriteAttribute<Color4f>; diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 3398da9896b..2613a9db190 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -88,6 +88,8 @@ class GeometryComponent { GeometryComponentType type() const; + bool attribute_exists(const blender::StringRef attribute_name) const; + /* Returns true when the geometry component supports this attribute domain. */ virtual bool attribute_domain_supported(const AttributeDomain domain) const; /* Returns true when the given data type is supported in the given domain. */ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 56ed60c1f67..84ceed49674 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1351,6 +1351,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_ATTRIBUTE_MATH 1009 #define GEO_NODE_JOIN_GEOMETRY 1010 #define GEO_NODE_ATTRIBUTE_FILL 1011 +#define GEO_NODE_POINT_SEPARATE 1012 +#define GEO_NODE_ATTRIBUTE_COMPARE 1013 /** \} */ diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index d79168d5443..623335f65a1 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -392,6 +392,8 @@ const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType ty return &CPPType::get<int>(); case CD_PROP_COLOR: return &CPPType::get<Color4f>(); + case CD_PROP_BOOL: + return &CPPType::get<bool>(); default: return nullptr; } @@ -415,6 +417,9 @@ CustomDataType cpp_type_to_custom_data_type(const blender::fn::CPPType &type) if (type.is<Color4f>()) { return CD_PROP_COLOR; } + if (type.is<bool>()) { + return CD_PROP_BOOL; + } return static_cast<CustomDataType>(-1); } @@ -449,6 +454,9 @@ static ReadAttributePtr read_attribute_from_custom_data(const CustomData &custom case CD_PROP_COLOR: return std::make_unique<ArrayReadAttribute<Color4f>>( domain, Span(static_cast<Color4f *>(layer.data), size)); + case CD_PROP_BOOL: + return std::make_unique<ArrayReadAttribute<bool>>( + domain, Span(static_cast<bool *>(layer.data), size)); } } } @@ -490,6 +498,9 @@ static WriteAttributePtr write_attribute_from_custom_data( case CD_PROP_COLOR: return std::make_unique<ArrayWriteAttribute<Color4f>>( domain, MutableSpan(static_cast<Color4f *>(layer.data), size)); + case CD_PROP_BOOL: + return std::make_unique<ArrayWriteAttribute<bool>>( + domain, MutableSpan(static_cast<bool *>(layer.data), size)); } } } @@ -590,6 +601,15 @@ Set<std::string> GeometryComponent::attribute_names() const return {}; } +bool GeometryComponent::attribute_exists(const blender::StringRef attribute_name) const +{ + ReadAttributePtr attribute = this->attribute_try_get_for_read(attribute_name); + if (attribute) { + return true; + } + return false; +} + static ReadAttributePtr try_adapt_data_type(ReadAttributePtr attribute, const blender::fn::CPPType &to_type) { @@ -742,6 +762,7 @@ bool PointCloudComponent::attribute_domain_with_type_supported( const AttributeDomain domain, const CustomDataType data_type) const { return domain == ATTR_DOMAIN_POINT && ELEM(data_type, + CD_PROP_BOOL, CD_PROP_FLOAT, CD_PROP_FLOAT2, CD_PROP_FLOAT3, @@ -865,8 +886,13 @@ bool MeshComponent::attribute_domain_with_type_supported(const AttributeDomain d if (!this->attribute_domain_supported(domain)) { return false; } - return ELEM( - data_type, CD_PROP_FLOAT, CD_PROP_FLOAT2, CD_PROP_FLOAT3, CD_PROP_INT32, CD_PROP_COLOR); + return ELEM(data_type, + CD_PROP_BOOL, + CD_PROP_FLOAT, + CD_PROP_FLOAT2, + CD_PROP_FLOAT3, + CD_PROP_INT32, + CD_PROP_COLOR); } int MeshComponent::attribute_domain_size(const AttributeDomain domain) const diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index fdb3e246382..1e2bc570c65 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -1837,6 +1837,21 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { layerMultiply_propfloat2, NULL, layerAdd_propfloat2}, + /* 50: CD_PROP_POOL */ + {sizeof(bool), + "bool", + 1, + N_("Boolean"), + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL}, }; static const char *LAYERTYPENAMES[CD_NUMTYPES] = { @@ -1892,6 +1907,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = { "CDPropCol", "CDPropFloat3", "CDPropFloat2", + "CDPropBoolean", }; const CustomData_MeshMasks CD_MASK_BAREMESH = { diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index ecc17ff0e5e..402b22dcb2e 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -4694,6 +4694,8 @@ static void registerGeometryNodes(void) register_node_type_geo_random_attribute(); register_node_type_geo_attribute_math(); register_node_type_geo_join_geometry(); + register_node_type_geo_point_separate(); + register_node_type_geo_attribute_compare(); } static void registerFunctionNodes(void) diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 2ff32a4a82e..606583922f8 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)) @@ -3208,6 +3217,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_ATTRIBUTE_FILL: ntype->draw_buttons = node_geometry_buts_attribute_fill; break; diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index ae0bb20e529..832d55ea151 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -75,8 +75,7 @@ typedef struct CustomData { * MUST be >= CD_NUMTYPES, but we cant use a define here. * Correct size is ensured in CustomData_update_typemap assert(). */ - int typemap[50]; - char _pad[4]; + int typemap[51]; /** Number of layers, size of layers array. */ int totlayer, maxlayer; /** In editmode, total size of all data layers. */ @@ -156,7 +155,9 @@ typedef enum CustomDataType { CD_PROP_FLOAT3 = 48, CD_PROP_FLOAT2 = 49, - CD_NUMTYPES = 50, + CD_PROP_BOOL = 50, + + CD_NUMTYPES = 51, } CustomDataType; /* Bits for CustomDataMask */ @@ -208,6 +209,7 @@ typedef enum CustomDataType { #define CD_MASK_PROP_COLOR (1ULL << CD_PROP_COLOR) #define CD_MASK_PROP_FLOAT3 (1ULL << CD_PROP_FLOAT3) #define CD_MASK_PROP_FLOAT2 (1ULL << CD_PROP_FLOAT2) +#define CD_MASK_PROP_BOOL (1ULL << CD_PROP_BOOL) /** Multires loop data. */ #define CD_MASK_MULTIRES_GRIDS (CD_MASK_MDISPS | CD_GRID_PAINT_MASK) diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 13f8b11352a..a9e94ee9b0f 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1059,6 +1059,17 @@ typedef struct NodeDenoise { char _pad[7]; } NodeDenoise; +typedef struct NodeAttributeCompare { + /* e.g. NODE_FLOAT_COMPARE_LESS_THAN. */ + uint8_t operation; + + /* GeometryNodeAttributeInputMode */ + uint8_t input_type_a; + uint8_t input_type_b; + + char _pad[5]; +} NodeAttributeCompare; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -1336,14 +1347,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 { @@ -1469,6 +1480,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; #ifdef __cplusplus diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index 95f6340174a..f98ca47d767 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -44,6 +44,7 @@ const EnumPropertyItem rna_enum_attribute_type_items[] = { {CD_PROP_COLOR, "FLOAT_COLOR", 0, "Float Color", "RGBA color with floating-point precisions"}, {CD_MLOOPCOL, "BYTE_COLOR", 0, "Byte Color", "RGBA color with 8-bit precision"}, {CD_PROP_STRING, "STRING", 0, "String", "Text string"}, + {CD_PROP_BOOL, "BOOLEAN", 0, "Boolean", "True or false"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 07dba3e55a1..fb7b458cfa4 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -272,32 +272,32 @@ const EnumPropertyItem rna_enum_node_float_compare_items[] = { {NODE_FLOAT_COMPARE_LESS_THAN, "LESS_THAN", 0, - "A < B", + "Less Than", "True when the first input is smaller than second input"}, {NODE_FLOAT_COMPARE_LESS_EQUAL, "LESS_EQUAL", 0, - "A <= B", + "Less Than or Equal", "True when the first input is smaller than the second input or equal"}, {NODE_FLOAT_COMPARE_GREATER_THAN, "GREATER_THAN", 0, - "A > B", + "Greater Than", "True when the first input is greater than the second input"}, {NODE_FLOAT_COMPARE_GREATER_EQUAL, "GREATER_EQUAL", 0, - "A >= B", + "Greater Than or Equal", "True when the first input is greater than the second input or equal"}, {NODE_FLOAT_COMPARE_EQUAL, "EQUAL", 0, - "A = B", + "Equal", "True when both inputs are approximately equal"}, {NODE_FLOAT_COMPARE_NOT_EQUAL, "NOT_EQUAL", 0, - "A != B", + "Not Equal", "True when both inputs are not approximately equal"}, {0, NULL, 0, NULL, NULL}, }; @@ -435,6 +435,14 @@ static const EnumPropertyItem rna_node_geometry_attribute_input_b_items[] = { {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", ""}, + {GEO_NODE_ATTRIBUTE_INPUT_BOOLEAN, "BOOLEAN", 0, "Boolean", ""}, + {0, NULL, 0, NULL, NULL}, +}; #endif #ifdef RNA_RUNTIME @@ -1859,7 +1867,7 @@ static const EnumPropertyItem *itemf_function_check( static bool attribute_random_type_supported(const EnumPropertyItem *item) { - return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3); + return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_BOOL); } static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_type_itemf( bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) @@ -8399,6 +8407,29 @@ static void def_geo_attribute_math(StructRNA *srna) 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); + 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_ui_text(prop, "Input Type B", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index d09b1a8534d..3b2987d8fa8 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -138,6 +138,7 @@ set(SRC function/nodes/node_fn_switch.cc function/node_function_util.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_common.cc @@ -148,6 +149,7 @@ set(SRC geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_point_distribute.cc geometry/nodes/node_geo_point_instance.cc + geometry/nodes/node_geo_point_separate.cc geometry/nodes/node_geo_random_attribute.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 6433841582b..12042446295 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_random_attribute(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); #ifdef __cplusplus } diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index a7df4bc3e1b..805f843c162 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -26,6 +26,8 @@ namespace blender::nodes { +using bke::BooleanReadAttribute; +using bke::BooleanWriteAttribute; using bke::Color4fReadAttribute; using bke::Color4fWriteAttribute; using bke::Float3ReadAttribute; @@ -168,10 +170,21 @@ class GeoNodeExecParams { return this->get_input_attribute(name, component, domain, type, &default_value); } + /** + * Get the type of an input property or the associated constant socket types. + * Fall back to the default value if no attribute exists with the name. + */ + CustomDataType get_input_attribute_data_type(const StringRef name, + const GeometryComponent &component, + const CustomDataType default_type) const; + private: /* Utilities for detecting common errors at when using this class. */ void check_extract_input(StringRef identifier, const CPPType *requested_type = nullptr) const; void check_set_output(StringRef identifier, const CPPType &value_type) const; + + /* Finds a socket with the name (not identifier) that is currently active. */ + const bNodeSocket *find_available_socket(const StringRef name) const; }; } // namespace blender::nodes 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 8ca978d1339..90a5ddc54dc 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -278,6 +278,8 @@ DefNode(GeometryNode, GEO_NODE_RANDOM_ATTRIBUTE, def_geo_random_attribute, "RAND DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "") +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..f9ded0e1995 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_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_PROP_INT32: + return 1; + 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_lowest_complexity(Span<CustomDataType> data_types) +{ + int lowest_complexity = INT_MAX; + CustomDataType least_complex_type = CD_PROP_BOOL; + + for (const CustomDataType data_type : data_types) { + const int complexity = attribute_data_type_complexity(data_type); + if (complexity < lowest_complexity) { + lowest_complexity = complexity; + least_complex_type = data_type; + } + } + + return least_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 ec389961615..b8728016230 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -42,4 +42,7 @@ namespace blender::nodes { void update_attribute_input_socket_availabilities(bNode &node, const StringRef name, const GeometryNodeAttributeInputMode mode); -} + +CustomDataType attribute_domain_lowest_complexity(Span<CustomDataType>); + +} // namespace blender::nodes
\ No newline at end of file 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..dc1fe858b07 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc @@ -0,0 +1,359 @@ +/* + * 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_is_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_epsilon = (bNodeSocket *)BLI_findlink(&node->inputs, 9); + nodeSetSocketAvailability(socket_epsilon, operation_is_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 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_v3v3(a, b) < threshold; + } +} + +static void do_equal_operation(const Color4fReadAttribute &input_a, + const Color4fReadAttribute &input_b, + const float threshold, + MutableSpan<bool> span_result) +{ + 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] = sqrtf(len_squared_v4v4(a, b)) < threshold; + } +} + +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 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_v3v3(a, b) >= threshold; + } +} + +static void do_not_equal_operation(const Color4fReadAttribute &input_a, + const Color4fReadAttribute &input_b, + const float threshold, + MutableSpan<bool> span_result) +{ + 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] = sqrtf(len_squared_v4v4(a, b)) >= threshold; + } +} + +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_is_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 lower + * complexity attribute type because any other information would be arbitrary anyway.*/ + return attribute_domain_lowest_complexity(Span<CustomDataType>{data_type_a, data_type_b}); + } + + /* Use float compare for every operation besides equality. (This might have to change). */ + 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 can use* implicit conversions and float comparison. */ + if (operation_is_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..088bb387316 --- /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.as_span()); +} + +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.as_span()); +} + +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/geometry/nodes/node_geo_random_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc index 5cacb96412c..5574a570feb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc @@ -59,6 +59,16 @@ static void geo_node_random_attribute_update(bNodeTree *UNUSED(ntree), bNode *no namespace blender::nodes { +static void randomize_attribute(BooleanWriteAttribute &attribute, RandomNumberGenerator &rng) +{ + MutableSpan<bool> attribute_span = attribute.get_span(); + for (const int i : IndexRange(attribute.size())) { + const bool value = rng.get_float() > 0.5f; + attribute_span[i] = value; + } + attribute.apply_span(); +} + static void randomize_attribute(FloatWriteAttribute &attribute, float min, float max, @@ -121,6 +131,11 @@ static void randomize_attribute(GeometryComponent &component, randomize_attribute(float3_attribute, min_value, max_value, rng); break; } + case CD_PROP_BOOL: { + BooleanWriteAttribute boolean_attribute = std::move(attribute); + randomize_attribute(boolean_attribute, rng); + break; + } default: break; } diff --git a/source/blender/nodes/intern/math_functions.cc b/source/blender/nodes/intern/math_functions.cc index cc5e9547a96..f80e9cdb5a1 100644 --- a/source/blender/nodes/intern/math_functions.cc +++ b/source/blender/nodes/intern/math_functions.cc @@ -114,4 +114,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 diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index a6d9115f01f..eef2c6c9125 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -19,23 +19,31 @@ namespace blender::nodes { -ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name, - const GeometryComponent &component, - const AttributeDomain domain, - const CustomDataType type, - const void *default_value) const +const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const { - const bNodeSocket *found_socket = nullptr; LISTBASE_FOREACH (const bNodeSocket *, socket, &node_.inputs) { if ((socket->flag & SOCK_UNAVAIL) != 0) { continue; } if (name == socket->name) { - found_socket = socket; - break; + return socket; } } - BLI_assert(found_socket != nullptr); + + return nullptr; +} + +ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name, + const GeometryComponent &component, + const AttributeDomain domain, + const CustomDataType type, + const void *default_value) const +{ + const bNodeSocket *found_socket = this->find_available_socket(name); + BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */ + if (found_socket == nullptr) { + return component.attribute_get_constant_for_read(domain, type, default_value); + } if (found_socket->type == SOCK_STRING) { const std::string name = this->get_input<std::string>(found_socket->identifier); @@ -60,6 +68,42 @@ ReadAttributePtr GeoNodeExecParams::get_input_attribute(const StringRef name, return component.attribute_get_constant_for_read(domain, type, default_value); } +CustomDataType GeoNodeExecParams::get_input_attribute_data_type( + const StringRef name, + const GeometryComponent &component, + const CustomDataType default_type) const +{ + const bNodeSocket *found_socket = this->find_available_socket(name); + BLI_assert(found_socket != nullptr); /* There should always be available socket for the name. */ + if (found_socket == nullptr) { + return default_type; + } + + if (found_socket->type == SOCK_STRING) { + const std::string name = this->get_input<std::string>(found_socket->identifier); + ReadAttributePtr attribute = component.attribute_try_get_for_read(name); + if (!attribute) { + return default_type; + } + return attribute->custom_data_type(); + } + if (found_socket->type == SOCK_FLOAT) { + return CD_PROP_FLOAT; + } + if (found_socket->type == SOCK_VECTOR) { + return CD_PROP_FLOAT3; + } + if (found_socket->type == SOCK_RGBA) { + return CD_PROP_COLOR; + } + if (found_socket->type == SOCK_BOOLEAN) { + return CD_PROP_BOOL; + } + + BLI_assert(false); + return default_type; +} + void GeoNodeExecParams::check_extract_input(StringRef identifier, const CPPType *requested_type) const { diff --git a/source/blender/nodes/intern/node_tree_multi_function.cc b/source/blender/nodes/intern/node_tree_multi_function.cc index ec5527a2970..8b55e8f96b9 100644 --- a/source/blender/nodes/intern/node_tree_multi_function.cc +++ b/source/blender/nodes/intern/node_tree_multi_function.cc @@ -208,6 +208,8 @@ static DataTypeConversions create_implicit_conversions() conversions, "float to Color4f", [](float a) { return Color4f(a, a, a, 1.0f); }); add_implicit_conversion<Color4f, float>( conversions, "Color4f to float", [](Color4f a) { return rgb_to_grayscale(a); }); + add_implicit_conversion<float3, bool>( + conversions, "float3 to boolean", [](float3 a) { return a.length() == 0.0f; }); return conversions; } |