diff options
-rw-r--r-- | source/blender/blenkernel/BKE_blender_version.h | 2 | ||||
-rw-r--r-- | source/blender/blenloader/intern/versioning_290.c | 15 | ||||
-rw-r--r-- | source/blender/editors/space_node/drawnode.c | 69 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_node_types.h | 9 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_nodetree.c | 25 | ||||
-rw-r--r-- | source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc | 172 |
6 files changed, 241 insertions, 51 deletions
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 8854949fa11..7c77c6a8f86 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 3 +#define BLENDER_FILE_SUBVERSION 4 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index d1cbf05c0ba..b389c5ed210 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -1630,6 +1630,21 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) FOREACH_NODETREE_END; } + if (!MAIN_VERSION_ATLEAST(bmain, 293, 4)) { + /* Add support for all operations to the "Attribute Math" node. */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_GEOMETRY) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == GEO_NODE_ATTRIBUTE_MATH) { + NodeAttributeMath *data = (NodeAttributeMath *)node->storage; + data->input_type_c = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; + } + } + } + } + FOREACH_NODETREE_END; + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 8f3602c8d50..7905483fac9 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -3166,13 +3166,80 @@ static void node_geometry_buts_random_attribute(uiLayout *layout, uiItemR(layout, ptr, "data_type", DEFAULT_FLAGS, "", ICON_NONE); } +static bool node_attribute_math_operation_use_input_b(const NodeMathOperation operation) +{ + switch (operation) { + case NODE_MATH_ADD: + case NODE_MATH_SUBTRACT: + case NODE_MATH_MULTIPLY: + case NODE_MATH_DIVIDE: + case NODE_MATH_POWER: + case NODE_MATH_LOGARITHM: + case NODE_MATH_MINIMUM: + case NODE_MATH_MAXIMUM: + case NODE_MATH_LESS_THAN: + case NODE_MATH_GREATER_THAN: + case NODE_MATH_MODULO: + case NODE_MATH_ARCTAN2: + case NODE_MATH_SNAP: + case NODE_MATH_WRAP: + case NODE_MATH_COMPARE: + case NODE_MATH_MULTIPLY_ADD: + case NODE_MATH_PINGPONG: + case NODE_MATH_SMOOTH_MIN: + case NODE_MATH_SMOOTH_MAX: + return true; + case NODE_MATH_SINE: + case NODE_MATH_COSINE: + case NODE_MATH_TANGENT: + case NODE_MATH_ARCSINE: + case NODE_MATH_ARCCOSINE: + case NODE_MATH_ARCTANGENT: + case NODE_MATH_ROUND: + case NODE_MATH_ABSOLUTE: + case NODE_MATH_FLOOR: + case NODE_MATH_CEIL: + case NODE_MATH_FRACTION: + case NODE_MATH_SQRT: + case NODE_MATH_INV_SQRT: + case NODE_MATH_SIGN: + case NODE_MATH_EXPONENT: + case NODE_MATH_RADIANS: + case NODE_MATH_DEGREES: + case NODE_MATH_SINH: + case NODE_MATH_COSH: + case NODE_MATH_TANH: + case NODE_MATH_TRUNC: + return false; + } + BLI_assert(false); + return false; +} + static void node_geometry_buts_attribute_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + bNode *node = (bNode *)ptr->data; + NodeAttributeMath *node_storage = (NodeAttributeMath *)node->storage; + NodeMathOperation operation = (NodeMathOperation)node_storage->operation; + uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE); uiItemR(layout, ptr, "input_type_a", DEFAULT_FLAGS, IFACE_("Type A"), ICON_NONE); - uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE); + + /* These "use input b / c" checks are copied from the node's code. + * They could be de-duplicated if the drawing code was moved to the node's file. */ + if (node_attribute_math_operation_use_input_b(operation)) { + uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE); + } + if (ELEM(operation, + NODE_MATH_MULTIPLY_ADD, + NODE_MATH_SMOOTH_MIN, + NODE_MATH_SMOOTH_MAX, + NODE_MATH_WRAP, + NODE_MATH_COMPARE)) { + uiItemR(layout, ptr, "input_type_c", DEFAULT_FLAGS, IFACE_("Type C"), ICON_NONE); + } } static void node_geometry_buts_attribute_vector_math(uiLayout *layout, diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index e26a2861f04..0c92099e23b 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1089,14 +1089,15 @@ typedef struct NodeAttributeCompare { } NodeAttributeCompare; typedef struct NodeAttributeMath { - /* e.g. NODE_MATH_ADD. */ + /* NodeMathOperation. */ uint8_t operation; /* GeometryNodeAttributeInputMode */ uint8_t input_type_a; uint8_t input_type_b; + uint8_t input_type_c; - char _pad[5]; + char _pad[4]; } NodeAttributeMath; typedef struct NodeAttributeMix { @@ -1387,7 +1388,7 @@ enum { #define SHD_MATH_CLAMP 1 /* Math node operations. */ -enum { +typedef enum NodeMathOperation { NODE_MATH_ADD = 0, NODE_MATH_SUBTRACT = 1, NODE_MATH_MULTIPLY = 2, @@ -1428,7 +1429,7 @@ enum { NODE_MATH_PINGPONG = 37, NODE_MATH_SMOOTH_MIN = 38, NODE_MATH_SMOOTH_MAX = 39, -}; +} NodeMathOperation; /* Vector Math node operations. */ typedef enum NodeVectorMathOperation { diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index db9c774bf9b..90badae2e8e 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -1980,22 +1980,6 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeFill_domain_itemf( return itemf_function_check(rna_enum_attribute_domain_items, attribute_fill_domain_supported); } -static bool attribute_math_operation_supported(const EnumPropertyItem *item) -{ - return ELEM(item->value, - NODE_MATH_ADD, - NODE_MATH_SUBTRACT, - NODE_MATH_MULTIPLY, - NODE_MATH_DIVIDE) && - (item->identifier[0] != '\0'); -} -static const EnumPropertyItem *rna_GeometryNodeAttributeMath_operation_itemf( - bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) -{ - *r_free = true; - 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. @@ -8579,10 +8563,9 @@ static void def_geo_attribute_math(StructRNA *srna) 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_math_items); - RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeMath_operation_itemf"); RNA_def_property_enum_default(prop, NODE_MATH_ADD); RNA_def_property_ui_text(prop, "Operation", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); prop = RNA_def_property(srna, "input_type_a", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "input_type_a"); @@ -8595,6 +8578,12 @@ static void def_geo_attribute_math(StructRNA *srna) RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); 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_c"); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); + RNA_def_property_ui_text(prop, "Input Type C", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } static void def_geo_attribute_vector_math(StructRNA *srna) diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc index f8ec9124db3..f3fc45fc1be 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc @@ -34,6 +34,8 @@ static bNodeSocketTemplate geo_node_attribute_math_in[] = { {SOCK_FLOAT, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, {SOCK_STRING, N_("B")}, {SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, + {SOCK_STRING, N_("C")}, + {SOCK_FLOAT, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, {SOCK_STRING, N_("Result")}, {-1, ""}, }; @@ -51,45 +53,132 @@ static void geo_node_attribute_math_init(bNodeTree *UNUSED(tree), bNode *node) data->operation = NODE_MATH_ADD; data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; + data->input_type_c = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; node->storage = data; } +static bool operation_use_input_c(const NodeMathOperation operation) +{ + return ELEM(operation, + NODE_MATH_MULTIPLY_ADD, + NODE_MATH_SMOOTH_MIN, + NODE_MATH_SMOOTH_MAX, + NODE_MATH_WRAP, + NODE_MATH_COMPARE); +} + +static bool operation_use_input_b(const NodeMathOperation operation) +{ + switch (operation) { + case NODE_MATH_ADD: + case NODE_MATH_SUBTRACT: + case NODE_MATH_MULTIPLY: + case NODE_MATH_DIVIDE: + case NODE_MATH_POWER: + case NODE_MATH_LOGARITHM: + case NODE_MATH_MINIMUM: + case NODE_MATH_MAXIMUM: + case NODE_MATH_LESS_THAN: + case NODE_MATH_GREATER_THAN: + case NODE_MATH_MODULO: + case NODE_MATH_ARCTAN2: + case NODE_MATH_SNAP: + case NODE_MATH_WRAP: + case NODE_MATH_COMPARE: + case NODE_MATH_MULTIPLY_ADD: + case NODE_MATH_PINGPONG: + case NODE_MATH_SMOOTH_MIN: + case NODE_MATH_SMOOTH_MAX: + return true; + case NODE_MATH_SINE: + case NODE_MATH_COSINE: + case NODE_MATH_TANGENT: + case NODE_MATH_ARCSINE: + case NODE_MATH_ARCCOSINE: + case NODE_MATH_ARCTANGENT: + case NODE_MATH_ROUND: + case NODE_MATH_ABSOLUTE: + case NODE_MATH_FLOOR: + case NODE_MATH_CEIL: + case NODE_MATH_FRACTION: + case NODE_MATH_SQRT: + case NODE_MATH_INV_SQRT: + case NODE_MATH_SIGN: + case NODE_MATH_EXPONENT: + case NODE_MATH_RADIANS: + case NODE_MATH_DEGREES: + case NODE_MATH_SINH: + case NODE_MATH_COSH: + case NODE_MATH_TANH: + case NODE_MATH_TRUNC: + return false; + } + BLI_assert(false); + return false; +} + namespace blender::nodes { static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node) { - NodeAttributeMath *node_storage = (NodeAttributeMath *)node->storage; + NodeAttributeMath &node_storage = *(NodeAttributeMath *)node->storage; + NodeMathOperation operation = static_cast<NodeMathOperation>(node_storage.operation); update_attribute_input_socket_availabilities( - *node, "A", (GeometryNodeAttributeInputMode)node_storage->input_type_a); + *node, "A", (GeometryNodeAttributeInputMode)node_storage.input_type_a); update_attribute_input_socket_availabilities( - *node, "B", (GeometryNodeAttributeInputMode)node_storage->input_type_b); + *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(const FloatReadAttribute &input_a, - const FloatReadAttribute &input_b, - FloatWriteAttribute result, - const int operation) +static void do_math_operation(Span<float> span_a, + Span<float> span_b, + Span<float> span_c, + MutableSpan<float> span_result, + const NodeMathOperation operation) { - const int size = input_a.size(); - - Span<float> span_a = input_a.get_span(); - Span<float> span_b = input_b.get_span(); - MutableSpan<float> span_result = result.get_span_for_write_only(); + bool success = try_dispatch_float_math_fl_fl_fl_to_fl( + operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { + for (const int i : IndexRange(span_result.size())) { + span_result[i] = math_function(span_a[i], span_b[i], span_c[i]); + } + }); + BLI_assert(success); + UNUSED_VARS_NDEBUG(success); +} +static void do_math_operation(Span<float> span_a, + Span<float> span_b, + MutableSpan<float> span_result, + const NodeMathOperation operation) +{ bool success = try_dispatch_float_math_fl_fl_to_fl( operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { - for (const int i : IndexRange(size)) { - const float in1 = span_a[i]; - const float in2 = span_b[i]; - const float out = math_function(in1, in2); - span_result[i] = out; + for (const int i : IndexRange(span_result.size())) { + span_result[i] = math_function(span_a[i], span_b[i]); } }); + BLI_assert(success); + UNUSED_VARS_NDEBUG(success); +} - result.apply_span(); - - /* The operation is not supported by this node currently. */ +static void do_math_operation(Span<float> span_input, + MutableSpan<float> span_result, + const NodeMathOperation operation) +{ + bool success = try_dispatch_float_math_fl_to_fl( + operation, [&](auto math_function, const FloatMathOperationInfo &UNUSED(info)) { + for (const int i : IndexRange(span_result.size())) { + span_result[i] = math_function(span_input[i]); + } + }); BLI_assert(success); UNUSED_VARS_NDEBUG(success); } @@ -98,7 +187,7 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP { const bNode &node = params.node(); const NodeAttributeMath *node_storage = (const NodeAttributeMath *)node.storage; - const int operation = node_storage->operation; + const NodeMathOperation operation = static_cast<NodeMathOperation>(node_storage->operation); /* The result type of this node is always float. */ const CustomDataType result_type = CD_PROP_FLOAT; @@ -115,15 +204,44 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP ReadAttributePtr attribute_a = params.get_input_attribute( "A", component, result_domain, result_type, nullptr); - ReadAttributePtr attribute_b = params.get_input_attribute( - "B", component, result_domain, result_type, nullptr); - if (!attribute_a || !attribute_b) { - /* Attribute wasn't found. */ + if (!attribute_a) { return; } - do_math_operation(*attribute_a, *attribute_b, *attribute_result, operation); - attribute_result.save(); + /* Note that passing the data with `get_span<float>()` works + * because the attributes were accessed with #CD_PROP_FLOAT. */ + if (operation_use_input_b(operation)) { + ReadAttributePtr attribute_b = params.get_input_attribute( + "B", component, result_domain, result_type, nullptr); + if (!attribute_b) { + return; + } + if (operation_use_input_c(operation)) { + ReadAttributePtr attribute_c = params.get_input_attribute( + "C", component, result_domain, result_type, nullptr); + if (!attribute_c) { + return; + } + do_math_operation(attribute_a->get_span<float>(), + attribute_b->get_span<float>(), + attribute_c->get_span<float>(), + attribute_result->get_span_for_write_only<float>(), + operation); + } + else { + do_math_operation(attribute_a->get_span<float>(), + attribute_b->get_span<float>(), + attribute_result->get_span_for_write_only<float>(), + operation); + } + } + else { + do_math_operation(attribute_a->get_span<float>(), + attribute_result->get_span_for_write_only<float>(), + operation); + } + + attribute_result.apply_span_and_save(); } static void geo_node_attribute_math_exec(GeoNodeExecParams params) |