Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/nodeitems_builtins.py2
-rw-r--r--source/blender/blenkernel/BKE_node.h2
-rw-r--r--source/blender/blenkernel/intern/node.c2
-rw-r--r--source/blender/editors/space_node/drawnode.c12
-rw-r--r--source/blender/makesdna/DNA_customdata_types.h2
-rw-r--r--source/blender/makesdna/DNA_node_types.h16
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c81
-rw-r--r--source/blender/nodes/CMakeLists.txt2
-rw-r--r--source/blender/nodes/NOD_geometry.h2
-rw-r--r--source/blender/nodes/NOD_math_functions.hh34
-rw-r--r--source/blender/nodes/NOD_static_types.h2
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.cc42
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc362
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_separate.cc212
-rw-r--r--source/blender/nodes/intern/math_functions.cc30
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 &params,
+ 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 &params)
+{
+ 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 &params,
+ 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 &params,
+ 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 &params,
+ 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