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:
authorHans Goudey <h.goudey@me.com>2021-01-11 21:06:52 +0300
committerHans Goudey <h.goudey@me.com>2021-01-11 21:06:52 +0300
commitecdbd83a8d30b982c4280f0dbd8ed821c657fa25 (patch)
tree9ecb883d232d9b18d44af4451b20aacb3fda7207 /source/blender/nodes
parent5bd822dc46f60da4b7de125f2d559fa16d781787 (diff)
Geometry Nodes: Attribute Vector Math Node
This patch implements the same operations and interface as the regular vector math node, but it runs for every element of the attribute. This should expand what's possible with geometry nodes quite a bit. Differential Revision: https://developer.blender.org/D9914
Diffstat (limited to 'source/blender/nodes')
-rw-r--r--source/blender/nodes/CMakeLists.txt1
-rw-r--r--source/blender/nodes/NOD_geometry.h1
-rw-r--r--source/blender/nodes/NOD_math_functions.hh226
-rw-r--r--source/blender/nodes/NOD_static_types.h1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc432
-rw-r--r--source/blender/nodes/intern/math_functions.cc66
6 files changed, 727 insertions, 0 deletions
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 5ab2f5b49ea..de4d23d8577 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -146,6 +146,7 @@ set(SRC
geometry/nodes/node_geo_attribute_math.cc
geometry/nodes/node_geo_attribute_mix.cc
geometry/nodes/node_geo_attribute_randomize.cc
+ geometry/nodes/node_geo_attribute_vector_math.cc
geometry/nodes/node_geo_boolean.cc
geometry/nodes/node_geo_common.cc
geometry/nodes/node_geo_edge_split.cc
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 4653c9aae3f..93123c9a684 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -27,6 +27,7 @@ void register_node_tree_type_geo(void);
void register_node_type_geo_group(void);
void register_node_type_geo_attribute_fill(void);
+void register_node_type_geo_attribute_vector_math(void);
void register_node_type_geo_boolean(void);
void register_node_type_geo_edge_split(void);
void register_node_type_geo_transform(void);
diff --git a/source/blender/nodes/NOD_math_functions.hh b/source/blender/nodes/NOD_math_functions.hh
index cc750f9595a..c662220fea7 100644
--- a/source/blender/nodes/NOD_math_functions.hh
+++ b/source/blender/nodes/NOD_math_functions.hh
@@ -18,6 +18,7 @@
#include "DNA_node_types.h"
+#include "BLI_float3.hh"
#include "BLI_math_base_safe.h"
#include "BLI_math_rotation.h"
#include "BLI_string_ref.hh"
@@ -36,6 +37,7 @@ struct FloatMathOperationInfo {
};
const FloatMathOperationInfo *get_float_math_operation_info(const int operation);
+const FloatMathOperationInfo *get_float3_math_operation_info(const int operation);
const FloatMathOperationInfo *get_float_compare_operation_info(const int operation);
/**
@@ -231,4 +233,228 @@ inline bool try_dispatch_float_math_fl_fl_to_bool(const FloatCompareOperation op
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_fl3_fl3_to_fl3(const NodeVectorMathOperation operation,
+ Callback &&callback)
+{
+ const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
+ if (info == nullptr) {
+ return false;
+ }
+
+ /* This is just a utility function to keep the individual cases smaller. */
+ auto dispatch = [&](auto math_function) -> bool {
+ callback(math_function, *info);
+ return true;
+ };
+
+ switch (operation) {
+ case NODE_VECTOR_MATH_ADD:
+ return dispatch([](float3 a, float3 b) { return a + b; });
+ case NODE_VECTOR_MATH_SUBTRACT:
+ return dispatch([](float3 a, float3 b) { return a - b; });
+ case NODE_VECTOR_MATH_MULTIPLY:
+ return dispatch([](float3 a, float3 b) { return a * b; });
+ case NODE_VECTOR_MATH_DIVIDE:
+ return dispatch([](float3 a, float3 b) {
+ return float3(safe_divide(a.x, b.x), safe_divide(a.y, b.y), safe_divide(a.z, b.z));
+ });
+ case NODE_VECTOR_MATH_CROSS_PRODUCT:
+ return dispatch([](float3 a, float3 b) { return float3::cross_high_precision(a, b); });
+ case NODE_VECTOR_MATH_PROJECT:
+ return dispatch([](float3 a, float3 b) {
+ float length_squared = float3::dot(a, b);
+ return (length_squared != 0.0) ? (float3::dot(a, b) / length_squared) * b : float3(0.0f);
+ });
+ case NODE_VECTOR_MATH_REFLECT:
+ return dispatch([](float3 a, float3 b) {
+ b.normalize();
+ return a.reflected(b);
+ });
+ case NODE_VECTOR_MATH_SNAP:
+ return dispatch([](float3 a, float3 b) {
+ return float3(floor(safe_divide(a.x, b.x)),
+ floor(safe_divide(a.y, b.y)),
+ floor(safe_divide(a.z, b.z))) *
+ b;
+ });
+ case NODE_VECTOR_MATH_MODULO:
+ return dispatch([](float3 a, float3 b) {
+ return float3(safe_modf(a.x, b.x), safe_modf(a.y, b.y), safe_modf(a.z, b.z));
+ });
+ case NODE_VECTOR_MATH_MINIMUM:
+ return dispatch([](float3 a, float3 b) {
+ return float3(min_ff(a.x, b.x), min_ff(a.y, b.y), min_ff(a.z, b.z));
+ });
+ case NODE_VECTOR_MATH_MAXIMUM:
+ return dispatch([](float3 a, float3 b) {
+ return float3(max_ff(a.x, b.x), max_ff(a.y, b.y), max_ff(a.z, b.z));
+ });
+ default:
+ return false;
+ }
+ 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_fl3_fl3_to_fl(const NodeVectorMathOperation operation,
+ Callback &&callback)
+{
+ const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
+ if (info == nullptr) {
+ return false;
+ }
+
+ /* This is just a utility function to keep the individual cases smaller. */
+ auto dispatch = [&](auto math_function) -> bool {
+ callback(math_function, *info);
+ return true;
+ };
+
+ switch (operation) {
+ case NODE_VECTOR_MATH_DOT_PRODUCT:
+ return dispatch([](float3 a, float3 b) { return float3::dot(a, b); });
+ case NODE_VECTOR_MATH_DISTANCE:
+ return dispatch([](float3 a, float3 b) { return float3::distance(a, b); });
+ default:
+ return false;
+ }
+ 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_fl3_fl3_fl3_to_fl3(const NodeVectorMathOperation operation,
+ Callback &&callback)
+{
+ const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
+ if (info == nullptr) {
+ return false;
+ }
+
+ /* This is just a utility function to keep the individual cases smaller. */
+ auto dispatch = [&](auto math_function) -> bool {
+ callback(math_function, *info);
+ return true;
+ };
+
+ switch (operation) {
+ case NODE_VECTOR_MATH_WRAP:
+ return dispatch([](float3 a, float3 b, float3 c) {
+ return float3(wrapf(a.x, b.x, c.x), wrapf(a.y, b.y, c.y), wrapf(a.z, b.z, c.z));
+ });
+ default:
+ return false;
+ }
+ 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_fl3_to_fl(const NodeVectorMathOperation operation,
+ Callback &&callback)
+{
+ const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
+ if (info == nullptr) {
+ return false;
+ }
+
+ /* This is just a utility function to keep the individual cases smaller. */
+ auto dispatch = [&](auto math_function) -> bool {
+ callback(math_function, *info);
+ return true;
+ };
+
+ switch (operation) {
+ case NODE_VECTOR_MATH_LENGTH:
+ return dispatch([](float3 in) { return in.length(); });
+ default:
+ return false;
+ }
+ 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_fl3_fl_to_fl3(const NodeVectorMathOperation operation,
+ Callback &&callback)
+{
+ const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
+ if (info == nullptr) {
+ return false;
+ }
+
+ /* This is just a utility function to keep the individual cases smaller. */
+ auto dispatch = [&](auto math_function) -> bool {
+ callback(math_function, *info);
+ return true;
+ };
+
+ switch (operation) {
+ case NODE_VECTOR_MATH_SCALE:
+ return dispatch([](float3 a, float b) { return a * b; });
+ default:
+ return false;
+ }
+ 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_fl3_to_fl3(const NodeVectorMathOperation operation,
+ Callback &&callback)
+{
+ const FloatMathOperationInfo *info = get_float3_math_operation_info(operation);
+ if (info == nullptr) {
+ return false;
+ }
+
+ /* This is just a utility function to keep the individual cases smaller. */
+ auto dispatch = [&](auto math_function) -> bool {
+ callback(math_function, *info);
+ return true;
+ };
+
+ switch (operation) {
+ case NODE_VECTOR_MATH_NORMALIZE:
+ return dispatch([](float3 in) {
+ float3 out = in;
+ out.normalize();
+ return out;
+ }); /* Should be safe. */
+ case NODE_VECTOR_MATH_FLOOR:
+ return dispatch([](float3 in) { return float3(floor(in.x), floor(in.y), floor(in.z)); });
+ case NODE_VECTOR_MATH_CEIL:
+ return dispatch([](float3 in) { return float3(ceil(in.x), ceil(in.y), ceil(in.z)); });
+ case NODE_VECTOR_MATH_FRACTION:
+ return dispatch(
+ [](float3 in) { return in - float3(floor(in.x), floor(in.y), floor(in.z)); });
+ case NODE_VECTOR_MATH_ABSOLUTE:
+ return dispatch([](float3 in) { return float3::abs(in); });
+ case NODE_VECTOR_MATH_SINE:
+ return dispatch([](float3 in) { return float3(sinf(in.x), sinf(in.y), sinf(in.z)); });
+ case NODE_VECTOR_MATH_COSINE:
+ return dispatch([](float3 in) { return float3(cosf(in.x), cosf(in.y), cosf(in.z)); });
+ case NODE_VECTOR_MATH_TANGENT:
+ return dispatch([](float3 in) { return float3(tanf(in.x), tanf(in.y), tanf(in.z)); });
+ 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 2cd08069f6f..55b06cb31a5 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -285,6 +285,7 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ram
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", "")
DefNode(GeometryNode, GEO_NODE_ROTATE_POINTS, def_geo_rotate_points, "ROTATE_POINTS", RotatePoints, "Rotate Points", "")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "ATTRIBUTE_VECTOR_MATH", AttributeVectorMath, "Attribute Vector Math", "")
/* undefine macros */
#undef DefNode
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
new file mode 100644
index 00000000000..0bbd5a07577
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc
@@ -0,0 +1,432 @@
+/*
+ * 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_vector_math_in[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {SOCK_STRING, N_("A")},
+ {SOCK_VECTOR, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_STRING, N_("B")},
+ {SOCK_VECTOR, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_STRING, N_("C")},
+ {SOCK_VECTOR, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX},
+ {SOCK_STRING, N_("Result")},
+ {-1, ""},
+};
+
+static bNodeSocketTemplate geo_node_attribute_vector_math_out[] = {
+ {SOCK_GEOMETRY, N_("Geometry")},
+ {-1, ""},
+};
+
+static void geo_node_attribute_vector_math_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeAttributeVectorMath *data = (NodeAttributeVectorMath *)MEM_callocN(
+ sizeof(NodeAttributeVectorMath), __func__);
+
+ data->operation = NODE_VECTOR_MATH_ADD;
+ data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE;
+ node->storage = data;
+}
+
+static CustomDataType operation_get_read_type_b(const NodeVectorMathOperation operation)
+{
+ if (operation == NODE_VECTOR_MATH_SCALE) {
+ return CD_PROP_FLOAT;
+ }
+ return CD_PROP_FLOAT3;
+}
+
+static bool operation_use_input_b(const NodeVectorMathOperation operation)
+{
+ return !ELEM(operation,
+ NODE_VECTOR_MATH_NORMALIZE,
+ NODE_VECTOR_MATH_FLOOR,
+ NODE_VECTOR_MATH_CEIL,
+ NODE_VECTOR_MATH_FRACTION,
+ NODE_VECTOR_MATH_ABSOLUTE,
+ NODE_VECTOR_MATH_SINE,
+ NODE_VECTOR_MATH_COSINE,
+ NODE_VECTOR_MATH_TANGENT,
+ NODE_VECTOR_MATH_LENGTH);
+}
+
+static bool operation_use_input_c(const NodeVectorMathOperation operation)
+{
+ return operation == NODE_VECTOR_MATH_WRAP;
+}
+
+static CustomDataType operation_get_result_type(const NodeVectorMathOperation operation)
+{
+ switch (operation) {
+ case NODE_VECTOR_MATH_ADD:
+ case NODE_VECTOR_MATH_SUBTRACT:
+ case NODE_VECTOR_MATH_MULTIPLY:
+ case NODE_VECTOR_MATH_DIVIDE:
+ case NODE_VECTOR_MATH_CROSS_PRODUCT:
+ case NODE_VECTOR_MATH_PROJECT:
+ case NODE_VECTOR_MATH_REFLECT:
+ case NODE_VECTOR_MATH_SCALE:
+ case NODE_VECTOR_MATH_NORMALIZE:
+ case NODE_VECTOR_MATH_SNAP:
+ case NODE_VECTOR_MATH_FLOOR:
+ case NODE_VECTOR_MATH_CEIL:
+ case NODE_VECTOR_MATH_MODULO:
+ case NODE_VECTOR_MATH_FRACTION:
+ case NODE_VECTOR_MATH_ABSOLUTE:
+ case NODE_VECTOR_MATH_MINIMUM:
+ case NODE_VECTOR_MATH_MAXIMUM:
+ case NODE_VECTOR_MATH_WRAP:
+ case NODE_VECTOR_MATH_SINE:
+ case NODE_VECTOR_MATH_COSINE:
+ case NODE_VECTOR_MATH_TANGENT:
+ return CD_PROP_FLOAT3;
+ case NODE_VECTOR_MATH_DOT_PRODUCT:
+ case NODE_VECTOR_MATH_DISTANCE:
+ case NODE_VECTOR_MATH_LENGTH:
+ return CD_PROP_FLOAT;
+ }
+
+ BLI_assert(false);
+ return CD_PROP_FLOAT3;
+}
+
+namespace blender::nodes {
+
+static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage;
+ const NodeVectorMathOperation operation = (const NodeVectorMathOperation)node_storage->operation;
+
+ 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,
+ operation_use_input_b(operation));
+ update_attribute_input_socket_availabilities(
+ *node,
+ "C",
+ (GeometryNodeAttributeInputMode)node_storage->input_type_c,
+ operation_use_input_c(operation));
+}
+
+static void do_math_operation_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
+ const Float3ReadAttribute &input_b,
+ Float3WriteAttribute result,
+ const NodeVectorMathOperation operation)
+{
+ const int size = input_a.size();
+
+ Span<float3> span_a = input_a.get_span();
+ Span<float3> span_b = input_b.get_span();
+ MutableSpan<float3> span_result = result.get_span();
+
+ bool success = try_dispatch_float_math_fl3_fl3_to_fl3(
+ operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
+ for (const int i : IndexRange(size)) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float3 out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
+
+ result.apply_span();
+
+ /* The operation is not supported by this node currently. */
+ BLI_assert(success);
+ UNUSED_VARS_NDEBUG(success);
+}
+
+static void do_math_operation_fl3_fl3_fl3_to_fl3(const Float3ReadAttribute &input_a,
+ const Float3ReadAttribute &input_b,
+ const Float3ReadAttribute &input_c,
+ Float3WriteAttribute result,
+ const NodeVectorMathOperation operation)
+{
+ const int size = input_a.size();
+
+ Span<float3> span_a = input_a.get_span();
+ Span<float3> span_b = input_b.get_span();
+ Span<float3> span_c = input_c.get_span();
+ MutableSpan<float3> span_result = result.get_span();
+
+ bool success = try_dispatch_float_math_fl3_fl3_fl3_to_fl3(
+ operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
+ for (const int i : IndexRange(size)) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float3 c = span_c[i];
+ const float3 out = math_function(a, b, c);
+ span_result[i] = out;
+ }
+ });
+
+ result.apply_span();
+
+ /* The operation is not supported by this node currently. */
+ BLI_assert(success);
+ UNUSED_VARS_NDEBUG(success);
+}
+
+static void do_math_operation_fl3_fl3_to_fl(const Float3ReadAttribute &input_a,
+ const Float3ReadAttribute &input_b,
+ FloatWriteAttribute result,
+ const NodeVectorMathOperation operation)
+{
+ const int size = input_a.size();
+
+ Span<float3> span_a = input_a.get_span();
+ Span<float3> span_b = input_b.get_span();
+ MutableSpan<float> span_result = result.get_span();
+
+ bool success = try_dispatch_float_math_fl3_fl3_to_fl(
+ operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
+ for (const int i : IndexRange(size)) {
+ const float3 a = span_a[i];
+ const float3 b = span_b[i];
+ const float out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
+
+ result.apply_span();
+
+ /* The operation is not supported by this node currently. */
+ BLI_assert(success);
+ UNUSED_VARS_NDEBUG(success);
+}
+
+static void do_math_operation_fl3_fl_to_fl3(const Float3ReadAttribute &input_a,
+ const FloatReadAttribute &input_b,
+ Float3WriteAttribute result,
+ const NodeVectorMathOperation operation)
+{
+ const int size = input_a.size();
+
+ Span<float3> span_a = input_a.get_span();
+ Span<float> span_b = input_b.get_span();
+ MutableSpan<float3> span_result = result.get_span();
+
+ bool success = try_dispatch_float_math_fl3_fl_to_fl3(
+ operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
+ for (const int i : IndexRange(size)) {
+ const float3 a = span_a[i];
+ const float b = span_b[i];
+ const float3 out = math_function(a, b);
+ span_result[i] = out;
+ }
+ });
+
+ result.apply_span();
+
+ /* The operation is not supported by this node currently. */
+ BLI_assert(success);
+ UNUSED_VARS_NDEBUG(success);
+}
+
+static void do_math_operation_fl3_to_fl3(const Float3ReadAttribute &input_a,
+ Float3WriteAttribute result,
+ const NodeVectorMathOperation operation)
+{
+ const int size = input_a.size();
+
+ Span<float3> span_a = input_a.get_span();
+ MutableSpan<float3> span_result = result.get_span();
+
+ bool success = try_dispatch_float_math_fl3_to_fl3(
+ operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
+ for (const int i : IndexRange(size)) {
+ const float3 in = span_a[i];
+ const float3 out = math_function(in);
+ span_result[i] = out;
+ }
+ });
+
+ result.apply_span();
+
+ /* The operation is not supported by this node currently. */
+ BLI_assert(success);
+ UNUSED_VARS_NDEBUG(success);
+}
+
+static void do_math_operation_fl3_to_fl(const Float3ReadAttribute &input_a,
+ FloatWriteAttribute result,
+ const NodeVectorMathOperation operation)
+{
+ const int size = input_a.size();
+
+ Span<float3> span_a = input_a.get_span();
+ MutableSpan<float> span_result = result.get_span();
+
+ bool success = try_dispatch_float_math_fl3_to_fl(
+ operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) {
+ for (const int i : IndexRange(size)) {
+ const float3 in = span_a[i];
+ const float out = math_function(in);
+ span_result[i] = out;
+ }
+ });
+
+ result.apply_span();
+
+ /* The operation is not supported by this node currently. */
+ BLI_assert(success);
+ UNUSED_VARS_NDEBUG(success);
+}
+
+static void attribute_vector_math_calc(GeometryComponent &component,
+ const GeoNodeExecParams &params)
+{
+ const bNode &node = params.node();
+ const NodeAttributeVectorMath *node_storage = (const NodeAttributeVectorMath *)node.storage;
+ const NodeVectorMathOperation operation = (NodeVectorMathOperation)node_storage->operation;
+
+ /* The number and type of the input attribute depend on the operation. */
+ const CustomDataType read_type_a = CD_PROP_FLOAT3;
+ const bool use_input_b = operation_use_input_b(operation);
+ const CustomDataType read_type_b = operation_get_read_type_b(operation);
+ const bool use_input_c = operation_use_input_c(operation);
+ const CustomDataType read_type_c = CD_PROP_FLOAT3;
+
+ /* The result domain is always point for now. */
+ const CustomDataType result_type = operation_get_result_type(operation);
+ const AttributeDomain result_domain = ATTR_DOMAIN_POINT;
+
+ ReadAttributePtr attribute_a = params.get_input_attribute(
+ "A", component, result_domain, read_type_a, nullptr);
+ if (!attribute_a) {
+ return;
+ }
+ ReadAttributePtr attribute_b;
+ ReadAttributePtr attribute_c;
+ if (use_input_b) {
+ attribute_b = params.get_input_attribute("B", component, result_domain, read_type_b, nullptr);
+ if (!attribute_b) {
+ return;
+ }
+ }
+ if (use_input_c) {
+ attribute_c = params.get_input_attribute("C", component, result_domain, read_type_c, nullptr);
+ if (!attribute_c) {
+ return;
+ }
+ }
+
+ /* 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;
+ }
+
+ switch (operation) {
+ case NODE_VECTOR_MATH_ADD:
+ case NODE_VECTOR_MATH_SUBTRACT:
+ case NODE_VECTOR_MATH_MULTIPLY:
+ case NODE_VECTOR_MATH_DIVIDE:
+ case NODE_VECTOR_MATH_CROSS_PRODUCT:
+ case NODE_VECTOR_MATH_PROJECT:
+ case NODE_VECTOR_MATH_REFLECT:
+ case NODE_VECTOR_MATH_SNAP:
+ case NODE_VECTOR_MATH_MODULO:
+ case NODE_VECTOR_MATH_MINIMUM:
+ case NODE_VECTOR_MATH_MAXIMUM:
+ do_math_operation_fl3_fl3_to_fl3(
+ std::move(attribute_a), std::move(attribute_b), std::move(attribute_result), operation);
+ break;
+ case NODE_VECTOR_MATH_DOT_PRODUCT:
+ case NODE_VECTOR_MATH_DISTANCE:
+ do_math_operation_fl3_fl3_to_fl(
+ std::move(attribute_a), std::move(attribute_b), std::move(attribute_result), operation);
+ break;
+ case NODE_VECTOR_MATH_LENGTH:
+ do_math_operation_fl3_to_fl(std::move(attribute_a), std::move(attribute_result), operation);
+ break;
+ case NODE_VECTOR_MATH_SCALE:
+ do_math_operation_fl3_fl_to_fl3(
+ std::move(attribute_a), std::move(attribute_b), std::move(attribute_result), operation);
+ break;
+ case NODE_VECTOR_MATH_NORMALIZE:
+ case NODE_VECTOR_MATH_FLOOR:
+ case NODE_VECTOR_MATH_CEIL:
+ case NODE_VECTOR_MATH_FRACTION:
+ case NODE_VECTOR_MATH_ABSOLUTE:
+ case NODE_VECTOR_MATH_SINE:
+ case NODE_VECTOR_MATH_COSINE:
+ case NODE_VECTOR_MATH_TANGENT:
+ do_math_operation_fl3_to_fl3(std::move(attribute_a), std::move(attribute_result), operation);
+ break;
+ case NODE_VECTOR_MATH_WRAP:
+ do_math_operation_fl3_fl3_fl3_to_fl3(std::move(attribute_a),
+ std::move(attribute_b),
+ std::move(attribute_c),
+ std::move(attribute_result),
+ operation);
+ break;
+ }
+}
+
+static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ if (geometry_set.has<MeshComponent>()) {
+ attribute_vector_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params);
+ }
+ if (geometry_set.has<PointCloudComponent>()) {
+ attribute_vector_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(),
+ params);
+ }
+
+ params.set_output("Geometry", geometry_set);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_attribute_vector_math()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_ATTRIBUTE_VECTOR_MATH, "Attribute Vector Math", NODE_CLASS_ATTRIBUTE, 0);
+ node_type_socket_templates(
+ &ntype, geo_node_attribute_vector_math_in, geo_node_attribute_vector_math_out);
+ ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_math_exec;
+ node_type_update(&ntype, blender::nodes::geo_node_attribute_vector_math_update);
+ node_type_init(&ntype, geo_node_attribute_vector_math_init);
+ node_type_storage(
+ &ntype, "NodeAttributeVectorMath", node_free_standard_storage, node_copy_standard_storage);
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/intern/math_functions.cc b/source/blender/nodes/intern/math_functions.cc
index 32a18f1c193..14de2fce9b3 100644
--- a/source/blender/nodes/intern/math_functions.cc
+++ b/source/blender/nodes/intern/math_functions.cc
@@ -146,4 +146,70 @@ const FloatMathOperationInfo *get_float_compare_operation_info(const int operati
return nullptr;
}
+const FloatMathOperationInfo *get_float3_math_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_VECTOR_MATH_ADD:
+ RETURN_OPERATION_INFO("Add", "vector_math_add");
+ case NODE_VECTOR_MATH_SUBTRACT:
+ RETURN_OPERATION_INFO("Subtract", "vector_math_subtract");
+ case NODE_VECTOR_MATH_MULTIPLY:
+ RETURN_OPERATION_INFO("Multiply", "vector_math_multiply");
+ case NODE_VECTOR_MATH_DIVIDE:
+ RETURN_OPERATION_INFO("Divide", "vector_math_divide");
+ case NODE_VECTOR_MATH_CROSS_PRODUCT:
+ RETURN_OPERATION_INFO("Cross Product", "vector_math_cross");
+ case NODE_VECTOR_MATH_PROJECT:
+ RETURN_OPERATION_INFO("Project", "vector_math_project");
+ case NODE_VECTOR_MATH_REFLECT:
+ RETURN_OPERATION_INFO("Reflect", "vector_math_reflect");
+ case NODE_VECTOR_MATH_DOT_PRODUCT:
+ RETURN_OPERATION_INFO("Dot Product", "vector_math_dot");
+ case NODE_VECTOR_MATH_DISTANCE:
+ RETURN_OPERATION_INFO("Distance", "vector_math_distance");
+ case NODE_VECTOR_MATH_LENGTH:
+ RETURN_OPERATION_INFO("Length", "vector_math_length");
+ case NODE_VECTOR_MATH_SCALE:
+ RETURN_OPERATION_INFO("Scale", "vector_math_scale");
+ case NODE_VECTOR_MATH_NORMALIZE:
+ RETURN_OPERATION_INFO("Normalize", "vector_math_normalize");
+ case NODE_VECTOR_MATH_SNAP:
+ RETURN_OPERATION_INFO("Snap", "vector_math_snap");
+ case NODE_VECTOR_MATH_FLOOR:
+ RETURN_OPERATION_INFO("Floor", "vector_math_floor");
+ case NODE_VECTOR_MATH_CEIL:
+ RETURN_OPERATION_INFO("Ceiling", "vector_math_ceil");
+ case NODE_VECTOR_MATH_MODULO:
+ RETURN_OPERATION_INFO("Modulo", "vector_math_modulo");
+ case NODE_VECTOR_MATH_FRACTION:
+ RETURN_OPERATION_INFO("Fraction", "vector_math_fraction");
+ case NODE_VECTOR_MATH_ABSOLUTE:
+ RETURN_OPERATION_INFO("Absolute", "vector_math_absolute");
+ case NODE_VECTOR_MATH_MINIMUM:
+ RETURN_OPERATION_INFO("Minimum", "vector_math_minimum");
+ case NODE_VECTOR_MATH_MAXIMUM:
+ RETURN_OPERATION_INFO("Maximum", "vector_math_maximum");
+ case NODE_VECTOR_MATH_WRAP:
+ RETURN_OPERATION_INFO("Wrap", "vector_math_wrap");
+ case NODE_VECTOR_MATH_SINE:
+ RETURN_OPERATION_INFO("Sine", "vector_math_sine");
+ case NODE_VECTOR_MATH_COSINE:
+ RETURN_OPERATION_INFO("Cosine", "vector_math_cosine");
+ case NODE_VECTOR_MATH_TANGENT:
+ RETURN_OPERATION_INFO("Tangent", "vector_math_tangent");
+ }
+
+#undef RETURN_OPERATION_INFO
+
+ return nullptr;
+}
+
} // namespace blender::nodes