Refactor: Nodes: Generalize node tree subtypes

This patch generalizes node tree subtypes to be usable for node trees
other than Geometry Nodes. In particular, this:

- Renames SpaceNode.geometry_nodes_type to node_tree_sub_type, which now
  store a tree type-specific enum.
- Renames SpaceNode.geometry_nodes_tool_tree to selected_node_group,
  which now stores any context-less tree of any type.

This breaks the python API due to renaming.

Pull Request: https://projects.blender.org/blender/blender/pulls/144544
This commit is contained in:
Omar Emara
2025-08-21 09:04:13 +02:00
committed by Omar Emara
parent 40f8a36fd5
commit 3d7c8d022e
15 changed files with 60 additions and 49 deletions

View File

@@ -353,11 +353,11 @@ class NewGeometryNodeGroupTool(Operator):
@classmethod
def poll(cls, context):
space = context.space_data
return space and space.type == 'NODE_EDITOR' and space.geometry_nodes_type == 'TOOL'
return space and space.type == 'NODE_EDITOR' and space.node_tree_sub_type == 'TOOL'
def execute(self, context):
group = geometry_node_group_empty_tool_new(context)
context.space_data.geometry_nodes_tool_tree = group
context.space_data.selected_node_group = group
return {'FINISHED'}

View File

@@ -229,7 +229,7 @@ class NODE_MT_geometry_node_GEO_GEOMETRY_READ(Menu):
node_add_menu.add_node_type(layout, "GeometryNodeInputNormal")
node_add_menu.add_node_type(layout, "GeometryNodeInputPosition", search_weight=1.0)
node_add_menu.add_node_type(layout, "GeometryNodeInputRadius")
if context.space_data.geometry_nodes_type == 'TOOL':
if context.space_data.node_tree_sub_type == 'TOOL':
node_add_menu.add_node_type(layout, "GeometryNodeToolSelection")
node_add_menu.add_node_type(layout, "GeometryNodeToolActiveElement")
node_add_menu.draw_assets_for_catalog(layout, "Geometry/Read")
@@ -244,7 +244,7 @@ class NODE_MT_geometry_node_GEO_GEOMETRY_WRITE(Menu):
node_add_menu.add_node_type(layout, "GeometryNodeSetGeometryName")
node_add_menu.add_node_type(layout, "GeometryNodeSetID")
node_add_menu.add_node_type(layout, "GeometryNodeSetPosition", search_weight=1.0)
if context.space_data.geometry_nodes_type == 'TOOL':
if context.space_data.node_tree_sub_type == 'TOOL':
node_add_menu.add_node_type(layout, "GeometryNodeToolSetSelection")
node_add_menu.draw_assets_for_catalog(layout, "Geometry/Write")
@@ -291,7 +291,7 @@ class NODE_MT_geometry_node_GEO_INPUT(Menu):
def draw(self, context):
layout = self.layout
layout.menu("NODE_MT_geometry_node_GEO_INPUT_CONSTANT")
if context.space_data.geometry_nodes_type != 'TOOL':
if context.space_data.node_tree_sub_type != 'TOOL':
layout.menu("NODE_MT_geometry_node_GEO_INPUT_GIZMO")
layout.menu("NODE_MT_geometry_node_GEO_INPUT_GROUP")
layout.menu("NODE_MT_category_import")
@@ -336,7 +336,7 @@ class NODE_MT_geometry_node_GEO_INPUT_SCENE(Menu):
def draw(self, context):
layout = self.layout
if context.space_data.geometry_nodes_type == 'TOOL':
if context.space_data.node_tree_sub_type == 'TOOL':
node_add_menu.add_node_type(layout, "GeometryNodeTool3DCursor")
node_add_menu.add_node_type(layout, "GeometryNodeInputActiveCamera")
node_add_menu.add_node_type_with_outputs(
@@ -358,7 +358,7 @@ class NODE_MT_geometry_node_GEO_INPUT_SCENE(Menu):
node_add_menu.add_node_type(layout, "GeometryNodeCollectionInfo")
node_add_menu.add_node_type(layout, "GeometryNodeImageInfo")
node_add_menu.add_node_type(layout, "GeometryNodeIsViewport")
if context.space_data.geometry_nodes_type == 'TOOL':
if context.space_data.node_tree_sub_type == 'TOOL':
node_add_menu.add_node_type_with_outputs(
context, layout, "GeometryNodeToolMousePosition",
["Mouse X", "Mouse Y", "Region Width", "Region Height"],
@@ -366,7 +366,7 @@ class NODE_MT_geometry_node_GEO_INPUT_SCENE(Menu):
node_add_menu.add_node_type(layout, "GeometryNodeObjectInfo")
node_add_menu.add_node_type_with_outputs(context, layout, "GeometryNodeInputSceneTime", ["Frame", "Seconds"])
node_add_menu.add_node_type(layout, "GeometryNodeSelfObject")
if context.space_data.geometry_nodes_type == 'TOOL':
if context.space_data.node_tree_sub_type == 'TOOL':
node_add_menu.add_node_type_with_outputs(
context, layout, "GeometryNodeViewportTransform",
["Projection", "View", "Is Orthographic"],
@@ -454,7 +454,7 @@ class NODE_MT_geometry_node_GEO_MESH_READ(Menu):
node_add_menu.add_node_type(layout, "GeometryNodeInputMeshFaceArea")
node_add_menu.add_node_type(layout, "GeometryNodeMeshFaceSetBoundaries")
node_add_menu.add_node_type(layout, "GeometryNodeInputMeshFaceNeighbors")
if context.space_data.geometry_nodes_type == 'TOOL':
if context.space_data.node_tree_sub_type == 'TOOL':
node_add_menu.add_node_type(layout, "GeometryNodeToolFaceSet")
node_add_menu.add_node_type(layout, "GeometryNodeInputMeshFaceIsPlanar")
node_add_menu.add_node_type(layout, "GeometryNodeInputShadeSmooth")
@@ -482,7 +482,7 @@ class NODE_MT_geometry_node_GEO_MESH_WRITE(Menu):
def draw(self, context):
layout = self.layout
if context.space_data.geometry_nodes_type == 'TOOL':
if context.space_data.node_tree_sub_type == 'TOOL':
node_add_menu.add_node_type(layout, "GeometryNodeToolSetFaceSet")
node_add_menu.add_node_type(layout, "GeometryNodeSetMeshNormal")
node_add_menu.add_node_type(layout, "GeometryNodeSetShadeSmooth")

View File

@@ -151,11 +151,11 @@ class NODE_HT_header(Header):
row.template_ID(scene, "compositing_node_group", new="node.new_compositing_node_group")
elif snode.tree_type == 'GeometryNodeTree':
layout.prop(snode, "geometry_nodes_type", text="")
layout.prop(snode, "node_tree_sub_type", text="")
NODE_MT_editor_menus.draw_collapsible(context, layout)
layout.separator_spacer()
if snode.geometry_nodes_type == 'MODIFIER':
if snode.node_tree_sub_type == 'MODIFIER':
ob = context.object
row = layout.row()
@@ -172,7 +172,7 @@ class NODE_HT_header(Header):
else:
row.template_ID(snode, "node_tree", new="node.new_geometry_nodes_modifier")
else:
layout.template_ID(snode, "geometry_nodes_tool_tree", new="node.new_geometry_node_group_tool")
layout.template_ID(snode, "selected_node_group", new="node.new_geometry_node_group_tool")
if snode.node_tree:
layout.popover(panel="NODE_PT_geometry_node_tool_object_types", text="Types")
layout.popover(panel="NODE_PT_geometry_node_tool_mode", text="Modes")

View File

@@ -163,7 +163,7 @@ static void find_socket_log_contexts(const Main &bmain,
if (snode.edittree == nullptr) {
continue;
}
if (snode.geometry_nodes_type != SNODE_GEOMETRY_TOOL) {
if (snode.node_tree_sub_type != SNODE_GEOMETRY_TOOL) {
continue;
}
bke::ComputeContextCache compute_context_cache;

View File

@@ -145,7 +145,7 @@ static void get_context_path_node_geometry(const bContext &C,
SpaceNode &snode,
Vector<ui::ContextPathItem> &path)
{
if (snode.flag & SNODE_PIN || snode.geometry_nodes_type == SNODE_GEOMETRY_TOOL) {
if (snode.flag & SNODE_PIN || snode.node_tree_sub_type == SNODE_GEOMETRY_TOOL) {
context_path_add_node_tree_and_node_groups(snode, path);
}
else {

View File

@@ -482,6 +482,10 @@ void ED_node_set_tree_type(SpaceNode *snode, blender::bke::bNodeTreeType *typein
else {
snode->tree_idname[0] = '\0';
}
/* Reset members that store tree type-dependant values. */
snode->node_tree_sub_type = 0;
snode->selected_node_group = nullptr;
}
bool ED_node_is_compositor(const SpaceNode *snode)

View File

@@ -913,7 +913,7 @@ static bool node_active_link_viewer_poll(bContext *C)
return true;
}
if (ED_node_is_geometry(snode)) {
if (snode->geometry_nodes_type == SNODE_GEOMETRY_TOOL) {
if (snode->node_tree_sub_type == SNODE_GEOMETRY_TOOL) {
/* The viewer node is not supported in the "Tool" context. */
return false;
}

View File

@@ -323,7 +323,7 @@ std::optional<nodes::FoundNestedNodeID> find_nested_node_id_in_root(
std::optional<ObjectAndModifier> get_modifier_for_node_editor(const SpaceNode &snode)
{
if (snode.geometry_nodes_type != SNODE_GEOMETRY_MODIFIER) {
if (snode.node_tree_sub_type != SNODE_GEOMETRY_MODIFIER) {
return std::nullopt;
}
if (snode.id == nullptr) {
@@ -475,7 +475,7 @@ static std::optional<const ComputeContext *> compute_context_for_tree_path(
static const ComputeContext *get_node_editor_root_compute_context(
const SpaceNode &snode, bke::ComputeContextCache &compute_context_cache)
{
switch (SpaceNodeGeometryNodesType(snode.geometry_nodes_type)) {
switch (SpaceNodeGeometryNodesType(snode.node_tree_sub_type)) {
case SNODE_GEOMETRY_MODIFIER: {
std::optional<ed::space_node::ObjectAndModifier> object_and_modifier =
ed::space_node::get_modifier_for_node_editor(snode);
@@ -1515,9 +1515,9 @@ static void node_id_remap(ID *old_id, ID *new_id, SpaceNode *snode)
}
else if (GS(old_id->name) == ID_NT) {
if (snode->geometry_nodes_tool_tree) {
if (&snode->geometry_nodes_tool_tree->id == old_id) {
snode->geometry_nodes_tool_tree = reinterpret_cast<bNodeTree *>(new_id);
if (snode->selected_node_group) {
if (&snode->selected_node_group->id == old_id) {
snode->selected_node_group = reinterpret_cast<bNodeTree *>(new_id);
}
}
@@ -1618,7 +1618,7 @@ static void node_foreach_id(SpaceLink *space_link, LibraryForeachIDData *data)
}
BKE_LIB_FOREACHID_PROCESS_IDSUPER(
data, snode->geometry_nodes_tool_tree, IDWALK_CB_USER_ONE | IDWALK_CB_DIRECT_WEAK_LINK);
data, snode->selected_node_group, IDWALK_CB_USER_ONE | IDWALK_CB_DIRECT_WEAK_LINK);
/* Both `snode->id` and `snode->nodetree` have been remapped now, so their data can be
* accessed. */

View File

@@ -873,7 +873,7 @@ typedef enum eSpaceNode_ShaderFrom {
SNODE_SHADER_LINESTYLE = 2,
} eSpaceNode_ShaderFrom;
/** #SpaceNode.geometry_nodes_type */
/** #SpaceNode.nodes_type */
typedef enum SpaceNodeGeometryNodesType {
SNODE_GEOMETRY_MODIFIER = 0,
SNODE_GEOMETRY_TOOL = 1,

View File

@@ -872,17 +872,18 @@ typedef struct SpaceNode {
/** Shader from object or world (#eSpaceNode_ShaderFrom). */
char shaderfrom;
/**
* Whether to edit any geometry node group, or follow the active modifier context.
* #SpaceNodeGeometryNodesType.
* The sub type of the node tree being edited.
* #SpaceNodeGeometryNodesType or #SpaceNodeCompositorNodesType.
*/
char geometry_nodes_type;
char node_tree_sub_type;
/**
* Used as the editor's top-level node group for #SNODE_GEOMETRY_TOOL. This is stored in the
* node editor because it isn't part of the context otherwise, and it isn't meant to be set
* separately from the editor's regular node group.
* Used as the editor's top-level node group for node trees that are not part of the context and
* thus needs to be stored in the node editor. For instance #SNODE_GEOMETRY_MODIFIER is part of
* the context since it is stored on the active modifier, while #SNODE_GEOMETRY_TOOL is not part
* of the context.
*/
struct bNodeTree *geometry_nodes_tool_tree;
struct bNodeTree *selected_node_group;
/** Grease-pencil data. */
struct bGPdata *gpd;

View File

@@ -202,6 +202,8 @@ DNA_STRUCT_RENAME_MEMBER(SceneEEVEE, motion_blur_shutter, motion_blur_shutter_de
DNA_STRUCT_RENAME_MEMBER(SceneEEVEE, shadow_cube_size, shadow_cube_size_deprecated)
DNA_STRUCT_RENAME_MEMBER(Sculpt, radial_symm, radial_symm_legacy)
DNA_STRUCT_RENAME_MEMBER(SpaceImage, pixel_snap_mode, pixel_round_mode)
DNA_STRUCT_RENAME_MEMBER(SpaceNode, geometry_nodes_type, node_tree_sub_type)
DNA_STRUCT_RENAME_MEMBER(SpaceNode, geometry_nodes_tool_tree, selected_node_group)
DNA_STRUCT_RENAME_MEMBER(SpaceSeq, overlay_type, overlay_frame_type)
DNA_STRUCT_RENAME_MEMBER(SpeedControlVars, globalSpeed, globalSpeed_legacy)
DNA_STRUCT_RENAME_MEMBER(Strip, endstill, endstill_legacy)

View File

@@ -2701,8 +2701,8 @@ static void rna_SpaceNodeEditor_node_tree_set(PointerRNA *ptr,
ED_node_tree_start(region, snode, (bNodeTree *)value.data, nullptr, nullptr);
}
static bool rna_SpaceNodeEditor_geometry_nodes_tool_tree_poll(PointerRNA * /*ptr*/,
const PointerRNA value)
static bool rna_SpaceNodeEditor_selected_node_group_poll(PointerRNA * /*ptr*/,
const PointerRNA value)
{
const bNodeTree &ntree = *static_cast<const bNodeTree *>(value.data);
if (ntree.type != NTREE_GEOMETRY) {
@@ -2719,7 +2719,7 @@ static bool rna_SpaceNodeEditor_geometry_nodes_tool_tree_poll(PointerRNA * /*ptr
static bool space_node_node_geometry_nodes_poll(const SpaceNode &snode, const bNodeTree &ntree)
{
switch (SpaceNodeGeometryNodesType(snode.geometry_nodes_type)) {
switch (SpaceNodeGeometryNodesType(snode.node_tree_sub_type)) {
case SNODE_GEOMETRY_MODIFIER:
if (!ntree.geometry_node_asset_traits) {
return false;
@@ -2762,12 +2762,12 @@ static void rna_SpaceNodeEditor_node_tree_update(const bContext *C, PointerRNA *
blender::ed::space_node::tree_update(C);
}
static void rna_SpaceNodeEditor_geometry_nodes_type_update(Main * /*main*/,
Scene * /*scene*/,
PointerRNA *ptr)
static void rna_SpaceNodeEditor_node_tree_sub_type_update(Main * /*main*/,
Scene * /*scene*/,
PointerRNA *ptr)
{
SpaceNode *snode = static_cast<SpaceNode *>(ptr->data);
if (snode->geometry_nodes_type == SNODE_GEOMETRY_TOOL) {
if (snode->node_tree_sub_type == SNODE_GEOMETRY_TOOL) {
snode->flag &= ~SNODE_PIN;
}
}
@@ -8101,13 +8101,13 @@ static void rna_def_space_node(BlenderRNA *brna)
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, nullptr);
prop = RNA_def_property(srna, "geometry_nodes_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "geometry_nodes_type");
prop = RNA_def_property(srna, "node_tree_sub_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "node_tree_sub_type");
RNA_def_property_enum_items(prop, geometry_nodes_type_items);
RNA_def_property_ui_text(prop, "Geometry Nodes Type", "");
RNA_def_property_ui_text(prop, "Node Tree Sub-Type", "");
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID);
RNA_def_property_update(
prop, NC_SPACE | ND_SPACE_NODE, "rna_SpaceNodeEditor_geometry_nodes_type_update");
prop, NC_SPACE | ND_SPACE_NODE, "rna_SpaceNodeEditor_node_tree_sub_type_update");
prop = RNA_def_property(srna, "id", PROP_POINTER, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
@@ -8156,11 +8156,11 @@ static void rna_def_space_node(BlenderRNA *brna)
RNA_def_property_update(
prop, NC_SPACE | ND_SPACE_NODE_VIEW, "rna_SpaceNodeEditor_show_backdrop_update");
prop = RNA_def_property(srna, "geometry_nodes_tool_tree", PROP_POINTER, PROP_NONE);
prop = RNA_def_property(srna, "selected_node_group", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_funcs(
prop, nullptr, nullptr, nullptr, "rna_SpaceNodeEditor_geometry_nodes_tool_tree_poll");
prop, nullptr, nullptr, nullptr, "rna_SpaceNodeEditor_selected_node_group_poll");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_CONTEXT_UPDATE);
RNA_def_property_ui_text(prop, "Node Tool Tree", "Node group to edit as node tool");
RNA_def_property_ui_text(prop, "Selected Node Group", "Node group to edit");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, "rna_SpaceNodeEditor_node_tree_update");
prop = RNA_def_property(srna, "show_annotation", PROP_BOOLEAN, PROP_NONE);

View File

@@ -34,8 +34,12 @@ static void geometry_node_tree_get_from_context(const bContext *C,
ID **r_from)
{
const SpaceNode *snode = CTX_wm_space_node(C);
if (snode->geometry_nodes_type == SNODE_GEOMETRY_TOOL) {
*r_ntree = snode->geometry_nodes_tool_tree;
if (snode->node_tree_sub_type == SNODE_GEOMETRY_TOOL) {
if (snode->selected_node_group && snode->selected_node_group->type == NTREE_GEOMETRY) {
*r_ntree = snode->selected_node_group;
return;
}
*r_ntree = nullptr;
return;
}

View File

@@ -35,7 +35,7 @@ bool check_tool_context_and_error(GeoNodeExecParams &params)
void search_link_ops_for_tool_node(GatherLinkSearchOpParams &params)
{
if (params.space_node().geometry_nodes_type == SNODE_GEOMETRY_TOOL) {
if (params.space_node().node_tree_sub_type == SNODE_GEOMETRY_TOOL) {
search_link_ops_for_basic_node(params);
}
}

View File

@@ -907,7 +907,7 @@ Map<const bNodeTreeZone *, ComputeContextHash> GeoNodesLog::
static GeoNodesLog *get_root_log(const SpaceNode &snode)
{
switch (SpaceNodeGeometryNodesType(snode.geometry_nodes_type)) {
switch (SpaceNodeGeometryNodesType(snode.node_tree_sub_type)) {
case SNODE_GEOMETRY_MODIFIER: {
std::optional<ed::space_node::ObjectAndModifier> object_and_modifier =
ed::space_node::get_modifier_for_node_editor(snode);
@@ -919,7 +919,7 @@ static GeoNodesLog *get_root_log(const SpaceNode &snode)
case SNODE_GEOMETRY_TOOL: {
const ed::geometry::GeoOperatorLog &log =
ed::geometry::node_group_operator_static_eval_log();
if (snode.geometry_nodes_tool_tree->id.name + 2 != log.node_group_name) {
if (snode.selected_node_group->id.name + 2 != log.node_group_name) {
return {};
}
return log.log.get();