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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/startup/nodeitems_builtins.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