Shading Nodes: Refactor Add menu

Refactor the Add menu in Shading nodes, with manually created menus,
inspired by Geometry Nodes and more recently the Compositor.

Minor sorting adjustments by splitting categories in groups, with
separators in between groups, and sorted alphabetically. Shading
node group assets are also populated in the menus.

This is the first part of the re-organization of the Add menu,
part of #111746

Pull Request: https://projects.blender.org/blender/blender/pulls/111798
This commit is contained in:
Pablo Vazquez
2023-09-01 20:46:12 +02:00
committed by Pablo Vazquez
parent b36367e663
commit 9db4c0ca4b
6 changed files with 285 additions and 21 deletions

View File

@@ -15,6 +15,7 @@ _modules = [
"node_add_menu",
"node_add_menu_compositor",
"node_add_menu_geometry",
"node_add_menu_shading",
"properties_animviz",
"properties_constraint",
"properties_data_armature",

View File

@@ -3,22 +3,26 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from bpy.types import Menu
from bl_ui import node_add_menu
from bpy.app.translations import (
pgettext_iface as iface_,
contexts as i18n_contexts,
)
def add_node_type(layout, node_type, *, label=None):
def add_node_type(layout, node_type, *, label=None, poll=None):
"""Add a node type to a menu."""
bl_rna = bpy.types.Node.bl_rna_get_subclass(node_type)
if not label:
label = bl_rna.name if bl_rna else iface_("Unknown")
translation_context = bl_rna.translation_context if bl_rna else i18n_contexts.default
props = layout.operator("node.add_node", text=label, text_ctxt=translation_context)
props.type = node_type
props.use_transform = True
return props
if poll == True or poll == None:
translation_context = bl_rna.translation_context if bl_rna else i18n_contexts.default
props = layout.operator("node.add_node", text=label, text_ctxt=translation_context)
props.type = node_type
props.use_transform = True
return props
def draw_node_group_add_menu(context, layout):
@@ -71,7 +75,19 @@ def add_repeat_zone(layout, label):
return props
class NODE_MT_category_layout(Menu):
bl_idname = "NODE_MT_category_layout"
bl_label = "Layout"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "NodeFrame")
node_add_menu.add_node_type(layout, "NodeReroute")
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
classes = (
NODE_MT_category_layout,
)
if __name__ == "__main__": # only for live edit.

View File

@@ -336,7 +336,7 @@ class NODE_MT_compositing_node_add_all(Menu):
layout.menu("NODE_MT_category_COMP_VECTOR")
layout.separator()
layout.menu("NODE_MT_category_COMP_GROUP")
layout.menu("NODE_MT_category_COMP_LAYOUT")
layout.menu("NODE_MT_category_layout")
node_add_menu.draw_root_assets(layout)
@@ -359,7 +359,6 @@ classes = (
NODE_MT_category_COMP_UTIL,
NODE_MT_category_COMP_VECTOR,
NODE_MT_category_COMP_GROUP,
NODE_MT_category_COMP_LAYOUT,
)
if __name__ == "__main__": # only for live edit.

View File

@@ -653,17 +653,6 @@ class NODE_MT_category_GEO_GROUP(Menu):
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_category_GEO_LAYOUT(Menu):
bl_idname = "NODE_MT_category_GEO_LAYOUT"
bl_label = "Layout"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "NodeFrame")
node_add_menu.add_node_type(layout, "NodeReroute")
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_geometry_node_add_all(Menu):
bl_idname = "NODE_MT_geometry_node_add_all"
bl_label = ""
@@ -690,7 +679,7 @@ class NODE_MT_geometry_node_add_all(Menu):
layout.menu("NODE_MT_category_GEO_UTILITIES")
layout.separator()
layout.menu("NODE_MT_category_GEO_GROUP")
layout.menu("NODE_MT_category_GEO_LAYOUT")
layout.menu("NODE_MT_category_layout")
node_add_menu.draw_root_assets(layout)
@@ -736,7 +725,6 @@ classes = (
NODE_MT_category_GEO_UTILITIES_MATH,
NODE_MT_category_GEO_UTILITIES_ROTATION,
NODE_MT_category_GEO_GROUP,
NODE_MT_category_GEO_LAYOUT,
)
if __name__ == "__main__": # only for live edit.

View File

@@ -0,0 +1,256 @@
# SPDX-FileCopyrightText: 2022-2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from bpy.types import Menu
from bl_ui import node_add_menu
from nodeitems_builtins import (
eevee_cycles_shader_nodes_poll,
line_style_shader_nodes_poll,
object_cycles_shader_nodes_poll,
object_eevee_cycles_shader_nodes_poll,
object_eevee_shader_nodes_poll,
world_shader_nodes_poll,
)
from bpy.app.translations import (
pgettext_iface as iface_,
)
class NODE_MT_category_SHADE_INPUT(Menu):
bl_idname = "NODE_MT_category_SHADE_INPUT"
bl_label = "Input"
def draw(self, context):
layout = self.layout
node_add_menu.add_node_type(layout, "ShaderNodeAmbientOcclusion")
node_add_menu.add_node_type(layout, "ShaderNodeAttribute")
node_add_menu.add_node_type(layout, "ShaderNodeBevel")
node_add_menu.add_node_type(layout, "ShaderNodeCameraData")
node_add_menu.add_node_type(layout, "ShaderNodeVertexColor")
node_add_menu.add_node_type(layout, "ShaderNodeHairInfo")
node_add_menu.add_node_type(layout, "ShaderNodeFresnel")
node_add_menu.add_node_type(layout, "ShaderNodeNewGeometry")
node_add_menu.add_node_type(layout, "ShaderNodeLayerWeight")
node_add_menu.add_node_type(layout, "ShaderNodeLightPath")
node_add_menu.add_node_type(layout, "ShaderNodeObjectInfo")
node_add_menu.add_node_type(layout, "ShaderNodeParticleInfo")
node_add_menu.add_node_type(layout, "ShaderNodePointInfo")
node_add_menu.add_node_type(layout, "ShaderNodeRGB")
node_add_menu.add_node_type(layout, "ShaderNodeTangent")
node_add_menu.add_node_type(layout, "ShaderNodeTexCoord")
node_add_menu.add_node_type(layout, "ShaderNodeUVAlongStroke", poll=line_style_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeUVMap")
node_add_menu.add_node_type(layout, "ShaderNodeValue")
node_add_menu.add_node_type(layout, "ShaderNodeVolumeInfo")
node_add_menu.add_node_type(layout, "ShaderNodeWireframe")
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_category_SHADE_OUTPUT(Menu):
bl_idname = "NODE_MT_category_SHADE_OUTPUT"
bl_label = "Output"
def draw(self, context):
layout = self.layout
node_add_menu.add_node_type(layout, "ShaderNodeOutputMaterial", poll=object_eevee_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeOutputLight", poll=object_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeOutputAOV"),
node_add_menu.add_node_type(layout, "ShaderNodeOutputWorld", poll=world_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeOutputLineStyle", poll=line_style_shader_nodes_poll(context)),
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_category_SHADE_SHADER(Menu):
bl_idname = "NODE_MT_category_SHADE_SHADER"
bl_label = "Shader"
def draw(self, context):
layout = self.layout
node_add_menu.add_node_type(layout, "ShaderNodeAddShader", poll=eevee_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeBackground", poll=world_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeBsdfDiffuse", poll=object_eevee_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeEmission", poll=eevee_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeBsdfGlass", poll=object_eevee_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeBsdfGlossy", poll=object_eevee_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeBsdfHair", poll=object_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeHoldout", poll=object_eevee_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeMixShader", poll=eevee_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeBsdfPrincipled", poll=object_eevee_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeBsdfHairPrincipled", poll=object_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeVolumePrincipled"),
node_add_menu.add_node_type(layout, "ShaderNodeBsdfRefraction", poll=object_eevee_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeBsdfSheen", poll=object_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeEeveeSpecular", poll=object_eevee_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeSubsurfaceScattering", poll=object_eevee_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeBsdfToon", poll=object_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeBsdfTranslucent", poll=object_eevee_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeBsdfTransparent", poll=object_eevee_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeVolumeAbsorption", poll=eevee_cycles_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeVolumeScatter", poll=eevee_cycles_shader_nodes_poll(context)),
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_category_SHADE_COLOR(Menu):
bl_idname = "NODE_MT_category_SHADE_COLOR"
bl_label = "Color"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "ShaderNodeBrightContrast")
node_add_menu.add_node_type(layout, "ShaderNodeGamma")
node_add_menu.add_node_type(layout, "ShaderNodeHueSaturation")
node_add_menu.add_node_type(layout, "ShaderNodeInvert")
node_add_menu.add_node_type(layout, "ShaderNodeLightFalloff")
node_add_menu.add_node_type(layout, "ShaderNodeMix")
props = node_add_menu.add_node_type(layout, "ShaderNodeMix", label=iface_("Mix Color"))
ops = props.settings.add()
ops.name = "data_type"
ops.value = "'RGBA'"
node_add_menu.add_node_type(layout, "ShaderNodeRGBCurve")
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_category_SHADE_CONVERTER(Menu):
bl_idname = "NODE_MT_category_SHADE_CONVERTER"
bl_label = "Converter"
def draw(self, context):
layout = self.layout
node_add_menu.add_node_type(layout, "ShaderNodeBlackbody")
node_add_menu.add_node_type(layout, "ShaderNodeClamp")
node_add_menu.add_node_type(layout, "ShaderNodeValToRGB")
node_add_menu.add_node_type(layout, "ShaderNodeCombineColor")
node_add_menu.add_node_type(layout, "ShaderNodeCombineXYZ")
node_add_menu.add_node_type(layout, "ShaderNodeFloatCurve")
node_add_menu.add_node_type(layout, "ShaderNodeMapRange")
node_add_menu.add_node_type(layout, "ShaderNodeMath")
node_add_menu.add_node_type(layout, "ShaderNodeMix")
node_add_menu.add_node_type(layout, "ShaderNodeRGBToBW")
node_add_menu.add_node_type(layout, "ShaderNodeSeparateColor")
node_add_menu.add_node_type(layout, "ShaderNodeSeparateXYZ")
node_add_menu.add_node_type(layout, "ShaderNodeShaderToRGB", poll=object_eevee_shader_nodes_poll(context)),
node_add_menu.add_node_type(layout, "ShaderNodeVectorMath")
node_add_menu.add_node_type(layout, "ShaderNodeWavelength")
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_category_SHADE_TEXTURE(Menu):
bl_idname = "NODE_MT_category_SHADE_TEXTURE"
bl_label = "Texture"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "ShaderNodeTexBrick")
node_add_menu.add_node_type(layout, "ShaderNodeTexChecker")
node_add_menu.add_node_type(layout, "ShaderNodeTexEnvironment")
node_add_menu.add_node_type(layout, "ShaderNodeTexGradient")
node_add_menu.add_node_type(layout, "ShaderNodeTexIES")
node_add_menu.add_node_type(layout, "ShaderNodeTexImage")
node_add_menu.add_node_type(layout, "ShaderNodeTexMagic")
node_add_menu.add_node_type(layout, "ShaderNodeTexMusgrave")
node_add_menu.add_node_type(layout, "ShaderNodeTexNoise")
node_add_menu.add_node_type(layout, "ShaderNodeTexPointDensity")
node_add_menu.add_node_type(layout, "ShaderNodeTexSky")
node_add_menu.add_node_type(layout, "ShaderNodeTexVoronoi")
node_add_menu.add_node_type(layout, "ShaderNodeTexWave")
node_add_menu.add_node_type(layout, "ShaderNodeTexWhiteNoise")
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_category_SHADE_VECTOR(Menu):
bl_idname = "NODE_MT_category_SHADE_VECTOR"
bl_label = "Vector"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "ShaderNodeBump")
node_add_menu.add_node_type(layout, "ShaderNodeDisplacement")
node_add_menu.add_node_type(layout, "ShaderNodeMapping")
node_add_menu.add_node_type(layout, "ShaderNodeNormal")
node_add_menu.add_node_type(layout, "ShaderNodeNormalMap")
node_add_menu.add_node_type(layout, "ShaderNodeVectorCurve")
node_add_menu.add_node_type(layout, "ShaderNodeVectorDisplacement")
node_add_menu.add_node_type(layout, "ShaderNodeVectorRotate")
node_add_menu.add_node_type(layout, "ShaderNodeVectorTransform")
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_category_SHADE_SCRIPT(Menu):
bl_idname = "NODE_MT_category_SHADE_SCRIPT"
bl_label = "Script"
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "ShaderNodeScript")
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_category_SHADE_GROUP(Menu):
bl_idname = "NODE_MT_category_SHADE_GROUP"
bl_label = "Group"
def draw(self, context):
layout = self.layout
node_add_menu.draw_node_group_add_menu(context, layout)
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_shading_node_add_all(Menu):
bl_idname = "NODE_MT_shading_node_add_all"
bl_label = "Add"
def draw(self, _context):
layout = self.layout
layout.menu("NODE_MT_category_SHADE_INPUT")
layout.menu("NODE_MT_category_SHADE_OUTPUT")
layout.separator()
layout.menu("NODE_MT_category_SHADE_COLOR")
layout.menu("NODE_MT_category_SHADE_CONVERTER")
layout.menu("NODE_MT_category_SHADE_SHADER")
layout.menu("NODE_MT_category_SHADE_TEXTURE")
layout.menu("NODE_MT_category_SHADE_VECTOR")
layout.separator()
layout.menu("NODE_MT_category_SHADE_SCRIPT")
layout.separator()
layout.menu("NODE_MT_category_SHADE_GROUP")
layout.menu("NODE_MT_category_layout")
node_add_menu.draw_root_assets(layout)
classes = (
NODE_MT_shading_node_add_all,
NODE_MT_category_SHADE_INPUT,
NODE_MT_category_SHADE_OUTPUT,
NODE_MT_category_SHADE_COLOR,
NODE_MT_category_SHADE_CONVERTER,
NODE_MT_category_SHADE_SHADER,
NODE_MT_category_SHADE_TEXTURE,
NODE_MT_category_SHADE_VECTOR,
NODE_MT_category_SHADE_SCRIPT,
NODE_MT_category_SHADE_GROUP,
)
if __name__ == "__main__": # only for live edit.
from bpy.utils import register_class
for cls in classes:
register_class(cls)

View File

@@ -239,6 +239,10 @@ class NODE_MT_add(bpy.types.Menu):
props = layout.operator("node.add_search", text="Search...", icon='VIEWZOOM')
layout.separator()
layout.menu_contents("NODE_MT_compositing_node_add_all")
elif snode.tree_type == 'ShaderNodeTree':
props = layout.operator("node.add_search", text="Search...", icon='VIEWZOOM')
layout.separator()
layout.menu_contents("NODE_MT_shading_node_add_all")
elif nodeitems_utils.has_node_categories(context):
props = layout.operator("node.add_search", text="Search...", icon='VIEWZOOM')
props.use_transform = True