Blender V4.3
node_tree_interface.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BKE_idprop.hh"
6#include "BKE_lib_id.hh"
7#include "BKE_lib_query.hh"
8#include "BKE_node.hh"
9#include "BKE_node_enum.hh"
11
12#include "BLI_math_vector.h"
13#include "BLI_stack.hh"
14#include "BLI_string.h"
15#include "BLI_vector.hh"
16
17#include "BLO_read_write.hh"
18
20#include "DNA_material_types.h"
22#include "DNA_node_types.h"
23
32
34
35namespace socket_types {
36
37/* Try to get a supported socket type from some final type.
38 * Built-in socket can have multiple registered RNA types for the base type, e.g.
39 * `NodeSocketFloatUnsigned`, `NodeSocketFloatFactor`. Only the "base type" (`NodeSocketFloat`)
40 * is considered valid for interface sockets.
41 */
42static const char *try_get_supported_socket_type(const StringRef socket_type)
43{
44 /* Make a copy of the string for `.c_str()` until the socket type map uses C++ types. */
45 const std::string idname(socket_type);
46 const blender::bke::bNodeSocketType *typeinfo = bke::node_socket_type_find(idname.c_str());
47 if (typeinfo == nullptr) {
48 return nullptr;
49 }
50 /* For builtin socket types only the base type is supported. */
51 if (node_is_static_socket_type(typeinfo)) {
53 }
54 return typeinfo->idname;
55}
56
57/* -------------------------------------------------------------------- */
61template<typename T> void socket_data_id_user_increment(T & /*data*/) {}
63{
64 id_us_plus(reinterpret_cast<ID *>(data.value));
65}
67{
68 id_us_plus(reinterpret_cast<ID *>(data.value));
69}
71{
72 id_us_plus(reinterpret_cast<ID *>(data.value));
73}
75{
76 id_us_plus(reinterpret_cast<ID *>(data.value));
77}
79{
80 id_us_plus(reinterpret_cast<ID *>(data.value));
81}
82
85/* -------------------------------------------------------------------- */
89template<typename T> void socket_data_id_user_decrement(T & /*data*/) {}
91{
92 id_us_min(reinterpret_cast<ID *>(data.value));
93}
95{
96 id_us_min(reinterpret_cast<ID *>(data.value));
97}
99{
100 id_us_min(reinterpret_cast<ID *>(data.value));
101}
103{
104 id_us_min(reinterpret_cast<ID *>(data.value));
105}
107{
108 id_us_min(reinterpret_cast<ID *>(data.value));
109}
110
113/* -------------------------------------------------------------------- */
117template<typename T> void socket_data_init_impl(T & /*data*/) {}
119{
120 data.subtype = PROP_NONE;
121 data.value = 0.0f;
122 data.min = -FLT_MAX;
123 data.max = FLT_MAX;
124}
126{
127 data.subtype = PROP_NONE;
128 data.value = 0;
129 data.min = INT_MIN;
130 data.max = INT_MAX;
131}
133{
134 data.value = false;
135}
138{
139 static float default_value[] = {0.0f, 0.0f, 0.0f};
140 data.subtype = PROP_NONE;
141 copy_v3_v3(data.value, default_value);
142 data.min = -FLT_MAX;
143 data.max = FLT_MAX;
144}
146{
147 static float default_value[] = {0.0f, 0.0f, 0.0f, 1.0f};
148 copy_v4_v4(data.value, default_value);
149}
151{
152 data.subtype = PROP_NONE;
153 data.value[0] = '\0';
154}
156{
157 data.value = nullptr;
158}
160{
161 data.value = nullptr;
162}
164{
165 data.value = nullptr;
166}
168{
169 data.value = nullptr;
170}
172{
173 data.value = nullptr;
174}
176{
177 data.value = -1;
178 data.enum_items = nullptr;
179 data.runtime_flag = 0;
180}
181
182static void *make_socket_data(const StringRef socket_type)
183{
184 void *socket_data = nullptr;
185 socket_data_to_static_type_tag(socket_type, [&socket_data](auto type_tag) {
186 using SocketDataType = typename decltype(type_tag)::type;
187 SocketDataType *new_socket_data = MEM_cnew<SocketDataType>(__func__);
188 socket_data_init_impl(*new_socket_data);
189 socket_data = new_socket_data;
190 });
191 return socket_data;
192}
193
196/* -------------------------------------------------------------------- */
200template<typename T> void socket_data_free_impl(T & /*data*/, const bool /*do_id_user*/) {}
201template<> void socket_data_free_impl(bNodeSocketValueMenu &dst, const bool /*do_id_user*/)
202{
203 if (dst.enum_items) {
204 /* Release shared data pointer. */
205 dst.enum_items->remove_user_and_delete_if_last();
206 }
207}
208
209static void socket_data_free(bNodeTreeInterfaceSocket &socket, const bool do_id_user)
210{
211 socket_data_to_static_type_tag(socket.socket_type, [&](auto type_tag) {
212 using SocketDataType = typename decltype(type_tag)::type;
213 if (do_id_user) {
214 socket_data_id_user_decrement(get_socket_data_as<SocketDataType>(socket));
215 }
217 });
218}
219
222/* -------------------------------------------------------------------- */
226template<typename T> void socket_data_copy_impl(T & /*dst*/, const T & /*src*/) {}
227template<>
229{
230 /* Copy of shared data pointer. */
231 if (dst.enum_items) {
232 dst.enum_items->add_user();
233 }
234}
235
237 const bNodeTreeInterfaceSocket &src,
238 int flag)
239{
240 socket_data_to_static_type_tag(dst.socket_type, [&](auto type_tag) {
241 using SocketDataType = typename decltype(type_tag)::type;
242 dst.socket_data = MEM_dupallocN(src.socket_data);
243 socket_data_copy_impl(get_socket_data_as<SocketDataType>(dst),
244 get_socket_data_as<SocketDataType>(src));
245 if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
246 socket_data_id_user_increment(get_socket_data_as<SocketDataType>(dst));
247 }
248 });
249}
250
251/* Copy socket data from a raw pointer, e.g. from a #bNodeSocket. */
253 const void *src_socket_data,
254 int flag)
255{
256 socket_data_to_static_type_tag(dst.socket_type, [&](auto type_tag) {
257 using SocketDataType = typename decltype(type_tag)::type;
258
259 if (dst.socket_data != nullptr) {
260 socket_data_free(dst, true);
261 MEM_SAFE_FREE(dst.socket_data);
262 }
263
264 dst.socket_data = MEM_dupallocN(src_socket_data);
266 *static_cast<const SocketDataType *>(src_socket_data));
269 }
270 });
271}
272
275/* -------------------------------------------------------------------- */
279/* NOTE: no default implementation, every used type must write at least the base struct. */
280
282{
284}
286{
288}
302{
304}
314{
316}
330{
332}
333
335{
336 socket_data_to_static_type_tag(socket.socket_type, [&](auto type_tag) {
337 using SocketDataType = typename decltype(type_tag)::type;
338 socket_data_write_impl(writer, get_socket_data_as<SocketDataType>(socket));
339 });
340}
341
344/* -------------------------------------------------------------------- */
348template<typename T> void socket_data_read_data_impl(BlendDataReader *reader, T **data)
349{
350 /* FIXME Avoid using low-level untyped read function here. Cannot use the BLO_read_struct
351 * currently (macro expansion would process `T` instead of the actual type). */
352 BLO_read_data_address(reader, data);
353}
355{
356 /* FIXME Avoid using low-level untyped read function here. No type info available here currently.
357 */
358 BLO_read_data_address(reader, data);
359 /* Clear runtime data. */
360 (*data)->enum_items = nullptr;
361 (*data)->runtime_flag = 0;
362}
363
365{
366 bool data_read = false;
367 socket_data_to_static_type_tag(socket.socket_type, [&](auto type_tag) {
368 using SocketDataType = typename decltype(type_tag)::type;
369 socket_data_read_data_impl(reader, reinterpret_cast<SocketDataType **>(&socket.socket_data));
370 data_read = true;
371 });
372 if (!data_read && socket.socket_data) {
373 /* Not sure how this can happen exactly, but it did happen in #127855. */
374 socket.socket_data = nullptr;
375 }
376}
377
380/* -------------------------------------------------------------------- */
384template<typename T>
386{
387}
396template<>
401template<>
406template<>
411
413{
414 socket_data_to_static_type_tag(socket.socket_type, [&](auto type_tag) {
415 using SocketDataType = typename decltype(type_tag)::type;
416 socket_data_foreach_id_impl(data, get_socket_data_as<SocketDataType>(socket));
417 });
418}
419
422} // namespace socket_types
423
424namespace item_types {
425
427
428static void item_copy(bNodeTreeInterfaceItem &dst,
429 const bNodeTreeInterfaceItem &src,
430 int flag,
431 UidGeneratorFn generate_uid);
432
440 const int flag,
441 UidGeneratorFn generate_uid)
442{
443 panel.items_num = items_src.size();
444 panel.items_array = MEM_cnew_array<bNodeTreeInterfaceItem *>(panel.items_num, __func__);
445
446 /* Copy buffers. */
447 for (const int i : items_src.index_range()) {
448 const bNodeTreeInterfaceItem *item_src = items_src[i];
449 panel.items_array[i] = static_cast<bNodeTreeInterfaceItem *>(MEM_dupallocN(item_src));
450 item_types::item_copy(*panel.items_array[i], *item_src, flag, generate_uid);
451 }
452}
453
460 const bNodeTreeInterfaceItem &src,
461 const int flag,
462 UidGeneratorFn generate_uid)
463{
464 switch (dst.item_type) {
466 bNodeTreeInterfaceSocket &dst_socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(dst);
467 const bNodeTreeInterfaceSocket &src_socket =
468 reinterpret_cast<const bNodeTreeInterfaceSocket &>(src);
469 BLI_assert(src_socket.socket_type != nullptr);
470
471 dst_socket.name = BLI_strdup_null(src_socket.name);
472 dst_socket.description = BLI_strdup_null(src_socket.description);
473 dst_socket.socket_type = BLI_strdup(src_socket.socket_type);
475 dst_socket.identifier = generate_uid ? BLI_sprintfN("Socket_%d", generate_uid()) :
476 BLI_strdup(src_socket.identifier);
477 if (src_socket.properties) {
478 dst_socket.properties = IDP_CopyProperty_ex(src_socket.properties, flag);
479 }
480 if (src_socket.socket_data != nullptr) {
481 socket_types::socket_data_copy(dst_socket, src_socket, flag);
482 }
483 break;
484 }
486 bNodeTreeInterfacePanel &dst_panel = reinterpret_cast<bNodeTreeInterfacePanel &>(dst);
487 const bNodeTreeInterfacePanel &src_panel = reinterpret_cast<const bNodeTreeInterfacePanel &>(
488 src);
489
490 dst_panel.name = BLI_strdup_null(src_panel.name);
491 dst_panel.description = BLI_strdup_null(src_panel.description);
492 dst_panel.identifier = generate_uid ? generate_uid() : src_panel.identifier;
493
494 panel_init(dst_panel, src_panel.items(), flag, generate_uid);
495 break;
496 }
497 }
498}
499
500static void item_free(bNodeTreeInterfaceItem &item, const bool do_id_user)
501{
502 switch (item.item_type) {
504 bNodeTreeInterfaceSocket &socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(item);
505
506 if (socket.socket_data != nullptr) {
507 socket_types::socket_data_free(socket, do_id_user);
509 }
510
511 MEM_SAFE_FREE(socket.name);
516 if (socket.properties) {
517 IDP_FreePropertyContent_ex(socket.properties, do_id_user);
518 MEM_freeN(socket.properties);
519 }
520 break;
521 }
523 bNodeTreeInterfacePanel &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
524
525 panel.clear(do_id_user);
526 MEM_SAFE_FREE(panel.name);
528 break;
529 }
530 }
531
532 MEM_freeN(&item);
533}
534
536
538{
539 switch (item.item_type) {
541 bNodeTreeInterfaceSocket &socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(item);
542 BLO_write_string(writer, socket.name);
543 BLO_write_string(writer, socket.identifier);
544 BLO_write_string(writer, socket.description);
545 BLO_write_string(writer, socket.socket_type);
547 if (socket.properties) {
548 IDP_BlendWrite(writer, socket.properties);
549 }
550
551 socket_types::socket_data_write(writer, socket);
552 break;
553 }
555 bNodeTreeInterfacePanel &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
556 BLO_write_string(writer, panel.name);
557 BLO_write_string(writer, panel.description);
558 BLO_write_pointer_array(writer, panel.items_num, panel.items_array);
559 for (bNodeTreeInterfaceItem *child_item : panel.items()) {
560 item_write_struct(writer, *child_item);
561 }
562 break;
563 }
564 }
565}
566
568{
569 switch (item.item_type) {
572 break;
573 }
576 break;
577 }
578 }
579
580 item_write_data(writer, item);
581}
582
584{
585 switch (item.item_type) {
587 bNodeTreeInterfaceSocket &socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(item);
588 BLO_read_string(reader, &socket.name);
589 BLO_read_string(reader, &socket.description);
590 BLO_read_string(reader, &socket.socket_type);
592 BLO_read_string(reader, &socket.identifier);
593 BLO_read_struct(reader, IDProperty, &socket.properties);
594 IDP_BlendDataRead(reader, &socket.properties);
595
597 break;
598 }
600 bNodeTreeInterfacePanel &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
601 BLO_read_string(reader, &panel.name);
602 BLO_read_string(reader, &panel.description);
604 reader, panel.items_num, reinterpret_cast<void **>(&panel.items_array));
605 for (const int i : blender::IndexRange(panel.items_num)) {
606 BLO_read_struct(reader, NodeEnumItem, &panel.items_array[i]);
607 item_read_data(reader, *panel.items_array[i]);
608 }
609 break;
610 }
611 }
612}
613
615{
616 switch (item.item_type) {
618 bNodeTreeInterfaceSocket &socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(item);
619
622 BKE_lib_query_idpropertiesForeachIDLink_callback(prop, data);
623 }));
624
626 break;
627 }
629 bNodeTreeInterfacePanel &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
630 for (bNodeTreeInterfaceItem *item : panel.items()) {
631 item_foreach_id(data, *item);
632 }
633 break;
634 }
635 }
636}
637
638/* Move all child items to the new parent. */
640{
641 switch (item.item_type) {
643 return {};
644 }
646 bNodeTreeInterfacePanel &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
647 return panel.items();
648 }
649 }
650 return {};
651}
652
653} // namespace item_types
654
655} // namespace blender::bke::node_interface
656
657using namespace blender::bke::node_interface;
658
659blender::bke::bNodeSocketType *bNodeTreeInterfaceSocket::socket_typeinfo() const
660{
662}
663
664blender::ColorGeometry4f bNodeTreeInterfaceSocket::socket_color() const
665{
666 blender::bke::bNodeSocketType *typeinfo = this->socket_typeinfo();
667 if (typeinfo && typeinfo->draw_color_simple) {
668 float color[4];
669 typeinfo->draw_color_simple(typeinfo, color);
670 return blender::ColorGeometry4f(color);
671 }
672 else {
673 return blender::ColorGeometry4f(1.0f, 0.0f, 1.0f, 1.0f);
674 }
675}
676
677bool bNodeTreeInterfaceSocket::set_socket_type(const char *new_socket_type)
678{
679 const char *idname = socket_types::try_get_supported_socket_type(new_socket_type);
680 if (idname == nullptr) {
681 return false;
682 }
683
684 if (this->socket_data != nullptr) {
687 }
689
690 this->socket_type = BLI_strdup(new_socket_type);
691 this->socket_data = socket_types::make_socket_data(new_socket_type);
692
693 return true;
694}
695
696void bNodeTreeInterfaceSocket::init_from_socket_instance(const bNodeSocket *socket)
697{
698 const char *idname = socket_types::try_get_supported_socket_type(socket->idname);
699 BLI_assert(idname != nullptr);
700
701 if (this->socket_data != nullptr) {
704 }
706 if (socket->flag & SOCK_HIDE_VALUE) {
708 }
709
710 this->socket_type = BLI_strdup(idname);
713}
714
715blender::IndexRange bNodeTreeInterfacePanel::items_range() const
716{
718}
719
720blender::Span<const bNodeTreeInterfaceItem *> bNodeTreeInterfacePanel::items() const
721{
723}
724
725blender::MutableSpan<bNodeTreeInterfaceItem *> bNodeTreeInterfacePanel::items()
726{
728}
729
730bool bNodeTreeInterfacePanel::contains(const bNodeTreeInterfaceItem &item) const
731{
732 return items().contains(&item);
733}
734
735bool bNodeTreeInterfacePanel::contains_recursive(const bNodeTreeInterfaceItem &item) const
736{
737 bool is_child = false;
738 /* Have to capture item address here instead of just a reference,
739 * otherwise pointer comparison will not work. */
740 this->foreach_item(
741 [&](const bNodeTreeInterfaceItem &titem) {
742 if (&titem == &item) {
743 is_child = true;
744 return false;
745 }
746 return true;
747 },
748 true);
749 return is_child;
750}
751
752int bNodeTreeInterfacePanel::item_position(const bNodeTreeInterfaceItem &item) const
753{
754 return items().first_index_try(&item);
755}
756
757int bNodeTreeInterfacePanel::item_index(const bNodeTreeInterfaceItem &item) const
758{
759 int index = 0;
760 bool found = false;
761 /* Have to capture item address here instead of just a reference,
762 * otherwise pointer comparison will not work. */
763 this->foreach_item([&](const bNodeTreeInterfaceItem &titem) {
764 if (&titem == &item) {
765 found = true;
766 return false;
767 }
768 ++index;
769 return true;
770 });
771 return found ? index : -1;
772}
773
774const bNodeTreeInterfaceItem *bNodeTreeInterfacePanel::item_at_index(int index) const
775{
776 int i = 0;
777 const bNodeTreeInterfaceItem *result = nullptr;
778 this->foreach_item([&](const bNodeTreeInterfaceItem &item) {
779 if (i == index) {
780 result = &item;
781 return false;
782 }
783 ++i;
784 return true;
785 });
786 return result;
787}
788
789bNodeTreeInterfacePanel *bNodeTreeInterfacePanel::find_parent_recursive(
790 const bNodeTreeInterfaceItem &item)
791{
792 std::queue<bNodeTreeInterfacePanel *> queue;
793
794 if (this->contains(item)) {
795 return this;
796 }
797 queue.push(this);
798
799 while (!queue.empty()) {
800 bNodeTreeInterfacePanel *parent = queue.front();
801 queue.pop();
802
803 for (bNodeTreeInterfaceItem *titem : parent->items()) {
804 if (titem->item_type != NODE_INTERFACE_PANEL) {
805 continue;
806 }
807
809 if (tpanel->contains(item)) {
810 return tpanel;
811 }
812 queue.push(tpanel);
813 }
814 }
815
816 return nullptr;
817}
818
819int bNodeTreeInterfacePanel::find_valid_insert_position_for_item(
820 const bNodeTreeInterfaceItem &item, const int initial_pos) const
821{
822 const bool sockets_above_panels = !(this->flag &
824 const blender::Span<const bNodeTreeInterfaceItem *> items = this->items();
825
826 /* True if item a should be above item b. */
827 auto item_compare = [sockets_above_panels](const bNodeTreeInterfaceItem &a,
828 const bNodeTreeInterfaceItem &b) -> bool {
829 if (a.item_type != b.item_type) {
830 /* Keep sockets above panels. */
831 if (sockets_above_panels) {
832 return a.item_type == NODE_INTERFACE_SOCKET;
833 }
834 }
835 else {
836 /* Keep outputs above inputs. */
837 if (a.item_type == NODE_INTERFACE_SOCKET) {
838 const bNodeTreeInterfaceSocket &sa = reinterpret_cast<const bNodeTreeInterfaceSocket &>(a);
839 const bNodeTreeInterfaceSocket &sb = reinterpret_cast<const bNodeTreeInterfaceSocket &>(b);
840 const bool is_output_a = sa.flag & NODE_INTERFACE_SOCKET_OUTPUT;
841 const bool is_output_b = sb.flag & NODE_INTERFACE_SOCKET_OUTPUT;
842 if (is_output_a != is_output_b) {
843 return is_output_a;
844 }
845 }
846 }
847 return false;
848 };
849
850 if (items.is_empty()) {
851 return initial_pos;
852 }
853
854 /* Insertion sort for a single item.
855 * items.size() is a valid position for appending. */
856 int test_pos = clamp_i(initial_pos, 0, items.size());
857 /* Move upward until valid position found. */
858 while (test_pos > 0 && item_compare(item, *items[test_pos - 1])) {
859 --test_pos;
860 }
861 /* Move downward until valid position found.
862 * Result can be out of range, this is valid, items get appended. */
863 while (test_pos < items.size() && item_compare(*items[test_pos], item)) {
864 ++test_pos;
865 }
866 return test_pos;
867}
868
869void bNodeTreeInterfacePanel::add_item(bNodeTreeInterfaceItem &item)
870{
871 /* Same as inserting at the end. */
872 insert_item(item, this->items_num);
873}
874
875void bNodeTreeInterfacePanel::insert_item(bNodeTreeInterfaceItem &item, int position)
876{
877 /* Are child panels allowed? */
880
881 /* Apply any constraints on the item positions. */
882 position = find_valid_insert_position_for_item(item, position);
883 position = std::min(std::max(position, 0), items_num);
884
885 blender::MutableSpan<bNodeTreeInterfaceItem *> old_items = this->items();
886 items_num++;
887 items_array = MEM_cnew_array<bNodeTreeInterfaceItem *>(items_num, __func__);
888 this->items().take_front(position).copy_from(old_items.take_front(position));
889 this->items().drop_front(position + 1).copy_from(old_items.drop_front(position));
890 this->items()[position] = &item;
891
892 if (old_items.data()) {
893 MEM_freeN(old_items.data());
894 }
895}
896
897bool bNodeTreeInterfacePanel::remove_item(bNodeTreeInterfaceItem &item, const bool free)
898{
899 const int position = this->item_position(item);
900 if (!this->items().index_range().contains(position)) {
901 return false;
902 }
903
904 blender::MutableSpan<bNodeTreeInterfaceItem *> old_items = this->items();
905 items_num--;
906 items_array = MEM_cnew_array<bNodeTreeInterfaceItem *>(items_num, __func__);
907 this->items().take_front(position).copy_from(old_items.take_front(position));
908 this->items().drop_front(position).copy_from(old_items.drop_front(position + 1));
909
910 /* Guaranteed not empty, contains at least the removed item */
911 MEM_freeN(old_items.data());
912
913 if (free) {
915 }
916
917 return true;
918}
919
920void bNodeTreeInterfacePanel::clear(bool do_id_user)
921{
922 for (bNodeTreeInterfaceItem *item : this->items()) {
923 item_types::item_free(*item, do_id_user);
924 }
926 items_array = nullptr;
927 items_num = 0;
928}
929
930bool bNodeTreeInterfacePanel::move_item(bNodeTreeInterfaceItem &item, int new_position)
931{
932 const int old_position = this->item_position(item);
933 if (!this->items().index_range().contains(old_position)) {
934 return false;
935 }
936 if (old_position == new_position) {
937 /* Nothing changes. */
938 return true;
939 }
940
941 new_position = find_valid_insert_position_for_item(item, new_position);
942 new_position = std::min(std::max(new_position, 0), items_num);
943
944 if (old_position < new_position) {
945 /* Actual target position and all existing items shifted by 1. */
946 const blender::Span<bNodeTreeInterfaceItem *> moved_items = this->items().slice(
947 old_position + 1, new_position - old_position - 1);
948 bNodeTreeInterfaceItem *tmp = this->items()[old_position];
949 std::copy(
950 moved_items.begin(), moved_items.end(), this->items().drop_front(old_position).data());
951 this->items()[new_position - 1] = tmp;
952 }
953 else /* old_position > new_position */ {
954 const blender::Span<bNodeTreeInterfaceItem *> moved_items = this->items().slice(
955 new_position, old_position - new_position);
956 bNodeTreeInterfaceItem *tmp = this->items()[old_position];
957 std::copy_backward(
958 moved_items.begin(), moved_items.end(), this->items().drop_front(old_position + 1).data());
959 this->items()[new_position] = tmp;
960 }
961
962 return true;
963}
964
965void bNodeTreeInterfacePanel::foreach_item(
966 blender::FunctionRef<bool(bNodeTreeInterfaceItem &item)> fn, bool include_self)
967{
970
971 if (include_self && fn(this->item) == false) {
972 return;
973 }
974 stack.push(this->items());
975
976 while (!stack.is_empty()) {
977 const ItemSpan current_items = stack.pop();
978
979 for (const int index : current_items.index_range()) {
980 bNodeTreeInterfaceItem *item = current_items[index];
981 if (fn(*item) == false) {
982 return;
983 }
984
986 bNodeTreeInterfacePanel *panel = reinterpret_cast<bNodeTreeInterfacePanel *>(item);
987 /* Reinsert remaining items. */
988 if (index < current_items.size() - 1) {
989 const ItemSpan remaining_items = current_items.drop_front(index + 1);
990 stack.push(remaining_items);
991 }
992 /* Handle child items first before continuing with current span. */
993 stack.push(panel->items());
994 break;
995 }
996 }
997 }
998}
999
1000void bNodeTreeInterfacePanel::foreach_item(
1001 blender::FunctionRef<bool(const bNodeTreeInterfaceItem &item)> fn, bool include_self) const
1002{
1005
1006 if (include_self && fn(this->item) == false) {
1007 return;
1008 }
1009 stack.push(this->items());
1010
1011 while (!stack.is_empty()) {
1012 const ItemSpan current_items = stack.pop();
1013
1014 for (const int index : current_items.index_range()) {
1015 const bNodeTreeInterfaceItem *item = current_items[index];
1016 if (fn(*item) == false) {
1017 return;
1018 }
1019
1021 const bNodeTreeInterfacePanel *panel = reinterpret_cast<const bNodeTreeInterfacePanel *>(
1022 item);
1023 /* Reinsert remaining items. */
1024 if (index < current_items.size() - 1) {
1025 const ItemSpan remaining_items = current_items.drop_front(index + 1);
1026 stack.push(remaining_items);
1027 }
1028 /* Handle child items first before continuing with current span. */
1029 stack.push(panel->items());
1030 break;
1031 }
1032 }
1033 }
1034}
1035
1037
1039 const StringRef name,
1040 const StringRef description,
1041 const StringRef socket_type,
1043{
1044 BLI_assert(!socket_type.is_empty());
1045
1046 const char *idname = socket_types::try_get_supported_socket_type(socket_type);
1047 if (idname == nullptr) {
1048 return nullptr;
1049 }
1050
1051 bNodeTreeInterfaceSocket *new_socket = MEM_cnew<bNodeTreeInterfaceSocket>(__func__);
1052 BLI_assert(new_socket);
1053
1054 /* Init common socket properties. */
1055 new_socket->identifier = BLI_sprintfN("Socket_%d", uid);
1056 new_socket->item.item_type = NODE_INTERFACE_SOCKET;
1057 new_socket->name = BLI_strdupn(name.data(), name.size());
1058 new_socket->description = description.is_empty() ?
1059 nullptr :
1060 BLI_strdupn(description.data(), description.size());
1061 new_socket->socket_type = BLI_strdupn(socket_type.data(), socket_type.size());
1062 new_socket->flag = flag;
1063
1064 new_socket->socket_data = socket_types::make_socket_data(socket_type);
1065
1066 return new_socket;
1067}
1068
1070 const bNode &from_node,
1071 const bNodeSocket &from_sock,
1072 const StringRef socket_type,
1073 const StringRef name)
1074{
1078
1079 bNodeTreeInterfaceSocket *iosock = ntree.tree_interface.add_socket(
1080 name, from_sock.description, socket_type, flag, nullptr);
1081 if (iosock == nullptr) {
1082 return nullptr;
1083 }
1084 const blender::bke::bNodeSocketType *typeinfo = iosock->socket_typeinfo();
1085 if (typeinfo->interface_from_socket) {
1086 typeinfo->interface_from_socket(&ntree.id, iosock, &from_node, &from_sock);
1087 UNUSED_VARS(from_sock);
1088 }
1089 return iosock;
1090}
1091
1093 const blender::StringRef name,
1094 const blender::StringRef description,
1096{
1097 BLI_assert(!name.is_empty());
1098
1099 bNodeTreeInterfacePanel *new_panel = MEM_cnew<bNodeTreeInterfacePanel>(__func__);
1100 new_panel->item.item_type = NODE_INTERFACE_PANEL;
1101 new_panel->name = BLI_strdupn(name.data(), name.size());
1102 new_panel->description = description.is_empty() ?
1103 nullptr :
1104 BLI_strdupn(description.data(), description.size());
1105 new_panel->identifier = uid;
1106 new_panel->flag = flag;
1107 return new_panel;
1108}
1109
1110} // namespace blender::bke::node_interface
1111
1112void bNodeTreeInterface::init_data()
1113{
1114 this->runtime = MEM_new<blender::bke::bNodeTreeInterfaceRuntime>(__func__);
1115 this->tag_missing_runtime_data();
1116
1117 /* Root panel is allowed to contain child panels. */
1119}
1120
1121void bNodeTreeInterface::copy_data(const bNodeTreeInterface &src, int flag)
1122{
1123 item_types::panel_init(this->root_panel, src.root_panel.items(), flag, nullptr);
1124 this->active_index = src.active_index;
1125
1126 this->runtime = MEM_new<blender::bke::bNodeTreeInterfaceRuntime>(__func__);
1127 this->tag_missing_runtime_data();
1128}
1129
1130void bNodeTreeInterface::free_data()
1131{
1132 MEM_delete(this->runtime);
1133
1134 /* Called when freeing the main database, don't do user refcount here. */
1135 this->root_panel.clear(false);
1136}
1137
1138void bNodeTreeInterface::write(BlendWriter *writer)
1139{
1140 /* Don't write the root panel struct itself, it's nested in the interface struct. */
1142}
1143
1144void bNodeTreeInterface::read_data(BlendDataReader *reader)
1145{
1147
1148 this->runtime = MEM_new<blender::bke::bNodeTreeInterfaceRuntime>(__func__);
1149 this->tag_missing_runtime_data();
1150}
1151
1152bNodeTreeInterfaceItem *bNodeTreeInterface::active_item()
1153{
1154 bNodeTreeInterfaceItem *active = nullptr;
1155 int count = active_index;
1156 this->foreach_item([&](bNodeTreeInterfaceItem &item) {
1157 if (count == 0) {
1158 active = &item;
1159 return false;
1160 }
1161 --count;
1162 return true;
1163 });
1164 return active;
1165}
1166
1167const bNodeTreeInterfaceItem *bNodeTreeInterface::active_item() const
1168{
1169 const bNodeTreeInterfaceItem *active = nullptr;
1170 int count = active_index;
1171 this->foreach_item([&](const bNodeTreeInterfaceItem &item) {
1172 if (count == 0) {
1173 active = &item;
1174 return false;
1175 }
1176 --count;
1177 return true;
1178 });
1179 return active;
1180}
1181
1182void bNodeTreeInterface::active_item_set(bNodeTreeInterfaceItem *item)
1183{
1184 active_index = 0;
1185 int count = 0;
1186 this->foreach_item([&](bNodeTreeInterfaceItem &titem) {
1187 if (&titem == item) {
1189 return false;
1190 }
1191 ++count;
1192 return true;
1193 });
1194}
1195
1196bNodeTreeInterfaceSocket *bNodeTreeInterface::add_socket(const blender::StringRef name,
1197 const blender::StringRef description,
1198 const blender::StringRef socket_type,
1201{
1202 /* Check that each interface socket is either an input or an output. Technically, it can be both
1203 * at the same time, but we don't want that for the time being. */
1206 if (parent == nullptr) {
1207 parent = &root_panel;
1208 }
1209 BLI_assert(this->find_item(parent->item));
1210
1212 next_uid++, name, description, socket_type, flag);
1213 if (new_socket) {
1214 parent->add_item(new_socket->item);
1215 }
1216
1217 this->tag_items_changed();
1218 return new_socket;
1219}
1220
1221bNodeTreeInterfaceSocket *bNodeTreeInterface::insert_socket(const blender::StringRef name,
1222 const blender::StringRef description,
1223 const blender::StringRef socket_type,
1226 const int position)
1227{
1228 if (parent == nullptr) {
1229 parent = &root_panel;
1230 }
1231 BLI_assert(this->find_item(parent->item));
1232
1234 next_uid++, name, description, socket_type, flag);
1235 if (new_socket) {
1236 parent->insert_item(new_socket->item, position);
1237 }
1238
1239 this->tag_items_changed();
1240 return new_socket;
1241}
1242
1243bNodeTreeInterfacePanel *bNodeTreeInterface::add_panel(const blender::StringRef name,
1244 const blender::StringRef description,
1247{
1248 if (parent == nullptr) {
1249 parent = &root_panel;
1250 }
1251 BLI_assert(this->find_item(parent->item));
1252
1254 /* Parent does not allow adding child panels. */
1255 return nullptr;
1256 }
1257
1258 bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name, description, flag);
1259 if (new_panel) {
1260 parent->add_item(new_panel->item);
1261 }
1262
1263 this->tag_items_changed();
1264 return new_panel;
1265}
1266
1267bNodeTreeInterfacePanel *bNodeTreeInterface::insert_panel(const blender::StringRef name,
1268 const blender::StringRef description,
1271 const int position)
1272{
1273 if (parent == nullptr) {
1274 parent = &root_panel;
1275 }
1276 BLI_assert(this->find_item(parent->item));
1277
1279 /* Parent does not allow adding child panels. */
1280 return nullptr;
1281 }
1282
1283 bNodeTreeInterfacePanel *new_panel = make_panel(next_uid++, name, description, flag);
1284 if (new_panel) {
1285 parent->insert_item(new_panel->item, position);
1286 }
1287
1288 this->tag_items_changed();
1289 return new_panel;
1290}
1291
1292bNodeTreeInterfaceItem *bNodeTreeInterface::add_item_copy(const bNodeTreeInterfaceItem &item,
1294{
1295 if (parent == nullptr) {
1296 parent = &root_panel;
1297 }
1298 BLI_assert(this->find_item(item));
1299 BLI_assert(this->find_item(parent->item));
1300
1301 if (item.item_type == NODE_INTERFACE_PANEL &&
1303 {
1304 /* Parent does not allow adding child panels. */
1305 return nullptr;
1306 }
1307
1308 bNodeTreeInterfaceItem *citem = static_cast<bNodeTreeInterfaceItem *>(MEM_dupallocN(&item));
1309 item_types::item_copy(*citem, item, 0, [&]() { return this->next_uid++; });
1310 parent->add_item(*citem);
1311
1312 this->tag_items_changed();
1313 return citem;
1314}
1315
1316bNodeTreeInterfaceItem *bNodeTreeInterface::insert_item_copy(const bNodeTreeInterfaceItem &item,
1318 int position)
1319{
1320 if (parent == nullptr) {
1321 parent = &root_panel;
1322 }
1323 BLI_assert(this->find_item(item));
1324 BLI_assert(this->find_item(parent->item));
1325
1326 if (item.item_type == NODE_INTERFACE_PANEL &&
1328 {
1329 /* Parent does not allow adding child panels. */
1330 return nullptr;
1331 }
1332
1333 bNodeTreeInterfaceItem *citem = static_cast<bNodeTreeInterfaceItem *>(MEM_dupallocN(&item));
1334 item_types::item_copy(*citem, item, 0, [&]() { return this->next_uid++; });
1335 parent->insert_item(*citem, position);
1336
1337 this->tag_items_changed();
1338 return citem;
1339}
1340
1341bool bNodeTreeInterface::remove_item(bNodeTreeInterfaceItem &item, bool move_content_to_parent)
1342{
1343 bNodeTreeInterfacePanel *parent = this->find_item_parent(item, true);
1344 if (parent == nullptr) {
1345 return false;
1346 }
1347 if (move_content_to_parent) {
1348 int position = parent->item_position(item);
1349 /* Cache children to avoid invalidating the iterator. */
1351 for (bNodeTreeInterfaceItem *child : children) {
1352 this->move_item_to_parent(*child, parent, position++);
1353 }
1354 }
1355 if (parent->remove_item(item, true)) {
1356 this->tag_items_changed();
1357 return true;
1358 }
1359
1360 return false;
1361}
1362
1363void bNodeTreeInterface::clear_items()
1364{
1365 root_panel.clear(true);
1366 this->tag_items_changed();
1367}
1368
1369bool bNodeTreeInterface::move_item(bNodeTreeInterfaceItem &item, const int new_position)
1370{
1371 bNodeTreeInterfacePanel *parent = this->find_item_parent(item, true);
1372 if (parent == nullptr) {
1373 return false;
1374 }
1375
1376 if (parent->move_item(item, new_position)) {
1377 this->tag_items_changed();
1378 return true;
1379 }
1380 return false;
1381}
1382
1383bool bNodeTreeInterface::move_item_to_parent(bNodeTreeInterfaceItem &item,
1384 bNodeTreeInterfacePanel *new_parent,
1385 int new_position)
1386{
1387 if (new_parent == nullptr) {
1388 new_parent = &this->root_panel;
1389 }
1390 bNodeTreeInterfacePanel *parent = this->find_item_parent(item, true);
1391 if (parent == nullptr) {
1392 return false;
1393 }
1394 if (item.item_type == NODE_INTERFACE_PANEL && new_parent &&
1396 {
1397 /* Parent does not allow adding child panels. */
1398 return false;
1399 }
1400 if (parent == new_parent) {
1401 if (parent->move_item(item, new_position)) {
1402 this->tag_items_changed();
1403 return true;
1404 }
1405 }
1406 else {
1407 /* NOTE: only remove and reinsert when parents different, otherwise removing the item can
1408 * change the desired target position! */
1409 if (parent->remove_item(item, false)) {
1410 new_parent->insert_item(item, new_position);
1411 this->tag_items_changed();
1412 return true;
1413 }
1414 }
1415 return false;
1416}
1417
1418void bNodeTreeInterface::foreach_id(LibraryForeachIDData *cb)
1419{
1421}
1422
1423bool bNodeTreeInterface::items_cache_is_available() const
1424{
1425 return !this->runtime->items_cache_mutex_.is_dirty();
1426}
1427
1428void bNodeTreeInterface::ensure_items_cache() const
1429{
1431
1432 runtime.items_cache_mutex_.ensure([&]() {
1433 /* Rebuild draw-order list of interface items for linear access. */
1434 runtime.items_.clear();
1435 runtime.inputs_.clear();
1436 runtime.outputs_.clear();
1437
1438 /* Items in the cache are mutable pointers, but node tree update considers ID data to be
1439 * immutable when caching. DNA ListBase pointers can be mutable even if their container is
1440 * const, but the items returned by #foreach_item inherit qualifiers from the container. */
1441 bNodeTreeInterface &mutable_self = const_cast<bNodeTreeInterface &>(*this);
1442
1443 mutable_self.foreach_item([&](bNodeTreeInterfaceItem &item) {
1444 runtime.items_.append(&item);
1446 if (socket->flag & NODE_INTERFACE_SOCKET_INPUT) {
1447 runtime.inputs_.append(socket);
1448 }
1449 if (socket->flag & NODE_INTERFACE_SOCKET_OUTPUT) {
1450 runtime.outputs_.append(socket);
1451 }
1452 }
1453 return true;
1454 });
1455 });
1456}
1457
1458void bNodeTreeInterface::tag_missing_runtime_data()
1459{
1460 this->runtime->changed_flag_ |= NODE_INTERFACE_CHANGED_ALL;
1461 this->runtime->items_cache_mutex_.tag_dirty();
1462}
1463
1464bool bNodeTreeInterface::is_changed() const
1465{
1466 return this->runtime->changed_flag_ != NODE_INTERFACE_CHANGED_NOTHING;
1467}
1468
1469void bNodeTreeInterface::tag_items_changed()
1470{
1471 this->runtime->changed_flag_ |= NODE_INTERFACE_CHANGED_ITEMS;
1472 this->runtime->items_cache_mutex_.tag_dirty();
1473}
1474
1475void bNodeTreeInterface::reset_changed_flags()
1476{
1477 this->runtime->changed_flag_ = NODE_INTERFACE_CHANGED_NOTHING;
1478}
void IDP_foreach_property(IDProperty *id_property_root, int type_filter, blender::FunctionRef< void(IDProperty *id_property)> callback)
#define IDP_BlendDataRead(reader, prop)
void IDP_FreePropertyContent_ex(IDProperty *prop, bool do_id_user)
Definition idprop.cc:1189
IDProperty * IDP_CopyProperty_ex(const IDProperty *prop, int flag) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:843
void IDP_BlendWrite(BlendWriter *writer, const IDProperty *prop)
Definition idprop.cc:1437
@ LIB_ID_CREATE_NO_USER_REFCOUNT
void id_us_plus(ID *id)
Definition lib_id.cc:351
void id_us_min(ID *id)
Definition lib_id.cc:359
#define BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data_, func_call_)
#define BKE_LIB_FOREACHID_PROCESS_IDSUPER(data_, id_super_, cb_flag_)
@ IDWALK_CB_USER
#define BLI_assert(a)
Definition BLI_assert.h:50
void BLI_kdtree_nd_ free(KDTree *tree)
MINLINE int clamp_i(int value, int min, int max)
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE void copy_v3_v3(float r[3], const float a[3])
char * BLI_sprintfN(const char *__restrict format,...) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_PRINTF_FORMAT(1
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.c:29
char * BLI_strdup_null(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_MALLOC
Definition string.c:45
#define UNUSED_VARS(...)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define BLO_read_data_address(reader, ptr_p)
#define BLO_write_struct(writer, struct_name, data_ptr)
void BLO_read_string(BlendDataReader *reader, char **ptr_p)
Definition readfile.cc:4992
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
void BLO_read_pointer_array(BlendDataReader *reader, int array_size, void **ptr_p)
Definition readfile.cc:5052
#define BLO_read_struct(reader, struct_name, ptr_p)
void BLO_write_pointer_array(BlendWriter *writer, uint num, const void *data_ptr)
@ IDP_TYPE_FILTER_ID
Object groups, one object can be in many groups at once.
@ NODE_INTERFACE_PANEL_ALLOW_CHILD_PANELS
@ NODE_INTERFACE_PANEL_ALLOW_SOCKETS_AFTER_PANELS
@ NODE_INTERFACE_SOCKET_HIDE_VALUE
@ SOCK_OUT
@ SOCK_IN
@ SOCK_HIDE_VALUE
#define MEM_SAFE_FREE(v)
@ PROP_NONE
Definition RNA_types.hh:136
void ensure(FunctionRef< void()> compute_cache)
constexpr T * data() const
Definition BLI_span.hh:540
constexpr MutableSpan drop_front(const int64_t n) const
Definition BLI_span.hh:608
constexpr MutableSpan take_front(const int64_t n) const
Definition BLI_span.hh:630
constexpr Span drop_front(int64_t n) const
Definition BLI_span.hh:172
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr const T * end() const
Definition BLI_span.hh:225
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr const T * begin() const
Definition BLI_span.hh:221
constexpr Span take_front(int64_t n) const
Definition BLI_span.hh:194
constexpr bool is_empty() const
Definition BLI_span.hh:261
bool is_empty() const
Definition BLI_stack.hh:308
void push(const T &value)
Definition BLI_stack.hh:213
constexpr bool is_empty() const
constexpr int64_t size() const
constexpr const char * data() const
local_group_size(16, 16) .push_constant(Type b
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
int count
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
void item_write_struct(BlendWriter *writer, bNodeTreeInterfaceItem &item)
static void item_copy(bNodeTreeInterfaceItem &dst, const bNodeTreeInterfaceItem &src, int flag, UidGeneratorFn generate_uid)
static void item_free(bNodeTreeInterfaceItem &item, const bool do_id_user)
static void item_write_data(BlendWriter *writer, bNodeTreeInterfaceItem &item)
static Span< bNodeTreeInterfaceItem * > item_children(bNodeTreeInterfaceItem &item)
static void item_read_data(BlendDataReader *reader, bNodeTreeInterfaceItem &item)
static void item_foreach_id(LibraryForeachIDData *data, bNodeTreeInterfaceItem &item)
static void panel_init(bNodeTreeInterfacePanel &panel, const Span< const bNodeTreeInterfaceItem * > items_src, const int flag, UidGeneratorFn generate_uid)
static void socket_data_read_data(BlendDataReader *reader, bNodeTreeInterfaceSocket &socket)
void socket_data_foreach_id_impl(LibraryForeachIDData *, T &)
static void * make_socket_data(const StringRef socket_type)
static const char * try_get_supported_socket_type(const StringRef socket_type)
static void socket_data_free(bNodeTreeInterfaceSocket &socket, const bool do_id_user)
void socket_data_to_static_type_tag(const StringRef socket_type, const Fn &fn)
static void socket_data_write(BlendWriter *writer, bNodeTreeInterfaceSocket &socket)
static void socket_data_foreach_id(LibraryForeachIDData *data, bNodeTreeInterfaceSocket &socket)
static void socket_data_copy(bNodeTreeInterfaceSocket &dst, const bNodeTreeInterfaceSocket &src, int flag)
static void socket_data_copy_ptr(bNodeTreeInterfaceSocket &dst, const void *src_socket_data, int flag)
void socket_data_write_impl(BlendWriter *writer, bNodeSocketValueFloat &data)
void socket_data_read_data_impl(BlendDataReader *reader, T **data)
T & get_item_as(bNodeTreeInterfaceItem &item)
static bNodeTreeInterfaceSocket * make_socket(const int uid, const StringRef name, const StringRef description, const StringRef socket_type, const NodeTreeInterfaceSocketFlag flag)
static bNodeTreeInterfacePanel * make_panel(const int uid, const blender::StringRef name, const blender::StringRef description, const NodeTreeInterfacePanelFlag flag)
T & get_socket_data_as(bNodeTreeInterfaceSocket &item)
bNodeTreeInterfaceSocket * add_interface_socket_from_node(bNodeTree &ntree, const bNode &from_node, const bNodeSocket &from_sock, const StringRef socket_type, const StringRef name)
bool node_is_static_socket_type(const bNodeSocketType *stype)
Definition node.cc:2117
bNodeSocketType * node_socket_type_find(const char *idname)
Definition node.cc:1763
const char * node_static_socket_type(int type, int subtype)
Definition node.cc:2126
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:337
NodeTreeInterfaceChangedFlag
@ NODE_INTERFACE_CHANGED_ALL
@ NODE_INTERFACE_CHANGED_ITEMS
@ NODE_INTERFACE_CHANGED_NOTHING
static bool is_child(const Object *ob, const Object *parent)
#define FLT_MAX
Definition stdcycles.h:14
Definition DNA_ID.h:413
const RuntimeNodeEnumItemsHandle * enum_items
char description[64]
void * default_value
char idname[64]
bNodeTreeInterfaceItem ** items_array
bNodeTreeInterfacePanel root_panel
bNodeTreeInterfaceRuntimeHandle * runtime
bNodeTreeInterface tree_interface
Defines a socket type.
Definition BKE_node.hh:151
void(* draw_color_simple)(const bNodeSocketType *socket_type, float *r_color)
Definition BKE_node.hh:162
void(* interface_from_socket)(ID *id, bNodeTreeInterfaceSocket *interface_socket, const bNode *node, const bNodeSocket *socket)
Definition BKE_node.hh:170
uint8_t flag
Definition wm_window.cc:138