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
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
-rw-r--r--release/scripts/startup/nodeitems_builtins.py1
-rw-r--r--source/blender/blenkernel/BKE_node.h1
-rw-r--r--source/blender/blenkernel/intern/node.c1
-rw-r--r--source/blender/editors/space_node/drawnode.c32
-rw-r--r--source/blender/makesdna/DNA_node_types.h16
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c197
-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
12 files changed, 927 insertions, 48 deletions
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index 22344ffcc41..99187589a3c 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -488,6 +488,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeAttributeFill"),
NodeItem("GeometryNodeAttributeMix"),
NodeItem("GeometryNodeAttributeColorRamp"),
+ NodeItem("GeometryNodeAttributeVectorMath"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
NodeItem("ShaderNodeValToRGB"),
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 89a51afede9..3a4862e2bd1 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -1357,6 +1357,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_POINT_SEPARATE 1014
#define GEO_NODE_ATTRIBUTE_COMPARE 1015
#define GEO_NODE_ROTATE_POINTS 1016
+#define GEO_NODE_ATTRIBUTE_VECTOR_MATH 1017
/** \} */
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index ad90c0fe82e..b675c82419f 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -4730,6 +4730,7 @@ static void registerGeometryNodes(void)
register_node_type_geo_attribute_compare();
register_node_type_geo_attribute_fill();
+ register_node_type_geo_attribute_vector_math();
register_node_type_geo_triangulate();
register_node_type_geo_edge_split();
register_node_type_geo_transform();
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index 4b5ba2af050..74ce0d713bf 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -3228,6 +3228,35 @@ static void node_geometry_buts_attribute_math(uiLayout *layout,
uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE);
}
+static void node_geometry_buts_attribute_vector_math(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ bNode *node = (bNode *)ptr->data;
+ NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage;
+
+ uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE);
+ uiItemR(layout, ptr, "input_type_a", DEFAULT_FLAGS, IFACE_("Type A"), ICON_NONE);
+
+ /* These "use input b / c" checks are copied from the node's code. They could be deduplicated if
+ * the drawing code was moved to the node's file. */
+ if (!ELEM(node_storage->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)) {
+ uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE);
+ }
+ if (ELEM(node_storage->operation, NODE_VECTOR_MATH_WRAP)) {
+ uiItemR(layout, ptr, "input_type_c", DEFAULT_FLAGS, IFACE_("Type C"), ICON_NONE);
+ }
+}
+
static void node_geometry_buts_point_instance(uiLayout *layout,
bContext *UNUSED(C),
PointerRNA *ptr)
@@ -3320,6 +3349,9 @@ static void node_geometry_set_butfunc(bNodeType *ntype)
case GEO_NODE_ATTRIBUTE_MIX:
ntype->draw_buttons = node_geometry_buts_attribute_mix;
break;
+ case GEO_NODE_ATTRIBUTE_VECTOR_MATH:
+ ntype->draw_buttons = node_geometry_buts_attribute_vector_math;
+ break;
case GEO_NODE_POINT_DISTRIBUTE:
ntype->draw_buttons = node_geometry_buts_attribute_point_distribute;
break;
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index f5d27ef3164..70084f52753 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -1108,6 +1108,18 @@ typedef struct NodeAttributeMix {
uint8_t input_type_b;
} NodeAttributeMix;
+typedef struct NodeAttributeVectorMath {
+ /* NodeVectorMathOperation */
+ uint8_t operation;
+
+ /* GeometryNodeAttributeInputMode */
+ uint8_t input_type_a;
+ uint8_t input_type_b;
+ uint8_t input_type_c;
+
+ char _pad[4];
+} NodeAttributeVectorMath;
+
typedef struct NodeAttributeColorRamp {
ColorBand color_ramp;
} NodeAttributeColorRamp;
@@ -1368,7 +1380,7 @@ enum {
};
/* Vector Math node operations. */
-enum {
+typedef enum NodeVectorMathOperation {
NODE_VECTOR_MATH_ADD = 0,
NODE_VECTOR_MATH_SUBTRACT = 1,
NODE_VECTOR_MATH_MULTIPLY = 2,
@@ -1396,7 +1408,7 @@ enum {
NODE_VECTOR_MATH_SINE = 21,
NODE_VECTOR_MATH_COSINE = 22,
NODE_VECTOR_MATH_TANGENT = 23,
-};
+} NodeVectorMathOperation;
/* Boolean math node operations. */
enum {
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 8423c1e58cc..26e11ed047b 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -426,75 +426,81 @@ static const EnumPropertyItem rna_node_geometry_triangulate_ngon_method_items[]
{0, NULL, 0, NULL, NULL},
};
-# define ITEM_ATTRIBUTE \
- { \
- GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", "" \
- }
-# define ITEM_FLOAT \
- { \
- GEO_NODE_ATTRIBUTE_INPUT_FLOAT, "FLOAT", 0, "Float", "" \
- }
-# define ITEM_VECTOR \
- { \
- GEO_NODE_ATTRIBUTE_INPUT_VECTOR, "VECTOR", 0, "Vector", "" \
- }
-# define ITEM_COLOR \
- { \
- GEO_NODE_ATTRIBUTE_INPUT_COLOR, "COLOR", 0, "Color", "" \
- }
-# define ITEM_BOOLEAN \
- { \
- GEO_NODE_ATTRIBUTE_INPUT_BOOLEAN, "BOOLEAN", 0, "Boolean", "" \
- }
+static const EnumPropertyItem rna_node_geometry_point_distribute_method_items[] = {
+ {GEO_NODE_POINT_DISTRIBUTE_RANDOM,
+ "RANDOM",
+ 0,
+ "Random",
+ "Distribute points randomly on the surface"},
+ {GEO_NODE_POINT_DISTRIBUTE_POISSON,
+ "POISSON",
+ 0,
+ "Poisson Disk",
+ "Project points on the surface evenly with a Poisson disk distribution"},
+ {0, NULL, 0, NULL, NULL},
+};
-static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_float[] = {
+#endif
+
+#define ITEM_ATTRIBUTE \
+ { \
+ GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", "" \
+ }
+#define ITEM_FLOAT \
+ { \
+ GEO_NODE_ATTRIBUTE_INPUT_FLOAT, "FLOAT", 0, "Float", "" \
+ }
+#define ITEM_VECTOR \
+ { \
+ GEO_NODE_ATTRIBUTE_INPUT_VECTOR, "VECTOR", 0, "Vector", "" \
+ }
+#define ITEM_COLOR \
+ { \
+ GEO_NODE_ATTRIBUTE_INPUT_COLOR, "COLOR", 0, "Color", "" \
+ }
+#define ITEM_BOOLEAN \
+ { \
+ GEO_NODE_ATTRIBUTE_INPUT_BOOLEAN, "BOOLEAN", 0, "Boolean", "" \
+ }
+
+/* Used in both runtime and static code. */
+static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_any[] = {
ITEM_ATTRIBUTE,
ITEM_FLOAT,
+ ITEM_VECTOR,
+ ITEM_COLOR,
+ ITEM_BOOLEAN,
{0, NULL, 0, NULL, NULL},
};
+
+#ifndef RNA_RUNTIME
+
static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_vector[] = {
ITEM_ATTRIBUTE,
ITEM_VECTOR,
{0, NULL, 0, NULL, NULL},
};
-static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_no_boolean[] = {
+static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_float[] = {
ITEM_ATTRIBUTE,
ITEM_FLOAT,
- ITEM_VECTOR,
- ITEM_COLOR,
{0, NULL, 0, NULL, NULL},
};
-static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_any[] = {
+static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_no_boolean[] = {
ITEM_ATTRIBUTE,
ITEM_FLOAT,
ITEM_VECTOR,
ITEM_COLOR,
- ITEM_BOOLEAN,
- {0, NULL, 0, NULL, NULL},
-};
-
-# undef ITEM_ATTRIBUTE
-# undef ITEM_FLOAT
-# undef ITEM_VECTOR
-# undef ITEM_COLOR
-# undef ITEM_BOOLEAN
-
-static const EnumPropertyItem rna_node_geometry_point_distribute_method_items[] = {
- {GEO_NODE_POINT_DISTRIBUTE_RANDOM,
- "RANDOM",
- 0,
- "Random",
- "Distribute points randomly on the surface"},
- {GEO_NODE_POINT_DISTRIBUTE_POISSON,
- "POISSON",
- 0,
- "Poisson Disk",
- "Project points on the surface evenly with a Poisson disk distribution"},
{0, NULL, 0, NULL, NULL},
};
#endif
+#undef ITEM_ATTRIBUTE
+#undef ITEM_FLOAT
+#undef ITEM_VECTOR
+#undef ITEM_COLOR
+#undef ITEM_BOOLEAN
+
#ifdef RNA_RUNTIME
# include "BLI_linklist.h"
@@ -521,6 +527,8 @@ static const EnumPropertyItem rna_node_geometry_point_distribute_method_items[]
# include "DNA_scene_types.h"
# include "WM_api.h"
+static void rna_Node_socket_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr);
+
int rna_node_tree_type_to_enum(bNodeTreeType *typeinfo)
{
int i = 0, result = -1;
@@ -1971,6 +1979,69 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeMath_operation_itemf(
return itemf_function_check(rna_enum_node_math_items, attribute_math_operation_supported);
}
+/**
+ * This bit of ugly code makes sure the float / attribute option shows up instead of
+ * vector / attribute if the node uses an operation that uses a float for input B.
+ */
+static const EnumPropertyItem *rna_GeometryNodeAttributeVectorMath_input_type_b_itemf(
+ bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ bNode *node = ptr->data;
+ NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage;
+
+ EnumPropertyItem *item_array = NULL;
+ int items_len = 0;
+ for (const EnumPropertyItem *item = rna_node_geometry_attribute_input_type_items_any;
+ item->identifier != NULL;
+ item++) {
+ if (item->value == GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE) {
+ RNA_enum_item_add(&item_array, &items_len, item);
+ }
+ else if (item->value == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) {
+ if (node_storage->operation == NODE_VECTOR_MATH_SCALE) {
+ RNA_enum_item_add(&item_array, &items_len, item);
+ }
+ }
+ else if (item->value == GEO_NODE_ATTRIBUTE_INPUT_VECTOR) {
+ if (node_storage->operation != NODE_VECTOR_MATH_SCALE) {
+ RNA_enum_item_add(&item_array, &items_len, item);
+ }
+ }
+ }
+ RNA_enum_item_end(&item_array, &items_len);
+
+ *r_free = true;
+ return item_array;
+}
+
+static void rna_GeometryNodeAttributeVectorMath_operation_update(Main *bmain,
+ Scene *scene,
+ PointerRNA *ptr)
+{
+ bNode *node = ptr->data;
+ NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage;
+
+ const NodeVectorMathOperation operation = (NodeVectorMathOperation)node_storage->operation;
+
+ /* The scale operation can't use a vector input, so reset
+ * the input type enum in case it's set to vector. */
+ if (operation == NODE_VECTOR_MATH_SCALE) {
+ if (node_storage->input_type_b == GEO_NODE_ATTRIBUTE_INPUT_VECTOR) {
+ node_storage->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_FLOAT;
+ }
+ }
+
+ /* Scale is also the only operation that uses the float input type, so a
+ * a check is also necessary for the other direction. */
+ if (operation != NODE_VECTOR_MATH_SCALE) {
+ if (node_storage->input_type_b == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) {
+ node_storage->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_VECTOR;
+ }
+ }
+
+ rna_Node_socket_update(bmain, scene, ptr);
+}
+
static StructRNA *rna_ShaderNode_register(Main *bmain,
ReportList *reports,
void *data,
@@ -8503,6 +8574,40 @@ 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_vector_math(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeAttributeVectorMath", "storage");
+
+ prop = RNA_def_property(srna, "operation", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "operation");
+ RNA_def_property_enum_items(prop, rna_enum_node_vec_math_items);
+ RNA_def_property_ui_text(prop, "Operation", "");
+ RNA_def_property_update(
+ prop, NC_NODE | NA_EDITED, "rna_GeometryNodeAttributeVectorMath_operation_update");
+
+ prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_bitflag_sdna(prop, NULL, "input_type_a");
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
+ 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_bitflag_sdna(prop, NULL, "input_type_b");
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_any);
+ RNA_def_property_enum_funcs(
+ prop, NULL, NULL, "rna_GeometryNodeAttributeVectorMath_input_type_b_itemf");
+ RNA_def_property_ui_text(prop, "Input Type B", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "input_type_c", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_bitflag_sdna(prop, NULL, "input_type_b");
+ RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector);
+ RNA_def_property_ui_text(prop, "Input Type B", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
static void def_geo_point_instance(StructRNA *srna)
{
static const EnumPropertyItem instance_type_items[] = {
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