Blender V4.3
node_add.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <numeric>
10
12
13#include "MEM_guardedalloc.h"
14
16#include "DNA_node_types.h"
17#include "DNA_texture_types.h"
18
19#include "BLI_easing.h"
20#include "BLI_listbase.h"
21#include "BLI_math_geom.h"
22
23#include "BLT_translation.hh"
24
25#include "BKE_context.hh"
26#include "BKE_image.hh"
27#include "BKE_lib_id.hh"
28#include "BKE_main.hh"
29#include "BKE_node.hh"
30#include "BKE_node_runtime.hh"
32#include "BKE_report.hh"
33#include "BKE_scene.hh"
34#include "BKE_texture.h"
35
37
38#include "ED_asset.hh"
40#include "ED_node.hh" /* own include */
41#include "ED_render.hh"
42#include "ED_screen.hh"
43
44#include "RNA_access.hh"
45#include "RNA_define.hh"
46#include "RNA_enum_types.hh"
47#include "RNA_prototypes.hh"
48
49#include "WM_api.hh"
50#include "WM_types.hh"
51
52#include "UI_view2d.hh"
53
54#include "io_utils.hh"
55#include <fmt/format.h>
56
57#include "node_intern.hh" /* own include */
58
60
61/* -------------------------------------------------------------------- */
65static void position_node_based_on_mouse(bNode &node, const float2 &location)
66{
67 node.locx = location.x - NODE_DY * 1.5f / UI_SCALE_FAC;
68 node.locy = location.y + NODE_DY * 0.5f / UI_SCALE_FAC;
69}
70
71bNode *add_node(const bContext &C, const StringRef idname, const float2 &location)
72{
73 SpaceNode &snode = *CTX_wm_space_node(&C);
74 Main &bmain = *CTX_data_main(&C);
76
78
79 const std::string idname_str = idname;
80
81 bNode *node = bke::node_add_node(&C, &node_tree, idname_str.c_str());
82 BLI_assert(node && node->typeinfo);
83
84 position_node_based_on_mouse(*node, location);
85
86 bke::node_set_selected(node, true);
87 ED_node_set_active(&bmain, &snode, &node_tree, node, nullptr);
88
90 return node;
91}
92
93bNode *add_static_node(const bContext &C, int type, const float2 &location)
94{
95 SpaceNode &snode = *CTX_wm_space_node(&C);
96 Main &bmain = *CTX_data_main(&C);
98
100
101 bNode *node = bke::node_add_static_node(&C, &node_tree, type);
102 BLI_assert(node && node->typeinfo);
103
104 position_node_based_on_mouse(*node, location);
105
106 bke::node_set_selected(node, true);
107 ED_node_set_active(&bmain, &snode, &node_tree, node, nullptr);
108
110 return node;
111}
112
115/* -------------------------------------------------------------------- */
119std::optional<float2> link_path_intersection(const bNodeLink &link, const Span<float2> path)
120{
121 std::array<float2, NODE_LINK_RESOL + 1> coords;
123
124 for (const int i : path.index_range().drop_back(1)) {
125 for (const int j : IndexRange(NODE_LINK_RESOL)) {
127 if (isect_seg_seg_v2_point(path[i], path[i + 1], coords[j], coords[j + 1], result) > 0) {
128 return result;
129 }
130 }
131 }
132
133 return std::nullopt;
134}
135
137 /* The output socket's owner node. */
139 /* Intersected links connected to the socket and their path intersection locations. */
141};
142
144{
145 const ARegion &region = *CTX_wm_region(C);
146 SpaceNode &snode = *CTX_wm_space_node(C);
147 bNodeTree &ntree = *snode.edittree;
148
149 Vector<float2> path;
150 RNA_BEGIN (op->ptr, itemptr, "path") {
151 float2 loc_region;
152 RNA_float_get_array(&itemptr, "loc", loc_region);
153 float2 loc_view;
154 UI_view2d_region_to_view(&region.v2d, loc_region.x, loc_region.y, &loc_view.x, &loc_view.y);
155 path.append(loc_view);
156 if (path.size() >= 256) {
157 break;
158 }
159 }
160 RNA_END;
161
162 if (path.is_empty()) {
164 }
165
166 node_deselect_all(ntree);
167
168 ntree.ensure_topology_cache();
169 const Vector<bNode *> frame_nodes = ntree.nodes_by_type("NodeFrame");
170
172
173 /* All link "cuts" that start at a particular output socket. Deduplicating new reroutes per
174 * output socket is useful because it allows reusing reroutes for connected intersections.
175 * Further deduplication using the second map means we only have one cut per link. */
177
178 LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
179 if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
180 continue;
181 }
182 const std::optional<float2> cut = link_path_intersection(*link, path);
183 if (!cut) {
184 continue;
185 }
186 RerouteCutsForSocket &from_cuts = cuts_per_socket.lookup_or_add_default(link->fromsock);
187 from_cuts.from_node = link->fromnode;
188 from_cuts.links.add(link, *cut);
189 }
190
191 for (const auto item : cuts_per_socket.items()) {
192 const Map<bNodeLink *, float2> &cuts = item.value.links;
193
194 bNode *reroute = bke::node_add_static_node(C, &ntree, NODE_REROUTE);
195
196 bke::node_add_link(&ntree,
197 item.value.from_node,
198 item.key,
199 reroute,
200 static_cast<bNodeSocket *>(reroute->inputs.first));
201
202 /* Reconnect links from the original output socket to the new reroute. */
203 for (bNodeLink *link : cuts.keys()) {
204 link->fromnode = reroute;
205 link->fromsock = static_cast<bNodeSocket *>(reroute->outputs.first);
207 }
208
209 /* Place the new reroute at the average location of all connected cuts. */
210 const float2 insert_point = std::accumulate(
211 cuts.values().begin(), cuts.values().end(), float2(0)) /
212 cuts.size();
213 reroute->locx = insert_point.x / UI_SCALE_FAC;
214 reroute->locy = insert_point.y / UI_SCALE_FAC;
215
216 /* Attach the reroute node to frame nodes behind it. */
217 for (const int i : frame_nodes.index_range()) {
218 bNode *frame_node = frame_nodes.last(i);
219 if (BLI_rctf_isect_pt_v(&frame_node->runtime->totr, insert_point)) {
220 bke::node_attach_node(&ntree, reroute, frame_node);
221 break;
222 }
223 }
224 }
225
227 return OPERATOR_FINISHED;
228}
229
231{
232 ot->name = "Add Reroute";
233 ot->idname = "NODE_OT_add_reroute";
234 ot->description = "Add a reroute node";
235
240
242
243 /* flags */
245
246 /* properties */
247 PropertyRNA *prop;
248 prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
250 /* internal */
251 RNA_def_int(ot->srna, "cursor", WM_CURSOR_CROSS, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
252}
253
256/* -------------------------------------------------------------------- */
261 const bNodeTree &node_group,
262 ReportList &reports)
263{
264 if (node_group.type != node_tree.type) {
265 return false;
266 }
267
268 const char *disabled_hint = nullptr;
269 if (!bke::node_group_poll(&node_tree, &node_group, &disabled_hint)) {
270 if (disabled_hint) {
271 BKE_reportf(&reports,
272 RPT_ERROR,
273 "Cannot add node group '%s' to '%s':\n %s",
274 node_group.id.name + 2,
275 node_tree.id.name + 2,
276 disabled_hint);
277 }
278 else {
279 BKE_reportf(&reports,
280 RPT_ERROR,
281 "Cannot add node group '%s' to '%s'",
282 node_group.id.name + 2,
283 node_tree.id.name + 2);
284 }
285
286 return false;
287 }
288
289 return true;
290}
291
293{
294 Main *bmain = CTX_data_main(C);
295 SpaceNode *snode = CTX_wm_space_node(C);
296 bNodeTree *ntree = snode->edittree;
297
298 bNodeTree *node_group = reinterpret_cast<bNodeTree *>(
300 if (!node_group) {
301 return OPERATOR_CANCELLED;
302 }
303 if (!node_group_add_poll(*ntree, *node_group, *op->reports)) {
304 return OPERATOR_CANCELLED;
305 }
306
308
309 const char *node_idname = node_group_idname(C);
310 if (node_idname[0] == '\0') {
311 BKE_report(op->reports, RPT_WARNING, "Could not determine type of group node");
312 return OPERATOR_CANCELLED;
313 }
314
315 bNode *group_node = add_node(*C, node_idname, snode->runtime->cursor);
316 if (!group_node) {
317 BKE_report(op->reports, RPT_WARNING, "Could not add node group");
318 return OPERATOR_CANCELLED;
319 }
320 if (!RNA_boolean_get(op->ptr, "show_datablock_in_node")) {
321 /* By default, don't show the data-block selector since it's not usually necessary for assets.
322 */
323 group_node->flag &= ~NODE_OPTIONS;
324 }
325 group_node->width = node_group->default_group_node_width;
326
327 group_node->id = &node_group->id;
328 id_us_plus(group_node->id);
330
331 bke::node_set_active(ntree, group_node);
332 ED_node_tree_propagate_change(C, bmain, nullptr);
335 return OPERATOR_FINISHED;
336}
337
339{
341 return false;
342 }
343 const SpaceNode *snode = CTX_wm_space_node(C);
344 if (snode->edittree->type == NTREE_CUSTOM) {
346 C, "Adding node groups isn't supported for custom (Python defined) node trees");
347 return false;
348 }
349 return true;
350}
351
352static int node_add_group_invoke(bContext *C, wmOperator *op, const wmEvent *event)
353{
354 ARegion *region = CTX_wm_region(C);
355 SpaceNode *snode = CTX_wm_space_node(C);
356
357 /* Convert mouse coordinates to v2d space. */
358 UI_view2d_region_to_view(&region->v2d,
359 event->mval[0],
360 event->mval[1],
361 &snode->runtime->cursor[0],
362 &snode->runtime->cursor[1]);
363
364 snode->runtime->cursor[0] /= UI_SCALE_FAC;
365 snode->runtime->cursor[1] /= UI_SCALE_FAC;
366
367 return node_add_group_exec(C, op);
368}
369
371{
372 /* identifiers */
373 ot->name = "Add Node Group";
374 ot->description = "Add an existing node group to the current node editor";
375 ot->idname = "NODE_OT_add_group";
376
377 /* callbacks */
381
382 /* flags */
384
386
388 ot->srna, "show_datablock_in_node", true, "Show the datablock selector in the node", "");
390}
391
394/* -------------------------------------------------------------------- */
398static bool add_node_group_asset(const bContext &C,
400 ReportList &reports)
401{
402 Main &bmain = *CTX_data_main(&C);
403 SpaceNode &snode = *CTX_wm_space_node(&C);
404 bNodeTree &edit_tree = *snode.edittree;
405
406 bNodeTree *node_group = reinterpret_cast<bNodeTree *>(
408 if (!node_group) {
409 return false;
410 }
411 if (!node_group_add_poll(edit_tree, *node_group, reports)) {
412 /* Remove the node group if it was newly appended but can't be added to the tree. */
413 id_us_plus(&node_group->id);
414 BKE_id_free_us(&bmain, node_group);
415 return false;
416 }
417
419
420 bNode *group_node = add_node(
422 if (!group_node) {
423 BKE_report(&reports, RPT_WARNING, "Could not add node group");
424 return false;
425 }
426 /* By default, don't show the data-block selector since it's not usually necessary for assets. */
427 group_node->flag &= ~NODE_OPTIONS;
428 group_node->width = node_group->default_group_node_width;
429
430 group_node->id = &node_group->id;
431 id_us_plus(group_node->id);
432 BKE_ntree_update_tag_node_property(&edit_tree, group_node);
433
434 bke::node_set_active(&edit_tree, group_node);
435 ED_node_tree_propagate_change(&C, &bmain, nullptr);
436 WM_event_add_notifier(&C, NC_NODE | NA_ADDED, nullptr);
438
439 return true;
440}
441
443{
444 ARegion &region = *CTX_wm_region(C);
445 SpaceNode &snode = *CTX_wm_space_node(C);
446
449 if (!asset) {
450 return OPERATOR_CANCELLED;
451 }
452
453 /* Convert mouse coordinates to v2d space. */
454 UI_view2d_region_to_view(&region.v2d,
455 event->mval[0],
456 event->mval[1],
457 &snode.runtime->cursor[0],
458 &snode.runtime->cursor[1]);
459
460 snode.runtime->cursor /= UI_SCALE_FAC;
461
462 if (!add_node_group_asset(*C, *asset, *op->reports)) {
463 return OPERATOR_CANCELLED;
464 }
465
466 wmOperatorType *ot = WM_operatortype_find("NODE_OT_translate_attach_remove_on_cancel", true);
467 BLI_assert(ot);
472
473 return OPERATOR_FINISHED;
474}
475
477 wmOperatorType * /*ot*/,
479{
482 if (!asset) {
483 return "";
484 }
485 const AssetMetaData &asset_data = asset->get_metadata();
486 if (!asset_data.description) {
487 return "";
488 }
489 return TIP_(asset_data.description);
490}
491
493{
494 ot->name = "Add Node Group Asset";
495 ot->description = "Add a node group asset to the active node tree";
496 ot->idname = "NODE_OT_add_group_asset";
497
501
503
505}
506
509/* -------------------------------------------------------------------- */
514{
515 Main *bmain = CTX_data_main(C);
516 SpaceNode *snode = CTX_wm_space_node(C);
517 bNodeTree *ntree = snode->edittree;
518
519 Object *object = reinterpret_cast<Object *>(
521
522 if (!object) {
523 return OPERATOR_CANCELLED;
524 }
525
527
528 bNode *object_node = add_static_node(*C, GEO_NODE_OBJECT_INFO, snode->runtime->cursor);
529 if (!object_node) {
530 BKE_report(op->reports, RPT_WARNING, "Could not add node object");
531 return OPERATOR_CANCELLED;
532 }
533
534 bNodeSocket *sock = bke::node_find_socket(object_node, SOCK_IN, "Object");
535 if (!sock) {
537 return OPERATOR_CANCELLED;
538 }
539
541 socket_data->value = object;
542 id_us_plus(&object->id);
543
544 bke::node_set_active(ntree, object_node);
545 ED_node_tree_propagate_change(C, bmain, ntree);
547
548 return OPERATOR_FINISHED;
549}
550
551static int node_add_object_invoke(bContext *C, wmOperator *op, const wmEvent *event)
552{
553 ARegion *region = CTX_wm_region(C);
554 SpaceNode *snode = CTX_wm_space_node(C);
555
556 /* Convert mouse coordinates to v2d space. */
557 UI_view2d_region_to_view(&region->v2d,
558 event->mval[0],
559 event->mval[1],
560 &snode->runtime->cursor[0],
561 &snode->runtime->cursor[1]);
562
563 snode->runtime->cursor[0] /= UI_SCALE_FAC;
564 snode->runtime->cursor[1] /= UI_SCALE_FAC;
565
566 return node_add_object_exec(C, op);
567}
568
570{
571 const SpaceNode *snode = CTX_wm_space_node(C);
573}
574
576{
577 /* identifiers */
578 ot->name = "Add Node Object";
579 ot->description = "Add an object info node to the current node editor";
580 ot->idname = "NODE_OT_add_object";
581
582 /* callbacks */
586
587 /* flags */
589
591}
592
595/* -------------------------------------------------------------------- */
600{
601 Main *bmain = CTX_data_main(C);
602 SpaceNode &snode = *CTX_wm_space_node(C);
603 bNodeTree &ntree = *snode.edittree;
604
605 Collection *collection = reinterpret_cast<Collection *>(
607
608 if (!collection) {
609 return OPERATOR_CANCELLED;
610 }
611
613
614 bNode *collection_node = add_static_node(*C, GEO_NODE_COLLECTION_INFO, snode.runtime->cursor);
615 if (!collection_node) {
616 BKE_report(op->reports, RPT_WARNING, "Could not add node collection");
617 return OPERATOR_CANCELLED;
618 }
619
620 bNodeSocket *sock = bke::node_find_socket(collection_node, SOCK_IN, "Collection");
621 if (!sock) {
622 BKE_report(op->reports, RPT_WARNING, "Could not find node collection socket");
623 return OPERATOR_CANCELLED;
624 }
625
627 socket_data->value = collection;
628 id_us_plus(&collection->id);
629
630 bke::node_set_active(&ntree, collection_node);
631 ED_node_tree_propagate_change(C, bmain, &ntree);
633
634 return OPERATOR_FINISHED;
635}
636
637static int node_add_collection_invoke(bContext *C, wmOperator *op, const wmEvent *event)
638{
639 ARegion *region = CTX_wm_region(C);
640 SpaceNode *snode = CTX_wm_space_node(C);
641
642 /* Convert mouse coordinates to v2d space. */
643 UI_view2d_region_to_view(&region->v2d,
644 event->mval[0],
645 event->mval[1],
646 &snode->runtime->cursor[0],
647 &snode->runtime->cursor[1]);
648
649 snode->runtime->cursor[0] /= UI_SCALE_FAC;
650 snode->runtime->cursor[1] /= UI_SCALE_FAC;
651
652 return node_add_collection_exec(C, op);
653}
654
656{
657 const SpaceNode *snode = CTX_wm_space_node(C);
659}
660
662{
663 /* identifiers */
664 ot->name = "Add Node Collection";
665 ot->description = "Add a collection info node to the current node editor";
666 ot->idname = "NODE_OT_add_collection";
667
668 /* callbacks */
672
673 /* flags */
675
677}
678
681/* -------------------------------------------------------------------- */
691
697
698static int node_add_file_modal(bContext *C, wmOperator *op, const wmEvent *event)
699{
700 NodeStackAnimationData *data = static_cast<NodeStackAnimationData *>(op->customdata);
701
702 if (event->type != TIMER || data == nullptr || data->anim_timer != event->customdata) {
704 }
705
706 const float node_stack_anim_duration = 0.25f;
707 const float duration = float(data->anim_timer->time_duration);
708 const float prev_duration = duration - float(data->anim_timer->time_delta);
709 const float clamped_duration = math::min(duration, node_stack_anim_duration);
710 const float delta_factor =
711 BLI_easing_cubic_ease_in_out(clamped_duration, 0.0f, 1.0f, node_stack_anim_duration) -
712 BLI_easing_cubic_ease_in_out(prev_duration, 0.0f, 1.0f, node_stack_anim_duration);
713
714 bool redraw = false;
715 /* Each node is pushed by all previous nodes in the stack. */
716 float stack_offset = 0.0f;
717
718 for (bNode *node : data->nodes) {
719 node->locy -= stack_offset;
720 stack_offset += (node->runtime->totr.ymax - node->runtime->totr.ymin) * delta_factor;
721 redraw = true;
722 }
723
724 if (redraw) {
726 }
727
728 /* End stack animation. */
729 if (duration > node_stack_anim_duration) {
730 WM_event_timer_remove(CTX_wm_manager(C), nullptr, data->anim_timer);
731 MEM_delete(data);
732 op->customdata = nullptr;
734 }
735
737}
738
740{
741 Main *bmain = CTX_data_main(C);
742 SpaceNode &snode = *CTX_wm_space_node(C);
743 int type = 0;
744 switch (snode.nodetree->type) {
745 case NTREE_SHADER:
746 type = SH_NODE_TEX_IMAGE;
747 break;
748 case NTREE_TEXTURE:
749 type = TEX_NODE_IMAGE;
750 break;
751 case NTREE_COMPOSIT:
752 type = CMP_NODE_IMAGE;
753 break;
754 case NTREE_GEOMETRY:
756 break;
757 default:
758 return OPERATOR_CANCELLED;
759 }
760 Vector<Image *> images;
761 /* Load all paths as ID Images. */
763 for (const std::string &path : paths) {
764 RNA_string_set(op->ptr, "filepath", path.c_str());
765 Image *image = (Image *)WM_operator_drop_load_path(C, op, ID_IM);
766 if (!image) {
767 BKE_report(op->reports, RPT_WARNING, fmt::format("Could not load {}", path).c_str());
768 continue;
769 }
770 images.append(image);
771 /* When adding new image file via drag-drop we need to load #ImBuf in order
772 * to get proper image source. */
773 BKE_image_signal(bmain, image, nullptr, IMA_SIGNAL_RELOAD);
775 }
776
777 /* If not path is provided, try to get a ID Image from operator. */
778 if (paths.is_empty()) {
779 Image *image = (Image *)WM_operator_drop_load_path(C, op, ID_IM);
780 if (image) {
781 images.append(image);
782 }
783 }
784
785 bNodeTree &node_tree = *snode.edittree;
786 float2 position = snode.runtime->cursor;
787 Vector<bNode *> nodes;
788 /* Add a node for each image. */
789 for (Image *image : images) {
790 bNode *node = add_static_node(*C, type, position);
791 if (!node) {
792 BKE_report(op->reports, RPT_WARNING, "Could not add an image node");
793 continue;
794 }
795 if (type == GEO_NODE_IMAGE_TEXTURE) {
796 bNodeSocket *image_socket = (bNodeSocket *)node->inputs.first;
797 bNodeSocketValueImage *socket_value = (bNodeSocketValueImage *)image_socket->default_value;
798 socket_value->value = image;
799 }
800 else {
801 node->id = (ID *)image;
803 }
805 nodes.append(node);
806 /* Initial offset between nodes. */
807 position[1] -= 20.0f;
808 }
809
810 if (nodes.is_empty()) {
811 return OPERATOR_CANCELLED;
812 }
813
814 /* Set new nodes as selected. */
816 for (bNode *node : nodes) {
817 bke::node_set_selected(node, true);
818 }
819 ED_node_set_active(bmain, &snode, &node_tree, nodes[0], nullptr);
820
822
825
826 if (nodes.size() == 1) {
827 return OPERATOR_FINISHED;
828 }
829
830 /* Start the stack animation, so each node is placed on top of each other. */
831 NodeStackAnimationData *data = MEM_new<NodeStackAnimationData>(__func__);
832 data->nodes = std::move(nodes);
833 data->anim_timer = WM_event_timer_add(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.02);
834 op->customdata = data;
836
838}
839
840static int node_add_file_invoke(bContext *C, wmOperator *op, const wmEvent *event)
841{
842 ARegion *region = CTX_wm_region(C);
843 SpaceNode *snode = CTX_wm_space_node(C);
844
845 /* Convert mouse coordinates to `v2d` space. */
846 UI_view2d_region_to_view(&region->v2d,
847 event->mval[0],
848 event->mval[1],
849 &snode->runtime->cursor[0],
850 &snode->runtime->cursor[1]);
851
852 snode->runtime->cursor[0] /= UI_SCALE_FAC;
853 snode->runtime->cursor[1] /= UI_SCALE_FAC;
854
856 RNA_struct_property_is_set(op->ptr, "filepath"))
857 {
858 return node_add_file_exec(C, op);
859 }
860 return WM_operator_filesel(C, op, event);
861}
862
864{
865 /* identifiers */
866 ot->name = "Add File Node";
867 ot->description = "Add a file node to the current node editor";
868 ot->idname = "NODE_OT_add_file";
869
870 /* callbacks */
875
876 /* flags */
878
888}
889
892/* -------------------------------------------------------------------- */
897{
898 SpaceNode *snode = CTX_wm_space_node(C);
899
901}
902
904{
905 Main *bmain = CTX_data_main(C);
906 SpaceNode &snode = *CTX_wm_space_node(C);
907
909 if (!mask) {
910 return OPERATOR_CANCELLED;
911 }
912
914
915 bNode *node = add_static_node(*C, CMP_NODE_MASK, snode.runtime->cursor);
916
917 if (!node) {
918 BKE_report(op->reports, RPT_WARNING, "Could not add a mask node");
919 return OPERATOR_CANCELLED;
920 }
921
922 node->id = mask;
923 id_us_plus(mask);
924
927
928 return OPERATOR_FINISHED;
929}
930
932{
933 /* identifiers */
934 ot->name = "Add Mask Node";
935 ot->description = "Add a mask node to the current node editor";
936 ot->idname = "NODE_OT_add_mask";
937
938 /* callbacks */
941
942 /* flags */
944
946}
947
950/* -------------------------------------------------------------------- */
955{
956 Main *bmain = CTX_data_main(C);
957 SpaceNode *snode = CTX_wm_space_node(C);
958 bNodeTree *ntree = snode->edittree;
959
960 Material *material = reinterpret_cast<Material *>(
962
963 if (!material) {
964 return OPERATOR_CANCELLED;
965 }
966
968
969 bNode *material_node = add_static_node(*C, GEO_NODE_INPUT_MATERIAL, snode->runtime->cursor);
970 if (!material_node) {
971 BKE_report(op->reports, RPT_WARNING, "Could not add material");
972 return OPERATOR_CANCELLED;
973 }
974
975 material_node->id = &material->id;
976 id_us_plus(&material->id);
977
978 ED_node_tree_propagate_change(C, bmain, ntree);
980
981 return OPERATOR_FINISHED;
982}
983
984static int node_add_material_invoke(bContext *C, wmOperator *op, const wmEvent *event)
985{
986 ARegion *region = CTX_wm_region(C);
987 SpaceNode *snode = CTX_wm_space_node(C);
988
989 /* Convert mouse coordinates to v2d space. */
990 UI_view2d_region_to_view(&region->v2d,
991 event->mval[0],
992 event->mval[1],
993 &snode->runtime->cursor[0],
994 &snode->runtime->cursor[1]);
995
996 snode->runtime->cursor[0] /= UI_SCALE_FAC;
997 snode->runtime->cursor[1] /= UI_SCALE_FAC;
998
999 return node_add_material_exec(C, op);
1000}
1001
1003{
1004 const SpaceNode *snode = CTX_wm_space_node(C);
1006}
1007
1009{
1010 /* identifiers */
1011 ot->name = "Add Material";
1012 ot->description = "Add a material node to the current node editor";
1013 ot->idname = "NODE_OT_add_material";
1014
1015 /* callbacks */
1019
1020 /* flags */
1022
1024}
1025
1028/* -------------------------------------------------------------------- */
1033{
1034 SpaceNode *snode = CTX_wm_space_node(C);
1035 Main *bmain = CTX_data_main(C);
1036 bNodeTree *ntree;
1038 PropertyRNA *prop;
1039 const char *idname;
1040 char treename_buf[MAX_ID_NAME - 2];
1041 const char *treename;
1042
1043 if (RNA_struct_property_is_set(op->ptr, "type")) {
1044 prop = RNA_struct_find_property(op->ptr, "type");
1045 RNA_property_enum_identifier(C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &idname);
1046 }
1047 else if (snode) {
1048 idname = snode->tree_idname;
1049 }
1050 else {
1051 return OPERATOR_CANCELLED;
1052 }
1053
1054 if (!bke::node_tree_type_find(idname)) {
1055 BKE_reportf(op->reports, RPT_ERROR, "Node tree type %s undefined", idname);
1056 return OPERATOR_CANCELLED;
1057 }
1058
1059 if (RNA_struct_property_is_set(op->ptr, "name")) {
1060 RNA_string_get(op->ptr, "name", treename_buf);
1061 treename = treename_buf;
1062 }
1063 else {
1064 const bke::bNodeTreeType *type = bke::node_tree_type_find(idname);
1065 treename = type->ui_name;
1066 }
1067
1068 ntree = bke::node_tree_add_tree(bmain, treename, idname);
1069
1070 /* Hook into UI. */
1072
1073 if (prop) {
1074 /* #RNA_property_pointer_set increases the user count, fixed here as the editor is the initial
1075 * user. */
1076 id_us_min(&ntree->id);
1077
1078 if (ptr.owner_id) {
1079 BKE_id_move_to_same_lib(*bmain, ntree->id, *ptr.owner_id);
1080 }
1081
1082 PointerRNA idptr = RNA_id_pointer_create(&ntree->id);
1083 RNA_property_pointer_set(&ptr, prop, idptr, nullptr);
1084 RNA_property_update(C, &ptr, prop);
1085 }
1086 else if (snode) {
1087 snode->nodetree = ntree;
1088
1090 }
1091
1092 WM_event_add_notifier(C, NC_NODE | NA_ADDED, nullptr);
1093
1094 return OPERATOR_FINISHED;
1095}
1096
1098 PointerRNA * /*ptr*/,
1099 PropertyRNA * /*prop*/,
1100 bool *r_free)
1101{
1102 return rna_node_tree_type_itemf(nullptr, nullptr, r_free);
1103}
1104
1106{
1107 PropertyRNA *prop;
1108
1109 /* identifiers */
1110 ot->name = "New Node Tree";
1111 ot->idname = "NODE_OT_new_node_tree";
1112 ot->description = "Create a new node tree";
1113
1114 /* api callbacks */
1116
1117 /* flags */
1119
1120 prop = RNA_def_enum(ot->srna, "type", rna_enum_dummy_NULL_items, 0, "Tree Type", "");
1122 RNA_def_string(ot->srna, "name", "NodeTree", MAX_ID_NAME - 2, "Name", "");
1123}
1124
1127} // namespace blender::ed::space_node
Main runtime representation of an asset.
SpaceNode * CTX_wm_space_node(const bContext *C)
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
wmWindow * CTX_wm_window(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
#define IMA_SIGNAL_RELOAD
Definition BKE_image.hh:137
void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
void BKE_id_free_us(Main *bmain, void *idv) ATTR_NONNULL()
void id_us_plus(ID *id)
Definition lib_id.cc:351
void BKE_id_move_to_same_lib(Main &bmain, ID &id, const ID &owner_id)
Definition lib_id.cc:862
void id_us_min(ID *id)
Definition lib_id.cc:359
#define NODE_REROUTE
Definition BKE_node.hh:804
#define SH_NODE_TEX_IMAGE
Definition BKE_node.hh:930
#define GEO_NODE_IMAGE_TEXTURE
Definition BKE_node.hh:1261
#define TEX_NODE_IMAGE
Definition BKE_node.hh:1143
#define CMP_NODE_IMAGE
Definition BKE_node.hh:1031
void BKE_ntree_update_tag_link_changed(bNodeTree *ntree)
void BKE_ntree_update_tag_node_property(bNodeTree *ntree, bNode *node)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
float BLI_easing_cubic_ease_in_out(float time, float begin, float change, float duration)
Definition easing.c:108
#define LISTBASE_FOREACH(type, var, list)
int isect_seg_seg_v2_point(const float v0[2], const float v1[2], const float v2[2], const float v3[2], float r_vi[2])
bool BLI_rctf_isect_pt_v(const struct rctf *rect, const float xy[2])
#define ELEM(...)
#define TIP_(msgid)
void DEG_relations_tag_update(Main *bmain)
#define MAX_ID_NAME
Definition DNA_ID.h:377
@ ID_IM
@ ID_NT
@ ID_MSK
@ ID_MA
@ ID_GR
@ ID_OB
Object groups, one object can be in many groups at once.
@ NTREE_TEXTURE
@ NTREE_CUSTOM
@ NTREE_SHADER
@ NTREE_GEOMETRY
@ NTREE_COMPOSIT
@ SOCK_IN
@ FILE_SORT_DEFAULT
@ FILE_SPECIAL
@ FILE_TYPE_MOVIE
@ FILE_TYPE_FOLDER
@ FILE_TYPE_IMAGE
@ FILE_DEFAULTDISPLAY
#define UI_SCALE_FAC
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
void ED_node_tree_propagate_change(const bContext *C, Main *bmain, bNodeTree *ntree)
Definition node_edit.cc:492
void ED_node_set_active(Main *bmain, SpaceNode *snode, bNodeTree *ntree, bNode *node, bool *r_active_texture_changed)
Definition node_edit.cc:722
void ED_node_tree_update(const bContext *C)
Definition node_draw.cc:147
void ED_preview_kill_jobs(wmWindowManager *wm, Main *bmain)
bool ED_operator_node_editable(bContext *C)
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
Read Guarded memory(de)allocation.
#define RNA_BEGIN(sptr, itemptr, propname)
#define RNA_END
const EnumPropertyItem * rna_node_tree_type_itemf(void *data, bool(*poll)(void *data, blender::bke::bNodeTreeType *), bool *r_free)
PropertyFlag
Definition RNA_types.hh:201
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
void UI_context_active_but_prop_get_templateID(const bContext *C, PointerRNA *r_ptr, PropertyRNA **r_prop)
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1663
@ WM_FILESEL_FILES
Definition WM_api.hh:937
@ WM_FILESEL_DIRECTORY
Definition WM_api.hh:934
@ WM_FILESEL_RELPATH
Definition WM_api.hh:933
@ WM_FILESEL_FILEPATH
Definition WM_api.hh:936
@ FILE_OPENFILE
Definition WM_api.hh:945
@ OPTYPE_INTERNAL
Definition WM_types.hh:182
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:198
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_NODE
Definition WM_types.hh:361
#define NA_ADDED
Definition WM_types.hh:552
#define NA_EDITED
Definition WM_types.hh:550
#define NC_IMAGE
Definition WM_types.hh:351
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:218
constexpr IndexRange drop_back(int64_t n) const
SubIterator begin() const
Definition BLI_map.hh:730
SubIterator end() const
Definition BLI_map.hh:740
Value & lookup_or_add_default(const Key &key)
Definition BLI_map.hh:601
KeyIterator keys() const
Definition BLI_map.hh:837
ValueIterator values() const
Definition BLI_map.hh:846
int64_t size() const
Definition BLI_map.hh:927
ItemIterator items() const
Definition BLI_map.hh:864
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
int64_t size() const
void append(const T &value)
const T & last(const int64_t n=0) const
bool is_empty() const
IndexRange index_range() const
input_tx image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "preview_img") .compute_source("compositor_compute_preview.glsl") .do_static_compilation(true)
OperationNode * node
draw_view in_light_buf[] float
ccl_device_inline float4 mask(const int4 mask, const float4 a)
void node_attach_node(bNodeTree *ntree, bNode *node, bNode *parent)
Definition node.cc:3107
void node_set_active(bNodeTree *ntree, bNode *node)
Definition node.cc:3896
bNode * node_add_static_node(const bContext *C, bNodeTree *ntree, int type)
Definition node.cc:2642
bool node_group_poll(const bNodeTree *nodetree, const bNodeTree *grouptree, const char **r_disabled_hint)
bNodeLink * node_add_link(bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, bNode *tonode, bNodeSocket *tosock)
Definition node.cc:2912
bNodeTree * node_tree_add_tree(Main *bmain, const char *name, const char *idname)
Definition node.cc:3226
bNode * node_add_node(const bContext *C, bNodeTree *ntree, const char *idname)
Definition node.cc:2617
bool node_set_selected(bNode *node, bool select)
Definition node.cc:3863
void node_tag_update_id(bNode *node)
Definition node.cc:4007
bNodeTreeType * node_tree_type_find(const char *idname)
Definition node.cc:1621
bNodeSocket * node_find_socket(bNode *node, eNodeSocketInOut in_out, StringRef identifier)
Definition node.cc:1829
ID * asset_local_id_ensure_imported(Main &bmain, const asset_system::AssetRepresentation &asset)
void operator_asset_reference_props_register(StructRNA &srna)
const asset_system::AssetRepresentation * operator_asset_reference_props_get_asset_from_all_library(const bContext &C, PointerRNA &ptr, ReportList *reports)
Vector< std::string > paths_from_operator_properties(PointerRNA *ptr)
Definition io_utils.cc:74
void NODE_OT_add_object(wmOperatorType *ot)
Definition node_add.cc:575
static bool node_add_material_poll(bContext *C)
Definition node_add.cc:1002
static int node_add_group_asset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition node_add.cc:442
void NODE_OT_add_mask(wmOperatorType *ot)
Definition node_add.cc:931
static int node_add_file_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition node_add.cc:840
static bool node_add_object_poll(bContext *C)
Definition node_add.cc:569
bNode * add_static_node(const bContext &C, int type, const float2 &location)
Definition node_add.cc:93
static int node_add_material_exec(bContext *C, wmOperator *op)
Definition node_add.cc:954
static bool node_add_group_poll(bContext *C)
Definition node_add.cc:338
static bool node_add_file_poll(bContext *C)
Definition node_add.cc:685
void NODE_OT_add_reroute(wmOperatorType *ot)
Definition node_add.cc:230
bool node_deselect_all(bNodeTree &node_tree)
void NODE_OT_add_collection(wmOperatorType *ot)
Definition node_add.cc:661
static int add_reroute_exec(bContext *C, wmOperator *op)
Definition node_add.cc:143
bNode * add_node(const bContext &C, const StringRef idname, const float2 &location)
Definition node_add.cc:71
static int node_add_mask_exec(bContext *C, wmOperator *op)
Definition node_add.cc:903
void NODE_OT_add_group_asset(wmOperatorType *ot)
Definition node_add.cc:492
static void position_node_based_on_mouse(bNode &node, const float2 &location)
Definition node_add.cc:65
static const EnumPropertyItem * new_node_tree_type_itemf(bContext *, PointerRNA *, PropertyRNA *, bool *r_free)
Definition node_add.cc:1097
static int new_node_tree_exec(bContext *C, wmOperator *op)
Definition node_add.cc:1032
static int node_add_group_exec(bContext *C, wmOperator *op)
Definition node_add.cc:292
bool node_link_is_hidden_or_dimmed(const View2D &v2d, const bNodeLink &link)
std::optional< float2 > link_path_intersection(const bNodeLink &link, const Span< float2 > path)
Definition node_add.cc:119
static bool node_group_add_poll(const bNodeTree &node_tree, const bNodeTree &node_group, ReportList &reports)
Definition node_add.cc:260
const char * node_group_idname(bContext *C)
static int node_add_object_exec(bContext *C, wmOperator *op)
Definition node_add.cc:513
static int node_add_file_exec(bContext *C, wmOperator *op)
Definition node_add.cc:739
static int node_add_file_modal(bContext *C, wmOperator *op, const wmEvent *event)
Definition node_add.cc:698
void node_link_bezier_points_evaluated(const bNodeLink &link, std::array< float2, NODE_LINK_RESOL+1 > &coords)
Definition drawnode.cc:1791
static bool node_add_mask_poll(bContext *C)
Definition node_add.cc:896
static int node_add_collection_exec(bContext *C, wmOperator *op)
Definition node_add.cc:599
static int node_add_group_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition node_add.cc:352
static int node_add_collection_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition node_add.cc:637
static std::string node_add_group_asset_get_description(bContext *C, wmOperatorType *, PointerRNA *ptr)
Definition node_add.cc:476
static int node_add_material_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition node_add.cc:984
void NODE_OT_add_group(wmOperatorType *ot)
Definition node_add.cc:370
void NODE_OT_add_file(wmOperatorType *ot)
Definition node_add.cc:863
static bool node_add_collection_poll(bContext *C)
Definition node_add.cc:655
void NODE_OT_add_material(wmOperatorType *ot)
Definition node_add.cc:1008
static int node_add_object_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Definition node_add.cc:551
void NODE_OT_new_node_tree(wmOperatorType *ot)
Definition node_add.cc:1105
static bool add_node_group_asset(const bContext &C, const asset_system::AssetRepresentation &asset, ReportList &reports)
Definition node_add.cc:398
T min(const T &a, const T &b)
#define NODE_LINK_RESOL
#define NODE_DY
bool RNA_property_enum_identifier(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value, const char **r_identifier)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_property_pointer_set(PointerRNA *ptr, PropertyRNA *prop, PointerRNA ptr_value, ReportList *reports)
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_id_pointer_create(ID *id)
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_collection_runtime(StructOrFunctionRNA *cont_, const char *identifier, StructRNA *type, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void RNA_def_enum_funcs(PropertyRNA *prop, EnumPropertyItemFunc itemfunc)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
const EnumPropertyItem rna_enum_dummy_NULL_items[]
Definition rna_rna.cc:29
The meta-data of an asset. By creating and giving this for a data-block (ID.asset_data),...
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
void * first
ID * owner_id
Definition RNA_types.hh:40
char tree_idname[64]
SpaceNode_Runtime * runtime
struct bNodeTree * edittree
struct bNodeTree * nodetree
struct Collection * value
void * default_value
char idname[64]
int default_group_node_width
ListBase links
float locy
float width
ListBase inputs
struct ID * id
float locx
bNodeRuntimeHandle * runtime
ListBase outputs
int mval[2]
Definition WM_types.hh:728
short type
Definition WM_types.hh:722
void * customdata
Definition WM_types.hh:772
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
std::string(* get_description)(bContext *C, wmOperatorType *ot, PointerRNA *ptr)
Definition WM_types.hh:1074
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
struct PointerRNA * ptr
@ WM_CURSOR_CROSS
Definition wm_cursors.hh:26
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
int WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ TIMER
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125
int WM_gesture_lines_modal(bContext *C, wmOperator *op, const wmEvent *event)
void WM_gesture_lines_cancel(bContext *C, wmOperator *op)
int WM_gesture_lines_invoke(bContext *C, wmOperator *op, const wmEvent *event)
bool WM_operator_properties_id_lookup_is_set(PointerRNA *ptr)
ID * WM_operator_properties_id_lookup_from_name_or_session_uid(Main *bmain, PointerRNA *ptr, const ID_Type type)
void WM_operator_properties_filesel(wmOperatorType *ot, const int filter, const short type, const eFileSel_Action action, const eFileSel_Flag flag, const short display, const short sort)
void WM_operator_properties_id_lookup(wmOperatorType *ot, const bool add_name_prop)
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
int WM_operator_filesel(bContext *C, wmOperator *op, const wmEvent *)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
void WM_operator_properties_free(PointerRNA *ptr)
ID * WM_operator_drop_load_path(bContext *C, wmOperator *op, const short idcode)
void WM_event_timer_remove(wmWindowManager *wm, wmWindow *, wmTimer *timer)
wmTimer * WM_event_timer_add(wmWindowManager *wm, wmWindow *win, const int event_type, const double time_step)