From 670453d1ecdbb43a552b2f607cac9d1b84f09d6c Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Sat, 13 Mar 2021 11:49:56 -0500 Subject: Geometry Nodes: Add Attribute Convert node The Attribute Convert node provides functionality to change attributes between different domains and data types. Before it was impossible to write to a UV Map attribute with the attribute math nodes since they did not output a 2D vector type. This makes it possible to "convert into" a UV map attribute. The data type conversion uses the implicit conversions provided by `\nodes\intern\node_tree_multi_function.cc`. The `Auto` domain mode chooses the domain based on the following rules: 1. If the result attribute already exists, use that domain. 2. If the result attribute doesn't exist, use the source attribute domain. 3. Otherwise use the default domain (points). See {T85700} Differential Revision: https://developer.blender.org/D10624 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenkernel/BKE_attribute.h | 1 + source/blender/blenkernel/BKE_node.h | 1 + source/blender/blenkernel/intern/node.cc | 1 + source/blender/makesdna/DNA_node_types.h | 8 + source/blender/makesrna/intern/rna_nodetree.c | 46 ++++++ source/blender/nodes/CMakeLists.txt | 1 + source/blender/nodes/NOD_geometry.h | 1 + source/blender/nodes/NOD_static_types.h | 1 + .../geometry/nodes/node_geo_attribute_convert.cc | 164 +++++++++++++++++++++ 10 files changed, 225 insertions(+) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index f9a789f800b..c6565f1b578 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -485,6 +485,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeAttributeRandomize"), NodeItem("GeometryNodeAttributeMath"), NodeItem("GeometryNodeAttributeCompare"), + NodeItem("GeometryNodeAttributeConvert"), NodeItem("GeometryNodeAttributeFill"), NodeItem("GeometryNodeAttributeMix"), NodeItem("GeometryNodeAttributeProximity"), diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h index 574d9904dc4..a98bfd1e3df 100644 --- a/source/blender/blenkernel/BKE_attribute.h +++ b/source/blender/blenkernel/BKE_attribute.h @@ -39,6 +39,7 @@ struct ReportList; /* Attribute.domain */ typedef enum AttributeDomain { + ATTR_DOMAIN_AUTO = -1, /* Use for nodes to choose automatically based on other data. */ ATTR_DOMAIN_POINT = 0, /* Mesh, Hair or PointCloud Point */ ATTR_DOMAIN_EDGE = 1, /* Mesh Edge */ ATTR_DOMAIN_CORNER = 2, /* Mesh Corner */ diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 2c543a0a014..0379dea9e8c 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1373,6 +1373,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_ATTRIBUTE_SEPARATE_XYZ 1028 #define GEO_NODE_SUBDIVIDE 1029 #define GEO_NODE_ATTRIBUTE_REMOVE 1030 +#define GEO_NODE_ATTRIBUTE_CONVERT 1031 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 528088b2ee7..156f6a0c7ae 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4788,6 +4788,7 @@ static void registerGeometryNodes() register_node_type_geo_attribute_color_ramp(); register_node_type_geo_attribute_combine_xyz(); register_node_type_geo_attribute_compare(); + register_node_type_geo_attribute_convert(); register_node_type_geo_attribute_fill(); register_node_type_geo_attribute_math(); register_node_type_geo_attribute_mix(); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index d19d270a7ac..ad1def0f3ec 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1226,6 +1226,14 @@ typedef struct NodeAttributeSeparateXYZ { uint8_t input_type; } NodeAttributeSeparateXYZ; +typedef struct NodeAttributeConvert { + /* CustomDataType. */ + uint8_t data_type; + char _pad[1]; + /* AttributeDomain. */ + int16_t domain; +} NodeAttributeConvert; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index dfb882cde33..aed1b82e940 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -1929,6 +1929,23 @@ static void rna_GeometryNodeAttributeRandomize_data_type_update(Main *bmain, rna_Node_socket_update(bmain, scene, ptr); } +static bool attribute_convert_type_supported(const EnumPropertyItem *item) +{ + return ELEM(item->value, + CD_PROP_FLOAT, + CD_PROP_FLOAT2, + CD_PROP_FLOAT3, + CD_PROP_COLOR, + CD_PROP_BOOL, + CD_PROP_INT32); +} +static const EnumPropertyItem *rna_GeometryNodeAttributeConvert_type_itemf( + bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +{ + *r_free = true; + return itemf_function_check(rna_enum_attribute_type_items, attribute_convert_type_supported); +} + static bool attribute_fill_type_supported(const EnumPropertyItem *item) { return ELEM( @@ -8647,6 +8664,35 @@ static void def_geo_attribute_fill(StructRNA *srna) "rna_GeometryNodeAttributeFill_domain_itemf"); } +static void def_geo_attribute_convert(StructRNA *srna) +{ + static const EnumPropertyItem rna_enum_attribute_convert_domain_items[] = { + {ATTR_DOMAIN_AUTO, "AUTO", 0, "Auto", ""}, + {ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"}, + {ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", "Attribute on mesh edge"}, + {ATTR_DOMAIN_CORNER, "CORNER", 0, "Corner", "Attribute on mesh polygon corner"}, + {ATTR_DOMAIN_POLYGON, "POLYGON", 0, "Polygon", "Attribute on mesh polygons"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeAttributeConvert", "storage"); + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeConvert_type_itemf"); + RNA_def_property_enum_default(prop, CD_PROP_FLOAT); + RNA_def_property_ui_text(prop, "Data Type", "The data type to save the result attribute with"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); + + prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_convert_domain_items); + RNA_def_property_enum_default(prop, ATTR_DOMAIN_AUTO); + RNA_def_property_ui_text(prop, "Domain", "The geometry domain to save the result attribute in"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_geo_attribute_math(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 9408e5348dd..bbc14751cdd 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -146,6 +146,7 @@ set(SRC geometry/nodes/node_geo_attribute_color_ramp.cc geometry/nodes/node_geo_attribute_combine_xyz.cc geometry/nodes/node_geo_attribute_compare.cc + geometry/nodes/node_geo_attribute_convert.cc geometry/nodes/node_geo_attribute_fill.cc geometry/nodes/node_geo_attribute_math.cc geometry/nodes/node_geo_attribute_mix.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index d7f56464b36..d28dc8209d4 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -30,6 +30,7 @@ void register_node_type_geo_align_rotation_to_vector(void); void register_node_type_geo_attribute_color_ramp(void); void register_node_type_geo_attribute_combine_xyz(void); void register_node_type_geo_attribute_compare(void); +void register_node_type_geo_attribute_convert(void); void register_node_type_geo_attribute_fill(void); void register_node_type_geo_attribute_math(void); void register_node_type_geo_attribute_mix(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 6669f47c6aa..493aaa05675 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -300,6 +300,7 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "ATTRIBUTE_SEPARATE_XYZ", AttributeSeparateXYZ, "Attribute Separate XYZ", "") DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "") /* undefine macros */ #undef DefNode diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc new file mode 100644 index 00000000000..11d220dd903 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc @@ -0,0 +1,164 @@ +/* + * 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 "UI_interface.h" +#include "UI_resources.h" + +static bNodeSocketTemplate geo_node_attribute_convert_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Attribute")}, + {SOCK_STRING, N_("Result")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_attribute_convert_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_attribute_convert_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); +} + +static void geo_node_attribute_convert_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeAttributeConvert *data = (NodeAttributeConvert *)MEM_callocN(sizeof(NodeAttributeConvert), + __func__); + + data->data_type = CD_PROP_FLOAT; + data->domain = ATTR_DOMAIN_AUTO; + node->storage = data; +} + +namespace blender::nodes { + +static AttributeDomain get_result_domain(const GeometryComponent &component, + StringRef source_name, + StringRef result_name) +{ + ReadAttributePtr result_attribute = component.attribute_try_get_for_read(result_name); + if (result_attribute) { + return result_attribute->domain(); + } + ReadAttributePtr source_attribute = component.attribute_try_get_for_read(source_name); + if (source_attribute) { + return source_attribute->domain(); + } + return ATTR_DOMAIN_POINT; +} + +static void attribute_convert_calc(GeometryComponent &component, + const GeoNodeExecParams ¶ms, + const StringRef source_name, + const StringRef result_name, + const CustomDataType result_type, + const AttributeDomain domain) +{ + const AttributeDomain result_domain = (domain == ATTR_DOMAIN_AUTO) ? + get_result_domain( + component, source_name, result_name) : + domain; + + ReadAttributePtr source_attribute = component.attribute_try_get_for_read( + source_name, result_domain, result_type); + if (!source_attribute) { + params.error_message_add(NodeWarningType::Error, + TIP_("No attribute with name \"") + source_name + "\""); + return; + } + + OutputAttributePtr result_attribute = component.attribute_try_get_for_output( + result_name, result_domain, result_type); + if (!result_attribute) { + return; + } + + fn::GSpan source_span = source_attribute->get_span(); + fn::GMutableSpan result_span = result_attribute->get_span_for_write_only(); + if (source_span.is_empty() || result_span.is_empty()) { + return; + } + BLI_assert(source_span.size() == result_span.size()); + + const CPPType *cpp_type = bke::custom_data_type_to_cpp_type(result_type); + BLI_assert(cpp_type != nullptr); + + cpp_type->copy_to_initialized_n(source_span.data(), result_span.data(), result_span.size()); + + result_attribute.apply_span_and_save(); +} + +static void geo_node_attribute_convert_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + + geometry_set = geometry_set_realize_instances(geometry_set); + + const std::string result_name = params.extract_input("Result"); + const std::string source_name = params.extract_input("Attribute"); + const NodeAttributeConvert &node_storage = *(const NodeAttributeConvert *)params.node().storage; + const CustomDataType data_type = static_cast(node_storage.data_type); + const AttributeDomain domain = static_cast(node_storage.domain); + + if (result_name.empty()) { + params.set_output("Geometry", geometry_set); + return; + } + + if (geometry_set.has()) { + attribute_convert_calc(geometry_set.get_component_for_write(), + params, + source_name, + result_name, + data_type, + domain); + } + if (geometry_set.has()) { + attribute_convert_calc(geometry_set.get_component_for_write(), + params, + source_name, + result_name, + data_type, + domain); + } + + params.set_output("Geometry", geometry_set); +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_convert() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_ATTRIBUTE_CONVERT, "Attribute Convert", NODE_CLASS_ATTRIBUTE, 0); + node_type_socket_templates( + &ntype, geo_node_attribute_convert_in, geo_node_attribute_convert_out); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_convert_exec; + ntype.draw_buttons = geo_node_attribute_convert_layout; + node_type_init(&ntype, geo_node_attribute_convert_init); + node_type_storage( + &ntype, "NodeAttributeConvert", node_free_standard_storage, node_copy_standard_storage); + + nodeRegisterType(&ntype); +} -- cgit v1.2.3