Currently, only the visibility of input sockets can be changed dynamically based on other menu inputs. However, under some circumstances, it can also be useful to hide certain outputs. For example, the built-in Curve Arc primitive node does that. This patch adds support for automatic detection of unused outputs. How to detect unused outputs is less straight forward compared to inputs. This patch uses the rule that an output is unused if it always outputs a "fallback value" (typically 0) irrespective of the currently used inputs. If the output is independent of all inputs, it stays visible though. There is a new small utility node called "Enable Output". It replaces a value with it's fallback value unless it is disabled. This simplifies setting up unused outputs. In theory, a normal switch node can also be used, but that is less usable and the user will have to hardcode the fallback value for each type which is not something that is explicitly exposed yet. Supporting dynamic output visibility is also a prerequisite for exposing some menu node options as sockets (e.g. in the Arc node). Pull Request: https://projects.blender.org/blender/blender/pulls/140856
409 lines
16 KiB
Python
409 lines
16 KiB
Python
# SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
from bl_ui import node_add_menu
|
|
from bpy.app.translations import (
|
|
contexts as i18n_contexts,
|
|
)
|
|
|
|
|
|
class NODE_MT_compositor_node_input_base(node_add_menu.NodeMenu):
|
|
bl_label = "Input"
|
|
|
|
def draw(self, context):
|
|
del context
|
|
layout = self.layout
|
|
self.draw_menu(layout, path="Input/Constant")
|
|
layout.separator()
|
|
self.node_operator(layout, "NodeGroupInput")
|
|
self.node_operator(layout, "CompositorNodeBokehImage")
|
|
self.node_operator(layout, "CompositorNodeImage")
|
|
self.node_operator(layout, "CompositorNodeImageInfo")
|
|
self.node_operator(layout, "CompositorNodeImageCoordinates")
|
|
self.node_operator(layout, "CompositorNodeMask")
|
|
self.node_operator(layout, "CompositorNodeMovieClip")
|
|
|
|
layout.separator()
|
|
self.draw_menu(layout, path="Input/Scene")
|
|
|
|
self.draw_assets_for_catalog(layout, self.bl_label)
|
|
|
|
|
|
class NODE_MT_compositor_node_input_constant_base(node_add_menu.NodeMenu):
|
|
bl_label = "Constant"
|
|
menu_path = "Input/Constant"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
self.node_operator(layout, "CompositorNodeRGB")
|
|
self.node_operator(layout, "ShaderNodeValue")
|
|
self.node_operator(layout, "CompositorNodeNormal")
|
|
|
|
self.draw_assets_for_catalog(layout, self.menu_path)
|
|
|
|
|
|
class NODE_MT_compositor_node_input_scene_base(node_add_menu.NodeMenu):
|
|
bl_label = "Scene"
|
|
menu_path = "Input/Scene"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
if context.space_data.node_tree_sub_type == 'SCENE':
|
|
self.node_operator(layout, "CompositorNodeRLayers")
|
|
self.node_operator_with_outputs(context, layout, "CompositorNodeSceneTime", ["Frame", "Seconds"])
|
|
self.node_operator(layout, "CompositorNodeTime")
|
|
|
|
self.draw_assets_for_catalog(layout, self.menu_path)
|
|
|
|
|
|
class NODE_MT_compositor_node_output_base(node_add_menu.NodeMenu):
|
|
bl_label = "Output"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
self.node_operator(layout, "NodeEnableOutput")
|
|
self.node_operator(layout, "NodeGroupOutput")
|
|
self.node_operator(layout, "CompositorNodeViewer")
|
|
if context.space_data.node_tree_sub_type == 'SCENE':
|
|
layout.separator()
|
|
self.node_operator(layout, "CompositorNodeOutputFile")
|
|
|
|
self.draw_assets_for_catalog(layout, self.bl_label)
|
|
|
|
|
|
class NODE_MT_compositor_node_color_base(node_add_menu.NodeMenu):
|
|
bl_label = "Color"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
self.draw_menu(layout, path="Color/Adjust")
|
|
layout.separator()
|
|
self.draw_menu(layout, path="Color/Mix")
|
|
layout.separator()
|
|
self.node_operator(layout, "CompositorNodePremulKey")
|
|
self.node_operator(layout, "ShaderNodeBlackbody")
|
|
self.node_operator(layout, "ShaderNodeValToRGB")
|
|
self.node_operator(layout, "CompositorNodeConvertColorSpace")
|
|
self.node_operator(layout, "CompositorNodeConvertToDisplay")
|
|
self.node_operator(layout, "CompositorNodeSetAlpha")
|
|
layout.separator()
|
|
self.node_operator(layout, "CompositorNodeInvert")
|
|
self.node_operator(layout, "CompositorNodeRGBToBW")
|
|
|
|
self.draw_assets_for_catalog(layout, self.bl_label)
|
|
|
|
|
|
class NODE_MT_compositor_node_color_adjust_base(node_add_menu.NodeMenu):
|
|
bl_label = "Adjust"
|
|
menu_path = "Color/Adjust"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
self.node_operator(layout, "CompositorNodeBrightContrast")
|
|
self.node_operator(layout, "CompositorNodeColorBalance")
|
|
self.node_operator(layout, "CompositorNodeColorCorrection")
|
|
self.node_operator(layout, "CompositorNodeExposure")
|
|
self.node_operator(layout, "ShaderNodeGamma")
|
|
self.node_operator(layout, "CompositorNodeHueCorrect")
|
|
self.node_operator(layout, "CompositorNodeHueSat")
|
|
self.node_operator(layout, "CompositorNodeCurveRGB")
|
|
self.node_operator(layout, "CompositorNodeTonemap")
|
|
|
|
self.draw_assets_for_catalog(layout, self.menu_path)
|
|
|
|
|
|
class NODE_MT_compositor_node_color_mix_base(node_add_menu.NodeMenu):
|
|
bl_label = "Mix"
|
|
menu_path = "Color/Mix"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
self.node_operator(layout, "CompositorNodeAlphaOver")
|
|
layout.separator()
|
|
self.node_operator(layout, "CompositorNodeCombineColor")
|
|
self.node_operator(layout, "CompositorNodeSeparateColor")
|
|
layout.separator()
|
|
self.node_operator(layout, "CompositorNodeZcombine")
|
|
self.color_mix_node(context, layout)
|
|
|
|
self.draw_assets_for_catalog(layout, self.menu_path)
|
|
|
|
|
|
class NODE_MT_compositor_node_filter_base(node_add_menu.NodeMenu):
|
|
bl_label = "Filter"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
self.draw_menu(layout, path="Filter/Blur")
|
|
layout.separator()
|
|
self.node_operator(layout, "CompositorNodeAntiAliasing")
|
|
self.node_operator(layout, "CompositorNodeConvolve")
|
|
self.node_operator(layout, "CompositorNodeDenoise")
|
|
self.node_operator(layout, "CompositorNodeDespeckle")
|
|
layout.separator()
|
|
self.node_operator(layout, "CompositorNodeDilateErode")
|
|
self.node_operator(layout, "CompositorNodeInpaint")
|
|
layout.separator()
|
|
self.node_operator_with_searchable_enum_socket(
|
|
context, layout, "CompositorNodeFilter", "Type", [
|
|
"Soften", "Box Sharpen", "Diamond Sharpen", "Laplace", "Sobel", "Prewitt", "Kirsch", "Shadow"])
|
|
self.node_operator_with_searchable_enum_socket(
|
|
context, layout, "CompositorNodeGlare", "Type", [
|
|
"Bloom", "Ghosts", "Streaks", "Fog Glow", "Simple Star", "Sun Beams", "Kernel"])
|
|
self.node_operator(layout, "CompositorNodeKuwahara")
|
|
self.node_operator(layout, "CompositorNodePixelate")
|
|
self.node_operator(layout, "CompositorNodePosterize")
|
|
|
|
self.draw_assets_for_catalog(layout, self.bl_label)
|
|
|
|
|
|
class NODE_MT_compositor_node_filter_blur_base(node_add_menu.NodeMenu):
|
|
bl_label = "Blur"
|
|
menu_path = "Filter/Blur"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
self.node_operator(layout, "CompositorNodeBilateralblur")
|
|
self.node_operator(layout, "CompositorNodeBlur")
|
|
self.node_operator(layout, "CompositorNodeBokehBlur")
|
|
self.node_operator(layout, "CompositorNodeDefocus")
|
|
self.node_operator(layout, "CompositorNodeDBlur")
|
|
self.node_operator(layout, "CompositorNodeVecBlur")
|
|
|
|
self.draw_assets_for_catalog(layout, self.menu_path)
|
|
|
|
|
|
class NODE_MT_compositor_node_keying_base(node_add_menu.NodeMenu):
|
|
bl_label = "Keying"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
self.node_operator(layout, "CompositorNodeChannelMatte")
|
|
self.node_operator(layout, "CompositorNodeChromaMatte")
|
|
self.node_operator(layout, "CompositorNodeColorMatte")
|
|
self.node_operator(layout, "CompositorNodeColorSpill")
|
|
self.node_operator(layout, "CompositorNodeDiffMatte")
|
|
self.node_operator(layout, "CompositorNodeDistanceMatte")
|
|
self.node_operator(layout, "CompositorNodeKeying")
|
|
self.node_operator(layout, "CompositorNodeKeyingScreen")
|
|
self.node_operator(layout, "CompositorNodeLumaMatte")
|
|
|
|
self.draw_assets_for_catalog(layout, self.bl_label)
|
|
|
|
|
|
class NODE_MT_compositor_node_mask_base(node_add_menu.NodeMenu):
|
|
bl_label = "Mask"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
self.node_operator(layout, "CompositorNodeCryptomatteV2")
|
|
self.node_operator(layout, "CompositorNodeCryptomatte")
|
|
layout.separator()
|
|
self.node_operator(layout, "CompositorNodeBoxMask")
|
|
self.node_operator(layout, "CompositorNodeEllipseMask")
|
|
layout.separator()
|
|
self.node_operator(layout, "CompositorNodeDoubleEdgeMask")
|
|
self.node_operator(layout, "CompositorNodeIDMask")
|
|
|
|
self.draw_assets_for_catalog(layout, self.bl_label)
|
|
|
|
|
|
class NODE_MT_compositor_node_tracking_base(node_add_menu.NodeMenu):
|
|
bl_label = "Tracking"
|
|
bl_translation_context = i18n_contexts.id_movieclip
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
self.node_operator(layout, "CompositorNodePlaneTrackDeform")
|
|
self.node_operator(layout, "CompositorNodeStabilize")
|
|
self.node_operator(layout, "CompositorNodeTrackPos")
|
|
|
|
self.draw_assets_for_catalog(layout, self.bl_label)
|
|
|
|
|
|
class NODE_MT_compositor_node_transform_base(node_add_menu.NodeMenu):
|
|
bl_label = "Transform"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
self.node_operator(layout, "CompositorNodeRotate")
|
|
self.node_operator(layout, "CompositorNodeScale")
|
|
self.node_operator(layout, "CompositorNodeTransform")
|
|
self.node_operator(layout, "CompositorNodeTranslate")
|
|
layout.separator()
|
|
self.node_operator(layout, "CompositorNodeCornerPin")
|
|
self.node_operator(layout, "CompositorNodeCrop")
|
|
layout.separator()
|
|
self.node_operator(layout, "CompositorNodeDisplace")
|
|
self.node_operator(layout, "CompositorNodeFlip")
|
|
self.node_operator(layout, "CompositorNodeMapUV")
|
|
layout.separator()
|
|
self.node_operator(layout, "CompositorNodeLensdist")
|
|
self.node_operator(layout, "CompositorNodeMovieDistortion")
|
|
|
|
self.draw_assets_for_catalog(layout, self.bl_label)
|
|
|
|
|
|
class NODE_MT_compositor_node_texture_base(node_add_menu.NodeMenu):
|
|
bl_label = "Texture"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
|
|
self.node_operator(layout, "ShaderNodeTexBrick")
|
|
self.node_operator(layout, "ShaderNodeTexChecker")
|
|
self.node_operator(layout, "ShaderNodeTexGabor")
|
|
self.node_operator(layout, "ShaderNodeTexGradient")
|
|
self.node_operator(layout, "ShaderNodeTexMagic")
|
|
self.node_operator(layout, "ShaderNodeTexNoise")
|
|
self.node_operator(layout, "ShaderNodeTexVoronoi")
|
|
self.node_operator(layout, "ShaderNodeTexWave")
|
|
self.node_operator(layout, "ShaderNodeTexWhiteNoise")
|
|
|
|
self.draw_assets_for_catalog(layout, self.bl_label)
|
|
|
|
|
|
class NODE_MT_compositor_node_utilities_base(node_add_menu.NodeMenu):
|
|
bl_label = "Utilities"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
self.node_operator(layout, "ShaderNodeMapRange")
|
|
self.node_operator_with_searchable_enum(context, layout, "ShaderNodeMath", "operation")
|
|
self.node_operator(layout, "ShaderNodeMix")
|
|
self.node_operator(layout, "ShaderNodeClamp")
|
|
self.node_operator(layout, "ShaderNodeFloatCurve")
|
|
layout.separator()
|
|
self.node_operator(layout, "CompositorNodeLevels")
|
|
self.node_operator(layout, "CompositorNodeNormalize")
|
|
layout.separator()
|
|
self.node_operator(layout, "CompositorNodeSplit")
|
|
self.node_operator(layout, "CompositorNodeSwitch")
|
|
self.node_operator(layout, "GeometryNodeMenuSwitch")
|
|
self.node_operator(
|
|
layout, "CompositorNodeSwitchView",
|
|
label="Switch Stereo View")
|
|
layout.separator()
|
|
self.node_operator(layout, "CompositorNodeRelativeToPixel")
|
|
|
|
self.draw_assets_for_catalog(layout, self.bl_label)
|
|
|
|
|
|
class NODE_MT_compositor_node_vector_base(node_add_menu.NodeMenu):
|
|
bl_label = "Vector"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
self.node_operator(layout, "ShaderNodeCombineXYZ")
|
|
self.node_operator(layout, "ShaderNodeSeparateXYZ")
|
|
layout.separator()
|
|
props = self.node_operator(layout, "ShaderNodeMix", label="Mix Vector")
|
|
ops = props.settings.add()
|
|
ops.name = "data_type"
|
|
ops.value = "'VECTOR'"
|
|
self.node_operator(layout, "ShaderNodeRadialTiling")
|
|
self.node_operator(layout, "ShaderNodeVectorCurve")
|
|
self.node_operator_with_searchable_enum(context, layout, "ShaderNodeVectorMath", "operation")
|
|
self.node_operator(layout, "ShaderNodeVectorRotate")
|
|
|
|
self.draw_assets_for_catalog(layout, self.bl_label)
|
|
|
|
|
|
class NODE_MT_compositor_node_all_base(node_add_menu.NodeMenu):
|
|
bl_label = ""
|
|
menu_path = "Root"
|
|
bl_translation_context = i18n_contexts.operator_default
|
|
|
|
# NOTE: Menus are looked up via their label, this is so that both the Add
|
|
# & Swap menus can share the same layout while each using their
|
|
# corresponding menus
|
|
def draw(self, context):
|
|
del context
|
|
layout = self.layout
|
|
self.draw_menu(layout, "Input")
|
|
self.draw_menu(layout, "Output")
|
|
layout.separator()
|
|
self.draw_menu(layout, "Color")
|
|
self.draw_menu(layout, "Filter")
|
|
layout.separator()
|
|
self.draw_menu(layout, "Keying")
|
|
self.draw_menu(layout, "Mask")
|
|
layout.separator()
|
|
self.draw_menu(layout, "Tracking")
|
|
layout.separator()
|
|
self.draw_menu(layout, "Texture")
|
|
self.draw_menu(layout, "Transform")
|
|
self.draw_menu(layout, "Utilities")
|
|
self.draw_menu(layout, "Vector")
|
|
layout.separator()
|
|
self.draw_menu(layout, "Group")
|
|
self.draw_menu(layout, "Layout")
|
|
|
|
self.draw_root_assets(layout)
|
|
|
|
|
|
add_menus = {
|
|
# menu bl_idname: baseclass
|
|
"NODE_MT_category_compositor_input": NODE_MT_compositor_node_input_base,
|
|
"NODE_MT_category_compositor_input_constant": NODE_MT_compositor_node_input_constant_base,
|
|
"NODE_MT_category_compositor_input_scene": NODE_MT_compositor_node_input_scene_base,
|
|
"NODE_MT_category_compositor_output": NODE_MT_compositor_node_output_base,
|
|
"NODE_MT_category_compositor_color": NODE_MT_compositor_node_color_base,
|
|
"NODE_MT_category_compositor_color_adjust": NODE_MT_compositor_node_color_adjust_base,
|
|
"NODE_MT_category_compositor_color_mix": NODE_MT_compositor_node_color_mix_base,
|
|
"NODE_MT_category_compositor_filter": NODE_MT_compositor_node_filter_base,
|
|
"NODE_MT_category_compositor_filter_blur": NODE_MT_compositor_node_filter_blur_base,
|
|
"NODE_MT_category_compositor_texture": NODE_MT_compositor_node_texture_base,
|
|
"NODE_MT_category_compositor_keying": NODE_MT_compositor_node_keying_base,
|
|
"NODE_MT_category_compositor_mask": NODE_MT_compositor_node_mask_base,
|
|
"NODE_MT_category_compositor_tracking": NODE_MT_compositor_node_tracking_base,
|
|
"NODE_MT_category_compositor_transform": NODE_MT_compositor_node_transform_base,
|
|
"NODE_MT_category_compositor_utilities": NODE_MT_compositor_node_utilities_base,
|
|
"NODE_MT_category_compositor_vector": NODE_MT_compositor_node_vector_base,
|
|
"NODE_MT_compositor_node_add_all": NODE_MT_compositor_node_all_base,
|
|
}
|
|
add_menus = node_add_menu.generate_menus(
|
|
add_menus,
|
|
template=node_add_menu.AddNodeMenu,
|
|
base_dict=node_add_menu.add_base_pathing_dict
|
|
)
|
|
|
|
|
|
swap_menus = {
|
|
# menu bl_idname: baseclass
|
|
"NODE_MT_compositor_node_input_swap": NODE_MT_compositor_node_input_base,
|
|
"NODE_MT_compositor_node_input_constant_swap": NODE_MT_compositor_node_input_constant_base,
|
|
"NODE_MT_compositor_node_input_scene_swap": NODE_MT_compositor_node_input_scene_base,
|
|
"NODE_MT_compositor_node_output_swap": NODE_MT_compositor_node_output_base,
|
|
"NODE_MT_compositor_node_color_swap": NODE_MT_compositor_node_color_base,
|
|
"NODE_MT_compositor_node_color_adjust_swap": NODE_MT_compositor_node_color_adjust_base,
|
|
"NODE_MT_compositor_node_color_mix_swap": NODE_MT_compositor_node_color_mix_base,
|
|
"NODE_MT_compositor_node_filter_swap": NODE_MT_compositor_node_filter_base,
|
|
"NODE_MT_compositor_node_filter_blur_swap": NODE_MT_compositor_node_filter_blur_base,
|
|
"NODE_MT_compositor_node_texture_swap": NODE_MT_compositor_node_texture_base,
|
|
"NODE_MT_compositor_node_keying_swap": NODE_MT_compositor_node_keying_base,
|
|
"NODE_MT_compositor_node_mask_swap": NODE_MT_compositor_node_mask_base,
|
|
"NODE_MT_compositor_node_tracking_swap": NODE_MT_compositor_node_tracking_base,
|
|
"NODE_MT_compositor_node_transform_swap": NODE_MT_compositor_node_transform_base,
|
|
"NODE_MT_compositor_node_utilities_swap": NODE_MT_compositor_node_utilities_base,
|
|
"NODE_MT_compositor_node_vector_swap": NODE_MT_compositor_node_vector_base,
|
|
"NODE_MT_compositor_node_swap_all": NODE_MT_compositor_node_all_base,
|
|
}
|
|
swap_menus = node_add_menu.generate_menus(
|
|
swap_menus,
|
|
template=node_add_menu.SwapNodeMenu,
|
|
base_dict=node_add_menu.swap_base_pathing_dict
|
|
)
|
|
|
|
|
|
classes = (
|
|
*add_menus,
|
|
*swap_menus,
|
|
)
|
|
|
|
if __name__ == "__main__": # only for live edit.
|
|
from bpy.utils import register_class
|
|
for cls in classes:
|
|
register_class(cls)
|