diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index c73f883846a..4afcae885c0 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1802,7 +1802,6 @@ typedef struct NodeGeometrySimulationOutput { #ifdef __cplusplus blender::Span items_span() const; blender::MutableSpan items_span(); - blender::IndexRange items_range() const; #endif } NodeGeometrySimulationOutput; @@ -1816,11 +1815,6 @@ typedef struct NodeRepeatItem { * names change. */ int identifier; - -#ifdef __cplusplus - static bool supports_type(eNodeSocketDatatype type); - std::string identifier_str() const; -#endif } NodeRepeatItem; typedef struct NodeGeometryRepeatInput { @@ -1839,8 +1833,6 @@ typedef struct NodeGeometryRepeatOutput { #ifdef __cplusplus blender::Span items_span() const; blender::MutableSpan items_span(); - NodeRepeatItem *add_item(const char *name, eNodeSocketDatatype type); - void set_item_name(NodeRepeatItem &item, const char *name); #endif } NodeGeometryRepeatOutput; diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 195ca1bccb0..a697664ead0 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -11,7 +11,9 @@ #include #include "BLI_function_ref.hh" +#include "BLI_linear_allocator.hh" #include "BLI_math_rotation.h" +#include "BLI_string.h" #include "BLI_string_utf8_symbols.h" #include "BLI_utildefines.h" @@ -596,7 +598,9 @@ static const EnumPropertyItem node_cryptomatte_layer_name_items[] = { # include "NOD_geometry.hh" # include "NOD_shader.h" # include "NOD_socket.hh" +# include "NOD_socket_items.hh" # include "NOD_texture.h" +# include "NOD_zone_socket_items.hh" # include "RE_engine.h" # include "RE_pipeline.h" @@ -604,6 +608,9 @@ static const EnumPropertyItem node_cryptomatte_layer_name_items[] = { # include "DNA_scene_types.h" # include "WM_api.hh" +using blender::nodes::RepeatItemsAccessor; +using blender::nodes::SimulationItemsAccessor; + extern FunctionRNA rna_NodeTree_poll_func; extern FunctionRNA rna_NodeTree_update_func; extern FunctionRNA rna_NodeTree_get_from_context_func; @@ -3122,105 +3129,6 @@ static void rna_NodeCryptomatte_update_remove(Main *bmain, Scene *scene, Pointer rna_Node_update(bmain, scene, ptr); } -static void rna_SimulationStateItem_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr) -{ - bNodeTree *ntree = reinterpret_cast(ptr->owner_id); - NodeSimulationItem *item = static_cast(ptr->data); - bNode *node = NOD_geometry_simulation_output_find_node_by_item(ntree, item); - - BKE_ntree_update_tag_node_property(ntree, node); - ED_node_tree_propagate_change(nullptr, bmain, ntree); -} - -static bNode *find_node_by_repeat_item(PointerRNA *ptr) -{ - const NodeRepeatItem *item = static_cast(ptr->data); - bNodeTree *ntree = reinterpret_cast(ptr->owner_id); - ntree->ensure_topology_cache(); - for (bNode *node : ntree->nodes_by_type("GeometryNodeRepeatOutput")) { - NodeGeometryRepeatOutput *storage = static_cast(node->storage); - if (storage->items_span().contains_ptr(item)) { - return node; - } - } - return nullptr; -} - -static void rna_RepeatItem_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr) -{ - bNodeTree *ntree = reinterpret_cast(ptr->owner_id); - bNode *node = find_node_by_repeat_item(ptr); - - BKE_ntree_update_tag_node_property(ntree, node); - ED_node_tree_propagate_change(nullptr, bmain, ntree); -} - -static bool rna_SimulationStateItem_socket_type_supported(const EnumPropertyItem *item) -{ - return NOD_geometry_simulation_output_item_socket_type_supported( - (eNodeSocketDatatype)item->value); -} - -static const EnumPropertyItem *rna_SimulationStateItem_socket_type_itemf(bContext * /*C*/, - PointerRNA * /*ptr*/, - PropertyRNA * /*prop*/, - bool *r_free) -{ - *r_free = true; - return itemf_function_check(rna_enum_node_socket_data_type_items, - rna_SimulationStateItem_socket_type_supported); -} - -static bool rna_RepeatItem_socket_type_supported(const EnumPropertyItem *item) -{ - return NodeRepeatItem::supports_type(eNodeSocketDatatype(item->value)); -} - -static const EnumPropertyItem *rna_RepeatItem_socket_type_itemf(bContext * /*C*/, - PointerRNA * /*ptr*/, - PropertyRNA * /*prop*/, - bool *r_free) -{ - *r_free = true; - return itemf_function_check(rna_enum_node_socket_data_type_items, - rna_RepeatItem_socket_type_supported); -} - -static void rna_SimulationStateItem_name_set(PointerRNA *ptr, const char *value) -{ - bNodeTree *ntree = reinterpret_cast(ptr->owner_id); - NodeSimulationItem *item = static_cast(ptr->data); - bNode *node = NOD_geometry_simulation_output_find_node_by_item(ntree, item); - NodeGeometrySimulationOutput *sim = static_cast(node->storage); - - const char *defname = nodeStaticSocketLabel(item->socket_type, 0); - NOD_geometry_simulation_output_item_set_unique_name(sim, item, value, defname); -} - -static void rna_RepeatItem_name_set(PointerRNA *ptr, const char *value) -{ - bNode *node = find_node_by_repeat_item(ptr); - NodeRepeatItem *item = static_cast(ptr->data); - NodeGeometryRepeatOutput *storage = static_cast(node->storage); - storage->set_item_name(*item, value); -} - -static void rna_SimulationStateItem_color_get(PointerRNA *ptr, float *values) -{ - NodeSimulationItem *item = static_cast(ptr->data); - - const char *socket_type_idname = nodeStaticSocketType(item->socket_type, 0); - ED_node_type_draw_color(socket_type_idname, values); -} - -static void rna_RepeatItem_color_get(PointerRNA *ptr, float *values) -{ - NodeRepeatItem *item = static_cast(ptr->data); - - const char *socket_type_idname = nodeStaticSocketType(item->socket_type, 0); - ED_node_type_draw_color(socket_type_idname, values); -} - static PointerRNA rna_Node_paired_output_get(PointerRNA *ptr) { bNodeTree *ntree = reinterpret_cast(ptr->owner_id); @@ -3266,200 +3174,148 @@ static bool rna_Node_pair_with_output( return true; } -static NodeSimulationItem *rna_NodeGeometrySimulationOutput_items_new( +template +static void rna_Node_ItemArray_remove(ID *id, + bNode *node, + Main *bmain, + ReportList *reports, + typename Accessor::ItemT *item_to_remove) +{ + blender::nodes::socket_items::SocketItemsRef ref = Accessor::get_items_from_node(*node); + if (item_to_remove < *ref.items || item_to_remove >= *ref.items + *ref.items_num) { + BKE_reportf(reports, RPT_ERROR, "Unable to locate item '%s' in node", item_to_remove->name); + return; + } + const int remove_index = item_to_remove - *ref.items; + blender::nodes::socket_items::remove_item( + ref.items, ref.items_num, ref.active_index, remove_index, Accessor::destruct_item); + + bNodeTree *ntree = reinterpret_cast(id); + BKE_ntree_update_tag_node_property(ntree, node); + ED_node_tree_propagate_change(nullptr, bmain, ntree); + WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); +} + +template static void rna_Node_ItemArray_clear(ID *id, bNode *node, Main *bmain) +{ + blender::nodes::socket_items::SocketItemsRef ref = Accessor::get_items_from_node(*node); + blender::nodes::socket_items::clear_items( + ref.items, ref.items_num, ref.active_index, Accessor::destruct_item); + + bNodeTree *ntree = reinterpret_cast(id); + BKE_ntree_update_tag_node_property(ntree, node); + ED_node_tree_propagate_change(nullptr, bmain, ntree); + WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); +} + +template +static void rna_Node_ItemArray_move( + ID *id, bNode *node, Main *bmain, const int from_index, const int to_index) +{ + blender::nodes::socket_items::SocketItemsRef ref = Accessor::get_items_from_node(*node); + const int items_num = *ref.items_num; + if (from_index < 0 || to_index < 0 || from_index >= items_num || to_index >= items_num) { + return; + } + blender::nodes::socket_items::move_item(*ref.items, items_num, from_index, to_index); + + bNodeTree *ntree = reinterpret_cast(id); + BKE_ntree_update_tag_node_property(ntree, node); + ED_node_tree_propagate_change(nullptr, bmain, ntree); + WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); +} + +template static PointerRNA rna_Node_ItemArray_active_get(PointerRNA *ptr) +{ + bNode *node = static_cast(ptr->data); + blender::nodes::socket_items::SocketItemsRef ref = Accessor::get_items_from_node(*node); + typename Accessor::ItemT *active_item = nullptr; + const int active_index = *ref.active_index; + const int items_num = *ref.items_num; + if (active_index >= 0 && active_index < items_num) { + active_item = &(*ref.items)[active_index]; + } + return RNA_pointer_create(ptr->owner_id, Accessor::item_srna, active_item); +} +template +static void rna_Node_ItemArray_active_set(PointerRNA *ptr, + PointerRNA value, + ReportList * /*reports*/) +{ + using ItemT = typename Accessor::ItemT; + bNode *node = static_cast(ptr->data); + ItemT *item = static_cast(value.data); + + blender::nodes::socket_items::SocketItemsRef ref = Accessor::get_items_from_node(*node); + if (item >= *ref.items && item < *ref.items + *ref.items_num) { + *ref.active_index = item - *ref.items; + } +} + +template +static void rna_Node_ItemArray_item_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr) +{ + using ItemT = typename Accessor::ItemT; + bNodeTree &ntree = *reinterpret_cast(ptr->owner_id); + ItemT &item = *static_cast(ptr->data); + bNode *node = blender::nodes::socket_items::find_node_by_item(ntree, item); + BLI_assert(node != nullptr); + + BKE_ntree_update_tag_node_property(&ntree, node); + ED_node_tree_propagate_change(nullptr, bmain, &ntree); +} + +template +static const EnumPropertyItem *rna_Node_ItemArray_socket_type_itemf(bContext * /*C*/, + PointerRNA * /*ptr*/, + PropertyRNA * /*prop*/, + bool *r_free) +{ + *r_free = true; + return itemf_function_check( + rna_enum_node_socket_data_type_items, [](const EnumPropertyItem *item) { + return Accessor::supports_socket_type(eNodeSocketDatatype(item->value)); + }); +} + +template +static void rna_Node_ItemArray_item_name_set(PointerRNA *ptr, const char *value) +{ + using ItemT = typename Accessor::ItemT; + bNodeTree &ntree = *reinterpret_cast(ptr->owner_id); + ItemT &item = *static_cast(ptr->data); + bNode *node = blender::nodes::socket_items::find_node_by_item(ntree, item); + BLI_assert(node != nullptr); + blender::nodes::socket_items::set_item_name_and_make_unique(*node, item, value); +} + +template +static void rna_Node_ItemArray_item_color_get(PointerRNA *ptr, float *values) +{ + using ItemT = typename Accessors::ItemT; + ItemT &item = *static_cast(ptr->data); + const char *socket_type_idname = nodeStaticSocketType(*Accessors::get_socket_type(item), 0); + ED_node_type_draw_color(socket_type_idname, values); +} + +template +typename Accessor::ItemT *rna_Node_ItemArray_new_with_socket_and_name( ID *id, bNode *node, Main *bmain, ReportList *reports, int socket_type, const char *name) { - NodeGeometrySimulationOutput *sim = static_cast(node->storage); - NodeSimulationItem *item = NOD_geometry_simulation_output_add_item( - sim, short(socket_type), name); - - if (item == nullptr) { - BKE_report(reports, RPT_ERROR, "Unable to create socket"); + using ItemT = typename Accessor::ItemT; + if (!Accessor::supports_socket_type(eNodeSocketDatatype(socket_type))) { + BKE_report(reports, RPT_ERROR, "Unable to create item with this socket type"); + return nullptr; } - else { - bNodeTree *ntree = reinterpret_cast(id); - BKE_ntree_update_tag_node_property(ntree, node); - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); - } - - return item; -} - -static NodeRepeatItem *rna_NodeGeometryRepeatOutput_items_new( - ID *id, bNode *node, Main *bmain, ReportList *reports, int socket_type, const char *name) -{ - NodeGeometryRepeatOutput *storage = static_cast(node->storage); - NodeRepeatItem *item = storage->add_item(name, eNodeSocketDatatype(socket_type)); - if (item == nullptr) { - BKE_report(reports, RPT_ERROR, "Unable to create socket"); - } - else { - bNodeTree *ntree = reinterpret_cast(id); - BKE_ntree_update_tag_node_property(ntree, node); - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); - } - - return item; -} - -static void rna_NodeGeometrySimulationOutput_items_remove( - ID *id, bNode *node, Main *bmain, ReportList *reports, NodeSimulationItem *item) -{ - NodeGeometrySimulationOutput *sim = static_cast(node->storage); - if (!NOD_geometry_simulation_output_contains_item(sim, item)) { - BKE_reportf(reports, RPT_ERROR, "Unable to locate item '%s' in node", item->name); - } - else { - NOD_geometry_simulation_output_remove_item(sim, item); - - bNodeTree *ntree = reinterpret_cast(id); - BKE_ntree_update_tag_node_property(ntree, node); - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); - } -} - -static void rna_NodeGeometryRepeatOutput_items_remove( - ID *id, bNode *node, Main *bmain, ReportList *reports, NodeRepeatItem *item) -{ - NodeGeometryRepeatOutput *storage = static_cast(node->storage); - if (!storage->items_span().contains_ptr(item)) { - BKE_reportf(reports, RPT_ERROR, "Unable to locate item '%s' in node", item->name); - return; - } - - const int remove_index = item - storage->items; - NodeRepeatItem *old_items = storage->items; - storage->items = MEM_cnew_array(storage->items_num - 1, __func__); - std::copy_n(old_items, remove_index, storage->items); - std::copy_n(old_items + remove_index + 1, - storage->items_num - remove_index - 1, - storage->items + remove_index); - - MEM_SAFE_FREE(old_items[remove_index].name); - storage->items_num--; - MEM_SAFE_FREE(old_items); + ItemT *new_item = blender::nodes::socket_items::add_item_with_socket_and_name( + *node, eNodeSocketDatatype(socket_type), name); bNodeTree *ntree = reinterpret_cast(id); BKE_ntree_update_tag_node_property(ntree, node); ED_node_tree_propagate_change(nullptr, bmain, ntree); WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); -} -static void rna_NodeGeometrySimulationOutput_items_clear(ID *id, bNode *node, Main *bmain) -{ - NodeGeometrySimulationOutput *sim = static_cast(node->storage); - NOD_geometry_simulation_output_clear_items(sim); - - bNodeTree *ntree = reinterpret_cast(id); - BKE_ntree_update_tag_node_property(ntree, node); - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); -} - -static void rna_NodeGeometryRepeatOutput_items_clear(ID * /*id*/, bNode *node, Main * /*bmain*/) -{ - NodeGeometryRepeatOutput *storage = static_cast(node->storage); - for (NodeRepeatItem &item : storage->items_span()) { - MEM_SAFE_FREE(item.name); - } - MEM_SAFE_FREE(storage->items); - storage->items_num = 0; - storage->active_index = 0; -} - -static void rna_NodeGeometrySimulationOutput_items_move( - ID *id, bNode *node, Main *bmain, int from_index, int to_index) -{ - NodeGeometrySimulationOutput *sim = static_cast(node->storage); - - if (from_index < 0 || from_index >= sim->items_num || to_index < 0 || to_index >= sim->items_num) - { - return; - } - - NOD_geometry_simulation_output_move_item(sim, from_index, to_index); - - bNodeTree *ntree = reinterpret_cast(id); - BKE_ntree_update_tag_node_property(ntree, node); - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); -} - -static void rna_NodeGeometryRepeatOutput_items_move( - ID *id, bNode *node, Main *bmain, int from_index, int to_index) -{ - NodeGeometryRepeatOutput *storage = static_cast(node->storage); - if (from_index < 0 || from_index >= storage->items_num || to_index < 0 || - to_index >= storage->items_num) - { - return; - } - - if (from_index < to_index) { - const NodeRepeatItem tmp = storage->items[from_index]; - for (int i = from_index; i < to_index; i++) { - storage->items[i] = storage->items[i + 1]; - } - storage->items[to_index] = tmp; - } - else if (from_index > to_index) { - const NodeRepeatItem tmp = storage->items[from_index]; - for (int i = from_index; i > to_index; i--) { - storage->items[i] = storage->items[i - 1]; - } - storage->items[to_index] = tmp; - } - - bNodeTree *ntree = reinterpret_cast(id); - BKE_ntree_update_tag_node_property(ntree, node); - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); -} - -static PointerRNA rna_NodeGeometrySimulationOutput_active_item_get(PointerRNA *ptr) -{ - bNode *node = static_cast(ptr->data); - NodeGeometrySimulationOutput *sim = static_cast(node->storage); - NodeSimulationItem *item = NOD_geometry_simulation_output_get_active_item(sim); - PointerRNA r_ptr = RNA_pointer_create(ptr->owner_id, &RNA_SimulationStateItem, item); - return r_ptr; -} - -static PointerRNA rna_NodeGeometryRepeatOutput_active_item_get(PointerRNA *ptr) -{ - bNode *node = static_cast(ptr->data); - NodeGeometryRepeatOutput *storage = static_cast(node->storage); - blender::MutableSpan items = storage->items_span(); - PointerRNA r_ptr{}; - if (items.index_range().contains(storage->active_index)) { - r_ptr = RNA_pointer_create(ptr->owner_id, &RNA_RepeatItem, &items[storage->active_index]); - } - return r_ptr; -} - -static void rna_NodeGeometrySimulationOutput_active_item_set(PointerRNA *ptr, - PointerRNA value, - ReportList * /*reports*/) -{ - bNode *node = static_cast(ptr->data); - NodeGeometrySimulationOutput *sim = static_cast(node->storage); - NOD_geometry_simulation_output_set_active_item(sim, - static_cast(value.data)); -} - -static void rna_NodeGeometryRepeatOutput_active_item_set(PointerRNA *ptr, - PointerRNA value, - ReportList * /*reports*/) -{ - bNode *node = static_cast(ptr->data); - NodeGeometryRepeatOutput *storage = static_cast(node->storage); - NodeRepeatItem *item = static_cast(value.data); - if (storage->items_span().contains_ptr(item)) { - storage->active_index = item - storage->items; - } + return new_item; } /* ******** Node Socket Types ******** */ @@ -8869,56 +8725,98 @@ static void def_geo_repeat_input(StructRNA *srna) def_common_zone_input(srna); } -static void rna_def_simulation_state_item(BlenderRNA *brna) +static void rna_def_node_item_array_socket_item_common(StructRNA *srna, + const char *accessor, + blender::LinearAllocator<> &allocator) { PropertyRNA *prop; - StructRNA *srna = RNA_def_struct(brna, "SimulationStateItem", nullptr); - RNA_def_struct_ui_text(srna, "Simulation Item", ""); - RNA_def_struct_sdna(srna, "NodeSimulationItem"); + char name_set_func[64]; + SNPRINTF(name_set_func, "rna_Node_ItemArray_item_name_set<%s>", accessor); + + char item_update_func[64]; + SNPRINTF(item_update_func, "rna_Node_ItemArray_item_update<%s>", accessor); + const char *item_update_func_ptr = allocator.copy_string(item_update_func).c_str(); + + char socket_type_itemf[64]; + SNPRINTF(socket_type_itemf, "rna_Node_ItemArray_socket_type_itemf<%s>", accessor); + + char color_get_func[64]; + SNPRINTF(color_get_func, "rna_Node_ItemArray_item_color_get<%s>", accessor); prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); - RNA_def_property_string_funcs(prop, nullptr, nullptr, "rna_SimulationStateItem_name_set"); + RNA_def_property_string_funcs( + prop, nullptr, nullptr, allocator.copy_string(name_set_func).c_str()); RNA_def_property_ui_text(prop, "Name", ""); RNA_def_struct_name_property(srna, prop); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_SimulationStateItem_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, item_update_func_ptr); prop = RNA_def_property(srna, "socket_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_node_socket_data_type_items); - RNA_def_property_enum_funcs(prop, nullptr, nullptr, "rna_SimulationStateItem_socket_type_itemf"); + RNA_def_property_enum_funcs( + prop, nullptr, nullptr, allocator.copy_string(socket_type_itemf).c_str()); RNA_def_property_ui_text(prop, "Socket Type", ""); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_SimulationStateItem_update"); - - prop = RNA_def_property(srna, "attribute_domain", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items); - RNA_def_property_ui_text( - prop, - "Attribute Domain", - "Attribute domain where the attribute is stored in the simulation state"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_SimulationStateItem_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, item_update_func_ptr); prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_array(prop, 4); - RNA_def_property_float_funcs(prop, "rna_SimulationStateItem_color_get", nullptr, nullptr); + RNA_def_property_float_funcs( + prop, allocator.copy_string(color_get_func).c_str(), nullptr, nullptr); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text( prop, "Color", "Color of the corresponding socket type in the node editor"); } -static void rna_def_geo_simulation_output_items(BlenderRNA *brna) +static void rna_def_node_item_array_common_functions(StructRNA *srna, + const char *item_name, + const char *accessor_name, + blender::LinearAllocator<> &allocator) { - StructRNA *srna; PropertyRNA *parm; FunctionRNA *func; - srna = RNA_def_struct(brna, "NodeGeometrySimulationOutputItems", nullptr); - RNA_def_struct_sdna(srna, "bNode"); - RNA_def_struct_ui_text(srna, "Items", "Collection of simulation items"); + char remove_call[64]; + SNPRINTF(remove_call, "rna_Node_ItemArray_remove<%s>", accessor_name); + char clear_call[64]; + SNPRINTF(clear_call, "rna_Node_ItemArray_clear<%s>", accessor_name); + char move_call[64]; + SNPRINTF(move_call, "rna_Node_ItemArray_move<%s>", accessor_name); - func = RNA_def_function(srna, "new", "rna_NodeGeometrySimulationOutput_items_new"); - RNA_def_function_ui_description(func, "Add a item to this simulation zone"); + func = RNA_def_function(srna, "remove", allocator.copy_string(remove_call).c_str()); + RNA_def_function_ui_description(func, "Remove an item"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "item", item_name, "Item", "The item to remove"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + + func = RNA_def_function(srna, "clear", allocator.copy_string(clear_call).c_str()); + RNA_def_function_ui_description(func, "Remove all items"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN); + + func = RNA_def_function(srna, "move", allocator.copy_string(move_call).c_str()); + RNA_def_function_ui_description(func, "Move an item to another position"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN); + parm = RNA_def_int( + func, "from_index", -1, 0, INT_MAX, "From Index", "Index of the item to move", 0, 10000); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + parm = RNA_def_int( + func, "to_index", -1, 0, INT_MAX, "To Index", "Target index for the item", 0, 10000); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); +} + +static void rna_def_node_item_array_new_with_socket_and_name(StructRNA *srna, + const char *item_name, + const char *accessor_name, + blender::LinearAllocator<> &allocator) +{ + PropertyRNA *parm; + FunctionRNA *func; + + char name[128]; + SNPRINTF(name, "rna_Node_ItemArray_new_with_socket_and_name<%s>", accessor_name); + + func = RNA_def_function(srna, "new", allocator.copy_string(name).c_str()); + RNA_def_function_ui_description(func, "Add an item at the end"); RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS); parm = RNA_def_enum(func, "socket_type", @@ -8930,28 +8828,45 @@ static void rna_def_geo_simulation_output_items(BlenderRNA *brna) parm = RNA_def_string(func, "name", nullptr, MAX_NAME, "Name", ""); RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); /* return value */ - parm = RNA_def_pointer(func, "item", "SimulationStateItem", "Item", "New item"); + parm = RNA_def_pointer(func, "item", item_name, "Item", "New item"); RNA_def_function_return(func, parm); +} - func = RNA_def_function(srna, "remove", "rna_NodeGeometrySimulationOutput_items_remove"); - RNA_def_function_ui_description(func, "Remove an item from this simulation zone"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS); - parm = RNA_def_pointer(func, "item", "SimulationStateItem", "Item", "The item to remove"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); +static void rna_def_simulation_state_item(BlenderRNA *brna) +{ + PropertyRNA *prop; + static blender::LinearAllocator<> allocator; - func = RNA_def_function(srna, "clear", "rna_NodeGeometrySimulationOutput_items_clear"); - RNA_def_function_ui_description(func, "Remove all items from this simulation zone"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN); + StructRNA *srna = RNA_def_struct(brna, "SimulationStateItem", nullptr); + RNA_def_struct_ui_text(srna, "Simulation Item", ""); + RNA_def_struct_sdna(srna, "NodeSimulationItem"); - func = RNA_def_function(srna, "move", "rna_NodeGeometrySimulationOutput_items_move"); - RNA_def_function_ui_description(func, "Move an item to another position"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN); - parm = RNA_def_int( - func, "from_index", -1, 0, INT_MAX, "From Index", "Index of the item to move", 0, 10000); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - parm = RNA_def_int( - func, "to_index", -1, 0, INT_MAX, "To Index", "Target index for the item", 0, 10000); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + rna_def_node_item_array_socket_item_common(srna, "SimulationItemsAccessor", allocator); + + prop = RNA_def_property(srna, "attribute_domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items); + RNA_def_property_ui_text( + prop, + "Attribute Domain", + "Attribute domain where the attribute is stored in the simulation state"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_Node_ItemArray_item_update"); +} + +static void rna_def_geo_simulation_output_items(BlenderRNA *brna) +{ + StructRNA *srna; + static blender::LinearAllocator<> allocator; + + srna = RNA_def_struct(brna, "NodeGeometrySimulationOutputItems", nullptr); + RNA_def_struct_sdna(srna, "bNode"); + RNA_def_struct_ui_text(srna, "Items", "Collection of simulation items"); + + rna_def_node_item_array_new_with_socket_and_name( + srna, "SimulationStateItem", "SimulationItemsAccessor", allocator); + rna_def_node_item_array_common_functions( + srna, "SimulationStateItem", "SimulationItemsAccessor", allocator); } static void def_geo_simulation_output(StructRNA *srna) @@ -8976,8 +8891,8 @@ static void def_geo_simulation_output(StructRNA *srna) prop = RNA_def_property(srna, "active_item", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "SimulationStateItem"); RNA_def_property_pointer_funcs(prop, - "rna_NodeGeometrySimulationOutput_active_item_get", - "rna_NodeGeometrySimulationOutput_active_item_set", + "rna_Node_ItemArray_active_get", + "rna_Node_ItemArray_active_set", nullptr, nullptr); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NO_DEG_UPDATE); @@ -8987,78 +8902,27 @@ static void def_geo_simulation_output(StructRNA *srna) static void rna_def_repeat_item(BlenderRNA *brna) { - PropertyRNA *prop; + static blender::LinearAllocator<> allocator; StructRNA *srna = RNA_def_struct(brna, "RepeatItem", nullptr); RNA_def_struct_ui_text(srna, "Repeat Item", ""); RNA_def_struct_sdna(srna, "NodeRepeatItem"); - prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); - RNA_def_property_string_funcs(prop, nullptr, nullptr, "rna_RepeatItem_name_set"); - RNA_def_property_ui_text(prop, "Name", ""); - RNA_def_struct_name_property(srna, prop); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_RepeatItem_update"); - - prop = RNA_def_property(srna, "socket_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, rna_enum_node_socket_data_type_items); - RNA_def_property_enum_funcs(prop, nullptr, nullptr, "rna_RepeatItem_socket_type_itemf"); - RNA_def_property_ui_text(prop, "Socket Type", ""); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_RepeatItem_update"); - - prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_array(prop, 4); - RNA_def_property_float_funcs(prop, "rna_RepeatItem_color_get", nullptr, nullptr); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text( - prop, "Color", "Color of the corresponding socket type in the node editor"); + rna_def_node_item_array_socket_item_common(srna, "RepeatItemsAccessor", allocator); } static void rna_def_geo_repeat_output_items(BlenderRNA *brna) { StructRNA *srna; - PropertyRNA *parm; - FunctionRNA *func; + static blender::LinearAllocator<> allocator; srna = RNA_def_struct(brna, "NodeGeometryRepeatOutputItems", nullptr); RNA_def_struct_sdna(srna, "bNode"); RNA_def_struct_ui_text(srna, "Items", "Collection of repeat items"); - func = RNA_def_function(srna, "new", "rna_NodeGeometryRepeatOutput_items_new"); - RNA_def_function_ui_description(func, "Add a item to this repeat zone"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS); - parm = RNA_def_enum(func, - "socket_type", - rna_enum_node_socket_data_type_items, - SOCK_GEOMETRY, - "Socket Type", - "Socket type of the item"); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - parm = RNA_def_string(func, "name", nullptr, MAX_NAME, "Name", ""); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - /* return value */ - parm = RNA_def_pointer(func, "item", "RepeatItem", "Item", "New item"); - RNA_def_function_return(func, parm); - - func = RNA_def_function(srna, "remove", "rna_NodeGeometryRepeatOutput_items_remove"); - RNA_def_function_ui_description(func, "Remove an item from this repeat zone"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS); - parm = RNA_def_pointer(func, "item", "RepeatItem", "Item", "The item to remove"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - - func = RNA_def_function(srna, "clear", "rna_NodeGeometryRepeatOutput_items_clear"); - RNA_def_function_ui_description(func, "Remove all items from this repeat zone"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN); - - func = RNA_def_function(srna, "move", "rna_NodeGeometryRepeatOutput_items_move"); - RNA_def_function_ui_description(func, "Move an item to another position"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN); - parm = RNA_def_int( - func, "from_index", -1, 0, INT_MAX, "From Index", "Index of the item to move", 0, 10000); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - parm = RNA_def_int( - func, "to_index", -1, 0, INT_MAX, "To Index", "Target index for the item", 0, 10000); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + rna_def_node_item_array_new_with_socket_and_name( + srna, "RepeatItem", "RepeatItemsAccessor", allocator); + rna_def_node_item_array_common_functions(srna, "RepeatItem", "RepeatItemsAccessor", allocator); } static void def_geo_repeat_output(StructRNA *srna) @@ -9083,8 +8947,8 @@ static void def_geo_repeat_output(StructRNA *srna) prop = RNA_def_property(srna, "active_item", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "RepeatItem"); RNA_def_property_pointer_funcs(prop, - "rna_NodeGeometryRepeatOutput_active_item_get", - "rna_NodeGeometryRepeatOutput_active_item_set", + "rna_Node_ItemArray_active_get", + "rna_Node_ItemArray_active_set", nullptr, nullptr); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NO_DEG_UPDATE); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 7cd01e716a6..708fe4df9ee 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -91,6 +91,7 @@ set(SRC intern/node_socket.cc intern/node_socket_declarations.cc intern/node_util.cc + intern/node_zone_socket_items.cc intern/socket_search_link.cc NOD_common.h @@ -101,6 +102,7 @@ set(SRC NOD_geometry_nodes_execute.hh NOD_geometry_nodes_lazy_function.hh NOD_geometry_nodes_log.hh + NOD_socket_items.hh NOD_math_functions.hh NOD_multi_function.hh NOD_node_declaration.hh @@ -113,6 +115,7 @@ set(SRC NOD_socket_search_link.hh NOD_static_types.h NOD_texture.h + NOD_zone_socket_items.hh intern/node_common.h intern/node_exec.hh intern/node_util.hh diff --git a/source/blender/nodes/NOD_geometry.hh b/source/blender/nodes/NOD_geometry.hh index b49ba956cde..474840a7fc7 100644 --- a/source/blender/nodes/NOD_geometry.hh +++ b/source/blender/nodes/NOD_geometry.hh @@ -10,55 +10,3 @@ extern bNodeTreeType *ntreeType_Geometry; void register_node_tree_type_geo(); void register_node_type_geo_custom_group(bNodeType *ntype); - -/* -------------------------------------------------------------------- */ -/** \name Simulation Output Node - * \{ */ - -bool NOD_geometry_simulation_output_item_socket_type_supported(eNodeSocketDatatype socket_type); - -/** - * Set a unique item name. - * \return True if the unique name differs from the original name. - */ -bool NOD_geometry_simulation_output_item_set_unique_name(NodeGeometrySimulationOutput *sim, - NodeSimulationItem *item, - const char *name, - const char *defname); - -/** - * Find the node owning this simulation state item. - */ -bNode *NOD_geometry_simulation_output_find_node_by_item(bNodeTree *ntree, - const NodeSimulationItem *item); - -bool NOD_geometry_simulation_output_contains_item(NodeGeometrySimulationOutput *sim, - const NodeSimulationItem *item); -NodeSimulationItem *NOD_geometry_simulation_output_get_active_item( - NodeGeometrySimulationOutput *sim); -void NOD_geometry_simulation_output_set_active_item(NodeGeometrySimulationOutput *sim, - NodeSimulationItem *item); -NodeSimulationItem *NOD_geometry_simulation_output_find_item(NodeGeometrySimulationOutput *sim, - const char *name); -NodeSimulationItem *NOD_geometry_simulation_output_add_item(NodeGeometrySimulationOutput *sim, - short socket_type, - const char *name); -NodeSimulationItem *NOD_geometry_simulation_output_insert_item(NodeGeometrySimulationOutput *sim, - short socket_type, - const char *name, - int index); -NodeSimulationItem *NOD_geometry_simulation_output_add_item_from_socket( - NodeGeometrySimulationOutput *sim, const bNode *from_node, const bNodeSocket *from_sock); -NodeSimulationItem *NOD_geometry_simulation_output_insert_item_from_socket( - NodeGeometrySimulationOutput *sim, - const bNode *from_node, - const bNodeSocket *from_sock, - int index); -void NOD_geometry_simulation_output_remove_item(NodeGeometrySimulationOutput *sim, - NodeSimulationItem *item); -void NOD_geometry_simulation_output_clear_items(NodeGeometrySimulationOutput *sim); -void NOD_geometry_simulation_output_move_item(NodeGeometrySimulationOutput *sim, - int from_index, - int to_index); - -/** \} */ diff --git a/source/blender/nodes/NOD_socket_items.hh b/source/blender/nodes/NOD_socket_items.hh new file mode 100644 index 00000000000..db423973c70 --- /dev/null +++ b/source/blender/nodes/NOD_socket_items.hh @@ -0,0 +1,285 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** + * Some nodes have a dynamic number of sockets (e.g. simulation input/output). These nodes store an + * array of items in their `bNode->storage` (e.g. `NodeSimulationItem`). Different nodes have + * slightly different storage requirements, but a lot of the logic is still the same between nodes. + * This file implements various shared functionality that can be used by different nodes to deal + * with these item arrays. + * + * In order to use the functions, one has to implement an "accessor" which tells the shared code + * how to deal with specific item arrays. Different functions have different requirements for the + * accessor. It's easiest to just look at existing accessors like #SimulationItemsAccessor and + * #RepeatItemsAccessor and to implement the same methods. + */ + +#include "BLI_string.h" +#include "BLI_string_utils.h" + +#include "BKE_node.h" +#include "BKE_node_runtime.hh" + +#include "NOD_socket.hh" + +namespace blender::nodes::socket_items { + +/** + * References a "C-Array" that is stored elsewhere. This is different from a MutableSpan, because + * one can even resize the array through this reference. + */ +template struct SocketItemsRef { + T **items; + int *items_num; + int *active_index; +}; + +/** + * Iterates over the node tree to find the node that this item belongs to. + */ +template +inline bNode *find_node_by_item(bNodeTree &ntree, const typename Accessor::ItemT &item) +{ + ntree.ensure_topology_cache(); + for (bNode *node : ntree.nodes_by_type(Accessor::node_idname)) { + SocketItemsRef array = Accessor::get_items_from_node(*node); + if (&item >= *array.items && &item < *array.items + *array.items_num) { + return node; + } + } + return nullptr; +} + +/** + * Low level utility to remove an item from the array and to shift the elements after it. + */ +template +inline void remove_item(T **items, + int *items_num, + int *active_index, + const int remove_index, + void (*destruct_item)(T *)) +{ + static_assert(std::is_trivial_v); + BLI_assert(remove_index >= 0); + BLI_assert(remove_index < *items_num); + + const int old_items_num = *items_num; + const int new_items_num = old_items_num - 1; + const int old_active_index = *active_index; + + T *old_items = *items; + T *new_items = MEM_cnew_array(new_items_num, __func__); + + std::copy_n(old_items, remove_index, new_items); + std::copy_n( + old_items + remove_index + 1, old_items_num - remove_index - 1, new_items + remove_index); + + destruct_item(&old_items[remove_index]); + MEM_SAFE_FREE(old_items); + + const int new_active_index = std::max( + 0, old_active_index == new_items_num ? new_items_num - 1 : old_active_index); + + *items = new_items; + *items_num = new_items_num; + *active_index = new_active_index; +} + +/** + * Low level utility to remove all elements from an items array. + */ +template +inline void clear_items(T **items, int *items_num, int *active_index, void (*destruct_item)(T *)) +{ + static_assert(std::is_trivial_v); + for (const int i : blender::IndexRange(*items_num)) { + destruct_item(&(*items)[i]); + } + MEM_SAFE_FREE(*items); + *items_num = 0; + *active_index = 0; +} + +/** + * Low level utility to move one item from one index to another. + */ +template +inline void move_item(T *items, const int items_num, const int from_index, const int to_index) +{ + static_assert(std::is_trivial_v); + BLI_assert(from_index >= 0); + BLI_assert(from_index < items_num); + BLI_assert(to_index >= 0); + BLI_assert(to_index < items_num); + UNUSED_VARS_NDEBUG(items_num); + + if (from_index == to_index) { + return; + } + + if (from_index < to_index) { + const T tmp = items[from_index]; + for (int i = from_index; i < to_index; i++) { + items[i] = items[i + 1]; + } + items[to_index] = tmp; + } + else if (from_index > to_index) { + const T tmp = items[from_index]; + for (int i = from_index; i > to_index; i--) { + items[i] = items[i - 1]; + } + items[to_index] = tmp; + } +} + +/** + * Changes the name of an existing item and makes sure that the name is unique among other the + * other items in the same array. + */ +template +inline void set_item_name_and_make_unique(bNode &node, + typename Accessor::ItemT &item, + const char *value) +{ + using ItemT = typename Accessor::ItemT; + SocketItemsRef array = Accessor::get_items_from_node(node); + const char *default_name = nodeStaticSocketLabel(*Accessor::get_socket_type(item), 0); + + char unique_name[MAX_NAME + 4]; + STRNCPY(unique_name, value); + + struct Args { + SocketItemsRef array; + ItemT *item; + } args = {array, &item}; + BLI_uniquename_cb( + [](void *arg, const char *name) { + const Args &args = *static_cast(arg); + for (ItemT &item : blender::MutableSpan(*args.array.items, *args.array.items_num)) { + if (&item != args.item) { + if (STREQ(*Accessor::get_name(item), name)) { + return true; + } + } + } + return false; + }, + &args, + default_name, + '.', + unique_name, + ARRAY_SIZE(unique_name)); + + char **item_name = Accessor::get_name(item); + MEM_SAFE_FREE(*item_name); + *item_name = BLI_strdup(unique_name); +} + +/** + * Add a new item at the end with the given socket type and name. + */ +template +inline typename Accessor::ItemT *add_item_with_socket_and_name( + bNode &node, const eNodeSocketDatatype socket_type, const char *name) +{ + using ItemT = typename Accessor::ItemT; + BLI_assert(Accessor::supports_socket_type(socket_type)); + + SocketItemsRef array = Accessor::get_items_from_node(node); + + ItemT *old_items = *array.items; + const int old_items_num = *array.items_num; + const int new_items_num = old_items_num + 1; + + ItemT *new_items = MEM_cnew_array(new_items_num, __func__); + std::copy_n(old_items, old_items_num, new_items); + ItemT &new_item = new_items[old_items_num]; + + Accessor::init_with_socket_type_and_name(node, new_item, socket_type, name); + + MEM_SAFE_FREE(old_items); + *array.items = new_items; + *array.items_num = new_items_num; + *array.active_index = old_items_num; + + return &new_item; +} + +/** + * Check if the link connects to the `extend_socket`. If yes, create a new item for the linked + * socket, update the node and then change the link to point to the new socket. + * \return False if the link should be removed. + */ +template +[[nodiscard]] inline bool try_add_item_via_extend_socket(bNodeTree &ntree, + bNode &extend_node, + bNodeSocket &extend_socket, + bNode &storage_node, + bNodeLink &link) +{ + using ItemT = typename Accessor::ItemT; + bNodeSocket *src_socket = nullptr; + if (link.tosock == &extend_socket) { + src_socket = link.fromsock; + } + else if (link.fromsock == &extend_socket) { + src_socket = link.tosock; + } + else { + return false; + } + const eNodeSocketDatatype socket_type = eNodeSocketDatatype(src_socket->type); + if (!Accessor::supports_socket_type(socket_type)) { + return false; + } + const ItemT *item = add_item_with_socket_and_name( + storage_node, socket_type, src_socket->name); + if (item == nullptr) { + return false; + } + update_node_declaration_and_sockets(ntree, extend_node); + const std::string item_identifier = Accessor::socket_identifier_for_item(*item); + if (extend_socket.is_input()) { + bNodeSocket *new_socket = nodeFindSocket(&extend_node, SOCK_IN, item_identifier.c_str()); + link.tosock = new_socket; + } + else { + bNodeSocket *new_socket = nodeFindSocket(&extend_node, SOCK_OUT, item_identifier.c_str()); + link.fromsock = new_socket; + } + return true; +} + +/** + * Allow the item array to be extended from any extend-socket in the node. + * \return False if the link should be removed. + */ +template +[[nodiscard]] inline bool try_add_item_via_any_extend_socket(bNodeTree &ntree, + bNode &extend_node, + bNode &storage_node, + bNodeLink &link) +{ + bNodeSocket *possible_extend_socket = nullptr; + if (link.fromnode == &extend_node) { + possible_extend_socket = link.fromsock; + } + if (link.tonode == &extend_node) { + possible_extend_socket = link.tosock; + } + if (possible_extend_socket == nullptr) { + return true; + } + if (!STREQ(possible_extend_socket->idname, "NodeSocketVirtual")) { + return true; + } + return try_add_item_via_extend_socket( + ntree, extend_node, *possible_extend_socket, storage_node, link); +} + +} // namespace blender::nodes::socket_items diff --git a/source/blender/nodes/NOD_zone_socket_items.hh b/source/blender/nodes/NOD_zone_socket_items.hh new file mode 100644 index 00000000000..72f4c8e073b --- /dev/null +++ b/source/blender/nodes/NOD_zone_socket_items.hh @@ -0,0 +1,127 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "DNA_node_types.h" + +#include "NOD_socket_items.hh" + +namespace blender::nodes { + +/** + * Makes it possible to use various functions (e.g. the ones in `NOD_socket_items.hh`) with + * simulation items. + */ +struct SimulationItemsAccessor { + using ItemT = NodeSimulationItem; + static StructRNA *item_srna; + static int node_type; + static constexpr const char *node_idname = "GeometryNodeSimulationOutput"; + + static socket_items::SocketItemsRef get_items_from_node(bNode &node) + { + auto *storage = static_cast(node.storage); + return {&storage->items, &storage->items_num, &storage->active_index}; + } + static void destruct_item(NodeSimulationItem *item) + { + MEM_SAFE_FREE(item->name); + } + static short *get_socket_type(NodeSimulationItem &item) + { + return &item.socket_type; + } + static char **get_name(NodeSimulationItem &item) + { + return &item.name; + } + static bool supports_socket_type(const eNodeSocketDatatype socket_type) + { + return ELEM(socket_type, + SOCK_FLOAT, + SOCK_VECTOR, + SOCK_RGBA, + SOCK_BOOLEAN, + SOCK_ROTATION, + SOCK_INT, + SOCK_STRING, + SOCK_GEOMETRY); + } + static void init_with_socket_type_and_name(bNode &node, + NodeSimulationItem &item, + const eNodeSocketDatatype socket_type, + const char *name) + { + auto *storage = static_cast(node.storage); + item.socket_type = socket_type; + item.identifier = storage->next_identifier++; + socket_items::set_item_name_and_make_unique(node, item, name); + } + static std::string socket_identifier_for_item(const NodeSimulationItem &item) + { + return "Item_" + std::to_string(item.identifier); + } +}; + +/** + * Makes it possible to use various functions (e.g. the ones in `NOD_socket_items.hh`) with + * repeat items. + */ +struct RepeatItemsAccessor { + using ItemT = NodeRepeatItem; + static StructRNA *item_srna; + static int node_type; + static constexpr const char *node_idname = "GeometryNodeRepeatOutput"; + + static socket_items::SocketItemsRef get_items_from_node(bNode &node) + { + auto *storage = static_cast(node.storage); + return {&storage->items, &storage->items_num, &storage->active_index}; + } + static void destruct_item(NodeRepeatItem *item) + { + MEM_SAFE_FREE(item->name); + } + static short *get_socket_type(NodeRepeatItem &item) + { + return &item.socket_type; + } + static char **get_name(NodeRepeatItem &item) + { + return &item.name; + } + static bool supports_socket_type(const eNodeSocketDatatype socket_type) + { + return ELEM(socket_type, + SOCK_FLOAT, + SOCK_VECTOR, + SOCK_RGBA, + SOCK_BOOLEAN, + SOCK_ROTATION, + SOCK_INT, + SOCK_STRING, + SOCK_GEOMETRY, + SOCK_OBJECT, + SOCK_MATERIAL, + SOCK_IMAGE, + SOCK_COLLECTION); + } + static void init_with_socket_type_and_name(bNode &node, + NodeRepeatItem &item, + const eNodeSocketDatatype socket_type, + const char *name) + { + auto *storage = static_cast(node.storage); + item.socket_type = socket_type; + item.identifier = storage->next_identifier++; + socket_items::set_item_name_and_make_unique(node, item, name); + } + static std::string socket_identifier_for_item(const NodeRepeatItem &item) + { + return "Item_" + std::to_string(item.identifier); + } +}; + +} // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_repeat_input.cc b/source/blender/nodes/geometry/nodes/node_geo_repeat_input.cc index be3d69c508d..c4282e533c9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_repeat_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_repeat_input.cc @@ -12,6 +12,7 @@ #include "NOD_geometry.hh" #include "NOD_socket.hh" +#include "NOD_zone_socket_items.hh" #include "node_geometry_util.hh" @@ -45,40 +46,12 @@ static void node_init(bNodeTree * /*tree*/, bNode *node) static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link) { - const bNode *output_node = ntree->node_by_id(node_storage(*node).output_node_id); + bNode *output_node = ntree->node_by_id(node_storage(*node).output_node_id); if (!output_node) { return true; } - auto &storage = *static_cast(output_node->storage); - if (link->tonode == node) { - if (link->tosock->identifier == StringRef("__extend__")) { - if (const NodeRepeatItem *item = storage.add_item(link->fromsock->name, - eNodeSocketDatatype(link->fromsock->type))) - { - update_node_declaration_and_sockets(*ntree, *node); - link->tosock = nodeFindSocket(node, SOCK_IN, item->identifier_str().c_str()); - return true; - } - } - else { - return true; - } - } - if (link->fromnode == node) { - if (link->fromsock->identifier == StringRef("__extend__")) { - if (const NodeRepeatItem *item = storage.add_item(link->tosock->name, - eNodeSocketDatatype(link->tosock->type))) - { - update_node_declaration_and_sockets(*ntree, *node); - link->fromsock = nodeFindSocket(node, SOCK_OUT, item->identifier_str().c_str()); - return true; - } - } - else { - return true; - } - } - return false; + return socket_items::try_add_item_via_any_extend_socket( + *ntree, *node, *output_node, *link); } static void node_register() diff --git a/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc b/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc index b5038bd441a..34aeb0db759 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc @@ -14,6 +14,7 @@ #include "NOD_geometry.hh" #include "NOD_socket.hh" +#include "NOD_zone_socket_items.hh" #include "BLI_string_utils.h" @@ -87,7 +88,7 @@ static std::unique_ptr socket_declaration_for_repeat_item( } decl->name = item.name ? item.name : ""; - decl->identifier = item.identifier_str(); + decl->identifier = RepeatItemsAccessor::socket_identifier_for_item(item); decl->in_out = in_out; return decl; } @@ -175,36 +176,8 @@ static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const b static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link) { - NodeGeometryRepeatOutput &storage = node_storage(*node); - if (link->tonode == node) { - if (link->tosock->identifier == StringRef("__extend__")) { - if (const NodeRepeatItem *item = storage.add_item(link->fromsock->name, - eNodeSocketDatatype(link->fromsock->type))) - { - update_node_declaration_and_sockets(*ntree, *node); - link->tosock = nodeFindSocket(node, SOCK_IN, item->identifier_str().c_str()); - return true; - } - } - else { - return true; - } - } - if (link->fromnode == node) { - if (link->fromsock->identifier == StringRef("__extend__")) { - if (const NodeRepeatItem *item = storage.add_item(link->tosock->name, - eNodeSocketDatatype(link->tosock->type))) - { - update_node_declaration_and_sockets(*ntree, *node); - link->fromsock = nodeFindSocket(node, SOCK_OUT, item->identifier_str().c_str()); - return true; - } - } - else { - return true; - } - } - return false; + return socket_items::try_add_item_via_any_extend_socket( + *ntree, *node, *node, *link); } static void node_register() @@ -230,83 +203,3 @@ blender::MutableSpan NodeGeometryRepeatOutput::items_span() { return blender::MutableSpan(items, items_num); } - -bool NodeRepeatItem::supports_type(const eNodeSocketDatatype type) -{ - return ELEM(type, - SOCK_FLOAT, - SOCK_VECTOR, - SOCK_RGBA, - SOCK_BOOLEAN, - SOCK_ROTATION, - SOCK_INT, - SOCK_STRING, - SOCK_GEOMETRY, - SOCK_OBJECT, - SOCK_MATERIAL, - SOCK_IMAGE, - SOCK_COLLECTION); -} - -std::string NodeRepeatItem::identifier_str() const -{ - return "Item_" + std::to_string(this->identifier); -} - -NodeRepeatItem *NodeGeometryRepeatOutput::add_item(const char *name, - const eNodeSocketDatatype type) -{ - if (!NodeRepeatItem::supports_type(type)) { - return nullptr; - } - const int insert_index = this->items_num; - NodeRepeatItem *old_items = this->items; - - this->items = MEM_cnew_array(this->items_num + 1, __func__); - std::copy_n(old_items, insert_index, this->items); - NodeRepeatItem &new_item = this->items[insert_index]; - std::copy_n(old_items + insert_index + 1, - this->items_num - insert_index, - this->items + insert_index + 1); - - new_item.identifier = this->next_identifier++; - this->set_item_name(new_item, name); - new_item.socket_type = type; - - this->items_num++; - MEM_SAFE_FREE(old_items); - return &new_item; -} - -void NodeGeometryRepeatOutput::set_item_name(NodeRepeatItem &item, const char *name) -{ - char unique_name[MAX_NAME + 4]; - STRNCPY(unique_name, name); - - struct Args { - NodeGeometryRepeatOutput *storage; - const NodeRepeatItem *item; - } args = {this, &item}; - - const char *default_name = nodeStaticSocketLabel(item.socket_type, 0); - BLI_uniquename_cb( - [](void *arg, const char *name) { - const Args &args = *static_cast(arg); - for (const NodeRepeatItem &item : args.storage->items_span()) { - if (&item != args.item) { - if (STREQ(item.name, name)) { - return true; - } - } - } - return false; - }, - &args, - default_name, - '.', - unique_name, - ARRAY_SIZE(unique_name)); - - MEM_SAFE_FREE(item.name); - item.name = BLI_strdup(unique_name); -} diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc index ff6b9873e72..e13a24d7235 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc @@ -12,6 +12,8 @@ #include "NOD_geometry.hh" #include "NOD_socket.hh" +#include "NOD_socket_items.hh" +#include "NOD_zone_socket_items.hh" #include "node_geometry_util.hh" @@ -211,44 +213,12 @@ static void node_init(bNodeTree * /*tree*/, bNode *node) static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link) { - const bNode *output_node = ntree->node_by_id(node_storage(*node).output_node_id); + bNode *output_node = ntree->node_by_id(node_storage(*node).output_node_id); if (!output_node) { return true; } - - NodeGeometrySimulationOutput &storage = *static_cast( - output_node->storage); - - if (link->tonode == node) { - if (link->tosock->identifier == StringRef("__extend__")) { - if (const NodeSimulationItem *item = NOD_geometry_simulation_output_add_item_from_socket( - &storage, link->fromnode, link->fromsock)) - { - update_node_declaration_and_sockets(*ntree, *node); - link->tosock = nodeFindSocket( - node, SOCK_IN, socket_identifier_for_simulation_item(*item).c_str()); - } - else { - return false; - } - } - } - else { - BLI_assert(link->fromnode == node); - if (link->fromsock->identifier == StringRef("__extend__")) { - if (const NodeSimulationItem *item = NOD_geometry_simulation_output_add_item_from_socket( - &storage, link->tonode, link->tosock)) - { - update_node_declaration_and_sockets(*ntree, *node); - link->fromsock = nodeFindSocket( - node, SOCK_OUT, socket_identifier_for_simulation_item(*item).c_str()); - } - else { - return false; - } - } - } - return true; + return socket_items::try_add_item_via_any_extend_socket( + *ntree, *node, *output_node, *link); } static void node_register() diff --git a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc index 84048ee992d..7b7fd983df5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -25,6 +25,7 @@ #include "NOD_common.h" #include "NOD_geometry.hh" #include "NOD_socket.hh" +#include "NOD_zone_socket_items.hh" #include "FN_field_cpp_type.hh" @@ -51,7 +52,7 @@ namespace blender::nodes { std::string socket_identifier_for_simulation_item(const NodeSimulationItem &item) { - return "Item_" + std::to_string(item.identifier); + return SimulationItemsAccessor::socket_identifier_for_item(item); } static std::unique_ptr socket_declaration_for_simulation_item( @@ -60,7 +61,7 @@ static std::unique_ptr socket_declaration_for_simulation_item const int corresponding_input = -1) { const eNodeSocketDatatype socket_type = eNodeSocketDatatype(item.socket_type); - BLI_assert(NOD_geometry_simulation_output_item_socket_type_supported(socket_type)); + BLI_assert(SimulationItemsAccessor::supports_socket_type(socket_type)); std::unique_ptr decl; switch (socket_type) { @@ -138,28 +139,6 @@ void socket_declarations_for_simulation_items(const Span ite r_declaration.items.append(std::move(output_extend_decl)); } -struct SimulationItemsUniqueNameArgs { - NodeGeometrySimulationOutput *sim; - const NodeSimulationItem *item; -}; - -static bool simulation_items_unique_name_check(void *arg, const char *name) -{ - const SimulationItemsUniqueNameArgs &args = *static_cast( - arg); - for (const NodeSimulationItem &item : args.sim->items_span()) { - if (&item != args.item) { - if (STREQ(item.name, name)) { - return true; - } - } - } - if (STREQ(name, "Delta Time")) { - return true; - } - return false; -} - const CPPType &get_simulation_item_cpp_type(const eNodeSocketDatatype socket_type) { const char *socket_idname = nodeStaticSocketType(socket_type, 0); @@ -988,37 +967,8 @@ static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link) { - NodeGeometrySimulationOutput &storage = node_storage(*node); - if (link->tonode == node) { - if (link->tosock->identifier == StringRef("__extend__")) { - if (const NodeSimulationItem *item = NOD_geometry_simulation_output_add_item_from_socket( - &storage, link->fromnode, link->fromsock)) - { - update_node_declaration_and_sockets(*ntree, *node); - link->tosock = nodeFindSocket( - node, SOCK_IN, socket_identifier_for_simulation_item(*item).c_str()); - } - else { - return false; - } - } - } - else { - BLI_assert(link->fromnode == node); - if (link->fromsock->identifier == StringRef("__extend__")) { - if (const NodeSimulationItem *item = NOD_geometry_simulation_output_add_item_from_socket( - &storage, link->fromnode, link->tosock)) - { - update_node_declaration_and_sockets(*ntree, *node); - link->fromsock = nodeFindSocket( - node, SOCK_OUT, socket_identifier_for_simulation_item(*item).c_str()); - } - else { - return false; - } - } - } - return true; + return socket_items::try_add_item_via_any_extend_socket( + *ntree, *node, *node, *link); } static void node_register() @@ -1048,203 +998,3 @@ blender::MutableSpan NodeGeometrySimulationOutput::items_spa { return blender::MutableSpan(items, items_num); } - -blender::IndexRange NodeGeometrySimulationOutput::items_range() const -{ - return blender::IndexRange(items_num); -} - -bool NOD_geometry_simulation_output_item_socket_type_supported( - const eNodeSocketDatatype socket_type) -{ - return ELEM(socket_type, - SOCK_FLOAT, - SOCK_VECTOR, - SOCK_RGBA, - SOCK_BOOLEAN, - SOCK_ROTATION, - SOCK_INT, - SOCK_STRING, - SOCK_GEOMETRY); -} - -bNode *NOD_geometry_simulation_output_find_node_by_item(bNodeTree *ntree, - const NodeSimulationItem *item) -{ - ntree->ensure_topology_cache(); - for (bNode *node : ntree->nodes_by_type("GeometryNodeSimulationOutput")) { - NodeGeometrySimulationOutput *sim = static_cast(node->storage); - if (sim->items_span().contains_ptr(item)) { - return node; - } - } - return nullptr; -} - -bool NOD_geometry_simulation_output_item_set_unique_name(NodeGeometrySimulationOutput *sim, - NodeSimulationItem *item, - const char *name, - const char *defname) -{ - char unique_name[MAX_NAME + 4]; - STRNCPY(unique_name, name); - - blender::nodes::SimulationItemsUniqueNameArgs args{sim, item}; - const bool name_changed = BLI_uniquename_cb(blender::nodes::simulation_items_unique_name_check, - &args, - defname, - '.', - unique_name, - ARRAY_SIZE(unique_name)); - MEM_delete(item->name); - item->name = BLI_strdup(unique_name); - return name_changed; -} - -bool NOD_geometry_simulation_output_contains_item(NodeGeometrySimulationOutput *sim, - const NodeSimulationItem *item) -{ - return sim->items_span().contains_ptr(item); -} - -NodeSimulationItem *NOD_geometry_simulation_output_get_active_item( - NodeGeometrySimulationOutput *sim) -{ - if (!sim->items_range().contains(sim->active_index)) { - return nullptr; - } - return &sim->items[sim->active_index]; -} - -void NOD_geometry_simulation_output_set_active_item(NodeGeometrySimulationOutput *sim, - NodeSimulationItem *item) -{ - if (sim->items_span().contains_ptr(item)) { - sim->active_index = item - sim->items; - } -} - -NodeSimulationItem *NOD_geometry_simulation_output_find_item(NodeGeometrySimulationOutput *sim, - const char *name) -{ - for (NodeSimulationItem &item : sim->items_span()) { - if (STREQ(item.name, name)) { - return &item; - } - } - return nullptr; -} - -NodeSimulationItem *NOD_geometry_simulation_output_add_item(NodeGeometrySimulationOutput *sim, - const short socket_type, - const char *name) -{ - return NOD_geometry_simulation_output_insert_item(sim, socket_type, name, sim->items_num); -} - -NodeSimulationItem *NOD_geometry_simulation_output_insert_item(NodeGeometrySimulationOutput *sim, - const short socket_type, - const char *name, - int index) -{ - if (!NOD_geometry_simulation_output_item_socket_type_supported(eNodeSocketDatatype(socket_type))) - { - return nullptr; - } - - NodeSimulationItem *old_items = sim->items; - sim->items = MEM_cnew_array(sim->items_num + 1, __func__); - for (const int i : blender::IndexRange(index)) { - sim->items[i] = old_items[i]; - } - for (const int i : blender::IndexRange(index, sim->items_num - index)) { - sim->items[i + 1] = old_items[i]; - } - - const char *defname = nodeStaticSocketLabel(socket_type, 0); - NodeSimulationItem &added_item = sim->items[index]; - added_item.identifier = sim->next_identifier++; - NOD_geometry_simulation_output_item_set_unique_name(sim, &added_item, name, defname); - added_item.socket_type = socket_type; - - sim->items_num++; - MEM_SAFE_FREE(old_items); - - return &added_item; -} - -NodeSimulationItem *NOD_geometry_simulation_output_add_item_from_socket( - NodeGeometrySimulationOutput *sim, const bNode * /*from_node*/, const bNodeSocket *from_sock) -{ - return NOD_geometry_simulation_output_insert_item( - sim, from_sock->type, from_sock->name, sim->items_num); -} - -NodeSimulationItem *NOD_geometry_simulation_output_insert_item_from_socket( - NodeGeometrySimulationOutput *sim, - const bNode * /*from_node*/, - const bNodeSocket *from_sock, - int index) -{ - return NOD_geometry_simulation_output_insert_item(sim, from_sock->type, from_sock->name, index); -} - -void NOD_geometry_simulation_output_remove_item(NodeGeometrySimulationOutput *sim, - NodeSimulationItem *item) -{ - const int index = item - sim->items; - if (index < 0 || index >= sim->items_num) { - return; - } - - NodeSimulationItem *old_items = sim->items; - sim->items = MEM_cnew_array(sim->items_num - 1, __func__); - for (const int i : blender::IndexRange(index)) { - sim->items[i] = old_items[i]; - } - for (const int i : blender::IndexRange(index, sim->items_num - index).drop_front(1)) { - sim->items[i - 1] = old_items[i]; - } - - MEM_SAFE_FREE(old_items[index].name); - - sim->items_num--; - MEM_SAFE_FREE(old_items); -} - -void NOD_geometry_simulation_output_clear_items(NodeGeometrySimulationOutput *sim) -{ - for (NodeSimulationItem &item : sim->items_span()) { - MEM_SAFE_FREE(item.name); - } - MEM_SAFE_FREE(sim->items); - sim->items = nullptr; - sim->items_num = 0; -} - -void NOD_geometry_simulation_output_move_item(NodeGeometrySimulationOutput *sim, - int from_index, - int to_index) -{ - BLI_assert(from_index >= 0 && from_index < sim->items_num); - BLI_assert(to_index >= 0 && to_index < sim->items_num); - - if (from_index == to_index) { - return; - } - - if (from_index < to_index) { - const NodeSimulationItem tmp = sim->items[from_index]; - for (int i = from_index; i < to_index; ++i) { - sim->items[i] = sim->items[i + 1]; - } - sim->items[to_index] = tmp; - } - else /* from_index > to_index */ { - const NodeSimulationItem tmp = sim->items[from_index]; - for (int i = from_index; i > to_index; --i) { - sim->items[i] = sim->items[i - 1]; - } - sim->items[to_index] = tmp; - } -} diff --git a/source/blender/nodes/intern/node_zone_socket_items.cc b/source/blender/nodes/intern/node_zone_socket_items.cc new file mode 100644 index 00000000000..51c4815d87f --- /dev/null +++ b/source/blender/nodes/intern/node_zone_socket_items.cc @@ -0,0 +1,21 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "RNA_prototypes.h" + +#include "NOD_zone_socket_items.hh" + +#include "BKE_node.hh" + +namespace blender::nodes { + +/* Defined here to avoid including the relevant headers in the header. */ + +StructRNA *SimulationItemsAccessor::item_srna = &RNA_SimulationStateItem; +int SimulationItemsAccessor::node_type = GEO_NODE_SIMULATION_OUTPUT; + +StructRNA *RepeatItemsAccessor::item_srna = &RNA_RepeatItem; +int RepeatItemsAccessor::node_type = GEO_NODE_REPEAT_OUTPUT; + +} // namespace blender::nodes