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_attribute_access.hh2
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh2
-rw-r--r--source/blender/blenkernel/BKE_node.h2
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc30
-rw-r--r--source/blender/blenkernel/intern/customdata.c16
-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.h8
-rw-r--r--source/blender/makesdna/DNA_node_types.h16
-rw-r--r--source/blender/makesrna/intern/rna_attribute.c1
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c45
-rw-r--r--source/blender/nodes/CMakeLists.txt2
-rw-r--r--source/blender/nodes/NOD_geometry.h2
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh13
-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.hh5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc359
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_point_separate.cc212
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_random_attribute.cc15
-rw-r--r--source/blender/nodes/intern/math_functions.cc30
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc62
-rw-r--r--source/blender/nodes/intern/node_tree_multi_function.cc2
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 &params,
+ 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 &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 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 &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.as_span());
+}
+
+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.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;
}