diff options
Diffstat (limited to 'source/blender')
16 files changed, 310 insertions, 46 deletions
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 91d46e3951f..222aa559e8a 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -222,6 +222,10 @@ class GeoNodeExecParams { const GeometryComponent &component, const CustomDataType default_type) const; + AttributeDomain get_highest_priority_input_domain(Span<std::string> names, + const GeometryComponent &component, + const AttributeDomain default_domain) const; + private: /* Utilities for detecting common errors at when using this class. */ void check_extract_input(StringRef identifier, const CPPType *requested_type = nullptr) const; diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc index 95c25795356..3e30658e056 100644 --- a/source/blender/nodes/geometry/node_geometry_util.cc +++ b/source/blender/nodes/geometry/node_geometry_util.cc @@ -338,6 +338,52 @@ CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_ return most_complex_type; } +/** + * \note Generally the order should mirror the order of the domains + * established in each component's ComponentAttributeProviders. + */ +static int attribute_domain_priority(const AttributeDomain domain) +{ + switch (domain) { +#if 0 + case ATTR_DOMAIN_CURVE: + return 0; +#endif + case ATTR_DOMAIN_POLYGON: + return 1; + case ATTR_DOMAIN_EDGE: + return 2; + case ATTR_DOMAIN_POINT: + return 3; + case ATTR_DOMAIN_CORNER: + return 4; + default: + /* Domain not supported in nodes yet. */ + BLI_assert(false); + return 0; + } +} + +/** + * Domains with a higher "information density" have a higher priority, in order + * to choose a domain that will not lose data through domain conversion. + */ +AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains) +{ + int highest_priority = INT_MIN; + AttributeDomain highest_priority_domain = ATTR_DOMAIN_CORNER; + + for (const AttributeDomain domain : domains) { + const int priority = attribute_domain_priority(domain); + if (priority > highest_priority) { + highest_priority = priority; + highest_priority_domain = domain; + } + } + + return highest_priority_domain; +} + } // namespace blender::nodes bool geo_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree) diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 7ca44d82a38..13a58be86f6 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -45,6 +45,7 @@ void update_attribute_input_socket_availabilities(bNode &node, const bool name_is_available = true); CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType>); +AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains); Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &component, const AttributeDomain domain); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc index fc5fb4c5488..0309fb83bd7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc @@ -42,27 +42,44 @@ static void geo_node_attribute_color_ramp_layout(uiLayout *layout, namespace blender::nodes { +static AttributeDomain get_result_domain(const GeometryComponent &component, + StringRef input_name, + StringRef result_name) +{ + /* Use the domain of the result attribute if it already exists. */ + ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); + if (result_attribute) { + return result_attribute->domain(); + } + + /* Otherwise use the input attribute's domain if it exists. */ + ReadAttributePtr input_attribute = component.attribute_try_get_for_read(input_name); + if (input_attribute) { + return input_attribute->domain(); + } + + return ATTR_DOMAIN_POINT; +} + static void execute_on_component(const GeoNodeExecParams ¶ms, GeometryComponent &component) { const bNode &bnode = params.node(); NodeAttributeColorRamp *node_storage = (NodeAttributeColorRamp *)bnode.storage; + const std::string result_name = params.get_input<std::string>("Result"); + const std::string input_name = params.get_input<std::string>("Attribute"); /* Always output a color attribute for now. We might want to allow users to customize. * Using the type of an existing attribute could work, but does not have a real benefit * currently. */ const CustomDataType result_type = CD_PROP_COLOR; + const AttributeDomain result_domain = get_result_domain(component, input_name, result_name); - const std::string result_name = params.get_input<std::string>("Result"); - /* Once we support more domains at the user level, we have to decide how the result domain is - * chosen. */ - const AttributeDomain result_domain = ATTR_DOMAIN_POINT; OutputAttributePtr attribute_result = component.attribute_try_get_for_output( result_name, result_domain, result_type); if (!attribute_result) { return; } - const std::string input_name = params.get_input<std::string>("Attribute"); FloatReadAttribute attribute_in = component.attribute_get_for_read<float>( input_name, result_domain, 0.0f); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc index a231b4f9e92..5214d938fb1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc @@ -69,14 +69,27 @@ static void geo_node_attribute_combine_xyz_update(bNodeTree *UNUSED(ntree), bNod *node, "Z", (GeometryNodeAttributeInputMode)node_storage->input_type_z); } +static AttributeDomain get_result_domain(const GeometryComponent &component, + const GeoNodeExecParams ¶ms, + StringRef result_name) +{ + /* Use the domain of the result attribute if it already exists. */ + ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); + if (result_attribute) { + return result_attribute->domain(); + } + + /* Otherwise use the highest priority domain from existing input attributes, or the default. */ + return params.get_highest_priority_input_domain({"X", "Y", "Z"}, component, ATTR_DOMAIN_POINT); +} + static void combine_attributes(GeometryComponent &component, const GeoNodeExecParams ¶ms) { const std::string result_name = params.get_input<std::string>("Result"); - /* The result domain is always point for now. */ - const AttributeDomain result_domain = ATTR_DOMAIN_POINT; if (result_name.empty()) { return; } + const AttributeDomain result_domain = get_result_domain(component, params, result_name); OutputAttributePtr attribute_result = component.attribute_try_get_for_output( result_name, result_domain, CD_PROP_FLOAT3); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc index ee97102de7b..a6cd0dd7779 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc @@ -240,20 +240,31 @@ static CustomDataType get_data_type(GeometryComponent &component, return CD_PROP_FLOAT; } +static AttributeDomain get_result_domain(const GeometryComponent &component, + const GeoNodeExecParams ¶ms, + StringRef result_name) +{ + /* Use the domain of the result attribute if it already exists. */ + ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); + if (result_attribute) { + return result_attribute->domain(); + } + + /* Otherwise use the highest priority domain from existing input attributes, or the default. */ + return params.get_highest_priority_input_domain({"A", "B"}, component, ATTR_DOMAIN_POINT); +} + static void attribute_compare_calc(GeometryComponent &component, const GeoNodeExecParams ¶ms) { const bNode &node = params.node(); NodeAttributeCompare *node_storage = (NodeAttributeCompare *)node.storage; const FloatCompareOperation operation = static_cast<FloatCompareOperation>( node_storage->operation); + const std::string result_name = params.get_input<std::string>("Result"); - /* The result type of this node is always float. */ const CustomDataType result_type = CD_PROP_BOOL; - /* The result domain is always point for now. */ - const AttributeDomain result_domain = ATTR_DOMAIN_POINT; + const AttributeDomain result_domain = get_result_domain(component, params, result_name); - /* 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"); OutputAttributePtr attribute_result = component.attribute_try_get_for_output( result_name, result_domain, result_type); if (!attribute_result) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc index a5ac1926963..d2a7e40877f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc @@ -67,16 +67,32 @@ static void geo_node_attribute_fill_update(bNodeTree *UNUSED(ntree), bNode *node namespace blender::nodes { -static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams ¶ms) +static AttributeDomain get_result_domain(const GeometryComponent &component, + const GeoNodeExecParams ¶ms, + StringRef attribute_name) { + /* Use the domain of the result attribute if it already exists. */ + ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name); + if (result_attribute) { + return result_attribute->domain(); + } + + /* Otherwise use the input domain chosen in the interface. */ const bNode &node = params.node(); - const CustomDataType data_type = static_cast<CustomDataType>(node.custom1); - const AttributeDomain domain = static_cast<AttributeDomain>(node.custom2); + return static_cast<AttributeDomain>(node.custom2); +} + +static void fill_attribute(GeometryComponent &component, const GeoNodeExecParams ¶ms) +{ const std::string attribute_name = params.get_input<std::string>("Attribute"); if (attribute_name.empty()) { return; } + const bNode &node = params.node(); + const CustomDataType data_type = static_cast<CustomDataType>(node.custom1); + const AttributeDomain domain = get_result_domain(component, params, attribute_name); + OutputAttributePtr attribute = component.attribute_try_get_for_output( attribute_name, domain, data_type); if (!attribute) { 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 1780ec69df2..17718949de3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc @@ -202,19 +202,40 @@ static void do_math_operation(Span<float> span_input, UNUSED_VARS_NDEBUG(success); } +static AttributeDomain get_result_domain(const GeometryComponent &component, + const GeoNodeExecParams ¶ms, + const NodeMathOperation operation, + StringRef result_name) +{ + /* Use the domain of the result attribute if it already exists. */ + ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); + if (result_attribute) { + return result_attribute->domain(); + } + + /* Otherwise use the highest priority domain from existing input attributes, or the default. */ + const AttributeDomain default_domain = ATTR_DOMAIN_POINT; + if (operation_use_input_b(operation)) { + if (operation_use_input_c(operation)) { + return params.get_highest_priority_input_domain({"A", "B", "C"}, component, default_domain); + } + return params.get_highest_priority_input_domain({"A", "B"}, component, default_domain); + } + return params.get_highest_priority_input_domain({"A"}, component, default_domain); +} + static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecParams ¶ms) { const bNode &node = params.node(); const NodeAttributeMath *node_storage = (const NodeAttributeMath *)node.storage; const NodeMathOperation operation = static_cast<NodeMathOperation>(node_storage->operation); + const std::string result_name = params.get_input<std::string>("Result"); /* The result type of this node is always float. */ const CustomDataType result_type = CD_PROP_FLOAT; - /* The result domain is always point for now. */ - const AttributeDomain result_domain = ATTR_DOMAIN_POINT; + const AttributeDomain result_domain = get_result_domain( + component, params, operation, result_name); - /* 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"); OutputAttributePtr attribute_result = component.attribute_try_get_for_output( result_name, result_domain, result_type); if (!attribute_result) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc index 2dbfcd2819c..e515f253a46 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc @@ -125,10 +125,25 @@ static void do_mix_operation(const CustomDataType result_type, } } +static AttributeDomain get_result_domain(const GeometryComponent &component, + const GeoNodeExecParams ¶ms, + StringRef result_name) +{ + /* Use the domain of the result attribute if it already exists. */ + ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); + if (result_attribute) { + return result_attribute->domain(); + } + + /* Otherwise use the highest priority domain from existing input attributes, or the default. */ + return params.get_highest_priority_input_domain({"A", "B"}, component, ATTR_DOMAIN_POINT); +} + static void attribute_mix_calc(GeometryComponent &component, const GeoNodeExecParams ¶ms) { const bNode &node = params.node(); const NodeAttributeMix *node_storage = (const NodeAttributeMix *)node.storage; + const std::string result_name = params.get_input<std::string>("Result"); /* Use the highest complexity data type among the inputs and outputs, that way the node will * never "remove information". Use CD_PROP_BOOL as the lowest complexity data type, but in any @@ -139,14 +154,7 @@ static void attribute_mix_calc(GeometryComponent &component, const GeoNodeExecPa params.get_input_attribute_data_type("Result", component, CD_PROP_BOOL), }); - /* Once we support more domains at the user level, we have to decide how the result domain is - * chosen. */ - AttributeDomain result_domain = ATTR_DOMAIN_POINT; - const std::string result_name = params.get_input<std::string>("Result"); - const ReadAttributePtr result_attribute_read = component.attribute_try_get_for_read(result_name); - if (result_attribute_read) { - result_domain = result_attribute_read->domain(); - } + const AttributeDomain result_domain = get_result_domain(component, params, result_name); OutputAttributePtr attribute_result = component.attribute_try_get_for_output( result_name, result_domain, result_type); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc index e46fdaa127a..51f208ed07d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc @@ -148,9 +148,12 @@ static void attribute_calc_proximity(GeometryComponent &component, GeometrySet &geometry_set_target, GeoNodeExecParams ¶ms) { + /* This node works on the "point" domain, since that is where positions are stored. */ + const AttributeDomain result_domain = ATTR_DOMAIN_POINT; + const std::string result_attribute_name = params.get_input<std::string>("Result"); OutputAttributePtr distance_attribute = component.attribute_try_get_for_output( - result_attribute_name, ATTR_DOMAIN_POINT, CD_PROP_FLOAT); + result_attribute_name, result_domain, CD_PROP_FLOAT); ReadAttributePtr position_attribute = component.attribute_try_get_for_read("position"); BLI_assert(position_attribute->custom_data_type() == CD_PROP_FLOAT3); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc index f4399b45b2e..7e95714a44b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc @@ -153,17 +153,32 @@ Array<uint32_t> get_geometry_element_ids_as_uints(const GeometryComponent &compo return hashes; } +static AttributeDomain get_result_domain(const GeometryComponent &component, + const GeoNodeExecParams ¶ms, + StringRef attribute_name) +{ + /* Use the domain of the result attribute if it already exists. */ + ReadAttributePtr result_attribute = component.attribute_try_get_for_read(attribute_name); + if (result_attribute) { + return result_attribute->domain(); + } + + /* Otherwise use the input domain chosen in the interface. */ + const bNode &node = params.node(); + return static_cast<AttributeDomain>(node.custom2); +} + static void randomize_attribute(GeometryComponent &component, const GeoNodeExecParams ¶ms, const int seed) { - const bNode &node = params.node(); - const CustomDataType data_type = static_cast<CustomDataType>(node.custom1); - const AttributeDomain domain = static_cast<AttributeDomain>(node.custom2); const std::string attribute_name = params.get_input<std::string>("Attribute"); if (attribute_name.empty()) { return; } + const bNode &node = params.node(); + const CustomDataType data_type = static_cast<CustomDataType>(node.custom1); + const AttributeDomain domain = get_result_domain(component, params, attribute_name); OutputAttributePtr attribute = component.attribute_try_get_for_output( attribute_name, domain, data_type); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc index fc3cd099d41..dd8f0adb740 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc @@ -48,29 +48,51 @@ static void geo_node_attribute_sample_texture_layout(uiLayout *layout, namespace blender::nodes { +static AttributeDomain get_result_domain(const GeometryComponent &component, + StringRef result_attribute_name, + StringRef map_attribute_name) +{ + /* Use the domain of the result attribute if it already exists. */ + ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_attribute_name); + if (result_attribute) { + return result_attribute->domain(); + } + + /* Otherwise use the name of the map attribute. */ + ReadAttributePtr map_attribute = component.attribute_try_get_for_read(map_attribute_name); + if (map_attribute) { + return map_attribute->domain(); + } + + /* The node won't execute in this case, but we still have to return a value. */ + return ATTR_DOMAIN_POINT; +} + static void execute_on_component(GeometryComponent &component, const GeoNodeExecParams ¶ms) { const bNode &node = params.node(); Tex *texture = reinterpret_cast<Tex *>(node.id); - const std::string result_attribute_name = params.get_input<std::string>("Result"); - if (texture == nullptr) { return; } + const std::string result_attribute_name = params.get_input<std::string>("Result"); const std::string mapping_name = params.get_input<std::string>("Mapping"); if (!component.attribute_exists(mapping_name)) { return; } + const AttributeDomain result_domain = get_result_domain( + component, result_attribute_name, mapping_name); + OutputAttributePtr attribute_out = component.attribute_try_get_for_output( - result_attribute_name, ATTR_DOMAIN_POINT, CD_PROP_COLOR); + result_attribute_name, result_domain, CD_PROP_COLOR); if (!attribute_out) { return; } Float3ReadAttribute mapping_attribute = component.attribute_get_for_read<float3>( - mapping_name, ATTR_DOMAIN_POINT, {0, 0, 0}); + mapping_name, result_domain, {0, 0, 0}); MutableSpan<Color4f> colors = attribute_out->get_span<Color4f>(); for (const int i : IndexRange(mapping_attribute.size())) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc index 07941f7db79..2e02ad9836d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc @@ -69,22 +69,49 @@ static void extract_input(const int index, const Span<float3> &input, MutableSpa } } +static AttributeDomain get_result_domain(const GeometryComponent &component, + const GeoNodeExecParams ¶ms, + StringRef result_name_x, + StringRef result_name_y, + StringRef result_name_z) +{ + /* Use the highest priority domain from any existing attribute outputs. */ + Vector<AttributeDomain, 3> output_domains; + ReadAttributePtr attribute_x = component.attribute_try_get_for_read(result_name_x); + ReadAttributePtr attribute_y = component.attribute_try_get_for_read(result_name_y); + ReadAttributePtr attribute_z = component.attribute_try_get_for_read(result_name_z); + if (attribute_x) { + output_domains.append(attribute_x->domain()); + } + if (attribute_y) { + output_domains.append(attribute_y->domain()); + } + if (attribute_z) { + output_domains.append(attribute_z->domain()); + } + if (output_domains.size() > 0) { + return attribute_domain_highest_priority(output_domains); + } + + /* Otherwise use the domain of the input attribute, or the default. */ + return params.get_highest_priority_input_domain({"Vector"}, component, ATTR_DOMAIN_POINT); +} + static void separate_attribute(GeometryComponent &component, const GeoNodeExecParams ¶ms) { const std::string result_name_x = params.get_input<std::string>("Result X"); const std::string result_name_y = params.get_input<std::string>("Result Y"); const std::string result_name_z = params.get_input<std::string>("Result Z"); - /* The node is only for float3 to float conversions. */ - const CustomDataType input_type = CD_PROP_FLOAT3; - const CustomDataType result_type = CD_PROP_FLOAT; - /* The result domain is always point for now. */ - const AttributeDomain result_domain = ATTR_DOMAIN_POINT; - - /* No output to write to. */ if (result_name_x.empty() && result_name_y.empty() && result_name_z.empty()) { return; } + /* The node is only for float3 to float conversions. */ + const CustomDataType input_type = CD_PROP_FLOAT3; + const CustomDataType result_type = CD_PROP_FLOAT; + const AttributeDomain result_domain = get_result_domain( + component, params, result_name_x, result_name_y, result_name_z); + ReadAttributePtr attribute_input = params.get_input_attribute( "Vector", component, result_domain, input_type, nullptr); if (!attribute_input) { 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 index adcdab58e30..45b28d154f4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc @@ -327,12 +327,35 @@ static void do_math_operation_fl3_to_fl(const Float3ReadAttribute &input_a, UNUSED_VARS_NDEBUG(success); } +static AttributeDomain get_result_domain(const GeometryComponent &component, + const GeoNodeExecParams ¶ms, + const NodeVectorMathOperation operation, + StringRef result_name) +{ + /* Use the domain of the result attribute if it already exists. */ + ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); + if (result_attribute) { + return result_attribute->domain(); + } + + /* Otherwise use the highest priority domain from existing input attributes, or the default. */ + const AttributeDomain default_domain = ATTR_DOMAIN_POINT; + if (operation_use_input_b(operation)) { + if (operation_use_input_c(operation)) { + return params.get_highest_priority_input_domain({"A", "B", "C"}, component, default_domain); + } + return params.get_highest_priority_input_domain({"A", "B"}, component, default_domain); + } + return params.get_highest_priority_input_domain({"A"}, component, default_domain); +} + static void attribute_vector_math_calc(GeometryComponent &component, const GeoNodeExecParams ¶ms) { const bNode &node = params.node(); const NodeAttributeVectorMath *node_storage = (const NodeAttributeVectorMath *)node.storage; const NodeVectorMathOperation operation = (NodeVectorMathOperation)node_storage->operation; + const std::string result_name = params.get_input<std::string>("Result"); /* The number and type of the input attribute depend on the operation. */ const CustomDataType read_type_a = CD_PROP_FLOAT3; @@ -343,7 +366,8 @@ static void attribute_vector_math_calc(GeometryComponent &component, /* The result domain is always point for now. */ const CustomDataType result_type = operation_get_result_type(operation); - const AttributeDomain result_domain = ATTR_DOMAIN_POINT; + const AttributeDomain result_domain = get_result_domain( + component, params, operation, result_name); ReadAttributePtr attribute_a = params.get_input_attribute( "A", component, result_domain, read_type_a, nullptr); @@ -366,7 +390,6 @@ static void attribute_vector_math_calc(GeometryComponent &component, } /* 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"); OutputAttributePtr attribute_result = component.attribute_try_get_for_output( result_name, result_domain, result_type); if (!attribute_result) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index ca2e1be6e6f..4fec14ec9c9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -147,16 +147,17 @@ static void determine_final_data_type_and_domain(Span<const GeometryComponent *> AttributeDomain *r_domain) { Vector<CustomDataType> data_types; + Vector<AttributeDomain> domains; for (const GeometryComponent *component : components) { ReadAttributePtr attribute = component->attribute_try_get_for_read(attribute_name); if (attribute) { data_types.append(attribute->custom_data_type()); - /* TODO: Use highest priority domain. */ - *r_domain = attribute->domain(); + domains.append(attribute->domain()); } } *r_type = attribute_data_type_highest_complexity(data_types); + *r_domain = attribute_domain_highest_priority(domains); } static void fill_new_attribute(Span<const GeometryComponent *> src_components, diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index eef2c6c9125..6ddeb73e31f 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -17,6 +17,8 @@ #include "NOD_geometry_exec.hh" #include "NOD_type_callbacks.hh" +#include "node_geometry_util.hh" + namespace blender::nodes { const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const @@ -104,6 +106,40 @@ CustomDataType GeoNodeExecParams::get_input_attribute_data_type( return default_type; } +/** + * If any of the corresponding input sockets are attributes instead of single values, + * use the highest priority attribute domain from among them. + * Otherwise return the default domain. + */ +AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain( + Span<std::string> names, + const GeometryComponent &component, + const AttributeDomain default_domain) const +{ + Vector<AttributeDomain, 8> input_domains; + for (const std::string &name : names) { + const bNodeSocket *found_socket = this->find_available_socket(name); + BLI_assert(found_socket != nullptr); /* A socket should be available socket for the name. */ + if (found_socket == nullptr) { + continue; + } + + if (found_socket->type == SOCK_STRING) { + const std::string name = this->get_input<std::string>(found_socket->identifier); + ReadAttributePtr attribute = component.attribute_try_get_for_read(name); + if (attribute) { + input_domains.append(attribute->domain()); + } + } + } + + if (input_domains.size() > 0) { + return attribute_domain_highest_priority(input_domains); + } + + return default_domain; +} + void GeoNodeExecParams::check_extract_input(StringRef identifier, const CPPType *requested_type) const { |