Skip to content

Commit

Permalink
Shading Nodes: Refactor Add menu
Browse files Browse the repository at this point in the history
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
  • Loading branch information
venomgfx authored and Pablo Vazquez committed Sep 1, 2023
1 parent b36367e commit 9db4c0c
Show file tree
Hide file tree
Showing 6 changed files with 285 additions and 21 deletions.
1 change: 1 addition & 0 deletions scripts/startup/bl_ui/__init__.py
Expand Up @@ -15,6 +15,7 @@
"node_add_menu",
"node_add_menu_compositor",
"node_add_menu_geometry",
"node_add_menu_shading",
"properties_animviz",
"properties_constraint",
"properties_data_armature",
Expand Down
28 changes: 22 additions & 6 deletions scripts/startup/bl_ui/node_add_menu.py
Expand Up @@ -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):
Expand Down Expand Up @@ -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.
Expand Down
3 changes: 1 addition & 2 deletions scripts/startup/bl_ui/node_add_menu_compositor.py
Expand Up @@ -336,7 +336,7 @@ def draw(self, context):
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)

Expand All @@ -359,7 +359,6 @@ def draw(self, context):
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.
Expand Down
14 changes: 1 addition & 13 deletions scripts/startup/bl_ui/node_add_menu_geometry.py
Expand Up @@ -653,17 +653,6 @@ def draw(self, context):
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 = ""
Expand All @@ -690,7 +679,7 @@ def draw(self, context):
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)


Expand Down Expand Up @@ -736,7 +725,6 @@ def draw(self, context):
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.
Expand Down
256 changes: 256 additions & 0 deletions scripts/startup/bl_ui/node_add_menu_shading.py
@@ -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)
4 changes: 4 additions & 0 deletions scripts/startup/bl_ui/space_node.py
Expand Up @@ -239,6 +239,10 @@ def draw(self, context):
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
Expand Down

0 comments on commit 9db4c0c

Please sign in to comment.