Geometry Nodes: deduplicate code to deal with dynamic socket amounts #113114

Merged
Jacques Lucke merged 56 commits from JacquesLucke/blender:simplify-item-array-handling into main 2023-10-04 11:03:01 +02:00
11 changed files with 706 additions and 880 deletions

View File

@ -1802,7 +1802,6 @@ typedef struct NodeGeometrySimulationOutput {
#ifdef __cplusplus
blender::Span<NodeSimulationItem> items_span() const;
blender::MutableSpan<NodeSimulationItem> 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<NodeRepeatItem> items_span() const;
blender::MutableSpan<NodeRepeatItem> items_span();
NodeRepeatItem *add_item(const char *name, eNodeSocketDatatype type);
void set_item_name(NodeRepeatItem &item, const char *name);
#endif
} NodeGeometryRepeatOutput;

View File

@ -11,7 +11,9 @@
#include <cstring>
#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<bNodeTree *>(ptr->owner_id);
NodeSimulationItem *item = static_cast<NodeSimulationItem *>(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<const NodeRepeatItem *>(ptr->data);
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(ptr->owner_id);
ntree->ensure_topology_cache();
for (bNode *node : ntree->nodes_by_type("GeometryNodeRepeatOutput")) {
NodeGeometryRepeatOutput *storage = static_cast<NodeGeometryRepeatOutput *>(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<bNodeTree *>(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<bNodeTree *>(ptr->owner_id);
NodeSimulationItem *item = static_cast<NodeSimulationItem *>(ptr->data);
bNode *node = NOD_geometry_simulation_output_find_node_by_item(ntree, item);
NodeGeometrySimulationOutput *sim = static_cast<NodeGeometrySimulationOutput *>(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<NodeRepeatItem *>(ptr->data);
NodeGeometryRepeatOutput *storage = static_cast<NodeGeometryRepeatOutput *>(node->storage);
storage->set_item_name(*item, value);
}
static void rna_SimulationStateItem_color_get(PointerRNA *ptr, float *values)
{
NodeSimulationItem *item = static_cast<NodeSimulationItem *>(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<NodeRepeatItem *>(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<bNodeTree *>(ptr->owner_id);
@ -3266,200 +3174,148 @@ static bool rna_Node_pair_with_output(
return true;
}
static NodeSimulationItem *rna_NodeGeometrySimulationOutput_items_new(
template<typename Accessor>
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<bNodeTree *>(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<typename Accessor> 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<bNodeTree *>(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<typename Accessor>
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<bNodeTree *>(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<typename Accessor> static PointerRNA rna_Node_ItemArray_active_get(PointerRNA *ptr)
{
bNode *node = static_cast<bNode *>(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<typename Accessor>
static void rna_Node_ItemArray_active_set(PointerRNA *ptr,
PointerRNA value,
ReportList * /*reports*/)
{
using ItemT = typename Accessor::ItemT;
bNode *node = static_cast<bNode *>(ptr->data);
ItemT *item = static_cast<ItemT *>(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<typename Accessor>
static void rna_Node_ItemArray_item_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr)
{
using ItemT = typename Accessor::ItemT;
bNodeTree &ntree = *reinterpret_cast<bNodeTree *>(ptr->owner_id);
ItemT &item = *static_cast<ItemT *>(ptr->data);
bNode *node = blender::nodes::socket_items::find_node_by_item<Accessor>(ntree, item);
BLI_assert(node != nullptr);
BKE_ntree_update_tag_node_property(&ntree, node);
ED_node_tree_propagate_change(nullptr, bmain, &ntree);
}
template<typename Accessor>
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<typename Accessor>
static void rna_Node_ItemArray_item_name_set(PointerRNA *ptr, const char *value)
{
using ItemT = typename Accessor::ItemT;
bNodeTree &ntree = *reinterpret_cast<bNodeTree *>(ptr->owner_id);
ItemT &item = *static_cast<ItemT *>(ptr->data);
bNode *node = blender::nodes::socket_items::find_node_by_item<Accessor>(ntree, item);
BLI_assert(node != nullptr);
blender::nodes::socket_items::set_item_name_and_make_unique<Accessor>(*node, item, value);
}
template<typename Accessors>
static void rna_Node_ItemArray_item_color_get(PointerRNA *ptr, float *values)
{
using ItemT = typename Accessors::ItemT;
ItemT &item = *static_cast<ItemT *>(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>
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<NodeGeometrySimulationOutput *>(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<bNodeTree *>(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<NodeGeometryRepeatOutput *>(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<bNodeTree *>(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<NodeGeometrySimulationOutput *>(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<bNodeTree *>(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<NodeGeometryRepeatOutput *>(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<NodeRepeatItem>(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<Accessor>(
*node, eNodeSocketDatatype(socket_type), name);
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(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<NodeGeometrySimulationOutput *>(node->storage);
NOD_geometry_simulation_output_clear_items(sim);
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(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<NodeGeometryRepeatOutput *>(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<NodeGeometrySimulationOutput *>(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<bNodeTree *>(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<NodeGeometryRepeatOutput *>(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<bNodeTree *>(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<bNode *>(ptr->data);
NodeGeometrySimulationOutput *sim = static_cast<NodeGeometrySimulationOutput *>(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<bNode *>(ptr->data);
NodeGeometryRepeatOutput *storage = static_cast<NodeGeometryRepeatOutput *>(node->storage);
blender::MutableSpan<NodeRepeatItem> 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<bNode *>(ptr->data);
NodeGeometrySimulationOutput *sim = static_cast<NodeGeometrySimulationOutput *>(node->storage);
NOD_geometry_simulation_output_set_active_item(sim,
static_cast<NodeSimulationItem *>(value.data));
}
static void rna_NodeGeometryRepeatOutput_active_item_set(PointerRNA *ptr,
PointerRNA value,
ReportList * /*reports*/)
{
bNode *node = static_cast<bNode *>(ptr->data);
NodeGeometryRepeatOutput *storage = static_cast<NodeGeometryRepeatOutput *>(node->storage);
NodeRepeatItem *item = static_cast<NodeRepeatItem *>(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;
JacquesLucke marked this conversation as resolved Outdated

a item -> an item

`a item` -> `an item`
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<SimulationItemsAccessor>");
}
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<SimulationItemsAccessor>",
"rna_Node_ItemArray_active_set<SimulationItemsAccessor>",
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<RepeatItemsAccessor>",
"rna_Node_ItemArray_active_set<RepeatItemsAccessor>",
nullptr,
nullptr);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NO_DEG_UPDATE);

View File

@ -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

View File

@ -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);
/** \} */

View File

@ -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<typename T> 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<typename Accessor>
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<typename T>
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<T>);
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<T>(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<typename T>
inline void clear_items(T **items, int *items_num, int *active_index, void (*destruct_item)(T *))
{
static_assert(std::is_trivial_v<T>);
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<typename T>
inline void move_item(T *items, const int items_num, const int from_index, const int to_index)
{
static_assert(std::is_trivial_v<T>);
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<typename Accessor>
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<ItemT> array;
ItemT *item;
} args = {array, &item};
BLI_uniquename_cb(
[](void *arg, const char *name) {
const Args &args = *static_cast<Args *>(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<typename Accessor>
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<ItemT>(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
JacquesLucke marked this conversation as resolved Outdated

the links connects -> the link connects

`the links connects` -> `the link connects`
* socket, update the node and then change the link to point to the new socket.
* \return False if the link should be removed.
*/
template<typename Accessor>
[[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<Accessor>(
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<typename Accessor>
[[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<Accessor>(
ntree, extend_node, *possible_extend_socket, storage_node, link);
}
} // namespace blender::nodes::socket_items

View File

@ -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<NodeSimulationItem> get_items_from_node(bNode &node)
{
auto *storage = static_cast<NodeGeometrySimulationOutput *>(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<NodeGeometrySimulationOutput *>(node.storage);
item.socket_type = socket_type;
item.identifier = storage->next_identifier++;
socket_items::set_item_name_and_make_unique<SimulationItemsAccessor>(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<NodeRepeatItem> get_items_from_node(bNode &node)
{
auto *storage = static_cast<NodeGeometryRepeatOutput *>(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<NodeGeometryRepeatOutput *>(node.storage);
item.socket_type = socket_type;
item.identifier = storage->next_identifier++;
socket_items::set_item_name_and_make_unique<RepeatItemsAccessor>(node, item, name);
}
static std::string socket_identifier_for_item(const NodeRepeatItem &item)
{
return "Item_" + std::to_string(item.identifier);
}
};
} // namespace blender::nodes

View File

@ -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<NodeGeometryRepeatOutput *>(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<RepeatItemsAccessor>(
*ntree, *node, *output_node, *link);
}
static void node_register()

View File

@ -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<SocketDeclaration> 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<RepeatItemsAccessor>(
*ntree, *node, *node, *link);
}
static void node_register()
@ -230,83 +203,3 @@ blender::MutableSpan<NodeRepeatItem> NodeGeometryRepeatOutput::items_span()
{
return blender::MutableSpan<NodeRepeatItem>(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<NodeRepeatItem>(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<Args *>(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);
}

View File

@ -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<NodeGeometrySimulationOutput *>(
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<SimulationItemsAccessor>(
*ntree, *node, *output_node, *link);
}
static void node_register()

View File

@ -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<SocketDeclaration> socket_declaration_for_simulation_item(
@ -60,7 +61,7 @@ static std::unique_ptr<SocketDeclaration> 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<SocketDeclaration> decl;
switch (socket_type) {
@ -138,28 +139,6 @@ void socket_declarations_for_simulation_items(const Span<NodeSimulationItem> 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<const SimulationItemsUniqueNameArgs *>(
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<SimulationItemsAccessor>(
*ntree, *node, *node, *link);
}
static void node_register()
@ -1048,203 +998,3 @@ blender::MutableSpan<NodeSimulationItem> NodeGeometrySimulationOutput::items_spa
{
return blender::MutableSpan<NodeSimulationItem>(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<NodeGeometrySimulationOutput *>(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<NodeSimulationItem>(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<NodeSimulationItem>(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;
}
}

View File

@ -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. */
JacquesLucke marked this conversation as resolved Outdated

I guess this one is to avoid including BKE_node.hh in NOD_zone_socket_items.hh? Seems reasonable, maybe worth a comment though?

I guess this one is to avoid including `BKE_node.hh` in `NOD_zone_socket_items.hh`? Seems reasonable, maybe worth a comment though?
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