Blender V4.3
space_node.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include "BLI_listbase.h"
12#include "BLI_string.h"
13
14#include "DNA_ID.h"
16#include "DNA_image_types.h"
17#include "DNA_material_types.h"
18#include "DNA_modifier_types.h"
19#include "DNA_node_types.h"
20#include "DNA_object_types.h"
21#include "DNA_screen_types.h"
22#include "DNA_space_types.h"
24
25#include "MEM_guardedalloc.h"
26
27#include "BKE_asset.hh"
29#include "BKE_context.hh"
30#include "BKE_gpencil_legacy.h"
31#include "BKE_idprop.hh"
32#include "BKE_lib_id.hh"
33#include "BKE_lib_query.hh"
34#include "BKE_lib_remap.hh"
35#include "BKE_node_runtime.hh"
37#include "BKE_screen.hh"
38
39#include "ED_image.hh"
40#include "ED_node.hh"
41#include "ED_node_preview.hh"
42#include "ED_screen.hh"
43#include "ED_space_api.hh"
44
45#include "UI_resources.hh"
46#include "UI_view2d.hh"
47
48#include "DEG_depsgraph.hh"
49
50#include "BLO_read_write.hh"
51
52#include "RNA_access.hh"
53#include "RNA_define.hh"
54#include "RNA_enum_types.hh"
55#include "RNA_prototypes.hh"
56
57#include "WM_api.hh"
58#include "WM_types.hh"
59
60#include "node_intern.hh" /* own include */
61
62using blender::float2;
63
64/* ******************** tree path ********************* */
65
66void ED_node_tree_start(SpaceNode *snode, bNodeTree *ntree, ID *id, ID *from)
67{
69 MEM_freeN(path);
70 }
72
73 if (ntree) {
74 bNodeTreePath *path = MEM_cnew<bNodeTreePath>("node tree path");
75 path->nodetree = ntree;
77
78 /* copy initial offset from bNodeTree */
79 copy_v2_v2(path->view_center, ntree->view_center);
80
81 if (id) {
82 STRNCPY(path->display_name, id->name + 2);
83 }
84
85 BLI_addtail(&snode->treepath, path);
86
87 if (ntree->type != NTREE_GEOMETRY) {
88 /* This can probably be removed for all node tree types. It mainly exists because it was not
89 * possible to store id references in custom properties. Also see #36024. I don't want to
90 * remove it for all tree types in bcon3 though. */
91 id_us_ensure_real(&ntree->id);
92 }
93 }
94
95 /* update current tree */
96 snode->nodetree = snode->edittree = ntree;
97 snode->id = id;
98 snode->from = from;
99
101
103}
104
105void ED_node_tree_push(SpaceNode *snode, bNodeTree *ntree, bNode *gnode)
106{
107 bNodeTreePath *path = MEM_cnew<bNodeTreePath>("node tree path");
108 bNodeTreePath *prev_path = (bNodeTreePath *)snode->treepath.last;
109 path->nodetree = ntree;
110 if (gnode) {
111 if (prev_path) {
113 prev_path->parent_key, prev_path->nodetree, gnode);
114 }
115 else {
117 }
118
119 STRNCPY(path->node_name, gnode->name);
120 STRNCPY(path->display_name, gnode->name);
121 }
122 else {
124 }
125
126 /* copy initial offset from bNodeTree */
127 copy_v2_v2(path->view_center, ntree->view_center);
128
129 BLI_addtail(&snode->treepath, path);
130
131 id_us_ensure_real(&ntree->id);
132
133 /* update current tree */
134 snode->edittree = ntree;
135
137
139}
140
142{
143 bNodeTreePath *path = (bNodeTreePath *)snode->treepath.last;
144
145 /* don't remove root */
146 if (path == snode->treepath.first) {
147 return;
148 }
149
150 BLI_remlink(&snode->treepath, path);
151 MEM_freeN(path);
152
153 /* update current tree */
154 path = (bNodeTreePath *)snode->treepath.last;
155 snode->edittree = path->nodetree;
156
158
159 /* listener updates the View2D center from edittree */
161}
162
164{
165 return BLI_listbase_count(&snode->treepath);
166}
167
169{
170 bNodeTreePath *path;
171 int i;
172 for (path = (bNodeTreePath *)snode->treepath.last, i = 0; path; path = path->prev, i++) {
173 if (i == level) {
174 return path->nodetree;
175 }
176 }
177 return nullptr;
178}
179
181{
182 int length = 0;
183 int i = 0;
184 LISTBASE_FOREACH_INDEX (bNodeTreePath *, path, &snode->treepath, i) {
185 length += strlen(path->display_name);
186 if (i > 0) {
187 length += 1; /* for separator char */
188 }
189 }
190 return length;
191}
192
193void ED_node_tree_path_get(SpaceNode *snode, char *value)
194{
195 int i = 0;
196#ifndef NDEBUG
197 const char *value_orig = value;
198#endif
199 /* Note that the caller ensures there is enough space available. */
200 LISTBASE_FOREACH_INDEX (bNodeTreePath *, path, &snode->treepath, i) {
201 const int len = strlen(path->display_name);
202 if (i != 0) {
203 *value++ = '/';
204 }
205 memcpy(value, path->display_name, len);
206 value += len;
207 }
208 *value = '\0';
209 BLI_assert(ptrdiff_t(ED_node_tree_path_length(snode)) == ptrdiff_t(value - value_orig));
210}
211
213{
214 bNodeTreePath *path = (bNodeTreePath *)snode->treepath.last;
215 if (snode->nodetree && path) {
216 /* A change in active viewer may result in the change of the output node used by the
217 * compositor, so we need to get notified about such changes. */
218 if (snode->nodetree->active_viewer_key.value != path->parent_key.value &&
219 snode->nodetree->type == NTREE_COMPOSIT)
220 {
223 }
224
225 snode->nodetree->active_viewer_key = path->parent_key;
226 }
227}
228
229void ED_node_cursor_location_get(const SpaceNode *snode, float value[2])
230{
231 copy_v2_v2(value, snode->runtime->cursor);
232}
233
234void ED_node_cursor_location_set(SpaceNode *snode, const float value[2])
235{
236 copy_v2_v2(snode->runtime->cursor, value);
237}
238
239namespace blender::ed::space_node {
240
242{
243 const bNodeTreePath *path = (bNodeTreePath *)snode.treepath.last;
244
245 if (path && path->prev) {
246 return float2(path->view_center) - float2(path->prev->view_center);
247 }
248 return float2(0);
249}
250
251static const bNode *group_node_by_name(const bNodeTree &ntree, StringRef name)
252{
253 for (const bNode *node : ntree.group_nodes()) {
254 if (node->name == name) {
255 return node;
256 }
257 }
258 return nullptr;
259}
260
261std::optional<int32_t> find_nested_node_id_in_root(const SpaceNode &snode, const bNode &query_node)
262{
263 BLI_assert(snode.edittree->runtime->nodes_by_id.contains(const_cast<bNode *>(&query_node)));
264
265 std::optional<int32_t> id_in_node;
266 const char *group_node_name = nullptr;
267 const bNode *node = &query_node;
268 LISTBASE_FOREACH_BACKWARD (const bNodeTreePath *, path, &snode.treepath) {
269 const bNodeTree *ntree = path->nodetree;
270 ntree->ensure_topology_cache();
271 if (group_node_name) {
272 node = group_node_by_name(*ntree, group_node_name);
273 }
274 bool found = false;
275 for (const bNestedNodeRef &ref : ntree->nested_node_refs_span()) {
276 if (node->is_group()) {
277 if (ref.path.node_id == node->identifier && ref.path.id_in_node == id_in_node) {
278 group_node_name = path->node_name;
279 id_in_node = ref.id;
280 found = true;
281 break;
282 }
283 }
284 else if (ref.path.node_id == node->identifier) {
285 group_node_name = path->node_name;
286 id_in_node = ref.id;
287 found = true;
288 break;
289 }
290 }
291 if (!found) {
292 return std::nullopt;
293 }
294 }
295 return id_in_node;
296}
297
298std::optional<ObjectAndModifier> get_modifier_for_node_editor(const SpaceNode &snode)
299{
300 if (snode.id == nullptr) {
301 return std::nullopt;
302 }
303 if (GS(snode.id->name) != ID_OB) {
304 return std::nullopt;
305 }
306 const Object *object = reinterpret_cast<Object *>(snode.id);
307 const NodesModifierData *used_modifier = nullptr;
308 if (snode.flag & SNODE_PIN) {
309 LISTBASE_FOREACH (const ModifierData *, md, &object->modifiers) {
310 if (md->type == eModifierType_Nodes) {
311 const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md);
312 /* Would be good to store the name of the pinned modifier in the node editor. */
313 if (nmd->node_group == snode.nodetree) {
314 used_modifier = nmd;
315 break;
316 }
317 }
318 }
319 }
320 else {
321 LISTBASE_FOREACH (const ModifierData *, md, &object->modifiers) {
322 if (md->type == eModifierType_Nodes) {
323 const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md);
324 if (nmd->node_group == snode.nodetree) {
325 if (md->flag & eModifierFlag_Active) {
326 used_modifier = nmd;
327 break;
328 }
329 }
330 }
331 }
332 }
333 if (used_modifier == nullptr) {
334 return std::nullopt;
335 }
336 return ObjectAndModifier{object, used_modifier};
337}
338
340 ComputeContextBuilder &compute_context_builder)
341{
343 LISTBASE_FOREACH (const bNodeTreePath *, item, &snode.treepath) {
344 tree_path.append(item);
345 }
346 if (tree_path.is_empty()) {
347 return true;
348 }
349
350 for (const int i : tree_path.index_range().drop_back(1)) {
351 bNodeTree *tree = tree_path[i]->nodetree;
352 const char *group_node_name = tree_path[i + 1]->node_name;
353 const bNode *group_node = blender::bke::node_find_node_by_name(tree, group_node_name);
354 if (group_node == nullptr) {
355 return false;
356 }
357 const blender::bke::bNodeTreeZones *tree_zones = tree->zones();
358 if (tree_zones == nullptr) {
359 return false;
360 }
362 tree_zones->get_zone_stack_for_node(group_node->identifier);
363 for (const blender::bke::bNodeTreeZone *zone : zone_stack) {
364 switch (zone->output_node->type) {
366 compute_context_builder.push<bke::SimulationZoneComputeContext>(*zone->output_node);
367 break;
368 }
370 const auto &storage = *static_cast<const NodeGeometryRepeatOutput *>(
371 zone->output_node->storage);
372 compute_context_builder.push<bke::RepeatZoneComputeContext>(*zone->output_node,
373 storage.inspection_index);
374 break;
375 }
377 const auto &storage = *static_cast<const NodeGeometryForeachGeometryElementOutput *>(
378 zone->output_node->storage);
380 *zone->output_node, storage.inspection_index);
381 break;
382 }
383 }
384 }
385 compute_context_builder.push<bke::GroupNodeComputeContext>(*group_node, *tree);
386 }
387 return true;
388}
389
390/* ******************** default callbacks for node space ***************** */
391
392static SpaceLink *node_create(const ScrArea * /*area*/, const Scene * /*scene*/)
393{
394 SpaceNode *snode = MEM_cnew<SpaceNode>("initnode");
395 snode->spacetype = SPACE_NODE;
396
400
401 /* backdrop */
402 snode->zoom = 1.0f;
403
404 /* select the first tree type for valid type */
405 NODE_TREE_TYPES_BEGIN (treetype) {
406 STRNCPY(snode->tree_idname, treetype->idname);
407 break;
408 }
410
411 /* header */
412 ARegion *region = MEM_cnew<ARegion>("header for node");
413
414 BLI_addtail(&snode->regionbase, region);
415 region->regiontype = RGN_TYPE_HEADER;
416 region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP;
417
418 /* buttons/list view */
419 region = MEM_cnew<ARegion>("buttons for node");
420
421 BLI_addtail(&snode->regionbase, region);
422 region->regiontype = RGN_TYPE_UI;
423 region->alignment = RGN_ALIGN_RIGHT;
424
425 /* toolbar */
426 region = MEM_cnew<ARegion>("node tools");
427
428 BLI_addtail(&snode->regionbase, region);
429 region->regiontype = RGN_TYPE_TOOLS;
430 region->alignment = RGN_ALIGN_LEFT;
431
432 region->flag = RGN_FLAG_HIDDEN;
433
434 /* main region */
435 region = MEM_cnew<ARegion>("main region for node");
436
437 BLI_addtail(&snode->regionbase, region);
438 region->regiontype = RGN_TYPE_WINDOW;
439
440 region->v2d.tot.xmin = -12.8f * U.widget_unit;
441 region->v2d.tot.ymin = -12.8f * U.widget_unit;
442 region->v2d.tot.xmax = 38.4f * U.widget_unit;
443 region->v2d.tot.ymax = 38.4f * U.widget_unit;
444
445 region->v2d.cur = region->v2d.tot;
446
447 region->v2d.min[0] = 1.0f;
448 region->v2d.min[1] = 1.0f;
449
450 region->v2d.max[0] = 32000.0f;
451 region->v2d.max[1] = 32000.0f;
452
453 region->v2d.minzoom = 0.05f;
454 region->v2d.maxzoom = 2.31f;
455
456 region->v2d.scroll = (V2D_SCROLL_RIGHT | V2D_SCROLL_BOTTOM);
457 region->v2d.keepzoom = V2D_LIMITZOOM | V2D_KEEPASPECT;
458 region->v2d.keeptot = 0;
459
460 return (SpaceLink *)snode;
461}
462
463static void node_free(SpaceLink *sl)
464{
465 SpaceNode *snode = (SpaceNode *)sl;
466 BLI_freelistN(&snode->treepath);
467 MEM_delete(snode->runtime);
468}
469
470/* spacetype; init callback */
471static void node_init(wmWindowManager * /*wm*/, ScrArea *area)
472{
473 SpaceNode *snode = static_cast<SpaceNode *>(area->spacedata.first);
474
475 if (snode->runtime == nullptr) {
476 snode->runtime = MEM_new<SpaceNode_Runtime>(__func__);
477 }
478}
479
480static void node_exit(wmWindowManager *wm, ScrArea *area)
481{
482 SpaceNode *snode = static_cast<SpaceNode *>(area->spacedata.first);
483
484 if (snode->runtime) {
485 free_previews(*wm, *snode);
486 }
487}
488
489static bool any_node_uses_id(const bNodeTree *ntree, const ID *id)
490{
491 if (ELEM(nullptr, ntree, id)) {
492 return false;
493 }
494 for (const bNode *node : ntree->all_nodes()) {
495 if (node->id == id) {
496 return true;
497 }
498 }
499 return false;
500}
501
511{
512 if (ED_node_is_compositor(snode)) {
513 snode->runtime->recalc_regular_compositing = true;
514 }
515
517}
518
520{
521 ScrArea *area = params->area;
522 const wmNotifier *wmn = params->notifier;
523
524 /* NOTE: #ED_area_tag_refresh will re-execute compositor. */
525 SpaceNode *snode = static_cast<SpaceNode *>(area->spacedata.first);
526 /* shaderfrom is only used for new shading nodes, otherwise all shaders are from objects */
527 short shader_type = snode->shaderfrom;
528
529 /* preview renders */
530 switch (wmn->category) {
531 case NC_SCENE:
532 switch (wmn->data) {
533 case ND_NODES: {
535 bNodeTreePath *path = (bNodeTreePath *)snode->treepath.last;
536 /* shift view to node tree center */
537 if (region && path) {
538 UI_view2d_center_set(&region->v2d, path->view_center[0], path->view_center[1]);
539 }
540
541 node_area_tag_tree_recalc(snode, area);
542 break;
543 }
544 case ND_FRAME:
545 node_area_tag_tree_recalc(snode, area);
546 break;
547 case ND_COMPO_RESULT: {
548 ED_area_tag_redraw(area);
549 /* Backdrop image offset is calculated during compositing so gizmos need to be updated
550 * afterwards. */
552 WM_gizmomap_tag_refresh(region->gizmo_map);
553 break;
554 }
555 }
556 break;
557
558 /* future: add ID checks? */
559 case NC_MATERIAL:
560 if (ED_node_is_shader(snode)) {
561 if (wmn->data == ND_SHADING) {
562 node_area_tag_tree_recalc(snode, area);
563 }
564 else if (wmn->data == ND_SHADING_DRAW) {
565 node_area_tag_tree_recalc(snode, area);
566 }
567 else if (wmn->data == ND_SHADING_LINKS) {
568 node_area_tag_tree_recalc(snode, area);
569 }
570 }
571 break;
572 case NC_TEXTURE:
573 if (ED_node_is_shader(snode) || ED_node_is_texture(snode)) {
574 if (wmn->data == ND_NODES) {
575 node_area_tag_tree_recalc(snode, area);
576 }
577 }
578 break;
579 case NC_WORLD:
580 if (ED_node_is_shader(snode) && shader_type == SNODE_SHADER_WORLD) {
581 node_area_tag_tree_recalc(snode, area);
582 }
583 break;
584 case NC_OBJECT:
585 if (ED_node_is_shader(snode)) {
586 if (wmn->data == ND_OB_SHADING) {
587 node_area_tag_tree_recalc(snode, area);
588 }
589 }
590 else if (ED_node_is_geometry(snode)) {
591 /* Rather strict check: only redraw when the reference matches the current editor's ID. */
592 if (wmn->data == ND_MODIFIER) {
593 if (wmn->reference == snode->id || snode->id == nullptr) {
594 node_area_tag_tree_recalc(snode, area);
595 }
596 }
597 }
598 break;
599 case NC_SPACE:
600 if (wmn->data == ND_SPACE_NODE) {
601 node_area_tag_tree_recalc(snode, area);
602 }
603 else if (wmn->data == ND_SPACE_NODE_VIEW) {
604 ED_area_tag_redraw(area);
605 }
606 break;
607 case NC_NODE:
608 if (wmn->action == NA_EDITED) {
609 if (ELEM(wmn->reference, snode->nodetree, snode->id, nullptr) || snode->id == nullptr) {
610 node_area_tag_tree_recalc(snode, area);
611 }
612 }
613 else if (wmn->action == NA_SELECTED) {
614 ED_area_tag_redraw(area);
615 }
616 break;
617 case NC_SCREEN:
618 switch (wmn->data) {
619 case ND_ANIMPLAY:
620 node_area_tag_tree_recalc(snode, area);
621 break;
622 }
623 break;
624 case NC_MASK:
625 if (wmn->action == NA_EDITED) {
626 if (snode->nodetree && snode->nodetree->type == NTREE_COMPOSIT) {
627 node_area_tag_tree_recalc(snode, area);
628 }
629 }
630 break;
631
632 case NC_IMAGE:
633 if (wmn->action == NA_EDITED) {
634 if (ED_node_is_compositor(snode)) {
635 /* Without this check drawing on an image could become very slow when the compositor is
636 * open. */
637 if (any_node_uses_id(snode->nodetree, (ID *)wmn->reference)) {
638 node_area_tag_tree_recalc(snode, area);
639 }
640 }
641 }
642 break;
643
644 case NC_MOVIECLIP:
645 if (wmn->action == NA_EDITED) {
646 if (ED_node_is_compositor(snode)) {
647 if (any_node_uses_id(snode->nodetree, (ID *)wmn->reference)) {
648 node_area_tag_tree_recalc(snode, area);
649 }
650 }
651 }
652 break;
653
654 case NC_LINESTYLE:
655 if (ED_node_is_shader(snode) && shader_type == SNODE_SHADER_LINESTYLE) {
656 node_area_tag_tree_recalc(snode, area);
657 }
658 break;
659 case NC_WM:
660 if (wmn->data == ND_UNDO) {
661 node_area_tag_tree_recalc(snode, area);
662 }
663 break;
664 case NC_GPENCIL:
665 if (ELEM(wmn->action, NA_EDITED, NA_SELECTED)) {
666 ED_area_tag_redraw(area);
667 }
668 break;
669 }
670}
671
672/* Returns true if an image editor exists that views the compositor result. */
674{
675 wmWindowManager *window_manager = CTX_wm_manager(C);
676 LISTBASE_FOREACH (wmWindow *, window, &window_manager->windows) {
677 bScreen *screen = WM_window_get_active_screen(window);
678 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
679 SpaceLink *space_link = static_cast<SpaceLink *>(area->spacedata.first);
680 if (!space_link || space_link->spacetype != SPACE_IMAGE) {
681 continue;
682 }
683 const SpaceImage *space_image = reinterpret_cast<const SpaceImage *>(space_link);
684 Image *image = ED_space_image(space_image);
685 if (image && image->source == IMA_SRC_VIEWER) {
686 return true;
687 }
688 }
689 }
690
691 return false;
692}
693
694static void node_area_refresh(const bContext *C, ScrArea *area)
695{
696 /* default now: refresh node is starting preview */
697 SpaceNode *snode = static_cast<SpaceNode *>(area->spacedata.first);
698
700
701 if (snode->nodetree) {
702 if (snode->nodetree->type == NTREE_COMPOSIT) {
703 Scene *scene = (Scene *)snode->id;
704 if (scene->use_nodes) {
706 snode->runtime->recalc_regular_compositing = false;
707 /* Only start compositing if its result will be visible either in the backdrop or in a
708 * viewer image. */
710 ED_node_composite_job(C, snode->nodetree, scene);
711 }
712 }
713 }
714 }
715 }
716}
717
719{
720 SpaceNode *snode = (SpaceNode *)sl;
721 SpaceNode *snoden = (SpaceNode *)MEM_dupallocN(snode);
722
723 BLI_duplicatelist(&snoden->treepath, &snode->treepath);
724
725 snoden->runtime = MEM_new<SpaceNode_Runtime>(__func__);
726
727 /* NOTE: no need to set node tree user counts,
728 * the editor only keeps at least 1 (id_us_ensure_real),
729 * which is already done by the original SpaceNode.
730 */
731
732 return (SpaceLink *)snoden;
733}
734
735/* add handlers, stuff you only do once or on area/region changes */
737{
738 wmKeyMap *keymap;
739
740 ED_region_panels_init(wm, region);
741
742 keymap = WM_keymap_ensure(wm->defaultconf, "Node Generic", SPACE_NODE, RGN_TYPE_WINDOW);
743 WM_event_add_keymap_handler(&region->handlers, keymap);
744}
745
746static void node_buttons_region_draw(const bContext *C, ARegion *region)
747{
748 ED_region_panels(C, region);
749}
750
751/* add handlers, stuff you only do once or on area/region changes */
753{
754 wmKeyMap *keymap;
755
756 ED_region_panels_init(wm, region);
757
758 keymap = WM_keymap_ensure(wm->defaultconf, "Node Generic", SPACE_NODE, RGN_TYPE_WINDOW);
759 WM_event_add_keymap_handler(&region->handlers, keymap);
760}
761
762static void node_toolbar_region_draw(const bContext *C, ARegion *region)
763{
764 ED_region_panels(C, region);
765}
766
767static void node_cursor(wmWindow *win, ScrArea *area, ARegion *region)
768{
769 SpaceNode *snode = static_cast<SpaceNode *>(area->spacedata.first);
770
771 /* convert mouse coordinates to v2d space */
772 UI_view2d_region_to_view(&region->v2d,
773 win->eventstate->xy[0] - region->winrct.xmin,
774 win->eventstate->xy[1] - region->winrct.ymin,
775 &snode->runtime->cursor[0],
776 &snode->runtime->cursor[1]);
777
778 /* here snode->runtime->cursor is used to detect the node edge for sizing */
779 node_set_cursor(*win, *region, *snode, snode->runtime->cursor);
780
781 /* XXX snode->runtime->cursor is in placing new nodes space */
782 snode->runtime->cursor[0] /= UI_SCALE_FAC;
783 snode->runtime->cursor[1] /= UI_SCALE_FAC;
784}
785
786/* Initialize main region, setting handlers. */
788{
789 wmKeyMap *keymap;
790 ListBase *lb;
791
792 UI_view2d_region_reinit(&region->v2d, V2D_COMMONVIEW_CUSTOM, region->winx, region->winy);
793
794 /* own keymaps */
795 keymap = WM_keymap_ensure(wm->defaultconf, "Node Generic", SPACE_NODE, RGN_TYPE_WINDOW);
796 WM_event_add_keymap_handler(&region->handlers, keymap);
797
798 keymap = WM_keymap_ensure(wm->defaultconf, "Node Editor", SPACE_NODE, RGN_TYPE_WINDOW);
799 WM_event_add_keymap_handler_v2d_mask(&region->handlers, keymap);
800
801 /* add drop boxes */
802 lb = WM_dropboxmap_find("Node Editor", SPACE_NODE, RGN_TYPE_WINDOW);
803
804 WM_event_add_dropbox_handler(&region->handlers, lb);
805
806 /* The backdrop image gizmo needs to change together with the view. So always refresh gizmos on
807 * region size changes. */
808 WM_gizmomap_tag_refresh(region->gizmo_map);
809}
810
811static void node_main_region_draw(const bContext *C, ARegion *region)
812{
813 node_draw_space(*C, *region);
814}
815
816/* ************* dropboxes ************* */
817
818static bool node_group_drop_poll(bContext *C, wmDrag *drag, const wmEvent * /*event*/)
819{
820 SpaceNode *snode = CTX_wm_space_node(C);
821
822 if (snode->edittree == nullptr) {
823 return false;
824 }
825
826 if (!WM_drag_is_ID_type(drag, ID_NT)) {
827 return false;
828 }
829
830 if (drag->type == WM_DRAG_ID) {
831 const bNodeTree *node_tree = reinterpret_cast<const bNodeTree *>(
833 if (!node_tree) {
834 return false;
835 }
836 return node_tree->type == snode->edittree->type;
837 }
838
839 if (drag->type == WM_DRAG_ASSET) {
840 const wmDragAsset *asset_data = WM_drag_get_asset_data(drag, ID_NT);
841 if (!asset_data) {
842 return false;
843 }
844 const AssetMetaData *metadata = &asset_data->asset->get_metadata();
845 const IDProperty *tree_type = BKE_asset_metadata_idprop_find(metadata, "type");
846 if (!tree_type || IDP_Int(tree_type) != snode->edittree->type) {
847 return false;
848 }
849 }
850
851 return true;
852}
853
854static bool node_object_drop_poll(bContext *C, wmDrag *drag, const wmEvent * /*event*/)
855{
857}
858
859static bool node_collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent * /*event*/)
860{
862}
863
864static bool node_id_im_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
865{
866 return WM_drag_is_ID_type(drag, ID_IM);
867}
868
869static bool node_mask_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/)
870{
871 return WM_drag_is_ID_type(drag, ID_MSK);
872}
873
874static bool node_material_drop_poll(bContext *C, wmDrag *drag, const wmEvent * /*event*/)
875{
877}
878
879static void node_group_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
880{
882
883 RNA_int_set(drop->ptr, "session_uid", int(id->session_uid));
884
885 RNA_boolean_set(drop->ptr, "show_datablock_in_node", (drag->type != WM_DRAG_ASSET));
886}
887
888static void node_id_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
889{
891
892 RNA_int_set(drop->ptr, "session_uid", int(id->session_uid));
893}
894
895static void node_id_im_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
896{
898 if (id) {
899 RNA_int_set(drop->ptr, "session_uid", int(id->session_uid));
900 RNA_struct_property_unset(drop->ptr, "filepath");
901 return;
902 }
903}
904
905/* this region dropbox definition */
906static void node_dropboxes()
907{
909
911 "NODE_OT_add_object",
915 nullptr);
917 "NODE_OT_add_collection",
921 nullptr);
923 "NODE_OT_add_group",
927 nullptr);
929 "NODE_OT_add_file",
933 nullptr);
935 "NODE_OT_add_mask",
939 nullptr);
941 "NODE_OT_add_material",
945 nullptr);
946}
947
948/* ************* end drop *********** */
949
950/* add handlers, stuff you only do once or on area/region changes */
951static void node_header_region_init(wmWindowManager * /*wm*/, ARegion *region)
952{
953 ED_region_header_init(region);
954}
955
956static void node_header_region_draw(const bContext *C, ARegion *region)
957{
958 /* find and set the context */
960
961 ED_region_header(C, region);
962}
963
964/* used for header + main region */
966{
967 ARegion *region = params->region;
968 const wmNotifier *wmn = params->notifier;
969 wmGizmoMap *gzmap = region->gizmo_map;
970
971 /* context changes */
972 switch (wmn->category) {
973 case NC_SPACE:
974 switch (wmn->data) {
975 case ND_SPACE_NODE:
976 ED_region_tag_redraw(region);
977 break;
980 break;
981 }
982 break;
983 case NC_SCREEN:
984 if (wmn->data == ND_LAYOUTSET || wmn->action == NA_EDITED) {
986 }
987 switch (wmn->data) {
988 case ND_ANIMPLAY:
989 case ND_LAYER:
990 ED_region_tag_redraw(region);
991 break;
992 }
993 break;
994 case NC_WM:
995 if (wmn->data == ND_JOB) {
996 ED_region_tag_redraw(region);
997 }
998 break;
999 case NC_SCENE:
1000 ED_region_tag_redraw(region);
1001 if (wmn->data == ND_RENDER_RESULT) {
1003 }
1004 break;
1005 case NC_NODE:
1006 ED_region_tag_redraw(region);
1007 if (ELEM(wmn->action, NA_EDITED, NA_SELECTED)) {
1009 }
1010 break;
1011 case NC_MATERIAL:
1012 case NC_TEXTURE:
1013 case NC_WORLD:
1014 case NC_LINESTYLE:
1015 ED_region_tag_redraw(region);
1016 break;
1017 case NC_OBJECT:
1018 if (wmn->data == ND_OB_SHADING) {
1019 ED_region_tag_redraw(region);
1020 }
1021 break;
1022 case NC_ID:
1023 if (ELEM(wmn->action, NA_RENAME, NA_EDITED)) {
1024 ED_region_tag_redraw(region);
1025 }
1026 break;
1027 case NC_GPENCIL:
1028 if (wmn->action == NA_EDITED) {
1029 ED_region_tag_redraw(region);
1030 }
1031 else if (wmn->data & ND_GPENCIL_EDITMODE) {
1032 ED_region_tag_redraw(region);
1033 }
1034 break;
1035 case NC_VIEWER_PATH:
1036 ED_region_tag_redraw(region);
1037 break;
1038 }
1039}
1040
1041} // namespace blender::ed::space_node
1042
1043/* Outside of blender namespace to avoid Python documentation build error with `ctypes`. */
1044extern "C" {
1045const char *node_context_dir[] = {
1046 "selected_nodes", "active_node", "light", "material", "world", nullptr};
1047};
1048
1049namespace blender::ed::space_node {
1050
1051static int /*eContextResult*/ node_context(const bContext *C,
1052 const char *member,
1053 bContextDataResult *result)
1054{
1055 SpaceNode *snode = CTX_wm_space_node(C);
1056
1057 if (CTX_data_dir(member)) {
1059 return CTX_RESULT_OK;
1060 }
1061 if (CTX_data_equals(member, "selected_nodes")) {
1062 if (snode->edittree) {
1063 for (bNode *node : snode->edittree->all_nodes()) {
1064 if (node->flag & NODE_SELECT) {
1065 CTX_data_list_add(result, &snode->edittree->id, &RNA_Node, node);
1066 }
1067 }
1068 }
1070 return CTX_RESULT_OK;
1071 }
1072 if (CTX_data_equals(member, "active_node")) {
1073 if (snode->edittree) {
1074 bNode *node = bke::node_get_active(snode->edittree);
1075 CTX_data_pointer_set(result, &snode->edittree->id, &RNA_Node, node);
1076 }
1077
1079 return CTX_RESULT_OK;
1080 }
1081 if (CTX_data_equals(member, "node_previews")) {
1082 if (snode->nodetree) {
1084 result, &snode->nodetree->id, &RNA_NodeInstanceHash, snode->nodetree->previews);
1085 }
1086
1088 return CTX_RESULT_OK;
1089 }
1090 if (CTX_data_equals(member, "material")) {
1091 if (snode->id && GS(snode->id->name) == ID_MA) {
1092 CTX_data_id_pointer_set(result, snode->id);
1093 }
1094 return CTX_RESULT_OK;
1095 }
1096 if (CTX_data_equals(member, "light")) {
1097 if (snode->id && GS(snode->id->name) == ID_LA) {
1098 CTX_data_id_pointer_set(result, snode->id);
1099 }
1100 return CTX_RESULT_OK;
1101 }
1102 if (CTX_data_equals(member, "world")) {
1103 if (snode->id && GS(snode->id->name) == ID_WO) {
1104 CTX_data_id_pointer_set(result, snode->id);
1105 }
1106 return CTX_RESULT_OK;
1107 }
1108
1110}
1111
1122
1123static void node_id_remap(ID *old_id, ID *new_id, SpaceNode *snode)
1124{
1125 if (snode->id == old_id) {
1126 /* nasty DNA logic for SpaceNode:
1127 * ideally should be handled by editor code, but would be bad level call
1128 */
1129 BLI_freelistN(&snode->treepath);
1130
1131 /* XXX Untested in case new_id != nullptr... */
1132 snode->id = new_id;
1133 snode->from = nullptr;
1134 snode->nodetree = nullptr;
1135 snode->edittree = nullptr;
1136 }
1137 else if (GS(old_id->name) == ID_OB) {
1138 if (snode->from == old_id) {
1139 if (new_id == nullptr) {
1140 snode->flag &= ~SNODE_PIN;
1141 }
1142 snode->from = new_id;
1143 }
1144 }
1145 else if (GS(old_id->name) == ID_GD_LEGACY) {
1146 if ((ID *)snode->gpd == old_id) {
1147 snode->gpd = (bGPdata *)new_id;
1148 id_us_min(old_id);
1149 id_us_plus(new_id);
1150 }
1151 }
1152 else if (GS(old_id->name) == ID_NT) {
1153
1154 if (snode->geometry_nodes_tool_tree) {
1155 if (&snode->geometry_nodes_tool_tree->id == old_id) {
1156 snode->geometry_nodes_tool_tree = reinterpret_cast<bNodeTree *>(new_id);
1157 }
1158 }
1159
1160 bNodeTreePath *path, *path_next;
1161
1162 for (path = (bNodeTreePath *)snode->treepath.first; path; path = path->next) {
1163 if ((ID *)path->nodetree == old_id) {
1164 path->nodetree = (bNodeTree *)new_id;
1165 id_us_ensure_real(new_id);
1166 }
1167 if (path == snode->treepath.first) {
1168 /* first nodetree in path is same as snode->nodetree */
1169 snode->nodetree = path->nodetree;
1170 }
1171 if (path->nodetree == nullptr) {
1172 break;
1173 }
1174 }
1175
1176 /* remaining path entries are invalid, remove */
1177 for (; path; path = path_next) {
1178 path_next = path->next;
1179
1180 BLI_remlink(&snode->treepath, path);
1181 MEM_freeN(path);
1182 }
1183
1184 /* edittree is just the last in the path,
1185 * set this directly since the path may have been shortened above */
1186 if (snode->treepath.last) {
1187 path = (bNodeTreePath *)snode->treepath.last;
1188 snode->edittree = path->nodetree;
1189 }
1190 else {
1191 snode->edittree = nullptr;
1192 }
1193 }
1194}
1195
1196static void node_id_remap(ScrArea * /*area*/,
1197 SpaceLink *slink,
1198 const blender::bke::id::IDRemapper &mappings)
1199{
1200 /* Although we should be able to perform all the mappings in a single go this lead to issues when
1201 * running the python test cases. Somehow the nodetree/edittree weren't updated to the new
1202 * pointers that generated a SEGFAULT.
1203 *
1204 * To move forward we should perhaps remove snode->edittree and snode->nodetree as they are just
1205 * copies of pointers. All usages should be calling a function that will receive the appropriate
1206 * instance.
1207 *
1208 * We could also move a remap address at a time to use the IDRemapper as that should get closer
1209 * to cleaner code. See {D13615} for more information about this topic.
1210 */
1211 mappings.iter([&](ID *old_id, ID *new_id) {
1212 node_id_remap(old_id, new_id, reinterpret_cast<SpaceNode *>(slink));
1213 });
1214}
1215
1216static void node_foreach_id(SpaceLink *space_link, LibraryForeachIDData *data)
1217{
1218 SpaceNode *snode = reinterpret_cast<SpaceNode *>(space_link);
1219 const int data_flags = BKE_lib_query_foreachid_process_flags_get(data);
1220 const bool is_readonly = (data_flags & IDWALK_READONLY) != 0;
1221 const bool allow_pointer_access = (data_flags & IDWALK_NO_ORIG_POINTERS_ACCESS) == 0;
1222 bool is_embedded_nodetree = snode->id != nullptr && allow_pointer_access &&
1223 bke::node_tree_from_id(snode->id) == snode->nodetree;
1224
1227
1228 bNodeTreePath *path = static_cast<bNodeTreePath *>(snode->treepath.first);
1229 BLI_assert(path == nullptr || path->nodetree == snode->nodetree);
1230
1231 if (is_embedded_nodetree) {
1233 if (path != nullptr) {
1235 }
1236
1237 /* Embedded ID pointers are not remapped (besides exceptions), ensure it still matches
1238 * actual data. Note that `snode->id` was already processed (and therefore potentially
1239 * remapped) above. */
1240 if (!is_readonly) {
1241 snode->nodetree = (snode->id == nullptr) ? nullptr : bke::node_tree_from_id(snode->id);
1242 if (path != nullptr) {
1243 path->nodetree = snode->nodetree;
1244 }
1245 }
1246 }
1247 else {
1250 if (path != nullptr) {
1253 }
1254 }
1255
1258
1259 /* Both `snode->id` and `snode->nodetree` have been remapped now, so their data can be
1260 * accessed. */
1261 BLI_assert(snode->id == nullptr || snode->nodetree == nullptr ||
1262 (snode->nodetree->id.flag & ID_FLAG_EMBEDDED_DATA) == 0 ||
1263 snode->nodetree == bke::node_tree_from_id(snode->id));
1264
1265 /* This is mainly here for readfile case ('lib_link' process), as in such case there is no access
1266 * to original data allowed, so no way to know whether the SpaceNode nodetree pointer is an
1267 * embedded one or not. */
1268 if (!is_readonly && snode->id && !snode->nodetree) {
1269 is_embedded_nodetree = true;
1270 snode->nodetree = bke::node_tree_from_id(snode->id);
1271 if (path != nullptr) {
1272 path->nodetree = snode->nodetree;
1273 }
1274 }
1275
1276 if (path != nullptr) {
1277 for (path = path->next; path != nullptr; path = path->next) {
1278 BLI_assert(path->nodetree != nullptr);
1279 if (allow_pointer_access) {
1281 }
1282
1285
1286 if (path->nodetree == nullptr) {
1287 BLI_assert(!is_readonly);
1288 /* Remaining path entries are invalid, remove them. */
1289 for (bNodeTreePath *path_next; path; path = path_next) {
1290 path_next = path->next;
1291 BLI_remlink(&snode->treepath, path);
1292 MEM_freeN(path);
1293 }
1294 break;
1295 }
1296 }
1297 }
1298 BLI_assert(path == nullptr);
1299
1300 if (!is_readonly) {
1301 /* `edittree` is just the last in the path, set this directly since the path may have
1302 * been shortened above. */
1303 if (snode->treepath.last != nullptr) {
1304 path = static_cast<bNodeTreePath *>(snode->treepath.last);
1305 snode->edittree = path->nodetree;
1306 }
1307 else {
1308 snode->edittree = nullptr;
1309 }
1310 }
1311 else {
1312 /* Only process this pointer in readonly case, otherwise could lead to a bad
1313 * double-remapping e.g. */
1314 if (is_embedded_nodetree && snode->edittree == snode->nodetree) {
1316 }
1317 else {
1319 }
1320 }
1321}
1322
1324{
1325 SpaceNode *snode = static_cast<SpaceNode *>(area->spacedata.first);
1327}
1328
1329static void node_space_subtype_set(ScrArea *area, int value)
1330{
1331 SpaceNode *snode = static_cast<SpaceNode *>(area->spacedata.first);
1333}
1334
1335static void node_space_subtype_item_extend(bContext *C, EnumPropertyItem **item, int *totitem)
1336{
1337 bool free;
1339 RNA_enum_items_add(item, totitem, item_src);
1340 if (free) {
1341 MEM_freeN((void *)item_src);
1342 }
1343}
1344
1346{
1347 SpaceNode *snode = static_cast<SpaceNode *>(area->spacedata.first);
1349 return tree_type->ui_name;
1350}
1351
1352static int node_space_icon_get(const ScrArea *area)
1353{
1354 SpaceNode *snode = static_cast<SpaceNode *>(area->spacedata.first);
1356 return tree_type->ui_icon;
1357}
1358
1360{
1361 SpaceNode *snode = (SpaceNode *)sl;
1362
1363 if (snode->gpd) {
1364 BLO_read_struct(reader, bGPdata, &snode->gpd);
1365 BKE_gpencil_blend_read_data(reader, snode->gpd);
1366 }
1367
1369 snode->edittree = nullptr;
1370 snode->runtime = nullptr;
1371}
1372
1374{
1375 SpaceNode *snode = (SpaceNode *)sl;
1376 BLO_write_struct(writer, SpaceNode, snode);
1377
1378 LISTBASE_FOREACH (bNodeTreePath *, path, &snode->treepath) {
1379 BLO_write_struct(writer, bNodeTreePath, path);
1380 }
1381}
1382
1383} // namespace blender::ed::space_node
1384
1386{
1387 using namespace blender::ed::space_node;
1388
1389 std::unique_ptr<SpaceType> st = std::make_unique<SpaceType>();
1390 ARegionType *art;
1391
1392 st->spaceid = SPACE_NODE;
1393 STRNCPY(st->name, "Node");
1394
1395 st->create = node_create;
1396 st->free = node_free;
1397 st->init = node_init;
1398 st->exit = node_exit;
1399 st->duplicate = node_duplicate;
1400 st->operatortypes = node_operatortypes;
1401 st->keymap = node_keymap;
1402 st->listener = node_area_listener;
1403 st->refresh = node_area_refresh;
1404 st->context = node_context;
1405 st->dropboxes = node_dropboxes;
1406 st->gizmos = node_widgets;
1407 st->id_remap = node_id_remap;
1408 st->foreach_id = node_foreach_id;
1409 st->space_subtype_item_extend = node_space_subtype_item_extend;
1410 st->space_subtype_get = node_space_subtype_get;
1411 st->space_subtype_set = node_space_subtype_set;
1412 st->space_name_get = node_space_name_get;
1413 st->space_icon_get = node_space_icon_get;
1414 st->blend_read_data = node_space_blend_read_data;
1415 st->blend_read_after_liblink = nullptr;
1416 st->blend_write = node_space_blend_write;
1417
1418 /* regions: main window */
1419 art = MEM_cnew<ARegionType>("spacetype node region");
1421 art->init = node_main_region_init;
1422 art->draw = node_main_region_draw;
1425 art->listener = node_region_listener;
1426 art->cursor = node_cursor;
1427 art->event_cursor = true;
1428 art->clip_gizmo_events_by_ui = true;
1429 art->lock = 1;
1430
1431 BLI_addhead(&st->regiontypes, art);
1432
1433 /* regions: header */
1434 art = MEM_cnew<ARegionType>("spacetype node region");
1436 art->prefsizey = HEADERY;
1438 art->listener = node_region_listener;
1439 art->init = node_header_region_init;
1440 art->draw = node_header_region_draw;
1441
1442 BLI_addhead(&st->regiontypes, art);
1443
1444 /* regions: list-view/buttons */
1445 art = MEM_cnew<ARegionType>("spacetype node region");
1446 art->regionid = RGN_TYPE_UI;
1449 art->listener = node_region_listener;
1451 art->init = node_buttons_region_init;
1452 art->draw = node_buttons_region_draw;
1453 BLI_addhead(&st->regiontypes, art);
1454
1455 /* regions: toolbar */
1456 art = MEM_cnew<ARegionType>("spacetype view3d tools region");
1457 art->regionid = RGN_TYPE_TOOLS;
1459 art->prefsizey = 50; /* XXX */
1461 art->listener = node_region_listener;
1464 art->init = node_toolbar_region_init;
1465 art->draw = node_toolbar_region_draw;
1466 BLI_addhead(&st->regiontypes, art);
1467
1468 WM_menutype_add(MEM_cnew<MenuType>(__func__, add_catalog_assets_menu_type()));
1469 WM_menutype_add(MEM_cnew<MenuType>(__func__, add_unassigned_assets_menu_type()));
1470 WM_menutype_add(MEM_cnew<MenuType>(__func__, add_root_catalogs_menu_type()));
1471
1472 BKE_spacetype_register(std::move(st));
1473}
Main runtime representation of an asset.
IDProperty * BKE_asset_metadata_idprop_find(const AssetMetaData *asset_data, const char *name) ATTR_WARN_UNUSED_RESULT
Definition asset.cc:186
void CTX_data_dir_set(bContextDataResult *result, const char **dir)
bool CTX_data_equals(const char *member, const char *str)
void CTX_data_pointer_set(bContextDataResult *result, ID *id, StructRNA *type, void *data)
bool CTX_data_dir(const char *member)
void CTX_data_id_pointer_set(bContextDataResult *result, ID *id)
SpaceNode * CTX_wm_space_node(const bContext *C)
@ CTX_RESULT_MEMBER_NOT_FOUND
@ CTX_RESULT_OK
void CTX_data_list_add(bContextDataResult *result, ID *id, StructRNA *type, void *data)
@ CTX_DATA_TYPE_POINTER
@ CTX_DATA_TYPE_COLLECTION
wmWindowManager * CTX_wm_manager(const bContext *C)
void CTX_data_type_set(bContextDataResult *result, short type)
void BKE_gpencil_blend_read_data(struct BlendDataReader *reader, struct bGPdata *gpd)
#define IDP_Int(prop)
void id_us_plus(ID *id)
Definition lib_id.cc:351
void id_us_ensure_real(ID *id)
Definition lib_id.cc:306
void id_us_min(ID *id)
Definition lib_id.cc:359
#define BKE_LIB_FOREACHID_PROCESS_IDSUPER(data_, id_super_, cb_flag_)
@ IDWALK_CB_USER_ONE
@ IDWALK_CB_EMBEDDED_NOT_OWNING
@ IDWALK_CB_DIRECT_WEAK_LINK
int BKE_lib_query_foreachid_process_flags_get(const LibraryForeachIDData *data)
Definition lib_query.cc:120
@ IDWALK_READONLY
@ IDWALK_NO_ORIG_POINTERS_ACCESS
#define BKE_LIB_FOREACHID_PROCESS_ID(data_, id_, cb_flag_)
#define GEO_NODE_FOREACH_GEOMETRY_ELEMENT_OUTPUT
Definition BKE_node.hh:1379
#define GEO_NODE_SIMULATION_OUTPUT
Definition BKE_node.hh:1331
#define NODE_TREE_TYPES_BEGIN(ntype)
Definition BKE_node.hh:499
#define NODE_TREE_TYPES_END
Definition BKE_node.hh:508
#define GEO_NODE_REPEAT_OUTPUT
Definition BKE_node.hh:1338
void BKE_spacetype_register(std::unique_ptr< SpaceType > st)
Definition screen.cc:268
ARegion * BKE_area_find_region_type(const ScrArea *area, int region_type)
Definition screen.cc:815
#define BLI_assert(a)
Definition BLI_assert.h:50
void BLI_kdtree_nd_ free(KDTree *tree)
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:90
#define LISTBASE_FOREACH(type, var, list)
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void void void void void void BLI_duplicatelist(struct ListBase *dst, const struct ListBase *src) ATTR_NONNULL(1
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE void copy_v2_v2(float r[2], const float a[2])
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define ELEM(...)
#define BLO_write_struct(writer, struct_name, data_ptr)
#define BLO_read_struct_list(reader, struct_name, list)
#define BLO_read_struct(reader, struct_name, ptr_p)
void DEG_id_tag_update(ID *id, unsigned int flags)
ID and Library types, which are fundamental for SDNA.
@ ID_RECALC_NTREE_OUTPUT
Definition DNA_ID.h:1122
@ ID_FLAG_EMBEDDED_DATA
Definition DNA_ID.h:725
@ ID_IM
@ ID_NT
@ ID_LA
@ ID_MSK
@ ID_WO
@ ID_MA
@ ID_GD_LEGACY
@ ID_GR
@ ID_OB
@ IMA_SRC_VIEWER
@ eModifierFlag_Active
@ eModifierType_Nodes
@ NTREE_GEOMETRY
@ NTREE_COMPOSIT
@ NODE_SELECT
Object is a sort of wrapper for general info.
#define HEADERY
@ RGN_ALIGN_BOTTOM
@ RGN_ALIGN_LEFT
@ RGN_ALIGN_TOP
@ RGN_ALIGN_RIGHT
@ RGN_TYPE_UI
@ RGN_TYPE_WINDOW
@ RGN_TYPE_HEADER
@ RGN_TYPE_TOOLS
@ RGN_FLAG_HIDDEN
@ SN_OVERLAY_SHOW_PATH
@ SN_OVERLAY_SHOW_PREVIEWS
@ SN_OVERLAY_SHOW_WIRE_COLORS
@ SN_OVERLAY_SHOW_OVERLAYS
@ SNODE_PIN
@ SNODE_USE_ALPHA
@ SNODE_BACKDRAW
@ SNODE_SHOW_GPENCIL
@ SPACE_NODE
@ SPACE_IMAGE
@ SNODE_SHADER_WORLD
@ SNODE_SHADER_LINESTYLE
@ USER_HEADER_BOTTOM
#define UI_SCALE_FAC
@ V2D_SCROLL_RIGHT
@ V2D_SCROLL_BOTTOM
@ V2D_LIMITZOOM
@ V2D_KEEPASPECT
Image * ED_space_image(const SpaceImage *sima)
Definition image_edit.cc:40
void ED_node_set_tree_type(SpaceNode *snode, blender::bke::bNodeTreeType *typeinfo)
Definition node_edit.cc:513
bool ED_node_is_compositor(const SpaceNode *snode)
Definition node_edit.cc:523
bool ED_node_is_shader(SpaceNode *snode)
Definition node_edit.cc:528
void ED_node_composite_job(const bContext *C, bNodeTree *nodetree, Scene *scene_owner)
Definition node_edit.cc:375
bool ED_node_is_geometry(SpaceNode *snode)
Definition node_edit.cc:538
bool ED_node_is_texture(SpaceNode *snode)
Definition node_edit.cc:533
void ED_area_tag_redraw(ScrArea *area)
Definition area.cc:708
void ED_area_do_mgs_subscribe_for_tool_ui(const wmRegionMessageSubscribeParams *params)
Definition area.cc:429
void ED_region_panels(const bContext *C, ARegion *region)
Definition area.cc:3336
void ED_region_header(const bContext *C, ARegion *region)
Definition area.cc:3646
void ED_region_header_init(ARegion *region)
Definition area.cc:3661
int ED_region_generic_tools_region_snap_size(const ARegion *region, int size, int axis)
Definition area_utils.cc:41
void ED_area_tag_refresh(ScrArea *area)
Definition area.cc:737
void ED_region_generic_tools_region_message_subscribe(const wmRegionMessageSubscribeParams *params)
Definition area_utils.cc:29
void ED_region_panels_init(wmWindowManager *wm, ARegion *region)
Definition area.cc:3343
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
@ ED_KEYMAP_UI
Definition ED_screen.hh:725
@ ED_KEYMAP_HEADER
Definition ED_screen.hh:731
@ ED_KEYMAP_TOOL
Definition ED_screen.hh:727
@ ED_KEYMAP_GPENCIL
Definition ED_screen.hh:733
@ ED_KEYMAP_GIZMO
Definition ED_screen.hh:726
@ ED_KEYMAP_VIEW2D
Definition ED_screen.hh:728
@ ED_KEYMAP_FRAMES
Definition ED_screen.hh:730
Read Guarded memory(de)allocation.
blender::bke::bNodeTreeType * rna_node_tree_type_from_enum(int value)
int rna_node_tree_idname_to_enum(const char *idname)
const EnumPropertyItem * RNA_enum_node_tree_types_itemf_impl(bContext *C, bool *r_free)
#define UI_SIDEBAR_PANEL_WIDTH
bool UI_but_active_drop_name(const bContext *C)
#define UI_TOOLBAR_WIDTH
void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy)
Definition view2d.cc:212
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
void UI_view2d_center_set(View2D *v2d, float x, float y)
Definition view2d.cc:1944
@ V2D_COMMONVIEW_CUSTOM
Definition UI_view2d.hh:31
#define NC_WORLD
Definition WM_types.hh:354
#define ND_SHADING
Definition WM_types.hh:444
#define NC_ID
Definition WM_types.hh:362
#define NC_NODE
Definition WM_types.hh:361
#define ND_RENDER_RESULT
Definition WM_types.hh:413
#define ND_JOB
Definition WM_types.hh:383
#define NC_WM
Definition WM_types.hh:341
#define ND_GPENCIL_EDITMODE
Definition WM_types.hh:470
#define NC_LINESTYLE
Definition WM_types.hh:367
#define NC_VIEWER_PATH
Definition WM_types.hh:373
#define ND_SPACE_NODE
Definition WM_types.hh:492
#define ND_COMPO_RESULT
Definition WM_types.hh:414
#define NC_SCREEN
Definition WM_types.hh:344
#define NC_MOVIECLIP
Definition WM_types.hh:364
#define ND_ANIMPLAY
Definition WM_types.hh:391
#define NC_SCENE
Definition WM_types.hh:345
#define ND_SPACE_NODE_VIEW
Definition WM_types.hh:502
#define ND_NODES
Definition WM_types.hh:403
#define ND_MODIFIER
Definition WM_types.hh:429
#define NA_EDITED
Definition WM_types.hh:550
#define NC_MATERIAL
Definition WM_types.hh:347
#define NC_IMAGE
Definition WM_types.hh:351
#define ND_UNDO
Definition WM_types.hh:384
#define ND_FRAME
Definition WM_types.hh:401
#define NC_GPENCIL
Definition WM_types.hh:366
#define NC_TEXTURE
Definition WM_types.hh:348
#define ND_LAYER
Definition WM_types.hh:417
#define NC_MASK
Definition WM_types.hh:365
#define NA_RENAME
Definition WM_types.hh:554
@ WM_DRAG_ASSET
Definition WM_types.hh:1154
@ WM_DRAG_ID
Definition WM_types.hh:1153
#define ND_OB_SHADING
Definition WM_types.hh:424
#define ND_LAYOUTSET
Definition WM_types.hh:393
#define NC_OBJECT
Definition WM_types.hh:346
#define ND_SHADING_LINKS
Definition WM_types.hh:446
#define ND_SHADING_DRAW
Definition WM_types.hh:445
#define NC_SPACE
Definition WM_types.hh:359
#define NA_SELECTED
Definition WM_types.hh:555
unsigned int U
Definition btGjkEpa3.h:78
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
constexpr IndexRange drop_back(int64_t n) const
void append(const T &value)
bool is_empty() const
IndexRange index_range() const
Vector< const bNodeTreeZone * > get_zone_stack_for_node(const int32_t node_id) const
Vector< std::unique_ptr< bNodeTreeZone > > zones
void iter(FunctionRef< void(ID *old_id, ID *new_id)> func) const
OperationNode * node
StackEntry * from
int len
KDTree_3d * tree
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
#define GS(x)
Definition iris.cc:202
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
bNode * node_get_active(bNodeTree *ntree)
Definition node.cc:3849
const bNodeInstanceKey NODE_INSTANCE_KEY_BASE
Definition node.cc:4020
bNodeInstanceKey node_instance_key(bNodeInstanceKey parent_key, const bNodeTree *ntree, const bNode *node)
Definition node.cc:4041
bNodeTreeType * node_tree_type_find(const char *idname)
Definition node.cc:1621
bNodeTree * node_tree_from_id(ID *id)
Definition node.cc:3732
bNode * node_find_node_by_name(bNodeTree *ntree, const char *name)
Definition node.cc:2437
static void node_header_region_init(wmWindowManager *, ARegion *region)
static void node_widgets()
static void node_space_subtype_item_extend(bContext *C, EnumPropertyItem **item, int *totitem)
static int node_space_icon_get(const ScrArea *area)
static void node_toolbar_region_draw(const bContext *C, ARegion *region)
static void node_space_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
static void node_group_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
static void node_header_region_draw(const bContext *C, ARegion *region)
bool push_compute_context_for_tree_path(const SpaceNode &snode, ComputeContextBuilder &compute_context_builder)
static void node_foreach_id(SpaceLink *space_link, LibraryForeachIDData *data)
void free_previews(wmWindowManager &wm, SpaceNode &snode)
static SpaceLink * node_duplicate(SpaceLink *sl)
static void node_main_region_init(wmWindowManager *wm, ARegion *region)
static void node_id_remap(ID *old_id, ID *new_id, SpaceNode *snode)
void node_set_cursor(wmWindow &win, ARegion &region, SpaceNode &snode, const float2 &cursor)
static void node_area_refresh(const bContext *C, ScrArea *area)
static bool node_material_drop_poll(bContext *C, wmDrag *drag, const wmEvent *)
void NODE_GGT_backdrop_corner_pin(wmGizmoGroupType *gzgt)
static bool node_mask_drop_poll(bContext *, wmDrag *drag, const wmEvent *)
static int node_space_subtype_get(ScrArea *area)
void NODE_GGT_backdrop_crop(wmGizmoGroupType *gzgt)
static bool node_group_drop_poll(bContext *C, wmDrag *drag, const wmEvent *)
static void node_init(wmWindowManager *, ScrArea *area)
static void node_region_listener(const wmRegionListenerParams *params)
static bool node_id_im_drop_poll(bContext *, wmDrag *drag, const wmEvent *)
float2 space_node_group_offset(const SpaceNode &snode)
static void node_free(SpaceLink *sl)
static bool node_collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *)
std::optional< ObjectAndModifier > get_modifier_for_node_editor(const SpaceNode &snode)
static blender::StringRefNull node_space_name_get(const ScrArea *area)
std::optional< int32_t > find_nested_node_id_in_root(const SpaceNode &snode, const bNode &node)
static void node_dropboxes()
void NODE_GGT_backdrop_transform(wmGizmoGroupType *gzgt)
void snode_set_context(const bContext &C)
Definition node_edit.cc:678
void NODE_GGT_backdrop_sun_beams(wmGizmoGroupType *gzgt)
static void node_area_listener(const wmSpaceTypeListenerParams *params)
static void node_id_im_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
static int node_context(const bContext *C, const char *member, bContextDataResult *result)
static bool node_object_drop_poll(bContext *C, wmDrag *drag, const wmEvent *)
void node_draw_space(const bContext &C, ARegion &region)
static void node_cursor(wmWindow *win, ScrArea *area, ARegion *region)
static void node_buttons_region_draw(const bContext *C, ARegion *region)
static void node_id_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
static void node_toolbar_region_init(wmWindowManager *wm, ARegion *region)
static void node_exit(wmWindowManager *wm, ScrArea *area)
static bool is_compositor_viewer_image_visible(const bContext *C)
static void node_main_region_draw(const bContext *C, ARegion *region)
static bool any_node_uses_id(const bNodeTree *ntree, const ID *id)
static const bNode * group_node_by_name(const bNodeTree &ntree, StringRef name)
static void node_buttons_region_init(wmWindowManager *wm, ARegion *region)
static void node_space_blend_write(BlendWriter *writer, SpaceLink *sl)
static void node_area_tag_tree_recalc(SpaceNode *snode, ScrArea *area)
static SpaceLink * node_create(const ScrArea *, const Scene *)
static void node_space_subtype_set(ScrArea *area, int value)
VecBase< float, 2 > float2
const char * node_context_dir[]
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
void RNA_struct_property_unset(PointerRNA *ptr, const char *identifier)
void RNA_enum_items_add(EnumPropertyItem **items, int *totitem, const EnumPropertyItem *item)
bNodeTree * ED_node_tree_get(SpaceNode *snode, int level)
void ED_node_tree_push(SpaceNode *snode, bNodeTree *ntree, bNode *gnode)
void ED_node_cursor_location_set(SpaceNode *snode, const float value[2])
void ED_node_tree_path_get(SpaceNode *snode, char *value)
int ED_node_tree_depth(SpaceNode *snode)
void ED_node_set_active_viewer_key(SpaceNode *snode)
int ED_node_tree_path_length(SpaceNode *snode)
void ED_node_tree_pop(SpaceNode *snode)
const char * node_context_dir[]
void ED_node_cursor_location_get(const SpaceNode *snode, float value[2])
void ED_spacetype_node()
void ED_node_tree_start(SpaceNode *snode, bNodeTree *ntree, ID *id, ID *from)
Definition space_node.cc:66
bool clip_gizmo_events_by_ui
void(* message_subscribe)(const wmRegionMessageSubscribeParams *params)
void(* cursor)(wmWindow *win, ScrArea *area, ARegion *region)
void(* listener)(const wmRegionListenerParams *params)
void(* draw)(const bContext *C, ARegion *region)
short event_cursor
int(* snap_size)(const ARegion *region, int size, int axis)
void(* init)(wmWindowManager *wm, ARegion *region)
The meta-data of an asset. By creating and giving this for a data-block (ID.asset_data),...
Definition DNA_ID.h:413
short flag
Definition DNA_ID.h:430
char name[66]
Definition DNA_ID.h:425
void * last
void * first
struct bNodeTree * node_group
char tree_idname[64]
SpaceNode_Runtime * runtime
struct ID * from
ListBase regionbase
struct bGPdata * gpd
ListBase treepath
struct bNodeTree * edittree
struct ID * id
struct bNodeTree * geometry_nodes_tool_tree
SpaceNodeOverlay overlay
struct bNodeTree * nodetree
unsigned int value
struct bNodeTree * nodetree
struct bNodeTreePath * next
struct bNodeTreePath * prev
bNodeInstanceKey parent_key
float view_center[2]
bNodeTreeRuntimeHandle * runtime
NodeInstanceHashHandle * previews
bNodeInstanceKey active_viewer_key
char name[64]
int32_t identifier
const AssetRepresentationHandle * asset
Definition WM_types.hh:1195
eWM_DragDataType type
Definition WM_types.hh:1282
PointerRNA * ptr
Definition WM_types.hh:1368
int xy[2]
Definition WM_types.hh:726
unsigned int data
Definition WM_types.hh:325
unsigned int action
Definition WM_types.hh:325
unsigned int category
Definition WM_types.hh:325
void * reference
Definition WM_types.hh:327
struct wmKeyConfig * defaultconf
struct wmEvent * eventstate
wmDropBox * WM_dropbox_add(ListBase *lb, const char *idname, bool(*poll)(bContext *C, wmDrag *drag, const wmEvent *event), void(*copy)(bContext *C, wmDrag *drag, wmDropBox *drop), void(*cancel)(Main *bmain, wmDrag *drag, wmDropBox *drop), WMDropboxTooltipFunc tooltip)
void WM_drag_free_imported_drag_ID(Main *bmain, wmDrag *drag, wmDropBox *drop)
Free asset ID imported for canceled drop.
bool WM_drag_is_ID_type(const wmDrag *drag, int idcode)
ID * WM_drag_get_local_ID_or_import_from_asset(const bContext *C, const wmDrag *drag, int idcode)
ListBase * WM_dropboxmap_find(const char *idname, int spaceid, int regionid)
wmDragAsset * WM_drag_get_asset_data(const wmDrag *drag, int idcode)
ID * WM_drag_get_local_ID(const wmDrag *drag, short idcode)
wmEventHandler_Dropbox * WM_event_add_dropbox_handler(ListBase *handlers, ListBase *dropboxes)
wmEventHandler_Keymap * WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
void WM_main_add_notifier(uint type, void *reference)
wmEventHandler_Keymap * WM_event_add_keymap_handler_v2d_mask(ListBase *handlers, wmKeyMap *keymap)
wmGizmoGroupTypeRef * WM_gizmogrouptype_append_and_link(wmGizmoMapType *gzmap_type, void(*wtfunc)(wmGizmoGroupType *))
void WM_gizmomap_tag_refresh(wmGizmoMap *gzmap)
wmGizmoMapType * WM_gizmomaptype_ensure(const wmGizmoMapType_Params *gzmap_params)
wmKeyMap * WM_keymap_ensure(wmKeyConfig *keyconf, const char *idname, int spaceid, int regionid)
Definition wm_keymap.cc:897
bool WM_menutype_add(MenuType *mt)
bScreen * WM_window_get_active_screen(const wmWindow *win)