From b404df6989088974d7365ca8ad13e035555fcc82 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 21 Aug 2023 14:07:16 +0200 Subject: [PATCH 01/14] Further tweaks to LSAN suppression rules. Mainly add more python-related suppressions. Also suppressed some reports from `pxr` (usd) libraries. Now most tests are passing again with ASAN/LSAN on on my machine, besides modifiers and geometry nodes ones, which often fail on a mysterious `libstdc++.so.6+0xb259a` leak. --- tools/config/analysis/lsan.supp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tools/config/analysis/lsan.supp b/tools/config/analysis/lsan.supp index a45411baccf..5e0d263317c 100644 --- a/tools/config/analysis/lsan.supp +++ b/tools/config/analysis/lsan.supp @@ -1,11 +1,24 @@ +# Python - START leak:_PyObject_Malloc +leak:_PyObject_Calloc leak:_PyObject_Realloc leak:_PyObject_GC +leak:_PyArgv_* +leak:_PyBytes_* +leak:_Py_* +leak:_PyUnicodeWriter_* +leak:list_append +leak:list_resize leak:PyThread_allocate_lock leak:libpython* +leak:python +# Numpy +leak:PyUFunc_* +# Python - END leak:imb_exitopenexr leak:imb_filetypes_exit leak:libIlm* +leak:pxrInternal_* leak: leak:libX11* leak:libglib* @@ -14,4 +27,3 @@ leak:i965_dri leak:libdrm* leak:radeon* leak:libGLX* -# leak:libasan* -- 2.30.2 From b3c7f3c8a9eb42f5d627d49244b2afabeddd64c8 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 25 Jul 2023 12:31:16 +0200 Subject: [PATCH 02/14] UI: Add initial UI support for ID pointers custom properties. Customprops to IDs are supported since years through code, but were never exposed directly in the UI of customporperties. This commit mainly: * Adds a new `DATA_BLOCK` type to UI customprops types. * Exposes the existing `id_type` settings to python API. Pull Request: https://projects.blender.org/blender/blender/pulls/110458 --- scripts/modules/rna_prop_ui.py | 11 ++++- scripts/startup/bl_operators/wm.py | 48 ++++++++++++++++++- .../python/generic/idprop_py_ui_api.cc | 27 ++++++++++- 3 files changed, 81 insertions(+), 5 deletions(-) diff --git a/scripts/modules/rna_prop_ui.py b/scripts/modules/rna_prop_ui.py index d83123d60d6..5340b0e37da 100644 --- a/scripts/modules/rna_prop_ui.py +++ b/scripts/modules/rna_prop_ui.py @@ -80,6 +80,7 @@ def rna_idprop_ui_create( description=None, overridable=False, subtype=None, + id_type='OBJECT', ): """Create and initialize a custom property with limits, defaults and other settings.""" @@ -91,11 +92,16 @@ def rna_idprop_ui_create( proptype, _ = rna_idprop_value_item_type(default) if (proptype is bool) or (proptype is str): - ui_data = item.id_properties_ui(prop) ui_data.update( description=description, default=default, ) + elif proptype is type(None) or issubclass(proptype, bpy.types.ID): + ui_data.update( + description=description, + default=default, + id_type=id_type, + ) else: if soft_min is None: soft_min = min @@ -156,6 +162,7 @@ def draw(layout, context, context_member, property_type, *, use_edit=True): to_dict = getattr(value, "to_dict", None) to_list = getattr(value, "to_list", None) + is_datablock = value is None or isinstance(value, bpy.types.ID) if to_dict: value = to_dict() @@ -178,6 +185,8 @@ def draw(layout, context, context_member, property_type, *, use_edit=True): props = value_column.operator("wm.properties_edit_value", text="Edit Value") props.data_path = context_member props.property_name = key + elif is_datablock: + value_column.template_ID(rna_item, '["%s"]' % escape_identifier(key), text="") else: value_column.prop(rna_item, '["%s"]' % escape_identifier(key), text="") diff --git a/scripts/startup/bl_operators/wm.py b/scripts/startup/bl_operators/wm.py index bc199836433..85867bca832 100644 --- a/scripts/startup/bl_operators/wm.py +++ b/scripts/startup/bl_operators/wm.py @@ -1387,6 +1387,7 @@ rna_custom_property_type_items = ( ('BOOL', "Boolean", "A true or false value"), ('BOOL_ARRAY', "Boolean Array", "An array of true or false values"), ('STRING', "String", "A string value"), + ('DATA_BLOCK', "Data-Block", "A data-block value"), ('PYTHON', "Python", "Edit a Python value directly, for unsupported property types"), ) @@ -1412,6 +1413,8 @@ rna_custom_property_subtype_vector_items = ( ('QUATERNION', "Quaternion Rotation", "Quaternion rotation (affects NLA blending)"), ) +rna_id_type_items = tuple((item.identifier, item.name, item.description, item.icon, item.value) + for item in bpy.types.Action.bl_rna.properties['id_root'].enum_items) class WM_OT_properties_edit(Operator): """Change a custom property's type, or adjust how it is displayed in the interface""" @@ -1554,6 +1557,14 @@ class WM_OT_properties_edit(Operator): maxlen=1024, ) + # Data-block properties. + + id_type: EnumProperty( + name="ID Type", + items=rna_id_type_items, + default='OBJECT', + ) + # Store the value converted to a string as a fallback for otherwise unsupported types. eval_string: StringProperty( name="Value", @@ -1623,9 +1634,21 @@ class WM_OT_properties_edit(Operator): if is_array: return 'PYTHON' return 'STRING' + elif prop_type == type(None) or issubclass(prop_type, bpy.types.ID): + if is_array: + return 'PYTHON' + return 'DATA_BLOCK' return 'PYTHON' + # For `DATA_BLOCK` types, return the `id_type` or an empty string for non data-block types. + @staticmethod + def get_property_id_type(item, property_name): + ui_data = item.id_properties_ui(property_name) + rna_data = ui_data.as_dict() + # For non `DATA_BLOCK` types, the `id_type` wont exist. + return rna_data.get("id_type", "") + def _init_subtype(self, subtype): self.subtype = subtype or 'NONE' @@ -1664,6 +1687,8 @@ class WM_OT_properties_edit(Operator): self.default_string = rna_data["default"] elif self.property_type in {'BOOL', 'BOOL_ARRAY'}: self.default_bool = self._convert_new_value_array(rna_data["default"], bool, 32) + elif self.property_type == 'DATA_BLOCK': + self.id_type = rna_data["id_type"] if self.property_type in {'FLOAT_ARRAY', 'INT_ARRAY', 'BOOL_ARRAY'}: self.array_length = len(item[name]) @@ -1677,7 +1702,7 @@ class WM_OT_properties_edit(Operator): # When the operator chooses a different type than the original property, # attempt to convert the old value to the new type for continuity and speed. - def _get_converted_value(self, item, name_old, prop_type_new): + def _get_converted_value(self, item, name_old, prop_type_new, id_type_old, id_type_new): if prop_type_new == 'INT': return self._convert_new_value_single(item[name_old], int) elif prop_type_new == 'FLOAT': @@ -1700,6 +1725,14 @@ class WM_OT_properties_edit(Operator): return [False] * self.array_length elif prop_type_new == 'STRING': return self.convert_custom_property_to_string(item, name_old) + elif prop_type_new == 'DATA_BLOCK': + if id_type_old != id_type_new: + return None + old_value = item[name_old] + if not isinstance(old_value, bpy.types.ID): + return None + return old_value + # If all else fails, create an empty string property. That should avoid errors later on anyway. return "" @@ -1761,6 +1794,12 @@ class WM_OT_properties_edit(Operator): default=self.default_string, description=self.description, ) + elif prop_type_new == 'DATA_BLOCK': + ui_data = item.id_properties_ui(name) + ui_data.update( + description=self.description, + id_type=self.id_type, + ) escaped_name = bpy.utils.escape_identifier(name) item.property_overridable_library_set('["%s"]' % escaped_name, self.is_overridable_library) @@ -1824,6 +1863,9 @@ class WM_OT_properties_edit(Operator): prop_type_new = self.property_type self._old_prop_name[:] = [name] + id_type_old = self.get_property_id_type(item, name_old) + id_type_new = self.id_type + if prop_type_new == 'PYTHON': try: new_value = eval(self.eval_string) @@ -1838,7 +1880,7 @@ class WM_OT_properties_edit(Operator): if name_old != name: del item[name_old] else: - new_value = self._get_converted_value(item, name_old, prop_type_new) + new_value = self._get_converted_value(item, name_old, prop_type_new, id_type_old, id_type_new) del item[name_old] item[name] = new_value @@ -1991,6 +2033,8 @@ class WM_OT_properties_edit(Operator): layout.prop(self, "default_bool", index=0) elif self.property_type == 'STRING': layout.prop(self, "default_string") + elif self.property_type == 'DATA_BLOCK': + layout.prop(self, "id_type") if self.property_type == 'PYTHON': layout.prop(self, "eval_string") diff --git a/source/blender/python/generic/idprop_py_ui_api.cc b/source/blender/python/generic/idprop_py_ui_api.cc index b187c0d10f8..455d1969dfb 100644 --- a/source/blender/python/generic/idprop_py_ui_api.cc +++ b/source/blender/python/generic/idprop_py_ui_api.cc @@ -439,9 +439,10 @@ static bool idprop_ui_data_update_id(IDProperty *idprop, PyObject *args, PyObjec { const char *rna_subtype = nullptr; const char *description = nullptr; - const char *kwlist[] = {"subtype", "description", nullptr}; + const char *id_type = nullptr; + const char *kwlist[] = {"subtype", "description", "id_type", nullptr}; if (!PyArg_ParseTupleAndKeywords( - args, kwargs, "|$zz:update", (char **)kwlist, &rna_subtype, &description)) + args, kwargs, "|$zzz:update", (char **)kwlist, &rna_subtype, &description, &id_type)) { return false; } @@ -455,6 +456,15 @@ static bool idprop_ui_data_update_id(IDProperty *idprop, PyObject *args, PyObjec return false; } + int id_type_tmp; + if (pyrna_enum_value_from_id( + rna_enum_id_type_items, id_type, &id_type_tmp, "IDPropertyUIManager.update") == -1) + { + return false; + } + + ui_data.id_type = short(id_type_tmp); + /* Write back to the property's UI data. */ IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base); *ui_data_orig = ui_data; @@ -471,6 +481,7 @@ PyDoc_STRVAR(BPy_IDPropertyUIManager_update_doc, "precision=None, " "step=None, " "default=None, " + "id_type=None, " "description=None)\n" "\n" " Update the RNA information of the IDProperty used for interaction and\n" @@ -619,6 +630,17 @@ static void idprop_ui_data_to_dict_string(IDProperty *property, PyObject *dict) Py_DECREF(item); } +static void idprop_ui_data_to_dict_id(IDProperty *property, PyObject *dict) +{ + IDPropertyUIDataID *ui_data = (IDPropertyUIDataID *)property->ui_data; + + const char *id_type = nullptr; + RNA_enum_identifier(rna_enum_id_type_items, ui_data->id_type, &id_type); + PyObject *item = PyUnicode_FromString(id_type); + PyDict_SetItemString(dict, "id_type", item); + Py_DECREF(item); +} + PyDoc_STRVAR(BPy_IDPropertyUIManager_as_dict_doc, ".. method:: as_dict()\n" "\n" @@ -655,6 +677,7 @@ static PyObject *BPy_IDIDPropertyUIManager_as_dict(BPy_IDPropertyUIManager *self idprop_ui_data_to_dict_string(property, dict); break; case IDP_UI_DATA_TYPE_ID: + idprop_ui_data_to_dict_id(property, dict); break; case IDP_UI_DATA_TYPE_INT: idprop_ui_data_to_dict_int(property, dict); -- 2.30.2 From 785e03b13f46effc9c90ba1a224e78611c2a84b6 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 21 Aug 2023 15:00:09 +0200 Subject: [PATCH 03/14] Cleanup: Reduce VKDebug Messenger Complexity Reduces the complexity of VKDebug messenger_callback. --- source/blender/gpu/vulkan/vk_debug.cc | 85 ++++++++++----------------- 1 file changed, 30 insertions(+), 55 deletions(-) diff --git a/source/blender/gpu/vulkan/vk_debug.cc b/source/blender/gpu/vulkan/vk_debug.cc index 80f8ca5bcf5..604741b3eb0 100644 --- a/source/blender/gpu/vulkan/vk_debug.cc +++ b/source/blender/gpu/vulkan/vk_debug.cc @@ -73,6 +73,7 @@ namespace blender::gpu::debug { void VKDebuggingTools::init(VkInstance vk_instance) { + PFN_vkGetInstanceProcAddr instance_proc_addr = vkGetInstanceProcAddr; enabled = false; vk_debug_utils_messenger = nullptr; @@ -248,6 +249,7 @@ void VKDebuggingTools::print_labels(const VkDebugUtilsMessengerCallbackDataEXT * ss << std::endl; printf("%s", ss.str().c_str()); } + VKAPI_ATTR VkBool32 VKAPI_CALL messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDebugUtilsMessageTypeFlagsEXT /* message_type*/, @@ -263,71 +265,44 @@ messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, if (debugging_tools.is_ignore(callback_data->messageIdNumber)) { return VK_FALSE; } - bool use_color = CLG_color_support_get(&LOG); - UNUSED_VARS(use_color); - bool enabled = false; - if ((message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) || - (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT)) + + CLG_Severity severity = CLG_SEVERITY_INFO; + if (message_severity & (VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)) { - if ((LOG.type->flag & CLG_FLAG_USE) && (LOG.type->level >= CLG_SEVERITY_INFO)) { - const char *format = "{0x%x}% s\n %s "; - CLG_logf(LOG.type, - CLG_SEVERITY_INFO, - "", - "", - format, - callback_data->messageIdNumber, - callback_data->pMessageIdName, - callback_data->pMessage); - enabled = true; - } + severity = CLG_SEVERITY_INFO; } - else { - CLG_Severity clog_severity; - switch (message_severity) { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: - clog_severity = CLG_SEVERITY_WARN; - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: - clog_severity = CLG_SEVERITY_ERROR; - break; - default: - BLI_assert_unreachable(); - } - enabled = true; - if (clog_severity == CLG_SEVERITY_ERROR) { - const char *format = " %s {0x%x}\n %s "; - CLG_logf(LOG.type, - clog_severity, - "", - "", - format, - callback_data->pMessageIdName, - callback_data->messageIdNumber, - callback_data->pMessage); - } - else if (LOG.type->level >= CLG_SEVERITY_WARN) { - const char *format = " %s {0x%x}\n %s "; - CLG_logf(LOG.type, - clog_severity, - "", - "", - format, - callback_data->pMessageIdName, - callback_data->messageIdNumber, - callback_data->pMessage); - } + if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { + severity = CLG_SEVERITY_WARN; } - if ((enabled) && ((callback_data->objectCount > 0) || (callback_data->cmdBufLabelCount > 0) || - (callback_data->queueLabelCount > 0))) - { + if (message_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { + severity = CLG_SEVERITY_ERROR; + } + + if ((LOG.type->flag & CLG_FLAG_USE) && (LOG.type->level <= severity)) { + const char *format = "{0x%x}% s\n %s "; + CLG_logf(LOG.type, + severity, + "", + "", + format, + callback_data->messageIdNumber, + callback_data->pMessageIdName, + callback_data->pMessage); + } + + const bool do_labels = (callback_data->objectCount + callback_data->cmdBufLabelCount + + callback_data->queueLabelCount) > 0; + if (do_labels) { debugging_tools.print_labels(callback_data); } + return VK_FALSE; }; VkResult VKDebuggingTools::init_messenger(VkInstance vk_instance) { + CLG_logref_init(&LOG); vk_message_id_number_ignored.clear(); BLI_assert(enabled); -- 2.30.2 From 8b311daf0a34ddb0cb97fff50f635082945c8a55 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 21 Aug 2023 15:03:43 +0200 Subject: [PATCH 04/14] Cleanup: Make format --- scripts/startup/bl_operators/wm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/startup/bl_operators/wm.py b/scripts/startup/bl_operators/wm.py index 85867bca832..b52e36940b1 100644 --- a/scripts/startup/bl_operators/wm.py +++ b/scripts/startup/bl_operators/wm.py @@ -1416,6 +1416,7 @@ rna_custom_property_subtype_vector_items = ( rna_id_type_items = tuple((item.identifier, item.name, item.description, item.icon, item.value) for item in bpy.types.Action.bl_rna.properties['id_root'].enum_items) + class WM_OT_properties_edit(Operator): """Change a custom property's type, or adjust how it is displayed in the interface""" bl_idname = "wm.properties_edit" -- 2.30.2 From 7f65080ab4cf37b725a719e5b8a789e63fa4878a Mon Sep 17 00:00:00 2001 From: Alaska Date: Mon, 21 Aug 2023 15:22:03 +0200 Subject: [PATCH 05/14] Fix #111277: NaN in Vector Displacement leading to render errors Fixes NaN in Vector Displacement node caused by the normalization of 0, 0, 0 vectors. This fixes both visual rendering issues and an "illegal address" error on the GPU. The "illegal address" error came from the Light Tree Sampling code not handling the NaN normals well, leading to weird code paths being taken, eventually leading to a kernel_assert and a user facing illegal address error. Pull Request: https://projects.blender.org/blender/blender/pulls/111294 --- intern/cycles/kernel/svm/displace.h | 2 +- .../material/gpu_shader_material_vector_displacement.glsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/intern/cycles/kernel/svm/displace.h b/intern/cycles/kernel/svm/displace.h index cca9dc7a238..ba4aface005 100644 --- a/intern/cycles/kernel/svm/displace.h +++ b/intern/cycles/kernel/svm/displace.h @@ -165,7 +165,7 @@ ccl_device_noinline int svm_node_vector_displacement( tangent = normalize(sd->dPdu); } - float3 bitangent = normalize(cross(normal, tangent)); + float3 bitangent = safe_normalize(cross(normal, tangent)); const AttributeDescriptor attr_sign = find_attribute(kg, sd, node.w); if (attr_sign.offset != ATTR_STD_NOT_FOUND) { float sign = primitive_surface_attribute_float(kg, sd, attr_sign, NULL, NULL); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl index 0ff074bc04f..a382887d979 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_displacement.glsl @@ -3,7 +3,7 @@ void node_vector_displacement_tangent( { vec3 oN = normalize(normal_world_to_object(g_data.N)); vec3 oT = normalize(normal_world_to_object(T.xyz)); - vec3 oB = T.w * normalize(cross(oN, oT)); + vec3 oB = T.w * safe_normalize(cross(oN, oT)); result = (vector.xyz - midlevel) * scale; result = result.x * oT + result.y * oN + result.z * oB; -- 2.30.2 From f052b18a65520ee9178326a8219a36c8114b60f3 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 11 Aug 2023 12:28:54 +0200 Subject: [PATCH 06/14] Always clear 'fake user' flag from linked data. Using this flag from linked data is always a double-edge sword, in one end some user have been relying on it to keep around data that is not really used as ID (like e.g. text data-blocks, node trees, see e.g. #103687, #105687). On the other end, it often causes over-keeping of linked data reference in production files. From now on, when an unused linked data is to be kept around, users should create an ID property to reference it. Implements #106321. Pull Request: https://projects.blender.org/blender/blender/pulls/111042 --- source/blender/blenloader/intern/readfile.cc | 4 +++ source/blender/blenloader/intern/writefile.cc | 10 ------ tests/python/bl_blendfile_io.py | 15 ++++---- tests/python/bl_blendfile_liblink.py | 34 +++++++++---------- 4 files changed, 27 insertions(+), 36 deletions(-) diff --git a/source/blender/blenloader/intern/readfile.cc b/source/blender/blenloader/intern/readfile.cc index 2b2eecae201..d9fa0287f4a 100644 --- a/source/blender/blenloader/intern/readfile.cc +++ b/source/blender/blenloader/intern/readfile.cc @@ -2061,6 +2061,10 @@ static void direct_link_id_common( } id->lib = current_library; + if (id->lib) { + /* Always fully clear fake user flag for linked data. */ + id->flag &= ~LIB_FAKEUSER; + } id->us = ID_FAKE_USERS(id); id->icon_id = 0; id->newid = nullptr; /* Needed because .blend may have been saved with crap value here... */ diff --git a/source/blender/blenloader/intern/writefile.cc b/source/blender/blenloader/intern/writefile.cc index 34e44d58ffa..a7d7209d85f 100644 --- a/source/blender/blenloader/intern/writefile.cc +++ b/source/blender/blenloader/intern/writefile.cc @@ -1225,16 +1225,6 @@ static bool write_file_handle(Main *mainvar, * asap afterward. */ id_lib_extern(id_iter); } - else if (ID_FAKE_USERS(id_iter) > 0 && id_iter->asset_data == nullptr) { - /* Even though fake user is not directly editable by the user on linked data, it is a - * common 'work-around' to set it in library files on data-blocks that need to be linked - * but typically do not have an actual real user (e.g. texts, etc.). - * See e.g. #105687 and #103867. - * - * Would be good to find a better solution, but for now consider these as directly linked - * as well. */ - id_lib_extern(id_iter); - } else { id_iter->tag |= LIB_TAG_INDIRECT; id_iter->tag &= ~LIB_TAG_EXTERN; diff --git a/tests/python/bl_blendfile_io.py b/tests/python/bl_blendfile_io.py index 50cd058fca8..418a49a069f 100644 --- a/tests/python/bl_blendfile_io.py +++ b/tests/python/bl_blendfile_io.py @@ -115,10 +115,11 @@ class TestIdRuntimeTag(TestHelper): assert linked_material.is_library_indirect is False link_dir = os.path.join(output_lib_path, "Mesh") - bpy.ops.wm.link(directory=link_dir, filename="LibMesh") + bpy.ops.wm.link(directory=link_dir, filename="LibMesh", instance_object_data=False) linked_mesh = bpy.data.meshes['LibMesh'] assert linked_mesh.is_library_indirect is False + assert linked_mesh.use_fake_user is False obj.data = linked_mesh obj.material_slots[0].link = 'OBJECT' @@ -131,17 +132,15 @@ class TestIdRuntimeTag(TestHelper): # so writing .blend file will have properly reset its tag to indirectly linked data. assert linked_material.is_library_indirect - # Only usage of this linked mesh is a runtime ID (object), but it is flagged as 'fake user' in its library, - # so writing .blend file will have kept its tag to directly linked data. - assert not linked_mesh.is_library_indirect + # Only usage of this linked mesh is a runtime ID (object), + # so writing .blend file will have properly reset its tag to indirectly linked data. + assert linked_mesh.is_library_indirect bpy.ops.wm.open_mainfile(filepath=output_work_path, load_ui=False) assert 'Cube' not in bpy.data.objects - assert 'LibMaterial' in bpy.data.materials # Pulled-in by the linked mesh. - linked_mesh = bpy.data.meshes['LibMesh'] - assert linked_mesh.use_fake_user is True - assert linked_mesh.is_library_indirect is False + assert 'LibMaterial' not in bpy.data.materials + assert 'libMesh' not in bpy.data.meshes TESTS = ( diff --git a/tests/python/bl_blendfile_liblink.py b/tests/python/bl_blendfile_liblink.py index 626abc27c95..6f058155c33 100644 --- a/tests/python/bl_blendfile_liblink.py +++ b/tests/python/bl_blendfile_liblink.py @@ -214,9 +214,9 @@ class TestBlendLibLinkIndirect(TestBlendLibLinkHelper): material = bpy.data.materials[0] assert material.library is not None - assert material.use_fake_user is True - assert material.users == 2 # Fake user is not cleared when linking. - assert material.is_library_indirect + assert material.use_fake_user is False # Fake user is cleared when linking. + assert material.users == 1 + assert material.is_library_indirect is True assert mesh.library is not None assert mesh.use_fake_user is False @@ -229,29 +229,28 @@ class TestBlendLibLinkIndirect(TestBlendLibLinkHelper): coll.objects.link(ob) bpy.context.scene.collection.children.link(coll) - assert material.users == 2 - assert material.is_library_indirect + assert material.users == 1 + assert material.is_library_indirect is True assert mesh.users == 1 assert mesh.is_library_indirect is False ob.material_slots[0].link = 'OBJECT' ob.material_slots[0].material = material - assert material.users == 3 + assert material.users == 2 assert material.is_library_indirect is False ob.material_slots[0].material = None - assert material.users == 2 + assert material.users == 1 # This is not properly updated whene removing a local user of linked data. assert material.is_library_indirect is False output_work_path = os.path.join(output_dir, self.unique_blendfile_name("blendfile")) bpy.ops.wm.save_as_mainfile(filepath=output_work_path, check_existing=False, compress=False) - assert material.users == 2 - # Currently linked data with 'fake user' set are considered as directly linked data. - assert not material.is_library_indirect + assert material.users == 1 + assert material.is_library_indirect is True bpy.ops.wm.open_mainfile(filepath=output_work_path, load_ui=False) @@ -264,10 +263,9 @@ class TestBlendLibLinkIndirect(TestBlendLibLinkHelper): material = bpy.data.materials[0] assert material.library is not None - assert material.use_fake_user is True - assert material.users == 2 # Fake user is not cleared when linking. - # Currently linked data with 'fake user' set are considered as directly linked data. - assert not material.is_library_indirect + assert material.use_fake_user is False # Fake user is cleared when linking. + assert material.users == 1 + assert material.is_library_indirect is True assert mesh.library is not None assert mesh.use_fake_user is False @@ -293,7 +291,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): assert len(bpy.data.materials) == 1 assert bpy.data.materials[0].library is not None - assert bpy.data.materials[0].users == 2 # Fake user is not cleared when linking. + assert bpy.data.materials[0].users == 1 # Fake user is cleared when linking. assert len(bpy.data.meshes) == 1 assert bpy.data.meshes[0].library is None assert bpy.data.meshes[0].use_fake_user is False @@ -310,7 +308,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): assert len(bpy.data.materials) == 1 assert bpy.data.materials[0].library is not None - assert bpy.data.materials[0].users == 2 # Fake user is not cleared when linking. + assert bpy.data.materials[0].users == 1 # Fake user is cleared when linking. assert len(bpy.data.meshes) == 1 assert bpy.data.meshes[0].library is None assert bpy.data.meshes[0].use_fake_user is False @@ -328,7 +326,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): assert len(bpy.data.materials) == 1 assert bpy.data.materials[0].library is not None - assert bpy.data.materials[0].users == 2 # Fake user is not cleared when linking. + assert bpy.data.materials[0].users == 1 # Fake user is cleared when linking. assert len(bpy.data.meshes) == 1 assert bpy.data.meshes[0].library is None assert bpy.data.meshes[0].use_fake_user is True @@ -345,7 +343,7 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): assert len(bpy.data.materials) == 1 assert bpy.data.materials[0].library is not None - assert bpy.data.materials[0].users == 2 # Fake user is not cleared when linking. + assert bpy.data.materials[0].users == 1 # Fake user is cleared when linking. assert len(bpy.data.meshes) == 1 assert bpy.data.meshes[0].library is None assert bpy.data.meshes[0].users == 1 -- 2.30.2 From ea3d85b9d723ddd73cfdd8dc23bede31a1328733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Mon, 21 Aug 2023 15:40:19 +0200 Subject: [PATCH 07/14] Node panels: RNA for node group interfaces Part 2/3 of #109135, #110272 Defines the RNA API for the new node tree interfaces. The bulk of the RNA definition lives in `rna_node_tree_interface.cc`. The legacy socket interfaces remain in place and will be removed later. Since the socket items share the `bNodeSocketValueXXX` structs with the `bNodeSocket` types they also use the same RNA. The `rna_def_node_socket_interface_subtypes` function is exposed so that when defining the interface RNA it can use the same code as the regular socket RNA. Pull Request: https://projects.blender.org/blender/blender/pulls/110952 --- source/blender/makesrna/RNA_enum_items.hh | 2 + source/blender/makesrna/RNA_enum_types.hh | 1 + source/blender/makesrna/intern/CMakeLists.txt | 1 + source/blender/makesrna/intern/makesrna.cc | 1 + source/blender/makesrna/intern/rna_internal.h | 4 + .../makesrna/intern/rna_node_socket.cc | 867 +++++++++++--- .../intern/rna_node_tree_interface.cc | 1045 +++++++++++++++++ .../blender/makesrna/intern/rna_nodetree.cc | 16 +- 8 files changed, 1802 insertions(+), 135 deletions(-) create mode 100644 source/blender/makesrna/intern/rna_node_tree_interface.cc diff --git a/source/blender/makesrna/RNA_enum_items.hh b/source/blender/makesrna/RNA_enum_items.hh index ce169b29fd0..2eb0c1c5553 100644 --- a/source/blender/makesrna/RNA_enum_items.hh +++ b/source/blender/makesrna/RNA_enum_items.hh @@ -174,6 +174,8 @@ DEF_ENUM(rna_enum_navigation_mode_items) DEF_ENUM(rna_enum_node_socket_in_out_items) DEF_ENUM(rna_enum_node_socket_type_items) +DEF_ENUM(rna_enum_node_tree_interface_item_type_items) + DEF_ENUM(rna_enum_node_math_items) DEF_ENUM(rna_enum_mapping_type_items) DEF_ENUM(rna_enum_node_vec_math_items) diff --git a/source/blender/makesrna/RNA_enum_types.hh b/source/blender/makesrna/RNA_enum_types.hh index 7f5aebf26ef..c3156698142 100644 --- a/source/blender/makesrna/RNA_enum_types.hh +++ b/source/blender/makesrna/RNA_enum_types.hh @@ -46,6 +46,7 @@ const EnumPropertyItem *rna_node_tree_type_itemf(void *data, bool (*poll)(void *data, struct bNodeTreeType *), bool *r_free); +int rna_node_socket_idname_to_enum(const char *idname); struct bNodeSocketType *rna_node_socket_type_from_enum(int value); const EnumPropertyItem *rna_node_socket_type_itemf( void *data, bool (*poll)(void *data, struct bNodeSocketType *), bool *r_free); diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 58bd0e5bc4b..be59ce5ec8d 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -65,6 +65,7 @@ set(DEFSRC rna_nla.cc rna_nodetree.cc rna_node_socket.cc + rna_node_tree_interface.cc rna_object.cc rna_object_force.cc rna_packedfile.cc diff --git a/source/blender/makesrna/intern/makesrna.cc b/source/blender/makesrna/intern/makesrna.cc index 44bcc1d2200..d033c0b2751 100644 --- a/source/blender/makesrna/intern/makesrna.cc +++ b/source/blender/makesrna/intern/makesrna.cc @@ -4736,6 +4736,7 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_nla.cc", nullptr, RNA_def_nla}, {"rna_nodetree.cc", nullptr, RNA_def_nodetree}, {"rna_node_socket.cc", nullptr, RNA_def_node_socket_subtypes}, + {"rna_node_tree_interface.cc", nullptr, RNA_def_node_tree_interface}, {"rna_object.cc", "rna_object_api.cc", RNA_def_object}, {"rna_object_force.cc", nullptr, RNA_def_object_force}, {"rna_depsgraph.cc", nullptr, RNA_def_depsgraph}, diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 984766951ff..b28c411bd08 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -175,6 +175,7 @@ void RNA_def_modifier(struct BlenderRNA *brna); void RNA_def_nla(struct BlenderRNA *brna); void RNA_def_nodetree(struct BlenderRNA *brna); void RNA_def_node_socket_subtypes(struct BlenderRNA *brna); +void RNA_def_node_tree_interface(struct BlenderRNA *brna); void RNA_def_object(struct BlenderRNA *brna); void RNA_def_object_force(struct BlenderRNA *brna); void RNA_def_packedfile(struct BlenderRNA *brna); @@ -403,6 +404,9 @@ char *rna_TextureSlot_path(const struct PointerRNA *ptr); char *rna_Node_ImageUser_path(const struct PointerRNA *ptr); char *rna_CameraBackgroundImage_image_or_movieclip_user_path(const struct PointerRNA *ptr); +/* Node socket subtypes for group interface. */ +void rna_def_node_socket_interface_subtypes(BlenderRNA *brna); + /* Set U.is_dirty and redraw. */ /** diff --git a/source/blender/makesrna/intern/rna_node_socket.cc b/source/blender/makesrna/intern/rna_node_socket.cc index c4e709271d9..fad7ce99f55 100644 --- a/source/blender/makesrna/intern/rna_node_socket.cc +++ b/source/blender/makesrna/intern/rna_node_socket.cc @@ -48,14 +48,12 @@ const EnumPropertyItem rna_enum_node_socket_type_items[] = { # include "ED_node.hh" -extern "C" { extern FunctionRNA rna_NodeSocket_draw_func; extern FunctionRNA rna_NodeSocket_draw_color_func; extern FunctionRNA rna_NodeSocketInterface_draw_func; extern FunctionRNA rna_NodeSocketInterface_draw_color_func; extern FunctionRNA rna_NodeSocketInterface_init_socket_func; extern FunctionRNA rna_NodeSocketInterface_from_socket_func; -} /* ******** Node Socket ******** */ @@ -558,8 +556,9 @@ static void rna_NodeSocketInterfaceStandard_draw_color(ID *id, RNA_pointer_create(id, &RNA_NodeSocketInterface, sock, &ptr); sock->typeinfo->interface_draw_color(C, &ptr, r_color); } +/* ******** Node Socket Subtypes ******** */ -static void rna_NodeSocketStandard_float_range( +void rna_NodeSocketStandard_float_range( PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax) { bNodeSocket *sock = static_cast(ptr->data); @@ -576,7 +575,7 @@ static void rna_NodeSocketStandard_float_range( *softmax = dval->max; } -static void rna_NodeSocketStandard_int_range( +void rna_NodeSocketStandard_int_range( PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) { bNodeSocket *sock = static_cast(ptr->data); @@ -593,7 +592,7 @@ static void rna_NodeSocketStandard_int_range( *softmax = dval->max; } -static void rna_NodeSocketStandard_vector_range( +void rna_NodeSocketStandard_vector_range( PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax) { bNodeSocket *sock = static_cast(ptr->data); @@ -623,8 +622,6 @@ static void rna_NodeSocketStandard_value_and_relation_update(bContext *C, Pointe DEG_relations_tag_update(bmain); } -/* ******** Node Socket Subtypes ******** */ - bool rna_NodeSocketMaterial_default_value_poll(PointerRNA * /*ptr*/, PointerRNA value) { /* Do not show grease pencil materials for now. */ @@ -939,6 +936,93 @@ static void rna_def_node_socket_interface(BlenderRNA *brna) RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); } +static void rna_def_node_socket_standard(BlenderRNA *brna) +{ + /* XXX Workaround: Registered functions are not exposed in python by bpy, + * it expects them to be registered from python and use the native implementation. + * However, the standard socket types below are not registering these functions from python, + * so in order to call them in py scripts we need to overload and + * replace them with plain C callbacks. + * These types provide a usable basis for socket types defined in C. + */ + + StructRNA *srna; + PropertyRNA *prop; + FunctionRNA *func; + PropertyRNA *parm; + + static float default_draw_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; + + srna = RNA_def_struct(brna, "NodeSocketStandard", "NodeSocket"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + /* draw socket */ + func = RNA_def_function(srna, "draw", "rna_NodeSocketStandard_draw"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID); + RNA_def_function_ui_description(func, "Draw socket"); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_property(func, "layout", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(parm, "UILayout"); + RNA_def_property_ui_text(parm, "Layout", "Layout in the UI"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_property(func, "node", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(parm, "Node"); + RNA_def_property_ui_text(parm, "Node", "Node the socket belongs to"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + parm = RNA_def_property(func, "text", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(parm, "Text", "Text label to draw alongside properties"); + // RNA_def_property_string_default(parm, ""); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + + func = RNA_def_function(srna, "draw_color", "rna_NodeSocketStandard_draw_color"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID); + RNA_def_function_ui_description(func, "Color of the socket icon"); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_property(func, "node", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(parm, "Node"); + RNA_def_property_ui_text(parm, "Node", "Node the socket belongs to"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + parm = RNA_def_float_array( + func, "color", 4, default_draw_color, 0.0f, 1.0f, "Color", "", 0.0f, 1.0f); + RNA_def_function_output(func, parm); + + /* Note: Legacy socket interface below. + * The new interface RNA is defined in a separate file, + * the NodeSocketInterface struct will be replaced. */ + + srna = RNA_def_struct(brna, "NodeSocketInterfaceStandard", "NodeSocketInterface"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + /* for easier type comparison in python */ + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "typeinfo->type"); + RNA_def_property_enum_items(prop, rna_enum_node_socket_type_items); + RNA_def_property_enum_default(prop, SOCK_FLOAT); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Type", "Data type"); + + func = RNA_def_function(srna, "draw", "rna_NodeSocketInterfaceStandard_draw"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID); + RNA_def_function_ui_description(func, "Draw template settings"); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_property(func, "layout", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(parm, "UILayout"); + RNA_def_property_ui_text(parm, "Layout", "Layout in the UI"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + + func = RNA_def_function(srna, "draw_color", "rna_NodeSocketInterfaceStandard_draw_color"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID); + RNA_def_function_ui_description(func, "Color of the socket icon"); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_float_array( + func, "color", 4, default_draw_color, 0.0f, 1.0f, "Color", "", 0.0f, 1.0f); + RNA_def_function_output(func, parm); +} + static void rna_def_node_socket_float(BlenderRNA *brna, const char *idname, const char *interface_idname, @@ -1525,167 +1609,684 @@ static void rna_def_node_socket_material(BlenderRNA *brna, RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); } -static void rna_def_node_socket_standard_types(BlenderRNA *brna) +/* Common functions for all builtin socket interface types. */ +static void rna_def_node_tree_interface_socket_builtin(StructRNA *srna) { - /* XXX Workaround: Registered functions are not exposed in python by bpy, - * it expects them to be registered from python and use the native implementation. - * However, the standard socket types below are not registering these functions from python, - * so in order to call them in py scripts we need to overload and - * replace them with plain C callbacks. - * These types provide a usable basis for socket types defined in C. + FunctionRNA *func; + PropertyRNA *parm; + + /* Override for functions, invoking the typeinfo callback directly + * instead of expecting an existing RNA registered function implementation. */ - StructRNA *srna; - PropertyRNA *parm, *prop; - FunctionRNA *func; - - static float default_draw_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; - - srna = RNA_def_struct(brna, "NodeSocketStandard", "NodeSocket"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - /* draw socket */ - func = RNA_def_function(srna, "draw", "rna_NodeSocketStandard_draw"); + func = RNA_def_function(srna, "draw", "rna_NodeTreeInterfaceSocket_draw_builtin"); RNA_def_function_flag(func, FUNC_USE_SELF_ID); - RNA_def_function_ui_description(func, "Draw socket"); + RNA_def_function_ui_description(func, "Draw interface socket settings"); parm = RNA_def_pointer(func, "context", "Context", "", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_property(func, "layout", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(parm, "UILayout"); RNA_def_property_ui_text(parm, "Layout", "Layout in the UI"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_property(func, "node", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(parm, "Node"); - RNA_def_property_ui_text(parm, "Node", "Node the socket belongs to"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); - parm = RNA_def_property(func, "text", PROP_STRING, PROP_NONE); - RNA_def_property_ui_text(parm, "Text", "Text label to draw alongside properties"); - // RNA_def_property_string_default(parm, ""); + + func = RNA_def_function(srna, "init_socket", "rna_NodeTreeInterfaceSocket_init_socket_builtin"); + RNA_def_function_ui_description(func, "Initialize a node socket instance"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_ALLOW_WRITE); + parm = RNA_def_pointer(func, "node", "Node", "Node", "Node of the socket to initialize"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "socket", "NodeSocket", "Socket", "Socket to initialize"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string( + func, "data_path", nullptr, 0, "Data Path", "Path to specialized socket data"); RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - func = RNA_def_function(srna, "draw_color", "rna_NodeSocketStandard_draw_color"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID); - RNA_def_function_ui_description(func, "Color of the socket icon"); - parm = RNA_def_pointer(func, "context", "Context", "", ""); + func = RNA_def_function(srna, "from_socket", "rna_NodeTreeInterfaceSocket_from_socket_builtin"); + RNA_def_function_ui_description(func, "Setup template parameters from an existing socket"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_ALLOW_WRITE); + parm = RNA_def_pointer(func, "node", "Node", "Node", "Node of the original socket"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_property(func, "node", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(parm, "Node"); - RNA_def_property_ui_text(parm, "Node", "Node the socket belongs to"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); - parm = RNA_def_float_array( - func, "color", 4, default_draw_color, 0.0f, 1.0f, "Color", "", 0.0f, 1.0f); - RNA_def_function_output(func, parm); - - srna = RNA_def_struct(brna, "NodeSocketInterfaceStandard", "NodeSocketInterface"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - /* for easier type comparison in python */ - prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, nullptr, "typeinfo->type"); - RNA_def_property_enum_items(prop, rna_enum_node_socket_type_items); - RNA_def_property_enum_default(prop, SOCK_FLOAT); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Type", "Data type"); - - func = RNA_def_function(srna, "draw", "rna_NodeSocketInterfaceStandard_draw"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID); - RNA_def_function_ui_description(func, "Draw template settings"); - parm = RNA_def_pointer(func, "context", "Context", "", ""); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_property(func, "layout", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(parm, "UILayout"); - RNA_def_property_ui_text(parm, "Layout", "Layout in the UI"); + parm = RNA_def_pointer(func, "socket", "NodeSocket", "Socket", "Original socket"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); +} - func = RNA_def_function(srna, "draw_color", "rna_NodeSocketInterfaceStandard_draw_color"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID); - RNA_def_function_ui_description(func, "Color of the socket icon"); - parm = RNA_def_pointer(func, "context", "Context", "", ""); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_float_array( - func, "color", 4, default_draw_color, 0.0f, 1.0f, "Color", "", 0.0f, 1.0f); - RNA_def_function_output(func, parm); +static void rna_def_node_socket_interface_float(BlenderRNA *brna, + const char *identifier, + PropertySubType subtype) +{ + StructRNA *srna; + PropertyRNA *prop; + float value_default; - /* XXX These types should eventually be registered at runtime. - * Then use the nodeStaticSocketType and nodeStaticSocketInterfaceType functions - * to get the idname strings from int type and subtype - * (see node_socket.cc, register_standard_node_socket_types). - */ + /* choose sensible common default based on subtype */ + switch (subtype) { + case PROP_FACTOR: + value_default = 1.0f; + break; + case PROP_PERCENTAGE: + value_default = 100.0f; + break; + default: + value_default = 0.0f; + break; + } - rna_def_node_socket_float(brna, "NodeSocketFloat", "NodeSocketInterfaceFloat", PROP_NONE); - rna_def_node_socket_float( - brna, "NodeSocketFloatUnsigned", "NodeSocketInterfaceFloatUnsigned", PROP_UNSIGNED); - rna_def_node_socket_float( - brna, "NodeSocketFloatPercentage", "NodeSocketInterfaceFloatPercentage", PROP_PERCENTAGE); - rna_def_node_socket_float( - brna, "NodeSocketFloatFactor", "NodeSocketInterfaceFloatFactor", PROP_FACTOR); - rna_def_node_socket_float( - brna, "NodeSocketFloatAngle", "NodeSocketInterfaceFloatAngle", PROP_ANGLE); - rna_def_node_socket_float( - brna, "NodeSocketFloatTime", "NodeSocketInterfaceFloatTime", PROP_TIME); - rna_def_node_socket_float(brna, - "NodeSocketFloatTimeAbsolute", - "NodeSocketInterfaceFloatTimeAbsolute", - PROP_TIME_ABSOLUTE); - rna_def_node_socket_float( - brna, "NodeSocketFloatDistance", "NodeSocketInterfaceFloatDistance", PROP_DISTANCE); + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text( + srna, "Float Node Socket Interface", "Floating-point number socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); - rna_def_node_socket_int(brna, "NodeSocketInt", "NodeSocketInterfaceInt", PROP_NONE); - rna_def_node_socket_int( - brna, "NodeSocketIntUnsigned", "NodeSocketInterfaceIntUnsigned", PROP_UNSIGNED); - rna_def_node_socket_int( - brna, "NodeSocketIntPercentage", "NodeSocketInterfaceIntPercentage", PROP_PERCENTAGE); - rna_def_node_socket_int( - brna, "NodeSocketIntFactor", "NodeSocketInterfaceIntFactor", PROP_FACTOR); + RNA_def_struct_sdna_from(srna, "bNodeSocketValueFloat", "socket_data"); - rna_def_node_socket_bool(brna, "NodeSocketBool", "NodeSocketInterfaceBool"); - rna_def_node_socket_rotation(brna, "NodeSocketRotation", "NodeSocketInterfaceRotation"); + prop = RNA_def_property(srna, "default_value", PROP_FLOAT, subtype); + RNA_def_property_float_sdna(prop, nullptr, "value"); + RNA_def_property_float_default(prop, value_default); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_float_funcs( + prop, nullptr, nullptr, "rna_NodeTreeInterfaceSocketFloat_default_value_range"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); - rna_def_node_socket_vector(brna, "NodeSocketVector", "NodeSocketInterfaceVector", PROP_NONE); - rna_def_node_socket_vector(brna, - "NodeSocketVectorTranslation", - "NodeSocketInterfaceVectorTranslation", - PROP_TRANSLATION); - rna_def_node_socket_vector( - brna, "NodeSocketVectorDirection", "NodeSocketInterfaceVectorDirection", PROP_DIRECTION); - rna_def_node_socket_vector( - brna, "NodeSocketVectorVelocity", "NodeSocketInterfaceVectorVelocity", PROP_VELOCITY); - rna_def_node_socket_vector(brna, - "NodeSocketVectorAcceleration", - "NodeSocketInterfaceVectorAcceleration", - PROP_ACCELERATION); - rna_def_node_socket_vector( - brna, "NodeSocketVectorEuler", "NodeSocketInterfaceVectorEuler", PROP_EULER); - rna_def_node_socket_vector( - brna, "NodeSocketVectorXYZ", "NodeSocketInterfaceVectorXYZ", PROP_XYZ); + prop = RNA_def_property(srna, "min_value", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, nullptr, "min"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Minimum Value", "Minimum value"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); - rna_def_node_socket_color(brna, "NodeSocketColor", "NodeSocketInterfaceColor"); + prop = RNA_def_property(srna, "max_value", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, nullptr, "max"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Maximum Value", "Maximum value"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); - rna_def_node_socket_string(brna, "NodeSocketString", "NodeSocketInterfaceString"); + RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); - rna_def_node_socket_shader(brna, "NodeSocketShader", "NodeSocketInterfaceShader"); + rna_def_node_tree_interface_socket_builtin(srna); +} + +static void rna_def_node_socket_interface_int(BlenderRNA *brna, + const char *identifier, + PropertySubType subtype) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Integer Node Socket Interface", "Integer number socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueInt", "socket_data"); + + prop = RNA_def_property(srna, "default_value", PROP_INT, subtype); + RNA_def_property_int_sdna(prop, nullptr, "value"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_int_funcs( + prop, nullptr, nullptr, "rna_NodeTreeInterfaceSocketInt_default_value_range"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + + prop = RNA_def_property(srna, "min_value", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, nullptr, "min"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Minimum Value", "Minimum value"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + + prop = RNA_def_property(srna, "max_value", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, nullptr, "max"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Maximum Value", "Maximum value"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + + RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); + + rna_def_node_tree_interface_socket_builtin(srna); +} + +static void rna_def_node_socket_interface_bool(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Boolean Node Socket Interface", "Boolean value socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueBoolean", "socket_data"); + + prop = RNA_def_property(srna, "default_value", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "value", 1); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + + RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); + + rna_def_node_tree_interface_socket_builtin(srna); +} + +static void rna_def_node_socket_interface_rotation(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text( + srna, "Rotation Node Socket Interface", "Rotation value socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueRotation", "socket_data"); + + prop = RNA_def_property(srna, "default_value", PROP_FLOAT, PROP_EULER); + RNA_def_property_float_sdna(prop, nullptr, "value_euler"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + + RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); + + rna_def_node_tree_interface_socket_builtin(srna); +} + +static void rna_def_node_socket_interface_vector(BlenderRNA *brna, + const char *identifier, + PropertySubType subtype) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Vector Node Socket Interface", "3D vector socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueVector", "socket_data"); + + prop = RNA_def_property(srna, "default_value", PROP_FLOAT, subtype); + RNA_def_property_float_sdna(prop, nullptr, "value"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_float_funcs( + prop, nullptr, nullptr, "rna_NodeTreeInterfaceSocketVector_default_value_range"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + + prop = RNA_def_property(srna, "min_value", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, nullptr, "min"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Minimum Value", "Minimum value"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + + prop = RNA_def_property(srna, "max_value", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, nullptr, "max"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Maximum Value", "Maximum value"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + + RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); + + rna_def_node_tree_interface_socket_builtin(srna); +} + +static void rna_def_node_socket_interface_color(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Color Node Socket Interface", "RGBA color socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueRGBA", "socket_data"); + + prop = RNA_def_property(srna, "default_value", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_sdna(prop, nullptr, "value"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + + RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); + + rna_def_node_tree_interface_socket_builtin(srna); +} + +static void rna_def_node_socket_interface_string(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "String Node Socket Interface", "String socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueString", "socket_data"); + + prop = RNA_def_property(srna, "default_value", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, nullptr, "value"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + + RNA_def_struct_sdna_from(srna, "bNodeTreeInterfaceSocket", nullptr); + + rna_def_node_tree_interface_socket_builtin(srna); +} + +static void rna_def_node_socket_interface_shader(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Shader Node Socket Interface", "Shader socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + rna_def_node_tree_interface_socket_builtin(srna); +} + +static void rna_def_node_socket_interface_object(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Object Node Socket Interface", "Object socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueObject", "socket_data"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "value"); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + + rna_def_node_tree_interface_socket_builtin(srna); +} + +static void rna_def_node_socket_interface_image(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Image Node Socket Interface", "Image socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueImage", "socket_data"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "value"); + RNA_def_property_struct_type(prop, "Image"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + + rna_def_node_tree_interface_socket_builtin(srna); +} + +static void rna_def_node_socket_interface_geometry(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Geometry Node Socket Interface", "Geometry socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + rna_def_node_tree_interface_socket_builtin(srna); +} + +static void rna_def_node_socket_interface_collection(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Collection Node Socket Interface", "Collection socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueCollection", "socket_data"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "value"); + RNA_def_property_struct_type(prop, "Collection"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + + rna_def_node_tree_interface_socket_builtin(srna); +} + +static void rna_def_node_socket_interface_texture(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Texture Node Socket Interface", "Texture socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueTexture", "socket_data"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "value"); + RNA_def_property_struct_type(prop, "Texture"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + + rna_def_node_tree_interface_socket_builtin(srna); +} + +static void rna_def_node_socket_interface_material(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeTreeInterfaceSocket"); + RNA_def_struct_ui_text(srna, "Material Node Socket Interface", "Material socket of a node"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "socket_data"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "value"); + RNA_def_property_struct_type(prop, "Material"); + RNA_def_property_pointer_funcs( + prop, nullptr, nullptr, nullptr, "rna_NodeTreeInterfaceSocketMaterial_default_value_poll"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceSocket_value_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + + rna_def_node_tree_interface_socket_builtin(srna); +} + +/* Info for generating static subtypes. */ +struct bNodeSocketStaticTypeInfo { + const char *socket_identifier; + const char *interface_identifier_legacy; + const char *interface_identifier; + eNodeSocketDatatype type; + PropertySubType subtype; + const char *label; +}; + +/* Note: Socket and interface subtypes could be defined from a single central list, + * but makesrna cannot have a dependency on BKE, so this list would have to live in RNA itself, + * with BKE etc. accessing the RNA API to get the subtypes info. */ +static const bNodeSocketStaticTypeInfo node_socket_subtypes[] = { + {"NodeSocketFloat", + "NodeSocketInterfaceFloat", + "NodeTreeInterfaceSocketFloat", + SOCK_FLOAT, + PROP_NONE}, + {"NodeSocketFloatUnsigned", + "NodeSocketInterfaceFloatUnsigned", + "NodeTreeInterfaceSocketFloatUnsigned", + SOCK_FLOAT, + PROP_UNSIGNED}, + {"NodeSocketFloatPercentage", + "NodeSocketInterfaceFloatPercentage", + "NodeTreeInterfaceSocketFloatPercentage", + SOCK_FLOAT, + PROP_PERCENTAGE}, + {"NodeSocketFloatFactor", + "NodeSocketInterfaceFloatFactor", + "NodeTreeInterfaceSocketFloatFactor", + SOCK_FLOAT, + PROP_FACTOR}, + {"NodeSocketFloatAngle", + "NodeSocketInterfaceFloatAngle", + "NodeTreeInterfaceSocketFloatAngle", + SOCK_FLOAT, + PROP_ANGLE}, + {"NodeSocketFloatTime", + "NodeSocketInterfaceFloatTime", + "NodeTreeInterfaceSocketFloatTime", + SOCK_FLOAT, + PROP_TIME}, + {"NodeSocketFloatTimeAbsolute", + "NodeSocketInterfaceFloatTimeAbsolute", + "NodeTreeInterfaceSocketFloatTimeAbsolute", + SOCK_FLOAT, + PROP_TIME_ABSOLUTE}, + {"NodeSocketFloatDistance", + "NodeSocketInterfaceFloatDistance", + "NodeTreeInterfaceSocketFloatDistance", + SOCK_FLOAT, + PROP_DISTANCE}, + {"NodeSocketInt", "NodeSocketInterfaceInt", "NodeTreeInterfaceSocketInt", SOCK_INT, PROP_NONE}, + {"NodeSocketIntUnsigned", + "NodeSocketInterfaceIntUnsigned", + "NodeTreeInterfaceSocketIntUnsigned", + SOCK_INT, + PROP_UNSIGNED}, + {"NodeSocketIntPercentage", + "NodeSocketInterfaceIntPercentage", + "NodeTreeInterfaceSocketIntPercentage", + SOCK_INT, + PROP_PERCENTAGE}, + {"NodeSocketIntFactor", + "NodeSocketInterfaceIntFactor", + "NodeTreeInterfaceSocketIntFactor", + SOCK_INT, + PROP_FACTOR}, + {"NodeSocketBool", + "NodeSocketInterfaceBool", + "NodeTreeInterfaceSocketBool", + SOCK_BOOLEAN, + PROP_NONE}, + {"NodeSocketRotation", + "NodeSocketInterfaceRotation", + "NodeTreeInterfaceSocketRotation", + SOCK_ROTATION, + PROP_NONE}, + {"NodeSocketVector", + "NodeSocketInterfaceVector", + "NodeTreeInterfaceSocketVector", + SOCK_VECTOR, + PROP_NONE}, + {"NodeSocketVectorTranslation", + "NodeSocketInterfaceVectorTranslation", + "NodeTreeInterfaceSocketVectorTranslation", + SOCK_VECTOR, + PROP_TRANSLATION}, + {"NodeSocketVectorDirection", + "NodeSocketInterfaceVectorDirection", + "NodeTreeInterfaceSocketVectorDirection", + SOCK_VECTOR, + PROP_DIRECTION}, + {"NodeSocketVectorVelocity", + "NodeSocketInterfaceVectorVelocity", + "NodeTreeInterfaceSocketVectorVelocity", + SOCK_VECTOR, + PROP_VELOCITY}, + {"NodeSocketVectorAcceleration", + "NodeSocketInterfaceVectorAcceleration", + "NodeTreeInterfaceSocketVectorAcceleration", + SOCK_VECTOR, + PROP_ACCELERATION}, + {"NodeSocketVectorEuler", + "NodeSocketInterfaceVectorEuler", + "NodeTreeInterfaceSocketVectorEuler", + SOCK_VECTOR, + PROP_EULER}, + {"NodeSocketVectorXYZ", + "NodeSocketInterfaceVectorXYZ", + "NodeTreeInterfaceSocketVectorXYZ", + SOCK_VECTOR, + PROP_XYZ}, + {"NodeSocketColor", + "NodeSocketInterfaceColor", + "NodeTreeInterfaceSocketColor", + SOCK_RGBA, + PROP_NONE}, + {"NodeSocketString", + "NodeSocketInterfaceString", + "NodeTreeInterfaceSocketString", + SOCK_STRING, + PROP_NONE}, + {"NodeSocketShader", + "NodeSocketInterfaceShader", + "NodeTreeInterfaceSocketShader", + SOCK_SHADER, + PROP_NONE}, + {"NodeSocketObject", + "NodeSocketInterfaceObject", + "NodeTreeInterfaceSocketObject", + SOCK_OBJECT, + PROP_NONE}, + {"NodeSocketImage", + "NodeSocketInterfaceImage", + "NodeTreeInterfaceSocketImage", + SOCK_IMAGE, + PROP_NONE}, + {"NodeSocketGeometry", + "NodeSocketInterfaceGeometry", + "NodeTreeInterfaceSocketGeometry", + SOCK_GEOMETRY, + PROP_NONE}, + {"NodeSocketCollection", + "NodeSocketInterfaceCollection", + "NodeTreeInterfaceSocketCollection", + SOCK_COLLECTION, + PROP_NONE}, + {"NodeSocketTexture", + "NodeSocketInterfaceTexture", + "NodeTreeInterfaceSocketTexture", + SOCK_TEXTURE, + PROP_NONE}, + {"NodeSocketMaterial", + "NodeSocketInterfaceMaterial", + "NodeTreeInterfaceSocketMaterial", + SOCK_MATERIAL, + PROP_NONE}, +}; + +static void rna_def_node_socket_subtypes(BlenderRNA *brna) +{ + for (const bNodeSocketStaticTypeInfo &info : node_socket_subtypes) { + const char *identifier = info.socket_identifier; + const char *interface_identifier = info.interface_identifier_legacy; + + switch (info.type) { + case SOCK_FLOAT: + rna_def_node_socket_float(brna, identifier, interface_identifier, info.subtype); + break; + case SOCK_INT: + rna_def_node_socket_int(brna, identifier, interface_identifier, info.subtype); + break; + case SOCK_BOOLEAN: + rna_def_node_socket_bool(brna, identifier, interface_identifier); + break; + case SOCK_ROTATION: + rna_def_node_socket_rotation(brna, identifier, interface_identifier); + break; + case SOCK_VECTOR: + rna_def_node_socket_vector(brna, identifier, interface_identifier, info.subtype); + break; + case SOCK_RGBA: + rna_def_node_socket_color(brna, identifier, interface_identifier); + break; + case SOCK_STRING: + rna_def_node_socket_string(brna, identifier, interface_identifier); + break; + case SOCK_SHADER: + rna_def_node_socket_shader(brna, identifier, interface_identifier); + break; + case SOCK_OBJECT: + rna_def_node_socket_object(brna, identifier, interface_identifier); + break; + case SOCK_IMAGE: + rna_def_node_socket_image(brna, identifier, interface_identifier); + break; + case SOCK_GEOMETRY: + rna_def_node_socket_geometry(brna, identifier, interface_identifier); + break; + case SOCK_COLLECTION: + rna_def_node_socket_collection(brna, identifier, interface_identifier); + break; + case SOCK_TEXTURE: + rna_def_node_socket_texture(brna, identifier, interface_identifier); + break; + case SOCK_MATERIAL: + rna_def_node_socket_material(brna, identifier, interface_identifier); + break; + + case SOCK_CUSTOM: + break; + } + } rna_def_node_socket_virtual(brna, "NodeSocketVirtual"); +} - rna_def_node_socket_object(brna, "NodeSocketObject", "NodeSocketInterfaceObject"); +/* Note: interface items are defined outside this file. + * The subtypes must be defined after the base type, so this function + * is called from the interface rna file to ensure correct order. */ +void rna_def_node_socket_interface_subtypes(BlenderRNA *brna) +{ + for (const bNodeSocketStaticTypeInfo &info : node_socket_subtypes) { + const char *identifier = info.interface_identifier; - rna_def_node_socket_image(brna, "NodeSocketImage", "NodeSocketInterfaceImage"); + switch (info.type) { + case SOCK_FLOAT: + rna_def_node_socket_interface_float(brna, identifier, info.subtype); + break; + case SOCK_INT: + rna_def_node_socket_interface_int(brna, identifier, info.subtype); + break; + case SOCK_BOOLEAN: + rna_def_node_socket_interface_bool(brna, identifier); + break; + case SOCK_ROTATION: + rna_def_node_socket_interface_rotation(brna, identifier); + break; + case SOCK_VECTOR: + rna_def_node_socket_interface_vector(brna, identifier, info.subtype); + break; + case SOCK_RGBA: + rna_def_node_socket_interface_color(brna, identifier); + break; + case SOCK_STRING: + rna_def_node_socket_interface_string(brna, identifier); + break; + case SOCK_SHADER: + rna_def_node_socket_interface_shader(brna, identifier); + break; + case SOCK_OBJECT: + rna_def_node_socket_interface_object(brna, identifier); + break; + case SOCK_IMAGE: + rna_def_node_socket_interface_image(brna, identifier); + break; + case SOCK_GEOMETRY: + rna_def_node_socket_interface_geometry(brna, identifier); + break; + case SOCK_COLLECTION: + rna_def_node_socket_interface_collection(brna, identifier); + break; + case SOCK_TEXTURE: + rna_def_node_socket_interface_texture(brna, identifier); + break; + case SOCK_MATERIAL: + rna_def_node_socket_interface_material(brna, identifier); + break; - rna_def_node_socket_geometry(brna, "NodeSocketGeometry", "NodeSocketInterfaceGeometry"); - - rna_def_node_socket_collection(brna, "NodeSocketCollection", "NodeSocketInterfaceCollection"); - - rna_def_node_socket_texture(brna, "NodeSocketTexture", "NodeSocketInterfaceTexture"); - - rna_def_node_socket_material(brna, "NodeSocketMaterial", "NodeSocketInterfaceMaterial"); + case SOCK_CUSTOM: + break; + } + } } void RNA_def_node_socket_subtypes(BlenderRNA *brna) { rna_def_node_socket(brna); rna_def_node_socket_interface(brna); + rna_def_node_socket_standard(brna); - rna_def_node_socket_standard_types(brna); + rna_def_node_socket_subtypes(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_node_tree_interface.cc b/source/blender/makesrna/intern/rna_node_tree_interface.cc new file mode 100644 index 00000000000..93405dcb3fe --- /dev/null +++ b/source/blender/makesrna/intern/rna_node_tree_interface.cc @@ -0,0 +1,1045 @@ +/* SPDX-FileCopyrightText: 2023 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup RNA + */ + +#include "DNA_node_tree_interface_types.h" + +#include "RNA_define.hh" +#include "RNA_enum_types.hh" +#include "RNA_types.hh" + +#include "rna_internal.h" + +#include "WM_types.hh" + +const EnumPropertyItem rna_enum_node_tree_interface_item_type_items[] = { + {NODE_INTERFACE_SOCKET, "SOCKET", 0, "Socket", ""}, + {NODE_INTERFACE_PANEL, "PANEL", 0, "Panel", ""}, + {0, nullptr, 0, nullptr, nullptr}}; + +#ifdef RNA_RUNTIME + +# include "BKE_node.h" +# include "BKE_node_runtime.hh" +# include "BKE_node_tree_interface.hh" +# include "BKE_node_tree_update.h" +# include "DNA_material_types.h" +# include "ED_node.hh" +# include "WM_api.hh" + +/* Internal RNA function declarations, used to invoke registered callbacks. */ +extern FunctionRNA rna_NodeTreeInterfaceSocket_draw_func; +extern FunctionRNA rna_NodeTreeInterfaceSocket_init_socket_func; +extern FunctionRNA rna_NodeTreeInterfaceSocket_from_socket_func; + +namespace node_interface = blender::bke::node_interface; + +static void rna_NodeTreeInterfaceItem_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(nullptr, bmain, ntree); +} + +static StructRNA *rna_NodeTreeInterfaceItem_refine(PointerRNA *ptr) +{ + bNodeTreeInterfaceItem *item = static_cast(ptr->data); + + switch (item->item_type) { + case NODE_INTERFACE_SOCKET: { + bNodeTreeInterfaceSocket &socket = node_interface::get_item_as( + *item); + bNodeSocketType *socket_typeinfo = nodeSocketTypeFind(socket.socket_type); + if (socket_typeinfo && socket_typeinfo->ext_interface.srna) { + return socket_typeinfo->ext_interface.srna; + } + return &RNA_NodeTreeInterfaceSocket; + } + case NODE_INTERFACE_PANEL: + return &RNA_NodeTreeInterfacePanel; + default: + return &RNA_NodeTreeInterfaceItem; + } +} + +static char *rna_NodeTreeInterfaceItem_path(const PointerRNA *ptr) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + const bNodeTreeInterfaceItem *item = static_cast(ptr->data); + if (!ntree->runtime) { + return nullptr; + } + + ntree->ensure_topology_cache(); + UNUSED_VARS(item); + // Note: New API, will be enabled after new interface cache is added. + // const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache(); + // for (const int index : cache.items.index_range()) { + // if (cache.items[index] == item) { + // return BLI_sprintfN("interface.ui_items[%d]", index); + // } + // } + return nullptr; +} + +static bool rna_NodeTreeInterfaceSocket_unregister(Main * /*bmain*/, StructRNA *type) +{ + bNodeSocketType *st = static_cast(RNA_struct_blender_type_get(type)); + if (!st) { + return false; + } + + RNA_struct_free_extension(type, &st->ext_interface); + + RNA_struct_free(&BLENDER_RNA, type); + + /* update while blender is running */ + WM_main_add_notifier(NC_NODE | NA_EDITED, nullptr); + return true; +} + +static void rna_NodeTreeInterfaceSocket_draw_builtin(ID *id, + bNodeTreeInterfaceSocket *interface_socket, + bContext *C, + uiLayout *layout) +{ + bNodeSocketType *typeinfo = interface_socket->socket_typeinfo(); + if (typeinfo && typeinfo->interface_draw) { + UNUSED_VARS(id, C, layout); + // Note: New API, will be enabled after typeinfo callbacks change. + // typeinfo->interface_draw(id, interface_socket, C, layout); + } +} + +// Note: New API, interface draw callback used after changing callbacks. +static void UNUSED_FUNCTION(rna_NodeTreeInterfaceSocket_draw_custom)( + ID *id, bNodeTreeInterfaceSocket *interface_socket, bContext *C, uiLayout *layout) +{ + bNodeSocketType *typeinfo = nodeSocketTypeFind(interface_socket->socket_type); + if (typeinfo == nullptr) { + return; + } + + PointerRNA ptr; + RNA_pointer_create(id, &RNA_NodeTreeInterfaceSocket, interface_socket, &ptr); + + FunctionRNA *func = &rna_NodeTreeInterfaceSocket_draw_func; + + ParameterList list; + RNA_parameter_list_create(&list, &ptr, func); + RNA_parameter_set_lookup(&list, "context", &C); + RNA_parameter_set_lookup(&list, "layout", &layout); + typeinfo->ext_interface.call(C, &ptr, func, &list); + + RNA_parameter_list_free(&list); +} + +static void rna_NodeTreeInterfaceSocket_init_socket_builtin( + ID *id, + bNodeTreeInterfaceSocket *interface_socket, + bNode *node, + bNodeSocket *socket, + const char *data_path) +{ + bNodeSocketType *typeinfo = interface_socket->socket_typeinfo(); + if (typeinfo && typeinfo->interface_draw) { + // Note: New API, callback signatures change. + UNUSED_VARS(id, node, socket, data_path); + // typeinfo->interface_init_socket(id, interface_socket, node, socket, data_path); + } +} + +// Note: New API, used when callbacks change. +static void UNUSED_FUNCTION(rna_NodeTreeInterfaceSocket_init_socket_custom)( + ID *id, + const bNodeTreeInterfaceSocket *interface_socket, + bNode *node, + bNodeSocket *socket, + const char *data_path) +{ + bNodeSocketType *typeinfo = nodeSocketTypeFind(interface_socket->socket_type); + if (typeinfo == nullptr) { + return; + } + + PointerRNA ptr, node_ptr, socket_ptr; + RNA_pointer_create(id, + &RNA_NodeTreeInterfaceSocket, + const_cast(interface_socket), + &ptr); + RNA_pointer_create(id, &RNA_Node, node, &node_ptr); + RNA_pointer_create(id, &RNA_NodeSocket, socket, &socket_ptr); + + FunctionRNA *func = &rna_NodeTreeInterfaceSocket_init_socket_func; + + ParameterList list; + RNA_parameter_list_create(&list, &ptr, func); + RNA_parameter_set_lookup(&list, "node", &node_ptr); + RNA_parameter_set_lookup(&list, "socket", &socket_ptr); + RNA_parameter_set_lookup(&list, "data_path", &data_path); + typeinfo->ext_interface.call(nullptr, &ptr, func, &list); + + RNA_parameter_list_free(&list); +} + +static void rna_NodeTreeInterfaceSocket_from_socket_builtin( + ID *id, bNodeTreeInterfaceSocket *interface_socket, bNode *node, bNodeSocket *socket) +{ + bNodeSocketType *typeinfo = interface_socket->socket_typeinfo(); + if (typeinfo && typeinfo->interface_draw) { + // Note: New API, callback signatures change. + UNUSED_VARS(id, node, socket); + // typeinfo->interface_from_socket(id, interface_socket, node, socket); + } +} + +// Note: New API, used after callback signatures change. +static void UNUSED_FUNCTION(rna_NodeTreeInterfaceSocket_from_socket_custom)( + ID *id, + bNodeTreeInterfaceSocket *interface_socket, + const bNode *node, + const bNodeSocket *socket) +{ + bNodeSocketType *typeinfo = nodeSocketTypeFind(interface_socket->socket_type); + if (typeinfo == nullptr) { + return; + } + + PointerRNA ptr, node_ptr, socket_ptr; + RNA_pointer_create(id, &RNA_NodeTreeInterfaceSocket, interface_socket, &ptr); + RNA_pointer_create(id, &RNA_Node, const_cast(node), &node_ptr); + RNA_pointer_create(id, &RNA_NodeSocket, const_cast(socket), &socket_ptr); + + FunctionRNA *func = &rna_NodeTreeInterfaceSocket_from_socket_func; + + ParameterList list; + RNA_parameter_list_create(&list, &ptr, func); + RNA_parameter_set_lookup(&list, "node", &node_ptr); + RNA_parameter_set_lookup(&list, "socket", &socket_ptr); + typeinfo->ext_interface.call(nullptr, &ptr, func, &list); + + RNA_parameter_list_free(&list); +} + +static StructRNA *rna_NodeTreeInterfaceSocket_register(Main * /*bmain*/, + ReportList * /*reports*/, + void *data, + const char *identifier, + StructValidateFunc validate, + StructCallbackFunc call, + StructFreeFunc free) +{ + bNodeTreeInterfaceSocket dummy_socket; + memset(&dummy_socket, 0, sizeof(bNodeTreeInterfaceSocket)); + /* Set #item_type so that refining the type ends up with RNA_NodeTreeInterfaceSocket. */ + dummy_socket.item.item_type = NODE_INTERFACE_SOCKET; + + PointerRNA dummy_socket_ptr; + RNA_pointer_create(nullptr, &RNA_NodeTreeInterfaceSocket, &dummy_socket, &dummy_socket_ptr); + + /* Validate the python class. */ + bool have_function[3]; + if (validate(&dummy_socket_ptr, data, have_function) != 0) { + return nullptr; + } + + /* Check if we have registered this socket type before. */ + bNodeSocketType *st = nodeSocketTypeFind(dummy_socket.socket_type); + if (st) { + /* Socket type registered before. */ + } + else { + /* Create a new node socket type. */ + st = MEM_cnew(__func__); + BLI_strncpy(st->idname, dummy_socket.socket_type, sizeof(st->idname)); + + nodeRegisterSocketType(st); + } + + st->free_self = (void (*)(bNodeSocketType * stype)) MEM_freeN; + + /* if RNA type is already registered, unregister first */ + if (st->ext_interface.srna) { + StructRNA *srna = st->ext_interface.srna; + RNA_struct_free_extension(srna, &st->ext_interface); + RNA_struct_free(&BLENDER_RNA, srna); + } + st->ext_interface.srna = RNA_def_struct_ptr( + &BLENDER_RNA, identifier, &RNA_NodeTreeInterfaceSocket); + st->ext_interface.data = data; + st->ext_interface.call = call; + st->ext_interface.free = free; + RNA_struct_blender_type_set(st->ext_interface.srna, st); + + // Note: New API callbacks. + // st->interface_draw = (have_function[0]) ? rna_NodeTreeInterfaceSocket_draw_custom : nullptr; + // st->interface_init_socket = (have_function[1]) ? + // rna_NodeTreeInterfaceSocket_init_socket_custom : + // nullptr; + // st->interface_from_socket = (have_function[2]) ? + // rna_NodeTreeInterfaceSocket_from_socket_custom : + // nullptr; + + /* Cleanup local dummy type. */ + MEM_SAFE_FREE(dummy_socket.socket_type); + + /* Update while blender is running */ + WM_main_add_notifier(NC_NODE | NA_EDITED, nullptr); + + return st->ext_interface.srna; +} + +static IDProperty **rna_NodeTreeInterfaceSocket_idprops(PointerRNA *ptr) +{ + bNodeTreeInterfaceSocket *socket = static_cast(ptr->data); + return &socket->properties; +} + +static void rna_NodeTreeInterfaceSocket_identifier_get(PointerRNA *ptr, char *value) +{ + bNodeTreeInterfaceSocket *socket = static_cast(ptr->data); + strcpy(value, socket->identifier); +} + +static int rna_NodeTreeInterfaceSocket_identifier_length(PointerRNA *ptr) +{ + bNodeTreeInterfaceSocket *socket = static_cast(ptr->data); + return strlen(socket->identifier); +} + +static int rna_NodeTreeInterfaceSocket_socket_type_get(PointerRNA *ptr) +{ + bNodeTreeInterfaceSocket *socket = static_cast(ptr->data); + return rna_node_socket_idname_to_enum(socket->socket_type); +} + +static void rna_NodeTreeInterfaceSocket_socket_type_set(PointerRNA *ptr, int value) +{ + bNodeSocketType *typeinfo = rna_node_socket_type_from_enum(value); + + if (typeinfo) { + bNodeTreeInterfaceSocket *socket = static_cast(ptr->data); + socket->set_socket_type(typeinfo->idname); + } +} + +static bool is_socket_type_supported(bNodeTreeType *ntreetype, bNodeSocketType *socket_type) +{ + /* Check if the node tree supports the socket type. */ + if (ntreetype->valid_socket_type && !ntreetype->valid_socket_type(ntreetype, socket_type)) { + return false; + } + + /* Only use basic socket types for this enum. */ + if (socket_type->subtype != PROP_NONE) { + return false; + } + + if (!U.experimental.use_rotation_socket && socket_type->type == SOCK_ROTATION) { + return false; + } + + return true; +} + +static bNodeSocketType *find_supported_socket_type(bNodeTreeType *ntree_type) +{ + NODE_SOCKET_TYPES_BEGIN (socket_type) { + if (is_socket_type_supported(ntree_type, socket_type)) { + return socket_type; + } + } + NODE_SOCKET_TYPES_END; + return nullptr; +} + +static bool rna_NodeTreeInterfaceSocket_socket_type_poll(void *userdata, + bNodeSocketType *socket_type) +{ + bNodeTreeType *ntreetype = static_cast(userdata); + return is_socket_type_supported(ntreetype, socket_type); +} + +static const EnumPropertyItem *rna_NodeTreeInterfaceSocket_socket_type_itemf( + bContext * /*C*/, PointerRNA *ptr, PropertyRNA * /*prop*/, bool *r_free) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + + if (!ntree) { + return DummyRNA_NULL_items; + } + + return rna_node_socket_type_itemf( + ntree->typeinfo, rna_NodeTreeInterfaceSocket_socket_type_poll, r_free); +} + +static PointerRNA rna_NodeTreeInterfaceItems_active_get(PointerRNA *ptr) +{ + bNodeTreeInterface *interface = static_cast(ptr->data); + PointerRNA r_ptr; + RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfaceItem, interface->active_item(), &r_ptr); + return r_ptr; +} + +static void rna_NodeTreeInterfaceItems_active_set(PointerRNA *ptr, + PointerRNA value, + ReportList * /*reports*/) +{ + bNodeTreeInterface *interface = static_cast(ptr->data); + bNodeTreeInterfaceItem *item = static_cast(value.data); + interface->active_item_set(item); +} + +static bNodeTreeInterfaceSocket *rna_NodeTreeInterfaceItems_new_socket( + ID *id, + bNodeTreeInterface *interface, + Main *bmain, + ReportList *reports, + const char *name, + const char *description, + bool is_input, + bool is_output, + int socket_type_enum, + bNodeTreeInterfacePanel *parent) +{ + if (parent != nullptr && !interface->find_item(parent->item)) { + BKE_report(reports, RPT_ERROR_INVALID_INPUT, "Parent is not part of the interface"); + return nullptr; + } + bNodeTree *ntree = reinterpret_cast(id); + bNodeSocketType *typeinfo = rna_node_socket_type_from_enum(socket_type_enum); + if (typeinfo == nullptr) { + BKE_report(reports, RPT_ERROR_INVALID_INPUT, "Unknown socket type"); + return nullptr; + } + + /* If data type is unsupported try to find a valid type. */ + if (!is_socket_type_supported(ntree->typeinfo, typeinfo)) { + typeinfo = find_supported_socket_type(ntree->typeinfo); + if (typeinfo == nullptr) { + BKE_report(reports, RPT_ERROR, "Could not find supported socket type"); + return nullptr; + } + } + const char *socket_type = typeinfo->idname; + + eNodeTreeInterfaceSocketFlag flag = eNodeTreeInterfaceSocketFlag(0); + SET_FLAG_FROM_TEST(flag, is_input, NODE_INTERFACE_SOCKET_INPUT); + SET_FLAG_FROM_TEST(flag, is_output, NODE_INTERFACE_SOCKET_OUTPUT); + + bNodeTreeInterfaceSocket *socket = interface->add_socket(name ? name : "", + description ? description : "", + socket_type ? socket_type : "", + flag, + parent); + + if (socket == nullptr) { + BKE_report(reports, RPT_ERROR, "Unable to create socket"); + } + else { + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(nullptr, bmain, ntree); + WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); + } + + return socket; +} + +static bNodeTreeInterfacePanel *rna_NodeTreeInterfaceItems_new_panel( + ID *id, + bNodeTreeInterface *interface, + Main *bmain, + ReportList *reports, + const char *name, + bNodeTreeInterfacePanel *parent) +{ + if (parent != nullptr && !interface->find_item(parent->item)) { + BKE_report(reports, RPT_ERROR_INVALID_INPUT, "Parent is not part of the interface"); + return nullptr; + } + + bNodeTreeInterfacePanel *panel = interface->add_panel(name ? name : "", parent); + + if (panel == nullptr) { + BKE_report(reports, RPT_ERROR, "Unable to create panel"); + } + else { + bNodeTree *ntree = reinterpret_cast(id); + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(nullptr, bmain, ntree); + WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); + } + + return panel; +} + +static bNodeTreeInterfaceItem *rna_NodeTreeInterfaceItems_copy_to_parent( + ID *id, + bNodeTreeInterface *interface, + Main *bmain, + ReportList *reports, + bNodeTreeInterfaceItem *item, + bNodeTreeInterfacePanel *parent) +{ + if (parent != nullptr && !interface->find_item(parent->item)) { + BKE_report(reports, RPT_ERROR_INVALID_INPUT, "Parent is not part of the interface"); + return nullptr; + } + + if (parent == nullptr) { + parent = &interface->root_panel; + } + const int index = parent->items().as_span().first_index_try(item); + if (!parent->items().index_range().contains(index)) { + return nullptr; + } + + bNodeTreeInterfaceItem *item_copy = interface->insert_item_copy(*item, parent, index + 1); + + if (item_copy == nullptr) { + BKE_report(reports, RPT_ERROR, "Unable to copy item"); + } + else { + bNodeTree *ntree = reinterpret_cast(id); + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(nullptr, bmain, ntree); + WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); + } + + return item_copy; +} + +static bNodeTreeInterfaceItem *rna_NodeTreeInterfaceItems_copy(ID *id, + bNodeTreeInterface *interface, + Main *bmain, + ReportList *reports, + bNodeTreeInterfaceItem *item) +{ + /* Copy to same parent as the item. */ + bNodeTreeInterfacePanel *parent = interface->find_item_parent(*item); + if (parent == nullptr) { + return nullptr; + } + return rna_NodeTreeInterfaceItems_copy_to_parent(id, interface, bmain, reports, item, parent); +} + +static void rna_NodeTreeInterfaceItems_remove(ID *id, + bNodeTreeInterface *interface, + Main *bmain, + bNodeTreeInterfaceItem *item, + bool move_content_to_parent) +{ + interface->remove_item(*item, move_content_to_parent); + + bNodeTree *ntree = reinterpret_cast(id); + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(nullptr, bmain, ntree); + WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); +} + +static void rna_NodeTreeInterfaceItems_clear(ID *id, bNodeTreeInterface *interface, Main *bmain) +{ + interface->clear_items(); + + bNodeTree *ntree = reinterpret_cast(id); + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(nullptr, bmain, ntree); + WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); +} + +static void rna_NodeTreeInterfaceItems_move( + ID *id, bNodeTreeInterface *interface, Main *bmain, bNodeTreeInterfaceItem *item, int to_index) +{ + interface->move_item(*item, to_index); + + bNodeTree *ntree = reinterpret_cast(id); + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(nullptr, bmain, ntree); + WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); +} + +static void rna_NodeTreeInterfaceItems_move_to_parent(ID *id, + bNodeTreeInterface *interface, + Main *bmain, + bNodeTreeInterfaceItem *item, + bNodeTreeInterfacePanel *parent, + int to_index) +{ + interface->move_item_to_parent(*item, parent, to_index); + + bNodeTree *ntree = reinterpret_cast(id); + BKE_ntree_update_tag_interface(ntree); + ED_node_tree_propagate_change(nullptr, bmain, ntree); + WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); +} + +/* ******** Node Socket Subtypes ******** */ + +void rna_NodeTreeInterfaceSocketFloat_default_value_range( + PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax) +{ + bNodeTreeInterfaceSocket *socket = static_cast(ptr->data); + bNodeSocketValueFloat *dval = static_cast(socket->socket_data); + bNodeSocketType *socket_typeinfo = nodeSocketTypeFind(socket->socket_type); + int subtype = socket_typeinfo ? socket_typeinfo->subtype : PROP_NONE; + + if (dval->max < dval->min) { + dval->max = dval->min; + } + + *min = (subtype == PROP_UNSIGNED ? 0.0f : -FLT_MAX); + *max = FLT_MAX; + *softmin = dval->min; + *softmax = dval->max; +} + +void rna_NodeTreeInterfaceSocketInt_default_value_range( + PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) +{ + bNodeTreeInterfaceSocket *socket = static_cast(ptr->data); + bNodeSocketValueInt *dval = static_cast(socket->socket_data); + bNodeSocketType *socket_typeinfo = nodeSocketTypeFind(socket->socket_type); + int subtype = socket_typeinfo ? socket_typeinfo->subtype : PROP_NONE; + + if (dval->max < dval->min) { + dval->max = dval->min; + } + + *min = (subtype == PROP_UNSIGNED ? 0 : INT_MIN); + *max = INT_MAX; + *softmin = dval->min; + *softmax = dval->max; +} + +void rna_NodeTreeInterfaceSocketVector_default_value_range( + PointerRNA *ptr, float *min, float *max, float *softmin, float *softmax) +{ + bNodeTreeInterfaceSocket *socket = static_cast(ptr->data); + bNodeSocketValueVector *dval = static_cast(socket->socket_data); + + if (dval->max < dval->min) { + dval->max = dval->min; + } + + *min = -FLT_MAX; + *max = FLT_MAX; + *softmin = dval->min; + *softmax = dval->max; +} + +/* using a context update function here, to avoid searching the node if possible */ +static void rna_NodeTreeInterfaceSocket_value_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + /* default update */ + rna_NodeTreeInterfaceItem_update(bmain, scene, ptr); +} + +static bool rna_NodeTreeInterfaceSocketMaterial_default_value_poll(PointerRNA * /*ptr*/, + PointerRNA value) +{ + /* Do not show grease pencil materials for now. */ + Material *ma = static_cast(value.data); + return ma->gp_style == nullptr; +} + +static void rna_NodeTreeInterface_items_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + if (!ntree->runtime) { + return; + } + + ntree->ensure_topology_cache(); + // Note: New API, enabled when interface cache is added. + UNUSED_VARS(iter); + // const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache(); + // rna_iterator_array_begin(iter, + // const_cast(cache.items.data()), + // sizeof(bNodeTreeInterfaceItem *), + // cache.items.size(), + // false, + // nullptr); +} + +static int rna_NodeTreeInterface_items_length(PointerRNA *ptr) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + if (!ntree->runtime) { + return 0; + } + + ntree->ensure_topology_cache(); + // Note: New API callbacks. + // return ntree->interface_cache().items.size(); + return 0; +} + +static int rna_NodeTreeInterface_items_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + if (!ntree->runtime) { + return 0; + } + + ntree->ensure_topology_cache(); + // Note: New API, enabled when interface cache is added. + UNUSED_VARS(index, r_ptr); + // const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache(); + // if (!cache.items.index_range().contains(index)) { + // return false; + // } + + // RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfaceItem, cache.items[index], r_ptr); + return true; +} + +static int rna_NodeTreeInterface_items_lookup_string(struct PointerRNA *ptr, + const char *key, + struct PointerRNA *r_ptr) +{ + bNodeTree *ntree = reinterpret_cast(ptr->owner_id); + if (!ntree->runtime) { + return 0; + } + + ntree->ensure_topology_cache(); + // Note: New API, enabled when interface cache is added. + UNUSED_VARS(key, r_ptr); + // const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache(); + // for (bNodeTreeInterfaceItem *item : cache.items) { + // switch (item->item_type) { + // case NODE_INTERFACE_SOCKET: { + // bNodeTreeInterfaceSocket *socket = reinterpret_cast(item); + // if (STREQ(socket->name, key)) { + // RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfaceSocket, socket, r_ptr); + // return true; + // } + // break; + // } + // case NODE_INTERFACE_PANEL: { + // bNodeTreeInterfacePanel *panel = reinterpret_cast(item); + // if (STREQ(panel->name, key)) { + // RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfacePanel, panel, r_ptr); + // return true; + // } + // break; + // } + // } + // } + return false; +} + +#else + +static void rna_def_node_interface_item(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "NodeTreeInterfaceItem", nullptr); + RNA_def_struct_ui_text(srna, "Node Tree Interface Item", "Item in a node tree interface"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceItem"); + RNA_def_struct_refine_func(srna, "rna_NodeTreeInterfaceItem_refine"); + RNA_def_struct_path_func(srna, "rna_NodeTreeInterfaceItem_path"); + + prop = RNA_def_property(srna, "item_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "item_type"); + RNA_def_property_enum_items(prop, rna_enum_node_tree_interface_item_type_items); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Item Type", "Type of interface item"); +} + +static void rna_def_node_interface_socket(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + FunctionRNA *func; + PropertyRNA *parm; + + srna = RNA_def_struct(brna, "NodeTreeInterfaceSocket", "NodeTreeInterfaceItem"); + RNA_def_struct_ui_text(srna, "Node Tree Interface Socket", "Declaration of a node socket"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfaceSocket"); + RNA_def_struct_register_funcs(srna, + "rna_NodeTreeInterfaceSocket_register", + "rna_NodeTreeInterfaceSocket_unregister", + nullptr); + RNA_def_struct_idprops_func(srna, "rna_NodeTreeInterfaceSocket_idprops"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Socket name"); + RNA_def_struct_name_property(srna, prop); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); + + prop = RNA_def_property(srna, "identifier", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, + "rna_NodeTreeInterfaceSocket_identifier_get", + "rna_NodeTreeInterfaceSocket_identifier_length", + nullptr); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Identifier", "Unique identifier for mapping sockets"); + + prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, nullptr, "description"); + RNA_def_property_ui_text(prop, "Description", "Socket description"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); + + prop = RNA_def_property(srna, "socket_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, DummyRNA_DEFAULT_items); + RNA_def_property_enum_funcs(prop, + "rna_NodeTreeInterfaceSocket_socket_type_get", + "rna_NodeTreeInterfaceSocket_socket_type_set", + "rna_NodeTreeInterfaceSocket_socket_type_itemf"); + RNA_def_property_ui_text( + prop, "Socket Type", "Type of the socket generated by this interface item"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); + + prop = RNA_def_property(srna, "is_input", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_INTERFACE_SOCKET_INPUT); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Is Input", "Whether the socket is an input"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); + + prop = RNA_def_property(srna, "is_output", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_INTERFACE_SOCKET_OUTPUT); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Is Output", "Whether the socket is an output"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); + + prop = RNA_def_property(srna, "hide_value", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_INTERFACE_SOCKET_HIDE_VALUE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text( + prop, "Hide Value", "Hide the socket input value even when the socket is not connected"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); + + prop = RNA_def_property(srna, "hide_in_modifier", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, + "Hide in Modifier", + "Don't show the input value in the geometry nodes modifier interface"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_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 used by the geometry nodes modifier to create an attribute output"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); + + prop = RNA_def_property(srna, "default_attribute_name", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, nullptr, "default_attribute_name"); + RNA_def_property_ui_text(prop, + "Default Attribute", + "The attribute name used by default when the node group is used by a " + "geometry nodes modifier"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); + + /* Registered properties and functions for custom socket types. */ + prop = RNA_def_property(srna, "bl_socket_idname", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, nullptr, "socket_type"); + RNA_def_property_flag(prop, PROP_REGISTER); + RNA_def_property_ui_text(prop, "Socket Type Name", "Name of the socket type"); + + func = RNA_def_function(srna, "draw", nullptr); + RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_USE_SELF_ID); + RNA_def_function_ui_description(func, "Draw properties of the socket interface"); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_property(func, "layout", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(parm, "UILayout"); + RNA_def_property_ui_text(parm, "Layout", "Layout in the UI"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + + func = RNA_def_function(srna, "init_socket", nullptr); + RNA_def_function_ui_description(func, "Initialize a node socket instance"); + RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_USE_SELF_ID | FUNC_ALLOW_WRITE); + parm = RNA_def_pointer(func, "node", "Node", "Node", "Node of the socket to initialize"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "socket", "NodeSocket", "Socket", "Socket to initialize"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_string( + func, "data_path", nullptr, 0, "Data Path", "Path to specialized socket data"); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + + func = RNA_def_function(srna, "from_socket", nullptr); + RNA_def_function_ui_description(func, "Setup template parameters from an existing socket"); + RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_USE_SELF_ID | FUNC_ALLOW_WRITE); + parm = RNA_def_pointer(func, "node", "Node", "Node", "Node of the original socket"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer(func, "socket", "NodeSocket", "Socket", "Original socket"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); +} + +static void rna_def_node_interface_panel(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "NodeTreeInterfacePanel", "NodeTreeInterfaceItem"); + RNA_def_struct_ui_text(srna, "Node Tree Interface Item", "Declaration of a node panel"); + RNA_def_struct_sdna(srna, "bNodeTreeInterfacePanel"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Panel name"); + RNA_def_struct_name_property(srna, prop); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); + + prop = RNA_def_property(srna, "interface_items", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, nullptr, "items_array", "items_num"); + RNA_def_property_struct_type(prop, "NodeTreeInterfaceItem"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Items", "Items in the node panel"); +} + +static void rna_def_node_tree_interface_items_api(StructRNA *srna) +{ + PropertyRNA *prop; + PropertyRNA *parm; + FunctionRNA *func; + + prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, nullptr, "active_index"); + RNA_def_property_ui_text(prop, "Active Index", "Index of the active item"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, NC_NODE, nullptr); + + prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "NodeTreeInterfaceItem"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_pointer_funcs(prop, + "rna_NodeTreeInterfaceItems_active_get", + "rna_NodeTreeInterfaceItems_active_set", + nullptr, + nullptr); + RNA_def_property_ui_text(prop, "Active", "Active item"); + RNA_def_property_update(prop, NC_NODE, nullptr); + + func = RNA_def_function(srna, "new_socket", "rna_NodeTreeInterfaceItems_new_socket"); + RNA_def_function_ui_description(func, "Add a new socket to the interface"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS); + parm = RNA_def_string(func, "name", nullptr, 0, "Name", "Name of the socket"); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + RNA_def_string(func, "description", nullptr, 0, "Description", "Description of the socket"); + RNA_def_boolean(func, "is_input", false, "Is Input", "Create an input socket"); + RNA_def_boolean(func, "is_output", false, "Is Output", "Create an output socket"); + parm = RNA_def_enum(func, + "socket_type", + DummyRNA_DEFAULT_items, + 0, + "Socket Type", + "Type of socket generated on nodes"); + /* Note: itemf callback works for the function parameter, it does not require a data pointer. */ + RNA_def_property_enum_funcs( + parm, nullptr, nullptr, "rna_NodeTreeInterfaceSocket_socket_type_itemf"); + RNA_def_pointer( + func, "parent", "NodeTreeInterfacePanel", "Parent", "Panel to add the socket in"); + /* return value */ + parm = RNA_def_pointer(func, "item", "NodeTreeInterfaceSocket", "Socket", "New socket"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "new_panel", "rna_NodeTreeInterfaceItems_new_panel"); + RNA_def_function_ui_description(func, "Add a new panel to the interface"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS); + parm = RNA_def_string(func, "name", nullptr, 0, "Name", "Name of the new panel"); + RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); + RNA_def_pointer(func, + "parent", + "NodeTreeInterfacePanel", + "Parent", + "Add panel as a child of the parent panel"); + /* return value */ + parm = RNA_def_pointer(func, "item", "NodeTreeInterfacePanel", "Panel", "New panel"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "copy", "rna_NodeTreeInterfaceItems_copy"); + RNA_def_function_ui_description(func, "Add a copy of an item to the interface"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "item", "NodeTreeInterfaceItem", "Item", "Item to copy"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + /* return value */ + parm = RNA_def_pointer( + func, "item_copy", "NodeTreeInterfaceItem", "Item Copy", "Copy of the item"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_NodeTreeInterfaceItems_remove"); + RNA_def_function_ui_description(func, "Remove an item from the interface"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN); + parm = RNA_def_pointer(func, "item", "NodeTreeInterfaceItem", "Item", "The item to remove"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + RNA_def_boolean( + func, + "move_content_to_parent", + true, + "Move Content", + "If the item is a panel, move the contents to the parent instead of deleting it"); + + func = RNA_def_function(srna, "clear", "rna_NodeTreeInterfaceItems_clear"); + RNA_def_function_ui_description(func, "Remove all items from the interface"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN); + + func = RNA_def_function(srna, "move", "rna_NodeTreeInterfaceItems_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_pointer(func, "item", "NodeTreeInterfaceItem", "Item", "The item to remove"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, 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); + + func = RNA_def_function(srna, "move_to_parent", "rna_NodeTreeInterfaceItems_move_to_parent"); + RNA_def_function_ui_description(func, "Move an item to a new panel and/or position."); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN); + parm = RNA_def_pointer(func, "item", "NodeTreeInterfaceItem", "Item", "The item to remove"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "parent", "NodeTreeInterfacePanel", "Parent", "New parent of the item"); + 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_tree_interface(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "NodeTreeInterface", nullptr); + RNA_def_struct_ui_text( + srna, "Node Tree Interface", "Declaration of sockets and ui panels of a node group"); + RNA_def_struct_sdna(srna, "bNodeTreeInterface"); + + prop = RNA_def_property(srna, "ui_items", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_funcs(prop, + "rna_NodeTreeInterface_items_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_dereference_get", + "rna_NodeTreeInterface_items_length", + "rna_NodeTreeInterface_items_lookup_int", + "rna_NodeTreeInterface_items_lookup_string", + nullptr); + RNA_def_property_struct_type(prop, "NodeTreeInterfaceItem"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Items", "Items in the node interface"); + + rna_def_node_tree_interface_items_api(srna); +} + +void RNA_def_node_tree_interface(BlenderRNA *brna) +{ + rna_def_node_interface_item(brna); + rna_def_node_interface_socket(brna); + rna_def_node_interface_panel(brna); + rna_def_node_tree_interface(brna); + + rna_def_node_socket_interface_subtypes(brna); +} + +#endif diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 8ba666af8b2..1af4d4d7958 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -604,7 +604,6 @@ static const EnumPropertyItem node_cryptomatte_layer_name_items[] = { # include "DNA_scene_types.h" # include "WM_api.hh" -extern "C" { extern FunctionRNA rna_NodeTree_poll_func; extern FunctionRNA rna_NodeTree_update_func; extern FunctionRNA rna_NodeTree_get_from_context_func; @@ -619,7 +618,6 @@ extern FunctionRNA rna_Node_free_func; extern FunctionRNA rna_Node_draw_buttons_func; extern FunctionRNA rna_Node_draw_buttons_ext_func; extern FunctionRNA rna_Node_draw_label_func; -} void rna_Node_socket_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr); @@ -689,6 +687,20 @@ const EnumPropertyItem *rna_node_tree_type_itemf(void *data, return item; } +int rna_node_socket_idname_to_enum(const char *idname) +{ + int i = 0, result = -1; + NODE_SOCKET_TYPES_BEGIN (stype) { + if (STREQ(stype->idname, idname)) { + result = i; + break; + } + i++; + } + NODE_SOCKET_TYPES_END; + return result; +} + bNodeSocketType *rna_node_socket_type_from_enum(int value) { int i = 0; -- 2.30.2 From a876ee9a6f6c89e170a00e07b67ef29c20086d83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Mon, 21 Aug 2023 11:02:05 +0200 Subject: [PATCH 08/14] Part 3/3 of #109135, #110272 Switch to new node group interfaces and deprecate old DNA and API. This completes support for panels in node drawing and in node group interface declarations in particular. The new node group interface DNA and RNA code has been added in parts 1 and 2 (#110885, #110952) but has not be enabled yet. This commit completes the integration by * enabling the new RNA API * using the new API in UI * read/write new interfaces from blend files * add versioning for backward compatibility * add forward-compatible writing code to reconstruct old interfaces All places accessing node group interface declarations should now be using the new API. A runtime cache has been added that allows simple linear access to socket inputs and outputs even when a panel hierarchy is used. Old DNA has been deprecated and should only be accessed for versioning (inputs/outputs renamed to inputs_legacy/outputs_legacy to catch errors). Versioning code ensures both backward and forward compatibility of existing files. The API for old interfaces is removed. The new API is very similar but is defined on the `ntree.interface` instead of the `ntree` directly. Breaking change notifications and detailed instructions for migrating will be added. A python test has been added for the node group API functions. This includes new functionality such as creating panels and moving items between different levels. --- .../startup/bl_operators/geometry_nodes.py | 4 +- scripts/startup/bl_operators/node.py | 126 ++ scripts/startup/bl_ui/space_node.py | 150 +- scripts/templates_py/custom_nodes.py | 20 +- source/blender/blenkernel/BKE_node.h | 39 +- source/blender/blenkernel/BKE_node.hh | 32 +- source/blender/blenkernel/BKE_node_runtime.hh | 66 +- .../blenkernel/BKE_node_tree_interface.hh | 5 +- source/blender/blenkernel/intern/node.cc | 449 +++-- .../blender/blenkernel/intern/node_runtime.cc | 17 +- .../intern/node_tree_anonymous_attributes.cc | 32 +- .../intern/node_tree_field_inferencing.cc | 13 +- .../blenkernel/intern/node_tree_zones.cc | 1 + .../blenloader/intern/versioning_250.cc | 6 +- .../blenloader/intern/versioning_260.cc | 24 +- .../blenloader/intern/versioning_300.cc | 11 +- .../blenloader/intern/versioning_400.cc | 36 + .../realtime_compositor/intern/utilities.cc | 2 +- .../intern/builder/deg_builder_nodes.cc | 11 +- .../intern/builder/deg_builder_relations.cc | 8 +- .../editors/curves/intern/curves_add.cc | 7 +- .../editors/geometry/node_group_operator.cc | 28 +- .../editors/include/UI_abstract_view.hh | 4 +- .../blender/editors/include/UI_interface_c.hh | 2 + .../blender/editors/include/UI_tree_view.hh | 2 +- .../blender/editors/interface/CMakeLists.txt | 1 + ...rface_template_grease_pencil_layer_tree.cc | 4 +- ...nterface_template_node_tree_declaration.cc | 519 ++++++ .../interface/views/abstract_view_item.cc | 10 +- .../editors/interface/views/tree_view.cc | 2 +- .../editors/object/object_relations.cc | 43 +- .../space_file/asset_catalog_tree_view.cc | 6 +- source/blender/editors/space_node/drawnode.cc | 135 +- .../editors/space_node/link_drag_search.cc | 83 +- .../blender/editors/space_node/node_draw.cc | 605 +++++-- .../blender/editors/space_node/node_edit.cc | 458 ----- .../blender/editors/space_node/node_group.cc | 60 +- .../blender/editors/space_node/node_intern.hh | 6 - source/blender/editors/space_node/node_ops.cc | 6 - .../editors/space_node/node_relationships.cc | 4 +- .../editors/space_node/node_shader_preview.cc | 3 +- .../editors/space_node/node_templates.cc | 24 +- source/blender/makesdna/DNA_node_types.h | 37 +- .../blender/makesdna/intern/dna_rename_defs.h | 2 + .../makesrna/intern/rna_node_socket.cc | 1499 ++++------------- .../intern/rna_node_tree_interface.cc | 128 +- .../blender/makesrna/intern/rna_nodetree.cc | 277 +-- source/blender/makesrna/intern/rna_ui_api.cc | 9 + source/blender/modifiers/intern/MOD_nodes.cc | 68 +- .../nodes/NOD_geometry_nodes_execute.hh | 9 +- source/blender/nodes/NOD_node_declaration.hh | 70 +- source/blender/nodes/NOD_socket.hh | 2 + .../blender/nodes/NOD_socket_declarations.hh | 1 + .../blender/nodes/NOD_socket_search_link.hh | 2 +- .../geometry/nodes/node_geo_repeat_output.cc | 18 +- .../nodes/node_geo_simulation_input.cc | 3 +- .../nodes/node_geo_simulation_output.cc | 16 +- .../nodes/intern/geometry_nodes_execute.cc | 150 +- .../intern/geometry_nodes_lazy_function.cc | 42 +- source/blender/nodes/intern/node_common.cc | 222 ++- .../blender/nodes/intern/node_declaration.cc | 81 +- source/blender/nodes/intern/node_socket.cc | 546 +++--- .../nodes/intern/node_socket_declarations.cc | 3 + .../nodes/intern/socket_search_link.cc | 2 +- source/blender/windowmanager/WM_types.hh | 5 + tests/python/CMakeLists.txt | 9 + tests/python/bl_node_group_interface.py | 492 ++++++ 67 files changed, 3527 insertions(+), 3230 deletions(-) create mode 100644 source/blender/editors/interface/interface_template_node_tree_declaration.cc create mode 100644 tests/python/bl_node_group_interface.py diff --git a/scripts/startup/bl_operators/geometry_nodes.py b/scripts/startup/bl_operators/geometry_nodes.py index 776604cf397..06dc14c04f6 100644 --- a/scripts/startup/bl_operators/geometry_nodes.py +++ b/scripts/startup/bl_operators/geometry_nodes.py @@ -14,8 +14,8 @@ from bpy.props import ( def build_default_empty_geometry_node_group(name): group = bpy.data.node_groups.new(name, 'GeometryNodeTree') - group.inputs.new('NodeSocketGeometry', data_("Geometry")) - group.outputs.new('NodeSocketGeometry', data_("Geometry")) + group.interface.new_socket(data_("Geometry"), is_input=True, socket_type='NodeSocketGeometry') + group.interface.new_socket(data_("Geometry"), is_output=True, socket_type='NodeSocketGeometry') input_node = group.nodes.new('NodeGroupInput') output_node = group.nodes.new('NodeGroupOutput') output_node.is_active_output = True diff --git a/scripts/startup/bl_operators/node.py b/scripts/startup/bl_operators/node.py index 0838778b07e..8abb865b515 100644 --- a/scripts/startup/bl_operators/node.py +++ b/scripts/startup/bl_operators/node.py @@ -260,6 +260,128 @@ class NODE_OT_tree_path_parent(Operator): return {'FINISHED'} +class NodeInterfaceOperator(): + @classmethod + def poll(cls, context): + space = context.space_data + if not space or space.type != 'NODE_EDITOR' or not space.edit_tree: + return False + if space.edit_tree.is_embedded_data: + return False + return True + + +class NODE_OT_interface_item_new(NodeInterfaceOperator, Operator): + '''Add a new item to the interface''' + bl_idname = "node.interface_item_new" + bl_label = "New Item" + bl_options = {'REGISTER', 'UNDO'} + + item_type: EnumProperty( + name="Item Type", + description="Type of the item to create", + items=[('SOCKET', "Socket", ""), ('PANEL', "Panel", "")], + default='SOCKET', + ) + + socket_type = 'NodeSocketFloat' + + def execute(self, context): + snode = context.space_data + tree = snode.edit_tree + interface = tree.interface + + # Remember index to move the item. + dst_index = interface.active_index + 1 + if self.item_type == 'SOCKET': + item = interface.new_socket("Socket", socket_type=self.socket_type) + elif self.item_type == 'PANEL': + item = interface.new_panel("Panel") + else: + return {'CANCELLED'} + + interface.move(item, dst_index) + interface.active = item + + return {'FINISHED'} + + +class NODE_OT_interface_item_copy(NodeInterfaceOperator, Operator): + '''Add a copy of the active item to the interface''' + bl_idname = "node.interface_item_copy" + bl_label = "Copy Item" + bl_options = {'REGISTER', 'UNDO'} + + @classmethod + def poll(cls, context): + if not super().poll(context): + return False + + snode = context.space_data + tree = snode.edit_tree + interface = tree.interface + return interface.active is not None + + def execute(self, context): + snode = context.space_data + tree = snode.edit_tree + interface = tree.interface + item = interface.active + + if item: + interface.copy(item) + interface.active = item + + return {'FINISHED'} + + +class NODE_OT_interface_item_remove(NodeInterfaceOperator, Operator): + '''Remove active item from the interface''' + bl_idname = "node.interface_item_remove" + bl_label = "Remove Item" + bl_options = {'REGISTER', 'UNDO'} + + def execute(self, context): + snode = context.space_data + tree = snode.edit_tree + interface = tree.interface + item = interface.active + + if item: + interface.remove(item) + interface.active_index -= 1 + + return {'FINISHED'} + + +class NODE_OT_interface_item_move(NodeInterfaceOperator, Operator): + '''Move an item up or down in the interface''' + bl_idname = "node.interface_item_move" + bl_label = "Move Item" + bl_options = {'REGISTER', 'UNDO'} + + direction: EnumProperty( + name="Direction", + items=[('UP', "Up", ""), ('DOWN', "Down", "")], + default='UP', + ) + + def execute(self, context): + snode = context.space_data + tree = snode.edit_tree + interface = tree.interface + item = interface.active + + if self.direction == 'UP': + interface.move(item, interface.active_index - 1) + interface.active_index -= 1 + elif self.direction == 'DOWN': + interface.move(item, interface.active_index + 1) + interface.active_index += 1 + + return {'FINISHED'} + + classes = ( NodeSetting, @@ -267,5 +389,9 @@ classes = ( NODE_OT_add_simulation_zone, NODE_OT_add_repeat_zone, NODE_OT_collapse_hide_unused_toggle, + NODE_OT_interface_item_new, + NODE_OT_interface_item_copy, + NODE_OT_interface_item_remove, + NODE_OT_interface_item_move, NODE_OT_tree_path_parent, ) diff --git a/scripts/startup/bl_ui/space_node.py b/scripts/startup/bl_ui/space_node.py index 1d6c57c5039..2544de487a6 100644 --- a/scripts/startup/bl_ui/space_node.py +++ b/scripts/startup/bl_ui/space_node.py @@ -860,22 +860,11 @@ class NODE_PT_overlay(Panel): col.prop(overlay, "show_named_attributes", text="Named Attributes") -class NODE_UL_interface_sockets(bpy.types.UIList): - def draw_item(self, context, layout, _data, item, icon, _active_data, _active_propname, _index): - socket = item - color = socket.draw_color(context) - - if self.layout_type in {'DEFAULT', 'COMPACT'}: - row = layout.row(align=True) - - row.template_node_socket(color=color) - row.prop(socket, "name", text="", emboss=False, icon_value=icon) - elif self.layout_type == 'GRID': - layout.alignment = 'CENTER' - layout.template_node_socket(color=color) - - -class NodeTreeInterfacePanel(Panel): +class NODE_PT_node_tree_declaration(Panel): + bl_space_type = 'NODE_EDITOR' + bl_region_type = 'UI' + bl_category = "Group" + bl_label = "Sockets" @classmethod def poll(cls, context): @@ -889,119 +878,56 @@ class NodeTreeInterfacePanel(Panel): return False return True - def draw_socket_list(self, context, in_out, sockets_propname, active_socket_propname): + def draw(self, context): layout = self.layout - snode = context.space_data tree = snode.edit_tree - sockets = getattr(tree, sockets_propname) - active_socket_index = getattr(tree, active_socket_propname) - active_socket = sockets[active_socket_index] if active_socket_index >= 0 else None split = layout.row() - split.template_list("NODE_UL_interface_sockets", in_out, tree, sockets_propname, tree, active_socket_propname) + split.template_node_tree_declaration(tree.interface) ops_col = split.column() add_remove_col = ops_col.column(align=True) - props = add_remove_col.operator("node.tree_socket_add", icon='ADD', text="") - props.in_out = in_out - props = add_remove_col.operator("node.tree_socket_remove", icon='REMOVE', text="") - props.in_out = in_out + add_remove_col.operator_menu_enum("node.interface_item_new", "item_type", icon='ADD', text="") + add_remove_col.operator("node.interface_item_copy", icon='DUPLICATE', text="") + add_remove_col.operator("node.interface_item_remove", icon='REMOVE', text="") ops_col.separator() up_down_col = ops_col.column(align=True) - props = up_down_col.operator("node.tree_socket_move", icon='TRIA_UP', text="") - props.in_out = in_out + props = up_down_col.operator("node.interface_item_move", icon='TRIA_UP', text="") props.direction = 'UP' - props = up_down_col.operator("node.tree_socket_move", icon='TRIA_DOWN', text="") - props.in_out = in_out + props = up_down_col.operator("node.interface_item_move", icon='TRIA_DOWN', text="") props.direction = 'DOWN' - if active_socket is not None: - # Mimicking property split. - layout.use_property_split = False - layout.use_property_decorate = False - layout_row = layout.row(align=True) - layout_split = layout_row.split(factor=0.4, align=True) + active_item = tree.interface.active + if active_item is not None: + if active_item.item_type == 'SOCKET': + layout.prop(active_item, "name") + layout.prop(active_item, "description") + layout.prop(active_item, "is_input", toggle=True) + layout.prop(active_item, "is_output", toggle=True) + layout.prop(active_item, "socket_type") - label_column = layout_split.column(align=True) - label_column.alignment = 'RIGHT' - # Menu to change the socket type. - label_column.label(text="Type") + active_item.draw(context, layout) - property_row = layout_split.row(align=True) - props = property_row.operator_menu_enum( - "node.tree_socket_change_type", - "socket_type", - text=(iface_(active_socket.bl_label) if active_socket.bl_label - else iface_(active_socket.bl_idname)), - ) - props.in_out = in_out - - with context.temp_override(interface_socket=active_socket): - if bpy.ops.node.tree_socket_change_subtype.poll(): - layout_row = layout.row(align=True) - layout_split = layout_row.split(factor=0.4, align=True) - - label_column = layout_split.column(align=True) - label_column.alignment = 'RIGHT' - label_column.label(text="Subtype") - property_row = layout_split.row(align=True) - - property_row.context_pointer_set("interface_socket", active_socket) - props = property_row.operator_menu_enum( - "node.tree_socket_change_subtype", - "socket_subtype", - text=(iface_(active_socket.bl_subtype_label) if active_socket.bl_subtype_label - else iface_(active_socket.bl_idname)), - ) - - layout.use_property_split = True - layout.use_property_decorate = False - - layout.prop(active_socket, "name") - # Display descriptions only for Geometry Nodes, since it's only used in the modifier panel. - if tree.type == 'GEOMETRY': - layout.prop(active_socket, "description") - field_socket_prefixes = { - "NodeSocketInt", - "NodeSocketColor", - "NodeSocketVector", - "NodeSocketBool", - "NodeSocketFloat", - } - is_field_type = any( - active_socket.bl_socket_idname.startswith(prefix) - for prefix in field_socket_prefixes - ) - if is_field_type: - if in_out == 'OUT': - layout.prop(active_socket, "attribute_domain") - layout.prop(active_socket, "default_attribute_name") - active_socket.draw(context, layout) - - -class NODE_PT_node_tree_interface_inputs(NodeTreeInterfacePanel): - bl_space_type = 'NODE_EDITOR' - bl_region_type = 'UI' - bl_category = "Group" - bl_label = "Inputs" - - def draw(self, context): - self.draw_socket_list(context, "IN", "inputs", "active_input") - - -class NODE_PT_node_tree_interface_outputs(NodeTreeInterfacePanel): - bl_space_type = 'NODE_EDITOR' - bl_region_type = 'UI' - bl_category = "Group" - bl_label = "Outputs" - - def draw(self, context): - self.draw_socket_list(context, "OUT", "outputs", "active_output") + # Display descriptions only for Geometry Nodes, since it's only used in the modifier panel. + if tree.type == 'GEOMETRY': + field_socket_types = { + "NodeSocketInt", + "NodeSocketColor", + "NodeSocketVector", + "NodeSocketBool", + "NodeSocketFloat", + } + if active_item.socket_type in field_socket_types: + if active_item.is_output: + layout.prop(active_item, "attribute_domain") + layout.prop(active_item, "default_attribute_name") + if active_item.item_type == 'PANEL': + layout.prop(active_item, "name") class NODE_UL_simulation_zone_items(bpy.types.UIList): @@ -1210,6 +1136,7 @@ classes = ( NODE_PT_material_slots, NODE_PT_geometry_node_asset_traits, NODE_PT_node_color_presets, + NODE_PT_node_tree_declaration, NODE_PT_active_node_generic, NODE_PT_active_node_color, NODE_PT_texture_mapping, @@ -1218,9 +1145,6 @@ classes = ( NODE_PT_quality, NODE_PT_annotation, NODE_PT_overlay, - NODE_UL_interface_sockets, - NODE_PT_node_tree_interface_inputs, - NODE_PT_node_tree_interface_outputs, NODE_UL_simulation_zone_items, NODE_PT_simulation_zone_items, NODE_UL_repeat_zone_items, diff --git a/scripts/templates_py/custom_nodes.py b/scripts/templates_py/custom_nodes.py index d337bebb1f9..e1776da0767 100644 --- a/scripts/templates_py/custom_nodes.py +++ b/scripts/templates_py/custom_nodes.py @@ -1,5 +1,5 @@ import bpy -from bpy.types import NodeTree, Node, NodeSocket +from bpy.types import NodeTree, Node, NodeSocket, NodeTreeInterfaceSocket # Implementation of custom nodes from Python @@ -52,6 +52,23 @@ class MyCustomSocket(NodeSocket): return (1.0, 0.4, 0.216, 0.5) +# Customizable interface properties to generate a socket from. +class MyCustomInterfaceSocket(NodeTreeInterfaceSocket): + # The type of socket that is generated. + bl_socket_idname = 'CustomSocketType' + + mean_value: bpy.props.FloatProperty(default=10.0) + randomize: bpy.props.BoolProperty(default=False) + + def draw(self, context, layout): + layout.label(text="Here we can display properties of the socket") + layout.prop(self, "mean_value") + layout.prop(self, "randomize") + + def init_socket(self, node, socket, data_path): + print("I am doing the socket thing") + + # Mix-in class for all custom nodes in this tree type. # Defines a poll function to enable instantiation. class MyCustomTreeNode: @@ -163,6 +180,7 @@ node_categories = [ classes = ( MyCustomTree, MyCustomSocket, + MyCustomInterfaceSocket, MyCustomNode, ) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index d3789a43ca7..a689c4da0ca 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -116,9 +116,8 @@ using NodeDeclareFunction = void (*)(blender::nodes::NodeDeclarationBuilder &bui using NodeDeclareDynamicFunction = void (*)(const bNodeTree &tree, const bNode &node, blender::nodes::NodeDeclaration &r_declaration); -using SocketGetCPPValueFunction = void (*)(const struct bNodeSocket &socket, void *r_value); -using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket &socket, - void *r_value); +using SocketGetCPPValueFunction = void (*)(const void *socket_value, void *r_value); +using SocketGetGeometryNodesCPPValueFunction = void (*)(const void *socket_value, void *r_value); /* Adds socket link operations that are specific to this node type. */ using NodeGatherSocketLinkOperationsFunction = @@ -154,6 +153,7 @@ typedef struct CPPTypeHandle CPPTypeHandle; * * Defines the appearance and behavior of a socket in the UI. */ +/* XXX replace interface parts when old tree interfaces are fully deprecated */ typedef struct bNodeSocketType { /** Identifier name. */ char idname[64]; @@ -172,17 +172,19 @@ typedef struct bNodeSocketType { struct PointerRNA *node_ptr, float *r_color); - void (*interface_draw)(struct bContext *C, struct uiLayout *layout, struct PointerRNA *ptr); - void (*interface_draw_color)(struct bContext *C, struct PointerRNA *ptr, float *r_color); - void (*interface_init_socket)(struct bNodeTree *ntree, - const struct bNodeSocket *interface_socket, + void (*interface_draw)(struct ID *id, + struct bNodeTreeInterfaceSocket *socket, + struct bContext *C, + struct uiLayout *layout); + void (*interface_init_socket)(struct ID *id, + const struct bNodeTreeInterfaceSocket *interface_socket, struct bNode *node, - struct bNodeSocket *sock, + struct bNodeSocket *socket, const char *data_path); - void (*interface_from_socket)(struct bNodeTree *ntree, - struct bNodeSocket *interface_socket, + void (*interface_from_socket)(struct ID *id, + struct bNodeTreeInterfaceSocket *interface_socket, const struct bNode *node, - const struct bNodeSocket *sock); + const struct bNodeSocket *socket); /* RNA integration */ ExtensionRNA ext_socket; @@ -540,19 +542,6 @@ void ntreeBlendWrite(struct BlendWriter *writer, struct bNodeTree *ntree); /** \} */ -/* -------------------------------------------------------------------- */ -/** \name Node Tree Interface - * \{ */ - -void ntreeRemoveSocketInterface(bNodeTree *ntree, bNodeSocket *sock); - -struct bNodeSocket *ntreeAddSocketInterface(struct bNodeTree *ntree, - eNodeSocketInOut in_out, - const char *idname, - const char *name); - -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Generic API, Nodes * \{ */ @@ -586,7 +575,7 @@ struct GHashIterator *nodeSocketTypeGetIterator(void); const char *nodeSocketTypeLabel(const bNodeSocketType *stype); const char *nodeStaticSocketType(int type, int subtype); -const char *nodeStaticSocketInterfaceType(int type, int subtype); +const char *nodeStaticSocketInterfaceTypeNew(int type, int subtype); const char *nodeStaticSocketLabel(int type, int subtype); /* Helper macros for iterating over node types. */ diff --git a/source/blender/blenkernel/BKE_node.hh b/source/blender/blenkernel/BKE_node.hh index ccfce8fb405..2cd99d0693d 100644 --- a/source/blender/blenkernel/BKE_node.hh +++ b/source/blender/blenkernel/BKE_node.hh @@ -11,6 +11,7 @@ #include "BLI_compiler_compat.h" #include "BLI_ghash.h" #include "BLI_math_vector_types.hh" +#include "BLI_span.hh" #include "DNA_listBase.h" @@ -60,37 +61,6 @@ void ntreeBlendReadLib(BlendLibReader *reader, bNodeTree *ntree); void ntreeBlendReadExpand(BlendExpander *expander, bNodeTree *ntree); -/* -------------------------------------------------------------------- */ -/** \name Node Tree Interface - * \{ */ - -bNodeSocket *ntreeFindSocketInterface(bNodeTree *ntree, - eNodeSocketInOut in_out, - const char *identifier); - -bNodeSocket *ntreeInsertSocketInterface(bNodeTree *ntree, - eNodeSocketInOut in_out, - const char *idname, - bNodeSocket *next_sock, - const char *name); - -bNodeSocket *ntreeAddSocketInterfaceFromSocket(bNodeTree *ntree, - const bNode *from_node, - const bNodeSocket *from_sock); - -bNodeSocket *ntreeAddSocketInterfaceFromSocketWithName(bNodeTree *ntree, - const bNode *from_node, - const bNodeSocket *from_sock, - const char *idname, - const char *name); - -bNodeSocket *ntreeInsertSocketInterfaceFromSocket(bNodeTree *ntree, - bNodeSocket *next_sock, - const bNode *from_node, - const bNodeSocket *from_sock); - -/** \} */ - bool node_type_is_undefined(const bNode *node); bool nodeIsStaticSocketType(const bNodeSocketType *stype); diff --git a/source/blender/blenkernel/BKE_node_runtime.hh b/source/blender/blenkernel/BKE_node_runtime.hh index d429999b3af..eee28af7388 100644 --- a/source/blender/blenkernel/BKE_node_runtime.hh +++ b/source/blender/blenkernel/BKE_node_runtime.hh @@ -18,6 +18,7 @@ #include "DNA_node_types.h" #include "BKE_node.hh" +#include "BKE_node_tree_interface.hh" struct bNode; struct bNodeSocket; @@ -170,8 +171,7 @@ class bNodeTreeRuntime : NonCopyable, NonMovable { bool has_undefined_nodes_or_sockets = false; bNode *group_output_node = nullptr; Vector root_frames; - Vector interface_inputs; - Vector interface_outputs; + bNodeTreeInterfaceCache interface_cache; }; /** @@ -216,6 +216,16 @@ class bNodeSocketRuntime : NonCopyable, NonMovable { int index_in_inout_sockets = -1; }; +class bNodePanelRuntime : NonCopyable, NonMovable { + public: + /** + * The location of the panel in the tree, calculated while drawing the nodes and invalid if the + * node tree hasn't been drawn yet. In the node tree's "world space" (the same as + * #bNode::runtime::totr). + */ + float2 location; +}; + /** * Run-time data for every node. This should only contain data that is somewhat persistent (i.e. * data that lives longer than a single depsgraph evaluation + redraw). Data that's only used in @@ -299,6 +309,9 @@ class bNodeRuntime : NonCopyable, NonMovable { /** Can be used to toposort a subset of nodes. */ int toposort_left_to_right_index = -1; int toposort_right_to_left_index = -1; + + /* Panel runtime state */ + Array panels; }; namespace node_tree_runtime { @@ -469,18 +482,6 @@ inline blender::Span bNodeTree::group_input_nodes() const return this->nodes_by_type("NodeGroupInput"); } -inline blender::Span bNodeTree::interface_inputs() const -{ - BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); - return this->runtime->interface_inputs; -} - -inline blender::Span bNodeTree::interface_outputs() const -{ - BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); - return this->runtime->interface_outputs; -} - inline blender::Span bNodeTree::all_input_sockets() const { BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); @@ -545,6 +546,12 @@ inline blender::Span bNodeTree::nested_node_refs_span() const return {this->nested_node_refs, this->nested_node_refs_num}; } +inline const blender::bke::bNodeTreeInterfaceCache &bNodeTree::interface_cache() const +{ + BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this)); + return this->runtime->interface_cache; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -693,6 +700,16 @@ inline const blender::nodes::NodeDeclaration *bNode::declaration() const return this->runtime->declaration; } +inline blender::Span bNode::panel_states() const +{ + return {panel_states_array, num_panel_states}; +} + +inline blender::MutableSpan bNode::panel_states() +{ + return {panel_states_array, num_panel_states}; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -756,6 +773,11 @@ inline bool bNodeSocket::is_available() const return (this->flag & SOCK_UNAVAIL) == 0; } +inline bool bNodeSocket::is_panel_collapsed() const +{ + return (this->flag & SOCK_PANEL_COLLAPSED) != 0; +} + inline bool bNodeSocket::is_visible() const { return !this->is_hidden() && this->is_available(); @@ -852,3 +874,19 @@ inline const bNode &bNodeSocket::owner_node() const } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #bNode Inline Methods + * \{ */ + +inline bool bNodePanelState::is_collapsed() const +{ + return flag & NODE_PANEL_COLLAPSED; +} + +inline bool bNodePanelState::is_parent_collapsed() const +{ + return flag & NODE_PANEL_PARENT_COLLAPSED; +} + +/** \} */ diff --git a/source/blender/blenkernel/BKE_node_tree_interface.hh b/source/blender/blenkernel/BKE_node_tree_interface.hh index 3e3da0b475e..4ef1fc96cf9 100644 --- a/source/blender/blenkernel/BKE_node_tree_interface.hh +++ b/source/blender/blenkernel/BKE_node_tree_interface.hh @@ -177,7 +177,7 @@ template const T &get_socket_data_as(const bNodeTreeInterfaceSocket } inline bNodeTreeInterfaceSocket *add_interface_socket_from_node(bNodeTree &ntree, - const bNode & /*from_node*/, + const bNode &from_node, const bNodeSocket &from_sock, const StringRefNull socket_type, const StringRefNull name) @@ -193,9 +193,8 @@ inline bNodeTreeInterfaceSocket *add_interface_socket_from_node(bNodeTree &ntree } const bNodeSocketType *typeinfo = iosock->socket_typeinfo(); if (typeinfo->interface_from_socket) { - /* XXX Enable when bNodeSocketType callbacks have been updated. */ + typeinfo->interface_from_socket(&ntree.id, iosock, &from_node, &from_sock); UNUSED_VARS(from_sock); - // typeinfo->interface_from_socket(ntree.id, iosock, &from_node, &from_sock); } return iosock; } diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 67ce4138167..9e232689a32 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -68,6 +68,7 @@ #include "BKE_node.hh" #include "BKE_node_runtime.hh" #include "BKE_node_tree_anonymous_attributes.hh" +#include "BKE_node_tree_interface.hh" #include "BKE_node_tree_update.h" #include "BKE_node_tree_zones.hh" #include "BKE_type_conversions.hh" @@ -115,6 +116,9 @@ using blender::nodes::OutputFieldDependency; using blender::nodes::OutputSocketFieldType; using blender::nodes::SocketDeclaration; +/* Forward declaration. */ +static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *sock); + static CLG_LogRef LOG = {"bke.node"}; namespace blender::bke { @@ -129,6 +133,9 @@ bNodeSocketType NodeSocketTypeUndefined; namespace blender::bke { static void ntree_set_typeinfo(bNodeTree *ntree, bNodeTreeType *typeinfo); +static void node_socket_set_typeinfo(bNodeTree *ntree, + bNodeSocket *sock, + bNodeSocketType *typeinfo); static void node_socket_copy(bNodeSocket *sock_dst, const bNodeSocket *sock_src, const int flag); static void free_localized_node_groups(bNodeTree *ntree); static void node_socket_interface_free(bNodeTree * /*ntree*/, @@ -190,20 +197,10 @@ static void ntree_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, cons nodeDeclarationEnsure(ntree_dst, node); } - /* copy interface sockets */ - BLI_listbase_clear(&ntree_dst->inputs); - LISTBASE_FOREACH (const bNodeSocket *, src_socket, &ntree_src->inputs) { - bNodeSocket *dst_socket = static_cast(MEM_dupallocN(src_socket)); - node_socket_copy(dst_socket, src_socket, flag_subdata); - BLI_addtail(&ntree_dst->inputs, dst_socket); - } - BLI_listbase_clear(&ntree_dst->outputs); - LISTBASE_FOREACH (const bNodeSocket *, src_socket, &ntree_src->outputs) { - bNodeSocket *dst_socket = static_cast(MEM_dupallocN(src_socket)); - node_socket_copy(dst_socket, src_socket, flag_subdata); - BLI_addtail(&ntree_dst->outputs, dst_socket); - } - + ntree_dst->tree_interface.copy_data(ntree_src->tree_interface, flag); + /* Legacy inputs/outputs lists may contain unmanaged pointers, don't copy these. */ + BLI_listbase_clear(&ntree_dst->inputs_legacy); + BLI_listbase_clear(&ntree_dst->outputs_legacy); /* copy preview hash */ if (ntree_src->previews && (flag & LIB_ID_COPY_NO_PREVIEW) == 0) { bNodeInstanceHashIterator iter; @@ -295,15 +292,18 @@ static void ntree_free_data(ID *id) node_free_node(ntree, node); } - /* free interface sockets */ - LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &ntree->inputs) { - node_socket_interface_free(ntree, sock, false); - MEM_freeN(sock); + ntree->tree_interface.free_data(); + /* Free legacy interface data */ + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->inputs_legacy) { + node_socket_interface_free(ntree, socket, false); + MEM_freeN(socket); } - LISTBASE_FOREACH_MUTABLE (bNodeSocket *, sock, &ntree->outputs) { - node_socket_interface_free(ntree, sock, false); - MEM_freeN(sock); + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->outputs_legacy) { + node_socket_interface_free(ntree, socket, false); + MEM_freeN(socket); } + BLI_listbase_clear(&ntree->inputs_legacy); + BLI_listbase_clear(&ntree->outputs_legacy); /* free preview hash */ if (ntree->previews) { @@ -399,12 +399,7 @@ static void node_foreach_id(ID *id, LibraryForeachIDData *data) } } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { - BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock)); - } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { - BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock)); - } + ntree->tree_interface.foreach_id(data); } static void node_foreach_cache(ID *id, @@ -471,6 +466,117 @@ static ID **node_owner_pointer_get(ID *id) } // namespace blender::bke +namespace blender::bke::forward_compat { + +static void write_node_socket_interface(BlendWriter *writer, bNodeSocket *sock) +{ + BLO_write_struct(writer, bNodeSocket, sock); + + if (sock->prop) { + IDP_BlendWrite(writer, sock->prop); + } + + BLO_write_string(writer, sock->default_attribute_name); + + write_node_socket_default_value(writer, sock); +} + +/* Construct a bNodeSocket that represents a node group socket the old way. */ +static bNodeSocket *make_socket(bNodeTree *ntree, + const eNodeSocketInOut in_out, + const StringRef idname, + const StringRef name, + const StringRef identifier) +{ + bNodeSocketType *stype = nodeSocketTypeFind(idname.data()); + if (stype == nullptr) { + return nullptr; + } + + bNodeSocket *sock = MEM_cnew(__func__); + sock->runtime = MEM_new(__func__); + STRNCPY(sock->idname, stype->idname); + sock->in_out = int(in_out); + sock->type = int(SOCK_CUSTOM); /* int type undefined by default */ + node_socket_set_typeinfo(ntree, sock, stype); + + sock->limit = (in_out == SOCK_IN ? 1 : 0xFFF); + + BLI_strncpy(sock->identifier, identifier.data(), sizeof(sock->identifier)); + BLI_strncpy(sock->name, name.data(), sizeof(sock->name)); + sock->storage = nullptr; + sock->flag |= SOCK_COLLAPSED; + + return sock; +} + +/** + * Socket interface reconstruction for forward compatibility. + * To enable previous Blender versions to read the new interface DNA data, + * construct the bNodeSocket inputs/outputs lists. + * This discards any information about panels and alternating input/output order, + * but all functional information is preserved for executing node trees. + */ +static void write_interface_as_sockets(BlendWriter *writer, bNodeTree *ntree) +{ + /* Store reference to old sockets before replacing pointers. */ + ListBase old_inputs = ntree->inputs_legacy; + ListBase old_outputs = ntree->outputs_legacy; + BLI_listbase_clear(&ntree->inputs_legacy); + BLI_listbase_clear(&ntree->outputs_legacy); + + /* Construct inputs/outputs socket lists in the node tree. */ + ntree->tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) { + if (const bNodeTreeInterfaceSocket *socket = + node_interface::get_item_as(&item)) + { + if (socket->flag & NODE_INTERFACE_SOCKET_INPUT) { + bNodeSocket *iosock = make_socket( + ntree, SOCK_IN, socket->socket_type, socket->name, socket->identifier); + node_socket_copy_default_value_data(eNodeSocketDatatype(iosock->typeinfo->type), + iosock->default_value, + socket->socket_data); + BLI_addtail(&ntree->inputs_legacy, iosock); + } + if (socket->flag & NODE_INTERFACE_SOCKET_OUTPUT) { + bNodeSocket *iosock = make_socket( + ntree, SOCK_OUT, socket->socket_type, socket->name, socket->identifier); + node_socket_copy_default_value_data(eNodeSocketDatatype(iosock->typeinfo->type), + iosock->default_value, + socket->socket_data); + BLI_addtail(&ntree->outputs_legacy, iosock); + } + } + return true; + }); + + /* Write inputs/outputs */ + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) { + write_node_socket_interface(writer, sock); + } + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) { + write_node_socket_interface(writer, sock); + } + + /* Clean up temporary inputs/outputs. */ + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->inputs_legacy) { + node_socket_interface_free(ntree, socket, false); + MEM_freeN(socket); + } + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->outputs_legacy) { + node_socket_interface_free(ntree, socket, false); + MEM_freeN(socket); + } + BLI_listbase_clear(&ntree->inputs_legacy); + BLI_listbase_clear(&ntree->outputs_legacy); + + /* Restore old data */ + ntree->inputs_legacy = old_inputs; + ntree->outputs_legacy = old_outputs; +} + +} // namespace blender::bke::forward_compat + static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *sock) { if (sock->default_value == nullptr) { @@ -538,19 +644,6 @@ static void write_node_socket(BlendWriter *writer, bNodeSocket *sock) write_node_socket_default_value(writer, sock); } -static void write_node_socket_interface(BlendWriter *writer, bNodeSocket *sock) -{ - BLO_write_struct(writer, bNodeSocket, sock); - - if (sock->prop) { - IDP_BlendWrite(writer, sock->prop); - } - - BLO_write_string(writer, sock->default_attribute_name); - - write_node_socket_default_value(writer, sock); -} - void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) { BKE_id_blend_write(writer, &ntree->id); @@ -576,6 +669,8 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { write_node_socket(writer, sock); } + BLO_write_struct_array( + writer, bNodePanelState, node->num_panel_states, node->panel_states_array); if (node->storage) { if (ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY) && @@ -687,14 +782,10 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) BLO_write_struct(writer, bNodeLink, link); } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { - write_node_socket_interface(writer, sock); + ntree->tree_interface.write(writer); + if (!BLO_write_is_undo(writer)) { + blender::bke::forward_compat::write_interface_as_sockets(writer, ntree); } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { - write_node_socket_interface(writer, sock); - } - - BLO_write_struct(writer, GeometryNodeAssetTraits, ntree->geometry_node_asset_traits); BLO_write_struct_array( writer, bNestedNodeRef, ntree->nested_node_refs_num, ntree->nested_node_refs); @@ -783,6 +874,7 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree) BLO_read_list(reader, &node->inputs); BLO_read_list(reader, &node->outputs); + BLO_read_data_address(reader, &node->panel_states_array); BLO_read_data_address(reader, &node->prop); IDP_BlendDataRead(reader, &node->prop); @@ -908,16 +1000,18 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree) } } - /* interface socket lists */ - BLO_read_list(reader, &ntree->inputs); - BLO_read_list(reader, &ntree->outputs); - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { + /* Read legacy interface socket lists for versioning. */ + BLO_read_list(reader, &ntree->inputs_legacy); + BLO_read_list(reader, &ntree->outputs_legacy); + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) { direct_link_node_socket(reader, sock); } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) { direct_link_node_socket(reader, sock); } + ntree->tree_interface.read_data(reader); + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { BLO_read_data_address(reader, &link->fromnode); BLO_read_data_address(reader, &link->tonode); @@ -1016,8 +1110,7 @@ void ntreeBlendReadLib(BlendLibReader *reader, bNodeTree *ntree) lib_link_node_sockets(reader, &ntree->id, &node->outputs); } - lib_link_node_sockets(reader, &ntree->id, &ntree->inputs); - lib_link_node_sockets(reader, &ntree->id, &ntree->outputs); + ntree->tree_interface.read_lib(reader, &ntree->id); /* Set `node->typeinfo` pointers. This is done in lib linking, after the * first versioning that can change types still without functions that @@ -1113,8 +1206,7 @@ void ntreeBlendReadExpand(BlendExpander *expander, bNodeTree *ntree) expand_node_sockets(expander, &node->outputs); } - expand_node_sockets(expander, &ntree->inputs); - expand_node_sockets(expander, &ntree->outputs); + ntree->tree_interface.read_expand(expander); } static void ntree_blend_read_expand(BlendExpander *expander, ID *id) @@ -1130,12 +1222,13 @@ static void node_tree_asset_pre_save(void *asset_ptr, AssetMetaData *asset_data) BKE_asset_metadata_idprop_ensure(asset_data, idprop::create("type", node_tree.type).release()); auto inputs = idprop::create_group("inputs"); auto outputs = idprop::create_group("outputs"); - LISTBASE_FOREACH (const bNodeSocket *, socket, &node_tree.inputs) { - auto property = idprop::create(socket->name, socket->typeinfo->idname); + node_tree.ensure_topology_cache(); + for (const bNodeTreeInterfaceSocket *socket : node_tree.interface_cache().inputs) { + auto property = idprop::create(socket->name, socket->socket_type); IDP_AddToGroup(inputs.get(), property.release()); } - LISTBASE_FOREACH (const bNodeSocket *, socket, &node_tree.outputs) { - auto property = idprop::create(socket->name, socket->typeinfo->idname); + for (const bNodeTreeInterfaceSocket *socket : node_tree.interface_cache().outputs) { + auto property = idprop::create(socket->name, socket->socket_type); IDP_AddToGroup(outputs.get(), property.release()); } BKE_asset_metadata_idprop_ensure(asset_data, inputs.release()); @@ -1376,18 +1469,6 @@ static void update_typeinfo(Main *bmain, } } } - - /* initialize tree sockets */ - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { - if (socktype && STREQ(sock->idname, socktype->idname)) { - node_socket_set_typeinfo(ntree, sock, unregister ? nullptr : socktype); - } - } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { - if (socktype && STREQ(sock->idname, socktype->idname)) { - node_socket_set_typeinfo(ntree, sock, unregister ? nullptr : socktype); - } - } } FOREACH_NODETREE_END; } @@ -1408,13 +1489,6 @@ void ntreeSetTypes(const bContext *C, bNodeTree *ntree) blender::bke::node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname)); } } - - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { - blender::bke::node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname)); - } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { - blender::bke::node_socket_set_typeinfo(ntree, sock, nodeSocketTypeFind(sock->idname)); - } } namespace blender::bke { @@ -1579,7 +1653,7 @@ GHashIterator *nodeTypeGetIterator() bNodeSocketType *nodeSocketTypeFind(const char *idname) { - if (idname[0]) { + if (idname && idname[0]) { bNodeSocketType *st = static_cast( BLI_ghash_lookup(blender::bke::nodesockettypes_hash, idname)); if (st) { @@ -2019,81 +2093,81 @@ const char *nodeStaticSocketType(const int type, const int subtype) return nullptr; } -const char *nodeStaticSocketInterfaceType(const int type, const int subtype) +const char *nodeStaticSocketInterfaceTypeNew(const int type, const int subtype) { switch (eNodeSocketDatatype(type)) { case SOCK_FLOAT: switch (PropertySubType(subtype)) { case PROP_UNSIGNED: - return "NodeSocketInterfaceFloatUnsigned"; + return "NodeTreeInterfaceSocketFloatUnsigned"; case PROP_PERCENTAGE: - return "NodeSocketInterfaceFloatPercentage"; + return "NodeTreeInterfaceSocketFloatPercentage"; case PROP_FACTOR: - return "NodeSocketInterfaceFloatFactor"; + return "NodeTreeInterfaceSocketFloatFactor"; case PROP_ANGLE: - return "NodeSocketInterfaceFloatAngle"; + return "NodeTreeInterfaceSocketFloatAngle"; case PROP_TIME: - return "NodeSocketInterfaceFloatTime"; + return "NodeTreeInterfaceSocketFloatTime"; case PROP_TIME_ABSOLUTE: - return "NodeSocketInterfaceFloatTimeAbsolute"; + return "NodeTreeInterfaceSocketFloatTimeAbsolute"; case PROP_DISTANCE: - return "NodeSocketInterfaceFloatDistance"; + return "NodeTreeInterfaceSocketFloatDistance"; case PROP_NONE: default: - return "NodeSocketInterfaceFloat"; + return "NodeTreeInterfaceSocketFloat"; } case SOCK_INT: switch (PropertySubType(subtype)) { case PROP_UNSIGNED: - return "NodeSocketInterfaceIntUnsigned"; + return "NodeTreeInterfaceSocketIntUnsigned"; case PROP_PERCENTAGE: - return "NodeSocketInterfaceIntPercentage"; + return "NodeTreeInterfaceSocketIntPercentage"; case PROP_FACTOR: - return "NodeSocketInterfaceIntFactor"; + return "NodeTreeInterfaceSocketIntFactor"; case PROP_NONE: default: - return "NodeSocketInterfaceInt"; + return "NodeTreeInterfaceSocketInt"; } case SOCK_BOOLEAN: - return "NodeSocketInterfaceBool"; + return "NodeTreeInterfaceSocketBool"; case SOCK_ROTATION: - return "NodeSocketInterfaceRotation"; + return "NodeTreeInterfaceSocketRotation"; case SOCK_VECTOR: switch (PropertySubType(subtype)) { case PROP_TRANSLATION: - return "NodeSocketInterfaceVectorTranslation"; + return "NodeTreeInterfaceSocketVectorTranslation"; case PROP_DIRECTION: - return "NodeSocketInterfaceVectorDirection"; + return "NodeTreeInterfaceSocketVectorDirection"; case PROP_VELOCITY: - return "NodeSocketInterfaceVectorVelocity"; + return "NodeTreeInterfaceSocketVectorVelocity"; case PROP_ACCELERATION: - return "NodeSocketInterfaceVectorAcceleration"; + return "NodeTreeInterfaceSocketVectorAcceleration"; case PROP_EULER: - return "NodeSocketInterfaceVectorEuler"; + return "NodeTreeInterfaceSocketVectorEuler"; case PROP_XYZ: - return "NodeSocketInterfaceVectorXYZ"; + return "NodeTreeInterfaceSocketVectorXYZ"; case PROP_NONE: default: - return "NodeSocketInterfaceVector"; + return "NodeTreeInterfaceSocketVector"; } case SOCK_RGBA: - return "NodeSocketInterfaceColor"; + return "NodeTreeInterfaceSocketColor"; case SOCK_STRING: - return "NodeSocketInterfaceString"; + return "NodeTreeInterfaceSocketString"; case SOCK_SHADER: - return "NodeSocketInterfaceShader"; + return "NodeTreeInterfaceSocketShader"; case SOCK_OBJECT: - return "NodeSocketInterfaceObject"; + return "NodeTreeInterfaceSocketObject"; case SOCK_IMAGE: - return "NodeSocketInterfaceImage"; + return "NodeTreeInterfaceSocketImage"; case SOCK_GEOMETRY: - return "NodeSocketInterfaceGeometry"; + return "NodeTreeInterfaceSocketGeometry"; case SOCK_COLLECTION: - return "NodeSocketInterfaceCollection"; + return "NodeTreeInterfaceSocketCollection"; case SOCK_TEXTURE: - return "NodeSocketInterfaceTexture"; + return "NodeTreeInterfaceSocketTexture"; case SOCK_MATERIAL: - return "NodeSocketInterfaceMaterial"; + return "NodeTreeInterfaceSocketMaterial"; case SOCK_CUSTOM: break; } @@ -2567,6 +2641,9 @@ bNode *node_copy_with_mapping(bNodeTree *dst_tree, node_dst->prop = IDP_CopyProperty_ex(node_src.prop, flag); } + node_dst->panel_states_array = static_cast( + MEM_dupallocN(node_src.panel_states_array)); + node_dst->runtime->internal_links = node_src.runtime->internal_links; for (bNodeLink &dst_link : node_dst->runtime->internal_links) { dst_link.fromnode = node_dst; @@ -3329,6 +3406,8 @@ void node_free_node(bNodeTree *ntree, bNode *node) MEM_freeN(sock); } + MEM_SAFE_FREE(node->panel_states_array); + if (node->prop) { /* Remember, no ID user refcount management here! */ IDP_FreePropertyContent_ex(node->prop, false); @@ -3646,152 +3725,6 @@ void ntreeLocalMerge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree) } } -/* ************ NODE TREE INTERFACE *************** */ - -static bNodeSocket *make_socket_interface(bNodeTree *ntree, - const eNodeSocketInOut in_out, - const char *idname, - const char *name) -{ - bNodeSocketType *stype = nodeSocketTypeFind(idname); - if (stype == nullptr) { - return nullptr; - } - - bNodeSocket *sock = MEM_cnew("socket template"); - sock->runtime = MEM_new(__func__); - STRNCPY(sock->idname, stype->idname); - sock->in_out = int(in_out); - sock->type = int(SOCK_CUSTOM); /* int type undefined by default */ - node_socket_set_typeinfo(ntree, sock, stype); - - /* assign new unique index */ - const int own_index = ntree->cur_index++; - /* use the own_index as socket identifier */ - if (in_out == SOCK_IN) { - SNPRINTF(sock->identifier, "Input_%d", own_index); - } - else { - SNPRINTF(sock->identifier, "Output_%d", own_index); - } - - sock->limit = (in_out == SOCK_IN ? 1 : 0xFFF); - - STRNCPY(sock->name, name); - sock->storage = nullptr; - sock->flag |= SOCK_COLLAPSED; - - return sock; -} - -bNodeSocket *ntreeFindSocketInterface(bNodeTree *ntree, - const eNodeSocketInOut in_out, - const char *identifier) -{ - ListBase *sockets = (in_out == SOCK_IN) ? &ntree->inputs : &ntree->outputs; - LISTBASE_FOREACH (bNodeSocket *, iosock, sockets) { - if (STREQ(iosock->identifier, identifier)) { - return iosock; - } - } - return nullptr; -} - -} // namespace blender::bke - -bNodeSocket *ntreeAddSocketInterface(bNodeTree *ntree, - const eNodeSocketInOut in_out, - const char *idname, - const char *name) -{ - bNodeSocket *iosock = blender::bke::make_socket_interface(ntree, in_out, idname, name); - if (in_out == SOCK_IN) { - BLI_addtail(&ntree->inputs, iosock); - } - else if (in_out == SOCK_OUT) { - BLI_addtail(&ntree->outputs, iosock); - } - - BKE_ntree_update_tag_interface(ntree); - return iosock; -} - -namespace blender::bke { - -bNodeSocket *ntreeInsertSocketInterface(bNodeTree *ntree, - const eNodeSocketInOut in_out, - const char *idname, - bNodeSocket *next_sock, - const char *name) -{ - bNodeSocket *iosock = make_socket_interface(ntree, in_out, idname, name); - if (in_out == SOCK_IN) { - BLI_insertlinkbefore(&ntree->inputs, next_sock, iosock); - } - else if (in_out == SOCK_OUT) { - BLI_insertlinkbefore(&ntree->outputs, next_sock, iosock); - } - - BKE_ntree_update_tag_interface(ntree); - return iosock; -} - -bNodeSocket *ntreeAddSocketInterfaceFromSocket(bNodeTree *ntree, - const bNode *from_node, - const bNodeSocket *from_sock) -{ - return ntreeAddSocketInterfaceFromSocketWithName( - ntree, from_node, from_sock, from_sock->idname, from_sock->name); -} - -bNodeSocket *ntreeAddSocketInterfaceFromSocketWithName(bNodeTree *ntree, - const bNode *from_node, - const bNodeSocket *from_sock, - const char *idname, - const char *name) -{ - bNodeSocket *iosock = ntreeAddSocketInterface( - ntree, eNodeSocketInOut(from_sock->in_out), idname, DATA_(name)); - if (iosock == nullptr) { - return nullptr; - } - if (iosock->typeinfo->interface_from_socket) { - iosock->typeinfo->interface_from_socket(ntree, iosock, from_node, from_sock); - } - return iosock; -} - -bNodeSocket *ntreeInsertSocketInterfaceFromSocket(bNodeTree *ntree, - bNodeSocket *next_sock, - const bNode *from_node, - const bNodeSocket *from_sock) -{ - bNodeSocket *iosock = ntreeInsertSocketInterface( - ntree, eNodeSocketInOut(from_sock->in_out), from_sock->idname, next_sock, from_sock->name); - if (iosock) { - if (iosock->typeinfo->interface_from_socket) { - iosock->typeinfo->interface_from_socket(ntree, iosock, from_node, from_sock); - } - } - return iosock; -} - -} // namespace blender::bke - -void ntreeRemoveSocketInterface(bNodeTree *ntree, bNodeSocket *sock) -{ - /* this is fast, this way we don't need an in_out argument */ - BLI_remlink(&ntree->inputs, sock); - BLI_remlink(&ntree->outputs, sock); - - blender::bke::node_socket_interface_free(ntree, sock, true); - MEM_freeN(sock); - - BKE_ntree_update_tag_interface(ntree); -} - -namespace blender::bke { - static bool ntree_contains_tree_exec(const bNodeTree *tree_to_search_in, const bNodeTree *tree_to_search_for, Set &already_passed) @@ -3935,7 +3868,7 @@ int nodeSocketLinkLimit(const bNodeSocket *sock) namespace blender::bke { static void update_socket_declarations(ListBase *sockets, - Span declarations) + Span declarations) { int index; LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, sockets, index) { diff --git a/source/blender/blenkernel/intern/node_runtime.cc b/source/blender/blenkernel/intern/node_runtime.cc index 29851ff5682..369e23a7224 100644 --- a/source/blender/blenkernel/intern/node_runtime.cc +++ b/source/blender/blenkernel/intern/node_runtime.cc @@ -24,11 +24,10 @@ void preprocess_geometry_node_tree_for_evaluation(bNodeTree &tree_cow) blender::nodes::ensure_geometry_nodes_lazy_function_graph(tree_cow); } -static void update_interface_sockets(const bNodeTree &ntree) +static void update_interface(const bNodeTree &ntree) { bNodeTreeRuntime &tree_runtime = *ntree.runtime; - tree_runtime.interface_inputs = ntree.inputs; - tree_runtime.interface_outputs = ntree.outputs; + tree_runtime.interface_cache.rebuild(const_cast(ntree.tree_interface)); } static void update_node_vector(const bNodeTree &ntree) @@ -92,6 +91,15 @@ static void update_socket_vectors_and_owner_node(const bNodeTree &ntree) } } +static void update_panels(const bNodeTree &ntree) +{ + bNodeTreeRuntime &tree_runtime = *ntree.runtime; + for (bNode *node : tree_runtime.nodes_by_id) { + bNodeRuntime &node_runtime = *node->runtime; + node_runtime.panels.reinitialize(node->num_panel_states); + } +} + static void update_internal_link_inputs(const bNodeTree &ntree) { bNodeTreeRuntime &tree_runtime = *ntree.runtime; @@ -528,10 +536,11 @@ static void ensure_topology_cache(const bNodeTree &ntree) { bNodeTreeRuntime &tree_runtime = *ntree.runtime; tree_runtime.topology_cache_mutex.ensure([&]() { - update_interface_sockets(ntree); + update_interface(ntree); update_node_vector(ntree); update_link_vector(ntree); update_socket_vectors_and_owner_node(ntree); + update_panels(ntree); update_internal_link_inputs(ntree); update_directly_linked_links_and_sockets(ntree); update_nodes_by_type(ntree); diff --git a/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc b/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc index 5e06978119b..d2f5f5331dd 100644 --- a/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc +++ b/source/blender/blenkernel/intern/node_tree_anonymous_attributes.cc @@ -19,9 +19,9 @@ namespace blender::bke::anonymous_attribute_inferencing { namespace aal = nodes::aal; using nodes::NodeDeclaration; -static bool is_possible_field_socket(const bNodeSocket &socket) +static bool is_possible_field_socket(const eNodeSocketDatatype type) { - return ELEM(socket.type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT); + return ELEM(type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT); } static bool socket_is_field(const bNodeSocket &socket) @@ -187,7 +187,7 @@ class bNodeTreeToDotOptionsForAnonymousAttributeInferencing : public bNodeTreeTo ss << "]"; return ss.str(); } - else if (is_possible_field_socket(socket)) { + else if (is_possible_field_socket(eNodeSocketDatatype(socket.type))) { std::stringstream ss; ss << socket.identifier << " ["; bits::foreach_1_index(result_.propagated_fields_by_socket[socket.index_in_tree()], @@ -204,6 +204,8 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages( { BLI_assert(!tree.has_available_link_cycle()); + const bNodeTreeInterfaceCache &cache = tree.interface_cache(); + ResourceScope scope; const Array relations_by_node = get_relations_by_node(tree, scope); @@ -211,12 +213,14 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages( Vector all_geometry_sources; /* Find input field and geometry sources. */ - for (const int i : tree.interface_inputs().index_range()) { - const bNodeSocket &interface_socket = *tree.interface_inputs()[i]; - if (interface_socket.type == SOCK_GEOMETRY) { + for (const int i : cache.inputs.index_range()) { + const bNodeTreeInterfaceSocket &interface_socket = *cache.inputs[i]; + const bNodeSocketType *typeinfo = nodeSocketTypeFind(interface_socket.socket_type); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + if (type == SOCK_GEOMETRY) { all_geometry_sources.append_and_get_index({InputGeometrySource{i}}); } - else if (is_possible_field_socket(interface_socket)) { + else if (is_possible_field_socket(type)) { all_field_sources.append_and_get_index({InputFieldSource{i}}); } } @@ -368,7 +372,7 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages( for (const int field_source_index : geometry_source.field_sources) { for (const bNodeSocket *other_socket : group_output_node->input_sockets().drop_back(1)) { - if (!is_possible_field_socket(*other_socket)) { + if (!is_possible_field_socket(eNodeSocketDatatype(other_socket->type))) { continue; } if (propagated_fields_by_socket[other_socket->index_in_tree()][field_source_index] @@ -383,7 +387,7 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages( } }); } - else if (is_possible_field_socket(*socket)) { + else if (is_possible_field_socket(eNodeSocketDatatype(socket->type))) { const BoundedBitSpan propagated_fields = propagated_fields_by_socket[socket->index_in_tree()]; bits::foreach_1_index(propagated_fields, [&](const int field_source_index) { @@ -450,9 +454,13 @@ static AnonymousAttributeInferencingResult analyse_anonymous_attribute_usages( required_fields_by_geometry_socket.all_bits() &= available_fields_by_geometry_socket.all_bits(); /* Create #EvalRelation for the tree. */ - for (const int interface_i : tree.interface_inputs().index_range()) { - const bNodeSocket &interface_socket = *tree.interface_inputs()[interface_i]; - if (interface_socket.type != SOCK_GEOMETRY) { + tree.ensure_topology_cache(); + + for (const int interface_i : cache.inputs.index_range()) { + const bNodeTreeInterfaceSocket &interface_socket = *cache.inputs[interface_i]; + const bNodeSocketType *typeinfo = interface_socket.socket_typeinfo(); + eNodeSocketDatatype socket_type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + if (socket_type != SOCK_GEOMETRY) { continue; } BitVector<> required_fields(all_field_sources.size(), false); diff --git a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc index 4cebc822171..5a1a6d434f7 100644 --- a/source/blender/blenkernel/intern/node_tree_field_inferencing.cc +++ b/source/blender/blenkernel/intern/node_tree_field_inferencing.cc @@ -500,9 +500,12 @@ static void determine_group_input_states( { { /* Non-field inputs never support fields. */ - int index; - LISTBASE_FOREACH_INDEX (bNodeSocket *, group_input, &tree.inputs, index) { - if (!is_field_socket_type((eNodeSocketDatatype)group_input->type)) { + for (const int index : tree.interface_cache().inputs.index_range()) { + const bNodeTreeInterfaceSocket *group_input = tree.interface_cache().inputs[index]; + const bNodeSocketType *typeinfo = group_input->socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : + SOCK_CUSTOM; + if (!is_field_socket_type(type)) { new_inferencing_interface.inputs[index] = InputSocketFieldType::None; } } @@ -695,9 +698,9 @@ bool update_field_inferencing(const bNodeTree &tree) /* Create new inferencing interface for this node group. */ std::unique_ptr new_inferencing_interface = std::make_unique(); - new_inferencing_interface->inputs.resize(BLI_listbase_count(&tree.inputs), + new_inferencing_interface->inputs.resize(tree.interface_cache().inputs.size(), InputSocketFieldType::IsSupported); - new_inferencing_interface->outputs.resize(BLI_listbase_count(&tree.outputs), + new_inferencing_interface->outputs.resize(tree.interface_cache().outputs.size(), OutputFieldDependency::ForDataSource()); /* Keep track of the state of all sockets. The index into this array is #SocketRef::id(). */ diff --git a/source/blender/blenkernel/intern/node_tree_zones.cc b/source/blender/blenkernel/intern/node_tree_zones.cc index 175afa47671..cc901d3a8d7 100644 --- a/source/blender/blenkernel/intern/node_tree_zones.cc +++ b/source/blender/blenkernel/intern/node_tree_zones.cc @@ -201,6 +201,7 @@ static void update_zone_border_links(const bNodeTree &tree, bNodeTreeZones &tree static std::unique_ptr discover_tree_zones(const bNodeTree &tree) { + tree.ensure_topology_cache(); if (tree.has_available_link_cycle()) { return {}; } diff --git a/source/blender/blenloader/intern/versioning_250.cc b/source/blender/blenloader/intern/versioning_250.cc index d7b168e1bfb..60dcd6e3b90 100644 --- a/source/blender/blenloader/intern/versioning_250.cc +++ b/source/blender/blenloader/intern/versioning_250.cc @@ -577,7 +577,7 @@ static bNodeSocket *do_versions_node_group_add_socket_2_56_2(bNodeTree *ngroup, // if (stype->value_structsize > 0) // gsock->default_value = MEM_callocN(stype->value_structsize, "default socket value"); - BLI_addtail(in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs, gsock); + BLI_addtail(in_out == SOCK_IN ? &ngroup->inputs_legacy : &ngroup->outputs_legacy, gsock); BKE_ntree_update_tag_interface(ngroup); @@ -2099,10 +2099,10 @@ void blo_do_versions_250(FileData *fd, Library * /*lib*/, Main *bmain) } } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) { do_versions_socket_default_value_259(sock); } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) { do_versions_socket_default_value_259(sock); } diff --git a/source/blender/blenloader/intern/versioning_260.cc b/source/blender/blenloader/intern/versioning_260.cc index 03391812cfe..9edab195562 100644 --- a/source/blender/blenloader/intern/versioning_260.cc +++ b/source/blender/blenloader/intern/versioning_260.cc @@ -229,10 +229,10 @@ static void do_versions_nodetree_socket_use_flags_2_62(bNodeTree *ntree) sock->flag &= ~SOCK_IS_LINKED; } } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) { sock->flag &= ~SOCK_IS_LINKED; } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) { sock->flag &= ~SOCK_IS_LINKED; } @@ -920,10 +920,10 @@ static void do_versions_nodetree_customnodes(bNodeTree *ntree, int /*is_group*/) } } /* tree sockets idname */ - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) { STRNCPY(sock->idname, node_socket_get_static_idname(sock)); } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) { STRNCPY(sock->idname, node_socket_get_static_idname(sock)); } } @@ -939,10 +939,10 @@ static void do_versions_nodetree_customnodes(bNodeTree *ntree, int /*is_group*/) sock->in_out = SOCK_OUT; } } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) { sock->in_out = SOCK_IN; } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) { sock->in_out = SOCK_OUT; } } @@ -969,18 +969,18 @@ static void do_versions_nodetree_customnodes(bNodeTree *ntree, int /*is_group*/) sizeof(sock->identifier)); } } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs_legacy) { STRNCPY(sock->identifier, sock->name); - BLI_uniquename(&ntree->inputs, + BLI_uniquename(&ntree->inputs_legacy, sock, "socket", '.', offsetof(bNodeSocket, identifier), sizeof(sock->identifier)); } - LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs_legacy) { STRNCPY(sock->identifier, sock->name); - BLI_uniquename(&ntree->outputs, + BLI_uniquename(&ntree->outputs_legacy, sock, "socket", '.', @@ -2725,11 +2725,11 @@ void do_versions_after_linking_260(Main *bmain) // const float offsety = 0.0f; if (create_io_nodes) { - if (ntree->inputs.first) { + if (ntree->inputs_legacy.first) { input_node = nodeAddStaticNode(nullptr, ntree, NODE_GROUP_INPUT); } - if (ntree->outputs.first) { + if (ntree->outputs_legacy.first) { output_node = nodeAddStaticNode(nullptr, ntree, NODE_GROUP_OUTPUT); } } diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 0e6a56ff6f4..1f8e6a1555b 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -318,10 +318,10 @@ static void do_versions_idproperty_ui_data(Main *bmain) LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { version_idproperty_ui_data(node->prop); } - LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs) { + LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs_legacy) { version_idproperty_ui_data(socket->prop); } - LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs) { + LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs_legacy) { version_idproperty_ui_data(socket->prop); } } @@ -544,8 +544,11 @@ static bNodeTree *add_realize_node_tree(Main *bmain) { bNodeTree *node_tree = ntreeAddTree(bmain, "Realize Instances 2.93 Legacy", "GeometryNodeTree"); - ntreeAddSocketInterface(node_tree, SOCK_IN, "NodeSocketGeometry", "Geometry"); - ntreeAddSocketInterface(node_tree, SOCK_OUT, "NodeSocketGeometry", "Geometry"); + node_tree->tree_interface.add_socket("Geometry", + "", + "NodeSocketGeometry", + NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT, + nullptr); bNode *group_input = nodeAddStaticNode(nullptr, node_tree, NODE_GROUP_INPUT); group_input->locx = -400.0f; diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 7f709336411..85bc19407ad 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -419,6 +419,36 @@ static void version_replace_principled_hair_model(bNodeTree *ntree) } } +static void versioning_convert_node_tree_socket_lists_to_interface(bNodeTree *ntree) +{ + bNodeTreeInterface &tree_interface = ntree->tree_interface; + + LISTBASE_FOREACH (const bNodeSocket *, socket, &ntree->inputs_legacy) { + eNodeTreeInterfaceSocketFlag flag = NODE_INTERFACE_SOCKET_INPUT; + SET_FLAG_FROM_TEST(flag, socket->flag & SOCK_HIDE_VALUE, NODE_INTERFACE_SOCKET_HIDE_VALUE); + SET_FLAG_FROM_TEST( + flag, socket->flag & SOCK_HIDE_IN_MODIFIER, NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER); + bNodeTreeInterfaceSocket *new_socket = tree_interface.add_socket( + socket->name, socket->description, socket->idname, flag, nullptr); + BLI_assert(new_socket != nullptr); + + MEM_SAFE_FREE(new_socket->identifier); + new_socket->identifier = BLI_strdup(socket->identifier); + } + LISTBASE_FOREACH (const bNodeSocket *, socket, &ntree->outputs_legacy) { + eNodeTreeInterfaceSocketFlag flag = NODE_INTERFACE_SOCKET_OUTPUT; + SET_FLAG_FROM_TEST(flag, socket->flag & SOCK_HIDE_VALUE, NODE_INTERFACE_SOCKET_HIDE_VALUE); + SET_FLAG_FROM_TEST( + flag, socket->flag & SOCK_HIDE_IN_MODIFIER, NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER); + bNodeTreeInterfaceSocket *new_socket = tree_interface.add_socket( + socket->name, socket->description, socket->idname, flag, nullptr); + BLI_assert(new_socket != nullptr); + + MEM_SAFE_FREE(new_socket->identifier); + new_socket->identifier = BLI_strdup(socket->identifier); + } +} + void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) { if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 1)) { @@ -576,6 +606,12 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } FOREACH_NODETREE_END; + /* Convert old socket lists into new interface items. */ + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + versioning_convert_node_tree_socket_lists_to_interface(ntree); + } + FOREACH_NODETREE_END; + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { diff --git a/source/blender/compositor/realtime_compositor/intern/utilities.cc b/source/blender/compositor/realtime_compositor/intern/utilities.cc index 4f239280d16..d01564cb012 100644 --- a/source/blender/compositor/realtime_compositor/intern/utilities.cc +++ b/source/blender/compositor/realtime_compositor/intern/utilities.cc @@ -122,7 +122,7 @@ InputDescriptor input_descriptor_from_input_socket(const bNodeSocket *socket) if (!node_declaration) { return input_descriptor; } - const SocketDeclarationPtr &socket_declaration = node_declaration->inputs[socket->index()]; + const SocketDeclaration *socket_declaration = node_declaration->inputs[socket->index()]; input_descriptor.domain_priority = socket_declaration->compositor_domain_priority(); input_descriptor.expects_single_value = socket_declaration->compositor_expects_single_value(); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 83a5f454d68..329b75148a2 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1871,6 +1871,9 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) if (built_map_.checkIsBuiltAndTag(ntree)) { return; } + + ntree->ensure_topology_cache(); + /* nodetree itself */ add_id_node(&ntree->id); /* General parameters. */ @@ -1953,11 +1956,11 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) } } - LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs) { - build_idproperties(socket->prop); + for (bNodeTreeInterfaceSocket *socket : ntree->interface_cache().inputs) { + build_idproperties(socket->properties); } - LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs) { - build_idproperties(socket->prop); + for (bNodeTreeInterfaceSocket *socket : ntree->interface_cache().outputs) { + build_idproperties(socket->properties); } /* TODO: link from nodetree to owner_component? */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 3c825e66039..e4bd05d8fee 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -2929,11 +2929,11 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) } } - LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs) { - build_idproperties(socket->prop); + for (bNodeTreeInterfaceSocket *socket : ntree->interface_cache().inputs) { + build_idproperties(socket->properties); } - LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs) { - build_idproperties(socket->prop); + for (bNodeTreeInterfaceSocket *socket : ntree->interface_cache().outputs) { + build_idproperties(socket->properties); } if (check_id_has_anim_component(&ntree->id)) { diff --git a/source/blender/editors/curves/intern/curves_add.cc b/source/blender/editors/curves/intern/curves_add.cc index 444f988086b..c940634db56 100644 --- a/source/blender/editors/curves/intern/curves_add.cc +++ b/source/blender/editors/curves/intern/curves_add.cc @@ -72,8 +72,11 @@ void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob) nmd.node_group = ntreeAddTree(bmain, DATA_("Surface Deform"), "GeometryNodeTree"); bNodeTree *ntree = nmd.node_group; - ntreeAddSocketInterface(ntree, SOCK_IN, "NodeSocketGeometry", "Geometry"); - ntreeAddSocketInterface(ntree, SOCK_OUT, "NodeSocketGeometry", "Geometry"); + ntree->tree_interface.add_socket("Geometry", + "", + "NodeSocketGeometry", + NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT, + nullptr); bNode *group_input = nodeAddStaticNode(&C, ntree, NODE_GROUP_INPUT); bNode *group_output = nodeAddStaticNode(&C, ntree, NODE_GROUP_OUTPUT); bNode *deform_node = nodeAddStaticNode(&C, ntree, GEO_NODE_DEFORM_CURVES_ON_SURFACE); diff --git a/source/blender/editors/geometry/node_group_operator.cc b/source/blender/editors/geometry/node_group_operator.cc index af56ecc5810..bb421d930a1 100644 --- a/source/blender/editors/geometry/node_group_operator.cc +++ b/source/blender/editors/geometry/node_group_operator.cc @@ -386,9 +386,12 @@ static std::string run_node_group_get_description(bContext *C, static void add_attribute_search_or_value_buttons(uiLayout *layout, PointerRNA *md_ptr, - const bNodeSocket &socket) + const bNodeTreeInterfaceSocket &socket) { - char socket_id_esc[sizeof(socket.identifier) * 2]; + bNodeSocketType *typeinfo = nodeSocketTypeFind(socket.socket_type); + const eNodeSocketDatatype socket_type = eNodeSocketDatatype(typeinfo->type); + + char socket_id_esc[MAX_NAME * 2]; BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); const std::string rna_path = "[\"" + std::string(socket_id_esc) + "\"]"; const std::string rna_path_use_attribute = "[\"" + std::string(socket_id_esc) + @@ -404,7 +407,7 @@ static void add_attribute_search_or_value_buttons(uiLayout *layout, uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT); const bool use_attribute = RNA_boolean_get(md_ptr, rna_path_use_attribute.c_str()); - if (socket.type == SOCK_BOOLEAN && !use_attribute) { + if (socket_type == SOCK_BOOLEAN && !use_attribute) { uiItemL(name_row, "", ICON_NONE); } else { @@ -412,7 +415,7 @@ static void add_attribute_search_or_value_buttons(uiLayout *layout, } uiLayout *prop_row = uiLayoutRow(split, true); - if (socket.type == SOCK_BOOLEAN) { + if (socket_type == SOCK_BOOLEAN) { uiLayoutSetPropSep(prop_row, false); uiLayoutSetAlignment(prop_row, UI_LAYOUT_ALIGN_EXPAND); } @@ -422,7 +425,7 @@ static void add_attribute_search_or_value_buttons(uiLayout *layout, uiItemR(prop_row, md_ptr, rna_path_attribute_name.c_str(), UI_ITEM_NONE, "", ICON_NONE); } else { - const char *name = socket.type == SOCK_BOOLEAN ? socket.name : ""; + const char *name = socket_type == SOCK_BOOLEAN ? socket.name : ""; uiItemR(prop_row, md_ptr, rna_path.c_str(), UI_ITEM_NONE, name, ICON_NONE); } @@ -435,9 +438,12 @@ static void draw_property_for_socket(const bNodeTree &node_tree, IDProperty *op_properties, PointerRNA *bmain_ptr, PointerRNA *op_ptr, - const bNodeSocket &socket, + const bNodeTreeInterfaceSocket &socket, const int socket_index) { + bNodeSocketType *typeinfo = nodeSocketTypeFind(socket.socket_type); + const eNodeSocketDatatype socket_type = eNodeSocketDatatype(typeinfo->type); + /* The property should be created in #MOD_nodes_update_interface with the correct type. */ IDProperty *property = IDP_GetPropertyFromGroup(op_properties, socket.identifier); @@ -447,7 +453,7 @@ static void draw_property_for_socket(const bNodeTree &node_tree, return; } - char socket_id_esc[sizeof(socket.identifier) * 2]; + char socket_id_esc[MAX_NAME * 2]; BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); char rna_path[sizeof(socket_id_esc) + 4]; @@ -459,7 +465,7 @@ static void draw_property_for_socket(const bNodeTree &node_tree, /* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough * information about what type of ID to select for editing the values. This is because * pointer IDProperties contain no information about their type. */ - switch (socket.type) { + switch (socket_type) { case SOCK_OBJECT: uiItemPointerR(row, op_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA); break; @@ -503,10 +509,12 @@ static void run_node_group_ui(bContext *C, wmOperator *op) return; } - int input_index; - LISTBASE_FOREACH_INDEX (bNodeSocket *, io_socket, &node_tree->inputs, input_index) { + node_tree->ensure_topology_cache(); + int input_index = 0; + for (bNodeTreeInterfaceSocket *io_socket : node_tree->interface_cache().inputs) { draw_property_for_socket( *node_tree, layout, op->properties, &bmain_ptr, op->ptr, *io_socket, input_index); + ++input_index; } } diff --git a/source/blender/editors/include/UI_abstract_view.hh b/source/blender/editors/include/UI_abstract_view.hh index b5874199ae7..b981969783c 100644 --- a/source/blender/editors/include/UI_abstract_view.hh +++ b/source/blender/editors/include/UI_abstract_view.hh @@ -188,7 +188,7 @@ class AbstractViewItem { * * \return True if the renaming was successful. */ - virtual bool rename(StringRefNull new_name); + virtual bool rename(const bContext &C, StringRefNull new_name); /** * Get the string that should be used for renaming, typically the item's label. This string will * not be modified, but if the renaming is canceled, the value will be reset to this. @@ -249,7 +249,7 @@ class AbstractViewItem { bool is_renaming() const; void begin_renaming(); void end_renaming(); - void rename_apply(); + void rename_apply(const bContext &C); template static ToType *from_item_handle(uiViewItemHandle *handle); diff --git a/source/blender/editors/include/UI_interface_c.hh b/source/blender/editors/include/UI_interface_c.hh index 76dac42a2a6..f77a080174b 100644 --- a/source/blender/editors/include/UI_interface_c.hh +++ b/source/blender/editors/include/UI_interface_c.hh @@ -2583,6 +2583,8 @@ void uiTemplateLightLinkingCollection(uiLayout *layout, PointerRNA *ptr, const c void uiTemplateGreasePencilLayerTree(uiLayout *layout, bContext *C); +void uiTemplateNodeTreeDeclaration(struct uiLayout *layout, struct PointerRNA *ptr); + /** * \return: A RNA pointer for the operator properties. */ diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh index 7d5690d33af..c0c85e7a985 100644 --- a/source/blender/editors/include/UI_tree_view.hh +++ b/source/blender/editors/include/UI_tree_view.hh @@ -207,7 +207,7 @@ class AbstractTreeViewItem : public AbstractViewItem, public TreeViewItemContain /** See AbstractViewItem::get_rename_string(). */ /* virtual */ StringRef get_rename_string() const override; /** See AbstractViewItem::rename(). */ - /* virtual */ bool rename(StringRefNull new_name) override; + /* virtual */ bool rename(const bContext &C, StringRefNull new_name) override; /** * Return whether the item can be collapsed. Used to disable collapsing for items with children. diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 529ecf59127..a1553caa9c2 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -70,6 +70,7 @@ set(SRC interface_template_grease_pencil_layer_tree.cc interface_template_light_linking.cc interface_template_list.cc + interface_template_node_tree_declaration.cc interface_template_search_menu.cc interface_template_search_operator.cc interface_templates.cc diff --git a/source/blender/editors/interface/interface_template_grease_pencil_layer_tree.cc b/source/blender/editors/interface/interface_template_grease_pencil_layer_tree.cc index fca714d4660..226e65d8b44 100644 --- a/source/blender/editors/interface/interface_template_grease_pencil_layer_tree.cc +++ b/source/blender/editors/interface/interface_template_grease_pencil_layer_tree.cc @@ -200,7 +200,7 @@ class LayerViewItem : public AbstractTreeViewItem { return true; } - bool rename(StringRefNull new_name) override + bool rename(const bContext & /*C*/, StringRefNull new_name) override { grease_pencil_.rename_node(layer_.as_node(), new_name); return true; @@ -309,7 +309,7 @@ class LayerGroupViewItem : public AbstractTreeViewItem { return true; } - bool rename(StringRefNull new_name) override + bool rename(const bContext & /*C*/, StringRefNull new_name) override { grease_pencil_.rename_node(group_.as_node(), new_name); return true; diff --git a/source/blender/editors/interface/interface_template_node_tree_declaration.cc b/source/blender/editors/interface/interface_template_node_tree_declaration.cc new file mode 100644 index 00000000000..a2a9005acdf --- /dev/null +++ b/source/blender/editors/interface/interface_template_node_tree_declaration.cc @@ -0,0 +1,519 @@ +/* SPDX-FileCopyrightText: 2023 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup edinterface + */ + +#include "BKE_context.h" +#include "BKE_node_tree_interface.hh" +#include "BKE_node_tree_update.h" + +#include "BLI_color.hh" +#include "BLI_string.h" + +#include "BLT_translation.h" + +#include "DNA_node_tree_interface_types.h" + +#include "ED_node.hh" + +#include "RNA_access.hh" +#include "RNA_prototypes.h" + +#include "UI_interface.hh" +#include "UI_resources.hh" +#include "UI_tree_view.hh" + +#include "WM_api.hh" + +namespace node_interface = blender::bke::node_interface; + +namespace blender::ui::nodes { + +namespace { + +class NodeTreeInterfaceView; + +class NodeTreeInterfaceDragController : public AbstractViewItemDragController { + public: + explicit NodeTreeInterfaceDragController(NodeTreeInterfaceView &view, + bNodeTreeInterfaceItem &item); + virtual ~NodeTreeInterfaceDragController() = default; + + eWM_DragDataType get_drag_type() const; + + void *create_drag_data() const; + + private: + bNodeTreeInterfaceItem &item_; +}; + +class NodeSocketDropTarget : public TreeViewItemDropTarget { + public: + explicit NodeSocketDropTarget(NodeTreeInterfaceView &view, bNodeTreeInterfaceSocket &socket); + + bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override; + std::string drop_tooltip(const DragInfo &drag_info) const override; + bool on_drop(struct bContext * /*C*/, const DragInfo &drag_info) const override; + + protected: + wmDragNodeTreeInterface *get_drag_node_tree_declaration(const wmDrag &drag) const; + + private: + bNodeTreeInterfaceSocket &socket_; +}; + +class NodePanelDropTarget : public TreeViewItemDropTarget { + public: + explicit NodePanelDropTarget(NodeTreeInterfaceView &view, bNodeTreeInterfacePanel &panel); + + bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override; + std::string drop_tooltip(const DragInfo &drag_info) const override; + bool on_drop(bContext *C, const DragInfo &drag_info) const override; + + protected: + wmDragNodeTreeInterface *get_drag_node_tree_declaration(const wmDrag &drag) const; + + private: + bNodeTreeInterfacePanel &panel_; +}; + +class NodeSocketViewItem : public BasicTreeViewItem { + public: + NodeSocketViewItem(bNodeTree &nodetree, + bNodeTreeInterface &interface, + bNodeTreeInterfaceSocket &socket) + : BasicTreeViewItem(socket.name, ICON_NONE), nodetree_(nodetree), socket_(socket) + { + set_is_active_fn([interface, socket]() { return interface.active_item() == &socket.item; }); + set_on_activate_fn([&interface](bContext & /*C*/, BasicTreeViewItem &new_active) { + NodeSocketViewItem &self = static_cast(new_active); + interface.active_item_set(&self.socket_.item); + }); + } + + void build_row(uiLayout &row) override + { + uiLayoutSetPropDecorate(&row, false); + + uiLayout *input_socket_layout = uiLayoutRow(&row, true); + if (socket_.flag & NODE_INTERFACE_SOCKET_INPUT) { + /* XXX Socket template only draws in embossed layouts (Julian). */ + uiLayoutSetEmboss(input_socket_layout, UI_EMBOSS); + /* XXX Context is not used by the template function. */ + bContext *C = nullptr; + uiTemplateNodeSocket(input_socket_layout, C, socket_.socket_color()); + } + else { + /* Blank item to align output socket labels with inputs. */ + uiItemL(input_socket_layout, "", ICON_BLANK1); + } + + add_label(row); + + uiLayout *output_socket_layout = uiLayoutRow(&row, true); + if (socket_.flag & NODE_INTERFACE_SOCKET_OUTPUT) { + /* XXX Socket template only draws in embossed layouts (Julian). */ + uiLayoutSetEmboss(output_socket_layout, UI_EMBOSS); + /* XXX Context is not used by the template function. */ + bContext *C = nullptr; + uiTemplateNodeSocket(output_socket_layout, C, socket_.socket_color()); + } + else { + /* Blank item to align input socket labels with outputs. */ + uiItemL(output_socket_layout, "", ICON_BLANK1); + } + } + + protected: + bool matches(const AbstractViewItem &other) const override + { + const NodeSocketViewItem *other_item = dynamic_cast(&other); + if (other_item == nullptr) { + return false; + } + + return &socket_ == &other_item->socket_; + } + + bool supports_renaming() const override + { + return true; + } + bool rename(const bContext &C, StringRefNull new_name) override + { + socket_.name = BLI_strdup(new_name.c_str()); + BKE_ntree_update_tag_interface(&nodetree_); + ED_node_tree_propagate_change(&C, CTX_data_main(&C), &nodetree_); + return true; + } + StringRef get_rename_string() const override + { + return socket_.name; + } + + std::unique_ptr create_drag_controller() const override; + std::unique_ptr create_drop_target() override; + + private: + bNodeTree &nodetree_; + bNodeTreeInterfaceSocket &socket_; +}; + +class NodePanelViewItem : public BasicTreeViewItem { + public: + NodePanelViewItem(bNodeTree &nodetree, + bNodeTreeInterface &interface, + bNodeTreeInterfacePanel &panel) + : BasicTreeViewItem(panel.name, ICON_NONE), nodetree_(nodetree), panel_(panel) + { + set_is_active_fn([interface, panel]() { return interface.active_item() == &panel.item; }); + set_on_activate_fn([&interface](bContext & /*C*/, BasicTreeViewItem &new_active) { + NodePanelViewItem &self = static_cast(new_active); + interface.active_item_set(&self.panel_.item); + }); + } + + void build_row(uiLayout &row) override + { + add_label(row); + + uiLayout *sub = uiLayoutRow(&row, true); + uiLayoutSetPropDecorate(sub, false); + } + + protected: + bool matches(const AbstractViewItem &other) const override + { + const NodePanelViewItem *other_item = dynamic_cast(&other); + if (other_item == nullptr) { + return false; + } + + return &panel_ == &other_item->panel_; + } + + bool supports_renaming() const override + { + return true; + } + bool rename(const bContext &C, StringRefNull new_name) override + { + panel_.name = BLI_strdup(new_name.c_str()); + BKE_ntree_update_tag_interface(&nodetree_); + ED_node_tree_propagate_change(&C, CTX_data_main(&C), &nodetree_); + return true; + } + StringRef get_rename_string() const override + { + return panel_.name; + } + + std::unique_ptr create_drag_controller() const override; + std::unique_ptr create_drop_target() override; + + private: + bNodeTree &nodetree_; + bNodeTreeInterfacePanel &panel_; +}; + +class NodeTreeInterfaceView : public AbstractTreeView { + public: + explicit NodeTreeInterfaceView(bNodeTree &nodetree, bNodeTreeInterface &interface) + : nodetree_(nodetree), interface_(interface) + { + } + + bNodeTree &nodetree() + { + return nodetree_; + } + + bNodeTreeInterface &interface() + { + return interface_; + } + + void build_tree() override + { + /* Draw root items */ + add_items_for_panel_recursive(interface_.root_panel, *this); + } + + protected: + void add_items_for_panel_recursive(bNodeTreeInterfacePanel &parent, + ui::TreeViewOrItem &parent_item) + { + for (bNodeTreeInterfaceItem *item : parent.items()) { + switch (item->item_type) { + case NODE_INTERFACE_SOCKET: { + bNodeTreeInterfaceSocket *socket = node_interface::get_item_as( + item); + NodeSocketViewItem &socket_item = parent_item.add_tree_item( + nodetree_, interface_, *socket); + socket_item.set_collapsed(false); + break; + } + case NODE_INTERFACE_PANEL: { + bNodeTreeInterfacePanel *panel = node_interface::get_item_as( + item); + NodePanelViewItem &panel_item = parent_item.add_tree_item( + nodetree_, interface_, *panel); + panel_item.set_collapsed(false); + add_items_for_panel_recursive(*panel, panel_item); + break; + } + } + } + } + + private: + bNodeTree &nodetree_; + bNodeTreeInterface &interface_; +}; + +std::unique_ptr NodeSocketViewItem::create_drag_controller() const +{ + return std::make_unique( + static_cast(get_tree_view()), socket_.item); +} + +std::unique_ptr NodeSocketViewItem::create_drop_target() +{ + return std::make_unique( + static_cast(get_tree_view()), socket_); +} + +std::unique_ptr NodePanelViewItem::create_drag_controller() const +{ + return std::make_unique( + static_cast(get_tree_view()), panel_.item); +} + +std::unique_ptr NodePanelViewItem::create_drop_target() +{ + return std::make_unique( + static_cast(get_tree_view()), panel_); +} + +NodeTreeInterfaceDragController::NodeTreeInterfaceDragController(NodeTreeInterfaceView &view, + bNodeTreeInterfaceItem &item) + : AbstractViewItemDragController(view), item_(item) +{ +} + +eWM_DragDataType NodeTreeInterfaceDragController::get_drag_type() const +{ + return WM_DRAG_NODE_TREE_INTERFACE; +} + +void *NodeTreeInterfaceDragController::create_drag_data() const +{ + wmDragNodeTreeInterface *drag_data = MEM_cnew(__func__); + drag_data->item = &item_; + return drag_data; +} + +NodeSocketDropTarget::NodeSocketDropTarget(NodeTreeInterfaceView &view, + bNodeTreeInterfaceSocket &socket) + : TreeViewItemDropTarget(view, DropBehavior::Reorder), socket_(socket) +{ +} + +bool NodeSocketDropTarget::can_drop(const wmDrag &drag, const char ** /*r_disabled_hint*/) const +{ + if (drag.type != WM_DRAG_NODE_TREE_INTERFACE) { + return false; + } + wmDragNodeTreeInterface *drag_data = get_drag_node_tree_declaration(drag); + + /* Can't drop an item onto its children. */ + if (const bNodeTreeInterfacePanel *panel = node_interface::get_item_as( + drag_data->item)) + { + if (panel->contains(socket_.item)) { + return false; + } + } + return true; +} + +std::string NodeSocketDropTarget::drop_tooltip(const DragInfo &drag_info) const +{ + switch (drag_info.drop_location) { + case DropLocation::Into: + return ""; + case DropLocation::Before: + return N_("Insert before socket"); + case DropLocation::After: + return N_("Insert after socket"); + } + return ""; +} + +bool NodeSocketDropTarget::on_drop(bContext *C, const DragInfo &drag_info) const +{ + wmDragNodeTreeInterface *drag_data = get_drag_node_tree_declaration(drag_info.drag_data); + BLI_assert(drag_data != nullptr); + bNodeTreeInterfaceItem *drag_item = drag_data->item; + BLI_assert(drag_item != nullptr); + + bNodeTree &nodetree = get_view().nodetree(); + bNodeTreeInterface &interface = get_view().interface(); + + bNodeTreeInterfacePanel *parent = interface.find_item_parent(socket_.item); + int index = -1; + + /* Insert into same panel as the target. */ + BLI_assert(parent != nullptr); + switch (drag_info.drop_location) { + case DropLocation::Before: + index = parent->items().as_span().first_index_try(&socket_.item); + break; + case DropLocation::After: + index = parent->items().as_span().first_index_try(&socket_.item) + 1; + break; + default: + /* All valid cases should be handled above. */ + BLI_assert_unreachable(); + break; + } + if (parent == nullptr || index < 0) { + return false; + } + + interface.move_item_to_parent(*drag_item, parent, index); + + /* General update */ + BKE_ntree_update_tag_interface(&nodetree); + ED_node_tree_propagate_change(C, CTX_data_main(C), &nodetree); + return true; +} + +wmDragNodeTreeInterface *NodeSocketDropTarget::get_drag_node_tree_declaration( + const wmDrag &drag) const +{ + BLI_assert(drag.type == WM_DRAG_NODE_TREE_INTERFACE); + return static_cast(drag.poin); +} + +NodePanelDropTarget::NodePanelDropTarget(NodeTreeInterfaceView &view, + bNodeTreeInterfacePanel &panel) + : TreeViewItemDropTarget(view, DropBehavior::ReorderAndInsert), panel_(panel) +{ +} + +bool NodePanelDropTarget::can_drop(const wmDrag &drag, const char ** /*r_disabled_hint*/) const +{ + if (drag.type != WM_DRAG_NODE_TREE_INTERFACE) { + return false; + } + wmDragNodeTreeInterface *drag_data = get_drag_node_tree_declaration(drag); + + /* Can't drop an item onto its children. */ + if (const bNodeTreeInterfacePanel *panel = node_interface::get_item_as( + drag_data->item)) + { + if (panel->contains(panel_.item)) { + return false; + } + } + + return true; +} + +std::string NodePanelDropTarget::drop_tooltip(const DragInfo &drag_info) const +{ + switch (drag_info.drop_location) { + case DropLocation::Into: + return "Insert into panel"; + case DropLocation::Before: + return N_("Insert before panel"); + case DropLocation::After: + return N_("Insert after panel"); + } + return ""; +} + +bool NodePanelDropTarget::on_drop(bContext *C, const DragInfo &drag_info) const +{ + wmDragNodeTreeInterface *drag_data = get_drag_node_tree_declaration(drag_info.drag_data); + BLI_assert(drag_data != nullptr); + bNodeTreeInterfaceItem *drag_item = drag_data->item; + BLI_assert(drag_item != nullptr); + + bNodeTree &nodetree = get_view().nodetree(); + bNodeTreeInterface &interface = get_view().interface(); + + bNodeTreeInterfacePanel *parent = nullptr; + int index = -1; + switch (drag_info.drop_location) { + case DropLocation::Into: { + /* Insert into target */ + parent = &panel_; + index = 0; + break; + } + case DropLocation::Before: { + /* Insert into same panel as the target. */ + parent = interface.find_item_parent(panel_.item); + BLI_assert(parent != nullptr); + index = parent->items().as_span().first_index_try(&panel_.item); + break; + } + case DropLocation::After: { + /* Insert into same panel as the target. */ + parent = interface.find_item_parent(panel_.item); + BLI_assert(parent != nullptr); + index = parent->items().as_span().first_index_try(&panel_.item) + 1; + break; + } + } + if (parent == nullptr || index < 0) { + return false; + } + + interface.move_item_to_parent(*drag_item, parent, index); + + /* General update */ + BKE_ntree_update_tag_interface(&nodetree); + ED_node_tree_propagate_change(C, CTX_data_main(C), &nodetree); + return true; +} + +wmDragNodeTreeInterface *NodePanelDropTarget::get_drag_node_tree_declaration( + const wmDrag &drag) const +{ + BLI_assert(drag.type == WM_DRAG_NODE_TREE_INTERFACE); + return static_cast(drag.poin); +} + +} // namespace + +} // namespace blender::ui::nodes + +namespace ui = blender::ui; + +void uiTemplateNodeTreeDeclaration(struct uiLayout *layout, struct PointerRNA *ptr) +{ + if (!ptr->data) { + return; + } + if (!RNA_struct_is_a(ptr->type, &RNA_NodeTreeInterface)) { + return; + } + bNodeTree &nodetree = *reinterpret_cast(ptr->owner_id); + bNodeTreeInterface &interface = *static_cast(ptr->data); + + uiBlock *block = uiLayoutGetBlock(layout); + + ui::AbstractTreeView *tree_view = UI_block_add_view( + *block, + "Node Tree Declaration Tree View", + std::make_unique(nodetree, interface)); + tree_view->set_min_rows(3); + + ui::TreeViewBuilder::build_tree_view(*tree_view, *layout); +} diff --git a/source/blender/editors/interface/views/abstract_view_item.cc b/source/blender/editors/interface/views/abstract_view_item.cc index 061414d9adf..0e9759f606f 100644 --- a/source/blender/editors/interface/views/abstract_view_item.cc +++ b/source/blender/editors/interface/views/abstract_view_item.cc @@ -106,7 +106,7 @@ bool AbstractViewItem::supports_renaming() const /* No renaming by default. */ return false; } -bool AbstractViewItem::rename(StringRefNull /*new_name*/) +bool AbstractViewItem::rename(const bContext & /*C*/, StringRefNull /*new_name*/) { /* No renaming by default. */ return false; @@ -138,10 +138,10 @@ void AbstractViewItem::begin_renaming() std::copy(std::begin(initial_str), std::end(initial_str), std::begin(view.get_rename_buffer())); } -void AbstractViewItem::rename_apply() +void AbstractViewItem::rename_apply(const bContext &C) { const AbstractView &view = get_view(); - rename(view.get_rename_buffer().data()); + rename(C, view.get_rename_buffer().data()); end_renaming(); } @@ -179,12 +179,12 @@ static AbstractViewItem *find_item_from_rename_button(const uiBut &rename_but) return nullptr; } -static void rename_button_fn(bContext * /*C*/, void *arg, char * /*origstr*/) +static void rename_button_fn(bContext *C, void *arg, char * /*origstr*/) { const uiBut *rename_but = static_cast(arg); AbstractViewItem *item = find_item_from_rename_button(*rename_but); BLI_assert(item); - item->rename_apply(); + item->rename_apply(*C); } void AbstractViewItem::add_rename_button(uiBlock &block) diff --git a/source/blender/editors/interface/views/tree_view.cc b/source/blender/editors/interface/views/tree_view.cc index fc33f7bff68..f0bc5a246fd 100644 --- a/source/blender/editors/interface/views/tree_view.cc +++ b/source/blender/editors/interface/views/tree_view.cc @@ -410,7 +410,7 @@ StringRef AbstractTreeViewItem::get_rename_string() const return label_; } -bool AbstractTreeViewItem::rename(StringRefNull new_name) +bool AbstractTreeViewItem::rename(const bContext & /*C*/, StringRefNull new_name) { /* It is important to update the label after renaming, so #AbstractTreeViewItem::matches_single() * recognizes the item. (It only compares labels by default.) */ diff --git a/source/blender/editors/object/object_relations.cc b/source/blender/editors/object/object_relations.cc index 2e2e84325de..cdbb1bb5be3 100644 --- a/source/blender/editors/object/object_relations.cc +++ b/source/blender/editors/object/object_relations.cc @@ -70,6 +70,8 @@ #include "BKE_mesh.hh" #include "BKE_modifier.h" #include "BKE_node.h" +#include "BKE_node_runtime.hh" +#include "BKE_node_tree_interface.hh" #include "BKE_object.h" #include "BKE_pointcloud.h" #include "BKE_report.h" @@ -2961,23 +2963,32 @@ char *ED_object_ot_drop_geometry_nodes_tooltip(bContext *C, static bool check_geometry_node_group_sockets(wmOperator *op, const bNodeTree *tree) { - const bNodeSocket *first_input = (const bNodeSocket *)tree->inputs.first; - if (!first_input) { - BKE_report(op->reports, RPT_ERROR, "The node group must have a geometry input socket"); - return false; + tree->ensure_topology_cache(); + if (!tree->interface_cache().inputs.is_empty()) { + const bNodeTreeInterfaceSocket *first_input = tree->interface_cache().inputs[0]; + if (!first_input) { + BKE_report(op->reports, RPT_ERROR, "The node group must have a geometry input socket"); + return false; + } + const bNodeSocketType *typeinfo = first_input->socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + if (type != SOCK_GEOMETRY) { + BKE_report(op->reports, RPT_ERROR, "The first input must be a geometry socket"); + return false; + } } - if (first_input->type != SOCK_GEOMETRY) { - BKE_report(op->reports, RPT_ERROR, "The first input must be a geometry socket"); - return false; - } - const bNodeSocket *first_output = (const bNodeSocket *)tree->outputs.first; - if (!first_output) { - BKE_report(op->reports, RPT_ERROR, "The node group must have a geometry output socket"); - return false; - } - if (first_output->type != SOCK_GEOMETRY) { - BKE_report(op->reports, RPT_ERROR, "The first output must be a geometry socket"); - return false; + if (!tree->interface_cache().outputs.is_empty()) { + const bNodeTreeInterfaceSocket *first_output = tree->interface_cache().outputs[0]; + if (!first_output) { + BKE_report(op->reports, RPT_ERROR, "The node group must have a geometry output socket"); + return false; + } + const bNodeSocketType *typeinfo = first_output->socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + if (type != SOCK_GEOMETRY) { + BKE_report(op->reports, RPT_ERROR, "The first output must be a geometry socket"); + return false; + } } return true; } diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc index ac2e1e92678..c251cafd53b 100644 --- a/source/blender/editors/space_file/asset_catalog_tree_view.cc +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -87,7 +87,7 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { void build_context_menu(bContext &C, uiLayout &column) const override; bool supports_renaming() const override; - bool rename(StringRefNull new_name) override; + bool rename(const bContext &C, StringRefNull new_name) override; /** Add drag support for catalog items. */ std::unique_ptr create_drag_controller() const override; @@ -330,10 +330,10 @@ bool AssetCatalogTreeViewItem::supports_renaming() const return !ED_asset_catalogs_read_only(*tree_view.asset_library_); } -bool AssetCatalogTreeViewItem::rename(StringRefNull new_name) +bool AssetCatalogTreeViewItem::rename(const bContext &C, StringRefNull new_name) { /* Important to keep state. */ - BasicTreeViewItem::rename(new_name); + BasicTreeViewItem::rename(C, new_name); const AssetCatalogTreeView &tree_view = static_cast( get_tree_view()); diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 2c428cfc9ce..ed801bc7446 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -1124,23 +1124,14 @@ static void node_socket_undefined_draw_color(bContext * /*C*/, r_color[3] = 1.0f; } -static void node_socket_undefined_interface_draw(bContext * /*C*/, - uiLayout *layout, - PointerRNA * /*ptr*/) +static void node_socket_undefined_interface_draw(ID * /*id*/, + bNodeTreeInterfaceSocket * /*interface_socket*/, + bContext * /*C*/, + uiLayout *layout) { uiItemL(layout, IFACE_("Undefined Socket Type"), ICON_ERROR); } -static void node_socket_undefined_interface_draw_color(bContext * /*C*/, - PointerRNA * /*ptr*/, - float *r_color) -{ - r_color[0] = 1.0f; - r_color[1] = 0.0f; - r_color[2] = 0.0f; - r_color[3] = 1.0f; -} - /** \} */ } // namespace blender::ed::space_node @@ -1162,7 +1153,6 @@ void ED_node_init_butfuncs() NodeSocketTypeUndefined.draw = node_socket_undefined_draw; NodeSocketTypeUndefined.draw_color = node_socket_undefined_draw_color; NodeSocketTypeUndefined.interface_draw = node_socket_undefined_interface_draw; - NodeSocketTypeUndefined.interface_draw_color = node_socket_undefined_interface_draw_color; /* node type ui functions */ NODE_TYPES_BEGIN (ntype) { @@ -1208,22 +1198,38 @@ static const float std_node_socket_colors[][4] = { {0.92, 0.46, 0.7, 1.0}, /* SOCK_ROTATION */ }; -/* common color callbacks for standard types */ -static void std_node_socket_draw_color(bContext * /*C*/, - PointerRNA *ptr, - PointerRNA * /*node_ptr*/, - float *r_color) +/* Callback for colors that does not depend on the socket pointer argument to get the type. */ +template +void std_node_socket_color_fn(bContext * /*C*/, + PointerRNA * /*ptr*/, + PointerRNA * /*node_ptr*/, + float *r_color) { - bNodeSocket *sock = (bNodeSocket *)ptr->data; - int type = sock->typeinfo->type; - copy_v4_v4(r_color, std_node_socket_colors[type]); -} -static void std_node_socket_interface_draw_color(bContext * /*C*/, PointerRNA *ptr, float *r_color) -{ - bNodeSocket *sock = (bNodeSocket *)ptr->data; - int type = sock->typeinfo->type; - copy_v4_v4(r_color, std_node_socket_colors[type]); -} + copy_v4_v4(r_color, std_node_socket_colors[socket_type]); +}; + +using SocketColorFn = void (*)(bContext * /*C*/, + PointerRNA * /*ptr*/, + PointerRNA * /*node_ptr*/, + float *r_color); +/* Callbacks for all built-in socket types. */ +static const SocketColorFn std_node_socket_color_funcs[] = { + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + nullptr /* UNUSED */, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, + std_node_socket_color_fn, +}; /* draw function for file output node sockets, * displays only sub-path and format, no value button */ @@ -1434,36 +1440,41 @@ static void std_node_socket_draw( } } -static void std_node_socket_interface_draw(bContext * /*C*/, uiLayout *layout, PointerRNA *ptr) +static void std_node_socket_interface_draw(ID *id, + bNodeTreeInterfaceSocket *interface_socket, + bContext * /*C*/, + uiLayout *layout) { - bNodeSocket *sock = (bNodeSocket *)ptr->data; - int type = sock->typeinfo->type; + PointerRNA ptr, tree_ptr; + RNA_pointer_create(id, &RNA_NodeTreeInterfaceSocket, interface_socket, &ptr); + RNA_id_pointer_create(id, &tree_ptr); - PointerRNA tree_ptr; - RNA_id_pointer_create(ptr->owner_id, &tree_ptr); + const bNodeSocketType *typeinfo = interface_socket->socket_typeinfo(); + BLI_assert(typeinfo != nullptr); + eNodeSocketDatatype type = eNodeSocketDatatype(typeinfo->type); uiLayout *col = uiLayoutColumn(layout, false); switch (type) { case SOCK_FLOAT: { - uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); + uiItemR(col, &ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); uiLayout *sub = uiLayoutColumn(col, true); - uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); - uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); + uiItemR(sub, &ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); + uiItemR(sub, &ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); break; } case SOCK_INT: { - uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); + uiItemR(col, &ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); uiLayout *sub = uiLayoutColumn(col, true); - uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); - uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); + uiItemR(sub, &ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); + uiItemR(sub, &ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); break; } case SOCK_VECTOR: { - uiItemR(col, ptr, "default_value", UI_ITEM_R_EXPAND, IFACE_("Default"), ICON_NONE); + uiItemR(col, &ptr, "default_value", UI_ITEM_R_EXPAND, IFACE_("Default"), ICON_NONE); uiLayout *sub = uiLayoutColumn(col, true); - uiItemR(sub, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); - uiItemR(sub, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); + uiItemR(sub, &ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); + uiItemR(sub, &ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); break; } case SOCK_BOOLEAN: @@ -1475,17 +1486,24 @@ static void std_node_socket_interface_draw(bContext * /*C*/, uiLayout *layout, P case SOCK_IMAGE: case SOCK_TEXTURE: case SOCK_MATERIAL: { - uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); + uiItemR(col, &ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), ICON_NONE); break; } + case SOCK_SHADER: + case SOCK_GEOMETRY: + break; + + case SOCK_CUSTOM: + BLI_assert_unreachable(); + break; } col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "hide_value", DEFAULT_FLAGS, nullptr, ICON_NONE); + uiItemR(col, &ptr, "hide_value", DEFAULT_FLAGS, nullptr, ICON_NONE); - const bNodeTree *node_tree = reinterpret_cast(ptr->owner_id); - if (sock->in_out == SOCK_IN && node_tree->type == NTREE_GEOMETRY) { - uiItemR(col, ptr, "hide_in_modifier", DEFAULT_FLAGS, nullptr, ICON_NONE); + const bNodeTree *node_tree = reinterpret_cast(id); + if (interface_socket->flag & NODE_INTERFACE_SOCKET_INPUT && node_tree->type == NTREE_GEOMETRY) { + uiItemR(col, &ptr, "hide_in_modifier", DEFAULT_FLAGS, nullptr, ICON_NONE); } } @@ -1503,9 +1521,8 @@ void ED_init_standard_node_socket_type(bNodeSocketType *stype) { using namespace blender::ed::space_node; stype->draw = std_node_socket_draw; - stype->draw_color = std_node_socket_draw_color; + stype->draw_color = std_node_socket_color_funcs[stype->type]; stype->interface_draw = std_node_socket_interface_draw; - stype->interface_draw_color = std_node_socket_interface_draw_color; } void ED_init_node_socket_type_virtual(bNodeSocketType *stype) @@ -1563,29 +1580,34 @@ void draw_nodespace_back_pix(const bContext &C, GPU_matrix_push_projection(); GPU_matrix_push(); - /* The draw manager is used to draw the backdrop image. */ + /* The draw manager is used to draw the + * backdrop image. */ GPUFrameBuffer *old_fb = GPU_framebuffer_active_get(); GPU_framebuffer_restore(); BLI_thread_lock(LOCK_DRAW_IMAGE); DRW_draw_view(&C); BLI_thread_unlock(LOCK_DRAW_IMAGE); GPU_framebuffer_bind_no_srgb(old_fb); - /* Draw manager changes the depth state. Set it back to NONE. Without this the node preview - * images aren't drawn correctly. */ + /* Draw manager changes the depth state. + * Set it back to NONE. Without this the + * node preview images aren't drawn + * correctly. */ GPU_depth_test(GPU_DEPTH_NONE); void *lock; Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock); if (ibuf) { - /* somehow the offset has to be calculated inverse */ + /* somehow the offset has to be + * calculated inverse */ wmOrtho2_region_pixelspace(®ion); const float offset_x = snode.xof + ima->offset_x * snode.zoom; const float offset_y = snode.yof + ima->offset_y * snode.zoom; const float x = (region.winx - snode.zoom * ibuf->x) / 2 + offset_x; const float y = (region.winy - snode.zoom * ibuf->y) / 2 + offset_y; - /** \note draw selected info on backdrop */ + /** \note draw selected info on backdrop + */ if (snode.edittree) { bNode *node = (bNode *)snode.edittree->nodes.first; rctf *viewer_border = &snode.nodetree->viewer_border; @@ -1984,7 +2006,8 @@ static void nodelink_batch_add_link(const SpaceNode &snode, const std::array &points, const NodeLinkDrawConfig &draw_config) { - /* Only allow these colors. If more is needed, you need to modify the shader accordingly. */ + /* Only allow these colors. If more is needed, you need to modify the shader accordingly. + */ BLI_assert( ELEM(draw_config.th_col1, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT)); BLI_assert( diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc index 131d39ea67b..80dc301c963 100644 --- a/source/blender/editors/space_node/link_drag_search.cc +++ b/source/blender/editors/space_node/link_drag_search.cc @@ -87,9 +87,16 @@ static void add_reroute_node_fn(nodes::LinkSearchOpParams ¶ms) static void add_group_input_node_fn(nodes::LinkSearchOpParams ¶ms) { /* Add a group input based on the connected socket, and add a new group input node. */ - bNodeSocket *interface_socket = bke::ntreeAddSocketInterfaceFromSocket( - ¶ms.node_tree, ¶ms.node, ¶ms.socket); - const int group_input_index = BLI_findindex(¶ms.node_tree.inputs, interface_socket); + const eNodeSocketInOut in_out = eNodeSocketInOut(params.socket.in_out); + eNodeTreeInterfaceSocketFlag flag = eNodeTreeInterfaceSocketFlag(0); + SET_FLAG_FROM_TEST(flag, in_out & SOCK_IN, NODE_INTERFACE_SOCKET_INPUT); + SET_FLAG_FROM_TEST(flag, in_out & SOCK_OUT, NODE_INTERFACE_SOCKET_OUTPUT); + bNodeTreeInterfaceSocket *socket_iface = params.node_tree.tree_interface.add_socket( + params.socket.name, + params.socket.description, + params.socket.typeinfo->idname, + flag, + nullptr); bNode &group_input = params.add_node("NodeGroupInput"); /* This is necessary to create the new sockets in the other input nodes. */ @@ -98,9 +105,10 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams ¶ms) /* Hide the new input in all other group input nodes, to avoid making them taller. */ for (bNode *node : params.node_tree.all_nodes()) { if (node->type == NODE_GROUP_INPUT) { - bNodeSocket *new_group_input_socket = (bNodeSocket *)BLI_findlink(&node->outputs, - group_input_index); - new_group_input_socket->flag |= SOCK_HIDDEN; + bNodeSocket *new_group_input_socket = nodeFindSocket(node, in_out, socket_iface->identifier); + if (new_group_input_socket) { + new_group_input_socket->flag |= SOCK_HIDDEN; + } } } @@ -109,37 +117,36 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams ¶ms) socket->flag |= SOCK_HIDDEN; } - bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&group_input.outputs, group_input_index); - if (socket == nullptr) { - /* Adding sockets can fail in some cases. There's no good reason not to be safe here. */ - return; - } - /* Unhide the socket for the new input in the new node and make a connection to it. */ - socket->flag &= ~SOCK_HIDDEN; - nodeAddLink(¶ms.node_tree, &group_input, socket, ¶ms.node, ¶ms.socket); + bNodeSocket *socket = nodeFindSocket(&group_input, in_out, socket_iface->identifier); + if (socket) { + /* Unhide the socket for the new input in the new node and make a connection to it. */ + socket->flag &= ~SOCK_HIDDEN; + nodeAddLink(¶ms.node_tree, &group_input, socket, ¶ms.node, ¶ms.socket); - bke::node_socket_move_default_value( - *CTX_data_main(¶ms.C), params.node_tree, params.socket, *socket); + bke::node_socket_move_default_value( + *CTX_data_main(¶ms.C), params.node_tree, params.socket, *socket); + } } static void add_existing_group_input_fn(nodes::LinkSearchOpParams ¶ms, - const bNodeSocket &interface_socket) + const bNodeTreeInterfaceSocket &interface_socket) { - const int group_input_index = BLI_findindex(¶ms.node_tree.inputs, &interface_socket); + const eNodeSocketInOut in_out = eNodeSocketInOut(params.socket.in_out); + eNodeTreeInterfaceSocketFlag flag = eNodeTreeInterfaceSocketFlag(0); + SET_FLAG_FROM_TEST(flag, in_out & SOCK_IN, NODE_INTERFACE_SOCKET_INPUT); + SET_FLAG_FROM_TEST(flag, in_out & SOCK_OUT, NODE_INTERFACE_SOCKET_OUTPUT); + bNode &group_input = params.add_node("NodeGroupInput"); LISTBASE_FOREACH (bNodeSocket *, socket, &group_input.outputs) { socket->flag |= SOCK_HIDDEN; } - bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&group_input.outputs, group_input_index); - if (socket == nullptr) { - /* Adding sockets can fail in some cases. There's no good reason not to be safe here. */ - return; + bNodeSocket *socket = nodeFindSocket(&group_input, in_out, interface_socket.identifier); + if (socket != nullptr) { + socket->flag &= ~SOCK_HIDDEN; + nodeAddLink(¶ms.node_tree, &group_input, socket, ¶ms.node, ¶ms.socket); } - - socket->flag &= ~SOCK_HIDDEN; - nodeAddLink(¶ms.node_tree, &group_input, socket, ¶ms.node, ¶ms.socket); } /** @@ -313,20 +320,30 @@ static void gather_socket_link_operations(const bContext &C, search_link_ops.append({IFACE_("Group Input"), add_group_input_node_fn}); int weight = -1; - LISTBASE_FOREACH (const bNodeSocket *, interface_socket, &node_tree.inputs) { - eNodeSocketDatatype from = (eNodeSocketDatatype)interface_socket->type; - eNodeSocketDatatype to = (eNodeSocketDatatype)socket.type; - if (node_tree.typeinfo->validate_link && !node_tree.typeinfo->validate_link(from, to)) { - continue; + node_tree.tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) { + if (item.item_type != NODE_INTERFACE_SOCKET) { + return true; + } + const bNodeTreeInterfaceSocket &interface_socket = + reinterpret_cast(item); + { + const bNodeSocketType *from_typeinfo = nodeSocketTypeFind(interface_socket.socket_type); + const eNodeSocketDatatype from = from_typeinfo ? eNodeSocketDatatype(from_typeinfo->type) : + SOCK_CUSTOM; + const eNodeSocketDatatype to = eNodeSocketDatatype(socket.typeinfo->type); + if (node_tree.typeinfo->validate_link && !node_tree.typeinfo->validate_link(from, to)) { + return true; + } } search_link_ops.append( - {std::string(IFACE_("Group Input")) + " " + UI_MENU_ARROW_SEP + interface_socket->name, + {std::string(IFACE_("Group Input")) + " " + UI_MENU_ARROW_SEP + interface_socket.name, [interface_socket](nodes::LinkSearchOpParams ¶ms) { - add_existing_group_input_fn(params, *interface_socket); + add_existing_group_input_fn(params, interface_socket); }, weight}); weight--; - } + return true; + }); } gather_search_link_ops_for_all_assets(C, node_tree, socket, search_link_ops); diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index e72de7a93b7..65cc38a71e3 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -141,7 +141,9 @@ void ED_node_tree_update(const bContext *C) if (snode) { snode_set_context(*C); - id_us_ensure_real(&snode->nodetree->id); + if (snode->nodetree) { + id_us_ensure_real(&snode->nodetree->id); + } } } @@ -344,6 +346,290 @@ float2 node_from_view(const bNode &node, const float2 &co) ; } +/* Draw UI for options, buttons, and previews. */ +static bool node_update_basis_buttons( + const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block, int &dy) +{ + /* Buttons rect? */ + const bool node_options = node.typeinfo->draw_buttons && (node.flag & NODE_OPTIONS); + if (!node_options) { + return false; + } + + PointerRNA nodeptr; + RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr); + + /* Get "global" coordinates. */ + float2 loc = node_to_view(node, float2(0)); + /* Round the node origin because text contents are always pixel-aligned. */ + loc.x = round(loc.x); + loc.y = round(loc.y); + + dy -= NODE_DYS / 2; + + uiLayout *layout = UI_block_layout(&block, + UI_LAYOUT_VERTICAL, + UI_LAYOUT_PANEL, + loc.x + NODE_DYS, + dy, + NODE_WIDTH(node) - NODE_DY, + 0, + 0, + UI_style_get_dpi()); + + if (node.flag & NODE_MUTED) { + uiLayoutSetActive(layout, false); + } + + uiLayoutSetContextPointer(layout, "node", &nodeptr); + + node.typeinfo->draw_buttons(layout, (bContext *)&C, &nodeptr); + + UI_block_align_end(&block); + int buty; + UI_block_layout_resolve(&block, nullptr, &buty); + + dy = buty - NODE_DYS / 2; + return true; +} + +static bool node_update_basis_socket(const bContext &C, + bNodeTree &ntree, + bNode &node, + bNodeSocket &socket, + uiBlock &block, + const int &locx, + int &locy) +{ + if (!socket.is_visible()) { + return false; + } + + PointerRNA nodeptr, sockptr; + RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr); + RNA_pointer_create(&ntree.id, &RNA_NodeSocket, &socket, &sockptr); + + const eNodeSocketInOut in_out = eNodeSocketInOut(socket.in_out); + + /* Add the half the height of a multi-input socket to cursor Y + * to account for the increased height of the taller sockets. */ + const bool is_multi_input = (in_out == SOCK_IN && socket.flag & SOCK_MULTI_INPUT); + const float multi_input_socket_offset = is_multi_input ? + std::max(socket.runtime->total_inputs - 2, 0) * + NODE_MULTI_INPUT_LINK_GAP : + 0.0f; + locy -= multi_input_socket_offset * 0.5f; + + uiLayout *layout = UI_block_layout(&block, + UI_LAYOUT_VERTICAL, + UI_LAYOUT_PANEL, + locx + NODE_DYS, + locy, + NODE_WIDTH(node) - NODE_DY, + NODE_DY, + 0, + UI_style_get_dpi()); + + if (node.flag & NODE_MUTED) { + uiLayoutSetActive(layout, false); + } + + /* Context pointers for current node and socket. */ + uiLayoutSetContextPointer(layout, "node", &nodeptr); + uiLayoutSetContextPointer(layout, "socket", &sockptr); + + uiLayout *row = uiLayoutRow(layout, true); + /* Alignment based on input/output. */ + uiLayoutSetAlignment(row, in_out == SOCK_IN ? UI_LAYOUT_ALIGN_LEFT : UI_LAYOUT_ALIGN_RIGHT); + + const char *socket_label = bke::nodeSocketLabel(&socket); + const char *socket_translation_context = node_socket_get_translation_context(socket); + socket.typeinfo->draw((bContext *)&C, + row, + &sockptr, + &nodeptr, + CTX_IFACE_(socket_translation_context, socket_label)); + + node_socket_add_tooltip_in_node_editor(ntree, socket, *row); + + UI_block_align_end(&block); + int buty; + UI_block_layout_resolve(&block, nullptr, &buty); + + /* Horizontal position for input/output. */ + const float offsetx = (in_out == SOCK_IN ? 0.0f : NODE_WIDTH(node)); + /* Round the socket location to stop it from jiggling. */ + socket.runtime->location = float2(round(locx + offsetx), round(locy - NODE_DYS)); + + locy = buty - multi_input_socket_offset * 0.5; + return true; +} + +/* Advanced drawing with panels and arbitrary input/output ordering. */ +static void node_update_basis_from_declaration( + const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block, const int locx, int &locy) +{ + namespace nodes = blender::nodes; + + BLI_assert(node.declaration() != nullptr); + BLI_assert(node.runtime->panels.size() == node.num_panel_states); + + const nodes::NodeDeclaration &decl = *node.declaration(); + const bool has_buttons = node_update_basis_buttons(C, ntree, node, block, locy); + + bNodeSocket *current_input = static_cast(node.inputs.first); + bNodeSocket *current_output = static_cast(node.outputs.first); + bNodePanelState *current_panel_state = node.panel_states_array; + bke::bNodePanelRuntime *current_panel_runtime = node.runtime->panels.begin(); + bool has_sockets = false; + + /* Parent panel stack with count of items still to be added. */ + struct PanelUpdate { + int remaining_items; + bool is_collapsed; + }; + + Stack panel_updates; + for (const nodes::ItemDeclarationPtr &item_decl : decl.items) { + bool is_parent_collapsed = false; + if (PanelUpdate *parent_update = panel_updates.is_empty() ? nullptr : &panel_updates.peek()) { + /* Adding an item to the parent panel, will be popped when reaching 0. */ + BLI_assert(parent_update->remaining_items > 0); + --parent_update->remaining_items; + is_parent_collapsed = parent_update->is_collapsed; + } + + if (nodes::PanelDeclaration *panel_decl = dynamic_cast( + item_decl.get())) { + BLI_assert(node.panel_states().contains_ptr(current_panel_state)); + BLI_assert(node.runtime->panels.as_span().contains_ptr(current_panel_runtime)); + + if (!is_parent_collapsed) { + locy -= NODE_DY; + } + + SET_FLAG_FROM_TEST( + current_panel_state->flag, is_parent_collapsed, NODE_PANEL_PARENT_COLLAPSED); + /* New top panel is collapsed if self or parent is collapsed. */ + const bool is_collapsed = is_parent_collapsed || current_panel_state->is_collapsed(); + panel_updates.push({panel_decl->num_items, is_collapsed}); + + /* Round the socket location to stop it from jiggling. */ + current_panel_runtime->location = float2(locx, round(locy + NODE_DYS)); + ++current_panel_state; + ++current_panel_runtime; + } + else if (nodes::SocketDeclaration *socket_decl = dynamic_cast( + item_decl.get())) + { + switch (socket_decl->in_out) { + case SOCK_IN: + if (!current_input) { + /* XXX should match the declaration, assert? */ + break; + } + + SET_FLAG_FROM_TEST(current_input->flag, is_parent_collapsed, SOCK_PANEL_COLLAPSED); + if (is_parent_collapsed) { + current_input->runtime->location = float2(locx, round(locy + NODE_DYS)); + } + else { + has_sockets |= node_update_basis_socket( + C, ntree, node, *current_input, block, locx, locy); + } + current_input = current_input->next; + break; + case SOCK_OUT: + if (!current_output) { + /* XXX should match the declaration, assert? */ + break; + } + + SET_FLAG_FROM_TEST(current_output->flag, is_parent_collapsed, SOCK_PANEL_COLLAPSED); + if (is_parent_collapsed) { + current_output->runtime->location = float2(round(locx + NODE_WIDTH(node)), + round(locy + NODE_DYS)); + } + else { + has_sockets |= node_update_basis_socket( + C, ntree, node, *current_output, block, locx, locy); + } + current_output = current_output->next; + break; + } + } + + /* Close parent panels that have all items added. */ + while (!panel_updates.is_empty()) { + if (panel_updates.peek().remaining_items > 0) { + /* Incomplete panel, continue adding items. */ + break; + } + /* Close panel and continue checking parent. */ + panel_updates.pop(); + } + } + if (!panel_updates.is_empty()) { + /* TODO warning, more panel items declared than added. */ + } + + /* Little bit of space in end. */ + if (has_sockets || !has_buttons) { + locy -= NODE_DYS / 2; + } +} + +/* Conventional drawing in outputs/buttons/inputs order. */ +static void node_update_basis_from_socket_lists( + const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block, const int locx, int &locy) +{ + const bool node_options = node.typeinfo->draw_buttons && (node.flag & NODE_OPTIONS); + const bool inputs_first = node.inputs.first && !(node.outputs.first || node_options); + + /* Add a little bit of padding above the top socket. */ + if (node.outputs.first || inputs_first) { + locy -= NODE_DYS / 2; + } + + /* Output sockets. */ + bool add_output_space = false; + + for (bNodeSocket *socket : node.output_sockets()) { + /* Clear flag, conventional drawing does not support panels. */ + socket->flag &= ~SOCK_PANEL_COLLAPSED; + + if (node_update_basis_socket(C, ntree, node, *socket, block, locx, locy)) { + if (socket->next) { + locy -= NODE_SOCKDY; + } + add_output_space = true; + } + } + + if (add_output_space) { + locy -= NODE_DY / 4; + } + + node_update_basis_buttons(C, ntree, node, block, locy); + + /* Input sockets. */ + for (bNodeSocket *socket : node.input_sockets()) { + /* Clear flag, conventional drawing does not support panels. */ + socket->flag &= ~SOCK_PANEL_COLLAPSED; + + if (node_update_basis_socket(C, ntree, node, *socket, block, locx, locy)) { + if (socket->next) { + locy -= NODE_SOCKDY; + } + } + } + + /* Little bit of space in end. */ + if (node.inputs.first || (node.flag & NODE_OPTIONS) == 0) { + locy -= NODE_DYS / 2; + } +} + /** * Based on settings and sockets in node, set drawing rect info. */ @@ -356,9 +642,6 @@ static void node_update_basis(const bContext &C, PointerRNA nodeptr; RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr); - const bool node_options = node.typeinfo->draw_buttons && (node.flag & NODE_OPTIONS); - const bool inputs_first = node.inputs.first && !(node.outputs.first || node_options); - /* Get "global" coordinates. */ float2 loc = node_to_view(node, float2(0)); /* Round the node origin because text contents are always pixel-aligned. */ @@ -370,172 +653,11 @@ static void node_update_basis(const bContext &C, /* Header. */ dy -= NODE_DY; - /* Add a little bit of padding above the top socket. */ - if (node.outputs.first || inputs_first) { - dy -= NODE_DYS / 2; + if (node.declaration()) { + node_update_basis_from_declaration(C, ntree, node, block, loc.x, dy); } - - /* Output sockets. */ - bool add_output_space = false; - - int buty; - for (bNodeSocket *socket : node.output_sockets()) { - if (!socket->is_visible()) { - continue; - } - - PointerRNA sockptr; - RNA_pointer_create(&ntree.id, &RNA_NodeSocket, socket, &sockptr); - - uiLayout *layout = UI_block_layout(&block, - UI_LAYOUT_VERTICAL, - UI_LAYOUT_PANEL, - loc.x + NODE_DYS, - dy, - NODE_WIDTH(node) - NODE_DY, - NODE_DY, - 0, - UI_style_get_dpi()); - - if (node.flag & NODE_MUTED) { - uiLayoutSetActive(layout, false); - } - - /* Context pointers for current node and socket. */ - uiLayoutSetContextPointer(layout, "node", &nodeptr); - uiLayoutSetContextPointer(layout, "socket", &sockptr); - - /* Align output buttons to the right. */ - uiLayout *row = uiLayoutRow(layout, true); - uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT); - - const char *socket_label = bke::nodeSocketLabel(socket); - const char *socket_translation_context = node_socket_get_translation_context(*socket); - socket->typeinfo->draw((bContext *)&C, - row, - &sockptr, - &nodeptr, - CTX_IFACE_(socket_translation_context, socket_label)); - - node_socket_add_tooltip_in_node_editor(ntree, *socket, *row); - - UI_block_align_end(&block); - UI_block_layout_resolve(&block, nullptr, &buty); - - /* Ensure minimum socket height in case layout is empty. */ - buty = min_ii(buty, dy - NODE_DY); - - /* Round the socket location to stop it from jiggling. */ - socket->runtime->location = float2(round(loc.x + NODE_WIDTH(node)), round(dy - NODE_DYS)); - - dy = buty; - if (socket->next) { - dy -= NODE_SOCKDY; - } - - add_output_space = true; - } - - if (add_output_space) { - dy -= NODE_DY / 4; - } - - /* Buttons rect? */ - if (node_options) { - dy -= NODE_DYS / 2; - - uiLayout *layout = UI_block_layout(&block, - UI_LAYOUT_VERTICAL, - UI_LAYOUT_PANEL, - loc.x + NODE_DYS, - dy, - NODE_WIDTH(node) - NODE_DY, - 0, - 0, - UI_style_get_dpi()); - - if (node.flag & NODE_MUTED) { - uiLayoutSetActive(layout, false); - } - - uiLayoutSetContextPointer(layout, "node", &nodeptr); - - node.typeinfo->draw_buttons(layout, (bContext *)&C, &nodeptr); - - UI_block_align_end(&block); - UI_block_layout_resolve(&block, nullptr, &buty); - - dy = buty - NODE_DYS / 2; - } - - /* Input sockets. */ - for (bNodeSocket *socket : node.input_sockets()) { - if (!socket->is_visible()) { - continue; - } - - PointerRNA sockptr; - RNA_pointer_create(&ntree.id, &RNA_NodeSocket, socket, &sockptr); - - /* Add the half the height of a multi-input socket to cursor Y - * to account for the increased height of the taller sockets. */ - float multi_input_socket_offset = 0.0f; - if (socket->flag & SOCK_MULTI_INPUT) { - if (socket->runtime->total_inputs > 2) { - multi_input_socket_offset = (socket->runtime->total_inputs - 2) * - NODE_MULTI_INPUT_LINK_GAP; - } - } - dy -= multi_input_socket_offset * 0.5f; - - uiLayout *layout = UI_block_layout(&block, - UI_LAYOUT_VERTICAL, - UI_LAYOUT_PANEL, - loc.x + NODE_DYS, - dy, - NODE_WIDTH(node) - NODE_DY, - NODE_DY, - 0, - UI_style_get_dpi()); - - if (node.flag & NODE_MUTED) { - uiLayoutSetActive(layout, false); - } - - /* Context pointers for current node and socket. */ - uiLayoutSetContextPointer(layout, "node", &nodeptr); - uiLayoutSetContextPointer(layout, "socket", &sockptr); - - uiLayout *row = uiLayoutRow(layout, true); - - const char *socket_label = bke::nodeSocketLabel(socket); - const char *socket_translation_context = node_socket_get_translation_context(*socket); - socket->typeinfo->draw((bContext *)&C, - row, - &sockptr, - &nodeptr, - CTX_IFACE_(socket_translation_context, socket_label)); - - node_socket_add_tooltip_in_node_editor(ntree, *socket, *row); - - UI_block_align_end(&block); - UI_block_layout_resolve(&block, nullptr, &buty); - - /* Ensure minimum socket height in case layout is empty. */ - buty = min_ii(buty, dy - NODE_DY); - - /* Round the socket vertical position to stop it from jiggling. */ - socket->runtime->location = float2(loc.x, round(dy - NODE_DYS)); - - dy = buty - multi_input_socket_offset * 0.5; - if (socket->next) { - dy -= NODE_SOCKDY; - } - } - - /* Little bit of space in end. */ - if (node.inputs.first || (node.flag & NODE_OPTIONS) == 0) { - dy -= NODE_DYS / 2; + else { + node_update_basis_from_socket_lists(C, ntree, node, block, loc.x, dy); } node.runtime->totr.xmin = loc.x; @@ -740,13 +862,14 @@ static void node_socket_draw_multi_input(const float color[4], const float height, const float2 location) { - /* The other sockets are drawn with the keyframe shader. There, the outline has a base thickness - * that can be varied but always scales with the size the socket is drawn at. Using + /* The other sockets are drawn with the keyframe shader. There, the outline has a base + * thickness that can be varied but always scales with the size the socket is drawn at. Using * `UI_SCALE_FAC` has the same effect here. It scales the outline correctly across different * screen DPI's and UI scales without being affected by the 'line-width'. */ const float outline_width = NODE_SOCK_OUTLINE_SCALE * UI_SCALE_FAC; - /* UI_draw_roundbox draws the outline on the outer side, so compensate for the outline width. */ + /* UI_draw_roundbox draws the outline on the outer side, so compensate for the outline width. + */ const rctf rect = { location.x - width + outline_width * 0.5f, location.x + width - outline_width * 0.5f, @@ -1471,7 +1594,7 @@ static void node_draw_sockets(const View2D &v2d, /* Socket inputs. */ int selected_input_len = 0; for (const bNodeSocket *sock : node.input_sockets()) { - if (!sock->is_visible()) { + if (!sock->is_visible() || sock->is_panel_collapsed()) { continue; } if (select_all || (sock->flag & SELECT)) { @@ -1504,7 +1627,7 @@ static void node_draw_sockets(const View2D &v2d, int selected_output_len = 0; if (draw_outputs) { for (const bNodeSocket *sock : node.output_sockets()) { - if (!sock->is_visible()) { + if (!sock->is_visible() || sock->is_panel_collapsed()) { continue; } if (select_all || (sock->flag & SELECT)) { @@ -1629,6 +1752,130 @@ static void node_draw_sockets(const View2D &v2d, } } +static void node_panel_toggle_button_cb(bContext *C, void *panel_state_argv, void *ntree_argv) +{ + Main *bmain = CTX_data_main(C); + bNodePanelState *panel_state = (bNodePanelState *)panel_state_argv; + bNodeTree *ntree = (bNodeTree *)ntree_argv; + + panel_state->flag ^= NODE_PANEL_COLLAPSED; + + ED_node_tree_propagate_change(C, bmain, ntree); +} + +static void node_draw_panels(const View2D & /*v2d*/, + const bContext & /*C*/, + TreeDrawContext &tree_draw_ctx, + bNodeTree &ntree, + const bNode &node, + uiBlock &block) +{ + namespace nodes = blender::nodes; + + BLI_assert(node.declaration() != nullptr); + // BLI_assert(node.panel_states().size() == node.declaration().num_panels); + BLI_assert(node.runtime->panels.size() == node.panel_states().size()); + + const nodes::NodeDeclaration &decl = *node.declaration(); + const rctf &rct = node.runtime->totr; + const int color_id = node_get_colorid(tree_draw_ctx, node); + + int panel_i = 0; + for (const nodes::ItemDeclarationPtr &item_decl : decl.items) { + const nodes::PanelDeclaration *panel_decl = dynamic_cast( + item_decl.get()); + if (panel_decl == nullptr) { + /* Not a panel. */ + continue; + } + + const bNodePanelState &state = node.panel_states()[panel_i]; + /* Don't draw hidden panels. */ + if (state.is_parent_collapsed()) { + continue; + } + const bke::bNodePanelRuntime &runtime = node.runtime->panels[panel_i]; + + const rctf rect = { + rct.xmin, + rct.xmax, + runtime.location.y - NODE_DYS, + runtime.location.y + NODE_DYS, + }; + + UI_block_emboss_set(&block, UI_EMBOSS_NONE); + + /* Panel background. */ + float color_panel[4]; + if (node.flag & NODE_MUTED) { + UI_GetThemeColorBlend4f(TH_BACK, color_id, 0.1f, color_panel); + } + else { + UI_GetThemeColorBlend4f(TH_NODE, color_id, 0.2f, color_panel); + } + UI_draw_roundbox_corner_set(UI_CNR_NONE); + UI_draw_roundbox_4fv(&rect, true, BASIS_RAD, color_panel); + + /* Collapse/expand icon. */ + const int but_size = U.widget_unit * 0.8f; + uiDefIconBut(&block, + UI_BTYPE_BUT_TOGGLE, + 0, + state.is_collapsed() ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT, + rct.xmin + (NODE_MARGIN_X / 3), + runtime.location.y - but_size / 2, + but_size, + but_size, + nullptr, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + ""); + + /* Panel label. */ + uiBut *but = uiDefBut(&block, + UI_BTYPE_LABEL, + 0, + panel_decl->name.c_str(), + int(rct.xmin + NODE_MARGIN_X + 0.4f), + int(runtime.location.y - NODE_DYS), + short(rct.xmax - rct.xmin - 0.35f * U.widget_unit), + short(NODE_DY), + nullptr, + 0, + 0, + 0, + 0, + ""); + if (node.flag & NODE_MUTED) { + UI_but_flag_enable(but, UI_BUT_INACTIVE); + } + + /* Invisible button covering the entire header for collapsing/expanding. */ + but = uiDefIconBut(&block, + UI_BTYPE_BUT_TOGGLE, + 0, + ICON_NONE, + rect.xmin, + rect.ymin, + rect.xmax - rect.xmin, + rect.ymax - rect.ymin, + nullptr, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + ""); + UI_but_func_set( + but, node_panel_toggle_button_cb, const_cast(&state), &ntree); + + UI_block_emboss_set(&block, UI_EMBOSS); + + ++panel_i; + } +} + static int node_error_type_to_icon(const geo_log::NodeWarningType type) { switch (type) { @@ -2012,7 +2259,8 @@ static Vector node_get_extra_info(TreeDrawContext &tree_draw_c row.text = node_get_execution_time_label(tree_draw_ctx, snode, node); if (!row.text.empty()) { row.tooltip = TIP_( - "The execution time from the node tree's latest evaluation. For frame and group nodes, " + "The execution time from the node tree's latest evaluation. For frame and group " + "nodes, " "the time for all sub-nodes"); row.icon = ICON_PREVIEW_RANGE; rows.append(std::move(row)); @@ -2565,6 +2813,10 @@ static void node_draw_basis(const bContext &C, node_draw_sockets(v2d, C, ntree, node, block, true, false); } + if (node.declaration() != nullptr) { + node_draw_panels(v2d, C, tree_draw_ctx, ntree, node, block); + } + UI_block_end(&C, &block); UI_block_draw(&C, &block); } @@ -3299,7 +3551,8 @@ static void node_draw_zones(TreeDrawContext & /*tree_draw_ctx*/, return bounding_box_area_by_zone[a] > bounding_box_area_by_zone[b]; }); - /* Draw all the contour lines after to prevent them from getting hidden by overlapping zones. */ + /* Draw all the contour lines after to prevent them from getting hidden by overlapping zones. + */ for (const int zone_i : zone_draw_order) { float zone_color[4]; UI_GetThemeColor4fv(get_theme_id(zone_i), zone_color); diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index cfaa85a9331..67d2967b280 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -2218,464 +2218,6 @@ void NODE_OT_node_copy_color(wmOperatorType *ot) /** \} */ -/* -------------------------------------------------------------------- */ -/** \name Node-Tree Add Interface Socket Operator - * \{ */ - -static bNodeSocket *ntree_get_active_interface_socket(const ListBase *lb) -{ - LISTBASE_FOREACH (bNodeSocket *, socket, lb) { - if (socket->flag & SELECT) { - return socket; - } - } - return nullptr; -} - -static int ntree_socket_add_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - - PointerRNA ntree_ptr; - RNA_id_pointer_create((ID *)ntree, &ntree_ptr); - - const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out"); - ListBase *sockets = (in_out == SOCK_IN) ? &ntree->inputs : &ntree->outputs; - - const char *default_name = (in_out == SOCK_IN) ? DATA_("Input") : DATA_("Output"); - bNodeSocket *active_sock = ntree_get_active_interface_socket(sockets); - - bNodeSocket *sock; - if (active_sock) { - /* Insert a copy of the active socket right after it. */ - sock = blender::bke::ntreeInsertSocketInterface( - ntree, in_out, active_sock->idname, active_sock->next, active_sock->name); - /* XXX this only works for actual sockets, not interface templates! */ - // nodeSocketCopyValue(sock, &ntree_ptr, active_sock, &ntree_ptr); - } - else { - /* XXX TODO: define default socket type for a tree! */ - sock = ntreeAddSocketInterface(ntree, in_out, "NodeSocketFloat", default_name); - } - - /* Deactivate sockets. */ - LISTBASE_FOREACH (bNodeSocket *, socket_iter, sockets) { - socket_iter->flag &= ~SELECT; - } - /* Make the new socket selected. */ - sock->flag |= SELECT; - - ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree); - - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); - - return OPERATOR_FINISHED; -} - -void NODE_OT_tree_socket_add(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Node Tree Interface Socket"; - ot->description = "Add an input or output to the active node tree"; - ot->idname = "NODE_OT_tree_socket_add"; - - /* api callbacks */ - ot->exec = ntree_socket_add_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Node-Tree Remove Interface Socket Operator - * \{ */ - -static int ntree_socket_remove_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out"); - - bNodeSocket *iosock = ntree_get_active_interface_socket(in_out == SOCK_IN ? &ntree->inputs : - &ntree->outputs); - if (iosock == nullptr) { - return OPERATOR_CANCELLED; - } - - /* Preferably next socket becomes active, otherwise try previous socket. */ - bNodeSocket *active_sock = (iosock->next ? iosock->next : iosock->prev); - ntreeRemoveSocketInterface(ntree, iosock); - - /* Set active socket. */ - if (active_sock) { - active_sock->flag |= SELECT; - } - - ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); - - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); - - return OPERATOR_FINISHED; -} - -void NODE_OT_tree_socket_remove(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Remove Node Tree Interface Socket"; - ot->description = "Remove an input or output from the active node tree"; - ot->idname = "NODE_OT_tree_socket_remove"; - - /* api callbacks */ - ot->exec = ntree_socket_remove_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Node-Tree Change Interface Socket Type Operator - * \{ */ - -static int ntree_socket_change_type_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out"); - const bNodeSocketType *socket_type = rna_node_socket_type_from_enum( - RNA_enum_get(op->ptr, "socket_type")); - ListBase *sockets = (in_out == SOCK_IN) ? &ntree->inputs : &ntree->outputs; - - Main *main = CTX_data_main(C); - - bNodeSocket *iosock = ntree_get_active_interface_socket(sockets); - if (iosock == nullptr) { - return OPERATOR_CANCELLED; - } - - /* The type remains the same, so we don't need to change anything. */ - if (iosock->typeinfo == socket_type) { - return OPERATOR_FINISHED; - } - - blender::bke::nodeModifySocketType(ntree, nullptr, iosock, socket_type->idname); - - /* Need the extra update here because the loop above does not check for valid links in the node - * group we're currently editing. */ - BKE_ntree_update_tag_interface(ntree); - - /* Deactivate sockets. */ - LISTBASE_FOREACH (bNodeSocket *, socket_iter, sockets) { - socket_iter->flag &= ~SELECT; - } - /* Make the new socket active. */ - iosock->flag |= SELECT; - - ED_node_tree_propagate_change(C, main, ntree); - - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); - - return OPERATOR_FINISHED; -} - -static bool socket_change_poll_type(void *userdata, bNodeSocketType *socket_type) -{ - /* Check if the node tree supports the socket type. */ - bNodeTreeType *ntreetype = (bNodeTreeType *)userdata; - if (ntreetype->valid_socket_type && !ntreetype->valid_socket_type(ntreetype, socket_type)) { - return false; - } - - /* Only use basic socket types for this enum. */ - if (socket_type->subtype != PROP_NONE) { - return false; - } - - if (!U.experimental.use_rotation_socket && socket_type->type == SOCK_ROTATION) { - return false; - } - - return true; -} - -static const EnumPropertyItem *socket_change_type_itemf(bContext *C, - PointerRNA * /*ptr*/, - PropertyRNA * /*prop*/, - bool *r_free) -{ - if (!C) { - return DummyRNA_NULL_items; - } - - SpaceNode *snode = CTX_wm_space_node(C); - if (!snode || !snode->edittree) { - return DummyRNA_NULL_items; - } - - return rna_node_socket_type_itemf(snode->edittree->typeinfo, socket_change_poll_type, r_free); -} - -void NODE_OT_tree_socket_change_type(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Change Node Tree Interface Socket Type"; - ot->description = "Change the type of an input or output of the active node tree"; - ot->idname = "NODE_OT_tree_socket_change_type"; - - /* api callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = ntree_socket_change_type_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); - prop = RNA_def_enum(ot->srna, "socket_type", DummyRNA_DEFAULT_items, 0, "Socket Type", ""); - RNA_def_enum_funcs(prop, socket_change_type_itemf); - ot->prop = prop; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Node-Tree Change Interface Socket Subtype Operator - * \{ */ - -static int ntree_socket_change_subtype_exec(bContext *C, wmOperator *op) -{ - Main *main = CTX_data_main(C); - const int socket_subtype = RNA_enum_get(op->ptr, "socket_subtype"); - - PointerRNA io_socket_ptr = CTX_data_pointer_get_type( - C, "interface_socket", &RNA_NodeSocketInterface); - bNodeSocket *io_socket = static_cast(io_socket_ptr.data); - if (!io_socket) { - return OPERATOR_CANCELLED; - } - - bNodeTree &node_tree = *reinterpret_cast(io_socket_ptr.owner_id); - - ListBase *sockets; - if (node_tree.interface_inputs().contains(io_socket)) { - sockets = &node_tree.inputs; - } - else if (node_tree.interface_outputs().contains(io_socket)) { - sockets = &node_tree.outputs; - } - else { - /* The interface socket should be in the inputs or outputs. */ - BLI_assert_unreachable(); - return OPERATOR_CANCELLED; - } - - nodeModifySocketTypeStatic(&node_tree, nullptr, io_socket, io_socket->type, socket_subtype); - - /* Deactivate sockets. */ - LISTBASE_FOREACH (bNodeSocket *, socket_iter, sockets) { - socket_iter->flag &= ~SELECT; - } - /* Make the new socket active. */ - io_socket->flag |= SELECT; - - BKE_ntree_update_tag_interface(&node_tree); - ED_node_tree_propagate_change(C, main, &node_tree); - - return OPERATOR_FINISHED; -} - -static Set socket_type_get_subtypes(const eNodeSocketDatatype type) -{ - switch (type) { - case SOCK_FLOAT: - return {PROP_PERCENTAGE, - PROP_FACTOR, - PROP_ANGLE, - PROP_TIME, - PROP_TIME_ABSOLUTE, - PROP_DISTANCE, - PROP_NONE}; - case SOCK_INT: - return {PROP_PERCENTAGE, PROP_FACTOR, PROP_NONE}; - case SOCK_VECTOR: - return {PROP_TRANSLATION, - /* Direction doesn't seem to work. */ - // PROP_DIRECTION, - PROP_VELOCITY, - PROP_ACCELERATION, - PROP_EULER, - PROP_XYZ, - PROP_NONE}; - default: - return {}; - } -} - -static const EnumPropertyItem *socket_change_subtype_itemf(bContext *C, - PointerRNA * /*ptr*/, - PropertyRNA * /*prop*/, - bool *r_free) -{ - if (!C) { - return DummyRNA_NULL_items; - } - SpaceNode *snode = CTX_wm_space_node(C); - if (!snode || !snode->edittree) { - return DummyRNA_NULL_items; - } - - PointerRNA active_socket_ptr = CTX_data_pointer_get_type( - C, "interface_socket", &RNA_NodeSocketInterface); - const bNodeSocket *active_socket = static_cast(active_socket_ptr.data); - if (!active_socket) { - return DummyRNA_NULL_items; - } - - const Set subtypes = socket_type_get_subtypes(eNodeSocketDatatype(active_socket->type)); - if (subtypes.is_empty()) { - return DummyRNA_NULL_items; - } - - EnumPropertyItem *items = nullptr; - int items_count = 0; - for (const EnumPropertyItem *item = rna_enum_property_subtype_items; item->name != nullptr; - item++) { - if (subtypes.contains(item->value)) { - RNA_enum_item_add(&items, &items_count, item); - } - } - - if (items_count == 0) { - return DummyRNA_NULL_items; - } - - RNA_enum_item_end(&items, &items_count); - *r_free = true; - return items; -} - -static bool ntree_socket_change_subtype_poll(bContext *C) -{ - if (!ED_operator_node_editable(C)) { - return false; - } - PointerRNA io_socket_ptr = CTX_data_pointer_get_type( - C, "interface_socket", &RNA_NodeSocketInterface); - const bNodeSocket *io_socket = static_cast(io_socket_ptr.data); - if (!io_socket) { - return false; - } - return !socket_type_get_subtypes(eNodeSocketDatatype(io_socket->type)).is_empty(); -} - -void NODE_OT_tree_socket_change_subtype(wmOperatorType *ot) -{ - ot->name = "Change Node Tree Socket Subtype"; - ot->description = "Change the subtype of a socket of the active node tree"; - ot->idname = "NODE_OT_tree_socket_change_subtype"; - - ot->invoke = WM_menu_invoke; - ot->exec = ntree_socket_change_subtype_exec; - ot->poll = ntree_socket_change_subtype_poll; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - ot->prop = RNA_def_enum( - ot->srna, "socket_subtype", DummyRNA_DEFAULT_items, 0, "Socket Subtype", ""); - RNA_def_enum_funcs(ot->prop, socket_change_subtype_itemf); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Node-Tree Move Interface Socket Operator - * \{ */ - -static const EnumPropertyItem move_direction_items[] = { - {1, "UP", 0, "Up", ""}, - {2, "DOWN", 0, "Down", ""}, - {0, nullptr, 0, nullptr, nullptr}, -}; - -static int ntree_socket_move_exec(bContext *C, wmOperator *op) -{ - SpaceNode *snode = CTX_wm_space_node(C); - bNodeTree *ntree = snode->edittree; - int direction = RNA_enum_get(op->ptr, "direction"); - - const eNodeSocketInOut in_out = (eNodeSocketInOut)RNA_enum_get(op->ptr, "in_out"); - ListBase *sockets = in_out == SOCK_IN ? &ntree->inputs : &ntree->outputs; - - bNodeSocket *iosock = ntree_get_active_interface_socket(sockets); - - if (iosock == nullptr) { - return OPERATOR_CANCELLED; - } - - switch (direction) { - case 1: { /* up */ - bNodeSocket *before = iosock->prev; - BLI_remlink(sockets, iosock); - if (before) { - BLI_insertlinkbefore(sockets, before, iosock); - } - else { - BLI_addhead(sockets, iosock); - } - break; - } - case 2: { /* down */ - bNodeSocket *after = iosock->next; - BLI_remlink(sockets, iosock); - if (after) { - BLI_insertlinkafter(sockets, after, iosock); - } - else { - BLI_addtail(sockets, iosock); - } - break; - } - } - - BKE_ntree_update_tag_interface(ntree); - ED_node_tree_propagate_change(C, CTX_data_main(C), ntree); - - WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr); - - return OPERATOR_FINISHED; -} - -void NODE_OT_tree_socket_move(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Move Node Tree Socket"; - ot->description = "Move a socket up or down in the active node tree's interface"; - ot->idname = "NODE_OT_tree_socket_move"; - - /* api callbacks */ - ot->exec = ntree_socket_move_exec; - ot->poll = ED_operator_node_editable; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - RNA_def_enum(ot->srna, "direction", move_direction_items, 1, "Direction", ""); - RNA_def_enum(ot->srna, "in_out", rna_enum_node_socket_in_out_items, SOCK_IN, "Socket Type", ""); -} - -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Node Shader Script Update * \{ */ diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc index c2a5136dd60..02b6bf144cc 100644 --- a/source/blender/editors/space_node/node_group.cc +++ b/source/blender/editors/space_node/node_group.cc @@ -907,9 +907,9 @@ static bool prefer_node_for_interface_name(const bNode &node) return node.is_group() || node.is_group_input() || node.is_group_output(); } -static bNodeSocket *add_interface_from_socket(const bNodeTree &original_tree, - bNodeTree &tree_for_interface, - const bNodeSocket &socket) +static bNodeTreeInterfaceSocket *add_interface_from_socket(const bNodeTree &original_tree, + bNodeTree &tree_for_interface, + const bNodeSocket &socket) { /* The "example socket" has to have the same `in_out` status as the new interface socket. */ const bNodeSocket &socket_for_io = find_socket_to_use_for_interface(original_tree, socket); @@ -917,11 +917,8 @@ static bNodeSocket *add_interface_from_socket(const bNodeTree &original_tree, const bNodeSocket &socket_for_name = prefer_node_for_interface_name(socket.owner_node()) ? socket : socket_for_io; - return bke::ntreeAddSocketInterfaceFromSocketWithName(&tree_for_interface, - &node_for_io, - &socket_for_io, - socket_for_io.idname, - socket_for_name.name); + return bke::node_interface::add_interface_socket_from_node( + tree_for_interface, node_for_io, socket_for_io, socket_for_io.idname, socket_for_name.name); } static void update_nested_node_refs_after_moving_nodes_into_group( @@ -1011,12 +1008,12 @@ static void node_group_make_insert_selected(const bContext &C, bNode *from_node; /* All the links that came from the socket on the unselected node. */ Vector links; - const bNodeSocket *interface_socket; + const bNodeTreeInterfaceSocket *interface_socket; }; struct OutputLinkInfo { bNodeLink *link; - const bNodeSocket *interface_socket; + const bNodeTreeInterfaceSocket *interface_socket; }; /* Map from single non-selected output sockets to potentially many selected input sockets. */ @@ -1049,6 +1046,9 @@ static void node_group_make_insert_selected(const bContext &C, if (!info.interface_socket) { info.interface_socket = add_interface_from_socket(ntree, group, *link->tosock); } + else { + links_to_remove.add(link); + } } } for (bNodeSocket *output_socket : node->output_sockets()) { @@ -1065,7 +1065,14 @@ static void node_group_make_insert_selected(const bContext &C, internal_links_to_move.add(link); continue; } - output_links.append({link, add_interface_from_socket(ntree, group, *link->fromsock)}); + bNodeTreeInterfaceSocket *io_socket = add_interface_from_socket( + ntree, group, *link->fromsock); + if (io_socket) { + output_links.append({link, io_socket}); + } + else { + links_to_remove.add(link); + } } } } @@ -1073,7 +1080,7 @@ static void node_group_make_insert_selected(const bContext &C, struct NewInternalLinkInfo { bNode *node; bNodeSocket *socket; - const bNodeSocket *interface_socket; + const bNodeTreeInterfaceSocket *interface_socket; }; const bool expose_visible = nodes_to_move.size() == 1; @@ -1088,9 +1095,11 @@ static void node_group_make_insert_selected(const bContext &C, if (socket->is_directly_linked()) { continue; } - const bNodeSocket *io_socket = bke::ntreeAddSocketInterfaceFromSocket( - &group, node, socket); - new_internal_links.append({node, socket, io_socket}); + const bNodeTreeInterfaceSocket *io_socket = + bke::node_interface::add_interface_socket_from_node(group, *node, *socket); + if (io_socket) { + new_internal_links.append({node, socket, io_socket}); + } } }; expose_sockets(node->input_sockets()); @@ -1168,8 +1177,9 @@ static void node_group_make_insert_selected(const bContext &C, /* Handle links to the new group inputs. */ for (const auto item : input_links.items()) { - const char *interface_identifier = item.value.interface_socket->identifier; - bNodeSocket *input_socket = node_group_input_find_socket(input_node, interface_identifier); + const StringRefNull interface_identifier = item.value.interface_socket->identifier; + bNodeSocket *input_socket = node_group_input_find_socket(input_node, + interface_identifier.c_str()); for (bNodeLink *link : item.value.links) { /* Move the link into the new group, connected from the input node to the original socket. */ @@ -1185,20 +1195,21 @@ static void node_group_make_insert_selected(const bContext &C, /* Handle links to new group outputs. */ for (const OutputLinkInfo &info : output_links) { /* Create a new link inside of the group. */ - const char *io_identifier = info.interface_socket->identifier; - bNodeSocket *output_sock = node_group_output_find_socket(output_node, io_identifier); + const StringRefNull io_identifier = info.interface_socket->identifier; + bNodeSocket *output_sock = node_group_output_find_socket(output_node, io_identifier.c_str()); nodeAddLink(&group, info.link->fromnode, info.link->fromsock, output_node, output_sock); } /* Handle new links inside the group. */ for (const NewInternalLinkInfo &info : new_internal_links) { - const char *io_identifier = info.interface_socket->identifier; + const StringRefNull io_identifier = info.interface_socket->identifier; if (info.socket->in_out == SOCK_IN) { - bNodeSocket *input_socket = node_group_input_find_socket(input_node, io_identifier); + bNodeSocket *input_socket = node_group_input_find_socket(input_node, io_identifier.c_str()); nodeAddLink(&group, input_node, input_socket, info.node, info.socket); } else { - bNodeSocket *output_socket = node_group_output_find_socket(output_node, io_identifier); + bNodeSocket *output_socket = node_group_output_find_socket(output_node, + io_identifier.c_str()); nodeAddLink(&group, info.node, info.socket, output_node, output_socket); } } @@ -1212,8 +1223,9 @@ static void node_group_make_insert_selected(const bContext &C, /* Add new links to inputs outside of the group. */ for (const auto item : input_links.items()) { - const char *interface_identifier = item.value.interface_socket->identifier; - bNodeSocket *group_node_socket = node_group_find_input_socket(gnode, interface_identifier); + const StringRefNull interface_identifier = item.value.interface_socket->identifier; + bNodeSocket *group_node_socket = node_group_find_input_socket(gnode, + interface_identifier.c_str()); nodeAddLink(&ntree, item.value.from_node, item.key, gnode, group_node_socket); } diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index cf23d1b6f8e..7aed32c8ae0 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -378,12 +378,6 @@ void NODE_OT_switch_view_update(wmOperatorType *ot); void NODE_OT_clipboard_copy(wmOperatorType *ot); void NODE_OT_clipboard_paste(wmOperatorType *ot); -void NODE_OT_tree_socket_add(wmOperatorType *ot); -void NODE_OT_tree_socket_remove(wmOperatorType *ot); -void NODE_OT_tree_socket_change_type(wmOperatorType *ot); -void NODE_OT_tree_socket_change_subtype(wmOperatorType *ot); -void NODE_OT_tree_socket_move(wmOperatorType *ot); - void NODE_OT_shader_script_update(wmOperatorType *ot); void NODE_OT_viewer_border(wmOperatorType *ot); diff --git a/source/blender/editors/space_node/node_ops.cc b/source/blender/editors/space_node/node_ops.cc index caabbb40d96..2b04473bddd 100644 --- a/source/blender/editors/space_node/node_ops.cc +++ b/source/blender/editors/space_node/node_ops.cc @@ -106,12 +106,6 @@ void node_operatortypes() WM_operatortype_append(NODE_OT_switch_view_update); - WM_operatortype_append(NODE_OT_tree_socket_add); - WM_operatortype_append(NODE_OT_tree_socket_remove); - WM_operatortype_append(NODE_OT_tree_socket_change_type); - WM_operatortype_append(NODE_OT_tree_socket_change_subtype); - WM_operatortype_append(NODE_OT_tree_socket_move); - WM_operatortype_append(NODE_OT_cryptomatte_layer_add); WM_operatortype_append(NODE_OT_cryptomatte_layer_remove); } diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 2a9244070a3..8601c46da3a 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -2259,8 +2259,8 @@ bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_ bke::nodeDeclarationEnsure(&ntree, &node); const nodes::NodeDeclaration *node_decl = node.declaration(); if (node_decl != nullptr) { - Span socket_decls = (in_out == SOCK_IN) ? node_decl->inputs : - node_decl->outputs; + Span socket_decls = (in_out == SOCK_IN) ? node_decl->inputs : + node_decl->outputs; int index; LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, sockets, index) { const nodes::SocketDeclaration &socket_decl = *socket_decls[index]; diff --git a/source/blender/editors/space_node/node_shader_preview.cc b/source/blender/editors/space_node/node_shader_preview.cc index 8e5125ff948..2edd51c2341 100644 --- a/source/blender/editors/space_node/node_shader_preview.cc +++ b/source/blender/editors/space_node/node_shader_preview.cc @@ -376,7 +376,8 @@ static void connect_nested_node_to_node(const Span treepath, output_node->flag |= NODE_DO_OUTPUT; } - ntreeAddSocketInterface(nested_nt, SOCK_OUT, nested_socket_iter->idname, route_name); + nested_nt->tree_interface.add_socket( + route_name, "", nested_socket_iter->idname, NODE_INTERFACE_SOCKET_OUTPUT, nullptr); BKE_ntree_update_main_tree(G.pr_main, nested_nt, nullptr); bNodeSocket *out_socket = blender::bke::node_find_enabled_input_socket(*output_node, route_name); diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc index e5024190c7e..d51aefdcfbf 100644 --- a/source/blender/editors/space_node/node_templates.cc +++ b/source/blender/editors/space_node/node_templates.cc @@ -26,6 +26,8 @@ #include "BKE_context.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_node_runtime.hh" +#include "BKE_node_tree_interface.hh" #include "BKE_node_tree_update.h" #include "RNA_access.hh" @@ -315,7 +317,6 @@ static Vector ui_node_link_items(NodeLinkArg *arg, /* XXX this should become a callback for node types! */ if (arg->node_type->type == NODE_GROUP) { bNodeTree *ngroup; - int i; for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup; ngroup = (bNodeTree *)ngroup->id.next) @@ -327,7 +328,6 @@ static Vector ui_node_link_items(NodeLinkArg *arg, } } - i = 0; for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup; ngroup = (bNodeTree *)ngroup->id.next) { @@ -337,17 +337,19 @@ static Vector ui_node_link_items(NodeLinkArg *arg, continue; } - ListBase *lb = (in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs); - bNodeSocket *stemp; - int index; - for (stemp = (bNodeSocket *)lb->first, index = 0; stemp; stemp = stemp->next, index++, i++) { + Span iosockets = (in_out == SOCK_IN ? + ngroup->interface_cache().inputs : + ngroup->interface_cache().outputs); + for (const int index : iosockets.index_range()) { + bNodeTreeInterfaceSocket *iosock = iosockets[index]; NodeLinkItem item; item.socket_index = index; /* NOTE: int stemp->type is not fully reliable, not used for node group * interface sockets. use the typeinfo->type instead. */ - item.socket_type = stemp->typeinfo->type; - item.socket_name = stemp->name; + const bNodeSocketType *typeinfo = iosock->socket_typeinfo(); + item.socket_type = typeinfo->type; + item.socket_name = iosock->name; item.node_name = ngroup->id.name + 2; item.ngroup = ngroup; @@ -361,10 +363,10 @@ static Vector ui_node_link_items(NodeLinkArg *arg, r_node_decl.emplace(NodeDeclaration()); blender::nodes::build_node_declaration(*arg->node_type, *r_node_decl); - Span socket_decls = (in_out == SOCK_IN) ? r_node_decl->inputs : - r_node_decl->outputs; + Span socket_decls = (in_out == SOCK_IN) ? r_node_decl->inputs : + r_node_decl->outputs; int index = 0; - for (const SocketDeclarationPtr &socket_decl_ptr : socket_decls) { + for (const SocketDeclaration *socket_decl_ptr : socket_decls) { const SocketDeclaration &socket_decl = *socket_decl_ptr; NodeLinkItem item; item.socket_index = index++; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 4dc8fdff619..441daa223fd 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -34,6 +34,7 @@ namespace blender::bke { class bNodeTreeRuntime; class bNodeRuntime; class bNodeSocketRuntime; +struct bNodeTreeInterfaceCache; } // namespace blender::bke namespace blender::bke { class bNodeTreeZones; @@ -187,6 +188,7 @@ typedef struct bNodeSocket { #ifdef __cplusplus bool is_hidden() const; bool is_available() const; + bool is_panel_collapsed() const; bool is_visible() const; bool is_multi_input() const; bool is_input() const; @@ -304,8 +306,30 @@ typedef enum eNodeSocketFlag { * Only used for geometry nodes. Don't show the socket value in the modifier interface. */ SOCK_HIDE_IN_MODIFIER = (1 << 13), + /** The panel containing the socket is collapsed. */ + SOCK_PANEL_COLLAPSED = (1 << 14), } eNodeSocketFlag; +typedef enum eNodePanelFlag { + /* Panel is collapsed (user setting). */ + NODE_PANEL_COLLAPSED = (1 << 0), + /* The parent panel is collapsed. */ + NODE_PANEL_PARENT_COLLAPSED = (1 << 1), +} eNodePanelFlag; + +typedef struct bNodePanelState { + /* Unique identifier for validating state against panels in node declaration. */ + short uid; + /* eNodePanelFlag */ + char flag; + char _pad; + +#ifdef __cplusplus + bool is_collapsed() const; + bool is_parent_collapsed() const; +#endif +} bNodePanelState; + typedef struct bNode { struct bNode *next, *prev; @@ -383,7 +407,9 @@ typedef struct bNode { /** Custom user-defined color. */ float color[3]; - char _pad2[4]; + /** Panel states for this node instance. */ + int num_panel_states; + bNodePanelState *panel_states_array; bNodeRuntimeHandle *runtime; @@ -423,6 +449,8 @@ typedef struct bNode { bNodeSocket &output_by_identifier(blender::StringRef identifier); /** If node is frame, will return all children nodes. */ blender::Span direct_children_in_frame() const; + blender::Span panel_states() const; + blender::MutableSpan panel_states(); /** Node tree this node belongs to. */ const bNodeTree &owner_tree() const; #endif @@ -636,7 +664,7 @@ typedef struct bNodeTree { * Warning! Don't make links to these sockets, input/output nodes are used for that. * These sockets are used only for generating external interfaces. */ - ListBase inputs, outputs; + ListBase inputs_legacy DNA_DEPRECATED, outputs_legacy DNA_DEPRECATED; bNodeTreeInterface tree_interface; @@ -735,12 +763,11 @@ typedef struct bNodeTree { const bNode *group_output_node() const; /** Get all input nodes of the node group. */ blender::Span group_input_nodes() const; - /** Inputs and outputs of the entire node group. */ - blender::Span interface_inputs() const; - blender::Span interface_outputs() const; /** Zones in the node tree. Currently there are only simulation zones in geometry nodes. */ const blender::bke::bNodeTreeZones *zones() const; + + const blender::bke::bNodeTreeInterfaceCache &interface_cache() const; #endif } bNodeTree; diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index 444cfd655f5..3349879a804 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -183,6 +183,8 @@ DNA_STRUCT_RENAME_ELEM(bPoseChannel, scaleIn, scale_in_x) DNA_STRUCT_RENAME_ELEM(bPoseChannel, scaleOut, scale_out_x) DNA_STRUCT_RENAME_ELEM(bPoseChannel, scale_in_y, scale_in_z) DNA_STRUCT_RENAME_ELEM(bPoseChannel, scale_out_y, scale_out_z) +DNA_STRUCT_RENAME_ELEM(bNodeTree, inputs, inputs_legacy) +DNA_STRUCT_RENAME_ELEM(bNodeTree, outputs, outputs_legacy) DNA_STRUCT_RENAME_ELEM(bSameVolumeConstraint, flag, free_axis) DNA_STRUCT_RENAME_ELEM(bSound, name, filepath) DNA_STRUCT_RENAME_ELEM(bTheme, tact, space_action) diff --git a/source/blender/makesrna/intern/rna_node_socket.cc b/source/blender/makesrna/intern/rna_node_socket.cc index fad7ce99f55..6c3a0e25f5f 100644 --- a/source/blender/makesrna/intern/rna_node_socket.cc +++ b/source/blender/makesrna/intern/rna_node_socket.cc @@ -50,10 +50,6 @@ const EnumPropertyItem rna_enum_node_socket_type_items[] = { extern FunctionRNA rna_NodeSocket_draw_func; extern FunctionRNA rna_NodeSocket_draw_color_func; -extern FunctionRNA rna_NodeSocketInterface_draw_func; -extern FunctionRNA rna_NodeSocketInterface_draw_color_func; -extern FunctionRNA rna_NodeSocketInterface_init_socket_func; -extern FunctionRNA rna_NodeSocketInterface_from_socket_func; /* ******** Node Socket ******** */ @@ -285,236 +281,6 @@ static void rna_NodeSocket_hide_set(PointerRNA *ptr, bool value) } } -static void rna_NodeSocketInterface_draw(bContext *C, uiLayout *layout, PointerRNA *ptr) -{ - bNodeSocket *stemp = static_cast(ptr->data); - ParameterList list; - FunctionRNA *func; - - if (!stemp->typeinfo) { - return; - } - - func = &rna_NodeSocketInterface_draw_func; /* RNA_struct_find_function(&ptr, "draw"); */ - - RNA_parameter_list_create(&list, ptr, func); - RNA_parameter_set_lookup(&list, "context", &C); - RNA_parameter_set_lookup(&list, "layout", &layout); - stemp->typeinfo->ext_interface.call(C, ptr, func, &list); - - RNA_parameter_list_free(&list); -} - -static void rna_NodeSocketInterface_draw_color(bContext *C, PointerRNA *ptr, float *r_color) -{ - bNodeSocket *sock = static_cast(ptr->data); - ParameterList list; - FunctionRNA *func; - void *ret; - - if (!sock->typeinfo) { - return; - } - - func = - &rna_NodeSocketInterface_draw_color_func; /* RNA_struct_find_function(&ptr, "draw_color"); */ - - RNA_parameter_list_create(&list, ptr, func); - RNA_parameter_set_lookup(&list, "context", &C); - sock->typeinfo->ext_interface.call(C, ptr, func, &list); - - RNA_parameter_get_lookup(&list, "color", &ret); - copy_v4_v4(r_color, static_cast(ret)); - - RNA_parameter_list_free(&list); -} - -static void rna_NodeSocketInterface_init_socket(bNodeTree *ntree, - const bNodeSocket *interface_socket, - bNode *node, - bNodeSocket *sock, - const char *data_path) -{ - PointerRNA ptr, node_ptr, sock_ptr; - ParameterList list; - FunctionRNA *func; - - if (!interface_socket->typeinfo) { - return; - } - - RNA_pointer_create( - &ntree->id, &RNA_NodeSocketInterface, const_cast(interface_socket), &ptr); - RNA_pointer_create(&ntree->id, &RNA_Node, node, &node_ptr); - RNA_pointer_create(&ntree->id, &RNA_NodeSocket, sock, &sock_ptr); - // RNA_struct_find_function(&ptr, "init_socket"); - func = &rna_NodeSocketInterface_init_socket_func; - - RNA_parameter_list_create(&list, &ptr, func); - RNA_parameter_set_lookup(&list, "node", &node_ptr); - RNA_parameter_set_lookup(&list, "socket", &sock_ptr); - RNA_parameter_set_lookup(&list, "data_path", &data_path); - interface_socket->typeinfo->ext_interface.call(nullptr, &ptr, func, &list); - - RNA_parameter_list_free(&list); -} - -static void rna_NodeSocketInterface_from_socket(bNodeTree *ntree, - bNodeSocket *interface_socket, - const bNode *node, - const bNodeSocket *sock) -{ - PointerRNA ptr, node_ptr, sock_ptr; - ParameterList list; - FunctionRNA *func; - - if (!interface_socket->typeinfo) { - return; - } - - RNA_pointer_create(&ntree->id, &RNA_NodeSocketInterface, interface_socket, &ptr); - RNA_pointer_create(&ntree->id, &RNA_Node, const_cast(node), &node_ptr); - RNA_pointer_create(&ntree->id, &RNA_NodeSocket, const_cast(sock), &sock_ptr); - // RNA_struct_find_function(&ptr, "from_socket"); - func = &rna_NodeSocketInterface_from_socket_func; - - RNA_parameter_list_create(&list, &ptr, func); - RNA_parameter_set_lookup(&list, "node", &node_ptr); - RNA_parameter_set_lookup(&list, "socket", &sock_ptr); - interface_socket->typeinfo->ext_interface.call(nullptr, &ptr, func, &list); - - RNA_parameter_list_free(&list); -} - -static bool rna_NodeSocketInterface_unregister(Main * /*bmain*/, StructRNA *type) -{ - bNodeSocketType *st = static_cast(RNA_struct_blender_type_get(type)); - if (!st) { - return false; - } - - RNA_struct_free_extension(type, &st->ext_interface); - - RNA_struct_free(&BLENDER_RNA, type); - - /* update while blender is running */ - WM_main_add_notifier(NC_NODE | NA_EDITED, nullptr); - return true; -} - -static StructRNA *rna_NodeSocketInterface_register(Main * /*bmain*/, - ReportList * /*reports*/, - void *data, - const char *identifier, - StructValidateFunc validate, - StructCallbackFunc call, - StructFreeFunc free) -{ - bNodeSocketType *st, dummy_st; - bNodeSocket dummy_sock; - PointerRNA dummy_sock_ptr; - bool have_function[4]; - - /* setup dummy socket & socket type to store static properties in */ - memset(&dummy_st, 0, sizeof(bNodeSocketType)); - - memset(&dummy_sock, 0, sizeof(bNodeSocket)); - dummy_sock.typeinfo = &dummy_st; - RNA_pointer_create(nullptr, &RNA_NodeSocketInterface, &dummy_sock, &dummy_sock_ptr); - - /* validate the python class */ - if (validate(&dummy_sock_ptr, data, have_function) != 0) { - return nullptr; - } - - /* check if we have registered this socket type before */ - st = nodeSocketTypeFind(dummy_st.idname); - if (st) { - /* basic socket type registered by a socket class before. */ - } - else { - /* create a new node socket type */ - st = static_cast(MEM_mallocN(sizeof(bNodeSocketType), "node socket type")); - memcpy(st, &dummy_st, sizeof(dummy_st)); - - nodeRegisterSocketType(st); - } - - st->free_self = (void (*)(bNodeSocketType * stype)) MEM_freeN; - - /* if RNA type is already registered, unregister first */ - if (st->ext_interface.srna) { - StructRNA *srna = st->ext_interface.srna; - RNA_struct_free_extension(srna, &st->ext_interface); - RNA_struct_free(&BLENDER_RNA, srna); - } - st->ext_interface.srna = RNA_def_struct_ptr(&BLENDER_RNA, identifier, &RNA_NodeSocketInterface); - st->ext_interface.data = data; - st->ext_interface.call = call; - st->ext_interface.free = free; - RNA_struct_blender_type_set(st->ext_interface.srna, st); - - st->interface_draw = (have_function[0]) ? rna_NodeSocketInterface_draw : nullptr; - st->interface_draw_color = (have_function[1]) ? rna_NodeSocketInterface_draw_color : nullptr; - st->interface_init_socket = (have_function[2]) ? rna_NodeSocketInterface_init_socket : nullptr; - st->interface_from_socket = (have_function[3]) ? rna_NodeSocketInterface_from_socket : nullptr; - - /* update while blender is running */ - WM_main_add_notifier(NC_NODE | NA_EDITED, nullptr); - - return st->ext_interface.srna; -} - -static StructRNA *rna_NodeSocketInterface_refine(PointerRNA *ptr) -{ - bNodeSocket *sock = static_cast(ptr->data); - - if (sock->typeinfo && sock->typeinfo->ext_interface.srna) { - return sock->typeinfo->ext_interface.srna; - } - else { - return &RNA_NodeSocketInterface; - } -} - -static char *rna_NodeSocketInterface_path(const PointerRNA *ptr) -{ - const bNodeTree *ntree = reinterpret_cast(ptr->owner_id); - const bNodeSocket *sock = static_cast(ptr->data); - int socketindex; - - socketindex = BLI_findindex(&ntree->inputs, sock); - if (socketindex != -1) { - return BLI_sprintfN("inputs[%d]", socketindex); - } - - socketindex = BLI_findindex(&ntree->outputs, sock); - if (socketindex != -1) { - return BLI_sprintfN("outputs[%d]", socketindex); - } - - return nullptr; -} - -static IDProperty **rna_NodeSocketInterface_idprops(PointerRNA *ptr) -{ - bNodeSocket *sock = static_cast(ptr->data); - return &sock->prop; -} - -static void rna_NodeSocketInterface_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr) -{ - bNodeTree *ntree = reinterpret_cast(ptr->owner_id); - bNodeSocket *stemp = static_cast(ptr->data); - - if (!stemp->typeinfo) { - return; - } - - BKE_ntree_update_tag_interface(ntree); - ED_node_tree_propagate_change(nullptr, bmain, ntree); -} - /* ******** Standard Node Socket Base Types ******** */ static void rna_NodeSocketStandard_draw(ID *id, @@ -537,25 +303,6 @@ static void rna_NodeSocketStandard_draw_color( sock->typeinfo->draw_color(C, &ptr, nodeptr, r_color); } -static void rna_NodeSocketInterfaceStandard_draw(ID *id, - bNodeSocket *sock, - bContext *C, - uiLayout *layout) -{ - PointerRNA ptr; - RNA_pointer_create(id, &RNA_NodeSocketInterface, sock, &ptr); - sock->typeinfo->interface_draw(C, layout, &ptr); -} - -static void rna_NodeSocketInterfaceStandard_draw_color(ID *id, - bNodeSocket *sock, - bContext *C, - float r_color[4]) -{ - PointerRNA ptr; - RNA_pointer_create(id, &RNA_NodeSocketInterface, sock, &ptr); - sock->typeinfo->interface_draw_color(C, &ptr, r_color); -} /* ******** Node Socket Subtypes ******** */ void rna_NodeSocketStandard_float_range( @@ -807,135 +554,6 @@ static void rna_def_node_socket(BlenderRNA *brna) RNA_def_function_output(func, parm); } -static void rna_def_node_socket_interface(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - PropertyRNA *parm; - FunctionRNA *func; - - static float default_draw_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; - - srna = RNA_def_struct(brna, "NodeSocketInterface", nullptr); - RNA_def_struct_ui_text(srna, "Node Socket Template", "Parameters to define node sockets"); - /* XXX Using bNodeSocket DNA for templates is a compatibility hack. - * This allows to keep the inputs/outputs lists in bNodeTree working for earlier versions - * and at the same time use them for socket templates in groups. - */ - RNA_def_struct_sdna(srna, "bNodeSocket"); - RNA_def_struct_refine_func(srna, "rna_NodeSocketInterface_refine"); - RNA_def_struct_path_func(srna, "rna_NodeSocketInterface_path"); - RNA_def_struct_idprops_func(srna, "rna_NodeSocketInterface_idprops"); - RNA_def_struct_register_funcs( - srna, "rna_NodeSocketInterface_register", "rna_NodeSocketInterface_unregister", nullptr); - - prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); - RNA_def_property_ui_text(prop, "Name", "Socket name"); - RNA_def_struct_name_property(srna, prop); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "identifier", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, nullptr, "identifier"); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Identifier", "Unique identifier for mapping sockets"); - - prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, nullptr, "description"); - RNA_def_property_ui_text(prop, "Tooltip", "Socket tooltip"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "is_output", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_funcs(prop, "rna_NodeSocket_is_output_get", nullptr); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Is Output", "True if the socket is an output, otherwise input"); - - prop = RNA_def_property(srna, "hide_value", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "flag", SOCK_HIDE_VALUE); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text( - prop, "Hide Value", "Hide the socket input value even when the socket is not connected"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "hide_in_modifier", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "flag", SOCK_HIDE_IN_MODIFIER); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, - "Hide in Modifier", - "Don't show the input value in the geometry nodes modifier interface"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_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 used by the geometry nodes modifier to create an attribute output"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "default_attribute_name", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, nullptr, "default_attribute_name"); - RNA_def_property_ui_text(prop, - "Default Attribute", - "The attribute name used by default when the node group is used by a " - "geometry nodes modifier"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - /* registration */ - prop = RNA_def_property(srna, "bl_socket_idname", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, nullptr, "typeinfo->idname"); - RNA_def_property_flag(prop, PROP_REGISTER); - RNA_def_property_ui_text(prop, "ID Name", ""); - - prop = RNA_def_property(srna, "bl_label", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, nullptr, "typeinfo->label"); - RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); - RNA_def_property_ui_text(prop, "Type Label", "Label to display for the socket type in the UI"); - - prop = RNA_def_property(srna, "bl_subtype_label", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, nullptr, "typeinfo->subtype_label"); - RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); - RNA_def_property_ui_text( - prop, "Subtype Label", "Label to display for the socket subtype in the UI"); - - func = RNA_def_function(srna, "draw", nullptr); - RNA_def_function_ui_description(func, "Draw template settings"); - RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL); - parm = RNA_def_pointer(func, "context", "Context", "", ""); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_property(func, "layout", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(parm, "UILayout"); - RNA_def_property_ui_text(parm, "Layout", "Layout in the UI"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - - func = RNA_def_function(srna, "draw_color", nullptr); - RNA_def_function_ui_description(func, "Color of the socket icon"); - RNA_def_function_flag(func, FUNC_REGISTER); - parm = RNA_def_pointer(func, "context", "Context", "", ""); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_float_array( - func, "color", 4, default_draw_color, 0.0f, 1.0f, "Color", "", 0.0f, 1.0f); - RNA_def_function_output(func, parm); - - func = RNA_def_function(srna, "init_socket", nullptr); - RNA_def_function_ui_description(func, "Initialize a node socket instance"); - RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE); - parm = RNA_def_pointer(func, "node", "Node", "Node", "Node of the socket to initialize"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); - parm = RNA_def_pointer(func, "socket", "NodeSocket", "Socket", "Socket to initialize"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); - parm = RNA_def_string( - func, "data_path", nullptr, 0, "Data Path", "Path to specialized socket data"); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - - func = RNA_def_function(srna, "from_socket", nullptr); - RNA_def_function_ui_description(func, "Setup template parameters from an existing socket"); - RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE); - parm = RNA_def_pointer(func, "node", "Node", "Node", "Node of the original socket"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); - parm = RNA_def_pointer(func, "socket", "NodeSocket", "Socket", "Original socket"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); -} - static void rna_def_node_socket_standard(BlenderRNA *brna) { /* XXX Workaround: Registered functions are not exposed in python by bpy, @@ -947,9 +565,8 @@ static void rna_def_node_socket_standard(BlenderRNA *brna) */ StructRNA *srna; - PropertyRNA *prop; - FunctionRNA *func; PropertyRNA *parm; + FunctionRNA *func; static float default_draw_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; @@ -987,626 +604,6 @@ static void rna_def_node_socket_standard(BlenderRNA *brna) parm = RNA_def_float_array( func, "color", 4, default_draw_color, 0.0f, 1.0f, "Color", "", 0.0f, 1.0f); RNA_def_function_output(func, parm); - - /* Note: Legacy socket interface below. - * The new interface RNA is defined in a separate file, - * the NodeSocketInterface struct will be replaced. */ - - srna = RNA_def_struct(brna, "NodeSocketInterfaceStandard", "NodeSocketInterface"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - /* for easier type comparison in python */ - prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, nullptr, "typeinfo->type"); - RNA_def_property_enum_items(prop, rna_enum_node_socket_type_items); - RNA_def_property_enum_default(prop, SOCK_FLOAT); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Type", "Data type"); - - func = RNA_def_function(srna, "draw", "rna_NodeSocketInterfaceStandard_draw"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID); - RNA_def_function_ui_description(func, "Draw template settings"); - parm = RNA_def_pointer(func, "context", "Context", "", ""); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_property(func, "layout", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(parm, "UILayout"); - RNA_def_property_ui_text(parm, "Layout", "Layout in the UI"); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - - func = RNA_def_function(srna, "draw_color", "rna_NodeSocketInterfaceStandard_draw_color"); - RNA_def_function_flag(func, FUNC_USE_SELF_ID); - RNA_def_function_ui_description(func, "Color of the socket icon"); - parm = RNA_def_pointer(func, "context", "Context", "", ""); - RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - parm = RNA_def_float_array( - func, "color", 4, default_draw_color, 0.0f, 1.0f, "Color", "", 0.0f, 1.0f); - RNA_def_function_output(func, parm); -} - -static void rna_def_node_socket_float(BlenderRNA *brna, - const char *idname, - const char *interface_idname, - PropertySubType subtype) -{ - StructRNA *srna; - PropertyRNA *prop; - float value_default; - - /* choose sensible common default based on subtype */ - switch (subtype) { - case PROP_FACTOR: - value_default = 1.0f; - break; - case PROP_PERCENTAGE: - value_default = 100.0f; - break; - default: - value_default = 0.0f; - break; - } - - srna = RNA_def_struct(brna, idname, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Float Node Socket", "Floating-point number socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueFloat", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_FLOAT, subtype); - RNA_def_property_float_sdna(prop, nullptr, "value"); - RNA_def_property_float_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_float_range"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text( - srna, "Float Node Socket Interface", "Floating-point number socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueFloat", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_FLOAT, subtype); - RNA_def_property_float_sdna(prop, nullptr, "value"); - RNA_def_property_float_default(prop, value_default); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_float_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_float_range"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "min_value", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, nullptr, "min"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Minimum Value", "Minimum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "max_value", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, nullptr, "max"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Maximum Value", "Maximum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); -} - -static void rna_def_node_socket_int(BlenderRNA *brna, - const char *identifier, - const char *interface_idname, - PropertySubType subtype) -{ - StructRNA *srna; - PropertyRNA *prop; - int value_default; - - /* choose sensible common default based on subtype */ - switch (subtype) { - case PROP_FACTOR: - value_default = 1; - break; - case PROP_PERCENTAGE: - value_default = 100; - break; - default: - value_default = 0; - break; - } - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Integer Node Socket", "Integer number socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueInt", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_INT, subtype); - RNA_def_property_int_sdna(prop, nullptr, "value"); - RNA_def_property_int_default(prop, value_default); - RNA_def_property_int_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_int_range"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Integer Node Socket Interface", "Integer number socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueInt", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_INT, subtype); - RNA_def_property_int_sdna(prop, nullptr, "value"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_int_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_int_range"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "min_value", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, nullptr, "min"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Minimum Value", "Minimum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "max_value", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, nullptr, "max"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Maximum Value", "Maximum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); -} - -static void rna_def_node_socket_bool(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Boolean Node Socket", "Boolean value socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueBoolean", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "value", 1); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Boolean Node Socket Interface", "Boolean value socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueBoolean", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "value", 1); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); -} - -static void rna_def_node_socket_rotation(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Rotation Node Socket", "Rotation value socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueRotation", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_FLOAT, PROP_EULER); - RNA_def_property_float_sdna(prop, nullptr, "value_euler"); - // RNA_def_property_array(prop, 3); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text( - srna, "Rotation Node Socket Interface", "Rotation value socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueRotation", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_FLOAT, PROP_EULER); - RNA_def_property_float_sdna(prop, nullptr, "value_euler"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); -} - -static void rna_def_node_socket_vector(BlenderRNA *brna, - const char *identifier, - const char *interface_idname, - PropertySubType subtype) -{ - StructRNA *srna; - PropertyRNA *prop; - const float *value_default; - - /* choose sensible common default based on subtype */ - switch (subtype) { - case PROP_DIRECTION: { - static const float default_direction[3] = {0.0f, 0.0f, 1.0f}; - value_default = default_direction; - break; - } - default: { - static const float default_vector[3] = {0.0f, 0.0f, 0.0f}; - value_default = default_vector; - break; - } - } - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Vector Node Socket", "3D vector socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueVector", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_FLOAT, subtype); - RNA_def_property_float_sdna(prop, nullptr, "value"); - RNA_def_property_float_array_default(prop, value_default); - RNA_def_property_float_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_vector_range"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Vector Node Socket Interface", "3D vector socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueVector", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_FLOAT, subtype); - RNA_def_property_float_sdna(prop, nullptr, "value"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_float_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_vector_range"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "min_value", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, nullptr, "min"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Minimum Value", "Minimum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - prop = RNA_def_property(srna, "max_value", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, nullptr, "max"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Maximum Value", "Maximum value"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); -} - -static void rna_def_node_socket_color(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Color Node Socket", "RGBA color socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueRGBA", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_FLOAT, PROP_COLOR); - RNA_def_property_float_sdna(prop, nullptr, "value"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Color Node Socket Interface", "RGBA color socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueRGBA", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_FLOAT, PROP_COLOR); - RNA_def_property_float_sdna(prop, nullptr, "value"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); -} - -static void rna_def_node_socket_string(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "String Node Socket", "String socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueString", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, nullptr, "value"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "String Node Socket Interface", "String socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueString", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, nullptr, "value"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - - RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); -} - -static void rna_def_node_socket_shader(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Shader Node Socket", "Shader socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Shader Node Socket Interface", "Shader socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); -} - -static void rna_def_node_socket_virtual(BlenderRNA *brna, const char *identifier) -{ - StructRNA *srna; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Virtual Node Socket", "Virtual socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); -} - -static void rna_def_node_socket_object(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Object Node Socket", "Object socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueObject", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Object"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update( - prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Object Node Socket Interface", "Object socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueObject", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Object"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); -} - -static void rna_def_node_socket_image(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Image Node Socket", "Image socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueImage", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Image"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update( - prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Image Node Socket Interface", "Image socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueImage", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Image"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); -} - -static void rna_def_node_socket_geometry(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Geometry Node Socket", "Geometry socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Geometry Node Socket Interface", "Geometry socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); -} - -static void rna_def_node_socket_collection(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Collection Node Socket", "Collection socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueCollection", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Collection"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update( - prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Collection Node Socket Interface", "Collection socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueCollection", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Collection"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); -} - -static void rna_def_node_socket_texture(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Texture Node Socket", "Texture socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueTexture", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Texture"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update( - prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Texture Node Socket Interface", "Texture socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueTexture", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Texture"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); -} - -static void rna_def_node_socket_material(BlenderRNA *brna, - const char *identifier, - const char *interface_idname) -{ - StructRNA *srna; - PropertyRNA *prop; - - srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); - RNA_def_struct_ui_text(srna, "Material Node Socket", "Material socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Material"); - RNA_def_property_pointer_funcs( - prop, nullptr, nullptr, nullptr, "rna_NodeSocketMaterial_default_value_poll"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update( - prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - - /* socket interface */ - srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard"); - RNA_def_struct_ui_text(srna, "Material Node Socket Interface", "Material socket of a node"); - RNA_def_struct_sdna(srna, "bNodeSocket"); - - RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "default_value"); - - prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, nullptr, "value"); - RNA_def_property_struct_type(prop, "Material"); - RNA_def_property_pointer_funcs( - prop, nullptr, nullptr, nullptr, "rna_NodeSocketMaterial_default_value_poll"); - RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT); - RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); } /* Common functions for all builtin socket interface types. */ @@ -1649,6 +646,29 @@ static void rna_def_node_tree_interface_socket_builtin(StructRNA *srna) RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); } +static void rna_def_node_socket_float(BlenderRNA *brna, + const char *identifier, + PropertySubType subtype) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Float Node Socket", "Floating-point number socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueFloat", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_FLOAT, subtype); + RNA_def_property_float_sdna(prop, nullptr, "value"); + RNA_def_property_float_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_float_range"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); +} + static void rna_def_node_socket_interface_float(BlenderRNA *brna, const char *identifier, PropertySubType subtype) @@ -1703,6 +723,44 @@ static void rna_def_node_socket_interface_float(BlenderRNA *brna, rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_int(BlenderRNA *brna, + const char *identifier, + PropertySubType subtype) +{ + StructRNA *srna; + PropertyRNA *prop; + int value_default; + + /* choose sensible common default based on subtype */ + switch (subtype) { + case PROP_FACTOR: + value_default = 1; + break; + case PROP_PERCENTAGE: + value_default = 100; + break; + default: + value_default = 0; + break; + } + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Integer Node Socket", "Integer number socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueInt", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_INT, subtype); + RNA_def_property_int_sdna(prop, nullptr, "value"); + RNA_def_property_int_default(prop, value_default); + RNA_def_property_int_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_int_range"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); +} + static void rna_def_node_socket_interface_int(BlenderRNA *brna, const char *identifier, PropertySubType subtype) @@ -1741,6 +799,26 @@ static void rna_def_node_socket_interface_int(BlenderRNA *brna, rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_bool(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Boolean Node Socket", "Boolean value socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueBoolean", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "value", 1); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); +} + static void rna_def_node_socket_interface_bool(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1763,6 +841,27 @@ static void rna_def_node_socket_interface_bool(BlenderRNA *brna, const char *ide rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_rotation(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Rotation Node Socket", "Rotation value socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueRotation", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_FLOAT, PROP_EULER); + RNA_def_property_float_sdna(prop, nullptr, "value_euler"); + // RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); +} + static void rna_def_node_socket_interface_rotation(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1786,6 +885,45 @@ static void rna_def_node_socket_interface_rotation(BlenderRNA *brna, const char rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_vector(BlenderRNA *brna, + const char *identifier, + PropertySubType subtype) +{ + StructRNA *srna; + PropertyRNA *prop; + const float *value_default; + + /* choose sensible common default based on subtype */ + switch (subtype) { + case PROP_DIRECTION: { + static const float default_direction[3] = {0.0f, 0.0f, 1.0f}; + value_default = default_direction; + break; + } + default: { + static const float default_vector[3] = {0.0f, 0.0f, 0.0f}; + value_default = default_vector; + break; + } + } + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Vector Node Socket", "3D vector socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueVector", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_FLOAT, subtype); + RNA_def_property_float_sdna(prop, nullptr, "value"); + RNA_def_property_float_array_default(prop, value_default); + RNA_def_property_float_funcs(prop, nullptr, nullptr, "rna_NodeSocketStandard_vector_range"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); +} + static void rna_def_node_socket_interface_vector(BlenderRNA *brna, const char *identifier, PropertySubType subtype) @@ -1824,6 +962,26 @@ static void rna_def_node_socket_interface_vector(BlenderRNA *brna, rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_color(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Color Node Socket", "RGBA color socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueRGBA", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_sdna(prop, nullptr, "value"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); +} + static void rna_def_node_socket_interface_color(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1846,6 +1004,26 @@ static void rna_def_node_socket_interface_color(BlenderRNA *brna, const char *id rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_string(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "String Node Socket", "String socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueString", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, nullptr, "value"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + + RNA_def_struct_sdna_from(srna, "bNodeSocket", nullptr); +} + static void rna_def_node_socket_interface_string(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1868,6 +1046,15 @@ static void rna_def_node_socket_interface_string(BlenderRNA *brna, const char *i rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_shader(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Shader Node Socket", "Shader socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); +} + static void rna_def_node_socket_interface_shader(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1879,6 +1066,27 @@ static void rna_def_node_socket_interface_shader(BlenderRNA *brna, const char *i rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_object(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Object Node Socket", "Object socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueObject", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "value"); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); +} + static void rna_def_node_socket_interface_object(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1901,6 +1109,27 @@ static void rna_def_node_socket_interface_object(BlenderRNA *brna, const char *i rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_image(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Image Node Socket", "Image socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueImage", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "value"); + RNA_def_property_struct_type(prop, "Image"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); +} + static void rna_def_node_socket_interface_image(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1923,6 +1152,15 @@ static void rna_def_node_socket_interface_image(BlenderRNA *brna, const char *id rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_geometry(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Geometry Node Socket", "Geometry socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); +} + static void rna_def_node_socket_interface_geometry(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1934,6 +1172,27 @@ static void rna_def_node_socket_interface_geometry(BlenderRNA *brna, const char rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_collection(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Collection Node Socket", "Collection socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueCollection", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "value"); + RNA_def_property_struct_type(prop, "Collection"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); +} + static void rna_def_node_socket_interface_collection(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1956,6 +1215,27 @@ static void rna_def_node_socket_interface_collection(BlenderRNA *brna, const cha rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_texture(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Texture Node Socket", "Texture socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueTexture", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "value"); + RNA_def_property_struct_type(prop, "Texture"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); +} + static void rna_def_node_socket_interface_texture(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -1978,6 +1258,29 @@ static void rna_def_node_socket_interface_texture(BlenderRNA *brna, const char * rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_material(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Material Node Socket", "Material socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); + + RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "default_value"); + + prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "value"); + RNA_def_property_struct_type(prop, "Material"); + RNA_def_property_pointer_funcs( + prop, nullptr, nullptr, nullptr, "rna_NodeSocketMaterial_default_value_poll"); + RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket"); + RNA_def_property_update( + prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); +} + static void rna_def_node_socket_interface_material(BlenderRNA *brna, const char *identifier) { StructRNA *srna; @@ -2002,10 +1305,18 @@ static void rna_def_node_socket_interface_material(BlenderRNA *brna, const char rna_def_node_tree_interface_socket_builtin(srna); } +static void rna_def_node_socket_virtual(BlenderRNA *brna, const char *identifier) +{ + StructRNA *srna; + + srna = RNA_def_struct(brna, identifier, "NodeSocketStandard"); + RNA_def_struct_ui_text(srna, "Virtual Node Socket", "Virtual socket of a node"); + RNA_def_struct_sdna(srna, "bNodeSocket"); +} + /* Info for generating static subtypes. */ struct bNodeSocketStaticTypeInfo { const char *socket_identifier; - const char *interface_identifier_legacy; const char *interface_identifier; eNodeSocketDatatype type; PropertySubType subtype; @@ -2016,202 +1327,103 @@ struct bNodeSocketStaticTypeInfo { * but makesrna cannot have a dependency on BKE, so this list would have to live in RNA itself, * with BKE etc. accessing the RNA API to get the subtypes info. */ static const bNodeSocketStaticTypeInfo node_socket_subtypes[] = { - {"NodeSocketFloat", - "NodeSocketInterfaceFloat", - "NodeTreeInterfaceSocketFloat", - SOCK_FLOAT, - PROP_NONE}, - {"NodeSocketFloatUnsigned", - "NodeSocketInterfaceFloatUnsigned", - "NodeTreeInterfaceSocketFloatUnsigned", - SOCK_FLOAT, - PROP_UNSIGNED}, + {"NodeSocketFloat", "NodeTreeInterfaceSocketFloat", SOCK_FLOAT, PROP_NONE}, + {"NodeSocketFloatUnsigned", "NodeTreeInterfaceSocketFloatUnsigned", SOCK_FLOAT, PROP_UNSIGNED}, {"NodeSocketFloatPercentage", - "NodeSocketInterfaceFloatPercentage", "NodeTreeInterfaceSocketFloatPercentage", SOCK_FLOAT, PROP_PERCENTAGE}, - {"NodeSocketFloatFactor", - "NodeSocketInterfaceFloatFactor", - "NodeTreeInterfaceSocketFloatFactor", - SOCK_FLOAT, - PROP_FACTOR}, - {"NodeSocketFloatAngle", - "NodeSocketInterfaceFloatAngle", - "NodeTreeInterfaceSocketFloatAngle", - SOCK_FLOAT, - PROP_ANGLE}, - {"NodeSocketFloatTime", - "NodeSocketInterfaceFloatTime", - "NodeTreeInterfaceSocketFloatTime", - SOCK_FLOAT, - PROP_TIME}, + {"NodeSocketFloatFactor", "NodeTreeInterfaceSocketFloatFactor", SOCK_FLOAT, PROP_FACTOR}, + {"NodeSocketFloatAngle", "NodeTreeInterfaceSocketFloatAngle", SOCK_FLOAT, PROP_ANGLE}, + {"NodeSocketFloatTime", "NodeTreeInterfaceSocketFloatTime", SOCK_FLOAT, PROP_TIME}, {"NodeSocketFloatTimeAbsolute", - "NodeSocketInterfaceFloatTimeAbsolute", "NodeTreeInterfaceSocketFloatTimeAbsolute", SOCK_FLOAT, PROP_TIME_ABSOLUTE}, - {"NodeSocketFloatDistance", - "NodeSocketInterfaceFloatDistance", - "NodeTreeInterfaceSocketFloatDistance", - SOCK_FLOAT, - PROP_DISTANCE}, - {"NodeSocketInt", "NodeSocketInterfaceInt", "NodeTreeInterfaceSocketInt", SOCK_INT, PROP_NONE}, - {"NodeSocketIntUnsigned", - "NodeSocketInterfaceIntUnsigned", - "NodeTreeInterfaceSocketIntUnsigned", - SOCK_INT, - PROP_UNSIGNED}, - {"NodeSocketIntPercentage", - "NodeSocketInterfaceIntPercentage", - "NodeTreeInterfaceSocketIntPercentage", - SOCK_INT, - PROP_PERCENTAGE}, - {"NodeSocketIntFactor", - "NodeSocketInterfaceIntFactor", - "NodeTreeInterfaceSocketIntFactor", - SOCK_INT, - PROP_FACTOR}, - {"NodeSocketBool", - "NodeSocketInterfaceBool", - "NodeTreeInterfaceSocketBool", - SOCK_BOOLEAN, - PROP_NONE}, - {"NodeSocketRotation", - "NodeSocketInterfaceRotation", - "NodeTreeInterfaceSocketRotation", - SOCK_ROTATION, - PROP_NONE}, - {"NodeSocketVector", - "NodeSocketInterfaceVector", - "NodeTreeInterfaceSocketVector", - SOCK_VECTOR, - PROP_NONE}, + {"NodeSocketFloatDistance", "NodeTreeInterfaceSocketFloatDistance", SOCK_FLOAT, PROP_DISTANCE}, + {"NodeSocketInt", "NodeTreeInterfaceSocketInt", SOCK_INT, PROP_NONE}, + {"NodeSocketIntUnsigned", "NodeTreeInterfaceSocketIntUnsigned", SOCK_INT, PROP_UNSIGNED}, + {"NodeSocketIntPercentage", "NodeTreeInterfaceSocketIntPercentage", SOCK_INT, PROP_PERCENTAGE}, + {"NodeSocketIntFactor", "NodeTreeInterfaceSocketIntFactor", SOCK_INT, PROP_FACTOR}, + {"NodeSocketBool", "NodeTreeInterfaceSocketBool", SOCK_BOOLEAN, PROP_NONE}, + {"NodeSocketRotation", "NodeTreeInterfaceSocketRotation", SOCK_ROTATION, PROP_NONE}, + {"NodeSocketVector", "NodeTreeInterfaceSocketVector", SOCK_VECTOR, PROP_NONE}, {"NodeSocketVectorTranslation", - "NodeSocketInterfaceVectorTranslation", "NodeTreeInterfaceSocketVectorTranslation", SOCK_VECTOR, PROP_TRANSLATION}, {"NodeSocketVectorDirection", - "NodeSocketInterfaceVectorDirection", "NodeTreeInterfaceSocketVectorDirection", SOCK_VECTOR, PROP_DIRECTION}, {"NodeSocketVectorVelocity", - "NodeSocketInterfaceVectorVelocity", "NodeTreeInterfaceSocketVectorVelocity", SOCK_VECTOR, PROP_VELOCITY}, {"NodeSocketVectorAcceleration", - "NodeSocketInterfaceVectorAcceleration", "NodeTreeInterfaceSocketVectorAcceleration", SOCK_VECTOR, PROP_ACCELERATION}, - {"NodeSocketVectorEuler", - "NodeSocketInterfaceVectorEuler", - "NodeTreeInterfaceSocketVectorEuler", - SOCK_VECTOR, - PROP_EULER}, - {"NodeSocketVectorXYZ", - "NodeSocketInterfaceVectorXYZ", - "NodeTreeInterfaceSocketVectorXYZ", - SOCK_VECTOR, - PROP_XYZ}, - {"NodeSocketColor", - "NodeSocketInterfaceColor", - "NodeTreeInterfaceSocketColor", - SOCK_RGBA, - PROP_NONE}, - {"NodeSocketString", - "NodeSocketInterfaceString", - "NodeTreeInterfaceSocketString", - SOCK_STRING, - PROP_NONE}, - {"NodeSocketShader", - "NodeSocketInterfaceShader", - "NodeTreeInterfaceSocketShader", - SOCK_SHADER, - PROP_NONE}, - {"NodeSocketObject", - "NodeSocketInterfaceObject", - "NodeTreeInterfaceSocketObject", - SOCK_OBJECT, - PROP_NONE}, - {"NodeSocketImage", - "NodeSocketInterfaceImage", - "NodeTreeInterfaceSocketImage", - SOCK_IMAGE, - PROP_NONE}, - {"NodeSocketGeometry", - "NodeSocketInterfaceGeometry", - "NodeTreeInterfaceSocketGeometry", - SOCK_GEOMETRY, - PROP_NONE}, - {"NodeSocketCollection", - "NodeSocketInterfaceCollection", - "NodeTreeInterfaceSocketCollection", - SOCK_COLLECTION, - PROP_NONE}, - {"NodeSocketTexture", - "NodeSocketInterfaceTexture", - "NodeTreeInterfaceSocketTexture", - SOCK_TEXTURE, - PROP_NONE}, - {"NodeSocketMaterial", - "NodeSocketInterfaceMaterial", - "NodeTreeInterfaceSocketMaterial", - SOCK_MATERIAL, - PROP_NONE}, + {"NodeSocketVectorEuler", "NodeTreeInterfaceSocketVectorEuler", SOCK_VECTOR, PROP_EULER}, + {"NodeSocketVectorXYZ", "NodeTreeInterfaceSocketVectorXYZ", SOCK_VECTOR, PROP_XYZ}, + {"NodeSocketColor", "NodeTreeInterfaceSocketColor", SOCK_RGBA, PROP_NONE}, + {"NodeSocketString", "NodeTreeInterfaceSocketString", SOCK_STRING, PROP_NONE}, + {"NodeSocketShader", "NodeTreeInterfaceSocketShader", SOCK_SHADER, PROP_NONE}, + {"NodeSocketObject", "NodeTreeInterfaceSocketObject", SOCK_OBJECT, PROP_NONE}, + {"NodeSocketImage", "NodeTreeInterfaceSocketImage", SOCK_IMAGE, PROP_NONE}, + {"NodeSocketGeometry", "NodeTreeInterfaceSocketGeometry", SOCK_GEOMETRY, PROP_NONE}, + {"NodeSocketCollection", "NodeTreeInterfaceSocketCollection", SOCK_COLLECTION, PROP_NONE}, + {"NodeSocketTexture", "NodeTreeInterfaceSocketTexture", SOCK_TEXTURE, PROP_NONE}, + {"NodeSocketMaterial", "NodeTreeInterfaceSocketMaterial", SOCK_MATERIAL, PROP_NONE}, }; static void rna_def_node_socket_subtypes(BlenderRNA *brna) { for (const bNodeSocketStaticTypeInfo &info : node_socket_subtypes) { const char *identifier = info.socket_identifier; - const char *interface_identifier = info.interface_identifier_legacy; switch (info.type) { case SOCK_FLOAT: - rna_def_node_socket_float(brna, identifier, interface_identifier, info.subtype); + rna_def_node_socket_float(brna, identifier, info.subtype); break; case SOCK_INT: - rna_def_node_socket_int(brna, identifier, interface_identifier, info.subtype); + rna_def_node_socket_int(brna, identifier, info.subtype); break; case SOCK_BOOLEAN: - rna_def_node_socket_bool(brna, identifier, interface_identifier); + rna_def_node_socket_bool(brna, identifier); break; case SOCK_ROTATION: - rna_def_node_socket_rotation(brna, identifier, interface_identifier); + rna_def_node_socket_rotation(brna, identifier); break; case SOCK_VECTOR: - rna_def_node_socket_vector(brna, identifier, interface_identifier, info.subtype); + rna_def_node_socket_vector(brna, identifier, info.subtype); break; case SOCK_RGBA: - rna_def_node_socket_color(brna, identifier, interface_identifier); + rna_def_node_socket_color(brna, identifier); break; case SOCK_STRING: - rna_def_node_socket_string(brna, identifier, interface_identifier); + rna_def_node_socket_string(brna, identifier); break; case SOCK_SHADER: - rna_def_node_socket_shader(brna, identifier, interface_identifier); + rna_def_node_socket_shader(brna, identifier); break; case SOCK_OBJECT: - rna_def_node_socket_object(brna, identifier, interface_identifier); + rna_def_node_socket_object(brna, identifier); break; case SOCK_IMAGE: - rna_def_node_socket_image(brna, identifier, interface_identifier); + rna_def_node_socket_image(brna, identifier); break; case SOCK_GEOMETRY: - rna_def_node_socket_geometry(brna, identifier, interface_identifier); + rna_def_node_socket_geometry(brna, identifier); break; case SOCK_COLLECTION: - rna_def_node_socket_collection(brna, identifier, interface_identifier); + rna_def_node_socket_collection(brna, identifier); break; case SOCK_TEXTURE: - rna_def_node_socket_texture(brna, identifier, interface_identifier); + rna_def_node_socket_texture(brna, identifier); break; case SOCK_MATERIAL: - rna_def_node_socket_material(brna, identifier, interface_identifier); + rna_def_node_socket_material(brna, identifier); break; case SOCK_CUSTOM: @@ -2283,9 +1495,8 @@ void rna_def_node_socket_interface_subtypes(BlenderRNA *brna) void RNA_def_node_socket_subtypes(BlenderRNA *brna) { rna_def_node_socket(brna); - rna_def_node_socket_interface(brna); - rna_def_node_socket_standard(brna); + rna_def_node_socket_standard(brna); rna_def_node_socket_subtypes(brna); } diff --git a/source/blender/makesrna/intern/rna_node_tree_interface.cc b/source/blender/makesrna/intern/rna_node_tree_interface.cc index 93405dcb3fe..9fa103398dc 100644 --- a/source/blender/makesrna/intern/rna_node_tree_interface.cc +++ b/source/blender/makesrna/intern/rna_node_tree_interface.cc @@ -75,14 +75,12 @@ static char *rna_NodeTreeInterfaceItem_path(const PointerRNA *ptr) } ntree->ensure_topology_cache(); - UNUSED_VARS(item); - // Note: New API, will be enabled after new interface cache is added. - // const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache(); - // for (const int index : cache.items.index_range()) { - // if (cache.items[index] == item) { - // return BLI_sprintfN("interface.ui_items[%d]", index); - // } - // } + const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache(); + for (const int index : cache.items.index_range()) { + if (cache.items[index] == item) { + return BLI_sprintfN("interface.ui_items[%d]", index); + } + } return nullptr; } @@ -109,15 +107,14 @@ static void rna_NodeTreeInterfaceSocket_draw_builtin(ID *id, { bNodeSocketType *typeinfo = interface_socket->socket_typeinfo(); if (typeinfo && typeinfo->interface_draw) { - UNUSED_VARS(id, C, layout); - // Note: New API, will be enabled after typeinfo callbacks change. - // typeinfo->interface_draw(id, interface_socket, C, layout); + typeinfo->interface_draw(id, interface_socket, C, layout); } } -// Note: New API, interface draw callback used after changing callbacks. -static void UNUSED_FUNCTION(rna_NodeTreeInterfaceSocket_draw_custom)( - ID *id, bNodeTreeInterfaceSocket *interface_socket, bContext *C, uiLayout *layout) +static void rna_NodeTreeInterfaceSocket_draw_custom(ID *id, + bNodeTreeInterfaceSocket *interface_socket, + bContext *C, + uiLayout *layout) { bNodeSocketType *typeinfo = nodeSocketTypeFind(interface_socket->socket_type); if (typeinfo == nullptr) { @@ -147,14 +144,11 @@ static void rna_NodeTreeInterfaceSocket_init_socket_builtin( { bNodeSocketType *typeinfo = interface_socket->socket_typeinfo(); if (typeinfo && typeinfo->interface_draw) { - // Note: New API, callback signatures change. - UNUSED_VARS(id, node, socket, data_path); - // typeinfo->interface_init_socket(id, interface_socket, node, socket, data_path); + typeinfo->interface_init_socket(id, interface_socket, node, socket, data_path); } } -// Note: New API, used when callbacks change. -static void UNUSED_FUNCTION(rna_NodeTreeInterfaceSocket_init_socket_custom)( +static void rna_NodeTreeInterfaceSocket_init_socket_custom( ID *id, const bNodeTreeInterfaceSocket *interface_socket, bNode *node, @@ -191,14 +185,11 @@ static void rna_NodeTreeInterfaceSocket_from_socket_builtin( { bNodeSocketType *typeinfo = interface_socket->socket_typeinfo(); if (typeinfo && typeinfo->interface_draw) { - // Note: New API, callback signatures change. - UNUSED_VARS(id, node, socket); - // typeinfo->interface_from_socket(id, interface_socket, node, socket); + typeinfo->interface_from_socket(id, interface_socket, node, socket); } } -// Note: New API, used after callback signatures change. -static void UNUSED_FUNCTION(rna_NodeTreeInterfaceSocket_from_socket_custom)( +static void rna_NodeTreeInterfaceSocket_from_socket_custom( ID *id, bNodeTreeInterfaceSocket *interface_socket, const bNode *node, @@ -275,14 +266,11 @@ static StructRNA *rna_NodeTreeInterfaceSocket_register(Main * /*bmain*/, st->ext_interface.free = free; RNA_struct_blender_type_set(st->ext_interface.srna, st); - // Note: New API callbacks. - // st->interface_draw = (have_function[0]) ? rna_NodeTreeInterfaceSocket_draw_custom : nullptr; - // st->interface_init_socket = (have_function[1]) ? - // rna_NodeTreeInterfaceSocket_init_socket_custom : - // nullptr; - // st->interface_from_socket = (have_function[2]) ? - // rna_NodeTreeInterfaceSocket_from_socket_custom : - // nullptr; + st->interface_draw = (have_function[0]) ? rna_NodeTreeInterfaceSocket_draw_custom : nullptr; + st->interface_init_socket = (have_function[1]) ? rna_NodeTreeInterfaceSocket_init_socket_custom : + nullptr; + st->interface_from_socket = (have_function[2]) ? rna_NodeTreeInterfaceSocket_from_socket_custom : + nullptr; /* Cleanup local dummy type. */ MEM_SAFE_FREE(dummy_socket.socket_type); @@ -654,15 +642,13 @@ static void rna_NodeTreeInterface_items_begin(CollectionPropertyIterator *iter, } ntree->ensure_topology_cache(); - // Note: New API, enabled when interface cache is added. - UNUSED_VARS(iter); - // const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache(); - // rna_iterator_array_begin(iter, - // const_cast(cache.items.data()), - // sizeof(bNodeTreeInterfaceItem *), - // cache.items.size(), - // false, - // nullptr); + const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache(); + rna_iterator_array_begin(iter, + const_cast(cache.items.data()), + sizeof(bNodeTreeInterfaceItem *), + cache.items.size(), + false, + nullptr); } static int rna_NodeTreeInterface_items_length(PointerRNA *ptr) @@ -673,9 +659,7 @@ static int rna_NodeTreeInterface_items_length(PointerRNA *ptr) } ntree->ensure_topology_cache(); - // Note: New API callbacks. - // return ntree->interface_cache().items.size(); - return 0; + return ntree->interface_cache().items.size(); } static int rna_NodeTreeInterface_items_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr) @@ -686,14 +670,12 @@ static int rna_NodeTreeInterface_items_lookup_int(PointerRNA *ptr, int index, Po } ntree->ensure_topology_cache(); - // Note: New API, enabled when interface cache is added. - UNUSED_VARS(index, r_ptr); - // const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache(); - // if (!cache.items.index_range().contains(index)) { - // return false; - // } + const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache(); + if (!cache.items.index_range().contains(index)) { + return false; + } - // RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfaceItem, cache.items[index], r_ptr); + RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfaceItem, cache.items[index], r_ptr); return true; } @@ -707,29 +689,27 @@ static int rna_NodeTreeInterface_items_lookup_string(struct PointerRNA *ptr, } ntree->ensure_topology_cache(); - // Note: New API, enabled when interface cache is added. - UNUSED_VARS(key, r_ptr); - // const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache(); - // for (bNodeTreeInterfaceItem *item : cache.items) { - // switch (item->item_type) { - // case NODE_INTERFACE_SOCKET: { - // bNodeTreeInterfaceSocket *socket = reinterpret_cast(item); - // if (STREQ(socket->name, key)) { - // RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfaceSocket, socket, r_ptr); - // return true; - // } - // break; - // } - // case NODE_INTERFACE_PANEL: { - // bNodeTreeInterfacePanel *panel = reinterpret_cast(item); - // if (STREQ(panel->name, key)) { - // RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfacePanel, panel, r_ptr); - // return true; - // } - // break; - // } - // } - // } + const blender::bke::bNodeTreeInterfaceCache &cache = ntree->interface_cache(); + for (bNodeTreeInterfaceItem *item : cache.items) { + switch (item->item_type) { + case NODE_INTERFACE_SOCKET: { + bNodeTreeInterfaceSocket *socket = reinterpret_cast(item); + if (STREQ(socket->name, key)) { + RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfaceSocket, socket, r_ptr); + return true; + } + break; + } + case NODE_INTERFACE_PANEL: { + bNodeTreeInterfacePanel *panel = reinterpret_cast(item); + if (STREQ(panel->name, key)) { + RNA_pointer_create(ptr->owner_id, &RNA_NodeTreeInterfacePanel, panel, r_ptr); + return true; + } + break; + } + } + } return false; } diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 1af4d4d7958..6946837c403 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -1287,205 +1287,11 @@ static void rna_NodeTree_link_clear(bNodeTree *ntree, Main *bmain, ReportList *r WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); } -static int rna_NodeTree_active_input_get(PointerRNA *ptr) -{ - bNodeTree *ntree = static_cast(ptr->data); - int index = 0; - LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &ntree->inputs, index) { - if (socket->flag & SELECT) { - return index; - } - } - return -1; -} - -static void rna_NodeTree_active_input_set(PointerRNA *ptr, int value) -{ - bNodeTree *ntree = static_cast(ptr->data); - - int index = 0; - LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &ntree->inputs, index) { - SET_FLAG_FROM_TEST(socket->flag, index == value, SELECT); - } -} - -static int rna_NodeTree_active_output_get(PointerRNA *ptr) -{ - bNodeTree *ntree = static_cast(ptr->data); - int index = 0; - LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &ntree->outputs, index) { - if (socket->flag & SELECT) { - return index; - } - } - return -1; -} - -static void rna_NodeTree_active_output_set(PointerRNA *ptr, int value) -{ - bNodeTree *ntree = static_cast(ptr->data); - - int index = 0; - LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &ntree->outputs, index) { - SET_FLAG_FROM_TEST(socket->flag, index == value, SELECT); - } -} - static bool rna_NodeTree_contains_tree(bNodeTree *tree, bNodeTree *sub_tree) { return ntreeContainsTree(tree, sub_tree); } -static bNodeSocket *rna_NodeTree_inputs_new( - bNodeTree *ntree, Main *bmain, ReportList *reports, const char *type, const char *name) -{ - if (!rna_NodeTree_check(ntree, reports)) { - return nullptr; - } - - bNodeSocket *sock = ntreeAddSocketInterface(ntree, SOCK_IN, type, name); - - if (sock == nullptr) { - BKE_report(reports, RPT_ERROR, "Unable to create socket"); - } - else { - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); - } - - return sock; -} - -static bNodeSocket *rna_NodeTree_outputs_new( - bNodeTree *ntree, Main *bmain, ReportList *reports, const char *type, const char *name) -{ - if (!rna_NodeTree_check(ntree, reports)) { - return nullptr; - } - - bNodeSocket *sock = ntreeAddSocketInterface(ntree, SOCK_OUT, type, name); - - if (sock == nullptr) { - BKE_report(reports, RPT_ERROR, "Unable to create socket"); - } - else { - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); - } - - return sock; -} - -static void rna_NodeTree_socket_remove(bNodeTree *ntree, - Main *bmain, - ReportList *reports, - bNodeSocket *sock) -{ - if (!rna_NodeTree_check(ntree, reports)) { - return; - } - - if (BLI_findindex(&ntree->inputs, sock) == -1 && BLI_findindex(&ntree->outputs, sock) == -1) { - BKE_reportf(reports, RPT_ERROR, "Unable to locate socket '%s' in node", sock->identifier); - } - else { - ntreeRemoveSocketInterface(ntree, sock); - - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); - } -} - -static void rna_NodeTree_inputs_clear(bNodeTree *ntree, Main *bmain, ReportList *reports) -{ - if (!rna_NodeTree_check(ntree, reports)) { - return; - } - - LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->inputs) { - ntreeRemoveSocketInterface(ntree, socket); - } - - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); -} - -static void rna_NodeTree_outputs_clear(bNodeTree *ntree, Main *bmain, ReportList *reports) -{ - if (!rna_NodeTree_check(ntree, reports)) { - return; - } - - LISTBASE_FOREACH_MUTABLE (bNodeSocket *, socket, &ntree->outputs) { - ntreeRemoveSocketInterface(ntree, socket); - } - - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); -} - -static void rna_NodeTree_inputs_move(bNodeTree *ntree, Main *bmain, int from_index, int to_index) -{ - if (from_index == to_index) { - return; - } - if (from_index < 0 || to_index < 0) { - return; - } - - bNodeSocket *sock = static_cast(BLI_findlink(&ntree->inputs, from_index)); - if (to_index < from_index) { - bNodeSocket *nextsock = static_cast(BLI_findlink(&ntree->inputs, to_index)); - if (nextsock) { - BLI_remlink(&ntree->inputs, sock); - BLI_insertlinkbefore(&ntree->inputs, nextsock, sock); - } - } - else { - bNodeSocket *prevsock = static_cast(BLI_findlink(&ntree->inputs, to_index)); - if (prevsock) { - BLI_remlink(&ntree->inputs, sock); - BLI_insertlinkafter(&ntree->inputs, prevsock, sock); - } - } - - BKE_ntree_update_tag_interface(ntree); - - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); -} - -static void rna_NodeTree_outputs_move(bNodeTree *ntree, Main *bmain, int from_index, int to_index) -{ - if (from_index == to_index) { - return; - } - if (from_index < 0 || to_index < 0) { - return; - } - - bNodeSocket *sock = static_cast(BLI_findlink(&ntree->outputs, from_index)); - if (to_index < from_index) { - bNodeSocket *nextsock = static_cast(BLI_findlink(&ntree->outputs, to_index)); - if (nextsock) { - BLI_remlink(&ntree->outputs, sock); - BLI_insertlinkbefore(&ntree->outputs, nextsock, sock); - } - } - else { - bNodeSocket *prevsock = static_cast(BLI_findlink(&ntree->outputs, to_index)); - if (prevsock) { - BLI_remlink(&ntree->outputs, sock); - BLI_insertlinkafter(&ntree->outputs, prevsock, sock); - } - } - - BKE_ntree_update_tag_interface(ntree); - - ED_node_tree_propagate_change(nullptr, bmain, ntree); - WM_main_add_notifier(NC_NODE | NA_EDITED, ntree); -} - static void rna_NodeTree_interface_update(bNodeTree *ntree, bContext *C) { Main *bmain = CTX_data_main(C); @@ -10543,57 +10349,6 @@ static void rna_def_nodetree_link_api(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS); } -static void rna_def_node_tree_sockets_api(BlenderRNA *brna, PropertyRNA *cprop, int in_out) -{ - StructRNA *srna; - PropertyRNA *parm; - FunctionRNA *func; - const char *structtype = (in_out == SOCK_IN ? "NodeTreeInputs" : "NodeTreeOutputs"); - const char *uiname = (in_out == SOCK_IN ? "Node Tree Inputs" : "Node Tree Outputs"); - const char *newfunc = (in_out == SOCK_IN ? "rna_NodeTree_inputs_new" : - "rna_NodeTree_outputs_new"); - const char *clearfunc = (in_out == SOCK_IN ? "rna_NodeTree_inputs_clear" : - "rna_NodeTree_outputs_clear"); - const char *movefunc = (in_out == SOCK_IN ? "rna_NodeTree_inputs_move" : - "rna_NodeTree_outputs_move"); - - RNA_def_property_srna(cprop, structtype); - srna = RNA_def_struct(brna, structtype, nullptr); - RNA_def_struct_sdna(srna, "bNodeTree"); - RNA_def_struct_ui_text(srna, uiname, "Collection of Node Tree Sockets"); - - func = RNA_def_function(srna, "new", newfunc); - RNA_def_function_ui_description(func, "Add a socket to this node tree"); - RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS); - parm = RNA_def_string(func, "type", nullptr, MAX_NAME, "Type", "Data type"); - 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, "socket", "NodeSocketInterface", "", "New socket"); - RNA_def_function_return(func, parm); - - func = RNA_def_function(srna, "remove", "rna_NodeTree_socket_remove"); - RNA_def_function_ui_description(func, "Remove a socket from this node tree"); - RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS); - parm = RNA_def_pointer(func, "socket", "NodeSocketInterface", "", "The socket to remove"); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); - - func = RNA_def_function(srna, "clear", clearfunc); - RNA_def_function_ui_description(func, "Remove all sockets from this node tree"); - RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS); - - func = RNA_def_function(srna, "move", movefunc); - RNA_def_function_ui_description(func, "Move a socket to another position"); - RNA_def_function_flag(func, FUNC_USE_MAIN); - parm = RNA_def_int( - func, "from_index", -1, 0, INT_MAX, "From Index", "Index of the socket 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 socket", 0, 10000); - RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); -} - static void rna_def_nodetree(BlenderRNA *brna) { StructRNA *srna; @@ -10607,6 +10362,7 @@ static void rna_def_nodetree(BlenderRNA *brna) ICON_QUESTION, "Undefined", "Undefined type of nodes (can happen e.g. when a linked node tree goes missing)"}, + {NTREE_CUSTOM, "CUSTOM", ICON_NONE, "Custom", "Custom nodes"}, {NTREE_SHADER, "SHADER", ICON_MATERIAL, "Shader", "Shader nodes"}, {NTREE_TEXTURE, "TEXTURE", ICON_TEXTURE, "Texture", "Texture nodes"}, {NTREE_COMPOSIT, "COMPOSITING", ICON_RENDERLAYERS, "Compositing", "Compositing nodes"}, @@ -10668,34 +10424,11 @@ static void rna_def_nodetree(BlenderRNA *brna) "Type", "Node Tree type (deprecated, bl_idname is the actual node tree type identifier)"); - prop = RNA_def_property(srna, "inputs", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, nullptr, "inputs", nullptr); - RNA_def_property_struct_type(prop, "NodeSocketInterface"); + prop = RNA_def_property(srna, "interface", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, nullptr, "tree_interface"); + RNA_def_property_struct_type(prop, "NodeTreeInterface"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Inputs", "Node tree inputs"); - rna_def_node_tree_sockets_api(brna, prop, SOCK_IN); - - prop = RNA_def_property(srna, "active_input", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_funcs( - prop, "rna_NodeTree_active_input_get", "rna_NodeTree_active_input_set", nullptr); - RNA_def_property_ui_text(prop, "Active Input", "Index of the active input"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_NODE, nullptr); - - prop = RNA_def_property(srna, "outputs", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, nullptr, "outputs", nullptr); - RNA_def_property_struct_type(prop, "NodeSocketInterface"); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Outputs", "Node tree outputs"); - rna_def_node_tree_sockets_api(brna, prop, SOCK_OUT); - - prop = RNA_def_property(srna, "active_output", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_funcs( - prop, "rna_NodeTree_active_output_get", "rna_NodeTree_active_output_set", nullptr); - RNA_def_property_ui_text(prop, "Active Output", "Index of the active output"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_NODE, nullptr); - + RNA_def_property_ui_text(prop, "Interface", "Interface declaration for this node tree"); /* exposed as a function for runtime interface type properties */ func = RNA_def_function(srna, "interface_update", "rna_NodeTree_interface_update"); RNA_def_function_ui_description(func, "Updated node group interface"); diff --git a/source/blender/makesrna/intern/rna_ui_api.cc b/source/blender/makesrna/intern/rna_ui_api.cc index 472b4ac5dc0..15965d995ee 100644 --- a/source/blender/makesrna/intern/rna_ui_api.cc +++ b/source/blender/makesrna/intern/rna_ui_api.cc @@ -2085,6 +2085,15 @@ void RNA_api_ui_layout(StructRNA *srna) srna, "template_grease_pencil_layer_tree", "uiTemplateGreasePencilLayerTree"); RNA_def_function_ui_description(func, "View of the active grease pencil layer tree"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); + + func = RNA_def_function(srna, "template_node_tree_declaration", "uiTemplateNodeTreeDeclaration"); + RNA_def_function_ui_description(func, "Show a node tree interface declaration"); + parm = RNA_def_pointer(func, + "interface", + "NodeTreeInterface", + "Node Tree Interface", + "Interface of a node tree to display"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); } #endif diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index f1ef2e800ea..b016519e6ea 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -622,17 +622,19 @@ static void check_property_socket_sync(const Object *ob, ModifierData *md) int geometry_socket_count = 0; - int i; - LISTBASE_FOREACH_INDEX (const bNodeSocket *, socket, &nmd->node_group->inputs, i) { + for (const int i : nmd->node_group->interface_cache().inputs.index_range()) { + const bNodeTreeInterfaceSocket *socket = nmd->node_group->interface_cache().inputs[i]; + const bNodeSocketType *typeinfo = socket->socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; /* The first socket is the special geometry socket for the modifier object. */ - if (i == 0 && socket->type == SOCK_GEOMETRY) { + if (i == 0 && type == SOCK_GEOMETRY) { geometry_socket_count++; continue; } IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties, socket->identifier); if (property == nullptr) { - if (socket->type == SOCK_GEOMETRY) { + if (type == SOCK_GEOMETRY) { geometry_socket_count++; } else { @@ -649,7 +651,10 @@ static void check_property_socket_sync(const Object *ob, ModifierData *md) } if (geometry_socket_count == 1) { - if (((bNodeSocket *)nmd->node_group->inputs.first)->type != SOCK_GEOMETRY) { + const bNodeTreeInterfaceSocket *first_socket = nmd->node_group->interface_cache().inputs[0]; + const bNodeSocketType *typeinfo = first_socket->socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + if (type != SOCK_GEOMETRY) { BKE_modifier_set_error(ob, md, "Node group's geometry input must be the first"); } } @@ -1053,7 +1058,7 @@ static void add_attribute_search_button(const bContext &C, const NodesModifierData &nmd, PointerRNA *md_ptr, const StringRefNull rna_path_attribute_name, - const bNodeSocket &socket, + const bNodeTreeInterfaceSocket &socket, const bool is_output) { if (!nmd.runtime->eval_log) { @@ -1116,10 +1121,13 @@ static void add_attribute_search_or_value_buttons(const bContext &C, uiLayout *layout, const NodesModifierData &nmd, PointerRNA *md_ptr, - const bNodeSocket &socket) + const bNodeTreeInterfaceSocket &socket) { - char socket_id_esc[sizeof(socket.identifier) * 2]; - BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); + const StringRefNull identifier = socket.identifier; + const bNodeSocketType *typeinfo = socket.socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + char socket_id_esc[MAX_NAME * 2]; + BLI_str_escape(socket_id_esc, identifier.c_str(), sizeof(socket_id_esc)); const std::string rna_path = "[\"" + std::string(socket_id_esc) + "\"]"; const std::string rna_path_use_attribute = "[\"" + std::string(socket_id_esc) + nodes::input_use_attribute_suffix() + "\"]"; @@ -1134,7 +1142,7 @@ static void add_attribute_search_or_value_buttons(const bContext &C, uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT); const int use_attribute = RNA_int_get(md_ptr, rna_path_use_attribute.c_str()) != 0; - if (socket.type == SOCK_BOOLEAN && !use_attribute) { + if (type == SOCK_BOOLEAN && !use_attribute) { uiItemL(name_row, "", ICON_NONE); } else { @@ -1142,7 +1150,7 @@ static void add_attribute_search_or_value_buttons(const bContext &C, } uiLayout *prop_row = uiLayoutRow(split, true); - if (socket.type == SOCK_BOOLEAN) { + if (type == SOCK_BOOLEAN) { uiLayoutSetPropSep(prop_row, false); uiLayoutSetAlignment(prop_row, UI_LAYOUT_ALIGN_EXPAND); } @@ -1152,7 +1160,7 @@ static void add_attribute_search_or_value_buttons(const bContext &C, uiItemL(layout, "", ICON_BLANK1); } else { - const char *name = socket.type == SOCK_BOOLEAN ? socket.name : ""; + const char *name = type == SOCK_BOOLEAN ? socket.name : ""; uiItemR(prop_row, md_ptr, rna_path.c_str(), UI_ITEM_NONE, name, ICON_NONE); uiItemDecoratorR(layout, md_ptr, rna_path.c_str(), -1); } @@ -1178,11 +1186,12 @@ static void draw_property_for_socket(const bContext &C, NodesModifierData *nmd, PointerRNA *bmain_ptr, PointerRNA *md_ptr, - const bNodeSocket &socket, + const bNodeTreeInterfaceSocket &socket, const int socket_index) { + const StringRefNull identifier = socket.identifier; /* The property should be created in #MOD_nodes_update_interface with the correct type. */ - IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties, socket.identifier); + IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties, identifier.c_str()); /* IDProperties can be removed with python, so there could be a situation where * there isn't a property for a socket or it doesn't have the correct type. */ @@ -1190,8 +1199,8 @@ static void draw_property_for_socket(const bContext &C, return; } - char socket_id_esc[sizeof(socket.identifier) * 2]; - BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); + char socket_id_esc[MAX_NAME * 2]; + BLI_str_escape(socket_id_esc, identifier.c_str(), sizeof(socket_id_esc)); char rna_path[sizeof(socket_id_esc) + 4]; SNPRINTF(rna_path, "[\"%s\"]", socket_id_esc); @@ -1202,7 +1211,9 @@ static void draw_property_for_socket(const bContext &C, /* Use #uiItemPointerR to draw pointer properties because #uiItemR would not have enough * information about what type of ID to select for editing the values. This is because * pointer IDProperties contain no information about their type. */ - switch (socket.type) { + const bNodeSocketType *typeinfo = socket.socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + switch (type) { case SOCK_OBJECT: { uiItemPointerR(row, md_ptr, rna_path, bmain_ptr, "objects", socket.name, ICON_OBJECT_DATA); break; @@ -1242,10 +1253,11 @@ static void draw_property_for_output_socket(const bContext &C, uiLayout *layout, const NodesModifierData &nmd, PointerRNA *md_ptr, - const bNodeSocket &socket) + const bNodeTreeInterfaceSocket &socket) { - char socket_id_esc[sizeof(socket.identifier) * 2]; - BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); + const StringRefNull identifier = socket.identifier; + char socket_id_esc[MAX_NAME * 2]; + BLI_str_escape(socket_id_esc, identifier.c_str(), sizeof(socket_id_esc)); const std::string rna_path_attribute_name = "[\"" + StringRef(socket_id_esc) + nodes::input_attribute_name_suffix() + "\"]"; @@ -1286,9 +1298,12 @@ static void panel_draw(const bContext *C, Panel *panel) PointerRNA bmain_ptr; RNA_main_pointer_create(bmain, &bmain_ptr); - int socket_index; - LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &nmd->node_group->inputs, socket_index) { - if (!(socket->flag & SOCK_HIDE_IN_MODIFIER)) { + nmd->node_group->ensure_topology_cache(); + + for (const int socket_index : nmd->node_group->interface_cache().inputs.index_range()) { + const bNodeTreeInterfaceSocket *socket = + nmd->node_group->interface_cache().inputs[socket_index]; + if (!(socket->flag & NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER)) { draw_property_for_socket(*C, layout, nmd, &bmain_ptr, ptr, *socket, socket_index); } } @@ -1320,8 +1335,11 @@ static void output_attribute_panel_draw(const bContext *C, Panel *panel) bool has_output_attribute = false; if (nmd->node_group != nullptr && nmd->settings.properties != nullptr) { - LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) { - if (nodes::socket_type_has_attribute_toggle(*socket)) { + for (const bNodeTreeInterfaceSocket *socket : nmd->node_group->interface_cache().outputs) { + const bNodeSocketType *typeinfo = socket->socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : + SOCK_CUSTOM; + if (nodes::socket_type_has_attribute_toggle(type)) { has_output_attribute = true; draw_property_for_output_socket(*C, layout, *nmd, ptr, *socket); } diff --git a/source/blender/nodes/NOD_geometry_nodes_execute.hh b/source/blender/nodes/NOD_geometry_nodes_execute.hh index fc1e575bdc4..c2a5ae0c7c0 100644 --- a/source/blender/nodes/NOD_geometry_nodes_execute.hh +++ b/source/blender/nodes/NOD_geometry_nodes_execute.hh @@ -9,9 +9,11 @@ #include "BLI_multi_value_map.hh" #include "BKE_idprop.hh" +#include "BKE_node.h" struct bNodeTree; struct bNodeSocket; +struct bNodeTreeInterfaceSocket; struct Depsgraph; namespace blender::bke { struct GeometrySet; @@ -33,7 +35,7 @@ StringRef input_attribute_name_suffix(); /** * \return Whether using an attribute to input values of this type is supported. */ -bool socket_type_has_attribute_toggle(const bNodeSocket &socket); +bool socket_type_has_attribute_toggle(eNodeSocketDatatype type); /** * \return Whether using an attribute to input values of this type is supported, and the node @@ -41,10 +43,11 @@ bool socket_type_has_attribute_toggle(const bNodeSocket &socket); */ bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_index); -bool id_property_type_matches_socket(const bNodeSocket &socket, const IDProperty &property); +bool id_property_type_matches_socket(const bNodeTreeInterfaceSocket &socket, + const IDProperty &property); std::unique_ptr id_property_create_from_socket( - const bNodeSocket &socket); + const bNodeTreeInterfaceSocket &socket); bke::GeometrySet execute_geometry_nodes_on_geometry( const bNodeTree &btree, diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 9610a678308..44d2daa6633 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -154,10 +154,18 @@ namespace aal = anonymous_attribute_lifetime; using ImplicitInputValueFn = std::function; +/* Socket or panel declaration. */ +class ItemDeclaration { + public: + virtual ~ItemDeclaration() = default; +}; + +using ItemDeclarationPtr = std::unique_ptr; + /** * Describes a single input or output socket. This is subclassed for different socket types. */ -class SocketDeclaration { +class SocketDeclaration : public ItemDeclaration { public: std::string name; std::string identifier; @@ -479,10 +487,55 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { using SocketDeclarationPtr = std::unique_ptr; +/** + * Describes a panel containing sockets or other panels. + */ +class PanelDeclaration : public ItemDeclaration { + public: + int uid; + std::string name; + std::string description; + std::string translation_context; + bool default_collapsed = false; + int num_items = 0; + + private: + friend NodeDeclarationBuilder; + friend class PanelDeclarationBuilder; + + public: + virtual ~PanelDeclaration() = default; + + void build(bNodePanelState &panel) const; + bool matches(const bNodePanelState &panel) const; + void update_or_build(const bNodePanelState &old_panel, bNodePanelState &new_panel) const; +}; + +class PanelDeclarationBuilder { + protected: + NodeDeclarationBuilder *node_decl_builder_ = nullptr; + PanelDeclaration *decl_; + + friend class NodeDeclarationBuilder; + + public: + PanelDeclarationBuilder &default_closed(bool collapsed) + { + decl_->default_collapsed = collapsed; + return *this; + } +}; + +using PanelDeclarationPtr = std::unique_ptr; + class NodeDeclaration { public: - Vector inputs; - Vector outputs; + /* Combined list of socket and panel declarations. + * This determines order of sockets in the UI and panel content. */ + Vector items; + /* Note: inputs and outputs pointers are owned by the items list. */ + Vector inputs; + Vector outputs; std::unique_ptr anonymous_attribute_relations_; /** Leave the sockets in place, even if they don't match the declaration. Used for dynamic @@ -493,7 +546,7 @@ class NodeDeclaration { friend NodeDeclarationBuilder; bool matches(const bNode &node) const; - Span sockets(eNodeSocketInOut in_out) const; + Span sockets(eNodeSocketInOut in_out) const; const aal::RelationsInNode *anonymous_attribute_relations() const { @@ -732,8 +785,8 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef static_assert(std::is_base_of_v); using Builder = typename DeclType::Builder; - Vector &declarations = in_out == SOCK_IN ? declaration_.inputs : - declaration_.outputs; + Vector &declarations = in_out == SOCK_IN ? declaration_.inputs : + declaration_.outputs; std::unique_ptr socket_decl = std::make_unique(); std::unique_ptr socket_decl_builder = std::make_unique(); @@ -743,7 +796,8 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef socket_decl->name = name; socket_decl->identifier = identifier.is_empty() ? name : identifier; socket_decl->in_out = in_out; - socket_decl_builder->index_ = declarations.append_and_get_index(std::move(socket_decl)); + socket_decl_builder->index_ = declarations.append_and_get_index(socket_decl.get()); + declaration_.items.append(std::move(socket_decl)); Builder &socket_decl_builder_ref = *socket_decl_builder; ((in_out == SOCK_IN) ? input_builders_ : output_builders_) .append(std::move(socket_decl_builder)); @@ -756,7 +810,7 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket(StringRef /** \name #NodeDeclaration Inline Methods * \{ */ -inline Span NodeDeclaration::sockets(eNodeSocketInOut in_out) const +inline Span NodeDeclaration::sockets(eNodeSocketInOut in_out) const { if (in_out == SOCK_IN) { return inputs; diff --git a/source/blender/nodes/NOD_socket.hh b/source/blender/nodes/NOD_socket.hh index 8438a89b635..99219f917f9 100644 --- a/source/blender/nodes/NOD_socket.hh +++ b/source/blender/nodes/NOD_socket.hh @@ -20,6 +20,8 @@ bNodeSocket *node_add_socket_from_template(bNodeTree *ntree, void node_verify_sockets(bNodeTree *ntree, bNode *node, bool do_id_user); +void node_socket_init_default_value_data(eNodeSocketDatatype datatype, int subtype, void **data); +void node_socket_copy_default_value_data(eNodeSocketDatatype datatype, void *to, const void *from); void node_socket_init_default_value(bNodeSocket *sock); void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from); void register_standard_node_socket_types(); diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh index bb585b654b1..9de0aba344e 100644 --- a/source/blender/nodes/NOD_socket_declarations.hh +++ b/source/blender/nodes/NOD_socket_declarations.hh @@ -270,6 +270,7 @@ class ExtendBuilder : public SocketDeclarationBuilder { class Custom : public SocketDeclaration { public: const char *idname_; + std::function init_socket_fn; bNodeSocket &build(bNodeTree &ntree, bNode &node) const override; bool matches(const bNodeSocket &socket) const override; diff --git a/source/blender/nodes/NOD_socket_search_link.hh b/source/blender/nodes/NOD_socket_search_link.hh index 9209f5af8da..9dda1b4f008 100644 --- a/source/blender/nodes/NOD_socket_search_link.hh +++ b/source/blender/nodes/NOD_socket_search_link.hh @@ -146,6 +146,6 @@ class GatherLinkSearchOpParams { void search_link_ops_for_basic_node(GatherLinkSearchOpParams ¶ms); void search_link_ops_for_declarations(GatherLinkSearchOpParams ¶ms, - Span declarations); + Span declarations); } // namespace blender::nodes 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 6841e367587..8c97b03df71 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_repeat_output.cc @@ -96,12 +96,20 @@ void socket_declarations_for_repeat_items(const Span items, { for (const int i : items.index_range()) { const NodeRepeatItem &item = items[i]; - r_declaration.inputs.append(socket_declaration_for_repeat_item(item, SOCK_IN)); - r_declaration.outputs.append( - socket_declaration_for_repeat_item(item, SOCK_OUT, r_declaration.inputs.size() - 1)); + SocketDeclarationPtr input_decl = socket_declaration_for_repeat_item(item, SOCK_IN); + SocketDeclarationPtr output_decl = socket_declaration_for_repeat_item( + item, SOCK_OUT, r_declaration.inputs.size() - 1); + r_declaration.inputs.append(input_decl.get()); + r_declaration.items.append(std::move(input_decl)); + r_declaration.outputs.append(output_decl.get()); + r_declaration.items.append(std::move(output_decl)); } - r_declaration.inputs.append(decl::create_extend_declaration(SOCK_IN)); - r_declaration.outputs.append(decl::create_extend_declaration(SOCK_OUT)); + SocketDeclarationPtr input_extend_decl = decl::create_extend_declaration(SOCK_IN); + SocketDeclarationPtr output_extend_decl = decl::create_extend_declaration(SOCK_OUT); + r_declaration.inputs.append(input_extend_decl.get()); + r_declaration.items.append(std::move(input_extend_decl)); + r_declaration.outputs.append(output_extend_decl.get()); + r_declaration.items.append(std::move(output_extend_decl)); } } // namespace blender::nodes namespace blender::nodes::node_geo_repeat_output_cc { 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 d0e9db57d0d..9934cba5f09 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_input.cc @@ -191,7 +191,8 @@ static void node_declare_dynamic(const bNodeTree &node_tree, delta_time->identifier = "Delta Time"; delta_time->name = DATA_("Delta Time"); delta_time->in_out = SOCK_OUT; - r_declaration.outputs.append(std::move(delta_time)); + r_declaration.outputs.append(delta_time.get()); + r_declaration.items.append(std::move(delta_time)); const NodeGeometrySimulationOutput &storage = *static_cast( output_node->storage); 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 2b651476696..59b9b326a19 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_simulation_output.cc @@ -97,11 +97,19 @@ void socket_declarations_for_simulation_items(const Span ite { for (const int i : items.index_range()) { const NodeSimulationItem &item = items[i]; - r_declaration.inputs.append(socket_declaration_for_simulation_item(item, SOCK_IN, i)); - r_declaration.outputs.append(socket_declaration_for_simulation_item(item, SOCK_OUT, i)); + SocketDeclarationPtr input_decl = socket_declaration_for_simulation_item(item, SOCK_IN, i); + SocketDeclarationPtr output_decl = socket_declaration_for_simulation_item(item, SOCK_OUT, i); + r_declaration.inputs.append(input_decl.get()); + r_declaration.items.append(std::move(input_decl)); + r_declaration.outputs.append(output_decl.get()); + r_declaration.items.append(std::move(output_decl)); } - r_declaration.inputs.append(decl::create_extend_declaration(SOCK_IN)); - r_declaration.outputs.append(decl::create_extend_declaration(SOCK_OUT)); + SocketDeclarationPtr input_extend_decl = decl::create_extend_declaration(SOCK_IN); + SocketDeclarationPtr output_extend_decl = decl::create_extend_declaration(SOCK_OUT); + r_declaration.inputs.append(input_extend_decl.get()); + r_declaration.items.append(std::move(input_extend_decl)); + r_declaration.outputs.append(output_extend_decl.get()); + r_declaration.items.append(std::move(output_extend_decl)); } struct SimulationItemsUniqueNameArgs { diff --git a/source/blender/nodes/intern/geometry_nodes_execute.cc b/source/blender/nodes/intern/geometry_nodes_execute.cc index cdc4259923c..3609516d303 100644 --- a/source/blender/nodes/intern/geometry_nodes_execute.cc +++ b/source/blender/nodes/intern/geometry_nodes_execute.cc @@ -39,10 +39,9 @@ StringRef input_attribute_name_suffix() return "_attribute_name"; } -bool socket_type_has_attribute_toggle(const bNodeSocket &socket) +bool socket_type_has_attribute_toggle(const eNodeSocketDatatype type) { - return ELEM( - socket.type, SOCK_FLOAT, SOCK_VECTOR, SOCK_BOOLEAN, SOCK_RGBA, SOCK_INT, SOCK_ROTATION); + return ELEM(type, SOCK_FLOAT, SOCK_VECTOR, SOCK_BOOLEAN, SOCK_RGBA, SOCK_INT, SOCK_ROTATION); } bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_index) @@ -54,13 +53,16 @@ bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_ind } std::unique_ptr id_property_create_from_socket( - const bNodeSocket &socket) + const bNodeTreeInterfaceSocket &socket) { - switch (socket.type) { + const StringRefNull identifier = socket.identifier; + const bNodeSocketType *typeinfo = socket.socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + switch (type) { case SOCK_FLOAT: { const bNodeSocketValueFloat *value = static_cast( - socket.default_value); - auto property = bke::idprop::create(socket.identifier, value->value); + socket.socket_data); + auto property = bke::idprop::create(identifier, value->value); IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get()); ui_data->base.rna_subtype = value->subtype; ui_data->soft_min = double(value->min); @@ -70,8 +72,8 @@ std::unique_ptr id_property_create_f } case SOCK_INT: { const bNodeSocketValueInt *value = static_cast( - socket.default_value); - auto property = bke::idprop::create(socket.identifier, value->value); + socket.socket_data); + auto property = bke::idprop::create(identifier, value->value); IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property.get()); ui_data->base.rna_subtype = value->subtype; ui_data->soft_min = value->min; @@ -81,9 +83,9 @@ std::unique_ptr id_property_create_f } case SOCK_VECTOR: { const bNodeSocketValueVector *value = static_cast( - socket.default_value); + socket.socket_data); auto property = bke::idprop::create( - socket.identifier, Span{value->value[0], value->value[1], value->value[2]}); + identifier, Span{value->value[0], value->value[1], value->value[2]}); IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get()); ui_data->base.rna_subtype = value->subtype; ui_data->soft_min = double(value->min); @@ -97,9 +99,9 @@ std::unique_ptr id_property_create_f } case SOCK_RGBA: { const bNodeSocketValueRGBA *value = static_cast( - socket.default_value); + socket.socket_data); auto property = bke::idprop::create( - socket.identifier, + identifier, Span{value->value[0], value->value[1], value->value[2], value->value[3]}); IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get()); ui_data->base.rna_subtype = PROP_COLOR; @@ -116,17 +118,17 @@ std::unique_ptr id_property_create_f } case SOCK_BOOLEAN: { const bNodeSocketValueBoolean *value = static_cast( - socket.default_value); - auto property = bke::idprop::create_bool(socket.identifier, value->value); + socket.socket_data); + auto property = bke::idprop::create_bool(identifier, value->value); IDPropertyUIDataBool *ui_data = (IDPropertyUIDataBool *)IDP_ui_data_ensure(property.get()); ui_data->default_value = value->value != 0; return property; } case SOCK_ROTATION: { const bNodeSocketValueRotation *value = static_cast( - socket.default_value); + socket.socket_data); auto property = bke::idprop::create( - socket.identifier, + identifier, Span{value->value_euler[0], value->value_euler[1], value->value_euler[2]}); IDPropertyUIDataFloat *ui_data = reinterpret_cast( IDP_ui_data_ensure(property.get())); @@ -135,8 +137,8 @@ std::unique_ptr id_property_create_f } case SOCK_STRING: { const bNodeSocketValueString *value = static_cast( - socket.default_value); - auto property = bke::idprop::create(socket.identifier, value->value); + socket.socket_data); + auto property = bke::idprop::create(identifier, value->value); IDPropertyUIDataString *ui_data = (IDPropertyUIDataString *)IDP_ui_data_ensure( property.get()); ui_data->default_value = BLI_strdup(value->value); @@ -144,39 +146,46 @@ std::unique_ptr id_property_create_f } case SOCK_OBJECT: { const bNodeSocketValueObject *value = static_cast( - socket.default_value); - auto property = bke::idprop::create(socket.identifier, reinterpret_cast(value->value)); + socket.socket_data); + auto property = bke::idprop::create(identifier, reinterpret_cast(value->value)); IDPropertyUIDataID *ui_data = (IDPropertyUIDataID *)IDP_ui_data_ensure(property.get()); ui_data->id_type = ID_OB; return property; } case SOCK_COLLECTION: { const bNodeSocketValueCollection *value = static_cast( - socket.default_value); - return bke::idprop::create(socket.identifier, reinterpret_cast(value->value)); + socket.socket_data); + return bke::idprop::create(identifier, reinterpret_cast(value->value)); } case SOCK_TEXTURE: { const bNodeSocketValueTexture *value = static_cast( - socket.default_value); - return bke::idprop::create(socket.identifier, reinterpret_cast(value->value)); + socket.socket_data); + return bke::idprop::create(identifier, reinterpret_cast(value->value)); } case SOCK_IMAGE: { const bNodeSocketValueImage *value = static_cast( - socket.default_value); - return bke::idprop::create(socket.identifier, reinterpret_cast(value->value)); + socket.socket_data); + return bke::idprop::create(identifier, reinterpret_cast(value->value)); } case SOCK_MATERIAL: { const bNodeSocketValueMaterial *value = static_cast( - socket.default_value); - return bke::idprop::create(socket.identifier, reinterpret_cast(value->value)); + socket.socket_data); + return bke::idprop::create(identifier, reinterpret_cast(value->value)); } + case SOCK_CUSTOM: + case SOCK_GEOMETRY: + case SOCK_SHADER: + return nullptr; } return nullptr; } -bool id_property_type_matches_socket(const bNodeSocket &socket, const IDProperty &property) +bool id_property_type_matches_socket(const bNodeTreeInterfaceSocket &socket, + const IDProperty &property) { - switch (socket.type) { + const bNodeSocketType *typeinfo = socket.socket_typeinfo(); + const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM; + switch (type) { case SOCK_FLOAT: return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE); case SOCK_INT: @@ -198,6 +207,10 @@ bool id_property_type_matches_socket(const bNodeSocket &socket, const IDProperty case SOCK_IMAGE: case SOCK_MATERIAL: return property.type == IDP_ID; + case SOCK_CUSTOM: + case SOCK_GEOMETRY: + case SOCK_SHADER: + return false; } BLI_assert_unreachable(); return false; @@ -326,20 +339,21 @@ static void initialize_group_input(const bNodeTree &tree, const int input_index, void *r_value) { - const bNodeSocket &io_input = *tree.interface_inputs()[input_index]; - const bNodeSocketType &socket_type = *io_input.typeinfo; - const eNodeSocketDatatype socket_data_type = static_cast(io_input.type); + const bNodeTreeInterfaceSocket &io_input = *tree.interface_cache().inputs[input_index]; + const bNodeSocketType *typeinfo = io_input.socket_typeinfo(); + const eNodeSocketDatatype socket_data_type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : + SOCK_CUSTOM; if (properties == nullptr) { - socket_type.get_geometry_nodes_cpp_value(io_input, r_value); + typeinfo->get_geometry_nodes_cpp_value(io_input.socket_data, r_value); return; } const IDProperty *property = IDP_GetPropertyFromGroup(properties, io_input.identifier); if (property == nullptr) { - socket_type.get_geometry_nodes_cpp_value(io_input, r_value); + typeinfo->get_geometry_nodes_cpp_value(io_input.socket_data, r_value); return; } if (!id_property_type_matches_socket(io_input, *property)) { - socket_type.get_geometry_nodes_cpp_value(io_input, r_value); + typeinfo->get_geometry_nodes_cpp_value(io_input.socket_data, r_value); return; } @@ -349,9 +363,9 @@ static void initialize_group_input(const bNodeTree &tree, } const IDProperty *property_use_attribute = IDP_GetPropertyFromGroup( - properties, (io_input.identifier + input_use_attribute_suffix()).c_str()); + properties, (std::string(io_input.identifier) + input_use_attribute_suffix()).c_str()); const IDProperty *property_attribute_name = IDP_GetPropertyFromGroup( - properties, (io_input.identifier + input_attribute_name_suffix()).c_str()); + properties, (std::string(io_input.identifier) + input_attribute_name_suffix()).c_str()); if (property_use_attribute == nullptr || property_attribute_name == nullptr) { init_socket_cpp_value_from_property(*property, socket_data_type, r_value); return; @@ -365,9 +379,9 @@ static void initialize_group_input(const bNodeTree &tree, return; } fn::GField attribute_field = bke::AttributeFieldInput::Create(attribute_name, - *socket_type.base_cpp_type); + *typeinfo->base_cpp_type); const auto *value_or_field_cpp_type = fn::ValueOrFieldCPPType::get_from_self( - *socket_type.geometry_nodes_cpp_type); + *typeinfo->geometry_nodes_cpp_type); BLI_assert(value_or_field_cpp_type != nullptr); value_or_field_cpp_type->construct_from_field(r_value, std::move(attribute_field)); } @@ -398,7 +412,7 @@ static MultiValueMap find_output_attributes_to const bNode &output_node = *tree.group_output_node(); MultiValueMap outputs_by_domain; for (const bNodeSocket *socket : output_node.input_sockets().drop_front(1).drop_back(1)) { - if (!socket_type_has_attribute_toggle(*socket)) { + if (!socket_type_has_attribute_toggle(eNodeSocketDatatype(socket->type))) { continue; } @@ -421,7 +435,7 @@ static MultiValueMap find_output_attributes_to BLI_assert(value_or_field_type != nullptr); const fn::GField field = value_or_field_type->as_field(value.get()); - const bNodeSocket *interface_socket = (const bNodeSocket *)BLI_findlink(&tree.outputs, index); + const bNodeTreeInterfaceSocket *interface_socket = tree.interface_cache().outputs[index]; const eAttrDomain domain = (eAttrDomain)interface_socket->attribute_domain; OutputAttributeInfo output_info; output_info.field = std::move(field); @@ -572,15 +586,18 @@ bke::GeometrySet execute_geometry_nodes_on_geometry( Vector inputs_to_destruct; int input_index = -1; - for (const int i : btree.interface_inputs().index_range()) { + for (const int i : btree.interface_cache().inputs.index_range()) { input_index++; - const bNodeSocket &interface_socket = *btree.interface_inputs()[i]; - if (interface_socket.type == SOCK_GEOMETRY && input_index == 0) { + const bNodeTreeInterfaceSocket &interface_socket = *btree.interface_cache().inputs[i]; + const bNodeSocketType *typeinfo = interface_socket.socket_typeinfo(); + const eNodeSocketDatatype socket_type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : + SOCK_CUSTOM; + if (socket_type == SOCK_GEOMETRY && input_index == 0) { param_inputs[input_index] = &input_geometry; continue; } - const CPPType *type = interface_socket.typeinfo->geometry_nodes_cpp_type; + const CPPType *type = typeinfo->geometry_nodes_cpp_type; BLI_assert(type != nullptr); void *value = allocator.allocate(type->size(), type->alignment()); initialize_group_input(btree, properties, i, value); @@ -588,8 +605,8 @@ bke::GeometrySet execute_geometry_nodes_on_geometry( inputs_to_destruct.append({type, value}); } - Array output_used_inputs(btree.interface_outputs().size(), true); - for (const int i : btree.interface_outputs().index_range()) { + Array output_used_inputs(btree.interface_cache().outputs.size(), true); + for (const int i : btree.interface_cache().outputs.index_range()) { input_index++; param_inputs[input_index] = &output_used_inputs[i]; } @@ -640,26 +657,31 @@ void update_input_properties_from_node_tree(const bNodeTree &tree, IDProperty &properties) { tree.ensure_topology_cache(); - const Span tree_inputs = tree.interface_inputs(); + const Span tree_inputs = tree.interface_cache().inputs; for (const int i : tree_inputs.index_range()) { - const bNodeSocket &socket = *tree_inputs[i]; + const bNodeTreeInterfaceSocket &socket = *tree_inputs[i]; + const StringRefNull socket_identifier = socket.identifier; + const bNodeSocketType *typeinfo = socket.socket_typeinfo(); + const eNodeSocketDatatype socket_type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : + SOCK_CUSTOM; IDProperty *new_prop = nodes::id_property_create_from_socket(socket).release(); if (new_prop == nullptr) { /* Out of the set of supported input sockets, only * geometry sockets aren't added to the modifier. */ - BLI_assert(socket.type == SOCK_GEOMETRY); + BLI_assert(socket_type == SOCK_GEOMETRY); continue; } new_prop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY; - if (socket.description[0] != '\0') { + if (socket.description && socket.description[0] != '\0') { IDPropertyUIData *ui_data = IDP_ui_data_ensure(new_prop); ui_data->description = BLI_strdup(socket.description); } IDP_AddToGroup(&properties, new_prop); if (old_properties != nullptr) { - const IDProperty *old_prop = IDP_GetPropertyFromGroup(old_properties, socket.identifier); + const IDProperty *old_prop = IDP_GetPropertyFromGroup(old_properties, + socket_identifier.c_str()); if (old_prop != nullptr) { if (nodes::id_property_type_matches_socket(socket, *old_prop)) { /* #IDP_CopyPropertyContent replaces the UI data as well, which we don't (we only @@ -680,9 +702,9 @@ void update_input_properties_from_node_tree(const bNodeTree &tree, } } - if (nodes::socket_type_has_attribute_toggle(socket)) { - const std::string use_attribute_id = socket.identifier + input_use_attribute_suffix(); - const std::string attribute_name_id = socket.identifier + input_attribute_name_suffix(); + if (nodes::socket_type_has_attribute_toggle(eNodeSocketDatatype(socket_type))) { + const std::string use_attribute_id = socket_identifier + input_use_attribute_suffix(); + const std::string attribute_name_id = socket_identifier + input_attribute_name_suffix(); IDPropertyTemplate idprop = {0}; IDProperty *use_attribute_prop = IDP_New( @@ -720,16 +742,20 @@ void update_output_properties_from_node_tree(const bNodeTree &tree, IDProperty &properties) { tree.ensure_topology_cache(); - const Span tree_outputs = tree.interface_outputs(); + const Span tree_outputs = tree.interface_cache().outputs; for (const int i : tree_outputs.index_range()) { - const bNodeSocket &socket = *tree_outputs[i]; - if (!nodes::socket_type_has_attribute_toggle(socket)) { + const bNodeTreeInterfaceSocket &socket = *tree_outputs[i]; + const StringRefNull socket_identifier = socket.identifier; + const bNodeSocketType *typeinfo = socket.socket_typeinfo(); + const eNodeSocketDatatype socket_type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : + SOCK_CUSTOM; + if (!nodes::socket_type_has_attribute_toggle(socket_type)) { continue; } - const std::string idprop_name = socket.identifier + input_attribute_name_suffix(); + const std::string idprop_name = socket_identifier + input_attribute_name_suffix(); IDProperty *new_prop = IDP_NewStringMaxSize("", idprop_name.c_str(), MAX_NAME); - if (socket.description[0] != '\0') { + if (socket.description && socket.description[0] != '\0') { IDPropertyUIData *ui_data = IDP_ui_data_ensure(new_prop); ui_data->description = BLI_strdup(socket.description); } diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index eaf7854798e..9e94e0a3dfc 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -1085,7 +1085,7 @@ static GMutablePointer get_socket_default_value(LinearAllocator<> &allocator, return {}; } void *buffer = allocator.allocate(type->size(), type->alignment()); - typeinfo.get_geometry_nodes_cpp_value(bsocket, buffer); + typeinfo.get_geometry_nodes_cpp_value(bsocket.default_value, buffer); return {type, buffer}; } @@ -2621,9 +2621,11 @@ struct GeometryNodesLazyFunctionGraphBuilder { void build_group_input_node(lf::Graph &lf_graph) { Vector input_cpp_types; - const Span interface_inputs = btree_.interface_inputs(); - for (const bNodeSocket *interface_input : interface_inputs) { - input_cpp_types.append(interface_input->typeinfo->geometry_nodes_cpp_type); + const Span interface_inputs = + btree_.interface_cache().inputs; + for (const bNodeTreeInterfaceSocket *interface_input : interface_inputs) { + const bNodeSocketType *typeinfo = interface_input->socket_typeinfo(); + input_cpp_types.append(typeinfo->geometry_nodes_cpp_type); } /* Create a dummy node for the group inputs. */ @@ -2644,8 +2646,9 @@ struct GeometryNodesLazyFunctionGraphBuilder { { Vector output_cpp_types; auto &debug_info = scope_.construct(); - for (const bNodeSocket *interface_output : btree_.interface_outputs()) { - output_cpp_types.append(interface_output->typeinfo->geometry_nodes_cpp_type); + for (const bNodeTreeInterfaceSocket *interface_output : btree_.interface_cache().outputs) { + const bNodeSocketType *typeinfo = interface_output->socket_typeinfo(); + output_cpp_types.append(typeinfo->geometry_nodes_cpp_type); debug_info.socket_names.append(interface_output->name); } @@ -2800,7 +2803,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { void handle_group_input_node(const bNode &bnode, BuildGraphParams &graph_params) { - for (const int i : btree_.interface_inputs().index_range()) { + for (const int i : btree_.interface_cache().inputs.index_range()) { const bNodeSocket &bsocket = bnode.output_socket(i); lf::OutputSocket &lf_socket = group_input_lf_node_->output(i); graph_params.lf_output_by_bsocket.add_new(&bsocket, &lf_socket); @@ -2813,8 +2816,9 @@ struct GeometryNodesLazyFunctionGraphBuilder { { Vector output_cpp_types; auto &debug_info = scope_.construct(); - for (const bNodeSocket *interface_input : btree_.interface_outputs()) { - output_cpp_types.append(interface_input->typeinfo->geometry_nodes_cpp_type); + for (const bNodeTreeInterfaceSocket *interface_input : btree_.interface_cache().outputs) { + const bNodeSocketType *typeinfo = interface_input->socket_typeinfo(); + output_cpp_types.append(typeinfo->geometry_nodes_cpp_type); debug_info.socket_names.append(interface_input->name); } @@ -3533,7 +3537,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { for (const int i : output_indices.index_range()) { const int output_index = output_indices[i]; mapping_->attribute_set_by_geometry_output.add(output_index, &lf_node.output(i)); - debug_info.output_names.append(btree_.interface_outputs()[output_index]->name); + debug_info.output_names.append(btree_.interface_cache().outputs[output_index]->name); } } @@ -3542,7 +3546,8 @@ struct GeometryNodesLazyFunctionGraphBuilder { */ lf::DummyNode &build_output_usage_input_node(lf::Graph &lf_graph) { - const Span interface_outputs = btree_.interface_outputs(); + const Span interface_outputs = + btree_.interface_cache().outputs; Vector cpp_types; cpp_types.append_n_times(&CPPType::get(), interface_outputs.size()); @@ -3562,7 +3567,8 @@ struct GeometryNodesLazyFunctionGraphBuilder { */ void build_input_usage_output_node(lf::Graph &lf_graph) { - const Span interface_inputs = btree_.interface_inputs(); + const Span interface_inputs = + btree_.interface_cache().inputs; Vector cpp_types; cpp_types.append_n_times(&CPPType::get(), interface_inputs.size()); @@ -3630,7 +3636,7 @@ struct GeometryNodesLazyFunctionGraphBuilder { void build_group_input_usages(BuildGraphParams &graph_params) { const Span group_input_nodes = btree_.group_input_nodes(); - for (const int i : btree_.interface_inputs().index_range()) { + for (const int i : btree_.interface_cache().inputs.index_range()) { Vector target_usages; for (const bNode *group_input_node : group_input_nodes) { if (lf::OutputSocket *lf_socket = graph_params.usage_by_bsocket.lookup_default( @@ -3822,13 +3828,15 @@ const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_gr return nullptr; } } - for (const bNodeSocket *interface_bsocket : btree.interface_inputs()) { - if (interface_bsocket->typeinfo->geometry_nodes_cpp_type == nullptr) { + for (const bNodeTreeInterfaceSocket *interface_bsocket : btree.interface_cache().inputs) { + const bNodeSocketType *typeinfo = interface_bsocket->socket_typeinfo(); + if (typeinfo->geometry_nodes_cpp_type == nullptr) { return nullptr; } } - for (const bNodeSocket *interface_bsocket : btree.interface_outputs()) { - if (interface_bsocket->typeinfo->geometry_nodes_cpp_type == nullptr) { + for (const bNodeTreeInterfaceSocket *interface_bsocket : btree.interface_cache().outputs) { + const bNodeSocketType *typeinfo = interface_bsocket->socket_typeinfo(); + if (typeinfo->geometry_nodes_cpp_type == nullptr) { return nullptr; } } diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc index 192537e4c2d..1eb5d26e4cf 100644 --- a/source/blender/nodes/intern/node_common.cc +++ b/source/blender/nodes/intern/node_common.cc @@ -25,6 +25,7 @@ #include "BKE_node.hh" #include "BKE_node_runtime.hh" +#include "BKE_node_tree_interface.hh" #include "BKE_node_tree_update.h" #include "RNA_types.hh" @@ -48,6 +49,8 @@ using blender::Stack; using blender::StringRef; using blender::Vector; +namespace node_interface = blender::bke::node_interface; + /* -------------------------------------------------------------------- */ /** \name Node Group * \{ */ @@ -131,13 +134,14 @@ bool nodeGroupPoll(const bNodeTree *nodetree, namespace blender::nodes { -static std::function get_default_id_getter(const bNodeTree &ntree, - const bNodeSocket &io_socket) +static std::function get_default_id_getter( + const bNodeTreeInterface &tree_interface, const bNodeTreeInterfaceSocket &io_socket) { - const int socket_index = io_socket.in_out == SOCK_IN ? BLI_findindex(&ntree.inputs, &io_socket) : - BLI_findindex(&ntree.outputs, &io_socket); + const int item_index = tree_interface.find_item_index(io_socket.item); + BLI_assert(item_index >= 0); + /* Avoid capturing pointers that can become dangling. */ - return [in_out = io_socket.in_out, socket_index](const bNode &node) -> ID * { + return [item_index](const bNode &node) -> ID * { if (node.id == nullptr) { return nullptr; } @@ -145,34 +149,65 @@ static std::function get_default_id_getter(const bNodeT return nullptr; } const bNodeTree &ntree = *reinterpret_cast(node.id); - const bNodeSocket *io_socket = nullptr; - if (in_out == SOCK_IN) { - /* Better be safe than sorry when the underlying node group changed. */ - if (socket_index < ntree.interface_inputs().size()) { - io_socket = ntree.interface_inputs()[socket_index]; - } - } - else { - if (socket_index < ntree.interface_outputs().size()) { - io_socket = ntree.interface_outputs()[socket_index]; - } - } - if (io_socket == nullptr) { + const bNodeTreeInterfaceItem *io_item = ntree.tree_interface.get_item_at_index(item_index); + if (io_item == nullptr || io_item->item_type != NODE_INTERFACE_SOCKET) { return nullptr; } - return *static_cast(io_socket->default_value); + const bNodeTreeInterfaceSocket &io_socket = + node_interface::get_item_as(*io_item); + return *static_cast(io_socket.socket_data); }; } -static SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &ntree, - const bNodeSocket &io_socket) +static std::function +get_init_socket_fn(const bNodeTreeInterface &interface, const bNodeTreeInterfaceSocket &io_socket) +{ + const int item_index = interface.find_item_index(io_socket.item); + BLI_assert(item_index >= 0); + + /* Avoid capturing pointers that can become dangling. */ + return [item_index](bNode &node, bNodeSocket &socket, const char *data_path) { + if (node.id == nullptr) { + return; + } + if (GS(node.id->name) != ID_NT) { + return; + } + bNodeTree &ntree = *reinterpret_cast(node.id); + const bNodeTreeInterfaceItem *io_item = ntree.tree_interface.get_item_at_index(item_index); + if (io_item == nullptr || io_item->item_type != NODE_INTERFACE_SOCKET) { + return; + } + const bNodeTreeInterfaceSocket &io_socket = + node_interface::get_item_as(*io_item); + bNodeSocketType *typeinfo = io_socket.socket_typeinfo(); + if (typeinfo && typeinfo->interface_init_socket) { + typeinfo->interface_init_socket(&ntree.id, &io_socket, &node, &socket, data_path); + } + }; +} + +/* in_out overrides the socket declaration in/out type (bNodeTreeInterfaceSocket::flag) + * because a node group input is turned into an output socket for group input nodes. */ +static SocketDeclarationPtr declaration_for_interface_socket( + const bNodeTree &ntree, + const bNodeTreeInterfaceSocket &io_socket, + const eNodeSocketInOut in_out) { SocketDeclarationPtr dst; - switch (io_socket.type) { + + bNodeSocketType *typeinfo = nodeSocketTypeFind(io_socket.socket_type); + if (typeinfo == nullptr) { + return dst; + } + + eNodeSocketDatatype datatype = eNodeSocketDatatype(typeinfo->type); + + switch (datatype) { case SOCK_FLOAT: { - const auto &value = *io_socket.default_value_typed(); + const auto &value = node_interface::get_socket_data_as(io_socket); std::unique_ptr decl = std::make_unique(); - decl->subtype = PropertySubType(io_socket.typeinfo->subtype); + decl->subtype = PropertySubType(typeinfo->subtype); decl->default_value = value.value; decl->soft_min_value = value.min; decl->soft_max_value = value.max; @@ -180,9 +215,9 @@ static SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &nt break; } case SOCK_VECTOR: { - const auto &value = *io_socket.default_value_typed(); + const auto &value = node_interface::get_socket_data_as(io_socket); std::unique_ptr decl = std::make_unique(); - decl->subtype = PropertySubType(io_socket.typeinfo->subtype); + decl->subtype = PropertySubType(typeinfo->subtype); decl->default_value = value.value; decl->soft_min_value = value.min; decl->soft_max_value = value.max; @@ -190,7 +225,7 @@ static SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &nt break; } case SOCK_RGBA: { - const auto &value = *io_socket.default_value_typed(); + const auto &value = node_interface::get_socket_data_as(io_socket); std::unique_ptr decl = std::make_unique(); decl->default_value = value.value; dst = std::move(decl); @@ -202,23 +237,23 @@ static SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &nt break; } case SOCK_BOOLEAN: { - const auto &value = *io_socket.default_value_typed(); + const auto &value = node_interface::get_socket_data_as(io_socket); std::unique_ptr decl = std::make_unique(); decl->default_value = value.value; dst = std::move(decl); break; } case SOCK_ROTATION: { - const auto &value = *io_socket.default_value_typed(); + const auto &value = node_interface::get_socket_data_as(io_socket); std::unique_ptr decl = std::make_unique(); decl->default_value = math::EulerXYZ(float3(value.value_euler)); dst = std::move(decl); break; } case SOCK_INT: { - const auto &value = *io_socket.default_value_typed(); + const auto &value = node_interface::get_socket_data_as(io_socket); std::unique_ptr decl = std::make_unique(); - decl->subtype = PropertySubType(io_socket.typeinfo->subtype); + decl->subtype = PropertySubType(typeinfo->subtype); decl->default_value = value.value; decl->soft_min_value = value.min; decl->soft_max_value = value.max; @@ -226,7 +261,7 @@ static SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &nt break; } case SOCK_STRING: { - const auto &value = *io_socket.default_value_typed(); + const auto &value = node_interface::get_socket_data_as(io_socket); std::unique_ptr decl = std::make_unique(); decl->default_value = value.value; dst = std::move(decl); @@ -234,13 +269,13 @@ static SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &nt } case SOCK_OBJECT: { auto value = std::make_unique(); - value->default_value_fn = get_default_id_getter(ntree, io_socket); + value->default_value_fn = get_default_id_getter(ntree.tree_interface, io_socket); dst = std::move(value); break; } case SOCK_IMAGE: { auto value = std::make_unique(); - value->default_value_fn = get_default_id_getter(ntree, io_socket); + value->default_value_fn = get_default_id_getter(ntree.tree_interface, io_socket); dst = std::move(value); break; } @@ -249,37 +284,49 @@ static SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &nt break; case SOCK_COLLECTION: { auto value = std::make_unique(); - value->default_value_fn = get_default_id_getter(ntree, io_socket); + value->default_value_fn = get_default_id_getter(ntree.tree_interface, io_socket); dst = std::move(value); break; } case SOCK_TEXTURE: { auto value = std::make_unique(); - value->default_value_fn = get_default_id_getter(ntree, io_socket); + value->default_value_fn = get_default_id_getter(ntree.tree_interface, io_socket); dst = std::move(value); break; } case SOCK_MATERIAL: { auto value = std::make_unique(); - value->default_value_fn = get_default_id_getter(ntree, io_socket); + value->default_value_fn = get_default_id_getter(ntree.tree_interface, io_socket); dst = std::move(value); break; } case SOCK_CUSTOM: - std::unique_ptr decl = std::make_unique(); - decl->idname_ = io_socket.idname; - dst = std::move(decl); + auto value = std::make_unique(); + value->init_socket_fn = get_init_socket_fn(ntree.tree_interface, io_socket); + dst = std::move(value); break; } dst->name = io_socket.name; dst->identifier = io_socket.identifier; - dst->in_out = eNodeSocketInOut(io_socket.in_out); - dst->description = io_socket.description; + dst->in_out = in_out; + dst->description = io_socket.description ? io_socket.description : ""; dst->hide_value = io_socket.flag & SOCK_HIDE_VALUE; dst->compact = io_socket.flag & SOCK_COMPACT; return dst; } +static PanelDeclarationPtr declaration_for_interface_panel(const bNodeTree & /*ntree*/, + const bNodeTreeInterfacePanel &io_panel) +{ + PanelDeclarationPtr dst = std::make_unique(); + dst->uid = io_panel.identifier; + dst->name = io_panel.name ? io_panel.name : ""; + dst->description = ""; /* TODO io_panel.description */ + dst->default_collapsed = false; /* TODO io_panel.default_collapsed */ + dst->num_items = io_panel.items_num; + return dst; +} + void node_group_declare_dynamic(const bNodeTree & /*node_tree*/, const bNode &node, NodeDeclaration &r_declaration) @@ -294,12 +341,35 @@ void node_group_declare_dynamic(const bNodeTree & /*node_tree*/, } r_declaration.skip_updating_sockets = false; - LISTBASE_FOREACH (const bNodeSocket *, input, &group->inputs) { - r_declaration.inputs.append(declaration_for_interface_socket(*group, *input)); - } - LISTBASE_FOREACH (const bNodeSocket *, output, &group->outputs) { - r_declaration.outputs.append(declaration_for_interface_socket(*group, *output)); - } + group->tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) { + switch (item.item_type) { + case NODE_INTERFACE_SOCKET: { + const bNodeTreeInterfaceSocket &socket = + node_interface::get_item_as(item); + if (socket.flag & NODE_INTERFACE_SOCKET_INPUT) { + SocketDeclarationPtr socket_decl = declaration_for_interface_socket( + *group, socket, SOCK_IN); + r_declaration.inputs.append(socket_decl.get()); + r_declaration.items.append(std::move(socket_decl)); + } + if (socket.flag & NODE_INTERFACE_SOCKET_OUTPUT) { + SocketDeclarationPtr socket_decl = declaration_for_interface_socket( + *group, socket, SOCK_OUT); + r_declaration.outputs.append(socket_decl.get()); + r_declaration.items.append(std::move(socket_decl)); + } + break; + } + case NODE_INTERFACE_PANEL: { + const bNodeTreeInterfacePanel &panel = + node_interface::get_item_as(item); + PanelDeclarationPtr panel_decl = declaration_for_interface_panel(*group, panel); + r_declaration.items.append(std::move(panel_decl)); + break; + } + } + return true; + }); } } // namespace blender::nodes @@ -502,22 +572,50 @@ static void group_input_declare_dynamic(const bNodeTree &node_tree, const bNode & /*node*/, NodeDeclaration &r_declaration) { - LISTBASE_FOREACH (const bNodeSocket *, input, &node_tree.inputs) { - r_declaration.outputs.append(declaration_for_interface_socket(node_tree, *input)); - r_declaration.outputs.last()->in_out = SOCK_OUT; - } - r_declaration.outputs.append(decl::create_extend_declaration(SOCK_OUT)); + node_tree.tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) { + switch (item.item_type) { + case NODE_INTERFACE_SOCKET: { + const bNodeTreeInterfaceSocket &socket = + node_interface::get_item_as(item); + if (socket.flag & NODE_INTERFACE_SOCKET_INPUT) { + SocketDeclarationPtr socket_decl = declaration_for_interface_socket( + node_tree, socket, SOCK_OUT); + r_declaration.outputs.append(socket_decl.get()); + r_declaration.items.append(std::move(socket_decl)); + } + break; + } + } + return true; + }); + SocketDeclarationPtr extend_decl = decl::create_extend_declaration(SOCK_OUT); + r_declaration.outputs.append(extend_decl.get()); + r_declaration.items.append(std::move(extend_decl)); } static void group_output_declare_dynamic(const bNodeTree &node_tree, const bNode & /*node*/, NodeDeclaration &r_declaration) { - LISTBASE_FOREACH (const bNodeSocket *, input, &node_tree.outputs) { - r_declaration.inputs.append(declaration_for_interface_socket(node_tree, *input)); - r_declaration.inputs.last()->in_out = SOCK_IN; - } - r_declaration.inputs.append(decl::create_extend_declaration(SOCK_IN)); + node_tree.tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) { + switch (item.item_type) { + case NODE_INTERFACE_SOCKET: { + const bNodeTreeInterfaceSocket &socket = + node_interface::get_item_as(item); + if (socket.flag & NODE_INTERFACE_SOCKET_OUTPUT) { + SocketDeclarationPtr socket_decl = declaration_for_interface_socket( + node_tree, socket, SOCK_IN); + r_declaration.inputs.append(socket_decl.get()); + r_declaration.items.append(std::move(socket_decl)); + } + break; + } + } + return true; + }); + SocketDeclarationPtr extend_decl = decl::create_extend_declaration(SOCK_IN); + r_declaration.inputs.append(extend_decl.get()); + r_declaration.items.append(std::move(extend_decl)); } static bool group_input_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link) @@ -531,8 +629,8 @@ static bool group_input_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *li /* Don't connect to other "extend" sockets. */ return false; } - const bNodeSocket *io_socket = blender::bke::ntreeAddSocketInterfaceFromSocket( - ntree, link->tonode, link->tosock); + const bNodeTreeInterfaceSocket *io_socket = node_interface::add_interface_socket_from_node( + *ntree, *link->tonode, *link->tosock); if (!io_socket) { return false; } @@ -552,8 +650,8 @@ static bool group_output_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *l /* Don't connect to other "extend" sockets. */ return false; } - const bNodeSocket *io_socket = blender::bke::ntreeAddSocketInterfaceFromSocket( - ntree, link->fromnode, link->fromsock); + const bNodeTreeInterfaceSocket *io_socket = node_interface::add_interface_socket_from_node( + *ntree, *link->fromnode, *link->fromsock); if (!io_socket) { return false; } diff --git a/source/blender/nodes/intern/node_declaration.cc b/source/blender/nodes/intern/node_declaration.cc index 74aa7b2e3ca..ca1d4c2202e 100644 --- a/source/blender/nodes/intern/node_declaration.cc +++ b/source/blender/nodes/intern/node_declaration.cc @@ -6,8 +6,11 @@ #include "NOD_socket_declarations.hh" #include "NOD_socket_declarations_geometry.hh" +#include "BLI_utildefines.h" + #include "BKE_geometry_fields.hh" #include "BKE_node.hh" +#include "BKE_node_runtime.hh" namespace blender::nodes { @@ -22,6 +25,7 @@ void build_node_declaration_dynamic(const bNodeTree &node_tree, const bNode &node, NodeDeclaration &r_declaration) { + r_declaration.items.clear(); r_declaration.inputs.clear(); r_declaration.outputs.clear(); node.typeinfo->declare_dynamic(node_tree, node, r_declaration); @@ -45,13 +49,13 @@ void NodeDeclarationBuilder::finalize() Vector geometry_inputs; for (const int i : declaration_.inputs.index_range()) { - if (dynamic_cast(declaration_.inputs[i].get())) { + if (dynamic_cast(declaration_.inputs[i])) { geometry_inputs.append(i); } } Vector geometry_outputs; for (const int i : declaration_.outputs.index_range()) { - if (dynamic_cast(declaration_.outputs[i].get())) { + if (dynamic_cast(declaration_.outputs[i])) { geometry_outputs.append(i); } } @@ -139,25 +143,46 @@ std::ostream &operator<<(std::ostream &stream, const RelationsInNode &relations) bool NodeDeclaration::matches(const bNode &node) const { - auto check_sockets = [&](ListBase sockets, Span socket_decls) { - const int tot_sockets = BLI_listbase_count(&sockets); - if (tot_sockets != socket_decls.size()) { - return false; - } - int i; - LISTBASE_FOREACH_INDEX (const bNodeSocket *, socket, &sockets, i) { - const SocketDeclaration &socket_decl = *socket_decls[i]; - if (!socket_decl.matches(*socket)) { - return false; + const bNodeSocket *current_input = static_cast(node.inputs.first); + const bNodeSocket *current_output = static_cast(node.outputs.first); + const bNodePanelState *current_panel = node.panel_states_array; + for (const ItemDeclarationPtr &item_decl : items) { + if (const SocketDeclaration *socket_decl = dynamic_cast( + item_decl.get())) + { + switch (socket_decl->in_out) { + case SOCK_IN: + if (current_input == nullptr || !socket_decl->matches(*current_input)) { + return false; + } + current_input = current_input->next; + break; + case SOCK_OUT: + if (current_output == nullptr || !socket_decl->matches(*current_output)) { + return false; + } + current_output = current_output->next; + break; } } - return true; - }; - - if (!check_sockets(node.inputs, inputs)) { - return false; + else if (const PanelDeclaration *panel_decl = dynamic_cast( + item_decl.get())) + { + if (!node.panel_states().contains_ptr(current_panel) || !panel_decl->matches(*current_panel)) + { + return false; + } + ++current_panel; + } + else { + /* Unknown item type. */ + BLI_assert_unreachable(); + } } - if (!check_sockets(node.outputs, outputs)) { + /* If items are left over, some were removed from the declaration. */ + if (current_input == nullptr || current_output == nullptr || + !node.panel_states().contains_ptr(current_panel)) + { return false; } return true; @@ -212,6 +237,26 @@ bool SocketDeclaration::matches_common_data(const bNodeSocket &socket) const return true; } +void PanelDeclaration::build(bNodePanelState &panel) const +{ + panel = {0}; + panel.uid = this->uid; + SET_FLAG_FROM_TEST(panel.flag, this->default_collapsed, NODE_PANEL_COLLAPSED); +} + +bool PanelDeclaration::matches(const bNodePanelState &panel) const +{ + return panel.uid == this->uid; +} + +void PanelDeclaration::update_or_build(const bNodePanelState &old_panel, + bNodePanelState &new_panel) const +{ + build(new_panel); + /* Copy existing state to the new panel */ + SET_FLAG_FROM_TEST(new_panel.flag, old_panel.is_collapsed(), NODE_PANEL_COLLAPSED); +} + namespace implicit_field_inputs { void position(const bNode & /*node*/, void *r_value) diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 64b25cb689a..17f8359d0b5 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -178,77 +178,168 @@ static void verify_socket_template_list(bNodeTree *ntree, namespace blender::nodes { -static void refresh_socket_list(bNodeTree &ntree, +static void refresh_node_socket(bNodeTree &ntree, bNode &node, - ListBase &sockets, - Span socket_decls, - const bool do_id_user) + const SocketDeclaration &socket_decl, + Vector &old_sockets, + VectorSet &new_sockets) { - Vector old_sockets = sockets; - VectorSet new_sockets; - for (const SocketDeclarationPtr &socket_decl : socket_decls) { - /* Try to find a socket that corresponds to the declaration. */ - bNodeSocket *old_socket_with_same_identifier = nullptr; - for (const int i : old_sockets.index_range()) { - bNodeSocket &old_socket = *old_sockets[i]; - if (old_socket.identifier == socket_decl->identifier) { - old_sockets.remove_and_reorder(i); - old_socket_with_same_identifier = &old_socket; - break; - } + /* Try to find a socket that corresponds to the declaration. */ + bNodeSocket *old_socket_with_same_identifier = nullptr; + for (const int i : old_sockets.index_range()) { + bNodeSocket &old_socket = *old_sockets[i]; + if (old_socket.identifier == socket_decl.identifier) { + old_sockets.remove_and_reorder(i); + old_socket_with_same_identifier = &old_socket; + break; } - bNodeSocket *new_socket = nullptr; - if (old_socket_with_same_identifier == nullptr) { - /* Create a completely new socket. */ - new_socket = &socket_decl->build(ntree, node); + } + bNodeSocket *new_socket = nullptr; + if (old_socket_with_same_identifier == nullptr) { + /* Create a completely new socket. */ + new_socket = &socket_decl.build(ntree, node); + } + else { + STRNCPY(old_socket_with_same_identifier->name, socket_decl.name.c_str()); + if (socket_decl.matches(*old_socket_with_same_identifier)) { + /* The existing socket matches exactly, just use it. */ + new_socket = old_socket_with_same_identifier; } else { - STRNCPY(old_socket_with_same_identifier->name, socket_decl->name.c_str()); - if (socket_decl->matches(*old_socket_with_same_identifier)) { - /* The existing socket matches exactly, just use it. */ - new_socket = old_socket_with_same_identifier; + /* Clear out identifier to avoid name collisions when a new socket is created. */ + old_socket_with_same_identifier->identifier[0] = '\0'; + new_socket = &socket_decl.update_or_build(ntree, node, *old_socket_with_same_identifier); + + if (new_socket == old_socket_with_same_identifier) { + /* The existing socket has been updated, set the correct identifier again. */ + STRNCPY(new_socket->identifier, socket_decl.identifier.c_str()); } else { - /* Clear out identifier to avoid name collisions when a new socket is created. */ - old_socket_with_same_identifier->identifier[0] = '\0'; - new_socket = &socket_decl->update_or_build(ntree, node, *old_socket_with_same_identifier); - - if (new_socket == old_socket_with_same_identifier) { - /* The existing socket has been updated, set the correct identifier again. */ - STRNCPY(new_socket->identifier, socket_decl->identifier.c_str()); - } - else { - /* Move links to new socket with same identifier. */ - LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { - if (link->fromsock == old_socket_with_same_identifier) { - link->fromsock = new_socket; - } - else if (link->tosock == old_socket_with_same_identifier) { - link->tosock = new_socket; - } + /* Move links to new socket with same identifier. */ + LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) { + if (link->fromsock == old_socket_with_same_identifier) { + link->fromsock = new_socket; } - for (bNodeLink &internal_link : node.runtime->internal_links) { - if (internal_link.fromsock == old_socket_with_same_identifier) { - internal_link.fromsock = new_socket; - } - else if (internal_link.tosock == old_socket_with_same_identifier) { - internal_link.tosock = new_socket; - } + else if (link->tosock == old_socket_with_same_identifier) { + link->tosock = new_socket; + } + } + for (bNodeLink &internal_link : node.runtime->internal_links) { + if (internal_link.fromsock == old_socket_with_same_identifier) { + internal_link.fromsock = new_socket; + } + else if (internal_link.tosock == old_socket_with_same_identifier) { + internal_link.tosock = new_socket; } } } } - new_sockets.add_new(new_socket); - BKE_ntree_update_tag_socket_new(&ntree, new_socket); } - LISTBASE_FOREACH_MUTABLE (bNodeSocket *, old_socket, &sockets) { - if (!new_sockets.contains(old_socket)) { + new_sockets.add_new(new_socket); + BKE_ntree_update_tag_socket_new(&ntree, new_socket); +} + +static void refresh_node_panel(const PanelDeclaration &panel_decl, + Vector &old_panels, + bNodePanelState &new_panel) +{ + /* Try to find a panel that corresponds to the declaration. */ + bNodePanelState *old_panel_with_same_identifier = nullptr; + for (const int i : old_panels.index_range()) { + bNodePanelState &old_panel = old_panels[i]; + if (old_panel.uid == panel_decl.uid) { + /* Panel is removed after copying to #new_panel. */ + old_panel_with_same_identifier = &old_panel; + break; + } + } + + if (old_panel_with_same_identifier == nullptr) { + /* Create a completely new panel. */ + panel_decl.build(new_panel); + } + else { + if (panel_decl.matches(*old_panel_with_same_identifier)) { + /* The existing socket matches exactly, just use it. */ + new_panel = *old_panel_with_same_identifier; + } + else { + /* Clear out identifier to avoid name collisions when a new panel is created. */ + old_panel_with_same_identifier->uid = -1; + panel_decl.update_or_build(*old_panel_with_same_identifier, new_panel); + } + + /* Remove from old panels. */ + const int64_t old_panel_index = old_panel_with_same_identifier - old_panels.begin(); + old_panels.remove_and_reorder(old_panel_index); + } +} + +static void refresh_node_sockets_and_panels(bNodeTree &ntree, + bNode &node, + Span item_decls, + const bool do_id_user) +{ + /* Count panels */ + int new_num_panels = 0; + for (const ItemDeclarationPtr &item_decl : item_decls) { + if (dynamic_cast(item_decl.get())) { + ++new_num_panels; + } + } + /* New panel states buffer. */ + MEM_SAFE_FREE(node.panel_states_array); + node.num_panel_states = new_num_panels; + node.panel_states_array = MEM_cnew_array(new_num_panels, __func__); + + /* Find list of sockets to add, mixture of old and new sockets. */ + VectorSet new_inputs; + VectorSet new_outputs; + bNodePanelState *new_panel = node.panel_states_array; + { + Vector old_inputs = node.inputs; + Vector old_outputs = node.outputs; + Vector old_panels = Vector(node.panel_states()); + for (const ItemDeclarationPtr &item_decl : item_decls) { + if (const SocketDeclaration *socket_decl = dynamic_cast( + item_decl.get())) + { + if (socket_decl->in_out == SOCK_IN) { + refresh_node_socket(ntree, node, *socket_decl, old_inputs, new_inputs); + } + else { + refresh_node_socket(ntree, node, *socket_decl, old_outputs, new_outputs); + } + } + else if (const PanelDeclaration *panel_decl = dynamic_cast( + item_decl.get())) + { + refresh_node_panel(*panel_decl, old_panels, *new_panel); + ++new_panel; + } + } + } + + /* Destroy any remaining sockets that are no longer in the declaration. */ + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, old_socket, &node.inputs) { + if (!new_inputs.contains(old_socket)) { blender::bke::nodeRemoveSocketEx(&ntree, &node, old_socket, do_id_user); } } - BLI_listbase_clear(&sockets); - for (bNodeSocket *socket : new_sockets) { - BLI_addtail(&sockets, socket); + LISTBASE_FOREACH_MUTABLE (bNodeSocket *, old_socket, &node.outputs) { + if (!new_outputs.contains(old_socket)) { + blender::bke::nodeRemoveSocketEx(&ntree, &node, old_socket, do_id_user); + } + } + + /* Clear and reinsert sockets in the new order. */ + BLI_listbase_clear(&node.inputs); + BLI_listbase_clear(&node.outputs); + for (bNodeSocket *socket : new_inputs) { + BLI_addtail(&node.inputs, socket); + } + for (bNodeSocket *socket : new_outputs) { + BLI_addtail(&node.outputs, socket); } } @@ -261,8 +352,7 @@ static void refresh_node(bNodeTree &ntree, return; } if (!node_decl.matches(node)) { - refresh_socket_list(ntree, node, node.inputs, node_decl.inputs, do_id_user); - refresh_socket_list(ntree, node, node.outputs, node_decl.outputs, do_id_user); + refresh_node_sockets_and_panels(ntree, node, node_decl.items, do_id_user); } blender::bke::nodeSocketDeclarationsUpdate(&node); } @@ -306,16 +396,13 @@ void node_verify_sockets(bNodeTree *ntree, bNode *node, bool do_id_user) } } -void node_socket_init_default_value(bNodeSocket *sock) +void node_socket_init_default_value_data(eNodeSocketDatatype datatype, int subtype, void **data) { - int type = sock->typeinfo->type; - int subtype = sock->typeinfo->subtype; - - if (sock->default_value) { - return; /* already initialized */ + if (!data) { + return; } - switch (type) { + switch (datatype) { case SOCK_FLOAT: { bNodeSocketValueFloat *dval = MEM_cnew("node socket value float"); dval->subtype = subtype; @@ -323,7 +410,7 @@ void node_socket_init_default_value(bNodeSocket *sock) dval->min = -FLT_MAX; dval->max = FLT_MAX; - sock->default_value = dval; + *data = dval; break; } case SOCK_INT: { @@ -333,19 +420,19 @@ void node_socket_init_default_value(bNodeSocket *sock) dval->min = INT_MIN; dval->max = INT_MAX; - sock->default_value = dval; + *data = dval; break; } case SOCK_BOOLEAN: { bNodeSocketValueBoolean *dval = MEM_cnew("node socket value bool"); dval->value = false; - sock->default_value = dval; + *data = dval; break; } case SOCK_ROTATION: { bNodeSocketValueRotation *dval = MEM_cnew(__func__); - sock->default_value = dval; + *data = dval; break; } case SOCK_VECTOR: { @@ -356,7 +443,7 @@ void node_socket_init_default_value(bNodeSocket *sock) dval->min = -FLT_MAX; dval->max = FLT_MAX; - sock->default_value = dval; + *data = dval; break; } case SOCK_RGBA: { @@ -364,7 +451,7 @@ void node_socket_init_default_value(bNodeSocket *sock) bNodeSocketValueRGBA *dval = MEM_cnew("node socket value color"); copy_v4_v4(dval->value, default_value); - sock->default_value = dval; + *data = dval; break; } case SOCK_STRING: { @@ -372,21 +459,21 @@ void node_socket_init_default_value(bNodeSocket *sock) dval->subtype = subtype; dval->value[0] = '\0'; - sock->default_value = dval; + *data = dval; break; } case SOCK_OBJECT: { bNodeSocketValueObject *dval = MEM_cnew("node socket value object"); dval->value = nullptr; - sock->default_value = dval; + *data = dval; break; } case SOCK_IMAGE: { bNodeSocketValueImage *dval = MEM_cnew("node socket value image"); dval->value = nullptr; - sock->default_value = dval; + *data = dval; break; } case SOCK_COLLECTION: { @@ -394,7 +481,7 @@ void node_socket_init_default_value(bNodeSocket *sock) "node socket value object"); dval->value = nullptr; - sock->default_value = dval; + *data = dval; break; } case SOCK_TEXTURE: { @@ -402,7 +489,7 @@ void node_socket_init_default_value(bNodeSocket *sock) "node socket value texture"); dval->value = nullptr; - sock->default_value = dval; + *data = dval; break; } case SOCK_MATERIAL: { @@ -410,12 +497,120 @@ void node_socket_init_default_value(bNodeSocket *sock) "node socket value material"); dval->value = nullptr; - sock->default_value = dval; + *data = dval; break; } + + case SOCK_CUSTOM: + case SOCK_GEOMETRY: + case SOCK_SHADER: + break; } } +void node_socket_copy_default_value_data(eNodeSocketDatatype datatype, void *to, const void *from) +{ + if (!to || !from) { + return; + } + + switch (datatype) { + case SOCK_FLOAT: { + bNodeSocketValueFloat *toval = (bNodeSocketValueFloat *)to; + bNodeSocketValueFloat *fromval = (bNodeSocketValueFloat *)from; + *toval = *fromval; + break; + } + case SOCK_INT: { + bNodeSocketValueInt *toval = (bNodeSocketValueInt *)to; + bNodeSocketValueInt *fromval = (bNodeSocketValueInt *)from; + *toval = *fromval; + break; + } + case SOCK_BOOLEAN: { + bNodeSocketValueBoolean *toval = (bNodeSocketValueBoolean *)to; + bNodeSocketValueBoolean *fromval = (bNodeSocketValueBoolean *)from; + *toval = *fromval; + break; + } + case SOCK_VECTOR: { + bNodeSocketValueVector *toval = (bNodeSocketValueVector *)to; + bNodeSocketValueVector *fromval = (bNodeSocketValueVector *)from; + *toval = *fromval; + break; + } + case SOCK_RGBA: { + bNodeSocketValueRGBA *toval = (bNodeSocketValueRGBA *)to; + bNodeSocketValueRGBA *fromval = (bNodeSocketValueRGBA *)from; + *toval = *fromval; + break; + } + case SOCK_ROTATION: { + bNodeSocketValueRotation *toval = (bNodeSocketValueRotation *)to; + bNodeSocketValueRotation *fromval = (bNodeSocketValueRotation *)from; + *toval = *fromval; + break; + } + case SOCK_STRING: { + bNodeSocketValueString *toval = (bNodeSocketValueString *)to; + bNodeSocketValueString *fromval = (bNodeSocketValueString *)from; + *toval = *fromval; + break; + } + case SOCK_OBJECT: { + bNodeSocketValueObject *toval = (bNodeSocketValueObject *)to; + bNodeSocketValueObject *fromval = (bNodeSocketValueObject *)from; + *toval = *fromval; + id_us_plus(&toval->value->id); + break; + } + case SOCK_IMAGE: { + bNodeSocketValueImage *toval = (bNodeSocketValueImage *)to; + bNodeSocketValueImage *fromval = (bNodeSocketValueImage *)from; + *toval = *fromval; + id_us_plus(&toval->value->id); + break; + } + case SOCK_COLLECTION: { + bNodeSocketValueCollection *toval = (bNodeSocketValueCollection *)to; + bNodeSocketValueCollection *fromval = (bNodeSocketValueCollection *)from; + *toval = *fromval; + id_us_plus(&toval->value->id); + break; + } + case SOCK_TEXTURE: { + bNodeSocketValueTexture *toval = (bNodeSocketValueTexture *)to; + bNodeSocketValueTexture *fromval = (bNodeSocketValueTexture *)from; + *toval = *fromval; + id_us_plus(&toval->value->id); + break; + } + case SOCK_MATERIAL: { + bNodeSocketValueMaterial *toval = (bNodeSocketValueMaterial *)to; + bNodeSocketValueMaterial *fromval = (bNodeSocketValueMaterial *)from; + *toval = *fromval; + id_us_plus(&toval->value->id); + break; + } + + case SOCK_CUSTOM: + case SOCK_GEOMETRY: + case SOCK_SHADER: + break; + } +} + +void node_socket_init_default_value(bNodeSocket *sock) +{ + if (sock->default_value) { + return; /* already initialized */ + } + + node_socket_init_default_value_data(eNodeSocketDatatype(sock->typeinfo->type), + PropertySubType(sock->typeinfo->subtype), + &sock->default_value); +} + void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from) { /* sanity check */ @@ -434,114 +629,35 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from) STRNCPY(to->name, from->label); } - switch (from->typeinfo->type) { - case SOCK_FLOAT: { - bNodeSocketValueFloat *toval = (bNodeSocketValueFloat *)to->default_value; - bNodeSocketValueFloat *fromval = (bNodeSocketValueFloat *)from->default_value; - *toval = *fromval; - break; - } - case SOCK_INT: { - bNodeSocketValueInt *toval = (bNodeSocketValueInt *)to->default_value; - bNodeSocketValueInt *fromval = (bNodeSocketValueInt *)from->default_value; - *toval = *fromval; - break; - } - case SOCK_BOOLEAN: { - bNodeSocketValueBoolean *toval = (bNodeSocketValueBoolean *)to->default_value; - bNodeSocketValueBoolean *fromval = (bNodeSocketValueBoolean *)from->default_value; - *toval = *fromval; - break; - } - case SOCK_VECTOR: { - bNodeSocketValueVector *toval = (bNodeSocketValueVector *)to->default_value; - bNodeSocketValueVector *fromval = (bNodeSocketValueVector *)from->default_value; - *toval = *fromval; - break; - } - case SOCK_RGBA: { - bNodeSocketValueRGBA *toval = (bNodeSocketValueRGBA *)to->default_value; - bNodeSocketValueRGBA *fromval = (bNodeSocketValueRGBA *)from->default_value; - *toval = *fromval; - break; - } - case SOCK_ROTATION: { - bNodeSocketValueRotation *toval = (bNodeSocketValueRotation *)to->default_value; - bNodeSocketValueRotation *fromval = (bNodeSocketValueRotation *)from->default_value; - *toval = *fromval; - break; - } - case SOCK_STRING: { - bNodeSocketValueString *toval = (bNodeSocketValueString *)to->default_value; - bNodeSocketValueString *fromval = (bNodeSocketValueString *)from->default_value; - *toval = *fromval; - break; - } - case SOCK_OBJECT: { - bNodeSocketValueObject *toval = (bNodeSocketValueObject *)to->default_value; - bNodeSocketValueObject *fromval = (bNodeSocketValueObject *)from->default_value; - *toval = *fromval; - id_us_plus(&toval->value->id); - break; - } - case SOCK_IMAGE: { - bNodeSocketValueImage *toval = (bNodeSocketValueImage *)to->default_value; - bNodeSocketValueImage *fromval = (bNodeSocketValueImage *)from->default_value; - *toval = *fromval; - id_us_plus(&toval->value->id); - break; - } - case SOCK_COLLECTION: { - bNodeSocketValueCollection *toval = (bNodeSocketValueCollection *)to->default_value; - bNodeSocketValueCollection *fromval = (bNodeSocketValueCollection *)from->default_value; - *toval = *fromval; - id_us_plus(&toval->value->id); - break; - } - case SOCK_TEXTURE: { - bNodeSocketValueTexture *toval = (bNodeSocketValueTexture *)to->default_value; - bNodeSocketValueTexture *fromval = (bNodeSocketValueTexture *)from->default_value; - *toval = *fromval; - id_us_plus(&toval->value->id); - break; - } - case SOCK_MATERIAL: { - bNodeSocketValueMaterial *toval = (bNodeSocketValueMaterial *)to->default_value; - bNodeSocketValueMaterial *fromval = (bNodeSocketValueMaterial *)from->default_value; - *toval = *fromval; - id_us_plus(&toval->value->id); - break; - } - } + node_socket_copy_default_value_data( + eNodeSocketDatatype(to->typeinfo->type), to->default_value, from->default_value); to->flag |= (from->flag & SOCK_HIDE_VALUE); } -static void standard_node_socket_interface_init_socket(bNodeTree * /*ntree*/, - const bNodeSocket *interface_socket, - bNode * /*node*/, - bNodeSocket *sock, - const char * /*data_path*/) +static void standard_node_socket_interface_init_socket( + ID * /*id*/, + const bNodeTreeInterfaceSocket *interface_socket, + bNode * /*node*/, + bNodeSocket *sock, + const char * /*data_path*/) { /* initialize the type value */ sock->type = sock->typeinfo->type; - /* XXX socket interface 'type' value is not used really, - * but has to match or the copy function will bail out - */ - const_cast(interface_socket)->type = interface_socket->typeinfo->type; - /* copy default_value settings */ - node_socket_copy_default_value(sock, interface_socket); + node_socket_init_default_value_data( + eNodeSocketDatatype(sock->type), sock->typeinfo->subtype, &sock->default_value); + node_socket_copy_default_value_data( + eNodeSocketDatatype(sock->type), sock->default_value, interface_socket->socket_data); } -static void standard_node_socket_interface_from_socket(bNodeTree * /*ntree*/, - bNodeSocket *stemp, +static void standard_node_socket_interface_from_socket(ID * /*id*/, + bNodeTreeInterfaceSocket *iosock, const bNode * /*node*/, const bNodeSocket *sock) { /* initialize settings */ - stemp->type = stemp->typeinfo->type; - node_socket_copy_default_value(stemp, sock); + iosock->init_from_socket_instance(sock); } void ED_init_standard_node_socket_type(bNodeSocketType *); @@ -549,7 +665,7 @@ void ED_init_standard_node_socket_type(bNodeSocketType *); static bNodeSocketType *make_standard_socket_type(int type, int subtype) { const char *socket_idname = nodeStaticSocketType(type, subtype); - const char *interface_idname = nodeStaticSocketInterfaceType(type, subtype); + const char *interface_idname = nodeStaticSocketInterfaceTypeNew(type, subtype); const char *socket_label = nodeStaticSocketLabel(type, subtype); const char *socket_subtype_label = blender::bke::nodeSocketSubTypeLabel(subtype); bNodeSocketType *stype; @@ -626,13 +742,12 @@ static bNodeSocketType *make_socket_type_bool() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(bool *)r_value = ((bNodeSocketValueBoolean *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(bool *)r_value = ((bNodeSocketValueBoolean *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = &blender::CPPType::get>(); - socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { - bool value; - socket.typeinfo->get_base_cpp_value(socket, &value); + socktype->get_geometry_nodes_cpp_value = [](const void *socket_value, void *r_value) { + const bool value = ((bNodeSocketValueBoolean *)socket_value)->value; new (r_value) ValueOrField(value); }; return socktype; @@ -642,15 +757,16 @@ static bNodeSocketType *make_socket_type_rotation() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_ROTATION, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - const auto &value = *socket.default_value_typed(); - const math::EulerXYZ euler(float3(value.value_euler)); + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + const auto &typed_value = *(bNodeSocketValueRotation *)socket_value; + const math::EulerXYZ euler(float3(typed_value.value_euler)); *static_cast(r_value) = math::to_quaternion(euler); }; socktype->geometry_nodes_cpp_type = &blender::CPPType::get>(); - socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { - math::Quaternion value; - socket.typeinfo->get_base_cpp_value(socket, &value); + socktype->get_geometry_nodes_cpp_value = [](const void *socket_value, void *r_value) { + const auto &typed_value = *(bNodeSocketValueRotation *)socket_value; + const math::EulerXYZ euler(float3(typed_value.value_euler)); + const math::Quaternion value = math::to_quaternion(euler); new (r_value) ValueOrField(value); }; return socktype; @@ -660,13 +776,12 @@ static bNodeSocketType *make_socket_type_float(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_FLOAT, subtype); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(float *)r_value = ((bNodeSocketValueFloat *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(float *)r_value = ((bNodeSocketValueFloat *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = &blender::CPPType::get>(); - socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { - float value; - socket.typeinfo->get_base_cpp_value(socket, &value); + socktype->get_geometry_nodes_cpp_value = [](const void *socket_value, void *r_value) { + const float value = ((bNodeSocketValueFloat *)socket_value)->value; new (r_value) ValueOrField(value); }; return socktype; @@ -676,13 +791,12 @@ static bNodeSocketType *make_socket_type_int(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_INT, subtype); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(int *)r_value = ((bNodeSocketValueInt *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(int *)r_value = ((bNodeSocketValueInt *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = &blender::CPPType::get>(); - socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { - int value; - socket.typeinfo->get_base_cpp_value(socket, &value); + socktype->get_geometry_nodes_cpp_value = [](const void *socket_value, void *r_value) { + const int value = ((bNodeSocketValueInt *)socket_value)->value; new (r_value) ValueOrField(value); }; return socktype; @@ -692,13 +806,12 @@ static bNodeSocketType *make_socket_type_vector(PropertySubType subtype) { bNodeSocketType *socktype = make_standard_socket_type(SOCK_VECTOR, subtype); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(blender::float3 *)r_value = ((bNodeSocketValueVector *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = &blender::CPPType::get>(); - socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { - blender::float3 value; - socket.typeinfo->get_base_cpp_value(socket, &value); + socktype->get_geometry_nodes_cpp_value = [](const void *socket_value, void *r_value) { + const blender::float3 value = ((bNodeSocketValueVector *)socket_value)->value; new (r_value) ValueOrField(value); }; return socktype; @@ -708,14 +821,13 @@ static bNodeSocketType *make_socket_type_rgba() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(blender::ColorGeometry4f *)r_value = ((bNodeSocketValueRGBA *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = &blender::CPPType::get>(); - socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { - blender::ColorGeometry4f value; - socket.typeinfo->get_base_cpp_value(socket, &value); + socktype->get_geometry_nodes_cpp_value = [](const void *socket_value, void *r_value) { + const blender::ColorGeometry4f value = ((bNodeSocketValueRGBA *)socket_value)->value; new (r_value) ValueOrField(value); }; return socktype; @@ -725,14 +837,14 @@ static bNodeSocketType *make_socket_type_string() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_STRING, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - new (r_value) std::string(((bNodeSocketValueString *)socket.default_value)->value); + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + new (r_value) std::string(((bNodeSocketValueString *)socket_value)->value); }; socktype->geometry_nodes_cpp_type = &blender::CPPType::get>(); - socktype->get_geometry_nodes_cpp_value = [](const bNodeSocket &socket, void *r_value) { + socktype->get_geometry_nodes_cpp_value = [](const void *socket_value, void *r_value) { std::string value; value.~basic_string(); - socket.typeinfo->get_base_cpp_value(socket, &value); + new (&value) std::string(((bNodeSocketValueString *)socket_value)->value); new (r_value) ValueOrField(value); }; return socktype; @@ -742,8 +854,8 @@ static bNodeSocketType *make_socket_type_object() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(Object **)r_value = ((bNodeSocketValueObject *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(Object **)r_value = ((bNodeSocketValueObject *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; @@ -754,7 +866,7 @@ static bNodeSocketType *make_socket_type_geometry() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_GEOMETRY, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket & /*socket*/, void *r_value) { + socktype->get_base_cpp_value = [](const void * /*socket_value*/, void *r_value) { new (r_value) blender::bke::GeometrySet(); }; socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; @@ -766,8 +878,8 @@ static bNodeSocketType *make_socket_type_collection() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_COLLECTION, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(Collection **)r_value = ((bNodeSocketValueCollection *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(Collection **)r_value = ((bNodeSocketValueCollection *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; @@ -778,8 +890,8 @@ static bNodeSocketType *make_socket_type_texture() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_TEXTURE, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(Tex **)r_value = ((bNodeSocketValueTexture *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(Tex **)r_value = ((bNodeSocketValueTexture *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; @@ -790,8 +902,8 @@ static bNodeSocketType *make_socket_type_image() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_IMAGE, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(Image **)r_value = ((bNodeSocketValueImage *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(Image **)r_value = ((bNodeSocketValueImage *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; @@ -802,8 +914,8 @@ static bNodeSocketType *make_socket_type_material() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_MATERIAL, PROP_NONE); socktype->base_cpp_type = &blender::CPPType::get(); - socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { - *(Material **)r_value = ((bNodeSocketValueMaterial *)socket.default_value)->value; + socktype->get_base_cpp_value = [](const void *socket_value, void *r_value) { + *(Material **)r_value = ((bNodeSocketValueMaterial *)socket_value)->value; }; socktype->geometry_nodes_cpp_type = socktype->base_cpp_type; socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; diff --git a/source/blender/nodes/intern/node_socket_declarations.cc b/source/blender/nodes/intern/node_socket_declarations.cc index bbe82498df6..c63140a9d2d 100644 --- a/source/blender/nodes/intern/node_socket_declarations.cc +++ b/source/blender/nodes/intern/node_socket_declarations.cc @@ -680,6 +680,9 @@ bNodeSocket &Custom::build(bNodeTree &ntree, bNode &node) const { bNodeSocket &socket = *nodeAddSocket( &ntree, &node, this->in_out, idname_, this->identifier.c_str(), this->name.c_str()); + if (this->init_socket_fn) { + this->init_socket_fn(node, socket, "interface"); + } return socket; } diff --git a/source/blender/nodes/intern/socket_search_link.cc b/source/blender/nodes/intern/socket_search_link.cc index 79eee66a910..0edb8c2589d 100644 --- a/source/blender/nodes/intern/socket_search_link.cc +++ b/source/blender/nodes/intern/socket_search_link.cc @@ -94,7 +94,7 @@ void LinkSearchOpParams::update_and_connect_available_socket(bNode &new_node, } void search_link_ops_for_declarations(GatherLinkSearchOpParams ¶ms, - Span declarations) + Span declarations) { const bNodeType &node_type = params.node_type(); diff --git a/source/blender/windowmanager/WM_types.hh b/source/blender/windowmanager/WM_types.hh index 04ab71f220f..5600054aaf6 100644 --- a/source/blender/windowmanager/WM_types.hh +++ b/source/blender/windowmanager/WM_types.hh @@ -1084,6 +1084,7 @@ enum eWM_DragDataType { WM_DRAG_DATASTACK, WM_DRAG_ASSET_CATALOG, WM_DRAG_GREASE_PENCIL_LAYER, + WM_DRAG_NODE_TREE_INTERFACE, }; enum eWM_DragFlags { @@ -1109,6 +1110,10 @@ struct wmDragAssetCatalog { bUUID drag_catalog_id; }; +typedef struct wmDragNodeTreeInterface { + struct bNodeTreeInterfaceItem *item; +} wmDragNodeTreeInterface; + /** * For some specific cases we support dragging multiple assets (#WM_DRAG_ASSET_LIST). There is no * proper support for dragging multiple items in the `wmDrag`/`wmDrop` API yet, so this is really diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 95d8848cc9f..df4099adecb 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -313,6 +313,15 @@ add_blender_test( --testdir "${TEST_SRC_DIR}/animation" ) +# ------------------------------------------------------------------------------ +# NODE INTERFACE TESTS +add_blender_test( + bl_node_group_interface + --python ${CMAKE_CURRENT_LIST_DIR}/bl_node_group_interface.py + -- + --testdir "${TEST_SRC_DIR}/node_group" +) + # ------------------------------------------------------------------------------ # IO TESTS diff --git a/tests/python/bl_node_group_interface.py b/tests/python/bl_node_group_interface.py new file mode 100644 index 00000000000..d7139714e79 --- /dev/null +++ b/tests/python/bl_node_group_interface.py @@ -0,0 +1,492 @@ +# SPDX-FileCopyrightText: 2021-2023 Blender Foundation +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import pathlib +import sys +import unittest +import tempfile + +import bpy + +args = None + + +class AbstractNodeGroupInterfaceTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.testdir = args.testdir + cls._tempdir = tempfile.TemporaryDirectory() + cls.tempdir = pathlib.Path(cls._tempdir.name) + + def setUp(self): + self.assertTrue(self.testdir.exists(), + 'Test dir {0} should exist'.format(self.testdir)) + + # Make sure we always start with a known-empty file. + bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "empty.blend")) + + # XXX Will fail when rotation sockets are no longer experimental. + # Once that happens just remove this line. + bpy.context.preferences.experimental.use_rotation_socket = True + + def tearDown(self): + self._tempdir.cleanup() + + +class NodeGroupInterfaceTests: + tree_type = None + group_node_type = None + # Tree instance where node groups can be added + main_tree = None + + def make_group(self): + tree = bpy.data.node_groups.new("test", self.tree_type) + return tree + + def make_instance(self, tree): + group_node = self.main_tree.nodes.new(self.group_node_type) + group_node.node_tree = tree + return group_node + + def make_group_and_instance(self): + tree = self.make_group() + group_node = self.make_instance(tree) + return tree, group_node + + # Utility method for generating a non-zero default value. + @staticmethod + def make_default_socket_value(socket_type): + if (socket_type == "NodeSocketBool"): + return True + elif (socket_type == "NodeSocketColor"): + return (.5, 1.0, .3, .7) + elif (socket_type == "NodeSocketFloat"): + return 1.23 + elif (socket_type == "NodeSocketImage"): + return bpy.data.images.new("test", 4, 4) + elif (socket_type == "NodeSocketInt"): + return -6 + elif (socket_type == "NodeSocketMaterial"): + return bpy.data.materials.new("test") + elif (socket_type == "NodeSocketObject"): + return bpy.data.objects.new("test", bpy.data.meshes.new("test")) + elif (socket_type == "NodeSocketRotation"): + return (0.3, 5.0, -42) + elif (socket_type == "NodeSocketString"): + return "Hello World!" + elif (socket_type == "NodeSocketTexture"): + return bpy.data.textures.new("test", 'MAGIC') + elif (socket_type == "NodeSocketVector"): + return (4.0, -1.0, 0.0) + + # Utility method returning a comparator for socket values. + # Not all socket value types are trivially comparable, e.g. colors. + @staticmethod + def make_socket_value_comparator(socket_type): + def cmp_default(test, value, expected): + test.assertEqual(value, expected, f"Value {value} does not match expected value {expected}") + + def cmp_array(test, value, expected): + test.assertSequenceEqual(value[:], expected[:], f"Value {value} does not match expected value {expected}") + + if (socket_type in {"NodeSocketBool", + "NodeSocketFloat", + "NodeSocketImage", + "NodeSocketInt", + "NodeSocketMaterial", + "NodeSocketObject", + "NodeSocketRotation", + "NodeSocketString", + "NodeSocketTexture"}): + return cmp_default + elif (socket_type in {"NodeSocketColor", + "NodeSocketVector"}): + return cmp_array + + def test_empty_nodegroup(self): + tree, group_node = self.make_group_and_instance() + + self.assertFalse(tree.interface.ui_items, "Interface not empty") + self.assertFalse(group_node.inputs) + self.assertFalse(group_node.outputs) + + def do_test_invalid_socket_type(self, socket_type): + tree = self.make_group() + + with self.assertRaises(TypeError): + in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, is_input=True) + self.assertIsNone(in0, f"Socket created for invalid type {socket_type}") + with self.assertRaises(TypeError): + out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, is_output=True) + self.assertIsNone(out0, f"Socket created for invalid type {socket_type}") + + def do_test_sockets_in_out(self, socket_type): + tree, group_node = self.make_group_and_instance() + + out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, is_output=True) + self.assertIsNotNone(out0, f"Could not create socket of type {socket_type}") + + in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, is_input=True) + self.assertIsNotNone(in0, f"Could not create socket of type {socket_type}") + + in1 = tree.interface.new_socket("Input 1", socket_type=socket_type, is_input=True) + self.assertIsNotNone(in1, f"Could not create socket of type {socket_type}") + + out1 = tree.interface.new_socket("Output 1", socket_type=socket_type, is_output=True) + self.assertIsNotNone(out1, f"Could not create socket of type {socket_type}") + + inout0 = tree.interface.new_socket("Input/Output 0", socket_type=socket_type, is_output=True, is_input=True) + self.assertIsNotNone(inout0, f"Could not create socket of type {socket_type}") + + self.assertSequenceEqual([(s.name, s.bl_idname) for s in group_node.inputs], [ + ("Input 0", socket_type), + ("Input 1", socket_type), + ("Input/Output 0", socket_type), + ]) + self.assertSequenceEqual([(s.name, s.bl_idname) for s in group_node.outputs], [ + ("Output 0", socket_type), + ("Output 1", socket_type), + ("Input/Output 0", socket_type), + ]) + + def do_test_user_count(self, value, expected_users): + if (isinstance(value, bpy.types.ID)): + self.assertEqual( + value.users, + expected_users, + f"Socket default value has user count {value.users}, expected {expected_users}") + + def do_test_socket_type(self, socket_type): + default_value = self.make_default_socket_value(socket_type) + compare_value = self.make_socket_value_comparator(socket_type) + + # Create the tree first, add sockets, then create a group instance. + # That way the new instance should reflect the expected default values. + tree = self.make_group() + + in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, is_input=True) + if default_value is not None: + in0.default_value = default_value + out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, is_output=True) + self.assertIsNotNone(in0, f"Could not create socket of type {socket_type}") + self.assertIsNotNone(out0, f"Could not create socket of type {socket_type}") + + # Now make a node group instance to check default values. + group_node = self.make_instance(tree) + if compare_value: + compare_value(self, group_node.inputs[0].default_value, in0.default_value) + + # Test ID user count after assigning. + if (hasattr(in0, "default_value")): + # The default value is stored in both the interface and node, it should have 2 users now. + self.do_test_user_count(in0.default_value, 2) + + # Copy sockets + in1 = tree.interface.copy(in0) + out1 = tree.interface.copy(out0) + self.assertIsNotNone(in1, "Could not copy socket") + self.assertIsNotNone(out1, "Could not copy socket") + # User count on default values should increment by 2 after copy, + # one user for the interface and one for the group node instance. + if (hasattr(in1, "default_value")): + self.do_test_user_count(in1.default_value, 4) + + # Classic outputs..inputs socket layout + def do_test_items_order_classic(self, socket_type): + tree, group_node = self.make_group_and_instance() + + tree.interface.new_socket("Output 0", socket_type=socket_type, is_output=True) + tree.interface.new_socket("Input 0", socket_type=socket_type, is_input=True) + + self.assertSequenceEqual([(s.name, s.item_type) for s in tree.interface.ui_items], [ + ("Output 0", 'SOCKET'), + ("Input 0", 'SOCKET'), + ]) + self.assertSequenceEqual([s.name for s in group_node.inputs], [ + "Input 0", + ]) + self.assertSequenceEqual([s.name for s in group_node.outputs], [ + "Output 0", + ]) + # XXX currently no panel state access on node instances. + # self.assertFalse(group_node.panels) + + # Mixed sockets and panels + def do_test_items_order_mixed_with_panels(self, socket_type): + tree, group_node = self.make_group_and_instance() + + tree.interface.new_panel("Panel 0") + tree.interface.new_socket("Input 0", socket_type=socket_type, is_input=True) + tree.interface.new_socket("Output 0", socket_type=socket_type, is_output=True) + tree.interface.new_panel("Panel 1") + tree.interface.new_socket("Input 1", socket_type=socket_type, is_input=True) + tree.interface.new_panel("Panel 2") + tree.interface.new_socket("Output 1", socket_type=socket_type, is_output=True) + tree.interface.new_panel("Panel 3") + + self.assertSequenceEqual([(s.name, s.item_type) for s in tree.interface.ui_items], [ + ("Panel 0", 'PANEL'), + ("Input 0", 'SOCKET'), + ("Output 0", 'SOCKET'), + ("Panel 1", 'PANEL'), + ("Input 1", 'SOCKET'), + ("Panel 2", 'PANEL'), + ("Output 1", 'SOCKET'), + ("Panel 3", 'PANEL'), + ]) + self.assertSequenceEqual([s.name for s in group_node.inputs], [ + "Input 0", + "Input 1", + ]) + self.assertSequenceEqual([s.name for s in group_node.outputs], [ + "Output 0", + "Output 1", + ]) + # XXX currently no panel state access on node instances. + # self.assertSequenceEqual([p.name for p in group_node.panels], [ + # "Panel 0", + # "Panel 1", + # "Panel 2", + # "Panel 3", + # ]) + + def do_test_add(self, socket_type): + tree, group_node = self.make_group_and_instance() + + in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, is_input=True) + self.assertSequenceEqual(tree.interface.ui_items, [in0]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], []) + + out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, is_output=True) + self.assertSequenceEqual(tree.interface.ui_items, [in0, out0]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0"]) + + panel0 = tree.interface.new_panel("Panel 0") + self.assertSequenceEqual(tree.interface.ui_items, [in0, out0, panel0]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0"]) + + # Add items to the panel. + in1 = tree.interface.new_socket("Input 1", socket_type=socket_type, is_input=True, parent=panel0) + self.assertSequenceEqual(tree.interface.ui_items, [in0, out0, panel0, in1]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0", "Input 1"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0"]) + + out1 = tree.interface.new_socket("Output 1", socket_type=socket_type, is_output=True, parent=panel0) + self.assertSequenceEqual(tree.interface.ui_items, [in0, out0, panel0, in1, out1]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0", "Input 1"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0", "Output 1"]) + + panel1 = tree.interface.new_panel("Panel 1", parent=panel0) + self.assertSequenceEqual(tree.interface.ui_items, [in0, out0, panel0, in1, out1, panel1]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0", "Input 1"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0", "Output 1"]) + + def do_test_remove(self, socket_type): + tree, group_node = self.make_group_and_instance() + + in0 = tree.interface.new_socket("Input 0", socket_type=socket_type, is_input=True) + out0 = tree.interface.new_socket("Output 0", socket_type=socket_type, is_output=True) + panel0 = tree.interface.new_panel("Panel 0") + in1 = tree.interface.new_socket("Input 1", socket_type=socket_type, is_input=True, parent=panel0) + out1 = tree.interface.new_socket("Output 1", socket_type=socket_type, is_output=True, parent=panel0) + panel1 = tree.interface.new_panel("Panel 1") + in2 = tree.interface.new_socket("Input 2", socket_type=socket_type, is_input=True, parent=panel1) + out2 = tree.interface.new_socket("Output 2", socket_type=socket_type, is_output=True, parent=panel1) + panel2 = tree.interface.new_panel("Panel 2") + + self.assertSequenceEqual(tree.interface.ui_items, [in0, out0, panel0, in1, out1, panel1, in2, out2, panel2]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0", "Input 1", "Input 2"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0", "Output 1", "Output 2"]) + + # Remove from root panel. + tree.interface.remove(in0) + self.assertSequenceEqual(tree.interface.ui_items, [out0, panel0, in1, out1, panel1, in2, out2, panel2]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1", "Input 2"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0", "Output 1", "Output 2"]) + + # Removing a panel should move content to the parent. + tree.interface.remove(panel0) + self.assertSequenceEqual(tree.interface.ui_items, [out0, in1, out1, panel1, in2, out2, panel2]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1", "Input 2"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0", "Output 1", "Output 2"]) + + tree.interface.remove(out0) + self.assertSequenceEqual(tree.interface.ui_items, [in1, out1, panel1, in2, out2, panel2]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1", "Input 2"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 1", "Output 2"]) + + # Remove content from panel + tree.interface.remove(out2) + self.assertSequenceEqual(tree.interface.ui_items, [in1, out1, panel1, in2, panel2]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1", "Input 2"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 1"]) + + # Remove a panel and its content + tree.interface.remove(panel1, move_content_to_parent=False) + self.assertSequenceEqual(tree.interface.ui_items, [in1, out1, panel2]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 1"]) + + # Remove empty panel + tree.interface.remove(panel2) + self.assertSequenceEqual(tree.interface.ui_items, [in1, out1]) + self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 1"]) + self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 1"]) + + +class GeometryNodeGroupInterfaceTest(AbstractNodeGroupInterfaceTest, NodeGroupInterfaceTests): + tree_type = "GeometryNodeTree" + group_node_type = "GeometryNodeGroup" + + def setUp(self): + super().setUp() + self.main_tree = bpy.data.node_groups.new("main", self.tree_type) + + def test_sockets_in_out(self): + self.do_test_sockets_in_out("NodeSocketFloat") + + def test_all_socket_types(self): + self.do_test_invalid_socket_type("INVALID_SOCKET_TYPE_11!1") + self.do_test_socket_type("NodeSocketBool") + self.do_test_socket_type("NodeSocketCollection") + self.do_test_socket_type("NodeSocketColor") + self.do_test_socket_type("NodeSocketFloat") + self.do_test_socket_type("NodeSocketGeometry") + self.do_test_socket_type("NodeSocketImage") + self.do_test_socket_type("NodeSocketInt") + self.do_test_socket_type("NodeSocketMaterial") + self.do_test_socket_type("NodeSocketObject") + self.do_test_socket_type("NodeSocketRotation") + self.do_test_invalid_socket_type("NodeSocketShader") + self.do_test_socket_type("NodeSocketString") + self.do_test_socket_type("NodeSocketTexture") + self.do_test_socket_type("NodeSocketVector") + self.do_test_invalid_socket_type("NodeSocketVirtual") + + def test_items_order_classic(self): + self.do_test_items_order_classic("NodeSocketFloat") + + def test_items_order_mixed_with_panels(self): + self.do_test_items_order_mixed_with_panels("NodeSocketFloat") + + def test_add(self): + self.do_test_add("NodeSocketFloat") + + def test_remove(self): + self.do_test_remove("NodeSocketFloat") + + +class ShaderNodeGroupInterfaceTest(AbstractNodeGroupInterfaceTest, NodeGroupInterfaceTests): + tree_type = "ShaderNodeTree" + group_node_type = "ShaderNodeGroup" + + def setUp(self): + super().setUp() + self.material = bpy.data.materials.new("test") + self.material.use_nodes = True + self.main_tree = self.material.node_tree + + def test_invalid_socket_type(self): + self.do_test_invalid_socket_type("INVALID_SOCKET_TYPE_11!1") + + def test_sockets_in_out(self): + self.do_test_sockets_in_out("NodeSocketFloat") + + def test_all_socket_types(self): + self.do_test_invalid_socket_type("NodeSocketBool") + self.do_test_invalid_socket_type("NodeSocketCollection") + self.do_test_socket_type("NodeSocketColor") + self.do_test_socket_type("NodeSocketFloat") + self.do_test_invalid_socket_type("NodeSocketGeometry") + self.do_test_invalid_socket_type("NodeSocketImage") + self.do_test_invalid_socket_type("NodeSocketInt") + self.do_test_invalid_socket_type("NodeSocketMaterial") + self.do_test_invalid_socket_type("NodeSocketObject") + self.do_test_invalid_socket_type("NodeSocketRotation") + self.do_test_socket_type("NodeSocketShader") + self.do_test_invalid_socket_type("NodeSocketString") + self.do_test_invalid_socket_type("NodeSocketTexture") + self.do_test_socket_type("NodeSocketVector") + self.do_test_invalid_socket_type("NodeSocketVirtual") + + def test_items_order_classic(self): + self.do_test_items_order_classic("NodeSocketFloat") + + def test_items_order_mixed_with_panels(self): + self.do_test_items_order_mixed_with_panels("NodeSocketFloat") + + def test_add(self): + self.do_test_add("NodeSocketFloat") + + def test_remove(self): + self.do_test_remove("NodeSocketFloat") + + +class CompositorNodeGroupInterfaceTest(AbstractNodeGroupInterfaceTest, NodeGroupInterfaceTests): + tree_type = "CompositorNodeTree" + group_node_type = "CompositorNodeGroup" + + def setUp(self): + super().setUp() + self.scene = bpy.data.scenes.new("test") + self.scene.use_nodes = True + self.main_tree = self.scene.node_tree + + def test_invalid_socket_type(self): + self.do_test_invalid_socket_type("INVALID_SOCKET_TYPE_11!1") + + def test_sockets_in_out(self): + self.do_test_sockets_in_out("NodeSocketFloat") + + def test_all_socket_types(self): + self.do_test_invalid_socket_type("NodeSocketBool") + self.do_test_invalid_socket_type("NodeSocketCollection") + self.do_test_socket_type("NodeSocketColor") + self.do_test_socket_type("NodeSocketFloat") + self.do_test_invalid_socket_type("NodeSocketGeometry") + self.do_test_invalid_socket_type("NodeSocketImage") + self.do_test_invalid_socket_type("NodeSocketInt") + self.do_test_invalid_socket_type("NodeSocketMaterial") + self.do_test_invalid_socket_type("NodeSocketObject") + self.do_test_invalid_socket_type("NodeSocketRotation") + self.do_test_invalid_socket_type("NodeSocketShader") + self.do_test_invalid_socket_type("NodeSocketString") + self.do_test_invalid_socket_type("NodeSocketTexture") + self.do_test_socket_type("NodeSocketVector") + self.do_test_invalid_socket_type("NodeSocketVirtual") + + def test_items_order_classic(self): + self.do_test_items_order_classic("NodeSocketFloat") + + def test_items_order_mixed_with_panels(self): + self.do_test_items_order_mixed_with_panels("NodeSocketFloat") + + def test_add(self): + self.do_test_add("NodeSocketFloat") + + def test_remove(self): + self.do_test_remove("NodeSocketFloat") + + +def main(): + global args + import argparse + + if '--' in sys.argv: + argv = [sys.argv[0]] + sys.argv[sys.argv.index('--') + 1:] + else: + argv = sys.argv + + parser = argparse.ArgumentParser() + parser.add_argument('--testdir', required=True, type=pathlib.Path) + args, remaining = parser.parse_known_args(argv) + + unittest.main(argv=remaining) + + +if __name__ == "__main__": + main() -- 2.30.2 From 5a19e46285f691647f89ae63f279b23455bf5e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Mon, 21 Aug 2023 12:05:06 +0200 Subject: [PATCH 09/14] Extented drop down for adding sockets to quickly add input or output. The socket added by the "Socket" drop down option was neither input nor output, which is confusing. To get sensible defaults, the menu now has separate "Input" and "Output" options. --- scripts/startup/bl_operators/node.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/scripts/startup/bl_operators/node.py b/scripts/startup/bl_operators/node.py index 8abb865b515..bc24e84105b 100644 --- a/scripts/startup/bl_operators/node.py +++ b/scripts/startup/bl_operators/node.py @@ -280,8 +280,11 @@ class NODE_OT_interface_item_new(NodeInterfaceOperator, Operator): item_type: EnumProperty( name="Item Type", description="Type of the item to create", - items=[('SOCKET', "Socket", ""), ('PANEL', "Panel", "")], - default='SOCKET', + items=[ + ('INPUT', "Input Socket", ""), + ('OUTPUT', "Output Socket", ""), + ('PANEL', "Panel", "")], + default='INPUT', ) socket_type = 'NodeSocketFloat' @@ -293,8 +296,10 @@ class NODE_OT_interface_item_new(NodeInterfaceOperator, Operator): # Remember index to move the item. dst_index = interface.active_index + 1 - if self.item_type == 'SOCKET': - item = interface.new_socket("Socket", socket_type=self.socket_type) + if self.item_type == 'INPUT': + item = interface.new_socket("Socket", socket_type=self.socket_type, is_input=True) + elif self.item_type == 'OUTPUT': + item = interface.new_socket("Socket", socket_type=self.socket_type, is_output=True) elif self.item_type == 'PANEL': item = interface.new_panel("Panel") else: -- 2.30.2 From 74081c3b005afa876c418ac4e3720cfe386a7795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Mon, 21 Aug 2023 12:59:45 +0200 Subject: [PATCH 10/14] Added description and "default closed" flags to panel declarations. --- scripts/startup/bl_ui/space_node.py | 2 ++ source/blender/blenkernel/BKE_node.h | 1 - .../blenkernel/BKE_node_tree_interface.hh | 2 +- .../blenkernel/intern/node_tree_interface.cc | 25 ++++++++++++----- .../blenloader/intern/versioning_400.cc | 4 +-- ...nterface_template_node_tree_declaration.cc | 10 +++---- .../editors/space_node/link_drag_search.cc | 4 +-- .../blender/editors/space_node/node_draw.cc | 12 +++------ .../makesdna/DNA_node_tree_interface_types.h | 27 ++++++++++++++----- .../intern/rna_node_tree_interface.cc | 24 +++++++++++++++-- source/blender/nodes/intern/node_common.cc | 4 +-- 11 files changed, 79 insertions(+), 36 deletions(-) diff --git a/scripts/startup/bl_ui/space_node.py b/scripts/startup/bl_ui/space_node.py index 2544de487a6..a270f4c8bad 100644 --- a/scripts/startup/bl_ui/space_node.py +++ b/scripts/startup/bl_ui/space_node.py @@ -928,6 +928,8 @@ class NODE_PT_node_tree_declaration(Panel): layout.prop(active_item, "default_attribute_name") if active_item.item_type == 'PANEL': layout.prop(active_item, "name") + layout.prop(active_item, "description") + layout.prop(active_item, "default_closed") class NODE_UL_simulation_zone_items(bpy.types.UIList): diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index a689c4da0ca..b9c625c3ca2 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -153,7 +153,6 @@ typedef struct CPPTypeHandle CPPTypeHandle; * * Defines the appearance and behavior of a socket in the UI. */ -/* XXX replace interface parts when old tree interfaces are fully deprecated */ typedef struct bNodeSocketType { /** Identifier name. */ char idname[64]; diff --git a/source/blender/blenkernel/BKE_node_tree_interface.hh b/source/blender/blenkernel/BKE_node_tree_interface.hh index 4ef1fc96cf9..73ee8017bdf 100644 --- a/source/blender/blenkernel/BKE_node_tree_interface.hh +++ b/source/blender/blenkernel/BKE_node_tree_interface.hh @@ -182,7 +182,7 @@ inline bNodeTreeInterfaceSocket *add_interface_socket_from_node(bNodeTree &ntree const StringRefNull socket_type, const StringRefNull name) { - eNodeTreeInterfaceSocketFlag flag = eNodeTreeInterfaceSocketFlag(0); + NodeTreeInterfaceSocketFlag flag = NodeTreeInterfaceSocketFlag(0); SET_FLAG_FROM_TEST(flag, from_sock.in_out & SOCK_IN, NODE_INTERFACE_SOCKET_INPUT); SET_FLAG_FROM_TEST(flag, from_sock.in_out & SOCK_OUT, NODE_INTERFACE_SOCKET_OUTPUT); diff --git a/source/blender/blenkernel/intern/node_tree_interface.cc b/source/blender/blenkernel/intern/node_tree_interface.cc index a539069e302..d8b39d545d3 100644 --- a/source/blender/blenkernel/intern/node_tree_interface.cc +++ b/source/blender/blenkernel/intern/node_tree_interface.cc @@ -490,6 +490,7 @@ static void item_copy(bNodeTreeInterfaceItem &dst, BLI_assert(src_panel.name != nullptr); dst_panel.name = BLI_strdup(src_panel.name); + dst_panel.description = BLI_strdup_null(src_panel.description); dst_panel.copy_from(src_panel.items(), flag); break; } @@ -523,6 +524,7 @@ static void item_free(bNodeTreeInterfaceItem &item, const bool do_id_user) panel.clear(do_id_user); MEM_SAFE_FREE(panel.name); + MEM_SAFE_FREE(panel.description); break; } } @@ -551,6 +553,7 @@ static void item_write_data(BlendWriter *writer, bNodeTreeInterfaceItem &item) case NODE_INTERFACE_PANEL: { bNodeTreeInterfacePanel &panel = reinterpret_cast(item); BLO_write_string(writer, panel.name); + BLO_write_string(writer, panel.description); BLO_write_pointer_array(writer, panel.items_num, panel.items_array); for (bNodeTreeInterfaceItem *child_item : panel.items()) { item_write_struct(writer, *child_item); @@ -595,6 +598,7 @@ static void item_read_data(BlendDataReader *reader, bNodeTreeInterfaceItem &item case NODE_INTERFACE_PANEL: { bNodeTreeInterfacePanel &panel = reinterpret_cast(item); BLO_read_data_address(reader, &panel.name); + BLO_read_data_address(reader, &panel.description); BLO_read_pointer_array(reader, reinterpret_cast(&panel.items_array)); for (const int i : blender::IndexRange(panel.items_num)) { BLO_read_data_address(reader, &panel.items_array[i]); @@ -1016,7 +1020,7 @@ static bNodeTreeInterfaceSocket *make_socket(const int uid, blender::StringRefNull name, blender::StringRefNull description, blender::StringRefNull socket_type, - const eNodeTreeInterfaceSocketFlag flag) + const NodeTreeInterfaceSocketFlag flag) { BLI_assert(name.c_str() != nullptr); BLI_assert(socket_type.c_str() != nullptr); @@ -1042,14 +1046,19 @@ static bNodeTreeInterfaceSocket *make_socket(const int uid, return new_socket; } -static bNodeTreeInterfacePanel *make_panel(const int uid, blender::StringRefNull name) +static bNodeTreeInterfacePanel *make_panel(const int uid, + blender::StringRefNull name, + blender::StringRefNull description, + const NodeTreeInterfacePanelFlag flag) { BLI_assert(name.c_str() != nullptr); bNodeTreeInterfacePanel *new_panel = MEM_cnew(__func__); new_panel->item.item_type = NODE_INTERFACE_PANEL; new_panel->name = BLI_strdup(name.c_str()); + new_panel->description = BLI_strdup_null(description.c_str()); new_panel->identifier = uid; + new_panel->flag = flag; return new_panel; } @@ -1148,7 +1157,7 @@ void bNodeTreeInterface::active_item_set(bNodeTreeInterfaceItem *item) bNodeTreeInterfaceSocket *bNodeTreeInterface::add_socket(blender::StringRefNull name, blender::StringRefNull description, blender::StringRefNull socket_type, - const eNodeTreeInterfaceSocketFlag flag, + const NodeTreeInterfaceSocketFlag flag, bNodeTreeInterfacePanel *parent) { if (parent == nullptr) { @@ -1168,7 +1177,7 @@ bNodeTreeInterfaceSocket *bNodeTreeInterface::insert_socket( blender::StringRefNull name, blender::StringRefNull description, blender::StringRefNull socket_type, - const eNodeTreeInterfaceSocketFlag flag, + const NodeTreeInterfaceSocketFlag flag, bNodeTreeInterfacePanel *parent, const int position) { @@ -1186,6 +1195,8 @@ bNodeTreeInterfaceSocket *bNodeTreeInterface::insert_socket( } bNodeTreeInterfacePanel *bNodeTreeInterface::add_panel(blender::StringRefNull name, + blender::StringRefNull description, + const NodeTreeInterfacePanelFlag flag, bNodeTreeInterfacePanel *parent) { if (parent == nullptr) { @@ -1193,7 +1204,7 @@ bNodeTreeInterfacePanel *bNodeTreeInterface::add_panel(blender::StringRefNull na } BLI_assert(this->find_item(parent->item)); - bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name); + bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name, description, flag); if (new_panel) { parent->add_item(new_panel->item); } @@ -1201,6 +1212,8 @@ bNodeTreeInterfacePanel *bNodeTreeInterface::add_panel(blender::StringRefNull na } bNodeTreeInterfacePanel *bNodeTreeInterface::insert_panel(blender::StringRefNull name, + blender::StringRefNull description, + const NodeTreeInterfacePanelFlag flag, bNodeTreeInterfacePanel *parent, const int position) { @@ -1209,7 +1222,7 @@ bNodeTreeInterfacePanel *bNodeTreeInterface::insert_panel(blender::StringRefNull } BLI_assert(this->find_item(parent->item)); - bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name); + bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name, description, flag); if (new_panel) { parent->insert_item(new_panel->item, position); } diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 85bc19407ad..d27d0e9d462 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -424,7 +424,7 @@ static void versioning_convert_node_tree_socket_lists_to_interface(bNodeTree *nt bNodeTreeInterface &tree_interface = ntree->tree_interface; LISTBASE_FOREACH (const bNodeSocket *, socket, &ntree->inputs_legacy) { - eNodeTreeInterfaceSocketFlag flag = NODE_INTERFACE_SOCKET_INPUT; + NodeTreeInterfaceSocketFlag flag = NODE_INTERFACE_SOCKET_INPUT; SET_FLAG_FROM_TEST(flag, socket->flag & SOCK_HIDE_VALUE, NODE_INTERFACE_SOCKET_HIDE_VALUE); SET_FLAG_FROM_TEST( flag, socket->flag & SOCK_HIDE_IN_MODIFIER, NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER); @@ -436,7 +436,7 @@ static void versioning_convert_node_tree_socket_lists_to_interface(bNodeTree *nt new_socket->identifier = BLI_strdup(socket->identifier); } LISTBASE_FOREACH (const bNodeSocket *, socket, &ntree->outputs_legacy) { - eNodeTreeInterfaceSocketFlag flag = NODE_INTERFACE_SOCKET_OUTPUT; + NodeTreeInterfaceSocketFlag flag = NODE_INTERFACE_SOCKET_OUTPUT; SET_FLAG_FROM_TEST(flag, socket->flag & SOCK_HIDE_VALUE, NODE_INTERFACE_SOCKET_HIDE_VALUE); SET_FLAG_FROM_TEST( flag, socket->flag & SOCK_HIDE_IN_MODIFIER, NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER); diff --git a/source/blender/editors/interface/interface_template_node_tree_declaration.cc b/source/blender/editors/interface/interface_template_node_tree_declaration.cc index a2a9005acdf..e8fc1910d87 100644 --- a/source/blender/editors/interface/interface_template_node_tree_declaration.cc +++ b/source/blender/editors/interface/interface_template_node_tree_declaration.cc @@ -102,9 +102,8 @@ class NodeSocketViewItem : public BasicTreeViewItem { if (socket_.flag & NODE_INTERFACE_SOCKET_INPUT) { /* XXX Socket template only draws in embossed layouts (Julian). */ uiLayoutSetEmboss(input_socket_layout, UI_EMBOSS); - /* XXX Context is not used by the template function. */ - bContext *C = nullptr; - uiTemplateNodeSocket(input_socket_layout, C, socket_.socket_color()); + /* Context is not used by the template function. */ + uiTemplateNodeSocket(input_socket_layout, /*C*/ nullptr, socket_.socket_color()); } else { /* Blank item to align output socket labels with inputs. */ @@ -117,9 +116,8 @@ class NodeSocketViewItem : public BasicTreeViewItem { if (socket_.flag & NODE_INTERFACE_SOCKET_OUTPUT) { /* XXX Socket template only draws in embossed layouts (Julian). */ uiLayoutSetEmboss(output_socket_layout, UI_EMBOSS); - /* XXX Context is not used by the template function. */ - bContext *C = nullptr; - uiTemplateNodeSocket(output_socket_layout, C, socket_.socket_color()); + /* Context is not used by the template function. */ + uiTemplateNodeSocket(output_socket_layout, /*C*/ nullptr, socket_.socket_color()); } else { /* Blank item to align input socket labels with outputs. */ diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc index 80dc301c963..cf18616b9e9 100644 --- a/source/blender/editors/space_node/link_drag_search.cc +++ b/source/blender/editors/space_node/link_drag_search.cc @@ -88,7 +88,7 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams ¶ms) { /* Add a group input based on the connected socket, and add a new group input node. */ const eNodeSocketInOut in_out = eNodeSocketInOut(params.socket.in_out); - eNodeTreeInterfaceSocketFlag flag = eNodeTreeInterfaceSocketFlag(0); + NodeTreeInterfaceSocketFlag flag = NodeTreeInterfaceSocketFlag(0); SET_FLAG_FROM_TEST(flag, in_out & SOCK_IN, NODE_INTERFACE_SOCKET_INPUT); SET_FLAG_FROM_TEST(flag, in_out & SOCK_OUT, NODE_INTERFACE_SOCKET_OUTPUT); bNodeTreeInterfaceSocket *socket_iface = params.node_tree.tree_interface.add_socket( @@ -132,7 +132,7 @@ static void add_existing_group_input_fn(nodes::LinkSearchOpParams ¶ms, const bNodeTreeInterfaceSocket &interface_socket) { const eNodeSocketInOut in_out = eNodeSocketInOut(params.socket.in_out); - eNodeTreeInterfaceSocketFlag flag = eNodeTreeInterfaceSocketFlag(0); + NodeTreeInterfaceSocketFlag flag = NodeTreeInterfaceSocketFlag(0); SET_FLAG_FROM_TEST(flag, in_out & SOCK_IN, NODE_INTERFACE_SOCKET_INPUT); SET_FLAG_FROM_TEST(flag, in_out & SOCK_OUT, NODE_INTERFACE_SOCKET_OUTPUT); diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 65cc38a71e3..b7203d8070f 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -524,10 +524,8 @@ static void node_update_basis_from_declaration( { switch (socket_decl->in_out) { case SOCK_IN: - if (!current_input) { - /* XXX should match the declaration, assert? */ - break; - } + /* Must match the declaration. */ + BLI_assert(current_input != nullptr); SET_FLAG_FROM_TEST(current_input->flag, is_parent_collapsed, SOCK_PANEL_COLLAPSED); if (is_parent_collapsed) { @@ -540,10 +538,8 @@ static void node_update_basis_from_declaration( current_input = current_input->next; break; case SOCK_OUT: - if (!current_output) { - /* XXX should match the declaration, assert? */ - break; - } + /* Must match the declaration. */ + BLI_assert(current_output != nullptr); SET_FLAG_FROM_TEST(current_output->flag, is_parent_collapsed, SOCK_PANEL_COLLAPSED); if (is_parent_collapsed) { diff --git a/source/blender/makesdna/DNA_node_tree_interface_types.h b/source/blender/makesdna/DNA_node_tree_interface_types.h index 601c0840007..4c657b25606 100644 --- a/source/blender/makesdna/DNA_node_tree_interface_types.h +++ b/source/blender/makesdna/DNA_node_tree_interface_types.h @@ -53,13 +53,13 @@ typedef struct bNodeTreeInterfaceItem { } bNodeTreeInterfaceItem; /* Socket interface flags */ -typedef enum eNodeTreeInterfaceSocketFlag { +typedef enum NodeTreeInterfaceSocketFlag { NODE_INTERFACE_SOCKET_INPUT = 1 << 0, NODE_INTERFACE_SOCKET_OUTPUT = 1 << 1, NODE_INTERFACE_SOCKET_HIDE_VALUE = 1 << 2, NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER = 1 << 3, -} eNodeTreeInterfaceSocketFlag; -ENUM_OPERATORS(eNodeTreeInterfaceSocketFlag, NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER); +} NodeTreeInterfaceSocketFlag; +ENUM_OPERATORS(NodeTreeInterfaceSocketFlag, NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER); typedef struct bNodeTreeInterfaceSocket { bNodeTreeInterfaceItem item; @@ -101,11 +101,21 @@ typedef struct bNodeTreeInterfaceSocket { #endif } bNodeTreeInterfaceSocket; +/* Panel interface flags */ +typedef enum NodeTreeInterfacePanelFlag { + NODE_INTERFACE_PANEL_DEFAULT_CLOSED = 1 << 0, +} NodeTreeInterfacePanelFlag; +ENUM_OPERATORS(NodeTreeInterfacePanelFlag, NODE_INTERFACE_PANEL_DEFAULT_CLOSED); + typedef struct bNodeTreeInterfacePanel { bNodeTreeInterfaceItem item; /* UI name of the panel. */ char *name; + char *description; + /* eNodeTreeInterfacePanelFlag */ + int flag; + char _pad[4]; bNodeTreeInterfaceItem **items_array; int items_num; @@ -258,7 +268,7 @@ typedef struct bNodeTreeInterface { bNodeTreeInterfaceSocket *add_socket(blender::StringRefNull name, blender::StringRefNull description, blender::StringRefNull socket_type, - eNodeTreeInterfaceSocketFlag flag, + NodeTreeInterfaceSocketFlag flag, bNodeTreeInterfacePanel *parent); /** * Insert a new socket. @@ -269,7 +279,7 @@ typedef struct bNodeTreeInterface { bNodeTreeInterfaceSocket *insert_socket(blender::StringRefNull name, blender::StringRefNull description, blender::StringRefNull socket_type, - eNodeTreeInterfaceSocketFlag flag, + NodeTreeInterfaceSocketFlag flag, bNodeTreeInterfacePanel *parent, int position); @@ -278,7 +288,10 @@ typedef struct bNodeTreeInterface { * \param parent: Panel in which the new panel is added as a child. If parent is null the new * panel is made a child of the root panel. */ - bNodeTreeInterfacePanel *add_panel(blender::StringRefNull name, bNodeTreeInterfacePanel *parent); + bNodeTreeInterfacePanel *add_panel(blender::StringRefNull name, + blender::StringRefNull description, + const NodeTreeInterfacePanelFlag flag, + bNodeTreeInterfacePanel *parent); /** * Insert a new panel. * \param parent: Panel in which the new panel is added as a child. If parent is null the new @@ -286,6 +299,8 @@ typedef struct bNodeTreeInterface { * \param position: Position of the child panel within the parent panel. */ bNodeTreeInterfacePanel *insert_panel(blender::StringRefNull name, + blender::StringRefNull description, + const NodeTreeInterfacePanelFlag flag, bNodeTreeInterfacePanel *parent, int position); diff --git a/source/blender/makesrna/intern/rna_node_tree_interface.cc b/source/blender/makesrna/intern/rna_node_tree_interface.cc index 9fa103398dc..6df791182f5 100644 --- a/source/blender/makesrna/intern/rna_node_tree_interface.cc +++ b/source/blender/makesrna/intern/rna_node_tree_interface.cc @@ -415,7 +415,7 @@ static bNodeTreeInterfaceSocket *rna_NodeTreeInterfaceItems_new_socket( } const char *socket_type = typeinfo->idname; - eNodeTreeInterfaceSocketFlag flag = eNodeTreeInterfaceSocketFlag(0); + NodeTreeInterfaceSocketFlag flag = NodeTreeInterfaceSocketFlag(0); SET_FLAG_FROM_TEST(flag, is_input, NODE_INTERFACE_SOCKET_INPUT); SET_FLAG_FROM_TEST(flag, is_output, NODE_INTERFACE_SOCKET_OUTPUT); @@ -443,6 +443,8 @@ static bNodeTreeInterfacePanel *rna_NodeTreeInterfaceItems_new_panel( Main *bmain, ReportList *reports, const char *name, + const char *description, + bool default_closed, bNodeTreeInterfacePanel *parent) { if (parent != nullptr && !interface->find_item(parent->item)) { @@ -450,7 +452,11 @@ static bNodeTreeInterfacePanel *rna_NodeTreeInterfaceItems_new_panel( return nullptr; } - bNodeTreeInterfacePanel *panel = interface->add_panel(name ? name : "", parent); + NodeTreeInterfacePanelFlag flag = NodeTreeInterfacePanelFlag(0); + SET_FLAG_FROM_TEST(flag, default_closed, NODE_INTERFACE_PANEL_DEFAULT_CLOSED); + + bNodeTreeInterfacePanel *panel = interface->add_panel( + name ? name : "", description ? description : "", flag, parent); if (panel == nullptr) { BKE_report(reports, RPT_ERROR, "Unable to create panel"); @@ -870,6 +876,17 @@ static void rna_def_node_interface_panel(BlenderRNA *brna) RNA_def_struct_name_property(srna, prop); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); + prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, nullptr, "description"); + RNA_def_property_ui_text(prop, "Description", "Panel description"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); + + prop = RNA_def_property(srna, "default_closed", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_INTERFACE_PANEL_DEFAULT_CLOSED); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Default Closed", "Panel is closed by default on new nodes"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update"); + prop = RNA_def_property(srna, "interface_items", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, nullptr, "items_array", "items_num"); RNA_def_property_struct_type(prop, "NodeTreeInterfaceItem"); @@ -927,6 +944,9 @@ static void rna_def_node_tree_interface_items_api(StructRNA *srna) RNA_def_function_ui_description(func, "Add a new panel to the interface"); RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS); parm = RNA_def_string(func, "name", nullptr, 0, "Name", "Name of the new panel"); + RNA_def_string(func, "description", nullptr, 0, "Description", "Description of the panel"); + RNA_def_boolean( + func, "default_closed", false, "Default Closed", "Panel is closed by default on new nodes"); RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED); RNA_def_pointer(func, "parent", diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc index 1eb5d26e4cf..f7c17bf0f47 100644 --- a/source/blender/nodes/intern/node_common.cc +++ b/source/blender/nodes/intern/node_common.cc @@ -321,8 +321,8 @@ static PanelDeclarationPtr declaration_for_interface_panel(const bNodeTree & /*n PanelDeclarationPtr dst = std::make_unique(); dst->uid = io_panel.identifier; dst->name = io_panel.name ? io_panel.name : ""; - dst->description = ""; /* TODO io_panel.description */ - dst->default_collapsed = false; /* TODO io_panel.default_collapsed */ + dst->description = io_panel.description ? io_panel.description : ""; + dst->default_collapsed = (io_panel.flag & NODE_INTERFACE_PANEL_DEFAULT_CLOSED); dst->num_items = io_panel.items_num; return dst; } -- 2.30.2 From 92b382f8599db81a0230db44477ae5b7dab0e05b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Mon, 21 Aug 2023 13:00:31 +0200 Subject: [PATCH 11/14] Cleanup: Fixed TODO comment. --- source/blender/editors/space_node/node_draw.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index b7203d8070f..086c5f1365a 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -565,9 +565,8 @@ static void node_update_basis_from_declaration( panel_updates.pop(); } } - if (!panel_updates.is_empty()) { - /* TODO warning, more panel items declared than added. */ - } + /* Enough items should have been added to close all panels. */ + BLI_assert(panel_updates.is_empty()); /* Little bit of space in end. */ if (has_sockets || !has_buttons) { -- 2.30.2 From 53c0a3ee0424cab1dc64abcba761058218becd84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Mon, 21 Aug 2023 14:14:07 +0200 Subject: [PATCH 12/14] Added a flag to control when a panel is allowed to contain other panels. --- .../blenkernel/intern/node_tree_interface.cc | 53 +++++++++++++++---- .../makesdna/DNA_node_tree_interface_types.h | 3 ++ 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/intern/node_tree_interface.cc b/source/blender/blenkernel/intern/node_tree_interface.cc index d8b39d545d3..6a0f919c047 100644 --- a/source/blender/blenkernel/intern/node_tree_interface.cc +++ b/source/blender/blenkernel/intern/node_tree_interface.cc @@ -852,6 +852,10 @@ bNodeTreeInterfacePanel *bNodeTreeInterfacePanel::find_parent_recursive( void bNodeTreeInterfacePanel::add_item(bNodeTreeInterfaceItem &item) { + /* Are child panels allowed? */ + BLI_assert(item.item_type != NODE_INTERFACE_PANEL || + (flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)); + blender::MutableSpan old_items = this->items(); items_num++; items_array = MEM_cnew_array(items_num, __func__); @@ -865,6 +869,10 @@ void bNodeTreeInterfacePanel::add_item(bNodeTreeInterfaceItem &item) void bNodeTreeInterfacePanel::insert_item(bNodeTreeInterfaceItem &item, int position) { + /* Are child panels allowed? */ + BLI_assert(item.item_type != NODE_INTERFACE_PANEL || + (flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)); + position = std::min(std::max(position, 0), items_num); blender::MutableSpan old_items = this->items(); @@ -1071,6 +1079,8 @@ void bNodeTreeInterfacePanel::copy_from( /* Copy buffers. */ for (const int i : items_src.index_range()) { const bNodeTreeInterfaceItem *item_src = items_src[i]; + BLI_assert(item_src->item_type != NODE_INTERFACE_PANEL || + (flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)); items_array[i] = static_cast(MEM_dupallocN(item_src)); item_types::item_copy(*items_array[i], *item_src, flag); } @@ -1173,13 +1183,12 @@ bNodeTreeInterfaceSocket *bNodeTreeInterface::add_socket(blender::StringRefNull return new_socket; } -bNodeTreeInterfaceSocket *bNodeTreeInterface::insert_socket( - blender::StringRefNull name, - blender::StringRefNull description, - blender::StringRefNull socket_type, - const NodeTreeInterfaceSocketFlag flag, - bNodeTreeInterfacePanel *parent, - const int position) +bNodeTreeInterfaceSocket *bNodeTreeInterface::insert_socket(blender::StringRefNull name, + blender::StringRefNull description, + blender::StringRefNull socket_type, + const NodeTreeInterfaceSocketFlag flag, + bNodeTreeInterfacePanel *parent, + const int position) { if (parent == nullptr) { parent = &root_panel; @@ -1204,6 +1213,11 @@ bNodeTreeInterfacePanel *bNodeTreeInterface::add_panel(blender::StringRefNull na } BLI_assert(this->find_item(parent->item)); + if (!(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) { + /* Parent does not allow adding child panels. */ + return nullptr; + } + bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name, description, flag); if (new_panel) { parent->add_item(new_panel->item); @@ -1222,6 +1236,11 @@ bNodeTreeInterfacePanel *bNodeTreeInterface::insert_panel(blender::StringRefNull } BLI_assert(this->find_item(parent->item)); + if (!(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) { + /* Parent does not allow adding child panels. */ + return nullptr; + } + bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name, description, flag); if (new_panel) { parent->insert_item(new_panel->item, position); @@ -1238,8 +1257,11 @@ bNodeTreeInterfaceItem *bNodeTreeInterface::add_item_copy(const bNodeTreeInterfa BLI_assert(this->find_item(item)); BLI_assert(this->find_item(parent->item)); - if (parent == nullptr) { - parent = &root_panel; + if (item.item_type == NODE_INTERFACE_PANEL && + !(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) + { + /* Parent does not allow adding child panels. */ + return nullptr; } bNodeTreeInterfaceItem *citem = static_cast(MEM_dupallocN(&item)); @@ -1259,6 +1281,13 @@ bNodeTreeInterfaceItem *bNodeTreeInterface::insert_item_copy(const bNodeTreeInte BLI_assert(this->find_item(item)); BLI_assert(this->find_item(parent->item)); + if (item.item_type == NODE_INTERFACE_PANEL && + !(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) + { + /* Parent does not allow adding child panels. */ + return nullptr; + } + bNodeTreeInterfaceItem *citem = static_cast(MEM_dupallocN(&item)); item_types::item_copy(*citem, item, 0); parent->insert_item(*citem, position); @@ -1308,6 +1337,12 @@ bool bNodeTreeInterface::move_item_to_parent(bNodeTreeInterfaceItem &item, if (parent == nullptr) { return false; } + if (item.item_type == NODE_INTERFACE_PANEL && + !(new_parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) + { + /* Parent does not allow adding child panels. */ + return false; + } if (parent->remove_item(item, false)) { new_parent->insert_item(item, new_position); return true; diff --git a/source/blender/makesdna/DNA_node_tree_interface_types.h b/source/blender/makesdna/DNA_node_tree_interface_types.h index 4c657b25606..fc5c81bce5c 100644 --- a/source/blender/makesdna/DNA_node_tree_interface_types.h +++ b/source/blender/makesdna/DNA_node_tree_interface_types.h @@ -103,7 +103,10 @@ typedef struct bNodeTreeInterfaceSocket { /* Panel interface flags */ typedef enum NodeTreeInterfacePanelFlag { + /* Panel starts closed on new node instances. */ NODE_INTERFACE_PANEL_DEFAULT_CLOSED = 1 << 0, + /* Allow child panels inside this panel. */ + NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS = 1 << 1, } NodeTreeInterfacePanelFlag; ENUM_OPERATORS(NodeTreeInterfacePanelFlag, NODE_INTERFACE_PANEL_DEFAULT_CLOSED); -- 2.30.2 From 2cb1aa06ee5049442f3ce11ed6d42b49f5878af0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Mon, 21 Aug 2023 14:30:22 +0200 Subject: [PATCH 13/14] Allow child panels in the interface root panel. --- source/blender/blenkernel/intern/node.cc | 1 + source/blender/blenkernel/intern/node_tree_interface.cc | 6 ++++++ source/blender/makesdna/DNA_node_tree_interface_types.h | 2 ++ 3 files changed, 9 insertions(+) diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 9e232689a32..76541f02df7 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -145,6 +145,7 @@ static void node_socket_interface_free(bNodeTree * /*ntree*/, static void ntree_init_data(ID *id) { bNodeTree *ntree = reinterpret_cast(id); + ntree->tree_interface.init_data(); ntree->runtime = MEM_new(__func__); ntree_set_typeinfo(ntree, nullptr); } diff --git a/source/blender/blenkernel/intern/node_tree_interface.cc b/source/blender/blenkernel/intern/node_tree_interface.cc index 6a0f919c047..77c92b619cf 100644 --- a/source/blender/blenkernel/intern/node_tree_interface.cc +++ b/source/blender/blenkernel/intern/node_tree_interface.cc @@ -1086,6 +1086,12 @@ void bNodeTreeInterfacePanel::copy_from( } } +void bNodeTreeInterface::init_data() +{ + /* Root panel is allowed to contain child panels. */ + root_panel.flag |= NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS; +} + void bNodeTreeInterface::copy_data(const bNodeTreeInterface &src, int flag) { this->root_panel.copy_from(src.root_panel.items(), flag); diff --git a/source/blender/makesdna/DNA_node_tree_interface_types.h b/source/blender/makesdna/DNA_node_tree_interface_types.h index fc5c81bce5c..dfa6225b2ee 100644 --- a/source/blender/makesdna/DNA_node_tree_interface_types.h +++ b/source/blender/makesdna/DNA_node_tree_interface_types.h @@ -212,6 +212,8 @@ typedef struct bNodeTreeInterface { #ifdef __cplusplus + /** Initialize data of new interface instance. */ + void init_data(); /** Copy data from another interface. * \param flag: ID creation/copying flags, e.g. LIB_ID_CREATE_NO_MAIN. */ -- 2.30.2 From a837b068eb5f160ee044d5393d030efe0058cc47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20T=C3=B6nne?= Date: Mon, 21 Aug 2023 15:35:58 +0200 Subject: [PATCH 14/14] Check if child panels are allowed before trying to create them. --- .../intern/rna_node_tree_interface.cc | 45 +++++++++++++++---- tests/python/bl_node_group_interface.py | 5 ++- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/source/blender/makesrna/intern/rna_node_tree_interface.cc b/source/blender/makesrna/intern/rna_node_tree_interface.cc index 6df791182f5..5b0d467eda6 100644 --- a/source/blender/makesrna/intern/rna_node_tree_interface.cc +++ b/source/blender/makesrna/intern/rna_node_tree_interface.cc @@ -447,9 +447,15 @@ static bNodeTreeInterfacePanel *rna_NodeTreeInterfaceItems_new_panel( bool default_closed, bNodeTreeInterfacePanel *parent) { - if (parent != nullptr && !interface->find_item(parent->item)) { - BKE_report(reports, RPT_ERROR_INVALID_INPUT, "Parent is not part of the interface"); - return nullptr; + if (parent != nullptr) { + if (!interface->find_item(parent->item)) { + BKE_report(reports, RPT_ERROR_INVALID_INPUT, "Parent is not part of the interface"); + return nullptr; + } + if (!(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) { + BKE_report(reports, RPT_WARNING, "Parent panel does not allow child panels"); + return nullptr; + } } NodeTreeInterfacePanelFlag flag = NodeTreeInterfacePanelFlag(0); @@ -479,9 +485,17 @@ static bNodeTreeInterfaceItem *rna_NodeTreeInterfaceItems_copy_to_parent( bNodeTreeInterfaceItem *item, bNodeTreeInterfacePanel *parent) { - if (parent != nullptr && !interface->find_item(parent->item)) { - BKE_report(reports, RPT_ERROR_INVALID_INPUT, "Parent is not part of the interface"); - return nullptr; + if (parent != nullptr) { + if (!interface->find_item(parent->item)) { + BKE_report(reports, RPT_ERROR_INVALID_INPUT, "Parent is not part of the interface"); + return nullptr; + } + if (item->item_type == NODE_INTERFACE_PANEL && + !(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) + { + BKE_report(reports, RPT_WARNING, "Parent panel does not allow child panels"); + return nullptr; + } } if (parent == nullptr) { @@ -559,10 +573,18 @@ static void rna_NodeTreeInterfaceItems_move( static void rna_NodeTreeInterfaceItems_move_to_parent(ID *id, bNodeTreeInterface *interface, Main *bmain, + ReportList *reports, bNodeTreeInterfaceItem *item, bNodeTreeInterfacePanel *parent, int to_index) { + if (item->item_type == NODE_INTERFACE_PANEL && + !(parent->flag & NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS)) + { + BKE_report(reports, RPT_WARNING, "Parent panel does not allow child panels"); + return; + } + interface->move_item_to_parent(*item, parent, to_index); bNodeTree *ntree = reinterpret_cast(id); @@ -892,6 +914,12 @@ static void rna_def_node_interface_panel(BlenderRNA *brna) RNA_def_property_struct_type(prop, "NodeTreeInterfaceItem"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Items", "Items in the node panel"); + + prop = RNA_def_property(srna, "is_child_panel_allowed", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, "Is Child Panel Allowed", "True if the panel can contain child panels"); } static void rna_def_node_tree_interface_items_api(StructRNA *srna) @@ -931,7 +959,8 @@ static void rna_def_node_tree_interface_items_api(StructRNA *srna) 0, "Socket Type", "Type of socket generated on nodes"); - /* Note: itemf callback works for the function parameter, it does not require a data pointer. */ + /* Note: itemf callback works for the function parameter, it does not require a data pointer. + */ RNA_def_property_enum_funcs( parm, nullptr, nullptr, "rna_NodeTreeInterfaceSocket_socket_type_itemf"); RNA_def_pointer( @@ -994,7 +1023,7 @@ static void rna_def_node_tree_interface_items_api(StructRNA *srna) func = RNA_def_function(srna, "move_to_parent", "rna_NodeTreeInterfaceItems_move_to_parent"); RNA_def_function_ui_description(func, "Move an item to a new panel and/or position."); - RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS); parm = RNA_def_pointer(func, "item", "NodeTreeInterfaceItem", "Item", "The item to remove"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); parm = RNA_def_pointer( diff --git a/tests/python/bl_node_group_interface.py b/tests/python/bl_node_group_interface.py index d7139714e79..0e57eba785e 100644 --- a/tests/python/bl_node_group_interface.py +++ b/tests/python/bl_node_group_interface.py @@ -280,8 +280,11 @@ class NodeGroupInterfaceTests: self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0", "Input 1"]) self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0", "Output 1"]) + # Nested panel is not allowed, should produce error messages. + self.assertFalse(panel0.is_child_panel_allowed) panel1 = tree.interface.new_panel("Panel 1", parent=panel0) - self.assertSequenceEqual(tree.interface.ui_items, [in0, out0, panel0, in1, out1, panel1]) + self.assertIsNone(panel1) + self.assertSequenceEqual(tree.interface.ui_items, [in0, out0, panel0, in1, out1]) self.assertSequenceEqual([s.name for s in group_node.inputs], ["Input 0", "Input 1"]) self.assertSequenceEqual([s.name for s in group_node.outputs], ["Output 0", "Output 1"]) -- 2.30.2