Blender V4.3
ed_viewer_path.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "ED_viewer_path.hh"
6#include "ED_screen.hh"
7
9#include "BKE_context.hh"
10#include "BKE_main.hh"
11#include "BKE_node_runtime.hh"
13#include "BKE_workspace.hh"
14
15#include "BLI_listbase.h"
16#include "BLI_string.h"
17#include "BLI_vector.hh"
18
19#include "DNA_modifier_types.h"
20#include "DNA_node_types.h"
22
23#include "DEG_depsgraph.hh"
24
25#include "WM_api.hh"
26
28
29using bke::bNodeTreeZone;
30using bke::bNodeTreeZones;
31
33{
34 switch (zone.output_node->type) {
37 node_elem->sim_output_node_id = zone.output_node->identifier;
38 return &node_elem->base;
39 }
41 const auto &storage = *static_cast<NodeGeometryRepeatOutput *>(zone.output_node->storage);
43 node_elem->repeat_output_node_id = zone.output_node->identifier;
44 node_elem->iteration = storage.inspection_index;
45 return &node_elem->base;
46 }
48 const auto &storage = *static_cast<NodeGeometryForeachGeometryElementOutput *>(
49 zone.output_node->storage);
52 node_elem->zone_output_node_id = zone.output_node->identifier;
53 node_elem->index = storage.inspection_index;
54 return &node_elem->base;
55 }
56 }
58 return nullptr;
59}
60
62 const bNode &node,
63 ViewerPath &r_dst)
64{
65 /* Only valid if the node space has a context object. */
66 BLI_assert(snode.id != nullptr && GS(snode.id->name) == ID_OB);
67
69
70 Object *ob = reinterpret_cast<Object *>(snode.id);
72 id_elem->id = &ob->id;
73 BLI_addtail(&r_dst.path, id_elem);
74
75 NodesModifierData *modifier = nullptr;
77 if (md->type != eModifierType_Nodes) {
78 continue;
79 }
80 NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(md);
81 if (nmd->node_group != snode.nodetree) {
82 continue;
83 }
84 if (snode.flag & SNODE_PIN) {
85 /* If the node group is pinned, use the first matching modifier. This can be improved by
86 * storing the modifier name in the node editor when the context is pinned. */
87 modifier = nmd;
88 break;
89 }
90 if (md->flag & eModifierFlag_Active) {
91 modifier = nmd;
92 }
93 }
94 if (modifier == nullptr) {
95 return;
96 }
98 modifier_elem->modifier_name = BLI_strdup(modifier->modifier.name);
99 BLI_addtail(&r_dst.path, modifier_elem);
100
102 LISTBASE_FOREACH (const bNodeTreePath *, item, &snode.treepath) {
103 tree_path.append(item);
104 }
105
106 for (const int i : tree_path.index_range().drop_back(1)) {
107 bNodeTree *tree = tree_path[i]->nodetree;
108 /* The tree path contains the name of the node but not its ID. */
109 const char *node_name = tree_path[i + 1]->node_name;
110 const bNode *node = bke::node_find_node_by_name(tree, node_name);
111 /* The name in the tree path should match a group node in the tree. Sometimes, the tree-path is
112 * out of date though. */
113 if (node == nullptr) {
114 return;
115 }
116
117 tree->ensure_topology_cache();
118 const bNodeTreeZones *tree_zones = tree->zones();
119 if (!tree_zones) {
120 return;
121 }
122 const Vector<const bNodeTreeZone *> zone_stack = tree_zones->get_zone_stack_for_node(
123 node->identifier);
124 for (const bNodeTreeZone *zone : zone_stack) {
125 ViewerPathElem *zone_elem = viewer_path_elem_for_zone(*zone);
126 BLI_addtail(&r_dst.path, zone_elem);
127 }
128
130 node_elem->node_id = node->identifier;
131 node_elem->base.ui_name = BLI_strdup(node->name);
132 BLI_addtail(&r_dst.path, node_elem);
133 }
134
135 snode.edittree->ensure_topology_cache();
136 const bNodeTreeZones *tree_zones = snode.edittree->zones();
137 if (!tree_zones) {
138 return;
139 }
140 const Vector<const bNodeTreeZone *> zone_stack = tree_zones->get_zone_stack_for_node(
141 node.identifier);
142 for (const bNodeTreeZone *zone : zone_stack) {
143 ViewerPathElem *zone_elem = viewer_path_elem_for_zone(*zone);
144 BLI_addtail(&r_dst.path, zone_elem);
145 }
146
148 viewer_node_elem->node_id = node.identifier;
149 viewer_node_elem->base.ui_name = BLI_strdup(node.name);
150 BLI_addtail(&r_dst.path, viewer_node_elem);
151}
152
153void activate_geometry_node(Main &bmain, SpaceNode &snode, bNode &node)
154{
156 if (wm == nullptr) {
157 return;
158 }
159 for (bNode *iter_node : snode.edittree->all_nodes()) {
160 if (iter_node->type == GEO_NODE_VIEWER) {
161 SET_FLAG_FROM_TEST(iter_node->flag, iter_node == &node, NODE_DO_OUTPUT);
162 }
163 }
164 ViewerPath new_viewer_path{};
165 BLI_SCOPED_DEFER([&]() { BKE_viewer_path_clear(&new_viewer_path); });
166 if (snode.id != nullptr && GS(snode.id->name) == ID_OB) {
167 viewer_path_for_geometry_node(snode, node, new_viewer_path);
168 }
169
170 bool found_view3d_with_enabled_viewer = false;
171 View3D *any_view3d_without_viewer = nullptr;
172 LISTBASE_FOREACH (wmWindow *, window, &wm->windows) {
173 WorkSpace *workspace = BKE_workspace_active_get(window->workspace_hook);
174 bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook);
175 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
176 SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first);
177 if (sl->spacetype == SPACE_SPREADSHEET) {
178 SpaceSpreadsheet &sspreadsheet = *reinterpret_cast<SpaceSpreadsheet *>(sl);
179 if (!(sspreadsheet.flag & SPREADSHEET_FLAG_PINNED)) {
181 }
182 }
183 else if (sl->spacetype == SPACE_VIEW3D) {
184 View3D &v3d = *reinterpret_cast<View3D *>(sl);
185 if (v3d.flag2 & V3D_SHOW_VIEWER) {
186 found_view3d_with_enabled_viewer = true;
187 }
188 else {
189 any_view3d_without_viewer = &v3d;
190 }
191 }
192 }
193
194 /* Enable viewer in one viewport if it is disabled in all of them. */
195 if (!found_view3d_with_enabled_viewer && any_view3d_without_viewer != nullptr) {
196 any_view3d_without_viewer->flag2 |= V3D_SHOW_VIEWER;
197 }
198
200 BKE_viewer_path_copy(&workspace->viewer_path, &new_viewer_path);
201
202 /* Make sure the viewed data becomes available. */
205 }
206}
207
209{
210 if (BLI_listbase_count(&viewer_path.path) != 1) {
211 return nullptr;
212 }
213 const ViewerPathElem *elem = static_cast<ViewerPathElem *>(viewer_path.path.first);
214 if (elem->type != VIEWER_PATH_ELEM_TYPE_ID) {
215 return nullptr;
216 }
217 ID *id = reinterpret_cast<const IDViewerPathElem *>(elem)->id;
218 if (id == nullptr) {
219 return nullptr;
220 }
221 if (GS(id->name) != ID_OB) {
222 return nullptr;
223 }
224 return reinterpret_cast<Object *>(id);
225}
226
227std::optional<ViewerPathForGeometryNodesViewer> parse_geometry_nodes_viewer(
228 const ViewerPath &viewer_path)
229{
231 LISTBASE_FOREACH (const ViewerPathElem *, item, &viewer_path.path) {
232 elems_vec.append(item);
233 }
234
235 if (elems_vec.size() < 3) {
236 /* Need at least the object, modifier and viewer node name. */
237 return std::nullopt;
238 }
239 Span<const ViewerPathElem *> remaining_elems = elems_vec;
240 const ViewerPathElem &id_elem = *remaining_elems[0];
241 if (id_elem.type != VIEWER_PATH_ELEM_TYPE_ID) {
242 return std::nullopt;
243 }
244 ID *root_id = reinterpret_cast<const IDViewerPathElem &>(id_elem).id;
245 if (root_id == nullptr) {
246 return std::nullopt;
247 }
248 if (GS(root_id->name) != ID_OB) {
249 return std::nullopt;
250 }
251 Object *root_ob = reinterpret_cast<Object *>(root_id);
252 remaining_elems = remaining_elems.drop_front(1);
253 const ViewerPathElem &modifier_elem = *remaining_elems[0];
254 if (modifier_elem.type != VIEWER_PATH_ELEM_TYPE_MODIFIER) {
255 return std::nullopt;
256 }
257 const char *modifier_name =
258 reinterpret_cast<const ModifierViewerPathElem &>(modifier_elem).modifier_name;
259 if (modifier_name == nullptr) {
260 return std::nullopt;
261 }
262 remaining_elems = remaining_elems.drop_front(1);
264 for (const ViewerPathElem *elem : remaining_elems.drop_back(1)) {
265 if (!ELEM(elem->type,
270 {
271 return std::nullopt;
272 }
273 node_path.append(elem);
274 }
275 const ViewerPathElem *last_elem = remaining_elems.last();
276 if (last_elem->type != VIEWER_PATH_ELEM_TYPE_VIEWER_NODE) {
277 return std::nullopt;
278 }
279 const int32_t viewer_node_id =
280 reinterpret_cast<const ViewerNodeViewerPathElem *>(last_elem)->node_id;
281 return ViewerPathForGeometryNodesViewer{root_ob, modifier_name, node_path, viewer_node_id};
282}
283
285{
286 const NodesModifierData *modifier = nullptr;
287 LISTBASE_FOREACH (const ModifierData *, md, &parsed_viewer_path.object->modifiers) {
288 if (md->type != eModifierType_Nodes) {
289 continue;
290 }
291 if (md->name != parsed_viewer_path.modifier_name) {
292 continue;
293 }
294 modifier = reinterpret_cast<const NodesModifierData *>(md);
295 break;
296 }
297 if (modifier == nullptr) {
298 return false;
299 }
300 if (modifier->node_group == nullptr) {
301 return false;
302 }
303 const bNodeTree *ngroup = modifier->node_group;
304 const bNodeTreeZone *zone = nullptr;
305 for (const ViewerPathElem *path_elem : parsed_viewer_path.node_path) {
306 ngroup->ensure_topology_cache();
307 const bNodeTreeZones *tree_zones = ngroup->zones();
308 switch (path_elem->type) {
310 const auto &typed_elem = *reinterpret_cast<const SimulationZoneViewerPathElem *>(
311 path_elem);
312 const bNodeTreeZone *next_zone = tree_zones->get_zone_by_node(
313 typed_elem.sim_output_node_id);
314 if (next_zone == nullptr) {
315 return false;
316 }
317 if (next_zone->parent_zone != zone) {
318 return false;
319 }
320 zone = next_zone;
321 break;
322 }
324 const auto &typed_elem = *reinterpret_cast<const RepeatZoneViewerPathElem *>(path_elem);
325 const bNodeTreeZone *next_zone = tree_zones->get_zone_by_node(
326 typed_elem.repeat_output_node_id);
327 if (next_zone == nullptr) {
328 return false;
329 }
330 if (next_zone->parent_zone != zone) {
331 return false;
332 }
333 zone = next_zone;
334 break;
335 }
337 const auto &typed_elem =
338 *reinterpret_cast<const ForeachGeometryElementZoneViewerPathElem *>(path_elem);
339 const bNodeTreeZone *next_zone = tree_zones->get_zone_by_node(
340 typed_elem.zone_output_node_id);
341 if (next_zone == nullptr) {
342 return false;
343 }
344 if (next_zone->parent_zone != zone) {
345 return false;
346 }
347 zone = next_zone;
348 break;
349 }
351 const auto &typed_elem = *reinterpret_cast<const GroupNodeViewerPathElem *>(path_elem);
352 const bNode *group_node = ngroup->node_by_id(typed_elem.node_id);
353 if (group_node == nullptr) {
354 return false;
355 }
356 const bNodeTreeZone *parent_zone = tree_zones->get_zone_by_node(typed_elem.node_id);
357 if (parent_zone != zone) {
358 return false;
359 }
360 if (group_node->id == nullptr) {
361 return false;
362 }
363 ngroup = reinterpret_cast<const bNodeTree *>(group_node->id);
364 zone = nullptr;
365 break;
366 }
367 default: {
369 }
370 }
371 }
372
373 const bNode *viewer_node = ngroup->node_by_id(parsed_viewer_path.viewer_node_id);
374 if (viewer_node == nullptr) {
375 return false;
376 }
377 const bNodeTreeZones *tree_zones = ngroup->zones();
378 if (tree_zones == nullptr) {
379 return false;
380 }
381 if (tree_zones->get_zone_by_node(viewer_node->identifier) != zone) {
382 return false;
383 }
384 return true;
385}
386
388 ViewerPath &viewer_path)
389{
390 if (BLI_listbase_is_empty(&viewer_path.path)) {
392 }
393 const ViewerPathElem *last_elem = static_cast<ViewerPathElem *>(viewer_path.path.last);
394 if (last_elem->type != VIEWER_PATH_ELEM_TYPE_VIEWER_NODE) {
396 }
397 const int32_t viewer_node_id =
398 reinterpret_cast<const ViewerNodeViewerPathElem *>(last_elem)->node_id;
399
400 const Main *bmain = CTX_data_main(&C);
401 const wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
402 if (wm == nullptr) {
404 }
405 LISTBASE_FOREACH (const wmWindow *, window, &wm->windows) {
406 const bScreen *active_screen = BKE_workspace_active_screen_get(window->workspace_hook);
407 Vector<const bScreen *> screens = {active_screen};
408 if (ELEM(active_screen->state, SCREENMAXIMIZED, SCREENFULL)) {
409 const ScrArea *area = static_cast<ScrArea *>(active_screen->areabase.first);
410 screens.append(area->full);
411 }
412 for (const bScreen *screen : screens) {
413 LISTBASE_FOREACH (const ScrArea *, area, &screen->areabase) {
414 const SpaceLink *sl = static_cast<SpaceLink *>(area->spacedata.first);
415 if (sl == nullptr) {
416 continue;
417 }
418 if (sl->spacetype != SPACE_NODE) {
419 continue;
420 }
421 const SpaceNode &snode = *reinterpret_cast<const SpaceNode *>(sl);
422 if (snode.edittree == nullptr) {
423 continue;
424 }
425 if (snode.edittree->type != NTREE_GEOMETRY) {
426 continue;
427 }
428 if (!snode.id) {
429 continue;
430 }
431 snode.edittree->ensure_topology_cache();
432 const bNode *viewer_node = snode.edittree->node_by_id(viewer_node_id);
433 if (viewer_node == nullptr) {
434 continue;
435 }
436 if (!(viewer_node->flag & NODE_DO_OUTPUT)) {
437 continue;
438 }
439 ViewerPath tmp_viewer_path{};
440 BLI_SCOPED_DEFER([&]() { BKE_viewer_path_clear(&tmp_viewer_path); });
441 viewer_path_for_geometry_node(snode, *viewer_node, tmp_viewer_path);
443 &viewer_path, &tmp_viewer_path, VIEWER_PATH_EQUAL_FLAG_IGNORE_ITERATION))
444 {
445 continue;
446 }
447 if (!BKE_viewer_path_equal(&viewer_path, &tmp_viewer_path)) {
448 std::swap(viewer_path, tmp_viewer_path);
449 /* Make sure the viewed data becomes available. */
452 }
454 }
455 }
456 }
458}
459
461{
462 /* Viewer path is only valid if the context object is set. */
463 if (snode.id == nullptr || GS(snode.id->name) != ID_OB) {
464 return nullptr;
465 }
466
467 const std::optional<ViewerPathForGeometryNodesViewer> parsed_viewer_path =
468 parse_geometry_nodes_viewer(viewer_path);
469 if (!parsed_viewer_path.has_value()) {
470 return nullptr;
471 }
472
473 snode.edittree->ensure_topology_cache();
474 bNode *possible_viewer = snode.edittree->node_by_id(parsed_viewer_path->viewer_node_id);
475 if (possible_viewer == nullptr) {
476 return nullptr;
477 }
478 ViewerPath tmp_viewer_path;
479 BLI_SCOPED_DEFER([&]() { BKE_viewer_path_clear(&tmp_viewer_path); });
480 viewer_path_for_geometry_node(snode, *possible_viewer, tmp_viewer_path);
481
482 if (BKE_viewer_path_equal(&viewer_path, &tmp_viewer_path)) {
483 return possible_viewer;
484 }
485 return nullptr;
486}
487
489 const ViewerPathElem &elem_generic, ComputeContextBuilder &compute_context_builder)
490{
491 switch (ViewerPathElemType(elem_generic.type)) {
494 return false;
495 }
497 const auto &elem = reinterpret_cast<const ModifierViewerPathElem &>(elem_generic);
498 compute_context_builder.push<bke::ModifierComputeContext>(elem.modifier_name);
499 return true;
500 }
502 const auto &elem = reinterpret_cast<const GroupNodeViewerPathElem &>(elem_generic);
503 compute_context_builder.push<bke::GroupNodeComputeContext>(elem.node_id);
504 return true;
505 }
507 const auto &elem = reinterpret_cast<const SimulationZoneViewerPathElem &>(elem_generic);
508 compute_context_builder.push<bke::SimulationZoneComputeContext>(elem.sim_output_node_id);
509 return true;
510 }
512 const auto &elem = reinterpret_cast<const RepeatZoneViewerPathElem &>(elem_generic);
513 compute_context_builder.push<bke::RepeatZoneComputeContext>(elem.repeat_output_node_id,
514 elem.iteration);
515 return true;
516 }
518 const auto &elem = reinterpret_cast<const ForeachGeometryElementZoneViewerPathElem &>(
519 elem_generic);
521 elem.zone_output_node_id, elem.index);
522 return true;
523 }
524 }
525 return false;
526}
527
528} // namespace blender::ed::viewer_path
Main * CTX_data_main(const bContext *C)
#define GEO_NODE_FOREACH_GEOMETRY_ELEMENT_OUTPUT
Definition BKE_node.hh:1379
#define GEO_NODE_SIMULATION_OUTPUT
Definition BKE_node.hh:1331
#define GEO_NODE_REPEAT_OUTPUT
Definition BKE_node.hh:1338
void BKE_viewer_path_copy(ViewerPath *dst, const ViewerPath *src)
ViewerNodeViewerPathElem * BKE_viewer_path_elem_new_viewer_node()
@ VIEWER_PATH_EQUAL_FLAG_IGNORE_ITERATION
void BKE_viewer_path_clear(ViewerPath *viewer_path)
void BKE_viewer_path_init(ViewerPath *viewer_path)
SimulationZoneViewerPathElem * BKE_viewer_path_elem_new_simulation_zone()
ForeachGeometryElementZoneViewerPathElem * BKE_viewer_path_elem_new_foreach_geometry_element_zone()
GroupNodeViewerPathElem * BKE_viewer_path_elem_new_group_node()
IDViewerPathElem * BKE_viewer_path_elem_new_id()
ModifierViewerPathElem * BKE_viewer_path_elem_new_modifier()
RepeatZoneViewerPathElem * BKE_viewer_path_elem_new_repeat_zone()
bool BKE_viewer_path_equal(const ViewerPath *a, const ViewerPath *b, ViewerPathEqualFlag flag=ViewerPathEqualFlag(0))
bScreen * BKE_workspace_active_screen_get(const WorkSpaceInstanceHook *hook) GETTER_ATTRS
Definition workspace.cc:613
WorkSpace * BKE_workspace_active_get(WorkSpaceInstanceHook *hook) GETTER_ATTRS
Definition workspace.cc:562
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define BLI_SCOPED_DEFER(function_to_defer)
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ ID_OB
@ eModifierFlag_Active
@ eModifierType_Nodes
@ NTREE_GEOMETRY
@ NODE_DO_OUTPUT
@ SCREENFULL
@ SCREENMAXIMIZED
@ SNODE_PIN
@ SPACE_NODE
@ SPACE_SPREADSHEET
@ SPACE_VIEW3D
@ SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE
@ SPREADSHEET_FLAG_PINNED
@ V3D_SHOW_VIEWER
@ VIEWER_PATH_ELEM_TYPE_GROUP_NODE
@ VIEWER_PATH_ELEM_TYPE_SIMULATION_ZONE
@ VIEWER_PATH_ELEM_TYPE_FOREACH_GEOMETRY_ELEMENT_ZONE
@ VIEWER_PATH_ELEM_TYPE_VIEWER_NODE
@ VIEWER_PATH_ELEM_TYPE_REPEAT_ZONE
@ VIEWER_PATH_ELEM_TYPE_MODIFIER
@ VIEWER_PATH_ELEM_TYPE_ID
#define NC_VIEWER_PATH
Definition WM_types.hh:373
constexpr IndexRange drop_back(int64_t n) const
constexpr Span drop_front(int64_t n) const
Definition BLI_span.hh:172
constexpr Span drop_back(int64_t n) const
Definition BLI_span.hh:183
constexpr const T & last(const int64_t n=0) const
Definition BLI_span.hh:326
int64_t size() const
void append(const T &value)
IndexRange index_range() const
Vector< const bNodeTreeZone * > get_zone_stack_for_node(const int32_t node_id) const
const bNodeTreeZone * get_zone_by_node(const int32_t node_id) const
Vector< std::unique_ptr< bNodeTreeZone > > zones
KDTree_3d * tree
#define GS(x)
Definition iris.cc:202
static const char * modifier_name[LS_MODIFIER_NUM]
Definition linestyle.cc:680
bNode * node_find_node_by_name(bNodeTree *ntree, const char *name)
Definition node.cc:2437
bool add_compute_context_for_viewer_path_elem(const ViewerPathElem &elem, ComputeContextBuilder &compute_context_builder)
Object * parse_object_only(const ViewerPath &viewer_path)
static ViewerPathElem * viewer_path_elem_for_zone(const bNodeTreeZone &zone)
UpdateActiveGeometryNodesViewerResult update_active_geometry_nodes_viewer(const bContext &C, ViewerPath &viewer_path)
std::optional< ViewerPathForGeometryNodesViewer > parse_geometry_nodes_viewer(const ViewerPath &viewer_path)
void activate_geometry_node(Main &bmain, SpaceNode &snode, bNode &node)
static void viewer_path_for_geometry_node(const SpaceNode &snode, const bNode &node, ViewerPath &r_dst)
bool exists_geometry_nodes_viewer(const ViewerPathForGeometryNodesViewer &parsed_viewer_path)
bNode * find_geometry_nodes_viewer(const ViewerPath &viewer_path, SpaceNode &snode)
signed int int32_t
Definition stdint.h:77
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
void * last
void * first
ListBase wm
Definition BKE_main.hh:239
struct bNodeTree * node_group
ListBase modifiers
ListBase treepath
struct bNodeTree * edittree
struct ID * id
struct bNodeTree * nodetree
ViewerPath viewer_path
struct ID * id
int32_t identifier
ListBase areabase
blender::Vector< const ViewerPathElem * > node_path
void WM_main_add_notifier(uint type, void *reference)