Blender V4.5
node_edit.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
8
9#include <algorithm>
10#include <optional>
11
12#include "MEM_guardedalloc.h"
13
14#include "DNA_material_types.h"
15#include "DNA_node_types.h"
16#include "DNA_text_types.h"
17#include "DNA_world_types.h"
18
19#include "BKE_callbacks.hh"
20#include "BKE_context.hh"
21#include "BKE_global.hh"
22#include "BKE_image.hh"
23#include "BKE_lib_id.hh"
24#include "BKE_main.hh"
26#include "BKE_material.hh"
27#include "BKE_node.hh"
29#include "BKE_node_runtime.hh"
31#include "BKE_report.hh"
32#include "BKE_scene.hh"
33#include "BKE_scene_runtime.hh"
34#include "BKE_screen.hh"
35
36#include "BLI_listbase.h"
37#include "BLI_math_vector.h"
38#include "BLI_math_vector.hh"
39#include "BLI_string.h"
40#include "BLI_string_utf8.h"
41
42#include "BLT_translation.hh"
43
44#include "DEG_depsgraph.hh"
48
49#include "RE_engine.h"
50#include "RE_pipeline.h"
51
52#include "ED_image.hh"
53#include "ED_node.hh" /* own include */
54#include "ED_render.hh"
55#include "ED_screen.hh"
56#include "ED_viewer_path.hh"
57
58#include "RNA_access.hh"
59#include "RNA_define.hh"
60#include "RNA_prototypes.hh"
61
62#include "WM_api.hh"
63#include "WM_types.hh"
64
65#include "UI_view2d.hh"
66
67#include "GPU_capabilities.hh"
68#include "GPU_material.hh"
69
70#include "IMB_imbuf_types.hh"
71
72#include "NOD_composite.hh"
73#include "NOD_geometry.hh"
74#include "NOD_shader.h"
75#include "NOD_socket.hh"
76#include "NOD_texture.h"
77#include "node_intern.hh" /* own include */
78
79#include "COM_compositor.hh"
80#include "COM_context.hh"
81#include "COM_profiler.hh"
82
84
85#define USE_ESC_COMPO
86
87/* -------------------------------------------------------------------- */
90
91enum {
94};
95
96struct CompoJob {
97 /* Input parameters. */
103 /* Evaluated state/ */
106 /* Render instance. */
108 /* Job system integration. */
109 const bool *stop;
111 float *progress;
113
116};
117
119{
120 float sock_height = NODE_SOCKSIZE;
121 if (socket.flag & SOCK_MULTI_INPUT) {
122 sock_height += max_ii(NODE_MULTI_INPUT_LINK_GAP * 0.5f * socket.runtime->total_inputs,
124 }
125 return sock_height;
126}
127
129 const int index,
130 const int total_inputs)
131{
132 const float offset = (total_inputs * NODE_MULTI_INPUT_LINK_GAP - NODE_MULTI_INPUT_LINK_GAP) *
133 0.5f;
134 return {socket_position.x, socket_position.y - offset + index * NODE_MULTI_INPUT_LINK_GAP};
135}
136
137static void compo_tag_output_nodes(bNodeTree *nodetree, int recalc_flags)
138{
139 for (bNode *node : nodetree->all_nodes()) {
140 if (node->type_legacy == CMP_NODE_COMPOSITE) {
141 if (recalc_flags & COM_RECALC_COMPOSITE) {
142 node->flag |= NODE_DO_OUTPUT_RECALC;
143 }
144 }
145 else if (node->type_legacy == CMP_NODE_VIEWER) {
146 if (recalc_flags & COM_RECALC_VIEWER) {
147 node->flag |= NODE_DO_OUTPUT_RECALC;
148 }
149 }
150 else if (node->type_legacy == NODE_GROUP) {
151 if (node->id) {
152 compo_tag_output_nodes((bNodeTree *)node->id, recalc_flags);
153 }
154 }
155 }
156}
157
159{
161 int recalc_flags = 0;
162
163 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
164 const bScreen *screen = WM_window_get_active_screen(win);
165
166 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
167 if (area->spacetype == SPACE_IMAGE) {
168 SpaceImage *sima = (SpaceImage *)area->spacedata.first;
169 if (sima->image) {
170 if (sima->image->type == IMA_TYPE_R_RESULT) {
171 recalc_flags |= COM_RECALC_COMPOSITE;
172 }
173 else if (sima->image->type == IMA_TYPE_COMPOSITE) {
174 recalc_flags |= COM_RECALC_VIEWER;
175 }
176 }
177 }
178 else if (area->spacetype == SPACE_NODE) {
179 SpaceNode *snode = (SpaceNode *)area->spacedata.first;
180 if (snode->flag & SNODE_BACKDRAW) {
181 recalc_flags |= COM_RECALC_VIEWER;
182 }
183 }
184 }
185 }
186
187 return recalc_flags;
188}
189
190/* Called by compositor, only to check job 'stop' value. */
191static bool compo_breakjob(void *cjv)
192{
193 CompoJob *cj = (CompoJob *)cjv;
194
195 /* Without G.is_break 'ESC' won't quit - which annoys users. */
196 return (*(cj->stop)
197#ifdef USE_ESC_COMPO
198 || G.is_break
199#endif
200 );
201}
202
203/* Called by compositor, #wmJob sends notifier. */
204static void compo_statsdrawjob(void *cjv, const char * /*str*/)
205{
206 CompoJob *cj = (CompoJob *)cjv;
207
208 *(cj->do_update) = true;
209}
210
211/* Called by compositor, wmJob sends notifier. */
212static void compo_redrawjob(void *cjv)
213{
214 CompoJob *cj = (CompoJob *)cjv;
215
216 *(cj->do_update) = true;
217}
218
219static void compo_freejob(void *cjv)
220{
221 CompoJob *cj = (CompoJob *)cjv;
222
223 if (cj->localtree) {
224 /* Merge back node previews, only for completed jobs. */
225 if (!cj->cancelled) {
227 }
228
230 MEM_freeN(cj->localtree);
231 }
232
233 MEM_delete(cj);
234}
235
236/* Only now we copy the nodetree, so adding many jobs while
237 * sliding buttons doesn't frustrate. */
238static void compo_initjob(void *cjv)
239{
240 CompoJob *cj = (CompoJob *)cjv;
241 Main *bmain = cj->bmain;
242 Scene *scene = cj->scene;
243 ViewLayer *view_layer = cj->view_layer;
244
245 bke::CompositorRuntime &compositor_runtime = scene->runtime->compositor;
246
247 if (!compositor_runtime.preview_depsgraph) {
248 compositor_runtime.preview_depsgraph = DEG_graph_new(
249 bmain, scene, view_layer, DAG_EVAL_RENDER);
250 DEG_debug_name_set(compositor_runtime.preview_depsgraph, "COMPOSITOR");
251 }
252
253 /* Update the viewer layer of the compositor since it changed since the depsgraph was created. */
254 if (DEG_get_input_view_layer(compositor_runtime.preview_depsgraph) != view_layer) {
255 DEG_graph_replace_owners(compositor_runtime.preview_depsgraph, bmain, scene, view_layer);
257 }
258
259 cj->compositor_depsgraph = compositor_runtime.preview_depsgraph;
261
262 /* NOTE: Don't update animation to preserve unkeyed changes, this means can not use
263 * evaluate_on_framechange. */
265
267
268 cj->localtree = bke::node_tree_localize(ntree_eval, nullptr);
269
270 if (cj->recalc_flags) {
272 }
273
277 }
278}
279
280/* Called before redraw notifiers, it moves finished previews over. */
281static void compo_updatejob(void * /*cjv*/)
282{
284}
285
286static void compo_progressjob(void *cjv, float progress)
287{
288 CompoJob *cj = (CompoJob *)cjv;
289
290 *(cj->progress) = progress;
291}
292
293/* Only this runs inside thread. */
294static void compo_startjob(void *cjv, wmJobWorkerStatus *worker_status)
295{
296 CompoJob *cj = (CompoJob *)cjv;
297 bNodeTree *ntree = cj->localtree;
299
300 if (scene->use_nodes == false) {
301 return;
302 }
303
304 cj->stop = &worker_status->stop;
305 cj->do_update = &worker_status->do_update;
306 cj->progress = &worker_status->progress;
307
308 ntree->runtime->test_break = compo_breakjob;
309 ntree->runtime->tbh = cj;
310 ntree->runtime->stats_draw = compo_statsdrawjob;
311 ntree->runtime->sdh = cj;
312 ntree->runtime->progress = compo_progressjob;
313 ntree->runtime->prh = cj;
314 ntree->runtime->update_draw = compo_redrawjob;
315 ntree->runtime->udh = cj;
316
318
319 if ((scene->r.scemode & R_MULTIVIEW) == 0) {
320 COM_execute(cj->re, &scene->r, scene, ntree, "", nullptr, &cj->profiler, cj->needed_outputs);
321 }
322 else {
323 LISTBASE_FOREACH (SceneRenderView *, srv, &scene->r.views) {
324 if (BKE_scene_multiview_is_render_view_active(&scene->r, srv) == false) {
325 continue;
326 }
328 cj->re, &scene->r, scene, ntree, srv->name, nullptr, &cj->profiler, cj->needed_outputs);
329 }
330 }
331
332 ntree->runtime->test_break = nullptr;
333 ntree->runtime->stats_draw = nullptr;
334 ntree->runtime->progress = nullptr;
335}
336
337static void compo_canceljob(void *cjv)
338{
339 CompoJob *cj = (CompoJob *)cjv;
340 Main *bmain = cj->bmain;
341 Scene *scene = cj->scene;
343 cj->cancelled = true;
344
345 scene->runtime->compositor.per_node_execution_time = cj->profiler.get_nodes_evaluation_times();
346}
347
348static void compo_completejob(void *cjv)
349{
350 CompoJob *cj = (CompoJob *)cjv;
351 Main *bmain = cj->bmain;
352 Scene *scene = cj->scene;
354
355 scene->runtime->compositor.per_node_execution_time = cj->profiler.get_nodes_evaluation_times();
356}
357
359
360} // namespace blender::ed::space_node
361
362/* -------------------------------------------------------------------- */
365
366/* Identify if the compositor can run. Currently, this only checks if the compositor is set to GPU
367 * and the render size exceeds what can be allocated as a texture in it. */
369{
370 Scene *scene = CTX_data_scene(C);
371 /* CPU compositor can always run. */
373 return true;
374 }
375
376 int width, height;
377 BKE_render_resolution(&scene->r, false, &width, &height);
378 const int max_texture_size = GPU_max_texture_size();
379
380 /* There is no way to know if the render size is too large except if we actually allocate a test
381 * texture, which we want to avoid due its cost. So we employ a heuristic that so far has worked
382 * with all known GPU drivers. */
383 if (size_t(width) * height > (size_t(max_texture_size) * max_texture_size) / 4) {
384 WM_global_report(RPT_ERROR, "Render size too large for GPU, use CPU compositor instead");
385 return false;
386 }
387
388 return true;
389}
390
391/* Returns the compositor outputs that need to be computed because their result is visible to the
392 * user. */
394{
396
397 wmWindowManager *window_manager = CTX_wm_manager(C);
398 LISTBASE_FOREACH (wmWindow *, window, &window_manager->windows) {
399 bScreen *screen = WM_window_get_active_screen(window);
400 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
401 SpaceLink *space_link = static_cast<SpaceLink *>(area->spacedata.first);
402 if (!space_link || !ELEM(space_link->spacetype, SPACE_NODE, SPACE_IMAGE)) {
403 continue;
404 }
405 if (space_link->spacetype == SPACE_NODE) {
406 const SpaceNode *space_node = reinterpret_cast<const SpaceNode *>(space_link);
407 if (space_node->flag & SNODE_BACKDRAW) {
409 }
410 if (space_node->overlay.flag & SN_OVERLAY_SHOW_PREVIEWS) {
412 }
413 }
414 else if (space_link->spacetype == SPACE_IMAGE) {
415 const SpaceImage *space_image = reinterpret_cast<const SpaceImage *>(space_link);
416 Image *image = ED_space_image(space_image);
417 if (!image || image->source != IMA_SRC_VIEWER) {
418 continue;
419 }
420 if (image->type == IMA_TYPE_R_RESULT) {
422 }
423 else if (image->type == IMA_TYPE_COMPOSITE) {
425 }
426 }
427
428 /* All outputs are already needed, return early. */
429 if (needed_outputs ==
432 {
433 return needed_outputs;
434 }
435 }
436 }
437
438 return needed_outputs;
439}
440
441void ED_node_composite_job(const bContext *C, bNodeTree *nodetree, Scene *scene_owner)
442{
444 if (needed_outputs == blender::compositor::OutputTypes::None) {
445 return;
446 }
447
448 using namespace blender::ed::space_node;
449
450 Main *bmain = CTX_data_main(C);
451 Scene *scene = CTX_data_scene(C);
452 ViewLayer *view_layer = CTX_data_view_layer(C);
453
455 return;
456 }
457
458 /* See #32272. */
459 if (G.is_rendering) {
460 return;
461 }
462
463#ifdef USE_ESC_COMPO
464 G.is_break = false;
465#endif
466
468 scene, BKE_image_ensure_viewer(bmain, IMA_TYPE_R_RESULT, "Render Result"), false);
469
472 scene_owner,
473 "Compositing",
476 CompoJob *cj = MEM_new<CompoJob>("compo job");
477
478 /* Custom data for preview thread. */
479 cj->bmain = bmain;
480 cj->scene = scene;
481 cj->view_layer = view_layer;
482 cj->ntree = nodetree;
484 cj->needed_outputs = needed_outputs;
485
486 /* Set up job. */
493 nullptr,
496
498}
499
501
502namespace blender::ed::space_node {
503
504/* -------------------------------------------------------------------- */
507
509{
511 SpaceNode *snode = CTX_wm_space_node(C);
512 if (ED_node_is_compositor(snode)) {
513 return true;
514 }
515 }
516 return false;
517}
518
520{
522 SpaceNode *snode = CTX_wm_space_node(C);
523 if (ED_node_is_compositor(snode)) {
524 return true;
525 }
526 }
527 return false;
528}
529
531
532} // namespace blender::ed::space_node
533
534/* -------------------------------------------------------------------- */
537
539{
540 if (typeinfo) {
541 STRNCPY(snode->tree_idname, typeinfo->idname.c_str());
542 }
543 else {
544 snode->tree_idname[0] = '\0';
545 }
546}
547
549{
550 return snode->tree_idname == ntreeType_Composite->idname;
551}
552
554{
555 return snode->tree_idname == ntreeType_Shader->idname;
556}
557
559{
560 return snode->tree_idname == ntreeType_Texture->idname;
561}
562
564{
565 return snode->tree_idname == ntreeType_Geometry->idname;
566}
567
569{
570 return ED_node_is_compositor(snode) ||
571 (USER_EXPERIMENTAL_TEST(&U, use_shader_node_previews) && ED_node_is_shader(snode));
572}
573
575{
576 Main *bmain = CTX_data_main(C);
577
578 if (GS(id->name) == ID_MA) {
579 /* Materials */
581 Material *ma = (Material *)id;
582 Material *ma_default;
583
584 if (ob && ob->type == OB_VOLUME) {
585 ma_default = BKE_material_default_volume();
586 }
587 else {
588 ma_default = BKE_material_default_surface();
589 }
590
591 ma->nodetree = blender::bke::node_tree_copy_tree(bmain, *ma_default->nodetree);
592 ma->nodetree->owner_id = &ma->id;
593 for (bNode *node_iter : ma->nodetree->all_nodes()) {
594 STRNCPY_UTF8(node_iter->name, DATA_(node_iter->name));
596 }
597
599 }
600 else if (ELEM(GS(id->name), ID_WO, ID_LA)) {
601 /* Emission */
603 nullptr, id, "Shader Nodetree", ntreeType_Shader->idname);
604 bNode *shader, *output;
605
606 if (GS(id->name) == ID_WO) {
607 World *world = (World *)id;
608
612 *shader,
613 *blender::bke::node_find_socket(*shader, SOCK_OUT, "Background"),
614 *output,
616
617 bNodeSocket *color_sock = blender::bke::node_find_socket(*shader, SOCK_IN, "Color");
618 copy_v3_v3(((bNodeSocketValueRGBA *)color_sock->default_value)->value, &world->horr);
619 }
620 else {
621 shader = blender::bke::node_add_static_node(nullptr, *ntree, SH_NODE_EMISSION);
624 *shader,
625 *blender::bke::node_find_socket(*shader, SOCK_OUT, "Emission"),
626 *output,
628 }
629
630 shader->location[0] = -200.0f;
631 shader->location[1] = 100.0f;
632 output->location[0] = 200.0f;
633 output->location[1] = 100.0f;
636 }
637 else {
638 printf("ED_node_shader_default called on wrong ID type.\n");
639 return;
640 }
641}
642
644{
645 /* but lets check it anyway */
646 if (sce->nodetree) {
647 if (G.debug & G_DEBUG) {
648 printf("error in composite initialize\n");
649 }
650 return;
651 }
652
654 nullptr, &sce->id, "Compositing Node Tree", ntreeType_Composite->idname);
655
657 composite->location[0] = 200.0f;
658 composite->location[1] = 0.0f;
659
661 in->location[0] = -150.0f - in->width;
662 in->location[1] = 0.0f;
664
666 reroute->location[0] = 100.0f;
667 reroute->location[1] = -35.0f;
668
670 viewer->location[0] = 200.0f;
671 viewer->location[1] = -60.0f;
672
673 /* Viewer and Composite nodes are linked to Render Layer's output image socket through a reroute
674 * node. */
676 *in,
677 *(bNodeSocket *)in->outputs.first,
678 *reroute,
679 *(bNodeSocket *)reroute->inputs.first);
680
682 *reroute,
683 *(bNodeSocket *)reroute->outputs.first,
684 *composite,
685 *(bNodeSocket *)composite->inputs.first);
686
688 *reroute,
689 *(bNodeSocket *)reroute->outputs.first,
690 *viewer,
691 *(bNodeSocket *)viewer->inputs.first);
692
694}
695
697{
698 if (tex->nodetree) {
699 if (G.debug & G_DEBUG) {
700 printf("error in texture initialize\n");
701 }
702 return;
703 }
704
706 nullptr, &tex->id, "Texture Nodetree", ntreeType_Texture->idname);
707
709 out->location[0] = 300.0f;
710 out->location[1] = 300.0f;
711
713 in->location[0] = 10.0f;
714 in->location[1] = 300.0f;
716
717 bNodeSocket *fromsock = (bNodeSocket *)in->outputs.first;
718 bNodeSocket *tosock = (bNodeSocket *)out->inputs.first;
719 blender::bke::node_add_link(*tex->nodetree, *in, *fromsock, *out, *tosock);
720
722}
723
724namespace blender::ed::space_node {
725
727{
728 /* NOTE: Here we set the active tree(s), even called for each redraw now, so keep it fast :). */
729
730 SpaceNode *snode = CTX_wm_space_node(&C);
732 bNodeTree *ntree = snode->nodetree;
733 ID *id = snode->id, *from = snode->from;
734
735 /* Check the tree type. */
736 if (!treetype || (treetype->poll && !treetype->poll(&C, treetype))) {
737 /* Invalid tree type, skip.
738 * NOTE: not resetting the node path here, invalid #bNodeTreeType
739 * may still be registered at a later point. */
740 return;
741 }
742
743 if (snode->nodetree && !STREQ(snode->nodetree->idname, snode->tree_idname)) {
744 /* Current tree does not match selected type, clear tree path. */
745 ntree = nullptr;
746 id = nullptr;
747 from = nullptr;
748 }
749
750 if (!(snode->flag & SNODE_PIN) || ntree == nullptr) {
751 if (treetype->get_from_context) {
752 /* Reset and update from context. */
753 ntree = nullptr;
754 id = nullptr;
755 from = nullptr;
756
757 treetype->get_from_context(&C, treetype, &ntree, &id, &from);
758 }
759 }
760
761 if (snode->nodetree != ntree || snode->id != id || snode->from != from ||
762 (snode->treepath.last == nullptr && ntree))
763 {
764 ScrArea *area = CTX_wm_area(&C);
766 ED_node_tree_start(region, snode, ntree, id, from);
767 }
768}
769
770} // namespace blender::ed::space_node
771
773 Main *bmain, SpaceNode *snode, bNodeTree *ntree, bNode *node, bool *r_active_texture_changed)
774{
775 if (r_active_texture_changed) {
776 *r_active_texture_changed = false;
777 }
778
779 blender::bke::node_set_active(*ntree, *node);
780 if (node->type_legacy == NODE_GROUP) {
781 return;
782 }
783
784 const bool was_output = (node->flag & NODE_DO_OUTPUT) != 0;
785 bool do_update = false;
786
787 /* Generic node group output: set node as active output. */
788 if (node->is_group_output()) {
789 for (bNode *node_iter : ntree->all_nodes()) {
790 if (node_iter->is_group_output()) {
791 node_iter->flag &= ~NODE_DO_OUTPUT;
792 }
793 }
794
795 node->flag |= NODE_DO_OUTPUT;
796 if (!was_output) {
797 do_update = true;
799 }
800 }
801
802 /* Tree specific activate calls. */
803 if (ntree->type == NTREE_SHADER) {
804 if (ELEM(node->type_legacy,
809 {
810 for (bNode *node_iter : ntree->all_nodes()) {
811 if (node_iter->type_legacy == node->type_legacy) {
812 node_iter->flag &= ~NODE_DO_OUTPUT;
813 }
814 }
815
816 node->flag |= NODE_DO_OUTPUT;
818 }
819
820 BKE_main_ensure_invariants(*bmain, ntree->id);
821
822 if (node->flag & NODE_ACTIVE_TEXTURE) {
823 /* If active texture changed, free GLSL materials. */
824 LISTBASE_FOREACH (Material *, ma, &bmain->materials) {
825 if (ma->nodetree && ma->use_nodes &&
826 blender::bke::node_tree_contains_tree(*ma->nodetree, *ntree))
827 {
828 GPU_material_free(&ma->gpumaterial);
829
830 /* Sync to active texpaint slot, otherwise we can end up painting on a different slot
831 * than we are looking at. */
832 if (ma->texpaintslot) {
833 if (node->id != nullptr && GS(node->id->name) == ID_IM) {
834 Image *image = (Image *)node->id;
835 for (int i = 0; i < ma->tot_slots; i++) {
836 if (ma->texpaintslot[i].ima == image) {
837 ma->paint_active_slot = i;
838 }
839 }
840 }
841 }
842 }
843 }
844
845 LISTBASE_FOREACH (World *, wo, &bmain->worlds) {
846 if (wo->nodetree && wo->use_nodes &&
847 blender::bke::node_tree_contains_tree(*wo->nodetree, *ntree))
848 {
849 GPU_material_free(&wo->gpumaterial);
850 }
851 }
852
853 /* Sync to Image Editor under the following conditions:
854 * - current image is not pinned
855 * - current image is not a Render Result or ViewerNode (want to keep looking at these) */
856 if (node->id != nullptr && GS(node->id->name) == ID_IM) {
857 Image *image = (Image *)node->id;
858 ED_space_image_sync(bmain, image, true);
859 }
860
861 if (r_active_texture_changed) {
862 *r_active_texture_changed = true;
863 }
864 BKE_main_ensure_invariants(*bmain, ntree->id);
866 }
867
869 }
870 else if (ntree->type == NTREE_COMPOSIT) {
871 /* Make active viewer, currently only one is supported. */
872 if (node->type_legacy == CMP_NODE_VIEWER) {
873 for (bNode *node_iter : ntree->all_nodes()) {
874 if (node_iter->type_legacy == CMP_NODE_VIEWER) {
875 node_iter->flag &= ~NODE_DO_OUTPUT;
876 }
877 }
878
879 node->flag |= NODE_DO_OUTPUT;
880 if (was_output == 0) {
882 BKE_main_ensure_invariants(*bmain, ntree->id);
883 }
884
885 /* Adding a node doesn't link this yet. */
886 node->id = (ID *)BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
887 }
888 else if (node->type_legacy == CMP_NODE_COMPOSITE) {
889 if (was_output == 0) {
890 for (bNode *node_iter : ntree->all_nodes()) {
891 if (node_iter->type_legacy == CMP_NODE_COMPOSITE) {
892 node_iter->flag &= ~NODE_DO_OUTPUT;
893 }
894 }
895
896 node->flag |= NODE_DO_OUTPUT;
898 BKE_main_ensure_invariants(*bmain, ntree->id);
899 }
900 }
901 else if (do_update) {
902 BKE_main_ensure_invariants(*bmain, ntree->id);
903 }
904 }
905 else if (ntree->type == NTREE_GEOMETRY) {
906 if (node->type_legacy == GEO_NODE_VIEWER) {
907 if ((node->flag & NODE_DO_OUTPUT) == 0) {
908 for (bNode *node_iter : ntree->all_nodes()) {
909 if (node_iter->type_legacy == GEO_NODE_VIEWER) {
910 node_iter->flag &= ~NODE_DO_OUTPUT;
911 }
912 }
913 node->flag |= NODE_DO_OUTPUT;
914 }
916 }
917 }
918}
919
921{
922 /* XXX This does not work due to layout functions relying on node->block,
923 * which only exists during actual drawing. Can we rely on valid draw_bounds rects?
924 */
925 /* make sure nodes have correct bounding boxes after transform */
926 // node_update_nodetree(C, ntree, 0.0f, 0.0f);
927}
928
930
931namespace blender::ed::space_node {
932
933/* -------------------------------------------------------------------- */
936
937static bool socket_is_occluded(const float2 &location,
938 const bNode &node_the_socket_belongs_to,
939 const Span<bNode *> sorted_nodes)
940{
941 for (bNode *node : sorted_nodes) {
942 if (node == &node_the_socket_belongs_to) {
943 /* Nodes after this one are underneath and can't occlude the socket. */
944 return false;
945 }
946
947 rctf socket_hitbox;
948 const float socket_hitbox_radius = NODE_SOCKSIZE - 0.1f * U.widget_unit;
949 BLI_rctf_init_pt_radius(&socket_hitbox, location, socket_hitbox_radius);
950 if (BLI_rctf_inside_rctf(&node->runtime->draw_bounds, &socket_hitbox)) {
951 return true;
952 }
953 }
954 return false;
955}
956
958
959/* -------------------------------------------------------------------- */
962
970
972 bContext *C, wmOperator *op, const float2 &cursor, const bNode *node, NodeResizeDirection dir)
973{
974 Scene *scene = CTX_data_scene(C);
976
977 op->customdata = nsw;
978
979 nsw->mxstart = cursor.x;
980 nsw->mystart = cursor.y;
981
982 /* store old */
983 nsw->oldlocx = node->location[0];
984 nsw->oldlocy = node->location[1];
985 nsw->oldwidth = node->width;
986 nsw->oldheight = node->height;
987 nsw->directions = dir;
989
991 /* add modal handler */
993}
994
995static void node_resize_exit(bContext *C, wmOperator *op, bool cancel)
996{
998
1000
1001 /* Restore old data on cancel. */
1002 if (cancel) {
1003 SpaceNode *snode = CTX_wm_space_node(C);
1004 bNode *node = bke::node_get_active(*snode->edittree);
1005
1006 node->location[0] = nsw->oldlocx;
1007 node->location[1] = nsw->oldlocy;
1008 node->width = nsw->oldwidth;
1009 node->height = nsw->oldheight;
1010 }
1011
1012 MEM_freeN(nsw);
1013 op->customdata = nullptr;
1014}
1015
1022
1024{
1025 static const EnumPropertyItem modal_items[] = {
1026 {int(NodeResizeAction::Begin), "BEGIN", 0, "Resize Node", ""},
1027 {int(NodeResizeAction::Cancel), "CANCEL", 0, "Cancel", ""},
1028 {int(NodeResizeAction::SnapInvertOn), "SNAP_INVERT_ON", 0, "Snap Invert", ""},
1029 {int(NodeResizeAction::SnapInvertOff), "SNAP_INVERT_OFF", 0, "Snap Invert", ""},
1030 {0, nullptr, 0, nullptr, nullptr},
1031 };
1032
1033 wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Node Resize Modal Map");
1034
1035 if (keymap && keymap->modal_items) {
1036 return nullptr;
1037 }
1038
1039 keymap = WM_modalkeymap_ensure(keyconf, "Node Resize Modal Map", modal_items);
1040
1041 WM_modalkeymap_assign(keymap, "NODE_OT_resize");
1042
1043 return keymap;
1044}
1045
1046/* Compute the nearest 1D coordinate corresponding to the nearest grid in node editors. */
1047static float nearest_node_grid_coord(float co)
1048{
1049 /* Size and location of nodes are independent of UI scale, so grid size should be independent of
1050 * UI scale as well. */
1051 float grid_size = grid_size_get() / UI_SCALE_FAC;
1052 float rest = fmod(co, grid_size);
1053 float offset = rest - grid_size / 2 >= 0 ? grid_size : 0;
1054
1055 return co - rest + offset;
1056}
1057
1059{
1060 SpaceNode *snode = CTX_wm_space_node(C);
1061 ARegion *region = CTX_wm_region(C);
1062 bNode *node = bke::node_get_active(*snode->edittree);
1064
1065 if (event->type == EVT_MODAL_MAP) {
1066 switch (NodeResizeAction(event->val)) {
1069 }
1071 node_resize_exit(C, op, true);
1072 ED_region_tag_redraw(region);
1073 return OPERATOR_CANCELLED;
1074 }
1077 nsw->snap_to_grid = !nsw->snap_to_grid;
1079 }
1080 }
1081 }
1082
1083 switch (event->type) {
1084 case MOUSEMOVE: {
1085 int2 mval;
1086 WM_event_drag_start_mval(event, region, mval);
1087 float mx, my;
1088 UI_view2d_region_to_view(&region->v2d, mval.x, mval.y, &mx, &my);
1089 const float dx = (mx - nsw->mxstart) / UI_SCALE_FAC;
1090 const float dy = (my - nsw->mystart) / UI_SCALE_FAC;
1091
1092 if (node) {
1093 float *pwidth = &node->width;
1094 float *pheight = &node->height;
1095 float oldwidth = nsw->oldwidth;
1096 float widthmin = node->typeinfo->minwidth;
1097 float widthmax = node->typeinfo->maxwidth;
1098
1099 {
1100 if (nsw->directions & NODE_RESIZE_RIGHT) {
1101 *pwidth = oldwidth + dx;
1102
1103 if (nsw->snap_to_grid) {
1104 *pwidth = nearest_node_grid_coord(*pwidth);
1105 }
1106 CLAMP(*pwidth, widthmin, widthmax);
1107 }
1108 if (nsw->directions & NODE_RESIZE_LEFT) {
1109 float locmax = nsw->oldlocx + oldwidth;
1110 *pwidth = oldwidth - dx;
1111
1112 if (nsw->snap_to_grid) {
1113 *pwidth = nearest_node_grid_coord(*pwidth);
1114 }
1115 CLAMP(*pwidth, widthmin, widthmax);
1116 node->location[0] = locmax - *pwidth;
1117 }
1118 }
1119
1120 /* Height works the other way round. */
1121 {
1122 float heightmin = UI_SCALE_FAC * node->typeinfo->minheight;
1123 float heightmax = UI_SCALE_FAC * node->typeinfo->maxheight;
1124 if (nsw->directions & NODE_RESIZE_TOP) {
1125 float locmin = nsw->oldlocy - nsw->oldheight;
1126 *pheight = nsw->oldheight + dy;
1127
1128 if (nsw->snap_to_grid) {
1129 *pheight = nearest_node_grid_coord(*pheight);
1130 }
1131 CLAMP(*pheight, heightmin, heightmax);
1132 node->location[1] = locmin + *pheight;
1133 }
1134 if (nsw->directions & NODE_RESIZE_BOTTOM) {
1135 *pheight = nsw->oldheight - dy;
1136
1137 if (nsw->snap_to_grid) {
1138 *pheight = nearest_node_grid_coord(*pheight);
1139 }
1140 CLAMP(*pheight, heightmin, heightmax);
1141 }
1142 }
1143 }
1144
1145 ED_region_tag_redraw(region);
1146
1147 break;
1148 }
1149 case LEFTMOUSE:
1150 case MIDDLEMOUSE:
1151 case RIGHTMOUSE: {
1152 if (event->val == KM_RELEASE) {
1153 node_resize_exit(C, op, false);
1155
1156 return OPERATOR_FINISHED;
1157 }
1158 break;
1159 }
1160 default: {
1161 break;
1162 }
1163 }
1164
1166}
1167
1169{
1170 SpaceNode *snode = CTX_wm_space_node(C);
1171 ARegion *region = CTX_wm_region(C);
1172 const bNode *node = bke::node_get_active(*snode->edittree);
1173
1174 if (node == nullptr) {
1176 }
1177
1178 /* Convert mouse coordinates to `v2d` space. */
1179 float2 cursor;
1180 int2 mval;
1181 WM_event_drag_start_mval(event, region, mval);
1182 UI_view2d_region_to_view(&region->v2d, mval.x, mval.y, &cursor.x, &cursor.y);
1183 const NodeResizeDirection dir = node_get_resize_direction(*snode, node, cursor.x, cursor.y);
1184 if (dir == NODE_RESIZE_NONE) {
1186 }
1187
1188 node_resize_init(C, op, cursor, node, dir);
1190}
1191
1193{
1194 node_resize_exit(C, op, true);
1195}
1196
1198{
1199 /* identifiers */
1200 ot->name = "Resize Node";
1201 ot->idname = "NODE_OT_resize";
1202 ot->description = "Resize a node";
1203
1204 /* API callbacks. */
1205 ot->invoke = node_resize_invoke;
1206 ot->modal = node_resize_modal;
1208 ot->cancel = node_resize_cancel;
1209
1210 /* flags */
1211 ot->flag = OPTYPE_BLOCKING;
1212}
1213
1215
1216/* -------------------------------------------------------------------- */
1219
1221{
1222 LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
1223 if (sock->flag & SOCK_HIDDEN) {
1224 return true;
1225 }
1226 }
1227 LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
1228 if (sock->flag & SOCK_HIDDEN) {
1229 return true;
1230 }
1231 }
1232 return false;
1233}
1234
1235void node_set_hidden_sockets(bNode *node, int set)
1236{
1237 /* The Reroute node is the socket itself, do not hide this. */
1238 if (node->is_reroute()) {
1239 return;
1240 }
1241
1242 if (set == 0) {
1243 LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
1244 sock->flag &= ~SOCK_HIDDEN;
1245 }
1246 LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
1247 sock->flag &= ~SOCK_HIDDEN;
1248 }
1249 }
1250 else {
1251 /* Hide unused sockets. */
1252 LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
1253 if (sock->link == nullptr) {
1254 sock->flag |= SOCK_HIDDEN;
1255 }
1256 }
1257 LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
1258 if ((sock->flag & SOCK_IS_LINKED) == 0) {
1259 sock->flag |= SOCK_HIDDEN;
1260 }
1261 }
1262 }
1263}
1264
1265bool node_is_previewable(const SpaceNode &snode, const bNodeTree &ntree, const bNode &node)
1266{
1267 if (!(snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS) ||
1269 {
1270 return false;
1271 }
1272 if (ntree.type == NTREE_SHADER) {
1273 return USER_EXPERIMENTAL_TEST(&U, use_shader_node_previews) && !node.is_frame() &&
1275 }
1276 return node.typeinfo->flag & NODE_PREVIEW;
1277}
1278
1279static bool cursor_isect_multi_input_socket(const float2 &cursor, const bNodeSocket &socket)
1280{
1281 const float node_socket_height = node_socket_calculate_height(socket);
1282 const float2 location = socket.runtime->location;
1283 /* `.xmax = socket->location[0] + NODE_SOCKSIZE * 5.5f`
1284 * would be the same behavior as for regular sockets.
1285 * But keep it smaller because for multi-input socket you
1286 * sometimes want to drag the link to the other side, if you may
1287 * accidentally pick the wrong link otherwise. */
1288 rctf multi_socket_rect;
1289 BLI_rctf_init(&multi_socket_rect,
1290 location.x - NODE_SOCKSIZE * 4.0f,
1291 location.x + NODE_SOCKSIZE * 2.0f,
1292 location.y - node_socket_height,
1293 location.y + node_socket_height);
1294 if (BLI_rctf_isect_pt(&multi_socket_rect, cursor.x, cursor.y)) {
1295 return true;
1296 }
1297 return false;
1298}
1299
1301 ARegion &region,
1302 const float2 &cursor,
1303 const eNodeSocketInOut in_out)
1304{
1305 const float view2d_scale = UI_view2d_scale_get_x(&region.v2d);
1306 const float max_distance = NODE_SOCKSIZE + std::clamp(20.0f / view2d_scale, 5.0f, 30.0f);
1307 const float padded_socket_size = NODE_SOCKSIZE + 4;
1308
1309 bNodeTree &tree = *snode.edittree;
1310 tree.ensure_topology_cache();
1311
1313 if (sorted_nodes.is_empty()) {
1314 return nullptr;
1315 }
1316
1317 float best_distance = FLT_MAX;
1318 bNodeSocket *best_socket = nullptr;
1319
1320 auto update_best_socket = [&](bNodeSocket *socket, const float distance) {
1321 if (socket_is_occluded(socket->runtime->location, socket->owner_node(), sorted_nodes)) {
1322 return;
1323 }
1324 if (distance < best_distance) {
1325 best_distance = distance;
1326 best_socket = socket;
1327 }
1328 };
1329
1330 for (bNode *node : sorted_nodes) {
1331 const bool node_hidden = node->flag & NODE_HIDDEN;
1332 if (!node->is_reroute() && !node_hidden &&
1333 node->runtime->draw_bounds.ymax - cursor.y < NODE_DY)
1334 {
1335 /* Don't pick socket when cursor is over node header. This allows the user to always resize
1336 * by dragging on the left and right side of the header. */
1337 continue;
1338 }
1339 if (in_out & SOCK_IN) {
1340 for (bNodeSocket *sock : node->input_sockets()) {
1341 if (!sock->is_icon_visible()) {
1342 continue;
1343 }
1344 const float2 location = sock->runtime->location;
1345 const float distance = math::distance(location, cursor);
1346 if (sock->flag & SOCK_MULTI_INPUT && !node_hidden) {
1347 if (cursor_isect_multi_input_socket(cursor, *sock)) {
1348 update_best_socket(sock, distance);
1349 continue;
1350 }
1351 }
1352 if (distance < max_distance) {
1353 update_best_socket(sock, distance);
1354 }
1355 }
1356 }
1357 if (in_out & SOCK_OUT) {
1358 for (bNodeSocket *sock : node->output_sockets()) {
1359 if (!sock->is_icon_visible()) {
1360 continue;
1361 }
1362 const float2 location = sock->runtime->location;
1363 const float distance = math::distance(location, cursor);
1364 if (distance < max_distance) {
1365 if (node_hidden) {
1366 if (location.x - cursor.x > padded_socket_size) {
1367 /* Needed to be able to resize collapsed nodes. */
1368 continue;
1369 }
1370 }
1371 update_best_socket(sock, distance);
1372 }
1373 }
1374 }
1375 }
1376
1377 return best_socket;
1378}
1379
1381
1382/* -------------------------------------------------------------------- */
1385
1386float node_link_dim_factor(const View2D &v2d, const bNodeLink &link)
1387{
1388 if (link.fromsock == nullptr || link.tosock == nullptr) {
1389 return 1.0f;
1390 }
1392 return 0.2f;
1393 }
1394
1395 const float2 from = link.fromsock->runtime->location;
1396 const float2 to = link.tosock->runtime->location;
1397
1398 const float min_endpoint_distance = std::min(
1399 std::max(BLI_rctf_length_x(&v2d.cur, from.x), BLI_rctf_length_y(&v2d.cur, from.y)),
1400 std::max(BLI_rctf_length_x(&v2d.cur, to.x), BLI_rctf_length_y(&v2d.cur, to.y)));
1401
1402 if (min_endpoint_distance == 0.0f) {
1403 return 1.0f;
1404 }
1405 const float viewport_width = BLI_rctf_size_x(&v2d.cur);
1406 return std::clamp(1.0f - min_endpoint_distance / viewport_width * 10.0f, 0.05f, 1.0f);
1407}
1408
1410{
1411 return bke::node_link_is_hidden(link) || node_link_dim_factor(v2d, link) < 0.5f;
1412}
1413
1415
1416/* -------------------------------------------------------------------- */
1419
1421 const Map<bNode *, bNode *> &node_map,
1422 bNode *node)
1423{
1424 bNode *parent;
1425
1426 node->flag |= NODE_TEST;
1427
1428 /* Find first selected parent. */
1429 for (parent = node->parent; parent; parent = parent->parent) {
1430 if (parent->flag & SELECT) {
1431 if (!(parent->flag & NODE_TEST)) {
1432 node_duplicate_reparent_recursive(ntree, node_map, parent);
1433 }
1434 break;
1435 }
1436 }
1437 /* Reparent node copy to parent copy. */
1438 if (parent) {
1439 bke::node_detach_node(*ntree, *node_map.lookup(node));
1440 bke::node_attach_node(*ntree, *node_map.lookup(node), *node_map.lookup(parent));
1441 }
1442}
1443
1445{
1446 /* We don't have the old tree for looking up output nodes by ID,
1447 * so we have to build a map first to find copied output nodes in the new tree. */
1448 Map<int32_t, bNode *> dst_output_node_map;
1449 for (const auto &item : node_map.items()) {
1450 if (bke::all_zone_output_node_types().contains(item.key->type_legacy)) {
1451 dst_output_node_map.add_new(item.key->identifier, item.value);
1452 }
1453 }
1454
1455 for (bNode *dst_node : node_map.values()) {
1456 if (bke::all_zone_input_node_types().contains(dst_node->type_legacy)) {
1457 const bke::bNodeZoneType &zone_type = *bke::zone_type_by_node_type(dst_node->type_legacy);
1458 int &output_node_id = zone_type.get_corresponding_output_id(*dst_node);
1459 if (const bNode *output_node = dst_output_node_map.lookup_default(output_node_id, nullptr)) {
1460 output_node_id = output_node->identifier;
1461 }
1462 else {
1463 output_node_id = 0;
1465 }
1466 }
1467 }
1468}
1469
1471{
1472 Main *bmain = CTX_data_main(C);
1473 SpaceNode *snode = CTX_wm_space_node(C);
1474 bNodeTree *ntree = snode->edittree;
1475 const bool keep_inputs = RNA_boolean_get(op->ptr, "keep_inputs");
1476 bool linked = RNA_boolean_get(op->ptr, "linked") || ((U.dupflag & USER_DUP_NTREE) == 0);
1477 const bool dupli_node_tree = !linked;
1478
1480
1481 Map<bNode *, bNode *> node_map;
1483 Map<const ID *, ID *> duplicated_node_groups;
1484
1485 node_select_paired(*ntree);
1486
1487 for (bNode *node : get_selected_nodes(*ntree)) {
1489 ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
1490 node_map.add_new(node, new_node);
1491
1492 if (node->id && dupli_node_tree) {
1493 ID *new_group = duplicated_node_groups.lookup_or_add_cb(node->id, [&]() {
1494 ID *new_group = BKE_id_copy(bmain, node->id);
1495 /* Remove user added by copying. */
1496 id_us_min(new_group);
1497 return new_group;
1498 });
1499 id_us_plus(new_group);
1500 id_us_min(new_node->id);
1501 new_node->id = new_group;
1502 }
1503 }
1504
1505 if (node_map.is_empty()) {
1506 return OPERATOR_CANCELLED;
1507 }
1508
1509 /* Copy links between selected nodes. */
1510 bNodeLink *lastlink = (bNodeLink *)ntree->links.last;
1511 LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
1512 /* This creates new links between copied nodes. If keep_inputs is set, also copies input links
1513 * from unselected (when fromnode is null)! */
1514 if (link->tonode && (link->tonode->flag & NODE_SELECT) &&
1515 (keep_inputs || (link->fromnode && (link->fromnode->flag & NODE_SELECT))))
1516 {
1517 bNodeLink *newlink = MEM_callocN<bNodeLink>("bNodeLink");
1518 newlink->flag = link->flag;
1519 newlink->tonode = node_map.lookup(link->tonode);
1520 newlink->tosock = socket_map.lookup(link->tosock);
1521
1522 if (link->tosock->flag & SOCK_MULTI_INPUT) {
1523 newlink->multi_input_sort_id = link->multi_input_sort_id;
1524 }
1525
1526 if (link->fromnode && (link->fromnode->flag & NODE_SELECT)) {
1527 newlink->fromnode = node_map.lookup(link->fromnode);
1528 newlink->fromsock = socket_map.lookup(link->fromsock);
1529 }
1530 else {
1531 /* Input node not copied, this keeps the original input linked. */
1532 newlink->fromnode = link->fromnode;
1533 newlink->fromsock = link->fromsock;
1534 }
1535
1536 BLI_addtail(&ntree->links, newlink);
1537 }
1538
1539 /* Make sure we don't copy new links again. */
1540 if (link == lastlink) {
1541 break;
1542 }
1543 }
1544
1545 for (bNode *node : node_map.values()) {
1547 }
1548
1549 ntree->ensure_topology_cache();
1550 for (bNode *node : node_map.values()) {
1552 }
1553
1554 /* Clear flags for recursive depth-first iteration. */
1555 for (bNode *node : ntree->all_nodes()) {
1556 node->flag &= ~NODE_TEST;
1557 }
1558 /* Reparent copied nodes. */
1559 for (bNode *node : node_map.keys()) {
1560 if (!(node->flag & NODE_TEST)) {
1561 node_duplicate_reparent_recursive(ntree, node_map, node);
1562 }
1563 }
1564
1565 {
1566 /* Use temporary map that has const key, because that's what the function below expects. */
1567 Map<const bNode *, bNode *> const_node_map;
1568 for (const auto item : node_map.items()) {
1569 const_node_map.add(item.key, item.value);
1570 }
1571 remap_node_pairing(*ntree, const_node_map);
1572 }
1573
1574 /* Deselect old nodes, select the copies instead. */
1575 for (const auto item : node_map.items()) {
1576 bNode *src_node = item.key;
1577 bNode *dst_node = item.value;
1578
1579 bke::node_set_selected(*src_node, false);
1580 src_node->flag &= ~(NODE_ACTIVE | NODE_ACTIVE_TEXTURE);
1581 bke::node_set_selected(*dst_node, true);
1582 }
1583
1585 BKE_main_ensure_invariants(*bmain, snode->edittree->id);
1586 return OPERATOR_FINISHED;
1587}
1588
1590{
1591 PropertyRNA *prop;
1592
1593 /* identifiers */
1594 ot->name = "Duplicate Nodes";
1595 ot->description = "Duplicate selected nodes";
1596 ot->idname = "NODE_OT_duplicate";
1597
1598 /* API callbacks. */
1599 ot->exec = node_duplicate_exec;
1601
1602 /* flags */
1603 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1604
1606 ot->srna, "keep_inputs", false, "Keep Inputs", "Keep the input links to duplicated nodes");
1607
1608 prop = RNA_def_boolean(ot->srna,
1609 "linked",
1610 true,
1611 "Linked",
1612 "Duplicate node but not node trees, linking to the original data");
1614}
1615
1616/* Goes over all scenes, reads render layers. */
1618{
1619 Main *bmain = CTX_data_main(C);
1620 SpaceNode *snode = CTX_wm_space_node(C);
1621 Scene *curscene = CTX_data_scene(C);
1622 bNodeTree &edit_tree = *snode->edittree;
1623
1625
1626 /* first tag scenes unread */
1627 LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
1628 scene->id.tag |= ID_TAG_DOIT;
1629 }
1630
1631 for (bNode *node : edit_tree.all_nodes()) {
1632 if ((node->type_legacy == CMP_NODE_R_LAYERS) ||
1633 (node->type_legacy == CMP_NODE_CRYPTOMATTE &&
1634 node->custom1 == CMP_NODE_CRYPTOMATTE_SOURCE_RENDER))
1635 {
1636 ID *id = node->id;
1637 if (id == nullptr) {
1638 continue;
1639 }
1640 if (id->tag & ID_TAG_DOIT) {
1641 RE_ReadRenderResult(curscene, (Scene *)id);
1643 id->tag &= ~ID_TAG_DOIT;
1644 }
1645 }
1646 }
1647
1648 BKE_main_ensure_invariants(*bmain, edit_tree.id);
1649
1650 return OPERATOR_FINISHED;
1651}
1652
1654{
1655 ot->name = "Read View Layers";
1656 ot->idname = "NODE_OT_read_viewlayers";
1657 ot->description = "Read all render layers of all used scenes";
1658
1660
1661 ot->poll = composite_node_active;
1662}
1663
1665{
1666 Scene *sce = CTX_data_scene(C);
1667
1668 /* This is actually a test whether scene is used by the compositor or not.
1669 * All the nodes are using same render result, so there is no need to do
1670 * anything smart about check how exactly scene is used. */
1671 bNode *node = nullptr;
1672 for (bNode *node_iter : sce->nodetree->all_nodes()) {
1673 if (node_iter->id == (ID *)sce) {
1674 node = node_iter;
1675 break;
1676 }
1677 }
1678
1679 if (node) {
1680 ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&sce->view_layers, node->custom1);
1681
1682 if (view_layer) {
1683 PointerRNA op_ptr;
1684
1685 WM_operator_properties_create(&op_ptr, "RENDER_OT_render");
1686 RNA_string_set(&op_ptr, "layer", view_layer->name);
1687 RNA_string_set(&op_ptr, "scene", sce->id.name + 2);
1688
1689 /* To keep keyframe positions. */
1690 sce->r.scemode |= R_NO_FRAME_UPDATE;
1691
1692 WM_operator_name_call(C, "RENDER_OT_render", WM_OP_INVOKE_DEFAULT, &op_ptr, nullptr);
1693
1695
1696 return OPERATOR_FINISHED;
1697 }
1698 }
1699 return OPERATOR_CANCELLED;
1700}
1701
1703{
1704 ot->name = "Render Changed Layer";
1705 ot->idname = "NODE_OT_render_changed";
1706 ot->description = "Render current scene, when input node's layer has been changed";
1707
1709
1710 ot->poll = composite_node_active;
1711
1712 /* flags */
1713 ot->flag = 0;
1714}
1715
1717
1718/* -------------------------------------------------------------------- */
1721
1727static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag, const bool tag_update = false)
1728{
1729 int tot_eq = 0, tot_neq = 0;
1730
1731 for (bNode *node : snode->edittree->all_nodes()) {
1732 if (node->flag & SELECT) {
1733
1734 if (toggle_flag == NODE_PREVIEW && !node_is_previewable(*snode, *snode->edittree, *node)) {
1735 continue;
1736 }
1737 if (toggle_flag == NODE_OPTIONS &&
1738 !(node->typeinfo->draw_buttons || node->typeinfo->draw_buttons_ex))
1739 {
1740 continue;
1741 }
1742
1743 if (node->flag & toggle_flag) {
1744 tot_eq++;
1745 }
1746 else {
1747 tot_neq++;
1748 }
1749 }
1750 }
1751 for (bNode *node : snode->edittree->all_nodes()) {
1752 if (node->flag & SELECT) {
1753
1754 if (toggle_flag == NODE_PREVIEW && !node_is_previewable(*snode, *snode->edittree, *node)) {
1755 continue;
1756 }
1757 if (toggle_flag == NODE_OPTIONS &&
1758 !(node->typeinfo->draw_buttons || node->typeinfo->draw_buttons_ex))
1759 {
1760 continue;
1761 }
1762
1763 if ((tot_eq && tot_neq) || tot_eq == 0) {
1764 node->flag |= toggle_flag;
1765 }
1766 else {
1767 node->flag &= ~toggle_flag;
1768 }
1769
1770 if (tag_update) {
1772 }
1773 }
1774 }
1775}
1776
1778{
1779 SpaceNode *snode = CTX_wm_space_node(C);
1780
1781 /* Sanity checking (poll callback checks this already). */
1782 if ((snode == nullptr) || (snode->edittree == nullptr)) {
1783 return OPERATOR_CANCELLED;
1784 }
1785
1787
1789
1790 return OPERATOR_FINISHED;
1791}
1792
1794{
1795 /* identifiers */
1796 ot->name = "Hide";
1797 ot->description = "Toggle hiding of selected nodes";
1798 ot->idname = "NODE_OT_hide_toggle";
1799
1800 /* callbacks */
1801 ot->exec = node_hide_toggle_exec;
1803
1804 /* flags */
1805 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1806}
1807
1809{
1810 SpaceNode *snode = CTX_wm_space_node(C);
1811
1812 /* Sanity checking (poll callback checks this already). */
1813 if ((snode == nullptr) || (snode->edittree == nullptr)) {
1814 return OPERATOR_CANCELLED;
1815 }
1816
1817 node_flag_toggle_exec(snode, NODE_PREVIEW, true);
1818
1821
1823
1824 return OPERATOR_FINISHED;
1825}
1826
1828{
1830 SpaceNode *snode = CTX_wm_space_node(C);
1831 if (ED_node_supports_preview(snode)) {
1832 return true;
1833 }
1834 }
1835 return false;
1836}
1837
1839{
1840 /* identifiers */
1841 ot->name = "Toggle Node Preview";
1842 ot->description = "Toggle preview display for selected nodes";
1843 ot->idname = "NODE_OT_preview_toggle";
1844
1845 /* callbacks */
1847 ot->poll = node_previewable;
1848
1849 /* flags */
1850 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1851}
1852
1854{
1855 SpaceNode *snode = CTX_wm_space_node(C);
1857 Main *bmain = CTX_data_main(C);
1858 bNodeTree *ntree = nullptr;
1859 bNode *node = nullptr;
1860
1861 if (ptr.data) {
1862 node = static_cast<bNode *>(ptr.data);
1863 ntree = reinterpret_cast<bNodeTree *>(ptr.owner_id);
1864 }
1865 else if (snode && snode->edittree) {
1866 ntree = snode->edittree;
1867 node = bke::node_get_active(*ntree);
1868 }
1869
1870 if (!node) {
1871 return OPERATOR_CANCELLED;
1872 }
1873
1874 if (node->is_type("CompositorNodeViewer")) {
1875 for (bNode *other_node : ntree->all_nodes()) {
1876 if (other_node->type_legacy == node->type_legacy) {
1877 other_node->flag &= ~NODE_DO_OUTPUT;
1878 }
1879 node->flag |= NODE_DO_OUTPUT;
1880
1883 }
1884 }
1885 else if (node->is_type("GeometryNodeViewer")) {
1886 /* Geometry nodes viewers don't rely on NODE_DO_OUTPUT flag alone. */
1887 viewer_path::activate_geometry_node(*bmain, *snode, *node);
1888 }
1889 else {
1890 return OPERATOR_CANCELLED;
1891 }
1892
1894 BKE_main_ensure_invariants(*bmain, snode->edittree->id);
1895 return OPERATOR_FINISHED;
1896}
1897
1899{
1900 ot->name = "Activate Viewer Node";
1901 ot->description = "Activate selected viewer node in compositor and geometry nodes";
1902 ot->idname = "NODE_OT_activate_viewer";
1903
1906
1907 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1908}
1909
1911{
1912 SpaceNode &snode = *CTX_wm_space_node(C);
1913 WorkSpace &workspace = *CTX_wm_workspace(C);
1914
1915 bNode *active_viewer = viewer_path::find_geometry_nodes_viewer(workspace.viewer_path, snode);
1916
1917 for (bNode *node : snode.edittree->all_nodes()) {
1918 if (node->type_legacy != GEO_NODE_VIEWER) {
1919 continue;
1920 }
1921 if (!(node->flag & SELECT)) {
1922 continue;
1923 }
1924 if (node == active_viewer) {
1925 node->flag &= ~NODE_DO_OUTPUT;
1927 }
1928 }
1929
1931
1932 return OPERATOR_FINISHED;
1933}
1934
1936{
1937 /* identifiers */
1938 ot->name = "Deactivate Viewer Node";
1939 ot->description = "Deactivate selected viewer node in geometry nodes";
1940 ot->idname = __func__;
1941
1942 /* callbacks */
1945
1946 /* flags */
1947 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1948}
1949
1951{
1952 SpaceNode *snode = CTX_wm_space_node(C);
1953
1954 /* Sanity checking (poll callback checks this already). */
1955 if ((snode == nullptr) || (snode->edittree == nullptr)) {
1956 return OPERATOR_CANCELLED;
1957 }
1958
1960
1962
1963 return OPERATOR_FINISHED;
1964}
1965
1967{
1968 /* identifiers */
1969 ot->name = "Toggle Node Options";
1970 ot->description = "Toggle option buttons display for selected nodes";
1971 ot->idname = "NODE_OT_options_toggle";
1972
1973 /* callbacks */
1976
1977 /* flags */
1978 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1979}
1980
1982{
1983 SpaceNode *snode = CTX_wm_space_node(C);
1984
1985 /* Sanity checking (poll callback checks this already). */
1986 if ((snode == nullptr) || (snode->edittree == nullptr)) {
1987 return OPERATOR_CANCELLED;
1988 }
1989
1991
1992 /* Toggle for all selected nodes */
1993 bool hidden = false;
1994 for (bNode *node : snode->edittree->all_nodes()) {
1995 if (node->flag & SELECT) {
1996 if (node_has_hidden_sockets(node)) {
1997 hidden = true;
1998 break;
1999 }
2000 }
2001 }
2002
2003 for (bNode *node : snode->edittree->all_nodes()) {
2004 if (node->flag & SELECT) {
2005 node_set_hidden_sockets(node, !hidden);
2006 }
2007 }
2008
2010
2012 /* Hack to force update of the button state after drawing, see #112462. */
2014
2015 return OPERATOR_FINISHED;
2016}
2017
2019{
2020 /* identifiers */
2021 ot->name = "Toggle Hidden Node Sockets";
2022 ot->description = "Toggle unused node socket display";
2023 ot->idname = "NODE_OT_hide_socket_toggle";
2024
2025 /* callbacks */
2028
2029 /* flags */
2030 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2031}
2032
2034
2035/* -------------------------------------------------------------------- */
2038
2040{
2041 Main *bmain = CTX_data_main(C);
2042 SpaceNode *snode = CTX_wm_space_node(C);
2043
2045
2046 for (bNode *node : snode->edittree->all_nodes()) {
2047 if ((node->flag & SELECT) && !node->typeinfo->no_muting) {
2048 node->flag ^= NODE_MUTED;
2050 }
2051 }
2052
2053 BKE_main_ensure_invariants(*bmain, snode->edittree->id);
2054
2055 return OPERATOR_FINISHED;
2056}
2057
2059{
2060 /* identifiers */
2061 ot->name = "Toggle Node Mute";
2062 ot->description = "Toggle muting of selected nodes";
2063 ot->idname = "NODE_OT_mute_toggle";
2064
2065 /* callbacks */
2066 ot->exec = node_mute_exec;
2068
2069 /* flags */
2070 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2071}
2072
2074
2075/* -------------------------------------------------------------------- */
2078
2080{
2081 Main *bmain = CTX_data_main(C);
2082 SpaceNode *snode = CTX_wm_space_node(C);
2083
2085
2086 /* Delete paired nodes as well. */
2088
2089 LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) {
2090 if (node->flag & SELECT) {
2091 bke::node_remove_node(bmain, *snode->edittree, *node, true);
2092 }
2093 }
2094
2096 BKE_main_ensure_invariants(*bmain, snode->edittree->id);
2097
2098 return OPERATOR_FINISHED;
2099}
2100
2102{
2103 /* identifiers */
2104 ot->name = "Delete";
2105 ot->description = "Remove selected nodes";
2106 ot->idname = "NODE_OT_delete";
2107
2108 /* API callbacks. */
2109 ot->exec = node_delete_exec;
2111
2112 /* flags */
2113 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2114}
2115
2117
2118/* -------------------------------------------------------------------- */
2121
2123{
2124 Main *bmain = CTX_data_main(C);
2125 SpaceNode *snode = CTX_wm_space_node(C);
2126
2128
2129 /* Delete paired nodes as well. */
2131
2132 LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) {
2133 if (node->flag & SELECT) {
2135 bke::node_remove_node(bmain, *snode->edittree, *node, true);
2136
2137 /* Since this node might have been animated, and that animation data been
2138 * deleted, a notifier call is necessary to redraw any animation editor. */
2140 }
2141 }
2142
2143 BKE_main_ensure_invariants(*bmain, snode->edittree->id);
2144
2145 return OPERATOR_FINISHED;
2146}
2147
2149{
2150 /* identifiers */
2151 ot->name = "Delete with Reconnect";
2152 ot->description = "Remove nodes and reconnect nodes as if deletion was muted";
2153 ot->idname = "NODE_OT_delete_reconnect";
2154
2155 /* API callbacks. */
2158
2159 /* flags */
2160 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2161}
2162
2164
2165/* -------------------------------------------------------------------- */
2168
2170{
2171 Scene *scene = CTX_data_scene(C);
2172 SpaceNode *snode = CTX_wm_space_node(C);
2174 bNodeTree *ntree = nullptr;
2175 bNode *node = nullptr;
2176 char file_path[MAX_NAME];
2177
2178 if (ptr.data) {
2179 node = (bNode *)ptr.data;
2180 ntree = (bNodeTree *)ptr.owner_id;
2181 }
2182 else if (snode && snode->edittree) {
2183 ntree = snode->edittree;
2184 node = bke::node_get_active(*snode->edittree);
2185 }
2186
2187 if (!node || node->type_legacy != CMP_NODE_OUTPUT_FILE) {
2188 return OPERATOR_CANCELLED;
2189 }
2190
2191 RNA_string_get(op->ptr, "file_path", file_path);
2192
2193 if (strlen(file_path) != 0) {
2194 ntreeCompositOutputFileAddSocket(ntree, node, file_path, &scene->r.im_format);
2195 }
2196 else {
2197 ntreeCompositOutputFileAddSocket(ntree, node, DATA_("Image"), &scene->r.im_format);
2198 }
2199
2201
2202 return OPERATOR_FINISHED;
2203}
2204
2206{
2207 /* identifiers */
2208 ot->name = "Add File Node Socket";
2209 ot->description = "Add a new input to a file output node";
2210 ot->idname = "NODE_OT_output_file_add_socket";
2211
2212 /* callbacks */
2215
2216 /* flags */
2217 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2218
2220 ot->srna, "file_path", nullptr, MAX_NAME, "File Path", "Subpath of the output file");
2221}
2222
2224
2225/* -------------------------------------------------------------------- */
2228
2230 wmOperator * /*op*/)
2231{
2232 SpaceNode *snode = CTX_wm_space_node(C);
2234 bNodeTree *ntree = nullptr;
2235 bNode *node = nullptr;
2236
2237 if (ptr.data) {
2238 node = (bNode *)ptr.data;
2239 ntree = (bNodeTree *)ptr.owner_id;
2240 }
2241 else if (snode && snode->edittree) {
2242 ntree = snode->edittree;
2243 node = bke::node_get_active(*snode->edittree);
2244 }
2245
2246 if (!node || node->type_legacy != CMP_NODE_OUTPUT_FILE) {
2247 return OPERATOR_CANCELLED;
2248 }
2249
2251 return OPERATOR_CANCELLED;
2252 }
2253
2255
2256 return OPERATOR_FINISHED;
2257}
2258
2260{
2261 /* identifiers */
2262 ot->name = "Remove File Node Socket";
2263 ot->description = "Remove the active input from a file output node";
2264 ot->idname = "NODE_OT_output_file_remove_active_socket";
2265
2266 /* callbacks */
2269
2270 /* flags */
2271 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2272}
2273
2275
2276/* -------------------------------------------------------------------- */
2279
2281{
2282 SpaceNode *snode = CTX_wm_space_node(C);
2284 bNode *node = nullptr;
2285
2286 if (ptr.data) {
2287 node = (bNode *)ptr.data;
2288 }
2289 else if (snode && snode->edittree) {
2290 node = bke::node_get_active(*snode->edittree);
2291 }
2292
2293 if (!node || node->type_legacy != CMP_NODE_OUTPUT_FILE) {
2294 return OPERATOR_CANCELLED;
2295 }
2296
2298
2299 bNodeSocket *sock = (bNodeSocket *)BLI_findlink(&node->inputs, nimf->active_input);
2300 if (!sock) {
2301 return OPERATOR_CANCELLED;
2302 }
2303
2304 int direction = RNA_enum_get(op->ptr, "direction");
2305
2306 if (direction == 1) {
2307 bNodeSocket *before = sock->prev;
2308 if (!before) {
2309 return OPERATOR_CANCELLED;
2310 }
2311 BLI_remlink(&node->inputs, sock);
2312 BLI_insertlinkbefore(&node->inputs, before, sock);
2313 nimf->active_input--;
2314 }
2315 else {
2316 bNodeSocket *after = sock->next;
2317 if (!after) {
2318 return OPERATOR_CANCELLED;
2319 }
2320 BLI_remlink(&node->inputs, sock);
2321 BLI_insertlinkafter(&node->inputs, after, sock);
2322 nimf->active_input++;
2323 }
2324
2327
2328 return OPERATOR_FINISHED;
2329}
2330
2332{
2333 static const EnumPropertyItem direction_items[] = {
2334 {1, "UP", 0, "Up", ""}, {2, "DOWN", 0, "Down", ""}, {0, nullptr, 0, nullptr, nullptr}};
2335
2336 /* identifiers */
2337 ot->name = "Move File Node Socket";
2338 ot->description = "Move the active input of a file output node up or down the list";
2339 ot->idname = "NODE_OT_output_file_move_active_socket";
2340
2341 /* callbacks */
2344
2345 /* flags */
2346 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2347
2348 RNA_def_enum(ot->srna, "direction", direction_items, 2, "Direction", "");
2349}
2350
2352
2353/* -------------------------------------------------------------------- */
2356
2358{
2359 SpaceNode &snode = *CTX_wm_space_node(C);
2360 bNodeTree &ntree = *snode.edittree;
2361
2362 bNode *active_node = bke::node_get_active(ntree);
2363 if (!active_node) {
2364 return OPERATOR_CANCELLED;
2365 }
2366
2367 for (bNode *node : ntree.all_nodes()) {
2368 if (node->flag & NODE_SELECT && node != active_node) {
2369 if (active_node->flag & NODE_CUSTOM_COLOR) {
2370 node->flag |= NODE_CUSTOM_COLOR;
2371 copy_v3_v3(node->color, active_node->color);
2372 }
2373 else {
2374 node->flag &= ~NODE_CUSTOM_COLOR;
2375 }
2376 }
2377 }
2378
2380
2381 return OPERATOR_FINISHED;
2382}
2383
2385{
2386 /* identifiers */
2387 ot->name = "Copy Color";
2388 ot->description = "Copy color to all selected nodes";
2389 ot->idname = "NODE_OT_node_copy_color";
2390
2391 /* API callbacks. */
2392 ot->exec = node_copy_color_exec;
2394
2395 /* flags */
2396 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2397}
2398
2400
2401/* -------------------------------------------------------------------- */
2404
2406{
2408 SpaceNode *snode = CTX_wm_space_node(C);
2409
2410 /* Test if we have a render engine that supports shaders scripts. */
2411 if (!(type && type->update_script_node)) {
2412 return false;
2413 }
2414
2415 /* See if we have a shader script node in context. */
2416 bNode *node = (bNode *)CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript).data;
2417
2418 if (!node && snode && snode->edittree) {
2419 node = bke::node_get_active(*snode->edittree);
2420 }
2421
2422 if (node && node->type_legacy == SH_NODE_SCRIPT) {
2424
2425 if (node->id || nss->filepath[0]) {
2427 }
2428 }
2429
2430 return false;
2431}
2432
2434{
2436 SpaceNode *snode = CTX_wm_space_node(C);
2437 PointerRNA nodeptr = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript);
2438
2439 /* setup render engine */
2440 RenderEngine *engine = RE_engine_create(type);
2441 engine->reports = op->reports;
2442
2443 bNodeTree *ntree_base = nullptr;
2444 bNode *node = nullptr;
2445 if (nodeptr.data) {
2446 ntree_base = (bNodeTree *)nodeptr.owner_id;
2447 node = (bNode *)nodeptr.data;
2448 }
2449 else if (snode && snode->edittree) {
2450 ntree_base = snode->edittree;
2451 node = bke::node_get_active(*snode->edittree);
2452 }
2453
2454 /* Update node. */
2455 type->update_script_node(engine, ntree_base, node);
2456
2457 RE_engine_free(engine);
2458
2459 return OPERATOR_FINISHED;
2460}
2461
2463{
2464 /* identifiers */
2465 ot->name = "Script Node Update";
2466 ot->description = "Update shader script node with new sockets and options from the script";
2467 ot->idname = "NODE_OT_shader_script_update";
2468
2469 /* API callbacks. */
2472
2473 /* flags */
2474 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2475}
2476
2478
2479/* -------------------------------------------------------------------- */
2482
2484 ARegion *region,
2485 int x,
2486 int y,
2487 int backdrop_width,
2488 int backdrop_height,
2489 float *fx,
2490 float *fy)
2491{
2492 float bufx = backdrop_width * snode->zoom;
2493 float bufy = backdrop_height * snode->zoom;
2494
2495 *fx = (bufx > 0.0f ? (float(x) - 0.5f * region->winx - snode->xof) / bufx + 0.5f : 0.0f);
2496 *fy = (bufy > 0.0f ? (float(y) - 0.5f * region->winy - snode->yof) / bufy + 0.5f : 0.0f);
2497}
2498
2500{
2501 Main *bmain = CTX_data_main(C);
2502 void *lock;
2503
2505
2506 Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
2507 ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
2508
2509 if (ibuf) {
2510 ARegion *region = CTX_wm_region(C);
2511 SpaceNode *snode = CTX_wm_space_node(C);
2512 bNodeTree *btree = snode->nodetree;
2513 rcti rect;
2514 rctf rectf;
2515
2516 /* Get border from operator. */
2518
2519 /* Convert border to unified space within backdrop image. */
2521 snode, region, rect.xmin, rect.ymin, ibuf->x, ibuf->y, &rectf.xmin, &rectf.ymin);
2522
2524 snode, region, rect.xmax, rect.ymax, ibuf->x, ibuf->y, &rectf.xmax, &rectf.ymax);
2525
2526 /* Clamp coordinates. */
2527 rectf.xmin = max_ff(rectf.xmin, 0.0f);
2528 rectf.ymin = max_ff(rectf.ymin, 0.0f);
2529 rectf.xmax = min_ff(rectf.xmax, 1.0f);
2530 rectf.ymax = min_ff(rectf.ymax, 1.0f);
2531
2532 if (rectf.xmin < rectf.xmax && rectf.ymin < rectf.ymax) {
2533 btree->viewer_border = rectf;
2534
2535 if (rectf.xmin == 0.0f && rectf.ymin == 0.0f && rectf.xmax == 1.0f && rectf.ymax == 1.0f) {
2536 btree->flag &= ~NTREE_VIEWER_BORDER;
2537 }
2538 else {
2539 btree->flag |= NTREE_VIEWER_BORDER;
2540 }
2541
2542 BKE_main_ensure_invariants(*bmain, btree->id);
2544 }
2545 else {
2546 btree->flag &= ~NTREE_VIEWER_BORDER;
2547 }
2548 }
2549
2550 BKE_image_release_ibuf(ima, ibuf, lock);
2551
2552 return OPERATOR_FINISHED;
2553}
2554
2556{
2557 /* identifiers */
2558 ot->name = "Viewer Region";
2559 ot->description = "Set the boundaries for viewer operations";
2560 ot->idname = "NODE_OT_viewer_border";
2561
2562 /* API callbacks. */
2563 ot->invoke = WM_gesture_box_invoke;
2564 ot->exec = viewer_border_exec;
2565 ot->modal = WM_gesture_box_modal;
2566 ot->cancel = WM_gesture_box_cancel;
2567 ot->poll = composite_node_active;
2568
2569 /* flags */
2570 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2571
2572 /* properties */
2574}
2575
2577{
2578 SpaceNode *snode = CTX_wm_space_node(C);
2579 bNodeTree *btree = snode->nodetree;
2580
2581 btree->flag &= ~NTREE_VIEWER_BORDER;
2584
2585 return OPERATOR_FINISHED;
2586}
2587
2589{
2590 /* identifiers */
2591 ot->name = "Clear Viewer Region";
2592 ot->description = "Clear the boundaries for viewer operations";
2593 ot->idname = "NODE_OT_clear_viewer_border";
2594
2595 /* API callbacks. */
2597 ot->poll = composite_node_active;
2598
2599 /* flags */
2600 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2601}
2602
2604
2605/* -------------------------------------------------------------------- */
2608
2610{
2611 SpaceNode *snode = CTX_wm_space_node(C);
2613 bNodeTree *ntree = nullptr;
2614 bNode *node = nullptr;
2615
2616 if (ptr.data) {
2617 node = (bNode *)ptr.data;
2618 ntree = (bNodeTree *)ptr.owner_id;
2619 }
2620 else if (snode && snode->edittree) {
2621 ntree = snode->edittree;
2622 node = bke::node_get_active(*snode->edittree);
2623 }
2624
2625 if (!node || node->type_legacy != CMP_NODE_CRYPTOMATTE_LEGACY) {
2626 return OPERATOR_CANCELLED;
2627 }
2628
2630
2632
2633 return OPERATOR_FINISHED;
2634}
2635
2637{
2638 /* identifiers */
2639 ot->name = "Add Cryptomatte Socket";
2640 ot->description = "Add a new input layer to a Cryptomatte node";
2641 ot->idname = "NODE_OT_cryptomatte_layer_add";
2642
2643 /* callbacks */
2646
2647 /* flags */
2648 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2649}
2650
2652
2653/* -------------------------------------------------------------------- */
2656
2658{
2659 SpaceNode *snode = CTX_wm_space_node(C);
2661 bNodeTree *ntree = nullptr;
2662 bNode *node = nullptr;
2663
2664 if (ptr.data) {
2665 node = (bNode *)ptr.data;
2666 ntree = (bNodeTree *)ptr.owner_id;
2667 }
2668 else if (snode && snode->edittree) {
2669 ntree = snode->edittree;
2670 node = bke::node_get_active(*snode->edittree);
2671 }
2672
2673 if (!node || node->type_legacy != CMP_NODE_CRYPTOMATTE_LEGACY) {
2674 return OPERATOR_CANCELLED;
2675 }
2676
2677 if (!ntreeCompositCryptomatteRemoveSocket(ntree, node)) {
2678 return OPERATOR_CANCELLED;
2679 }
2680
2682
2683 return OPERATOR_FINISHED;
2684}
2685
2687{
2688 /* identifiers */
2689 ot->name = "Remove Cryptomatte Socket";
2690 ot->description = "Remove layer from a Cryptomatte node";
2691 ot->idname = "NODE_OT_cryptomatte_layer_remove";
2692
2693 /* callbacks */
2696
2697 /* flags */
2698 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2699}
2700
2702
2703} // namespace blender::ed::space_node
void BKE_callback_exec_id(Main *bmain, ID *id, eCbEvent evt)
Definition callbacks.cc:43
@ BKE_CB_EVT_COMPOSITE_PRE
@ BKE_CB_EVT_COMPOSITE_CANCEL
@ BKE_CB_EVT_COMPOSITE_POST
PointerRNA CTX_data_pointer_get(const bContext *C, const char *member)
WorkSpace * CTX_wm_workspace(const bContext *C)
PointerRNA CTX_data_pointer_get_type(const bContext *C, const char *member, StructRNA *type)
SpaceNode * CTX_wm_space_node(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
RenderEngineType * CTX_data_engine_type(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
@ G_DEBUG
ImBuf * BKE_image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
Image * BKE_image_ensure_viewer(Main *bmain, int type, const char *name)
void BKE_image_release_ibuf(Image *ima, ImBuf *ibuf, void *lock)
void BKE_image_backup_render(Scene *scene, Image *ima, bool free_current_slot)
void id_us_plus(ID *id)
Definition lib_id.cc:353
@ LIB_ID_COPY_DEFAULT
void id_us_min(ID *id)
Definition lib_id.cc:361
void BKE_main_ensure_invariants(Main &bmain, std::optional< blender::Span< ID * > > modified_ids=std::nullopt)
General operations, lookup, etc. for materials.
Material * BKE_material_default_surface()
Material * BKE_material_default_volume()
#define NODE_REROUTE
Definition BKE_node.hh:798
#define NODE_GROUP
Definition BKE_node.hh:796
#define CMP_NODE_COMPOSITE
#define SH_NODE_OUTPUT_WORLD
#define CMP_NODE_VIEWER
#define SH_NODE_EMISSION
#define CMP_NODE_CRYPTOMATTE
#define GEO_NODE_VIEWER
#define TEX_NODE_CHECKER
#define CMP_NODE_CRYPTOMATTE_LEGACY
#define SH_NODE_OUTPUT_MATERIAL
#define CMP_NODE_OUTPUT_FILE
#define SH_NODE_BACKGROUND
#define TEX_NODE_OUTPUT
#define CMP_NODE_R_LAYERS
#define SH_NODE_OUTPUT_LIGHT
#define SH_NODE_OUTPUT_LINESTYLE
#define SH_NODE_SCRIPT
void BKE_ntree_update_tag_active_output_changed(bNodeTree *ntree)
void BKE_ntree_update_after_single_tree_change(Main &bmain, bNodeTree &modified_tree, const NodeTreeUpdateExtraParams &params={})
void BKE_ntree_update_tag_node_mute(bNodeTree *ntree, bNode *node)
void BKE_ntree_update_tag_node_property(bNodeTree *ntree, bNode *node)
void BKE_render_resolution(const RenderData *r, const bool use_crop, int *r_width, int *r_height)
Definition scene.cc:2927
bool BKE_scene_multiview_is_render_view_active(const RenderData *rd, const SceneRenderView *srv)
Definition scene.cc:3000
ARegion * BKE_area_find_region_type(const ScrArea *area, int region_type)
Definition screen.cc:840
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_insertlinkafter(ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:332
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void BLI_insertlinkbefore(ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:371
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
MINLINE int max_ii(int a, int b)
MINLINE void copy_v3_v3(float r[3], const float a[3])
float BLI_rctf_length_x(const rctf *rect, float x)
Definition rct.cc:171
void BLI_rctf_init(struct rctf *rect, float xmin, float xmax, float ymin, float ymax)
Definition rct.cc:404
bool BLI_rctf_isect_pt(const struct rctf *rect, float x, float y)
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
bool BLI_rctf_inside_rctf(const rctf *rct_a, const rctf *rct_b)
Definition rct.cc:193
void BLI_rctf_init_pt_radius(struct rctf *rect, const float xy[2], float size)
Definition rct.cc:458
float BLI_rctf_length_y(const rctf *rect, float y)
Definition rct.cc:182
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
#define STRNCPY_UTF8(dst, src)
#define CLAMP(a, b, c)
#define ELEM(...)
#define STREQ(a, b)
#define DATA_(msgid)
void COM_execute(Render *render, RenderData *render_data, Scene *scene, bNodeTree *node_tree, const char *view_name, blender::compositor::RenderContext *render_context, blender::compositor::Profiler *profiler, blender::compositor::OutputTypes needed_outputs)
The main method that is used to execute the compositor tree. It can be executed during editing (blenk...
@ DAG_EVAL_RENDER
Depsgraph * DEG_graph_new(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode)
Definition depsgraph.cc:278
void DEG_evaluate_on_refresh(Depsgraph *graph, DepsgraphEvaluateSyncWriteback sync_writeback=DEG_EVALUATE_SYNC_WRITEBACK_NO)
void DEG_graph_replace_owners(Depsgraph *depsgraph, Main *bmain, Scene *scene, ViewLayer *view_layer)
Definition depsgraph.cc:285
void DEG_graph_build_for_compositor_preview(Depsgraph *graph, bNodeTree *nodetree)
void DEG_graph_tag_relations_update(Depsgraph *graph)
void DEG_debug_name_set(Depsgraph *depsgraph, const char *name)
Scene * DEG_get_evaluated_scene(const Depsgraph *graph)
ViewLayer * DEG_get_input_view_layer(const Depsgraph *graph)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_TAG_DOIT
Definition DNA_ID.h:944
@ ID_IM
@ ID_LA
@ ID_WO
@ ID_MA
@ IMA_SRC_VIEWER
@ IMA_TYPE_R_RESULT
@ IMA_TYPE_COMPOSITE
@ NODE_TEST
@ NODE_OPTIONS
@ NODE_DO_OUTPUT
@ NODE_HIDDEN
@ NODE_ACTIVE
@ NODE_ACTIVE_TEXTURE
@ NODE_DO_OUTPUT_RECALC
@ NODE_CUSTOM_COLOR
@ NODE_MUTED
@ NODE_SELECT
@ NODE_PREVIEW
@ NTREE_SHADER
@ NTREE_GEOMETRY
@ NTREE_COMPOSIT
@ NTREE_VIEWER_BORDER
@ NODE_LINK_INSERT_TARGET_INVALID
@ CMP_NODE_CRYPTOMATTE_SOURCE_RENDER
eNodeSocketInOut
@ SOCK_OUT
@ SOCK_IN
@ SOCK_IS_LINKED
@ SOCK_MULTI_INPUT
@ SOCK_HIDDEN
@ OB_VOLUME
@ R_MULTIVIEW
@ R_NO_FRAME_UPDATE
@ SCE_COMPOSITOR_DEVICE_GPU
@ RGN_TYPE_WINDOW
@ SN_OVERLAY_SHOW_PREVIEWS
@ SN_OVERLAY_SHOW_OVERLAYS
@ SNODE_PIN
@ SNODE_BACKDRAW
@ SPACE_NODE
@ SPACE_IMAGE
@ SNODE_SHADER_OBJECT
#define UI_SCALE_FAC
@ USER_DUP_NTREE
#define USER_EXPERIMENTAL_TEST(userdef, member)
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
void ED_space_image_sync(Main *bmain, Image *image, bool ignore_render_viewer)
Definition image_edit.cc:70
Image * ED_space_image(const SpaceImage *sima)
Definition image_edit.cc:40
void ED_node_tree_start(ARegion *region, SpaceNode *snode, bNodeTree *ntree, ID *id, ID *from)
Definition space_node.cc:76
bool ED_node_is_compositor(const SpaceNode *snode)
Definition node_edit.cc:548
void ED_node_set_active_viewer_key(SpaceNode *snode)
void ED_node_post_apply_transform(bContext *C, bNodeTree *ntree)
Definition node_edit.cc:920
bool ED_node_supports_preview(SpaceNode *snode)
Definition node_edit.cc:568
void ED_preview_kill_jobs(wmWindowManager *wm, Main *bmain)
bool ED_operator_node_editable(bContext *C)
bool ED_operator_node_active(bContext *C)
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
int GPU_max_texture_size()
void GPU_material_free(ListBase *gpumaterial)
Read Guarded memory(de)allocation.
struct blender::bke::bNodeTreeType * ntreeType_Shader
struct blender::bke::bNodeTreeType * ntreeType_Texture
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
#define C
Definition RandGen.cpp:29
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:1667
float UI_view2d_scale_get_x(const View2D *v2d)
Definition view2d.cc:1920
@ WM_JOB_TYPE_COMPOSITE
Definition WM_api.hh:1727
@ WM_JOB_EXCL_RENDER
Definition WM_api.hh:1715
@ WM_JOB_PROGRESS
Definition WM_api.hh:1716
#define NC_NODE
Definition WM_types.hh:391
@ OPTYPE_BLOCKING
Definition WM_types.hh:184
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NC_ANIMATION
Definition WM_types.hh:385
#define ND_DISPLAY
Definition WM_types.hh:488
#define ND_COMPO_RESULT
Definition WM_types.hh:444
#define NC_SCENE
Definition WM_types.hh:375
#define ND_NODES
Definition WM_types.hh:433
#define NA_EDITED
Definition WM_types.hh:581
bool do_update
Definition WM_types.hh:1008
#define NC_MATERIAL
Definition WM_types.hh:377
#define NC_IMAGE
Definition WM_types.hh:381
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:238
float progress
Definition WM_types.hh:1019
#define ND_ANIMCHAN
Definition WM_types.hh:493
@ KM_RELEASE
Definition WM_types.hh:309
volatile int lock
#define U
bool is_empty() const
Definition BLI_array.hh:253
ValueIterator values() const &
Definition BLI_map.hh:884
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
Value & lookup_or_add_cb(const Key &key, const CreateValueF &create_value)
Definition BLI_map.hh:620
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:265
KeyIterator keys() const &
Definition BLI_map.hh:875
bool is_empty() const
Definition BLI_map.hh:986
ItemIterator items() const &
Definition BLI_map.hh:902
virtual const int & get_corresponding_output_id(const bNode &input_bnode) const =0
Map< bNodeInstanceKey, timeit::Nanoseconds > & get_nodes_evaluation_times()
Definition profiler.cc:17
#define SELECT
KDTree_3d * tree
#define in
#define out
#define printf(...)
#define output
float distance(VecOp< float, D >, VecOp< float, D >) RET
#define MAX_NAME
#define GS(a)
RenderEngine * RE_engine_create(RenderEngineType *type)
void RE_engine_free(RenderEngine *engine)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 fmod(const float2 a, const float b)
#define G(x, y, z)
const bNodeZoneType * zone_type_by_node_type(const int node_type)
void node_attach_node(bNodeTree &ntree, bNode &node, bNode &parent)
Definition node.cc:4255
bNodeTreeType * node_tree_type_find(StringRef idname)
Definition node.cc:2635
void node_tree_free_tree(bNodeTree &ntree)
Definition node.cc:4718
bNodeTree * node_tree_add_tree_embedded(Main *bmain, ID *owner_id, StringRefNull name, StringRefNull idname)
Definition node.cc:4375
bNodeTree * node_tree_copy_tree(Main *bmain, const bNodeTree &ntree)
Definition node.cc:4391
bNodeSocket * node_find_socket(bNode &node, eNodeSocketInOut in_out, StringRef identifier)
Definition node.cc:2864
void node_remove_node(Main *bmain, bNodeTree &ntree, bNode &node, bool do_id_user, bool remove_animation=true)
Definition node.cc:4649
bNode * node_get_active(bNodeTree &ntree)
Definition node.cc:4957
bNode * node_copy_with_mapping(bNodeTree *dst_tree, const bNode &node_src, int flag, bool use_unique, Map< const bNodeSocket *, bNodeSocket * > &new_socket_map)
Definition node.cc:3857
Span< int > all_zone_output_node_types()
void node_tree_local_merge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree)
Definition node.cc:4901
bNodeTree * node_tree_localize(bNodeTree *ntree, std::optional< ID * > new_owner_id)
Definition node.cc:4858
void node_internal_relink(bNodeTree &ntree, bNode &node)
Definition node.cc:4185
bool node_set_selected(bNode &node, bool select)
Definition node.cc:4967
bool node_tree_contains_tree(const bNodeTree &tree_to_search_in, const bNodeTree &tree_to_search_for)
Definition node.cc:4935
void node_detach_node(bNodeTree &ntree, bNode &node)
Definition node.cc:4263
bNode * node_add_static_node(const bContext *C, bNodeTree &ntree, int type)
Definition node.cc:3804
bNodeLink & node_add_link(bNodeTree &ntree, bNode &fromnode, bNodeSocket &fromsock, bNode &tonode, bNodeSocket &tosock)
Definition node.cc:4087
Span< int > all_zone_input_node_types()
bool node_declaration_ensure(bNodeTree &ntree, bNode &node)
Definition node.cc:5090
void node_set_active(bNodeTree &ntree, bNode &node)
Definition node.cc:4996
void node_unique_name(bNodeTree &ntree, bNode &node)
Definition node.cc:3764
bool node_link_is_hidden(const bNodeLink &link)
Definition node.cc:4159
static wmOperatorStatus node_output_file_move_active_socket_exec(bContext *C, wmOperator *op)
void NODE_OT_delete(wmOperatorType *ot)
void NODE_OT_cryptomatte_layer_add(wmOperatorType *ot)
static wmOperatorStatus node_output_file_add_socket_exec(bContext *C, wmOperator *op)
float node_socket_calculate_height(const bNodeSocket &socket)
Definition node_edit.cc:118
void NODE_OT_read_viewlayers(wmOperatorType *ot)
wmKeyMap * node_resize_modal_keymap(wmKeyConfig *keyconf)
int node_get_resize_cursor(NodeResizeDirection directions)
static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag, const bool tag_update=false)
void tree_draw_order_update(bNodeTree &ntree)
Definition node_draw.cc:321
static bool cursor_isect_multi_input_socket(const float2 &cursor, const bNodeSocket &socket)
static bool node_shader_script_update_poll(bContext *C)
static void node_resize_exit(bContext *C, wmOperator *op, bool cancel)
Definition node_edit.cc:995
static void node_resize_cancel(bContext *C, wmOperator *op)
static wmOperatorStatus node_delete_reconnect_exec(bContext *C, wmOperator *)
static wmOperatorStatus node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *)
static void compo_redrawjob(void *cjv)
Definition node_edit.cc:212
static void compo_statsdrawjob(void *cjv, const char *)
Definition node_edit.cc:204
void NODE_OT_node_copy_color(wmOperatorType *ot)
bool composite_node_editable(bContext *C)
Definition node_edit.cc:519
void NODE_OT_viewer_border(wmOperatorType *ot)
void node_select_paired(bNodeTree &node_tree)
void NODE_OT_activate_viewer(wmOperatorType *ot)
static wmOperatorStatus node_activate_viewer_exec(bContext *C, wmOperator *)
bNodeSocket * node_find_indicated_socket(SpaceNode &snode, ARegion &region, const float2 &cursor, const eNodeSocketInOut in_out)
Array< bNode * > tree_draw_order_calc_nodes_reversed(bNodeTree &ntree)
Definition node_draw.cc:345
void NODE_OT_deactivate_viewer(wmOperatorType *ot)
float2 node_link_calculate_multi_input_position(const float2 &socket_position, const int index, const int total_inputs)
Definition node_edit.cc:128
void NODE_OT_resize(wmOperatorType *ot)
void NODE_OT_render_changed(wmOperatorType *ot)
static wmOperatorStatus node_cryptomatte_add_socket_exec(bContext *C, wmOperator *)
static void compo_updatejob(void *)
Definition node_edit.cc:281
static wmOperatorStatus node_read_viewlayers_exec(bContext *C, wmOperator *)
static void node_duplicate_reparent_recursive(bNodeTree *ntree, const Map< bNode *, bNode * > &node_map, bNode *node)
static void compo_freejob(void *cjv)
Definition node_edit.cc:219
static bool node_previewable(bContext *C)
void NODE_OT_delete_reconnect(wmOperatorType *ot)
void NODE_OT_shader_script_update(wmOperatorType *ot)
void NODE_OT_hide_socket_toggle(wmOperatorType *ot)
static float nearest_node_grid_coord(float co)
void node_set_hidden_sockets(bNode *node, int set)
bool node_is_previewable(const SpaceNode &snode, const bNodeTree &ntree, const bNode &node)
void remap_node_pairing(bNodeTree &dst_tree, const Map< const bNode *, bNode * > &node_map)
static wmOperatorStatus node_hide_toggle_exec(bContext *C, wmOperator *)
float node_link_dim_factor(const View2D &v2d, const bNodeLink &link)
static wmOperatorStatus node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event)
static void compo_canceljob(void *cjv)
Definition node_edit.cc:337
void NODE_OT_output_file_remove_active_socket(wmOperatorType *ot)
static wmOperatorStatus clear_viewer_border_exec(bContext *C, wmOperator *)
static wmOperatorStatus node_copy_color_exec(bContext *C, wmOperator *)
static void viewer_border_corner_to_backdrop(SpaceNode *snode, ARegion *region, int x, int y, int backdrop_width, int backdrop_height, float *fx, float *fy)
static wmOperatorStatus node_shader_script_update_exec(bContext *C, wmOperator *op)
static wmOperatorStatus node_options_toggle_exec(bContext *C, wmOperator *)
bool node_link_is_hidden_or_dimmed(const View2D &v2d, const bNodeLink &link)
static void compo_progressjob(void *cjv, float progress)
Definition node_edit.cc:286
VectorSet< bNode * > get_selected_nodes(bNodeTree &node_tree)
static void compo_startjob(void *cjv, wmJobWorkerStatus *worker_status)
Definition node_edit.cc:294
static int compo_get_recalc_flags(const bContext *C)
Definition node_edit.cc:158
static wmOperatorStatus node_socket_toggle_exec(bContext *C, wmOperator *)
void NODE_OT_preview_toggle(wmOperatorType *ot)
void NODE_OT_clear_viewer_border(wmOperatorType *ot)
static void compo_tag_output_nodes(bNodeTree *nodetree, int recalc_flags)
Definition node_edit.cc:137
static bool compo_breakjob(void *cjv)
Definition node_edit.cc:191
void snode_set_context(const bContext &C)
Definition node_edit.cc:726
void NODE_OT_duplicate(wmOperatorType *ot)
NodeResizeDirection node_get_resize_direction(const SpaceNode &snode, const bNode *node, const int x, const int y)
Definition drawnode.cc:199
bool node_has_hidden_sockets(bNode *node)
void NODE_OT_output_file_add_socket(wmOperatorType *ot)
void NODE_OT_options_toggle(wmOperatorType *ot)
static wmOperatorStatus viewer_border_exec(bContext *C, wmOperator *op)
static wmOperatorStatus node_duplicate_exec(bContext *C, wmOperator *op)
static wmOperatorStatus node_deactivate_viewer_exec(bContext *C, wmOperator *)
bool composite_node_active(bContext *C)
Definition node_edit.cc:508
static bool socket_is_occluded(const float2 &location, const bNode &node_the_socket_belongs_to, const Span< bNode * > sorted_nodes)
Definition node_edit.cc:937
static void node_resize_init(bContext *C, wmOperator *op, const float2 &cursor, const bNode *node, NodeResizeDirection dir)
Definition node_edit.cc:971
static void compo_initjob(void *cjv)
Definition node_edit.cc:238
static void compo_completejob(void *cjv)
Definition node_edit.cc:348
wmOperatorStatus node_render_changed_exec(bContext *C, wmOperator *)
static wmOperatorStatus node_output_file_remove_active_socket_exec(bContext *C, wmOperator *)
void NODE_OT_hide_toggle(wmOperatorType *ot)
static wmOperatorStatus node_resize_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void update_multi_input_indices_for_removed_links(bNode &node)
static wmOperatorStatus node_delete_exec(bContext *C, wmOperator *)
void NODE_OT_cryptomatte_layer_remove(wmOperatorType *ot)
static wmOperatorStatus node_preview_toggle_exec(bContext *C, wmOperator *)
static wmOperatorStatus node_mute_exec(bContext *C, wmOperator *)
void NODE_OT_output_file_move_active_socket(wmOperatorType *ot)
void NODE_OT_mute_toggle(wmOperatorType *ot)
void activate_geometry_node(Main &bmain, SpaceNode &snode, bNode &node)
bNode * find_geometry_nodes_viewer(const ViewerPath &viewer_path, SpaceNode &snode)
T distance(const T &a, const T &b)
void update_node_declaration_and_sockets(bNodeTree &ntree, bNode &node)
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node)
bNodeSocket * ntreeCompositCryptomatteAddSocket(bNodeTree *ntree, bNode *node)
int ntreeCompositOutputFileRemoveActiveSocket(bNodeTree *ntree, bNode *node)
bNodeSocket * ntreeCompositOutputFileAddSocket(bNodeTree *ntree, bNode *node, const char *name, const ImageFormatData *im_format)
void ntreeCompositTagRender(Scene *scene)
blender::bke::bNodeTreeType * ntreeType_Composite
void ED_node_texture_default(const bContext *C, Tex *tex)
Definition node_edit.cc:696
void ED_node_set_tree_type(SpaceNode *snode, blender::bke::bNodeTreeType *typeinfo)
Definition node_edit.cc:538
void ED_node_composit_default(const bContext *C, Scene *sce)
Definition node_edit.cc:643
bool ED_node_is_compositor(const SpaceNode *snode)
Definition node_edit.cc:548
void ED_node_set_active(Main *bmain, SpaceNode *snode, bNodeTree *ntree, bNode *node, bool *r_active_texture_changed)
Definition node_edit.cc:772
static bool is_compositing_possible(const bContext *C)
Definition node_edit.cc:368
bool ED_node_is_shader(SpaceNode *snode)
Definition node_edit.cc:553
#define USE_ESC_COMPO
Definition node_edit.cc:85
static blender::compositor::OutputTypes get_compositor_needed_outputs(const bContext *C)
Definition node_edit.cc:393
void ED_node_composite_job(const bContext *C, bNodeTree *nodetree, Scene *scene_owner)
Definition node_edit.cc:441
bool ED_node_is_geometry(SpaceNode *snode)
Definition node_edit.cc:563
bool ED_node_supports_preview(SpaceNode *snode)
Definition node_edit.cc:568
void ED_node_shader_default(const bContext *C, ID *id)
Definition node_edit.cc:574
bool ED_node_is_texture(SpaceNode *snode)
Definition node_edit.cc:558
void ED_node_post_apply_transform(bContext *, bNodeTree *)
Definition node_edit.cc:920
blender::bke::bNodeTreeType * ntreeType_Geometry
#define NODE_MULTI_INPUT_LINK_GAP
#define NODE_SOCKSIZE
#define NODE_DY
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
void RNA_string_get(PointerRNA *ptr, const char *name, char *value)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
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_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)
Render * RE_NewInteractiveCompositorRender(const Scene *scene)
void RE_system_gpu_context_ensure(Render *re)
bool RE_ReadRenderResult(Scene *scene, Scene *scenode)
#define FLT_MAX
Definition stdcycles.h:14
Definition DNA_ID.h:404
int tag
Definition DNA_ID.h:424
char name[66]
Definition DNA_ID.h:415
short source
void * last
void * first
ListBase scenes
Definition BKE_main.hh:245
ListBase materials
Definition BKE_main.hh:251
ListBase worlds
Definition BKE_main.hh:260
struct bNodeTree * nodetree
ID * owner_id
Definition RNA_types.hh:51
void * data
Definition RNA_types.hh:53
struct ImageFormatData im_format
void(* update_script_node)(struct RenderEngine *engine, struct bNodeTree *ntree, struct bNode *node)
Definition RE_engine.h:108
struct ReportList * reports
Definition RE_engine.h:144
struct bNodeTree * nodetree
struct ToolSettings * toolsettings
SceneRuntimeHandle * runtime
struct RenderData r
ListBase view_layers
struct Image * image
char tree_idname[64]
struct ID * from
ListBase treepath
struct bNodeTree * edittree
struct ID * id
SpaceNodeOverlay overlay
struct bNodeTree * nodetree
struct bNodeTree * nodetree
char name[64]
ViewerPath viewer_path
float horr
bNodeSocketRuntimeHandle * runtime
struct bNodeSocket * next
struct bNodeSocket * prev
void * default_value
bNodeTreeRuntimeHandle * runtime
char idname[64]
ListBase nodes
ListBase links
float location[2]
bNodeTypeHandle * typeinfo
int16_t custom1
float width
ListBase inputs
float height
struct ID * id
float color[3]
struct bNode * parent
int16_t type_legacy
void * storage
ListBase outputs
ListBase areabase
bool(* poll)(const bContext *C, bNodeTreeType *ntreetype)
Definition BKE_node.hh:495
void(* get_from_context)(const bContext *C, bNodeTreeType *ntreetype, bNodeTree **r_ntree, ID **r_id, ID **r_from)
Definition BKE_node.hh:497
compositor::OutputTypes needed_outputs
Definition node_edit.cc:115
compositor::Profiler profiler
Definition node_edit.cc:114
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
wmEventType type
Definition WM_types.hh:754
short val
Definition WM_types.hh:756
const void * modal_items
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void WM_cursor_modal_set(wmWindow *win, int val)
void WM_cursor_modal_restore(wmWindow *win)
void WM_event_drag_start_mval(const wmEvent *event, const ARegion *region, int r_mval[2])
void WM_global_report(eReportType type, const char *message)
void WM_main_add_notifier(uint type, void *reference)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
wmOperatorStatus WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
void WM_event_add_mousemove(wmWindow *win)
@ RIGHTMOUSE
@ EVT_MODAL_MAP
@ MOUSEMOVE
@ LEFTMOUSE
@ MIDDLEMOUSE
PointerRNA * ptr
Definition wm_files.cc:4226
wmOperatorType * ot
Definition wm_files.cc:4225
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:353
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:456
wmJob * WM_jobs_get(wmWindowManager *wm, wmWindow *win, const void *owner, const char *name, const eWM_JobFlag flag, const eWM_JobType job_type)
Definition wm_jobs.cc:190
void WM_jobs_callbacks_ex(wmJob *wm_job, wm_jobs_start_callback startjob, void(*initjob)(void *), void(*update)(void *), void(*endjob)(void *), void(*completed)(void *), void(*canceled)(void *))
Definition wm_jobs.cc:374
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:337
wmKeyMap * WM_modalkeymap_ensure(wmKeyConfig *keyconf, const char *idname, const EnumPropertyItem *items)
Definition wm_keymap.cc:929
void WM_modalkeymap_assign(wmKeyMap *km, const char *opname)
wmKeyMap * WM_modalkeymap_find(wmKeyConfig *keyconf, const char *idname)
Definition wm_keymap.cc:956
void WM_operator_properties_border_to_rcti(wmOperator *op, rcti *r_rect)
void WM_operator_properties_gesture_box(wmOperatorType *ot)
void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
void WM_operator_properties_free(PointerRNA *ptr)
bScreen * WM_window_get_active_screen(const wmWindow *win)