diff options
Diffstat (limited to 'source/blender')
32 files changed, 1302 insertions, 109 deletions
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 27f9edac731..fab387377b2 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -113,6 +113,7 @@ namespace blender { namespace nodes { class SocketMFNetworkBuilder; class NodeMFNetworkBuilder; +class GeoNodeResolveParams; class GeoNodeExecParams; } // namespace nodes namespace fn { @@ -122,6 +123,7 @@ class MFDataType; } // namespace blender using NodeExpandInMFNetworkFunction = void (*)(blender::nodes::NodeMFNetworkBuilder &builder); +using NodeGeometryResolveFunction = void (*)(blender::nodes::GeoNodeResolveParams params); using NodeGeometryExecFunction = void (*)(blender::nodes::GeoNodeExecParams params); using SocketGetCPPTypeFunction = const blender::fn::CPPType *(*)(); using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value); @@ -130,6 +132,7 @@ using SocketExpandInMFNetworkFunction = void (*)(blender::nodes::SocketMFNetwork #else typedef void *NodeExpandInMFNetworkFunction; typedef void *SocketExpandInMFNetworkFunction; +typedef void *NodeGeometryResolveFunction; typedef void *NodeGeometryExecFunction; typedef void *SocketGetCPPTypeFunction; typedef void *SocketGetCPPValueFunction; @@ -152,6 +155,10 @@ typedef struct bNodeSocketType { struct PointerRNA *ptr, struct PointerRNA *node_ptr, float *r_color); + void (*draw_shape)(struct bContext *C, + struct PointerRNA *ptr, + struct PointerRNA *node_ptr, + eNodeSocketDisplayShape *r_display_shape); void (*interface_draw)(struct bContext *C, struct uiLayout *layout, struct PointerRNA *ptr); void (*interface_draw_color)(struct bContext *C, struct PointerRNA *ptr, float *r_color); @@ -324,6 +331,7 @@ typedef struct bNodeType { NodeExpandInMFNetworkFunction expand_in_mf_network; /* Execute a geometry node. */ + NodeGeometryResolveFunction geometry_node_resolve; NodeGeometryExecFunction geometry_node_execute; bool geometry_node_execute_supports_laziness; @@ -1443,6 +1451,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_PRIMITIVE_SPIRAL 1063 #define GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER 1064 #define GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT 1065 +#define GEO_NODE_ATTRIBUTE_GET 1066 +#define GEO_NODE_ATTRIBUTE_SET 1067 /** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 87b069d7c50..ce76562b3af 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -323,6 +323,7 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket case SOCK_CUSTOM: case SOCK_SHADER: case SOCK_GEOMETRY: + case SOCK_ATTRIBUTE: break; } } @@ -450,6 +451,9 @@ static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *so case SOCK_MATERIAL: BLO_write_struct(writer, bNodeSocketValueMaterial, sock->default_value); break; + case SOCK_ATTRIBUTE: + BLO_write_struct(writer, bNodeSocketValueAttribute, sock->default_value); + break; case __SOCK_MESH: case SOCK_CUSTOM: case SOCK_SHADER: @@ -856,6 +860,7 @@ static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSock case SOCK_CUSTOM: case SOCK_SHADER: case SOCK_GEOMETRY: + case SOCK_ATTRIBUTE: break; } } @@ -951,6 +956,7 @@ static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock) case SOCK_CUSTOM: case SOCK_SHADER: case SOCK_GEOMETRY: + case SOCK_ATTRIBUTE: break; } } @@ -1528,6 +1534,7 @@ static void socket_id_user_increment(bNodeSocket *sock) case SOCK_CUSTOM: case SOCK_SHADER: case SOCK_GEOMETRY: + case SOCK_ATTRIBUTE: break; } } @@ -1581,6 +1588,7 @@ static void socket_id_user_decrement(bNodeSocket *sock) case SOCK_CUSTOM: case SOCK_SHADER: case SOCK_GEOMETRY: + case SOCK_ATTRIBUTE: break; } } @@ -1720,6 +1728,8 @@ const char *nodeStaticSocketType(int type, int subtype) return "NodeSocketTexture"; case SOCK_MATERIAL: return "NodeSocketMaterial"; + case SOCK_ATTRIBUTE: + return "NodeSocketAttribute"; } return nullptr; } @@ -1797,6 +1807,8 @@ const char *nodeStaticSocketInterfaceType(int type, int subtype) return "NodeSocketInterfaceTexture"; case SOCK_MATERIAL: return "NodeSocketInterfaceMaterial"; + case SOCK_ATTRIBUTE: + return "NodeSocketInterfaceAttribute"; } return nullptr; } @@ -5039,12 +5051,14 @@ static void registerGeometryNodes() register_node_type_geo_attribute_convert(); register_node_type_geo_attribute_curve_map(); register_node_type_geo_attribute_fill(); + register_node_type_geo_attribute_get(); register_node_type_geo_attribute_map_range(); register_node_type_geo_attribute_math(); register_node_type_geo_attribute_mix(); register_node_type_geo_attribute_proximity(); register_node_type_geo_attribute_randomize(); register_node_type_geo_attribute_separate_xyz(); + register_node_type_geo_attribute_set(); register_node_type_geo_attribute_transfer(); register_node_type_geo_attribute_vector_math(); register_node_type_geo_attribute_vector_rotate(); diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h index ba65840dc99..ee2fbbe4ff5 100644 --- a/source/blender/editors/include/ED_node.h +++ b/source/blender/editors/include/ED_node.h @@ -89,7 +89,8 @@ void ED_node_draw_snap( void ED_node_socket_draw(struct bNodeSocket *sock, const struct rcti *rect, const float color[4], - float scale); + float scale, + char display_shape); void ED_node_tree_update(const struct bContext *C); void ED_node_tag_update_id(struct ID *id); void ED_node_tag_update_nodetree(struct Main *bmain, struct bNodeTree *ntree, struct bNode *node); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 91a4c430023..b4deb0a554b 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -36,6 +36,7 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_node.h" #include "RNA_access.h" @@ -2308,7 +2309,7 @@ static void widget_draw_node_link_socket(const uiWidgetColors *wcol, UI_widgetbase_draw_cache_flush(); GPU_blend(GPU_BLEND_NONE); - ED_node_socket_draw(but->custom_data, rect, col, scale); + ED_node_socket_draw(but->custom_data, rect, col, scale, SOCK_DISPLAY_SHAPE_CIRCLE); } else { widget_draw_icon(but, ICON_LAYER_USED, alpha, rect, wcol->text); diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 1c7ee9e622f..d99433b47a8 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -3345,7 +3345,7 @@ static void node_socket_undefined_draw(bContext *UNUSED(C), } static void node_socket_undefined_draw_color(bContext *UNUSED(C), - PointerRNA *UNUSED(ptr), + PointerRNA *ptr, PointerRNA *UNUSED(node_ptr), float *r_color) { @@ -3455,6 +3455,7 @@ static const float std_node_socket_colors[][4] = { {0.96, 0.96, 0.96, 1.0}, /* SOCK_COLLECTION */ {0.62, 0.31, 0.64, 1.0}, /* SOCK_TEXTURE */ {0.92, 0.46, 0.51, 1.0}, /* SOCK_MATERIAL */ + {1.0, 0.0, 1.0, 1.0}, /* SOCK_ATTRIBUTE (placeholder, copies from other types) */ }; /* common color callbacks for standard types */ @@ -3465,15 +3466,43 @@ static void std_node_socket_draw_color(bContext *UNUSED(C), { bNodeSocket *sock = (bNodeSocket *)ptr->data; int type = sock->typeinfo->type; - copy_v4_v4(r_color, std_node_socket_colors[type]); + if (type != SOCK_ATTRIBUTE) { + copy_v4_v4(r_color, std_node_socket_colors[type]); + } + else { + int data_type = ((bNodeSocketValueAttribute *)sock->default_value)->data_type; + copy_v4_v4(r_color, std_node_socket_colors[data_type]); + } } + +static void std_node_socket_draw_shape(bContext *UNUSED(C), + PointerRNA *ptr, + PointerRNA *UNUSED(node_ptr), + eNodeSocketDisplayShape *r_display_shape) +{ + bNodeSocket *sock = (bNodeSocket *)ptr->data; + int type = sock->typeinfo->type; + if (type != SOCK_ATTRIBUTE) { + *r_display_shape = (eNodeSocketDisplayShape)sock->display_shape; + } + else { + *r_display_shape = SOCK_DISPLAY_SHAPE_SQUARE; + } +} + static void std_node_socket_interface_draw_color(bContext *UNUSED(C), PointerRNA *ptr, float *r_color) { bNodeSocket *sock = (bNodeSocket *)ptr->data; int type = sock->typeinfo->type; - copy_v4_v4(r_color, std_node_socket_colors[type]); + if (type != SOCK_ATTRIBUTE) { + copy_v4_v4(r_color, std_node_socket_colors[type]); + } + else { + int data_type = ((bNodeSocketValueAttribute *)sock->default_value)->data_type; + copy_v4_v4(r_color, std_node_socket_colors[data_type]); + } } /* draw function for file output node sockets, @@ -3577,7 +3606,7 @@ static void std_node_socket_draw( const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id; if (node_tree->type == NTREE_GEOMETRY) { - node_geometry_add_attribute_search_button(C, node_tree, node, ptr, row); + node_geometry_add_attribute_search_button(C, node_tree, node, ptr, "default_value", row); } else { uiItemR(row, ptr, "default_value", DEFAULT_FLAGS, "", 0); @@ -3606,6 +3635,60 @@ static void std_node_socket_draw( uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); break; } + case SOCK_ATTRIBUTE: { + const int data_type = ((bNodeSocketValueAttribute *)sock->default_value)->data_type; + const bool use_attribute_name = ((bNodeSocketValueAttribute *)sock->default_value)->flag & + SOCK_ATTRIBUTE_USE_NAME; + + uiLayout *row = uiLayoutRow(layout, false); + if (use_attribute_name) { + uiLayout *split = uiLayoutSplit(row, 0.4f, false); + uiItemL(split, text, 0); + + const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id; + if (node_tree->type == NTREE_GEOMETRY) { + node_geometry_add_attribute_search_button(C, node_tree, node, ptr, "attribute_name", split); + } + else { + uiItemR(split, ptr, "attribute_name", DEFAULT_FLAGS, "", 0); + } + } + else { + switch (data_type) { + case SOCK_FLOAT: + uiItemR(row, ptr, "default_value_float", DEFAULT_FLAGS, text, 0); + break; + case SOCK_INT: + uiItemR(row, ptr, "default_value_int", DEFAULT_FLAGS, text, 0); + break; + case SOCK_BOOLEAN: + uiItemR(row, ptr, "default_value_bool", DEFAULT_FLAGS, text, 0); + break; + case SOCK_VECTOR: + if (sock->flag & SOCK_COMPACT) { + uiTemplateComponentMenu(row, ptr, "default_value_vector", text); + } + else { + if (sock->typeinfo->subtype == PROP_DIRECTION) { + uiItemR(row, ptr, "default_value_vector", DEFAULT_FLAGS, "", ICON_NONE); + } + else { + uiLayout *column = uiLayoutColumn(row, true); + uiItemR(column, ptr, "default_value_vector", DEFAULT_FLAGS, text, ICON_NONE); + } + } + break; + case SOCK_RGBA: { + uiLayout *split = uiLayoutSplit(row, 0.4f, false); + uiItemL(split, text, 0); + uiItemR(split, ptr, "default_value_color", DEFAULT_FLAGS, "", 0); + break; + } + } + } + uiItemR(row, ptr, "use_attribute_name", 0, "", ICON_VIEWZOOM); + break; + } default: node_socket_button_label(C, layout, ptr, node_ptr, text); break; @@ -3647,6 +3730,40 @@ static void std_node_socket_interface_draw(bContext *UNUSED(C), uiLayout *layout uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), 0); break; } + case SOCK_ATTRIBUTE: { + int data_type = ((bNodeSocketValueAttribute *)sock->default_value)->data_type; + switch (data_type) { + case SOCK_FLOAT: { + uiItemR(col, ptr, "default_value_float", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); + uiLayout *sub = uiLayoutColumn(col, true); + uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); + uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); + break; + } + case SOCK_INT: { + uiItemR(col, ptr, "default_value_int", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); + uiLayout *sub = uiLayoutColumn(col, true); + uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); + uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); + break; + } + case SOCK_VECTOR: { + uiItemR(col, ptr, "default_value_vector", UI_ITEM_R_EXPAND, IFACE_("Default"), ICON_NONE); + uiLayout *sub = uiLayoutColumn(col, true); + uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); + uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); + break; + } + case SOCK_BOOLEAN: + uiItemR(col, ptr, "default_value_bool", DEFAULT_FLAGS, IFACE_("Default"), 0); + break; + case SOCK_RGBA: { + uiItemR(col, ptr, "default_value_color", DEFAULT_FLAGS, IFACE_("Default"), 0); + break; + } + } + break; + } } uiItemR(layout, ptr, "hide_value", DEFAULT_FLAGS, nullptr, 0); @@ -3656,12 +3773,13 @@ void ED_init_standard_node_socket_type(bNodeSocketType *stype) { stype->draw = std_node_socket_draw; stype->draw_color = std_node_socket_draw_color; + stype->draw_shape = std_node_socket_draw_shape; stype->interface_draw = std_node_socket_interface_draw; stype->interface_draw_color = std_node_socket_interface_draw_color; } static void node_socket_virtual_draw_color(bContext *UNUSED(C), - PointerRNA *UNUSED(ptr), + PointerRNA *ptr, PointerRNA *UNUSED(node_ptr), float *r_color) { diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index b9dad060121..80b5975ce30 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -717,9 +717,9 @@ static void node_draw_mute_line(const View2D *v2d, const SpaceNode *snode, const #define MARKER_SHAPE_CIRCLE 0x2 #define MARKER_SHAPE_INNER_DOT 0x10 -static void node_socket_draw(const bNodeSocket *sock, - const float color[4], +static void node_socket_draw(const float color[4], const float color_outline[4], + const eNodeSocketDisplayShape display_shape, float size, int locx, int locy, @@ -732,7 +732,7 @@ static void node_socket_draw(const bNodeSocket *sock, int flags; /* Set shape flags. */ - switch (sock->display_shape) { + switch (display_shape) { case SOCK_DISPLAY_SHAPE_DIAMOND: case SOCK_DISPLAY_SHAPE_DIAMOND_DOT: flags = MARKER_SHAPE_DIAMOND; @@ -748,7 +748,7 @@ static void node_socket_draw(const bNodeSocket *sock, break; } - if (ELEM(sock->display_shape, + if (ELEM(display_shape, SOCK_DISPLAY_SHAPE_DIAMOND_DOT, SOCK_DISPLAY_SHAPE_SQUARE_DOT, SOCK_DISPLAY_SHAPE_CIRCLE_DOT)) { @@ -764,6 +764,7 @@ static void node_socket_draw(const bNodeSocket *sock, static void node_socket_draw_multi_input(const float color[4], const float color_outline[4], + const eNodeSocketDisplayShape UNUSED(display_shape), const float width, const float height, const int locx, @@ -807,14 +808,24 @@ static void node_socket_outline_color_get(const bool selected, /* Usual convention here would be node_socket_get_color(), but that's already used (for setting a * color property socket). */ -void node_socket_color_get( - bContext *C, bNodeTree *ntree, PointerRNA *node_ptr, bNodeSocket *sock, float r_color[4]) +void node_socket_color_get(bContext *C, + bNodeTree *ntree, + PointerRNA *node_ptr, + bNodeSocket *sock, + float r_color[4], + eNodeSocketDisplayShape *r_display_shape) { PointerRNA ptr; BLI_assert(RNA_struct_is_a(node_ptr->type, &RNA_Node)); RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr); sock->typeinfo->draw_color(C, &ptr, node_ptr, r_color); + if (sock->typeinfo->draw_shape) { + sock->typeinfo->draw_shape(C, &ptr, node_ptr, r_display_shape); + } + else { + *r_display_shape = (eNodeSocketDisplayShape)sock->display_shape; + } bNode *node = (bNode *)node_ptr->data; if (node->flag & NODE_MUTED) { @@ -836,13 +847,14 @@ static void node_socket_draw_nested(const bContext *C, { float color[4]; float outline_color[4]; + eNodeSocketDisplayShape display_shape; - node_socket_color_get((bContext *)C, ntree, node_ptr, sock, color); + node_socket_color_get((bContext *)C, ntree, node_ptr, sock, color, &display_shape); node_socket_outline_color_get(selected, sock->type, outline_color); - node_socket_draw(sock, - color, + node_socket_draw(color, outline_color, + display_shape, size, sock->locx, sock->locy, @@ -858,7 +870,11 @@ static void node_socket_draw_nested(const bContext *C, * \note this is only called from external code, internally #node_socket_draw_nested() is used for * optimized drawing of multiple/all sockets of a node. */ -void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[4], float scale) +void ED_node_socket_draw(bNodeSocket *sock, + const rcti *rect, + const float color[4], + float scale, + char display_shape) { const float size = 2.25f * NODE_SOCKSIZE * scale; rcti draw_rect = *rect; @@ -886,9 +902,9 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[ /* Single point. */ immBegin(GPU_PRIM_POINTS, 1); - node_socket_draw(sock, - color, + node_socket_draw(color, outline_color, + (eNodeSocketDisplayShape)display_shape, BLI_rcti_size_y(&draw_rect), BLI_rcti_cent_x(&draw_rect), BLI_rcti_cent_y(&draw_rect), @@ -1178,10 +1194,12 @@ void node_draw_sockets(const View2D *v2d, float color[4]; float outline_color[4]; - node_socket_color_get((bContext *)C, ntree, &node_ptr, socket, color); + eNodeSocketDisplayShape display_shape; + node_socket_color_get((bContext *)C, ntree, &node_ptr, socket, color, &display_shape); node_socket_outline_color_get(selected, socket->type, outline_color); - node_socket_draw_multi_input(color, outline_color, width, height, socket->locx, socket->locy); + node_socket_draw_multi_input( + color, outline_color, display_shape, width, height, socket->locx, socket->locy); } } diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index 94080a7b616..e92449e3535 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -140,8 +140,19 @@ static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v) AvailableAttributeInfo *item = static_cast<AvailableAttributeInfo *>(item_v); bNodeSocket &socket = data->socket; - bNodeSocketValueString *value = static_cast<bNodeSocketValueString *>(socket.default_value); - BLI_strncpy(value->value, item->name.c_str(), MAX_NAME); + switch (socket.type) { + case SOCK_STRING: { + bNodeSocketValueString *value = static_cast<bNodeSocketValueString *>(socket.default_value); + BLI_strncpy(value->value, item->name.c_str(), MAX_NAME); + break; + } + case SOCK_ATTRIBUTE: { + bNodeSocketValueAttribute *value = static_cast<bNodeSocketValueAttribute *>( + socket.default_value); + BLI_strncpy(value->attribute_name, item->name.c_str(), MAX_NAME); + break; + } + } ED_undo_push(C, "Assign Attribute Name"); } @@ -150,13 +161,14 @@ void node_geometry_add_attribute_search_button(const bContext *C, const bNodeTree *node_tree, const bNode *node, PointerRNA *socket_ptr, + const char *propname, uiLayout *layout) { const NodeUIStorage *ui_storage = BKE_node_tree_ui_storage_get_from_context( C, *node_tree, *node); if (ui_storage == nullptr) { - uiItemR(layout, socket_ptr, "default_value", 0, "", 0); + uiItemR(layout, socket_ptr, propname, 0, "", 0); return; } @@ -173,7 +185,7 @@ void node_geometry_add_attribute_search_button(const bContext *C, 10 * UI_UNIT_X, /* Dummy value, replaced by layout system. */ UI_UNIT_Y, socket_ptr, - "default_value", + propname, 0, 0.0f, 0.0f, diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index 8dfc43333e3..73de87b8c14 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -125,7 +125,8 @@ void node_socket_color_get(struct bContext *C, struct bNodeTree *ntree, struct PointerRNA *node_ptr, struct bNodeSocket *sock, - float r_color[4]); + float r_color[4], + eNodeSocketDisplayShape *r_display_shape); void node_update_nodetree(const struct bContext *C, struct bNodeTree *ntree); void node_draw_nodetree(const struct bContext *C, struct ARegion *region, @@ -318,6 +319,7 @@ void node_geometry_add_attribute_search_button(const struct bContext *C, const struct bNodeTree *node_tree, const struct bNode *node, struct PointerRNA *socket_ptr, + const char *propname, struct uiLayout *layout); extern const char *node_context_dir[]; diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index a1b8e4e3395..5c251b47746 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -1976,6 +1976,7 @@ static int get_main_socket_priority(const bNodeSocket *socket) case SOCK_COLLECTION: case SOCK_TEXTURE: case SOCK_MATERIAL: + case SOCK_ATTRIBUTE: return 5; } return -1; diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc index fae180ca6c5..93599bc3c2a 100644 --- a/source/blender/editors/space_node/node_templates.cc +++ b/source/blender/editors/space_node/node_templates.cc @@ -685,6 +685,7 @@ void uiTemplateNodeLink( NodeLinkArg *arg; uiBut *but; float socket_col[4]; + eNodeSocketDisplayShape display_shape; arg = (NodeLinkArg *)MEM_callocN(sizeof(NodeLinkArg), "NodeLinkArg"); arg->ntree = ntree; @@ -693,7 +694,7 @@ void uiTemplateNodeLink( PointerRNA node_ptr; RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr); - node_socket_color_get(C, ntree, &node_ptr, input, socket_col); + node_socket_color_get(C, ntree, &node_ptr, input, socket_col, &display_shape); UI_block_layout_set_current(block, layout); @@ -849,7 +850,7 @@ static void ui_node_draw_input( case SOCK_STRING: { const bNodeTree *node_tree = (const bNodeTree *)nodeptr.owner_id; if (node_tree->type == NTREE_GEOMETRY) { - node_geometry_add_attribute_search_button(C, node_tree, node, &inputptr, row); + node_geometry_add_attribute_search_button(C, node_tree, node, &inputptr, "default_value", row); } else { uiItemR(sub, &inputptr, "default_value", 0, "", ICON_NONE); @@ -858,6 +859,40 @@ static void ui_node_draw_input( split_wrapper.decorate_column, &inputptr, "default_value", RNA_NO_INDEX); break; } + case SOCK_ATTRIBUTE: { + int data_type = ((bNodeSocketValueAttribute *)input->default_value)->data_type; + switch (data_type) { + case SOCK_VECTOR: + if (input->type == SOCK_VECTOR) { + uiItemS(row); + sub = uiLayoutColumn(row, true); + } + uiItemR(sub, &inputptr, "default_value_vector", 0, "", ICON_NONE); + uiItemDecoratorR( + split_wrapper.decorate_column, &inputptr, "default_value_vector", RNA_NO_INDEX); + case SOCK_FLOAT: + uiItemR(sub, &inputptr, "default_value_float", 0, "", ICON_NONE); + uiItemDecoratorR( + split_wrapper.decorate_column, &inputptr, "default_value_float", RNA_NO_INDEX); + break; + case SOCK_INT: + uiItemR(sub, &inputptr, "default_value_int", 0, "", ICON_NONE); + uiItemDecoratorR( + split_wrapper.decorate_column, &inputptr, "default_value_int", RNA_NO_INDEX); + break; + case SOCK_BOOLEAN: + uiItemR(sub, &inputptr, "default_value_bool", 0, "", ICON_NONE); + uiItemDecoratorR( + split_wrapper.decorate_column, &inputptr, "default_value_bool", RNA_NO_INDEX); + break; + case SOCK_RGBA: + uiItemR(sub, &inputptr, "default_value_color", 0, "", ICON_NONE); + uiItemDecoratorR( + split_wrapper.decorate_column, &inputptr, "default_value_color", RNA_NO_INDEX); + break; + } + break; + } default: add_dummy_decorator = true; } diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 480a8c03c41..2a639ad85d9 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -169,6 +169,7 @@ typedef enum eNodeSocketDatatype { SOCK_COLLECTION = 11, SOCK_TEXTURE = 12, SOCK_MATERIAL = 13, + SOCK_ATTRIBUTE = 14, } eNodeSocketDatatype; /* Socket shape. */ @@ -617,6 +618,28 @@ typedef struct bNodeSocketValueMaterial { struct Material *value; } bNodeSocketValueMaterial; +typedef struct bNodeSocketValueAttribute { + int data_type; /* eNodeSocketDatatype */ + int flag; /* eNodeSocketAttributeFlag */ + + /* XXX Does DNA support union? */ + float value_float[4]; + int value_int; + char value_bool; + char _pad[3]; + float min, max; + + /** 1024 = FILEMAX. */ + char attribute_name[1024]; +} bNodeSocketValueAttribute; + +typedef enum eNodeSocketAttributeFlag { + /* Use the name string to look up an attribute when not connected, + * instead of a default value. + */ + SOCK_ATTRIBUTE_USE_NAME = (1 << 0), +} eNodeSocketAttributeFlag; + /* Data structs, for node->storage. */ enum { CMP_NODE_MASKTYPE_ADD = 0, diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index b3f46509955..b2743ca60d2 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -78,6 +78,18 @@ static const EnumPropertyItem node_socket_data_type_items[] = { {SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""}, {SOCK_TEXTURE, "TEXTURE", 0, "Texture", ""}, {SOCK_MATERIAL, "MATERIAL", 0, "Material", ""}, + {SOCK_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +/* Subset of socket data types that are supported by attributes. */ +static const EnumPropertyItem node_socket_attribute_data_type_items[] = { + {SOCK_FLOAT, "FLOAT", 0, "Float", ""}, + {SOCK_INT, "INT", 0, "Integer", ""}, + {SOCK_BOOLEAN, "BOOLEAN", 0, "Boolean", ""}, + {SOCK_VECTOR, "VECTOR", 0, "Vector", ""}, + {SOCK_RGBA, "RGBA", 0, "Color", ""}, + {SOCK_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -106,6 +118,7 @@ static const EnumPropertyItem node_socket_type_items[] = { {SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""}, {SOCK_TEXTURE, "TEXTURE", 0, "Texture", ""}, {SOCK_MATERIAL, "MATERIAL", 0, "Material", ""}, + {SOCK_ATTRIBUTE, "ATTRIBUTE", 0, "Attribute", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -2001,6 +2014,7 @@ static bool switch_type_supported(const EnumPropertyItem *item) SOCK_STRING, SOCK_RGBA, SOCK_GEOMETRY, + SOCK_ATTRIBUTE, SOCK_OBJECT, SOCK_COLLECTION, SOCK_TEXTURE, @@ -2232,6 +2246,20 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeCurveMap_type_itemf( return itemf_function_check(rna_enum_attribute_type_items, attribute_curve_map_type_supported); } +static bool attribute_get_type_supported(const EnumPropertyItem *item) +{ + return ELEM( + item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR, CD_PROP_BOOL, CD_PROP_INT32); +} +static const EnumPropertyItem *rna_GeometryNodeAttributeGet_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_get_type_supported); +} + static StructRNA *rna_ShaderNode_register(Main *bmain, ReportList *reports, void *data, @@ -3245,6 +3273,38 @@ static void rna_NodeSocketStandard_vector_range( *softmax = dval->max; } +static void rna_NodeSocketStandard_attribute_range_float( + PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax) +{ + bNodeSocket *sock = ptr->data; + bNodeSocketValueAttribute *dval = sock->default_value; + + if (dval->max < dval->min) { + dval->max = dval->min; + } + + *min = -FLT_MAX; + *max = FLT_MAX; + *softmin = dval->min; + *softmax = dval->max; +} + +static void rna_NodeSocketStandard_attribute_range_int( + PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) +{ + bNodeSocket *sock = ptr->data; + bNodeSocketValueAttribute *dval = sock->default_value; + + if (dval->max < dval->min) { + dval->max = dval->min; + } + + *min = INT_MIN; + *max = INT_MAX; + *softmin = (int)dval->min; + *softmax = (int)dval->max; +} + /* using a context update function here, to avoid searching the node if possible */ static void rna_NodeSocketStandard_value_update(struct bContext *C, PointerRNA *ptr) { @@ -10013,6 +10073,19 @@ static void def_geo_raycast(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_attribute_get(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeGet_type_itemf"); + RNA_def_property_enum_default(prop, CD_PROP_FLOAT); + RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) @@ -10640,6 +10713,154 @@ static void rna_def_node_socket_string(BlenderRNA *brna, RNA_def_struct_sdna_from(srna, "bNodeSocket", NULL); } +static void rna_def_node_socket_attribute(BlenderRNA *brna, + const char *identifier, + const char *interface_idname) +{ + StructRNA *srna; + PropertyRNA *prop; + + PropertySubType subtype = PROP_NONE; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Attribute Node Socket", "Attribute socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueAttribute", "default_value"); + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "data_type"); + RNA_def_property_enum_items(prop, node_socket_attribute_data_type_items); + RNA_def_property_enum_default(prop, SOCK_ATTRIBUTE); + RNA_def_property_ui_text( + prop, "Attribute Data Type", "Type of data stored in attribute elements"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + prop = RNA_def_property(srna, "use_attribute_name", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SOCK_ATTRIBUTE_USE_NAME); + RNA_def_property_ui_text(prop, "Use Attribute Name", "Use the name string to look up an attribute when not connected"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + prop = RNA_def_property(srna, "default_value_float", PROP_FLOAT, subtype); + RNA_def_property_float_sdna(prop, NULL, "value_float[0]"); + // RNA_def_property_float_array_default(prop, value_default); + RNA_def_property_float_funcs(prop, NULL, NULL, "rna_NodeSocketStandard_attribute_range_float"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + prop = RNA_def_property(srna, "default_value_int", PROP_INT, subtype); + RNA_def_property_int_sdna(prop, NULL, "value_int"); + // RNA_def_property_float_array_default(prop, value_default); + RNA_def_property_int_funcs(prop, NULL, NULL, "rna_NodeSocketStandard_attribute_range_int"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + prop = RNA_def_property(srna, "default_value_bool", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "value_bool", 1); + // RNA_def_property_float_array_default(prop, value_default); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + prop = RNA_def_property(srna, "default_value_vector", PROP_FLOAT, subtype); + RNA_def_property_float_sdna(prop, NULL, "value_float"); + RNA_def_property_array(prop, 3); + // RNA_def_property_float_array_default(prop, value_default); + RNA_def_property_float_funcs(prop, NULL, NULL, "rna_NodeSocketStandard_attribute_range_float"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + prop = RNA_def_property(srna, "default_value_color", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_sdna(prop, NULL, "value_float"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + prop = RNA_def_property(srna, "attribute_name", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "attribute_name"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Attribute Name", "Name of an attribute"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + RNA_def_struct_sdna_from(srna, "bNodeSocket", NULL); + + /* socket interface */ + srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); + RNA_def_struct_ui_text(srna, "Attribute Node Socket Interface", "Attribute socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueAttribute", "default_value"); + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "data_type"); + RNA_def_property_enum_items(prop, node_socket_attribute_data_type_items); + RNA_def_property_enum_default(prop, SOCK_ATTRIBUTE); + RNA_def_property_ui_text( + prop, "Attribute Data Type", "Type of data stored in attribute elements"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); + + prop = RNA_def_property(srna, "default_value_float", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "value_float"); + // RNA_def_property_float_default(prop, value_default); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_float_funcs(prop, NULL, NULL, "rna_NodeSocketStandard_attribute_range_float"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); + + prop = RNA_def_property(srna, "default_value_int", PROP_INT, subtype); + RNA_def_property_int_sdna(prop, NULL, "value_int"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_int_funcs(prop, NULL, NULL, "rna_NodeSocketStandard_attribute_range_int"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); + + prop = RNA_def_property(srna, "default_value_bool", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "value_bool", 1); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); + + prop = RNA_def_property(srna, "default_value_vector", PROP_FLOAT, subtype); + RNA_def_property_float_sdna(prop, NULL, "value_float"); + RNA_def_property_array(prop, 3); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_float_funcs(prop, NULL, NULL, "rna_NodeSocketStandard_attribute_range_float"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); + + prop = RNA_def_property(srna, "default_value_color", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_sdna(prop, NULL, "value_float"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); + + prop = RNA_def_property(srna, "min_value", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "min"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Minimum Value", "Minimum value"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); + + prop = RNA_def_property(srna, "max_value", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "max"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Maximum Value", "Maximum value"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); + + prop = RNA_def_property(srna, "attribute_name", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "attribute_name"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Attribute Name", "Name of an attribute"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); + + RNA_def_struct_sdna_from(srna, "bNodeSocket", NULL); +} + static void rna_def_node_socket_shader(BlenderRNA *brna, const char *identifier, const char *interface_idname) @@ -10996,6 +11217,8 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna) rna_def_node_socket_string(brna, "NodeSocketString", "NodeSocketInterfaceString"); + rna_def_node_socket_attribute(brna, "NodeSocketAttribute", "NodeSocketInterfaceAttribute"); + rna_def_node_socket_shader(brna, "NodeSocketShader", "NodeSocketInterfaceShader"); rna_def_node_socket_virtual(brna, "NodeSocketVirtual"); diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 8314a443ba6..c2f2f358b9e 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -34,6 +34,7 @@ namespace blender::modifiers::geometry_nodes { using fn::CPPType; using fn::GValueMap; +using nodes::GeoNodeResolveParams; using nodes::GeoNodeExecParams; using namespace fn::multi_function_types; @@ -202,6 +203,11 @@ struct NodeState { bool has_been_executed = false; /** + * Used to check that nodes are not resolved more than once. + */ + bool has_been_resolved = false; + + /** * Becomes true when the node will never be executed again and its inputs are destructed. * Generally, a node has finished once all of its outputs with (potential) users have been * computed. @@ -384,6 +390,7 @@ class GeometryNodesEvaluator { task_pool_ = BLI_task_pool_create(this, TASK_PRIORITY_HIGH); this->create_states_for_reachable_nodes(); + this->resolve_runtime_types(); this->forward_group_inputs(); this->schedule_initial_nodes(); @@ -436,6 +443,42 @@ class GeometryNodesEvaluator { }); } + /** + * Back-propagate base type, domain, etc. for automatic sockets. + * Output nodes are resolved first, so their inputs can determine + * the most appropriate type details among all the targets. + */ + void resolve_runtime_types() + { + /* Breadth first topological starting at the output side. */ + /* TODO find a better FIFO container */ + Vector<DNode> nodes_to_check; + VectorSet<DNode> resolved_nodes; + /* Start at the output sockets. */ + for (const DInputSocket &socket : params_.output_sockets) { + nodes_to_check.prepend(socket.node()); + } + while (!nodes_to_check.is_empty()) { + const DNode node = nodes_to_check.pop_last(); + if (resolved_nodes.contains(node)) { + /* This node has been handled already. */ + continue; + } + resolved_nodes.add(node); + + /* Resolve node socket types. */ + NodeState *node_state = node_states_.lookup_key_as(node).state; + this->resolve_node_runtime_types(node, *node_state); + + /* Push all linked origins on the stack. */ + for (const InputSocketRef *input_ref : node->inputs()) { + const DInputSocket input{node.context(), input_ref}; + input.foreach_origin_socket( + [&](const DSocket origin) { nodes_to_check.prepend(origin.node()); }); + } + } + } + void initialize_node_state(const DNode node, NodeState &node_state, LinearAllocator<> &allocator) { /* Construct arrays of the correct size. */ @@ -549,6 +592,31 @@ class GeometryNodesEvaluator { node_state.~NodeState(); } + void resolve_node_runtime_types(const DNode node, NodeState &node_state) + { + const bNode &bnode = *node->bnode(); + + if (node_state.has_been_resolved) { + /* Nodes must not be resolved more than once. */ + BLI_assert_unreachable(); + } + node_state.has_been_resolved = true; + + /* Use the geometry node resolve callback if it exists. */ + if (bnode.typeinfo->geometry_node_resolve != nullptr) { + this->resolve_geometry_node(node, node_state); + return; + } + } + + void resolve_geometry_node(const DNode node, NodeState &node_state) + { + const bNode &bnode = *node->bnode(); + + GeoNodeResolveParams params; + bnode.typeinfo->geometry_node_resolve(params); + } + void forward_group_inputs() { for (auto &&item : params_.input_values.items()) { diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 926a81e87fd..d2151e79c56 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -147,6 +147,7 @@ set(SRC geometry/nodes/node_geo_attribute_convert.cc geometry/nodes/node_geo_attribute_curve_map.cc geometry/nodes/node_geo_attribute_fill.cc + geometry/nodes/node_geo_attribute_get.cc geometry/nodes/node_geo_attribute_map_range.cc geometry/nodes/node_geo_attribute_math.cc geometry/nodes/node_geo_attribute_mix.cc @@ -155,6 +156,7 @@ set(SRC geometry/nodes/node_geo_attribute_remove.cc geometry/nodes/node_geo_attribute_sample_texture.cc geometry/nodes/node_geo_attribute_separate_xyz.cc + geometry/nodes/node_geo_attribute_set.cc geometry/nodes/node_geo_attribute_transfer.cc geometry/nodes/node_geo_attribute_vector_math.cc geometry/nodes/node_geo_attribute_vector_rotate.cc @@ -331,6 +333,7 @@ set(SRC intern/derived_node_tree.cc intern/math_functions.cc + intern/attribute_ref.cc intern/node_common.c intern/node_exec.cc intern/node_geometry_exec.cc @@ -347,6 +350,7 @@ set(SRC geometry/node_geometry_util.hh texture/node_texture_util.h + NOD_attribute_ref.hh NOD_common.h NOD_composite.h NOD_derived_node_tree.hh diff --git a/source/blender/nodes/NOD_attribute_ref.hh b/source/blender/nodes/NOD_attribute_ref.hh new file mode 100644 index 00000000000..529a98da727 --- /dev/null +++ b/source/blender/nodes/NOD_attribute_ref.hh @@ -0,0 +1,77 @@ +/* + * 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. + */ + +#pragma once + +#include <string> + +#include "BLI_color.hh" +#include "BLI_float3.hh" + +#include "FN_cpp_type.hh" + +#include "BKE_attribute.h" +#include "BKE_attribute_access.hh" + +/** + * Runtime data struct that references attributes during evaluation of geometry node trees. + * Attributes are identified by a name and a domain type. + */ +struct AttributeRef { + private: + std::string name_; + CustomDataType data_type_; + + /* Single value to use when the socket is not connected. */ + union { + float value_float_; + int value_int_; + bool value_bool_; + blender::float3 value_float3_; + blender::ColorGeometry4f value_color_; + }; + + public: + static const AttributeRef None; + + public: + const std::string &name() const; + CustomDataType data_type() const; + + AttributeRef(); + AttributeRef(CustomDataType data_type); + AttributeRef(const std::string &name, CustomDataType data_type); + + friend std::ostream &operator<<(std::ostream &stream, const AttributeRef &geometry_set); + + bool valid() const; + + void *single_value_ptr(); + const void *single_value_ptr() const; + + template<typename T> T &single_value() + { + BLI_assert(blender::fn::CPPType::get<T>() == + *blender::bke::custom_data_type_to_cpp_type(data_type_)); + return *(T *)single_value_ptr(); + } + template<typename T> const T &single_value() const + { + BLI_assert(blender::fn::CPPType::get<T>() == + *blender::bke::custom_data_type_to_cpp_type(data_type_)); + return *(const T *)single_value_ptr(); + } +}; diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index ba646e7654a..7251c91710b 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -37,6 +37,7 @@ void register_node_type_geo_attribute_compare(void); void register_node_type_geo_attribute_convert(void); void register_node_type_geo_attribute_curve_map(void); void register_node_type_geo_attribute_fill(void); +void register_node_type_geo_attribute_get(void); void register_node_type_geo_attribute_map_range(void); void register_node_type_geo_attribute_math(void); void register_node_type_geo_attribute_mix(void); @@ -47,6 +48,7 @@ void register_node_type_geo_attribute_transfer(void); void register_node_type_geo_attribute_vector_math(void); void register_node_type_geo_attribute_vector_rotate(void); void register_node_type_geo_attribute_remove(void); +void register_node_type_geo_attribute_set(void); void register_node_type_geo_boolean(void); void register_node_type_geo_bounding_box(void); void register_node_type_geo_collection_info(void); diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 23474201daa..caefccfdba0 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -25,6 +25,7 @@ #include "DNA_node_types.h" +#include "NOD_attribute_ref.hh" #include "NOD_derived_node_tree.hh" struct Depsgraph; @@ -110,6 +111,9 @@ class GeoNodeExecParamsProvider { virtual bool lazy_output_is_required(StringRef identifier) const = 0; }; +class GeoNodeResolveParams { +}; + class GeoNodeExecParams { private: GeoNodeExecParamsProvider *provider_; @@ -289,6 +293,10 @@ class GeoNodeExecParams { const GeometryComponent &component, const AttributeDomain default_domain) const; + /* Create an attribute reference with a unique name to use for an output attribute. */ + AttributeRef declare_output_attribute(const StringRef identifier, + CustomDataType data_type) const; + private: /* Utilities for detecting common errors at when using this class. */ void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const; diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 19256f7383a..12008a84b3d 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -275,6 +275,8 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_co DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CONVERT, def_geo_attribute_convert, "ATTRIBUTE_CONVERT", AttributeConvert, "Attribute Convert", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CURVE_MAP, def_geo_attribute_curve_map, "ATTRIBUTE_CURVE_MAP", AttributeCurveMap, "Attribute Curve Map", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_FILL, def_geo_attribute_fill, "ATTRIBUTE_FILL", AttributeFill, "Attribute Fill", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_GET, def_geo_attribute_get, "ATTRIBUTE_GET", AttributeGet, "Attribute Get", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SET, 0, "ATTRIBUTE_SET", AttributeSet, "Attribute Set", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "ATTRIBUTE_MAP_RANGE", AttributeMapRange, "Attribute Map Range", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MATH, def_geo_attribute_math, "ATTRIBUTE_MATH", AttributeMath, "Attribute Math", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_MIX", AttributeMix, "Attribute Mix", "") diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index f4cd00b88ed..f36d5b3c9ca 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -20,6 +20,8 @@ #include "NOD_geometry.h" +#include "BLI_listbase.h" + #include "BKE_context.h" #include "BKE_node.h" #include "BKE_object.h" @@ -86,10 +88,21 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa static bool geometry_node_tree_validate_link(bNodeTree *UNUSED(ntree), bNodeLink *link) { - /* Geometry, string, object, material, texture and collection sockets can only be connected to - * themselves. The other types can be converted between each other. */ - if (ELEM(link->fromsock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT) && - ELEM(link->tosock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT)) { + /* Temporary exception: For the time being allow direct conversion of attributes to strings + * to facilitate use of new attribute sockets with old-style attribute name inputs. */ + if (link->fromsock->type == SOCK_ATTRIBUTE && link->tosock->type == SOCK_STRING) { + return true; + } + + /* Geometry, attribute, string, object, material, texture and collection sockets can only be + * connected to themselves. Basic data types can be converted between each other. Basic data + * types can be connected to attributes */ + bool from_elemental = ELEM( + link->fromsock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT); + bool to_elemental = ELEM( + link->tosock->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT); + bool to_attribute = (link->tosock->type == SOCK_ATTRIBUTE); + if (from_elemental && (to_elemental || to_attribute)) { return true; } return (link->tosock->type == link->fromsock->type); @@ -109,7 +122,8 @@ static bool geometry_node_tree_socket_type_valid(eNodeSocketDatatype socket_type SOCK_GEOMETRY, SOCK_COLLECTION, SOCK_TEXTURE, - SOCK_MATERIAL); + SOCK_MATERIAL, + SOCK_ATTRIBUTE); } void register_node_tree_type_geo(void) diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc index 46e9d36c09c..d253a27ae59 100644 --- a/source/blender/nodes/geometry/node_geometry_util.cc +++ b/source/blender/nodes/geometry/node_geometry_util.cc @@ -46,6 +46,7 @@ void update_attribute_input_socket_availabilities(bNode &node, const bool socket_is_available = name_is_available && ((socket->type == SOCK_STRING && mode_ == GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE) || + (socket->type == SOCK_ATTRIBUTE && mode_ == GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE) || (socket->type == SOCK_FLOAT && mode_ == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) || (socket->type == SOCK_INT && mode_ == GEO_NODE_ATTRIBUTE_INPUT_INTEGER) || (socket->type == SOCK_VECTOR && mode_ == GEO_NODE_ATTRIBUTE_INPUT_VECTOR) || @@ -55,6 +56,62 @@ void update_attribute_input_socket_availabilities(bNode &node, } } +void set_attribute_socket_data_type(bNode &node, + const StringRef name, + eNodeSocketDatatype data_type) +{ + LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) { + if (socket->type == SOCK_ATTRIBUTE && name == socket->name) { + ((bNodeSocketValueAttribute *)socket->default_value)->data_type = data_type; + } + } + LISTBASE_FOREACH (bNodeSocket *, socket, &node.outputs) { + if (socket->type == SOCK_ATTRIBUTE && name == socket->name) { + ((bNodeSocketValueAttribute *)socket->default_value)->data_type = data_type; + } + } +} + +static eNodeSocketDatatype customdata_to_socket_type(CustomDataType custom_data_type) +{ + switch (custom_data_type) { + case CD_AUTO_FROM_NAME: + return SOCK_ATTRIBUTE; + case CD_PROP_FLOAT: + return SOCK_FLOAT; + case CD_PROP_INT32: + return SOCK_INT; + case CD_PROP_FLOAT3: + return SOCK_VECTOR; + case CD_PROP_COLOR: + return SOCK_RGBA; + case CD_MLOOPCOL: + return SOCK_RGBA; + case CD_PROP_STRING: + return SOCK_STRING; + case CD_PROP_BOOL: + return SOCK_BOOLEAN; + case CD_PROP_FLOAT2: + return SOCK_VECTOR; + default: + BLI_assert_unreachable(); + return SOCK_ATTRIBUTE; + } + BLI_assert_unreachable(); +} + +void set_attribute_socket_data_type(bNode &node, + const StringRef name, + CustomDataType custom_data_type) +{ + set_attribute_socket_data_type(node, name, customdata_to_socket_type(custom_data_type)); +} + +void reset_attribute_socket_data_type(bNode &node, const StringRef name) +{ + set_attribute_socket_data_type(node, name, SOCK_ATTRIBUTE); +} + } // namespace blender::nodes bool geo_node_poll_default(bNodeType *UNUSED(ntype), diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 79a98c5ebf0..67d32d540a8 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -29,6 +29,7 @@ #include "BLT_translation.h" +#include "NOD_attribute_ref.hh" #include "NOD_geometry.h" #include "NOD_geometry_exec.hh" @@ -46,6 +47,14 @@ void update_attribute_input_socket_availabilities(bNode &node, const GeometryNodeAttributeInputMode mode, const bool name_is_available = true); +void set_attribute_socket_data_type(bNode &node, + const StringRef name, + eNodeSocketDatatype data_type); +void set_attribute_socket_data_type(bNode &node, + const StringRef name, + CustomDataType custom_data_type); +void reset_attribute_socket_data_type(bNode &node, const StringRef name); + 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_get.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_get.cc new file mode 100644 index 00000000000..e8f9b6afc2b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_get.cc @@ -0,0 +1,99 @@ +/* + * 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 "BLI_kdopbvh.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" + +#include "BKE_bvhutils.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_sample.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_attribute_get_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Name")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_attribute_get_out[] = { + {SOCK_ATTRIBUTE, N_("Attribute")}, + {-1, ""}, +}; + +static void geo_node_attribute_get_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); +} + +static void geo_node_attribute_get_init(bNodeTree *UNUSED(tree), bNode *node) +{ + node->custom1 = CD_PROP_FLOAT; +} + +static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + blender::nodes::set_attribute_socket_data_type( + *node, "Attribute", (CustomDataType)node->custom1); +} + +namespace blender::nodes { + +static void geo_node_attribute_get_exec(GeoNodeExecParams params) +{ + const CustomDataType data_type = (CustomDataType)params.node().custom1; + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + const std::string attribute_name = params.extract_input<std::string>("Name"); + + if (attribute_name.empty()) { + params.set_output("Attribute", AttributeRef::None); + return; + } + + AttributeRef attribute(attribute_name, data_type); + + /* TODO check for existence of the attribute on the geometry. + * This isn't really necessary for it to function, but can help catch invalid + * references early in the node graph. + * Technically this node does not even need a geometry input other than for + * checking validity. It could also just create an AttributeRef on good faith. + */ + + params.set_output("Attribute", attribute); +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_get() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_GET, "Attribute Get", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_attribute_get_in, geo_node_attribute_get_out); + node_type_init(&ntype, geo_node_attribute_get_init); + node_type_update(&ntype, geo_node_attribute_vector_math_update); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_get_exec; + ntype.draw_buttons = geo_node_attribute_get_layout; + nodeRegisterType(&ntype); +} 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 9309863f4e9..7ff68e78356 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc @@ -25,18 +25,18 @@ static bNodeSocketTemplate geo_node_attribute_math_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("A")}, + {SOCK_ATTRIBUTE, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, {SOCK_FLOAT, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_STRING, N_("B")}, + {SOCK_ATTRIBUTE, 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_ATTRIBUTE, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, {SOCK_FLOAT, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_STRING, N_("Result")}, {-1, ""}, }; static bNodeSocketTemplate geo_node_attribute_math_out[] = { {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_ATTRIBUTE, N_("Result")}, {-1, ""}, }; @@ -128,6 +128,11 @@ static void geo_node_attribute_math_init(bNodeTree *UNUSED(tree), bNode *node) data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; data->input_type_c = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; node->storage = data; + + blender::nodes::set_attribute_socket_data_type(*node, "A", SOCK_FLOAT); + blender::nodes::set_attribute_socket_data_type(*node, "B", SOCK_FLOAT); + blender::nodes::set_attribute_socket_data_type(*node, "C", SOCK_FLOAT); + blender::nodes::set_attribute_socket_data_type(*node, "Result", SOCK_FLOAT); } namespace blender::nodes { @@ -204,16 +209,9 @@ static void do_math_operation(const VArray<float> &span_input, static AttributeDomain get_result_domain(const GeometryComponent &component, const GeoNodeExecParams ¶ms, - const NodeMathOperation operation, - StringRef result_name) + const NodeMathOperation operation) { - /* Use the domain of the result attribute if it already exists. */ - std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); - if (result_info) { - return result_info->domain; - } - - /* Otherwise use the highest priority domain from existing input attributes, or the default. */ + /* 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)) { @@ -224,19 +222,19 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, return params.get_highest_priority_input_domain({"A"}, component, default_domain); } -static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecParams ¶ms) +static void attribute_math_calc(GeometryComponent &component, + const GeoNodeExecParams ¶ms, + const AttributeRef &result) { 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 AttributeDomain result_domain = get_result_domain( - component, params, operation, result_name); + const AttributeDomain result_domain = get_result_domain(component, params, operation); OutputAttribute_Typed<float> attribute_result = - component.attribute_try_get_for_output_only<float>(result_name, result_domain); + component.attribute_try_get_for_output_only<float>(result.name(), result_domain); if (!attribute_result) { return; } @@ -270,20 +268,23 @@ static void attribute_math_calc(GeometryComponent &component, const GeoNodeExecP static void geo_node_attribute_math_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + AttributeRef result = params.declare_output_attribute("Result", CD_PROP_FLOAT); geometry_set = geometry_set_realize_instances(geometry_set); if (geometry_set.has<MeshComponent>()) { - attribute_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params); + attribute_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params, result); } if (geometry_set.has<PointCloudComponent>()) { - attribute_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), params); + attribute_math_calc( + geometry_set.get_component_for_write<PointCloudComponent>(), params, result); } if (geometry_set.has<CurveComponent>()) { - attribute_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params); + attribute_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params, result); } params.set_output("Geometry", geometry_set); + params.set_output("Result", result); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc index e4f3230ebb9..0a2ace160e6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc @@ -18,7 +18,7 @@ static bNodeSocketTemplate geo_node_attribute_remove_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, + {SOCK_ATTRIBUTE, N_("Attribute"), 0.0f, 0.0f, @@ -36,21 +36,26 @@ static bNodeSocketTemplate geo_node_attribute_remove_out[] = { {-1, ""}, }; +static void geo_node_attribute_remove_init(bNodeTree *UNUSED(tree), bNode *node) +{ + blender::nodes::set_attribute_socket_data_type(*node, "Attribute", SOCK_ATTRIBUTE); +} + namespace blender::nodes { static void remove_attribute(GeometryComponent &component, GeoNodeExecParams ¶ms, - Span<std::string> attribute_names) + Span<AttributeRef> attribute_refs) { - for (std::string attribute_name : attribute_names) { - if (attribute_name.empty()) { + for (const AttributeRef &attribute_ref : attribute_refs) { + if (!attribute_ref.valid()) { continue; } - if (!component.attribute_try_delete(attribute_name)) { + if (!component.attribute_try_delete(attribute_ref.name())) { params.error_message_add(NodeWarningType::Error, - TIP_("Cannot delete attribute with name \"") + attribute_name + - "\""); + TIP_("Cannot delete attribute with name \"") + + attribute_ref.name() + "\""); } } } @@ -58,21 +63,21 @@ static void remove_attribute(GeometryComponent &component, static void geo_node_attribute_remove_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); - Vector<std::string> attribute_names = params.extract_multi_input<std::string>("Attribute"); + Vector<AttributeRef> attribute_refs = params.extract_multi_input<AttributeRef>("Attribute"); geometry_set = geometry_set_realize_instances(geometry_set); if (geometry_set.has<MeshComponent>()) { remove_attribute( - geometry_set.get_component_for_write<MeshComponent>(), params, attribute_names); + geometry_set.get_component_for_write<MeshComponent>(), params, attribute_refs); } if (geometry_set.has<PointCloudComponent>()) { remove_attribute( - geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_names); + geometry_set.get_component_for_write<PointCloudComponent>(), params, attribute_refs); } if (geometry_set.has<CurveComponent>()) { remove_attribute( - geometry_set.get_component_for_write<CurveComponent>(), params, attribute_names); + geometry_set.get_component_for_write<CurveComponent>(), params, attribute_refs); } params.set_output("Geometry", geometry_set); @@ -86,6 +91,7 @@ void register_node_type_geo_attribute_remove() geo_node_type_base( &ntype, GEO_NODE_ATTRIBUTE_REMOVE, "Attribute Remove", NODE_CLASS_ATTRIBUTE, 0); node_type_socket_templates(&ntype, geo_node_attribute_remove_in, geo_node_attribute_remove_out); + node_type_init(&ntype, geo_node_attribute_remove_init); ntype.geometry_node_execute = blender::nodes::geo_node_attribute_remove_exec; nodeRegisterType(&ntype); } 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 e0a3f5ad334..46c6ab409a7 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 @@ -31,29 +31,28 @@ static bNodeSocketTemplate geo_node_attribute_sample_texture_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, {SOCK_TEXTURE, N_("Texture")}, - {SOCK_STRING, N_("Mapping")}, - {SOCK_STRING, N_("Result")}, + {SOCK_ATTRIBUTE, N_("Mapping"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, {-1, ""}, }; static bNodeSocketTemplate geo_node_attribute_sample_texture_out[] = { {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_ATTRIBUTE, N_("Result")}, {-1, ""}, }; +static void geo_node_attribute_sample_texture_init(bNodeTree *UNUSED(tree), bNode *node) +{ + blender::nodes::set_attribute_socket_data_type(*node, "Mapping", SOCK_VECTOR); + blender::nodes::set_attribute_socket_data_type(*node, "Result", SOCK_RGBA); +} + namespace blender::nodes { static AttributeDomain get_result_domain(const GeometryComponent &component, - const StringRef result_name, const StringRef map_name) { - /* Use the domain of the result attribute if it already exists. */ - std::optional<AttributeMetaData> result_info = component.attribute_get_meta_data(result_name); - if (result_info) { - return result_info->domain; - } - - /* Otherwise use the name of the map attribute. */ + /* Use the name of the map attribute. */ std::optional<AttributeMetaData> map_info = component.attribute_get_meta_data(map_name); if (map_info) { return map_info->domain; @@ -63,31 +62,31 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, return ATTR_DOMAIN_POINT; } -static void execute_on_component(GeometryComponent &component, const GeoNodeExecParams ¶ms) +static void execute_on_component(GeometryComponent &component, + const GeoNodeExecParams ¶ms, + const AttributeRef &result_ref) { Tex *texture = params.get_input<Tex *>("Texture"); 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)) { + const AttributeRef mapping_ref = params.get_input<AttributeRef>("Mapping"); + if (!component.attribute_exists(mapping_ref.name())) { return; } - const AttributeDomain result_domain = get_result_domain( - component, result_attribute_name, mapping_name); + const AttributeDomain result_domain = get_result_domain(component, mapping_ref.name()); OutputAttribute_Typed<ColorGeometry4f> attribute_out = - component.attribute_try_get_for_output_only<ColorGeometry4f>(result_attribute_name, + component.attribute_try_get_for_output_only<ColorGeometry4f>(result_ref.name(), result_domain); if (!attribute_out) { return; } GVArray_Typed<float3> mapping_attribute = component.attribute_get_for_read<float3>( - mapping_name, result_domain, {0, 0, 0}); + mapping_ref.name(), result_domain, {0, 0, 0}); MutableSpan<ColorGeometry4f> colors = attribute_out.as_span(); threading::parallel_for(IndexRange(mapping_attribute.size()), 128, [&](IndexRange range) { @@ -107,20 +106,25 @@ static void execute_on_component(GeometryComponent &component, const GeoNodeExec static void geo_node_attribute_sample_texture_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + AttributeRef result_attribute_ref = params.declare_output_attribute("Result", CD_PROP_COLOR); geometry_set = geometry_set_realize_instances(geometry_set); if (geometry_set.has<MeshComponent>()) { - execute_on_component(geometry_set.get_component_for_write<MeshComponent>(), params); + execute_on_component( + geometry_set.get_component_for_write<MeshComponent>(), params, result_attribute_ref); } if (geometry_set.has<PointCloudComponent>()) { - execute_on_component(geometry_set.get_component_for_write<PointCloudComponent>(), params); + execute_on_component( + geometry_set.get_component_for_write<PointCloudComponent>(), params, result_attribute_ref); } if (geometry_set.has<CurveComponent>()) { - execute_on_component(geometry_set.get_component_for_write<CurveComponent>(), params); + execute_on_component( + geometry_set.get_component_for_write<CurveComponent>(), params, result_attribute_ref); } params.set_output("Geometry", geometry_set); + params.set_output("Result", result_attribute_ref); } } // namespace blender::nodes @@ -135,6 +139,7 @@ void register_node_type_geo_sample_texture() NODE_CLASS_ATTRIBUTE, 0); node_type_size_preset(&ntype, NODE_SIZE_LARGE); + node_type_init(&ntype, &geo_node_attribute_sample_texture_init); node_type_socket_templates( &ntype, geo_node_attribute_sample_texture_in, geo_node_attribute_sample_texture_out); ntype.geometry_node_execute = blender::nodes::geo_node_attribute_sample_texture_exec; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_set.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_set.cc new file mode 100644 index 00000000000..8fc96d4b616 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_set.cc @@ -0,0 +1,107 @@ +/* + * 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 "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_attribute_set_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_ATTRIBUTE, N_("Attribute")}, + {SOCK_STRING, N_("Name")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_attribute_set_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_attribute_set_init(bNodeTree *UNUSED(tree), bNode *node) +{ + blender::nodes::set_attribute_socket_data_type(*node, "Attribute", SOCK_ATTRIBUTE); +} + +namespace blender::nodes { + +static void set_attribute(GeometryComponent &component, + const GeoNodeExecParams ¶ms, + const AttributeRef &attribute_ref, + const StringRef attribute_name) +{ + ReadAttributeLookup attribute_input = component.attribute_try_get_for_read( + attribute_ref.name(), attribute_ref.data_type()); + + if (attribute_input) { + OutputAttribute attribute_output = component.attribute_try_get_for_output_only( + attribute_name, attribute_input.domain, attribute_ref.data_type()); + + if (attribute_output) { + threading::parallel_for(IndexRange(attribute_output->size()), 512, [&](IndexRange range) { + BUFFER_FOR_CPP_TYPE_VALUE(attribute_output.cpp_type(), buffer); + for (const int i : range) { + attribute_input.varray->get(i, buffer); + attribute_output->set_by_relocate(i, buffer); + } + }); + } + + attribute_output.save(); + } +} + +static void geo_node_attribute_set_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + const AttributeRef &attribute_ref = params.extract_input<AttributeRef>("Attribute"); + const std::string attribute_name = params.extract_input<std::string>("Name"); + + if (attribute_name.empty()) { + params.set_output("Geometry", geometry_set); + return; + } + + for (const GeometryComponentType component_type : {GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_INSTANCES, + GEO_COMPONENT_TYPE_VOLUME, + GEO_COMPONENT_TYPE_CURVE}) { + if (geometry_set.has(component_type)) { + set_attribute(geometry_set.get_component_for_write(component_type), + params, + attribute_ref, + attribute_name); + } + } + + params.set_output("Geometry", geometry_set); +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_set() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_ATTRIBUTE_SET, "Attribute Set", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_attribute_set_in, geo_node_attribute_set_out); + node_type_init(&ntype, geo_node_attribute_set_init); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_set_exec; + nodeRegisterType(&ntype); +} 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 e2cf6e8b480..dbdf42f38b4 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 @@ -26,20 +26,20 @@ static bNodeSocketTemplate geo_node_attribute_vector_math_in[] = { {SOCK_GEOMETRY, N_("Geometry")}, - {SOCK_STRING, N_("A")}, + {SOCK_ATTRIBUTE, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, {SOCK_VECTOR, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, - {SOCK_STRING, N_("B")}, + {SOCK_ATTRIBUTE, N_("B"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, {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_ATTRIBUTE, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, {SOCK_VECTOR, N_("C"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX}, {SOCK_FLOAT, 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")}, + {SOCK_ATTRIBUTE, N_("Result")}, {-1, ""}, }; @@ -112,6 +112,11 @@ static void geo_node_attribute_vector_math_init(bNodeTree *UNUSED(tree), bNode * data->input_type_a = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; data->input_type_b = GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE; node->storage = data; + + blender::nodes::set_attribute_socket_data_type(*node, "A", SOCK_VECTOR); + blender::nodes::set_attribute_socket_data_type(*node, "B", SOCK_VECTOR); + blender::nodes::set_attribute_socket_data_type(*node, "C", SOCK_VECTOR); + blender::nodes::set_attribute_socket_data_type(*node, "Result", SOCK_VECTOR); } static CustomDataType operation_get_result_type(const NodeVectorMathOperation operation) @@ -171,6 +176,20 @@ static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNod "C", (GeometryNodeAttributeInputMode)node_storage->input_type_c, operation_use_input_c(operation)); + + blender::nodes::set_attribute_socket_data_type(*node, "A", SOCK_VECTOR); + blender::nodes::set_attribute_socket_data_type( + *node, "B", ELEM(operation, NODE_VECTOR_MATH_SCALE) ? SOCK_FLOAT : SOCK_VECTOR); + blender::nodes::set_attribute_socket_data_type( + *node, "C", ELEM(operation, NODE_VECTOR_MATH_REFRACT) ? SOCK_FLOAT : SOCK_VECTOR); + blender::nodes::set_attribute_socket_data_type(*node, + "Result", + ELEM(operation, + NODE_VECTOR_MATH_DOT_PRODUCT, + NODE_VECTOR_MATH_DISTANCE, + NODE_VECTOR_MATH_LENGTH) ? + SOCK_FLOAT : + SOCK_VECTOR); } static void do_math_operation_fl3_fl3_to_fl3(const VArray<float3> &input_a, @@ -385,15 +404,8 @@ static void do_math_operation_fl3_to_fl(const VArray<float3> &input_a, static AttributeDomain get_result_domain(const GeometryComponent &component, const GeoNodeExecParams ¶ms, - const NodeVectorMathOperation operation, - StringRef result_name) + const NodeVectorMathOperation operation) { - /* Use the domain of the result attribute if it already exists. */ - ReadAttributeLookup 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)) { @@ -406,12 +418,12 @@ static AttributeDomain get_result_domain(const GeometryComponent &component, } static void attribute_vector_math_calc(GeometryComponent &component, - const GeoNodeExecParams ¶ms) + GeoNodeExecParams ¶ms, + const AttributeRef& result) { 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; @@ -421,9 +433,7 @@ static void attribute_vector_math_calc(GeometryComponent &component, const CustomDataType read_type_c = operation_get_read_type_c(operation); /* The result domain is always point for now. */ - const CustomDataType result_type = operation_get_result_type(operation); - const AttributeDomain result_domain = get_result_domain( - component, params, operation, result_name); + const AttributeDomain result_domain = get_result_domain(component, params, operation); GVArrayPtr attribute_a = params.get_input_attribute( "A", component, result_domain, read_type_a, nullptr); @@ -447,7 +457,7 @@ static void attribute_vector_math_calc(GeometryComponent &component, /* Get result attribute first, in case it has to overwrite one of the existing attributes. */ OutputAttribute attribute_result = component.attribute_try_get_for_output_only( - result_name, result_domain, result_type); + result.name(), result_domain, result.data_type()); if (!attribute_result) { return; } @@ -519,22 +529,32 @@ static void attribute_vector_math_calc(GeometryComponent &component, static void geo_node_attribute_vector_math_exec(GeoNodeExecParams params) { + const bNode &node = params.node(); + const NodeAttributeVectorMath *node_storage = (const NodeAttributeVectorMath *)node.storage; + const NodeVectorMathOperation operation = (NodeVectorMathOperation)node_storage->operation; + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); geometry_set = geometry_set_realize_instances(geometry_set); + const CustomDataType result_type = operation_get_result_type(operation); + AttributeRef result = params.declare_output_attribute("Result", result_type); + if (geometry_set.has<MeshComponent>()) { - attribute_vector_math_calc(geometry_set.get_component_for_write<MeshComponent>(), params); + attribute_vector_math_calc( + geometry_set.get_component_for_write<MeshComponent>(), params, result); } if (geometry_set.has<PointCloudComponent>()) { - attribute_vector_math_calc(geometry_set.get_component_for_write<PointCloudComponent>(), - params); + attribute_vector_math_calc( + geometry_set.get_component_for_write<PointCloudComponent>(), params, result); } if (geometry_set.has<CurveComponent>()) { - attribute_vector_math_calc(geometry_set.get_component_for_write<CurveComponent>(), params); + attribute_vector_math_calc( + geometry_set.get_component_for_write<CurveComponent>(), params, result); } params.set_output("Geometry", geometry_set); + params.set_output("Result", result); } } // namespace blender::nodes diff --git a/source/blender/nodes/intern/attribute_ref.cc b/source/blender/nodes/intern/attribute_ref.cc new file mode 100644 index 00000000000..e021a471415 --- /dev/null +++ b/source/blender/nodes/intern/attribute_ref.cc @@ -0,0 +1,67 @@ +/* + * 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 "NOD_attribute_ref.hh" + +#include "FN_cpp_type_make.hh" + +const AttributeRef AttributeRef::None = AttributeRef(); + +const std::string &AttributeRef::name() const +{ + return name_; +} + +CustomDataType AttributeRef::data_type() const +{ + return data_type_; +} + +AttributeRef::AttributeRef() : name_(""), data_type_(CD_PROP_FLOAT) +{ +} + +AttributeRef::AttributeRef(CustomDataType data_type) : name_(""), data_type_(data_type) +{ +} + +AttributeRef::AttributeRef(const std::string &name, CustomDataType data_type) + : name_(name), data_type_(data_type) +{ +} + +std::ostream &operator<<(std::ostream &stream, const AttributeRef &attr) +{ + stream << "<AttributeRef name=" << attr.name_ << ", data_type=" << attr.data_type_ << ">"; + return stream; +} + +bool AttributeRef::valid() const +{ + return !name_.empty(); +} + +void *AttributeRef::single_value_ptr() +{ + return &value_float_; +} + +const void *AttributeRef::single_value_ptr() const +{ + return &value_float_; +} + +MAKE_CPP_TYPE(AttributeRef, AttributeRef, CPPTypeFlags::Printable); diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index 17a13f2d1b0..8b61758a673 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -73,6 +73,34 @@ GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name, return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value); } + const DataTypeConversions &conversions = get_implicit_type_conversions(); + if (found_socket->type == SOCK_ATTRIBUTE) { + const AttributeRef attribute_ref = this->get_input<AttributeRef>(found_socket->identifier); + + if (attribute_ref.valid()) { + /* Try getting the attribute without the default value. */ + GVArrayPtr attribute = component.attribute_try_get_for_read( + attribute_ref.name(), domain, type); + if (attribute) { + return attribute; + } + + /* If the attribute doesn't exist, use the default value and output an error message */ + this->error_message_add(NodeWarningType::Error, + TIP_("No attribute with name \"") + attribute_ref.name() + "\""); + return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value); + } + else { + /* Broadcast the attribute single value */ + BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); + conversions.convert_to_uninitialized( + *blender::bke::custom_data_type_to_cpp_type(attribute_ref.data_type()), + *cpp_type, + attribute_ref.single_value_ptr(), + buffer); + return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, buffer); + } + } if (found_socket->type == SOCK_STRING) { const std::string name = this->get_input<std::string>(found_socket->identifier); /* Try getting the attribute without the default value. */ @@ -90,7 +118,6 @@ GVArrayPtr GeoNodeExecParams::get_input_attribute(const StringRef name, } return std::make_unique<fn::GVArray_For_SingleValue>(*cpp_type, domain_size, default_value); } - const DataTypeConversions &conversions = get_implicit_type_conversions(); if (found_socket->type == SOCK_FLOAT) { const float value = this->get_input<float>(found_socket->identifier); BUFFER_FOR_CPP_TYPE_VALUE(*cpp_type, buffer); @@ -190,6 +217,13 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain( return default_domain; } +AttributeRef GeoNodeExecParams::declare_output_attribute(const StringRef identifier, + CustomDataType data_type) const +{ + return AttributeRef("Node" + std::to_string(provider_->dnode->id()) + "::" + identifier, + data_type); +} + void GeoNodeExecParams::check_input_access(StringRef identifier, const CPPType *requested_type) const { diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 9f0a145ace2..c0a8382a661 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -44,6 +44,7 @@ #include "MEM_guardedalloc.h" +#include "NOD_attribute_ref.hh" #include "NOD_node_tree_multi_function.hh" #include "NOD_socket.h" @@ -97,6 +98,19 @@ struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree, dval->value[3] = stemp->val4; break; } + case SOCK_ATTRIBUTE: { + bNodeSocketValueAttribute *dval = (bNodeSocketValueAttribute *)sock->default_value; + dval->data_type = SOCK_FLOAT; + dval->value_int = (int)stemp->val1; + dval->value_float[0] = stemp->val1; + dval->value_float[1] = stemp->val2; + dval->value_float[2] = stemp->val3; + dval->value_float[3] = stemp->val4; + dval->value_bool = (bool)stemp->val1; + dval->min = stemp->min; + dval->max = stemp->max; + break; + } } return sock; @@ -271,6 +285,16 @@ void node_socket_init_default_value(bNodeSocket *sock) sock->default_value = dval; break; } + case SOCK_ATTRIBUTE: { + bNodeSocketValueAttribute *dval = (bNodeSocketValueAttribute *)MEM_callocN( + sizeof(bNodeSocketValueAttribute), "node socket value attribute"); + dval->data_type = SOCK_FLOAT; + dval->min = -FLT_MAX; + dval->max = FLT_MAX; + + sock->default_value = dval; + break; + } case SOCK_OBJECT: { bNodeSocketValueObject *dval = (bNodeSocketValueObject *)MEM_callocN( sizeof(bNodeSocketValueObject), "node socket value object"); @@ -369,6 +393,12 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from) *toval = *fromval; break; } + case SOCK_ATTRIBUTE: { + bNodeSocketValueAttribute *toval = (bNodeSocketValueAttribute *)to->default_value; + bNodeSocketValueAttribute *fromval = (bNodeSocketValueAttribute *)from->default_value; + *toval = *fromval; + break; + } case SOCK_OBJECT: { bNodeSocketValueObject *toval = (bNodeSocketValueObject *)to->default_value; bNodeSocketValueObject *fromval = (bNodeSocketValueObject *)from->default_value; @@ -662,6 +692,70 @@ static bNodeSocketType *make_socket_type_string() return socktype; } +static CustomDataType socket_to_customdata_type(eNodeSocketDatatype socket_type) +{ + switch (socket_type) { + case SOCK_ATTRIBUTE: + return CD_AUTO_FROM_NAME; + case SOCK_FLOAT: + return CD_PROP_FLOAT; + case SOCK_INT: + return CD_PROP_INT32; + case SOCK_VECTOR: + return CD_PROP_FLOAT3; + case SOCK_RGBA: + return CD_PROP_COLOR; + case SOCK_STRING: + return CD_PROP_STRING; + case SOCK_BOOLEAN: + return CD_PROP_BOOL; + default: + BLI_assert_unreachable(); + return CD_AUTO_FROM_NAME; + } + BLI_assert_unreachable(); +} + +static bNodeSocketType *make_socket_type_attribute() +{ + bNodeSocketType *socktype = make_standard_socket_type(SOCK_ATTRIBUTE, PROP_NONE); + socktype->get_cpp_type = []() { return &blender::fn::CPPType::get<AttributeRef>(); }; + socktype->get_cpp_value = [](const bNodeSocket &socket, void *r_value) { + const bNodeSocketValueAttribute *default_value = (bNodeSocketValueAttribute *) + socket.default_value; + const eNodeSocketDatatype socket_data_type = (eNodeSocketDatatype)default_value->data_type; + if (default_value->flag & SOCK_ATTRIBUTE_USE_NAME) { + new (r_value) AttributeRef(default_value->attribute_name, socket_to_customdata_type(socket_data_type)); + } + else { + AttributeRef *attribute_ref = new (r_value) + AttributeRef(socket_to_customdata_type(socket_data_type)); + switch (socket_data_type) { + case SOCK_FLOAT: + attribute_ref->single_value<float>() = default_value->value_float[0]; + break; + case SOCK_VECTOR: + attribute_ref->single_value<blender::float3>() = blender::float3( + default_value->value_float); + break; + case SOCK_RGBA: + attribute_ref->single_value<blender::ColorGeometry4f>() = blender::ColorGeometry4f( + default_value->value_float); + break; + case SOCK_INT: + attribute_ref->single_value<int>() = default_value->value_int; + break; + case SOCK_BOOLEAN: + attribute_ref->single_value<bool>() = default_value->value_bool; + break; + default: + break; + } + } + }; + return socktype; +} + MAKE_CPP_TYPE(Object, Object *, CPPTypeFlags::BasicType) MAKE_CPP_TYPE(Collection, Collection *, CPPTypeFlags::BasicType) MAKE_CPP_TYPE(Texture, Tex *, CPPTypeFlags::BasicType) @@ -749,6 +843,8 @@ void register_standard_node_socket_types(void) nodeRegisterSocketType(make_socket_type_string()); + nodeRegisterSocketType(make_socket_type_attribute()); + nodeRegisterSocketType(make_standard_socket_type(SOCK_SHADER, PROP_NONE)); nodeRegisterSocketType(make_socket_type_object()); diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c index 1aec280fd2b..b0917e2d1cb 100644 --- a/source/blender/nodes/intern/node_util.c +++ b/source/blender/nodes/intern/node_util.c @@ -475,6 +475,13 @@ static int node_datatype_priority(eNodeSocketDatatype from, eNodeSocketDatatype return -1; } } + case SOCK_ATTRIBUTE: + switch (from) { + case SOCK_ATTRIBUTE: + return 1; + default: + return -1; + } default: return -1; } diff --git a/source/blender/nodes/intern/type_conversions.cc b/source/blender/nodes/intern/type_conversions.cc index 1a71a3418a5..15612524941 100644 --- a/source/blender/nodes/intern/type_conversions.cc +++ b/source/blender/nodes/intern/type_conversions.cc @@ -22,6 +22,8 @@ #include "BLI_float2.hh" #include "BLI_float3.hh" +#include "NOD_attribute_ref.hh" + namespace blender::nodes { using fn::GVArrayPtr; @@ -70,6 +72,12 @@ static ColorGeometry4f float_to_color(const float &a) { return ColorGeometry4f(a, a, a, 1.0f); } +static AttributeRef float_to_attribute(const float &a) +{ + AttributeRef attr_ref; + attr_ref.single_value<float>() = a; + return attr_ref; +} static float3 float2_to_float3(const float2 &a) { @@ -91,6 +99,12 @@ static ColorGeometry4f float2_to_color(const float2 &a) { return ColorGeometry4f(a.x, a.y, 0.0f, 1.0f); } +static AttributeRef float2_to_attribute(const float2 &a) +{ + AttributeRef attr_ref; + attr_ref.single_value<float3>() = float3(a.x, a.y, 0.0f); + return attr_ref; +} static bool float3_to_bool(const float3 &a) { @@ -112,6 +126,12 @@ static ColorGeometry4f float3_to_color(const float3 &a) { return ColorGeometry4f(a.x, a.y, a.z, 1.0f); } +static AttributeRef float3_to_attribute(const float3 &a) +{ + AttributeRef attr_ref; + attr_ref.single_value<float3>() = a; + return attr_ref; +} static bool int_to_bool(const int32_t &a) { @@ -133,6 +153,12 @@ static ColorGeometry4f int_to_color(const int32_t &a) { return ColorGeometry4f((float)a, (float)a, (float)a, 1.0f); } +static AttributeRef int_to_attribute(const int &a) +{ + AttributeRef attr_ref; + attr_ref.single_value<int>() = a; + return attr_ref; +} static float bool_to_float(const bool &a) { @@ -154,6 +180,12 @@ static ColorGeometry4f bool_to_color(const bool &a) { return (a) ? ColorGeometry4f(1.0f, 1.0f, 1.0f, 1.0f) : ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f); } +static AttributeRef bool_to_attribute(const bool &a) +{ + AttributeRef attr_ref; + attr_ref.single_value<bool>() = a; + return attr_ref; +} static bool color_to_bool(const ColorGeometry4f &a) { @@ -175,6 +207,18 @@ static float3 color_to_float3(const ColorGeometry4f &a) { return float3(a.r, a.g, a.b); } +static AttributeRef color_to_attribute(const ColorGeometry4f &a) +{ + AttributeRef attr_ref; + attr_ref.single_value<ColorGeometry4f>() = a; + return attr_ref; +} + +/* Temporary implicit conversion to allow attributes directly connected to string inputs. */ +static std::string attribute_to_string(const AttributeRef& a) +{ + return a.name(); +} static DataTypeConversions create_implicit_conversions() { @@ -185,36 +229,44 @@ static DataTypeConversions create_implicit_conversions() add_implicit_conversion<float, int32_t, float_to_int>(conversions); add_implicit_conversion<float, bool, float_to_bool>(conversions); add_implicit_conversion<float, ColorGeometry4f, float_to_color>(conversions); + add_implicit_conversion<float, AttributeRef, float_to_attribute>(conversions); add_implicit_conversion<float2, float3, float2_to_float3>(conversions); add_implicit_conversion<float2, float, float2_to_float>(conversions); add_implicit_conversion<float2, int32_t, float2_to_int>(conversions); add_implicit_conversion<float2, bool, float2_to_bool>(conversions); add_implicit_conversion<float2, ColorGeometry4f, float2_to_color>(conversions); + add_implicit_conversion<float2, AttributeRef, float2_to_attribute>(conversions); add_implicit_conversion<float3, bool, float3_to_bool>(conversions); add_implicit_conversion<float3, float, float3_to_float>(conversions); add_implicit_conversion<float3, int32_t, float3_to_int>(conversions); add_implicit_conversion<float3, float2, float3_to_float2>(conversions); add_implicit_conversion<float3, ColorGeometry4f, float3_to_color>(conversions); + add_implicit_conversion<float3, AttributeRef, float3_to_attribute>(conversions); add_implicit_conversion<int32_t, bool, int_to_bool>(conversions); add_implicit_conversion<int32_t, float, int_to_float>(conversions); add_implicit_conversion<int32_t, float2, int_to_float2>(conversions); add_implicit_conversion<int32_t, float3, int_to_float3>(conversions); add_implicit_conversion<int32_t, ColorGeometry4f, int_to_color>(conversions); + add_implicit_conversion<int32_t, AttributeRef, int_to_attribute>(conversions); add_implicit_conversion<bool, float, bool_to_float>(conversions); add_implicit_conversion<bool, int32_t, bool_to_int>(conversions); add_implicit_conversion<bool, float2, bool_to_float2>(conversions); add_implicit_conversion<bool, float3, bool_to_float3>(conversions); add_implicit_conversion<bool, ColorGeometry4f, bool_to_color>(conversions); + add_implicit_conversion<bool, AttributeRef, bool_to_attribute>(conversions); add_implicit_conversion<ColorGeometry4f, bool, color_to_bool>(conversions); add_implicit_conversion<ColorGeometry4f, float, color_to_float>(conversions); add_implicit_conversion<ColorGeometry4f, int32_t, color_to_int>(conversions); add_implicit_conversion<ColorGeometry4f, float2, color_to_float2>(conversions); add_implicit_conversion<ColorGeometry4f, float3, color_to_float3>(conversions); + add_implicit_conversion<ColorGeometry4f, AttributeRef, color_to_attribute>(conversions); + + add_implicit_conversion<AttributeRef, std::string, attribute_to_string>(conversions); return conversions; } |