This patch adds a new Compositor modifier that applies a compositing node group on a sequencer strip. This patch also introduces the concept of a compositor node tree space subtype, where we now have a Scene and a Sequencer subtypes. Practically, this just means that node like the Render Layers node will not be available in the node editor in the Sequencer subtype. Future improvements includes: - The compositor context is recreated on every modifier application, while it should ideally be persistent somehow to make use of the compositor static cache. This might require work from the compositor side by moving the static cache outside of the context and make it thread safe if needed. See `Render.compositor` for an example on persistent context. - GPU execution is not supported. This just needs a GPU context to be bound before execution, but the tricky part is getting a GPU context. See `render::Compositor::execute` for an example on bounding a GPU context and why it is less straight forward. - Node inputs are not exposed on the sequencer modifier interface. An approach similar to Geometry Nodes modifier could be used, look at `update_input_properties_from_node_tree` for reference, but notice that Geometry Nodes naturally exempt the main Geometry socket because Geometry inputs can't be exposed, but for the compositor, we will have to exempt the main Color and Mask sockets manually. !145971 Co-authored-by: Aras Pranckevicius <aras@nesnausk.org> Co-authored-by: Falk David <falk@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/139634
408 lines
16 KiB
Python
408 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, "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)
|