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:
Diffstat (limited to 'source/blender/nodes')
-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
9 files changed, 688 insertions, 0 deletions
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