diff --git a/scripts/startup/bl_ui/node_add_menu_compositor.py b/scripts/startup/bl_ui/node_add_menu_compositor.py index 9c5e1550182..65653d2f41f 100644 --- a/scripts/startup/bl_ui/node_add_menu_compositor.py +++ b/scripts/startup/bl_ui/node_add_menu_compositor.py @@ -49,7 +49,8 @@ class NODE_MT_compositor_node_input_scene_base(node_add_menu.NodeMenu): def draw(self, context): layout = self.layout - self.node_operator(layout, "CompositorNodeRLayers") + 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") @@ -60,12 +61,12 @@ class NODE_MT_compositor_node_output_base(node_add_menu.NodeMenu): bl_label = "Output" def draw(self, context): - del context layout = self.layout self.node_operator(layout, "NodeGroupOutput") self.node_operator(layout, "CompositorNodeViewer") - layout.separator() - self.node_operator(layout, "CompositorNodeOutputFile") + 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) diff --git a/scripts/startup/bl_ui/space_node.py b/scripts/startup/bl_ui/space_node.py index 69e107bba72..b028b0fe437 100644 --- a/scripts/startup/bl_ui/space_node.py +++ b/scripts/startup/bl_ui/space_node.py @@ -145,13 +145,29 @@ class NODE_HT_header(Header): layout.template_ID(id_from, "active_texture", new="texture.new") elif snode.tree_type == 'CompositorNodeTree': - + layout.prop(snode, "node_tree_sub_type", text="") NODE_MT_editor_menus.draw_collapsible(context, layout) - layout.separator_spacer() - row = layout.row() - row.enabled = not snode.pin - row.template_ID(scene, "compositing_node_group", new="node.new_compositing_node_group") + + if snode.node_tree_sub_type == 'SCENE': + row = layout.row() + row.enabled = not snode.pin + row.template_ID(scene, "compositing_node_group", new="node.new_compositing_node_group") + elif snode.node_tree_sub_type == 'SEQUENCER': + row = layout.row() + sequencer_scene = context.workspace.sequencer_scene + sequencer_editor = sequencer_scene.sequence_editor if sequencer_scene else None + active_strip = sequencer_editor.active_strip if sequencer_editor else None + active_modifier = active_strip.modifiers.active if active_strip else None + is_compositor_modifier_active = active_modifier and active_modifier.type == 'COMPOSITOR' + if is_compositor_modifier_active and not snode.pin: + row.template_ID( + active_modifier, + "node_group", + new="node.new_compositor_sequencer_node_group") + else: + row.enabled = False + row.template_ID(snode, "node_tree", new="node.new_compositor_sequencer_node_group") elif snode.tree_type == 'GeometryNodeTree': layout.prop(snode, "node_tree_sub_type", text="") @@ -200,7 +216,7 @@ class NODE_HT_header(Header): op.parent_tree_index = len(snode.path) - 2 # Backdrop - if is_compositor: + if is_compositor and snode.node_tree_sub_type == 'SCENE': row = layout.row(align=True) row.prop(snode, "show_backdrop", toggle=True) row.active = snode.node_tree is not None diff --git a/scripts/startup/bl_ui/space_sequencer.py b/scripts/startup/bl_ui/space_sequencer.py index 953e488dce3..4faf2bb551b 100644 --- a/scripts/startup/bl_ui/space_sequencer.py +++ b/scripts/startup/bl_ui/space_sequencer.py @@ -1571,6 +1571,7 @@ class SEQUENCER_MT_modifier_add(Menu): else: self.operator_modifier_add(layout, 'BRIGHT_CONTRAST') self.operator_modifier_add(layout, 'COLOR_BALANCE') + self.operator_modifier_add(layout, 'COMPOSITOR') self.operator_modifier_add(layout, 'CURVES') self.operator_modifier_add(layout, 'HUE_CORRECT') self.operator_modifier_add(layout, 'MASK') diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 3d1578418f1..27c98113d0c 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -807,6 +807,10 @@ static bool strip_foreach_member_id_cb(Strip *strip, void *user_data) }); LISTBASE_FOREACH (StripModifierData *, smd, &strip->modifiers) { FOREACHID_PROCESS_IDSUPER(data, smd->mask_id, IDWALK_CB_USER); + if (smd->type == eSeqModifierType_Compositor) { + auto *modifier_data = reinterpret_cast(smd); + FOREACHID_PROCESS_IDSUPER(data, modifier_data->node_group, IDWALK_CB_USER); + } } if (strip->type == STRIP_TYPE_TEXT && strip->effectdata) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index d37de16e23e..9d5e36ad191 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -2322,6 +2322,18 @@ static bool strip_node_build_cb(Strip *strip, void *user_data) ViewLayer *sequence_view_layer = BKE_view_layer_default_render(strip->scene); nb->build_scene_speakers(strip->scene, sequence_view_layer); } + LISTBASE_FOREACH (StripModifierData *, modifier, &strip->modifiers) { + if (modifier->type != eSeqModifierType_Compositor) { + continue; + } + + const SequencerCompositorModifierData *modifier_data = + reinterpret_cast(modifier); + if (!modifier_data->node_group) { + continue; + } + nb->build_nodetree(modifier_data->node_group); + } /* TODO(sergey): Movie clip, scene, camera, mask. */ return true; } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 7a8ed6f253e..c1a97036cdf 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -3441,6 +3441,21 @@ static bool strip_build_prop_cb(Strip *strip, void *user_data) ViewLayer *sequence_view_layer = BKE_view_layer_default_render(strip->scene); cd->builder->build_scene_speakers(strip->scene, sequence_view_layer); } + LISTBASE_FOREACH (StripModifierData *, modifier, &strip->modifiers) { + if (modifier->type != eSeqModifierType_Compositor) { + continue; + } + + const SequencerCompositorModifierData *modifier_data = + reinterpret_cast(modifier); + if (!modifier_data->node_group) { + continue; + } + cd->builder->build_nodetree(modifier_data->node_group); + OperationKey node_tree_key( + &modifier_data->node_group->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT); + cd->builder->add_relation(node_tree_key, cd->sequencer_key, "Modifier's Node Group"); + } /* TODO(sergey): Movie clip, camera, mask. */ return true; } diff --git a/source/blender/editors/include/ED_node_c.hh b/source/blender/editors/include/ED_node_c.hh index 9686e93f449..4cda54f8de0 100644 --- a/source/blender/editors/include/ED_node_c.hh +++ b/source/blender/editors/include/ED_node_c.hh @@ -71,7 +71,7 @@ void ED_node_set_tree_type(SpaceNode *snode, blender::bke::bNodeTreeType *typein bool ED_node_is_compositor(const SpaceNode *snode); bool ED_node_is_shader(SpaceNode *snode); bool ED_node_is_texture(SpaceNode *snode); -bool ED_node_is_geometry(SpaceNode *snode); +bool ED_node_is_geometry(const SpaceNode *snode); bool ED_node_supports_preview(SpaceNode *snode); /** diff --git a/source/blender/editors/render/render_update.cc b/source/blender/editors/render/render_update.cc index cc852292d81..6965c351fdf 100644 --- a/source/blender/editors/render/render_update.cc +++ b/source/blender/editors/render/render_update.cc @@ -328,6 +328,14 @@ static void update_sequencer(const DEGEditorUpdateContext *update_ctx, Main *bma blender::seq::cache_cleanup(changed_scene); } } + + /* Invalidate cache for strips that use this compositing tree as a modifier. */ + if (GS(id->name) == ID_NT) { + const bNodeTree *node_tree = reinterpret_cast(id); + if (node_tree->type == NTREE_COMPOSIT) { + blender::seq::relations_invalidate_compositor_modifiers(bmain, node_tree); + } + } } void ED_render_id_flush_update(const DEGEditorUpdateContext *update_ctx, ID *id) diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index 8567669bc7a..519275b6a5f 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -12,6 +12,7 @@ set(INC ../../compositor/utilities ../../makesrna ../../nodes/geometry/include + ../../sequencer ../asset # RNA_prototypes.hh @@ -69,6 +70,7 @@ set(LIB PRIVATE bf::nodes PRIVATE bf::render PRIVATE bf::windowmanager + PRIVATE bf::sequencer ) if(WITH_OPENIMAGEDENOISE) diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index f67d1c5984b..49a60faae52 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -1494,7 +1494,9 @@ void draw_nodespace_back_pix(const bContext &C, GPU_matrix_pop_projection(); GPU_matrix_pop(); - if (!(snode.flag & SNODE_BACKDRAW) || !ED_node_is_compositor(&snode)) { + if (!(snode.flag & SNODE_BACKDRAW) || !ED_node_is_compositor(&snode) || + snode.node_tree_sub_type != SNODE_COMPOSITOR_SCENE) + { return; } diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index fca972e7e95..094a9212f76 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -1733,6 +1733,92 @@ void NODE_OT_new_compositing_node_group(wmOperatorType *ot) RNA_def_string(ot->srna, "name", DATA_("Compositor Nodes"), MAX_ID_NAME - 2, "Name", ""); } +/* -------------------------------------------------------------------- */ +/** \name New Compositor Sequencer Node Group Operator + * \{ */ + +static void initialize_compositor_sequencer_node_group(const bContext *C, bNodeTree &ntree) +{ + BLI_assert(ntree.type == NTREE_COMPOSIT); + BLI_assert(BLI_listbase_count(&ntree.nodes) == 0); + + ntree.tree_interface.add_socket( + DATA_("Image"), "", "NodeSocketColor", NODE_INTERFACE_SOCKET_INPUT, nullptr); + ntree.tree_interface.add_socket( + DATA_("Mask"), "", "NodeSocketColor", NODE_INTERFACE_SOCKET_INPUT, nullptr); + ntree.tree_interface.add_socket( + DATA_("Image"), "", "NodeSocketColor", NODE_INTERFACE_SOCKET_OUTPUT, nullptr); + + bNode *output_node = blender::bke::node_add_node(C, ntree, "NodeGroupOutput"); + output_node->location[0] = 200.0f; + output_node->location[1] = 0.0f; + + bNode *input_node = blender::bke::node_add_node(C, ntree, "NodeGroupInput"); + input_node->location[0] = -150.0f - input_node->width; + input_node->location[1] = 0.0f; + blender::bke::node_set_active(ntree, *input_node); + + bNode *reroute = blender::bke::node_add_static_node(C, ntree, NODE_REROUTE); + reroute->location[0] = 100.0f; + reroute->location[1] = -35.0f; + + bNode *viewer = blender::bke::node_add_static_node(C, ntree, CMP_NODE_VIEWER); + viewer->location[0] = 200.0f; + viewer->location[1] = -80.0f; + + blender::bke::node_add_link(ntree, + *input_node, + *static_cast(input_node->outputs.first), + *reroute, + *static_cast(reroute->inputs.first)); + + blender::bke::node_add_link(ntree, + *reroute, + *static_cast(reroute->outputs.first), + *output_node, + *static_cast(output_node->inputs.first)); + + blender::bke::node_add_link(ntree, + *reroute, + *static_cast(reroute->outputs.first), + *viewer, + *static_cast(viewer->inputs.first)); + + BKE_ntree_update_after_single_tree_change(*CTX_data_main(C), ntree); +} + +static wmOperatorStatus new_compositor_sequencer_node_group_exec(bContext *C, wmOperator *op) +{ + char tree_name[MAX_ID_NAME - 2]; + RNA_string_get(op->ptr, "name", tree_name); + + bNodeTree *ntree = new_node_tree_impl(C, tree_name, "CompositorNodeTree"); + initialize_compositor_sequencer_node_group(C, *ntree); + + BKE_ntree_update_after_single_tree_change(*CTX_data_main(C), *ntree); + WM_event_add_notifier(C, NC_NODE | NA_ADDED, nullptr); + + return OPERATOR_FINISHED; +} + +void NODE_OT_new_compositor_sequencer_node_group(wmOperatorType *operator_type) +{ + operator_type->name = "New Compositor Sequencer Node Group"; + operator_type->idname = "NODE_OT_new_compositor_sequencer_node_group"; + operator_type->description = "Create a new compositor node group for sequencer"; + + operator_type->exec = new_compositor_sequencer_node_group_exec; + + operator_type->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_string(operator_type->srna, + "name", + DATA_("Sequencer Compositor Nodes"), + MAX_ID_NAME - 2, + "Name", + ""); +} + /** \} */ } // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc index 1dbfa84d09b..66df55ad924 100644 --- a/source/blender/editors/space_node/node_context_path.cc +++ b/source/blender/editors/space_node/node_context_path.cc @@ -21,6 +21,10 @@ #include "ED_screen.hh" +#include "SEQ_modifier.hh" +#include "SEQ_select.hh" +#include "SEQ_sequencer.hh" + #include "WM_api.hh" #include "UI_interface.hh" @@ -162,9 +166,47 @@ static void get_context_path_node_compositor(const bContext &C, context_path_add_node_tree_and_node_groups(snode, path); } else { - Scene *scene = CTX_data_scene(&C); - ui::context_path_add_generic(path, RNA_Scene, scene); - context_path_add_node_tree_and_node_groups(snode, path); + if (snode.node_tree_sub_type == SNODE_COMPOSITOR_SEQUENCER) { + Scene *sequencer_scene = CTX_data_sequencer_scene(&C); + if (!sequencer_scene) { + context_path_add_node_tree_and_node_groups(snode, path); + return; + } + Editing *ed = seq::editing_get(sequencer_scene); + if (!ed) { + context_path_add_node_tree_and_node_groups(snode, path); + return; + } + Strip *strip = seq::select_active_get(sequencer_scene); + if (!strip) { + context_path_add_node_tree_and_node_groups(snode, path); + return; + } + StripModifierData *smd = seq::modifier_get_active(strip); + if (!smd) { + context_path_add_node_tree_and_node_groups(snode, path); + return; + } + if (smd->type != eSeqModifierType_Compositor) { + context_path_add_node_tree_and_node_groups(snode, path); + return; + } + SequencerCompositorModifierData *scmd = reinterpret_cast( + smd); + if (scmd->node_group == nullptr) { + context_path_add_node_tree_and_node_groups(snode, path); + return; + } + ui::context_path_add_generic(path, RNA_Scene, sequencer_scene, ICON_SCENE); + ui::context_path_add_generic(path, RNA_Strip, strip, ICON_SEQ_STRIP_DUPLICATE); + ui::context_path_add_generic(path, RNA_NodeTree, scmd->node_group); + context_path_add_node_tree_and_node_groups(snode, path, true); + } + else { + Scene *scene = CTX_data_scene(&C); + ui::context_path_add_generic(path, RNA_Scene, scene); + context_path_add_node_tree_and_node_groups(snode, path); + } } } diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index a71ae180da3..af9f082cd50 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -504,7 +504,7 @@ bool ED_node_is_texture(SpaceNode *snode) return snode->tree_idname == ntreeType_Texture->idname; } -bool ED_node_is_geometry(SpaceNode *snode) +bool ED_node_is_geometry(const SpaceNode *snode) { return snode->tree_idname == ntreeType_Geometry->idname; } diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 07e26ec4d4e..a13d2d0c664 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -310,6 +310,7 @@ void NODE_OT_add_import_node(wmOperatorType *ot); void NODE_OT_swap_group_asset(wmOperatorType *ot); void NODE_OT_new_node_tree(wmOperatorType *ot); void NODE_OT_new_compositing_node_group(wmOperatorType *ot); +void NODE_OT_new_compositor_sequencer_node_group(wmOperatorType *operator_type); void NODE_OT_add_group_input_node(wmOperatorType *ot); /* `node_group.cc` */ diff --git a/source/blender/editors/space_node/node_ops.cc b/source/blender/editors/space_node/node_ops.cc index 94e5fad6607..7c27b98fa57 100644 --- a/source/blender/editors/space_node/node_ops.cc +++ b/source/blender/editors/space_node/node_ops.cc @@ -95,6 +95,7 @@ void node_operatortypes() WM_operatortype_append(NODE_OT_new_node_tree); WM_operatortype_append(NODE_OT_new_compositing_node_group); + WM_operatortype_append(NODE_OT_new_compositor_sequencer_node_group); WM_operatortype_append(NODE_OT_parent_set); WM_operatortype_append(NODE_OT_join); diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index c6d492b118f..35a697d9779 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -625,6 +625,11 @@ typedef enum eModTonemapType { SEQ_TONEMAP_RD_PHOTORECEPTOR = 1, } eModTonemapType; +typedef struct SequencerCompositorModifierData { + StripModifierData modifier; + struct bNodeTree *node_group; +} SequencerCompositorModifierData; + /** \} */ /* -------------------------------------------------------------------- */ @@ -888,6 +893,7 @@ typedef enum eStripModifierType { eSeqModifierType_WhiteBalance = 6, eSeqModifierType_Tonemap = 7, eSeqModifierType_SoundEqualizer = 8, + eSeqModifierType_Compositor = 9, /* Keep last. */ NUM_STRIP_MODIFIER_TYPES, } eStripModifierType; diff --git a/source/blender/makesdna/DNA_space_enums.h b/source/blender/makesdna/DNA_space_enums.h index 9c7d2d4044e..77bbe6455a9 100644 --- a/source/blender/makesdna/DNA_space_enums.h +++ b/source/blender/makesdna/DNA_space_enums.h @@ -879,6 +879,12 @@ typedef enum SpaceNodeGeometryNodesType { SNODE_GEOMETRY_TOOL = 1, } SpaceNodeGeometryNodesType; +/** #SpaceNode.nodes_type */ +typedef enum SpaceNodeCompositorNodesType { + SNODE_COMPOSITOR_SCENE = 0, + SNODE_COMPOSITOR_SEQUENCER = 1, +} SpaceNodeCompositorNodesType; + /** #SpaceNode.insert_ofs_dir */ enum { SNODE_INSERTOFS_DIR_RIGHT = 0, diff --git a/source/blender/makesrna/intern/rna_sequencer.cc b/source/blender/makesrna/intern/rna_sequencer.cc index 831d6ca383d..c73585db8ca 100644 --- a/source/blender/makesrna/intern/rna_sequencer.cc +++ b/source/blender/makesrna/intern/rna_sequencer.cc @@ -44,6 +44,7 @@ struct EffectInfo { #define RNA_ENUM_SEQUENCER_VIDEO_MODIFIER_TYPE_ITEMS \ {eSeqModifierType_BrightContrast, "BRIGHT_CONTRAST", ICON_MOD_BRIGHTNESS_CONTRAST, "Brightness/Contrast", ""}, \ {eSeqModifierType_ColorBalance, "COLOR_BALANCE", ICON_MOD_COLOR_BALANCE, "Color Balance", ""}, \ + {eSeqModifierType_Compositor, "COMPOSITOR", ICON_NODE_COMPOSITING, "Compositor", ""}, \ {eSeqModifierType_Curves, "CURVES", ICON_MOD_CURVES, "Curves", ""}, \ {eSeqModifierType_HueCorrect, "HUE_CORRECT", ICON_MOD_HUE_CORRECT, "Hue Correct", ""}, \ {eSeqModifierType_Mask, "MASK", ICON_MOD_MASK, "Mask", ""}, \ @@ -1444,6 +1445,8 @@ static StructRNA *rna_StripModifier_refine(PointerRNA *ptr) return &RNA_SequencerTonemapModifierData; case eSeqModifierType_SoundEqualizer: return &RNA_SoundEqualizerModifier; + case eSeqModifierType_Compositor: + return &RNA_SequencerCompositorModifierData; default: return &RNA_StripModifier; } @@ -1742,6 +1745,33 @@ static void rna_Strip_SoundEqualizer_Curve_clear(SoundEqualizerModifierData *sem WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL); } +static bool rna_CompositorModifier_node_group_poll(PointerRNA * /*ptr*/, PointerRNA value) +{ + const bNodeTree *node_tree = value.data_as(); + if (node_tree->type != NTREE_COMPOSIT) { + return false; + } + return true; +} + +static void rna_CompositorModifier_node_group_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + rna_StripModifier_update(bmain, scene, ptr); + + /* Tag depsgraph relations for an update since the modifier could now be referencing a different + * node tree. */ + DEG_relations_tag_update(bmain); + + /* Strips from other scenes could be modified, so use the scene of the strip as opposed to the + * active scene argument. */ + Scene *strip_scene = reinterpret_cast(ptr->owner_id); + Editing *ed = blender::seq::editing_get(strip_scene); + + /* The sequencer stores a cached mapping between compositor node trees and strips that use them + * as a modifier, so we need to invalidate the cache since the node tree changed. */ + blender::seq::strip_lookup_invalidate(ed); +} + #else static void rna_def_strip_element(BlenderRNA *brna) @@ -4065,6 +4095,24 @@ static void rna_def_tonemap_modifier(BlenderRNA *brna) rna_def_modifier_panel_open_prop(srna, "open_mask_input_panel", 1); } +static void rna_def_compositor_modifier(BlenderRNA *brna) +{ + StructRNA *srna = RNA_def_struct(brna, "SequencerCompositorModifierData", "StripModifier"); + RNA_def_struct_sdna(srna, "SequencerCompositorModifierData"); + RNA_def_struct_ui_icon(srna, ICON_NODE_COMPOSITING); + RNA_def_struct_ui_text(srna, "SequencerCompositorModifierData", "Compositor Modifier"); + + PropertyRNA *prop = RNA_def_property(srna, "node_group", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Node Group", "Node group that controls what this modifier does"); + RNA_def_property_pointer_funcs( + prop, nullptr, nullptr, nullptr, "rna_CompositorModifier_node_group_poll"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_update( + prop, NC_SCENE | ND_SEQUENCER, "rna_CompositorModifier_node_group_update"); + + rna_def_modifier_panel_open_prop(srna, "open_mask_input_panel", 1); +} + static void rna_def_modifiers(BlenderRNA *brna) { rna_def_modifier(brna); @@ -4076,6 +4124,7 @@ static void rna_def_modifiers(BlenderRNA *brna) rna_def_brightcontrast_modifier(brna); rna_def_whitebalance_modifier(brna); rna_def_tonemap_modifier(brna); + rna_def_compositor_modifier(brna); } static void rna_def_graphical_sound_equalizer(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_space.cc b/source/blender/makesrna/intern/rna_space.cc index ec737a08080..a9561bb9f02 100644 --- a/source/blender/makesrna/intern/rna_space.cc +++ b/source/blender/makesrna/intern/rna_space.cc @@ -2553,10 +2553,15 @@ static void rna_SpaceNodeEditor_node_tree_set(PointerRNA *ptr, ED_node_tree_start(region, snode, (bNodeTree *)value.data, nullptr, nullptr); } -static bool rna_SpaceNodeEditor_selected_node_group_poll(PointerRNA * /*ptr*/, +static bool rna_SpaceNodeEditor_selected_node_group_poll(PointerRNA *space_node_pointer, const PointerRNA value) { + SpaceNode *space_node = space_node_pointer->data_as(); const bNodeTree &ntree = *static_cast(value.data); + if (ED_node_is_compositor(space_node)) { + return ntree.type == NTREE_COMPOSIT; + } + if (ntree.type != NTREE_GEOMETRY) { return false; } @@ -2614,13 +2619,55 @@ static void rna_SpaceNodeEditor_node_tree_update(const bContext *C, PointerRNA * blender::ed::space_node::tree_update(C); } +static const EnumPropertyItem *rna_SpaceNodeEditor_node_tree_sub_type_itemf( + bContext * /*context*/, + PointerRNA *space_node_pointer, + PropertyRNA * /*property*/, + bool * /*r_free*/) +{ + static const EnumPropertyItem geometry_nodes_sub_type_items[] = { + {SNODE_GEOMETRY_MODIFIER, + "MODIFIER", + ICON_MODIFIER_DATA, + "Modifier", + "Edit node group from active object's active modifier"}, + {SNODE_GEOMETRY_TOOL, + "TOOL", + ICON_TOOL_SETTINGS, + "Tool", + "Edit any geometry node group for use as an operator"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static const EnumPropertyItem compositor_sub_type_items[] = { + {SNODE_COMPOSITOR_SCENE, "SCENE", 0, "Scene", "Edit node group for the current scene"}, + {SNODE_COMPOSITOR_SEQUENCER, "SEQUENCER", 0, "Sequencer", "Edit node group for sequencer"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + SpaceNode *space_node = space_node_pointer->data_as(); + if (ED_node_is_geometry(space_node)) { + return geometry_nodes_sub_type_items; + } + else { + return compositor_sub_type_items; + } +} + static void rna_SpaceNodeEditor_node_tree_sub_type_update(Main * /*main*/, Scene * /*scene*/, - PointerRNA *ptr) + PointerRNA *space_node_pointer) { - SpaceNode *snode = static_cast(ptr->data); - if (snode->node_tree_sub_type == SNODE_GEOMETRY_TOOL) { - snode->flag &= ~SNODE_PIN; + SpaceNode *space_node = space_node_pointer->data_as(); + if (ED_node_is_geometry(space_node)) { + if (space_node->node_tree_sub_type == SNODE_GEOMETRY_TOOL) { + space_node->flag &= ~SNODE_PIN; + } + } + else { + if (space_node->node_tree_sub_type == SNODE_COMPOSITOR_SEQUENCER) { + space_node->flag &= ~SNODE_PIN; + } } } @@ -7899,20 +7946,6 @@ static void rna_def_space_node(BlenderRNA *brna) {0, nullptr, 0, nullptr, nullptr}, }; - static const EnumPropertyItem geometry_nodes_type_items[] = { - {SNODE_GEOMETRY_MODIFIER, - "MODIFIER", - ICON_MODIFIER_DATA, - "Modifier", - "Edit node group from active object's active modifier"}, - {SNODE_GEOMETRY_TOOL, - "TOOL", - ICON_TOOL_SETTINGS, - "Tool", - "Edit any geometry node group for use as an operator"}, - {0, nullptr, 0, nullptr, nullptr}, - }; - static const EnumPropertyItem backdrop_channels_items[] = { {SNODE_USE_ALPHA, "COLOR_ALPHA", @@ -7966,7 +7999,9 @@ static void rna_def_space_node(BlenderRNA *brna) 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_enum_items(prop, rna_enum_dummy_NULL_items); + RNA_def_property_enum_funcs( + prop, nullptr, nullptr, "rna_SpaceNodeEditor_node_tree_sub_type_itemf"); RNA_def_property_ui_text(prop, "Node Tree Sub-Type", ""); RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); RNA_def_property_update( diff --git a/source/blender/nodes/NOD_composite.hh b/source/blender/nodes/NOD_composite.hh index c99a25c106d..654d73e4738 100644 --- a/source/blender/nodes/NOD_composite.hh +++ b/source/blender/nodes/NOD_composite.hh @@ -83,5 +83,6 @@ compositor::NodeOperation *get_group_input_compositor_operation(compositor::Cont compositor::NodeOperation *get_group_output_compositor_operation(compositor::Context &context, DNode node); void get_compositor_group_output_extra_info(blender::nodes::NodeExtraInfoParams ¶meters); +void get_compositor_group_input_extra_info(blender::nodes::NodeExtraInfoParams ¶meters); } // namespace blender::nodes diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt index e122754bd09..34b018559ce 100644 --- a/source/blender/nodes/composite/CMakeLists.txt +++ b/source/blender/nodes/composite/CMakeLists.txt @@ -14,6 +14,7 @@ set(INC ../../compositor/cached_resources ../../compositor/derived_resources ../../compositor/utilities + ../../sequencer # RNA_prototypes.hh ${CMAKE_BINARY_DIR}/source/blender/makesrna @@ -129,6 +130,7 @@ set(LIB PRIVATE bf::render PRIVATE bf::windowmanager PRIVATE bf::dependencies::optional::opencolorio + PRIVATE bf::sequencer ) add_node_discovery( diff --git a/source/blender/nodes/composite/node_composite_tree.cc b/source/blender/nodes/composite/node_composite_tree.cc index 3c756560d64..c331c3b8059 100644 --- a/source/blender/nodes/composite/node_composite_tree.cc +++ b/source/blender/nodes/composite/node_composite_tree.cc @@ -8,6 +8,7 @@ #include "DNA_node_types.h" #include "DNA_scene_types.h" +#include "DNA_space_types.h" #include "BLI_listbase.h" @@ -22,6 +23,10 @@ #include "UI_resources.hh" +#include "SEQ_modifier.hh" +#include "SEQ_select.hh" +#include "SEQ_sequencer.hh" + #include "node_common.h" #include "RNA_prototypes.hh" @@ -35,6 +40,41 @@ static void composite_get_from_context(const bContext *C, ID **r_id, ID **r_from) { + using namespace blender; + const SpaceNode *snode = CTX_wm_space_node(C); + if (snode->node_tree_sub_type == SNODE_COMPOSITOR_SEQUENCER) { + Scene *sequencer_scene = CTX_data_sequencer_scene(C); + if (!sequencer_scene) { + *r_ntree = nullptr; + return; + } + Editing *ed = seq::editing_get(sequencer_scene); + if (!ed) { + *r_ntree = nullptr; + return; + } + Strip *strip = seq::select_active_get(sequencer_scene); + if (!strip) { + *r_ntree = nullptr; + return; + } + StripModifierData *smd = seq::modifier_get_active(strip); + if (!smd) { + *r_ntree = nullptr; + return; + } + if (smd->type != eSeqModifierType_Compositor) { + *r_ntree = nullptr; + return; + } + SequencerCompositorModifierData *scmd = reinterpret_cast( + smd); + *r_from = nullptr; + *r_id = &sequencer_scene->id; + *r_ntree = scmd->node_group; + return; + } + Scene *scene = CTX_data_scene(C); *r_from = nullptr; diff --git a/source/blender/nodes/composite/nodes/node_composite_file_output.cc b/source/blender/nodes/composite/nodes/node_composite_file_output.cc index b99386d66cd..d4c3631d2b2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_file_output.cc +++ b/source/blender/nodes/composite/nodes/node_composite_file_output.cc @@ -49,6 +49,7 @@ #include "COM_utilities.hh" #include "NOD_compositor_file_output.hh" +#include "NOD_node_extra_info.hh" #include "NOD_socket_items_blend.hh" #include "NOD_socket_items_ops.hh" #include "NOD_socket_items_ui.hh" @@ -376,6 +377,18 @@ static void node_blend_read(bNodeTree & /*tree*/, bNode &node, BlendDataReader & socket_items::blend_read_data(&reader, node); } +static void node_extra_info(NodeExtraInfoParams ¶meters) +{ + SpaceNode *space_node = CTX_wm_space_node(¶meters.C); + if (space_node->node_tree_sub_type != SNODE_COMPOSITOR_SCENE) { + NodeExtraInfoRow row; + row.text = RPT_("Node Unsupported"); + row.tooltip = TIP_("The File Output node is only supported for scene compositing"); + row.icon = ICON_ERROR; + parameters.rows.append(std::move(row)); + } +} + using namespace blender::compositor; class FileOutputOperation : public NodeOperation { @@ -826,6 +839,7 @@ static void node_register() ntype, "NodeCompositorFileOutput", node_free_storage, node_copy_storage); ntype.blend_write_storage_content = node_blend_write; ntype.blend_data_read_storage_content = node_blend_read; + ntype.get_extra_info = node_extra_info; ntype.get_compositor_operation = get_compositor_operation; blender::bke::node_register_type(ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_group_input.cc b/source/blender/nodes/composite/nodes/node_composite_group_input.cc index 5b4f042c3db..a11325168d7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_group_input.cc +++ b/source/blender/nodes/composite/nodes/node_composite_group_input.cc @@ -6,7 +6,16 @@ #include "GPU_shader.hh" -#include "NOD_composite.hh" /* Own include. */ +#include "BLT_translation.hh" + +#include "UI_resources.hh" + +#include "DNA_space_types.h" + +#include "BKE_context.hh" + +#include "NOD_composite.hh" +#include "NOD_node_extra_info.hh" #include "COM_node_operation.hh" #include "COM_utilities.hh" @@ -137,4 +146,56 @@ compositor::NodeOperation *get_group_input_compositor_operation(compositor::Cont return new node_composite_group_input_cc::GroupInputOperation(context, node); } +void get_compositor_group_input_extra_info(blender::nodes::NodeExtraInfoParams ¶meters) +{ + if (parameters.tree.type != NTREE_COMPOSIT) { + return; + } + + SpaceNode *space_node = CTX_wm_space_node(¶meters.C); + if (space_node->edittree != space_node->nodetree) { + return; + } + + if (space_node->node_tree_sub_type != SNODE_COMPOSITOR_SEQUENCER) { + return; + } + + Span group_inputs = parameters.node.output_sockets().drop_back(1); + bool added_warning_for_unsupported_inputs = false; + for (const bNodeSocket *input : group_inputs) { + if (StringRef(input->name) == "Image") { + if (input->type != SOCK_RGBA) { + blender::nodes::NodeExtraInfoRow row; + row.text = IFACE_("Wrong Image Input Type"); + row.icon = ICON_ERROR; + row.tooltip = TIP_("Node group's main Image input should be of type Color"); + parameters.rows.append(std::move(row)); + } + } + else if (StringRef(input->name) == "Mask") { + if (input->type != SOCK_RGBA) { + blender::nodes::NodeExtraInfoRow row; + row.text = IFACE_("Wrong Mask Input Type"); + row.icon = ICON_ERROR; + row.tooltip = TIP_("Node group's Mask input should be of type Color"); + parameters.rows.append(std::move(row)); + } + } + else { + if (added_warning_for_unsupported_inputs) { + continue; + } + blender::nodes::NodeExtraInfoRow row; + row.text = IFACE_("Unsupported Inputs"); + row.icon = ICON_WARNING_LARGE; + row.tooltip = TIP_( + "Only a main Image and Mask inputs are supported, the rest are unsupported and will " + "return zero"); + parameters.rows.append(std::move(row)); + added_warning_for_unsupported_inputs = true; + } + } +} + } // namespace blender::nodes diff --git a/source/blender/nodes/composite/nodes/node_composite_image.cc b/source/blender/nodes/composite/nodes/node_composite_image.cc index 74e57b3f7c7..f424ec97df7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.cc +++ b/source/blender/nodes/composite/nodes/node_composite_image.cc @@ -29,6 +29,7 @@ #include "DNA_image_types.h" #include "DNA_scene_types.h" +#include "DNA_space_types.h" #include "DNA_vec_types.h" #include "RE_engine.h" @@ -749,12 +750,19 @@ static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, Pointer RNA_string_set(&op_ptr, "scene", scene_name); } -/* Give a warning if passes are used with a render engine that does not support them. */ static void node_extra_info(NodeExtraInfoParams ¶meters) { - const Scene *scene = CTX_data_scene(¶meters.C); + SpaceNode *space_node = CTX_wm_space_node(¶meters.C); + if (space_node->node_tree_sub_type != SNODE_COMPOSITOR_SCENE) { + NodeExtraInfoRow row; + row.text = RPT_("Node Unsupported"); + row.tooltip = TIP_("The Render Layers node is only supported for scene compositing"); + row.icon = ICON_ERROR; + parameters.rows.append(std::move(row)); + } /* EEVEE supports passes. */ + const Scene *scene = CTX_data_scene(¶meters.C); if (StringRef(scene->r.engine) == RE_engine_id_BLENDER_EEVEE) { return; } diff --git a/source/blender/nodes/intern/geometry_nodes_log.cc b/source/blender/nodes/intern/geometry_nodes_log.cc index e5d7621c3b4..f4969ac84c4 100644 --- a/source/blender/nodes/intern/geometry_nodes_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_log.cc @@ -934,6 +934,10 @@ Map GeoNodesLog:: static GeoNodesLog *get_root_log(const SpaceNode &snode) { + if (!ED_node_is_geometry(&snode)) { + return nullptr; + } + switch (SpaceNodeGeometryNodesType(snode.node_tree_sub_type)) { case SNODE_GEOMETRY_MODIFIER: { std::optional object_and_modifier = diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc index cf577c1adb9..d7731e47250 100644 --- a/source/blender/nodes/intern/node_common.cc +++ b/source/blender/nodes/intern/node_common.cc @@ -869,6 +869,11 @@ static bool group_output_insert_link(blender::bke::NodeInsertLinkParams ¶ms) } // namespace blender::nodes +static void node_group_input_extra_info(blender::nodes::NodeExtraInfoParams ¶ms) +{ + get_compositor_group_input_extra_info(params); +} + void register_node_type_group_input() { /* used for all tree types, needs dynamic allocation */ @@ -884,6 +889,7 @@ void register_node_type_group_input() blender::bke::node_type_size(*ntype, 140, 80, 400); ntype->declare = blender::nodes::group_input_declare; ntype->insert_link = blender::nodes::group_input_insert_link; + ntype->get_extra_info = node_group_input_extra_info; ntype->get_compositor_operation = blender::nodes::get_group_input_compositor_operation; blender::bke::node_register_type(*ntype); diff --git a/source/blender/sequencer/CMakeLists.txt b/source/blender/sequencer/CMakeLists.txt index d2af2e43836..bd53ebb4cfd 100644 --- a/source/blender/sequencer/CMakeLists.txt +++ b/source/blender/sequencer/CMakeLists.txt @@ -5,6 +5,9 @@ set(INC PUBLIC . intern + ../compositor + ../compositor/cached_resources + ../compositor/derived_resources ../editors/include ../animrig ../makesrna @@ -68,6 +71,7 @@ set(SRC intern/effects/vse_effect_wipe.cc intern/modifiers/MOD_brightness_contrast.cc intern/modifiers/MOD_color_balance.cc + intern/modifiers/MOD_compositor.cc intern/modifiers/MOD_curves.cc intern/modifiers/MOD_hue_correct.cc intern/modifiers/MOD_mask.cc @@ -120,8 +124,10 @@ set(LIB PRIVATE bf::intern::atomic PRIVATE bf::intern::clog PRIVATE bf::intern::guardedalloc + PRIVATE bf::nodes PRIVATE bf::render PRIVATE bf::windowmanager + bf_compositor ) if(WITH_AUDASPACE) diff --git a/source/blender/sequencer/SEQ_modifier.hh b/source/blender/sequencer/SEQ_modifier.hh index c44a46439ba..ab98a35f766 100644 --- a/source/blender/sequencer/SEQ_modifier.hh +++ b/source/blender/sequencer/SEQ_modifier.hh @@ -53,7 +53,11 @@ struct StripModifierTypeInfo { /* Apply modifier on an image buffer. * quad contains four corners of the (pre-transform) strip rectangle in pixel space. */ - void (*apply)(const StripScreenQuad &quad, StripModifierData *smd, ImBuf *ibuf, ImBuf *mask); + void (*apply)(const RenderData *render_data, + const StripScreenQuad &quad, + StripModifierData *smd, + ImBuf *ibuf, + ImBuf *mask); /** Register the panel types for the modifier's UI. */ void (*panel_register)(ARegionType *region_type); diff --git a/source/blender/sequencer/SEQ_modifiertypes.hh b/source/blender/sequencer/SEQ_modifiertypes.hh index 3928052d789..d607e1dc93c 100644 --- a/source/blender/sequencer/SEQ_modifiertypes.hh +++ b/source/blender/sequencer/SEQ_modifiertypes.hh @@ -17,6 +17,7 @@ namespace blender::seq { extern StripModifierTypeInfo seqModifierType_None; extern StripModifierTypeInfo seqModifierType_BrightContrast; extern StripModifierTypeInfo seqModifierType_ColorBalance; +extern StripModifierTypeInfo seqModifierType_Compositor; extern StripModifierTypeInfo seqModifierType_Curves; extern StripModifierTypeInfo seqModifierType_HueCorrect; extern StripModifierTypeInfo seqModifierType_Mask; diff --git a/source/blender/sequencer/SEQ_relations.hh b/source/blender/sequencer/SEQ_relations.hh index 6ef3878632a..a63cb2398e5 100644 --- a/source/blender/sequencer/SEQ_relations.hh +++ b/source/blender/sequencer/SEQ_relations.hh @@ -14,6 +14,7 @@ struct ListBase; struct Main; struct MovieClip; struct ReportList; +struct bNodeTree; struct Scene; struct Strip; @@ -53,6 +54,11 @@ void relations_invalidate_cache(Scene *scene, Strip *strip); void relations_invalidate_cache_raw(Scene *scene, Strip *strip); void relations_invalidate_scene_strips(const Main *bmain, const Scene *scene_target); +/** + * Invalidates the cache for all strips that uses the given node tree as a compositor modifier. + */ +void relations_invalidate_compositor_modifiers(const Main *bmain, const bNodeTree *node_tree); + void relations_invalidate_movieclip_strips(Main *bmain, MovieClip *clip_target); /** * Release FFmpeg handles of strips that are not currently displayed to minimize memory usage. diff --git a/source/blender/sequencer/SEQ_sequencer.hh b/source/blender/sequencer/SEQ_sequencer.hh index 40ca17c29ee..b08103073dc 100644 --- a/source/blender/sequencer/SEQ_sequencer.hh +++ b/source/blender/sequencer/SEQ_sequencer.hh @@ -144,6 +144,16 @@ Strip *lookup_strip_by_name(Editing *ed, const char *key); */ Span lookup_strips_by_scene(Editing *ed, const Scene *key); +/** + * Find all strips using provided compositor node tree as a modifier + * + * \param ed: Editing that owns lookup hash + * \param key: Node tree pointer + * + * \return Span of strips + */ +Span lookup_strips_by_compositor_node_group(Editing *ed, const bNodeTree *key); + /** * Find which meta strip the given timeline channel belongs to. Returns nullptr if it is a global * channel. diff --git a/source/blender/sequencer/intern/modifiers/MOD_brightness_contrast.cc b/source/blender/sequencer/intern/modifiers/MOD_brightness_contrast.cc index 2cd824de03e..a0a108f78dd 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_brightness_contrast.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_brightness_contrast.cc @@ -48,7 +48,8 @@ struct BrightContrastApplyOp { } }; -static void brightcontrast_apply(const StripScreenQuad & /*quad*/, +static void brightcontrast_apply(const RenderData * /*render_data*/, + const StripScreenQuad & /*quad*/, StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) diff --git a/source/blender/sequencer/intern/modifiers/MOD_color_balance.cc b/source/blender/sequencer/intern/modifiers/MOD_color_balance.cc index 2521ba4175a..998d784204f 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_color_balance.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_color_balance.cc @@ -244,7 +244,8 @@ static void colorBalance_init_data(StripModifierData *smd) } } -static void colorBalance_apply(const StripScreenQuad & /*quad*/, +static void colorBalance_apply(const RenderData * /*render_data*/, + const StripScreenQuad & /*quad*/, StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) diff --git a/source/blender/sequencer/intern/modifiers/MOD_compositor.cc b/source/blender/sequencer/intern/modifiers/MOD_compositor.cc new file mode 100644 index 00000000000..846e47d2e41 --- /dev/null +++ b/source/blender/sequencer/intern/modifiers/MOD_compositor.cc @@ -0,0 +1,242 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup sequencer + */ + +#include "BLI_math_base.h" +#include "BLI_rect.h" + +#include "BLT_translation.hh" + +#include "COM_context.hh" +#include "COM_domain.hh" +#include "COM_evaluator.hh" + +#include "DNA_sequence_types.h" + +#include "DEG_depsgraph_query.hh" + +#include "IMB_colormanagement.hh" + +#include "SEQ_modifier.hh" +#include "SEQ_modifiertypes.hh" +#include "SEQ_render.hh" + +#include "UI_interface.hh" +#include "UI_interface_layout.hh" + +#include "RNA_access.hh" + +#include "modifier.hh" +#include "render.hh" + +namespace blender::seq { + +class CompositorContext : public compositor::Context { + private: + const RenderData &render_data_; + const SequencerCompositorModifierData *modifier_data_; + + ImBuf *image_buffer_; + ImBuf *mask_buffer_; + + public: + CompositorContext(const RenderData &render_data, + const SequencerCompositorModifierData *modifier_data, + ImBuf *image_buffer, + ImBuf *mask_buffer) + : compositor::Context(), + render_data_(render_data), + modifier_data_(modifier_data), + image_buffer_(image_buffer), + mask_buffer_(mask_buffer) + { + } + + const Scene &get_scene() const override + { + return *render_data_.scene; + } + + const bNodeTree &get_node_tree() const override + { + return *DEG_get_evaluated(render_data_.depsgraph, modifier_data_->node_group); + } + + compositor::OutputTypes needed_outputs() const override + { + compositor::OutputTypes needed_outputs = compositor::OutputTypes::Composite; + if (!render_data_.for_render) { + needed_outputs |= compositor::OutputTypes::Viewer; + } + return needed_outputs; + } + + bool treat_viewer_as_compositor_output() const override + { + return true; + } + + Bounds get_compositing_region() const override + { + return Bounds(int2(0), int2(image_buffer_->x, image_buffer_->y)); + } + + compositor::Result get_output() override + { + compositor::Result result = this->create_result(compositor::ResultType::Color); + result.wrap_external(image_buffer_->float_buffer.data, + int2(image_buffer_->x, image_buffer_->y)); + return result; + } + + compositor::Result get_viewer_output(compositor::Domain /*domain*/, + bool /*is_data*/, + compositor::ResultPrecision /*precision*/) override + { + compositor::Result result = this->create_result(compositor::ResultType::Color); + result.wrap_external(image_buffer_->float_buffer.data, + int2(image_buffer_->x, image_buffer_->y)); + return result; + } + + compositor::Result get_input(const Scene * /*scene*/, + int /*view_layer_id*/, + const char *pass_name) override + { + compositor::Result result = this->create_result(compositor::ResultType::Color); + + if (StringRef(pass_name) == "Image") { + result.wrap_external(image_buffer_->float_buffer.data, + int2(image_buffer_->x, image_buffer_->y)); + } + else if (StringRef(pass_name) == "Mask" && mask_buffer_) { + result.wrap_external(mask_buffer_->float_buffer.data, + int2(mask_buffer_->x, mask_buffer_->y)); + } + + return result; + } + + bool use_gpu() const override + { + return false; + } +}; + +static void compositor_modifier_init_data(StripModifierData *strip_modifier_data) +{ + SequencerCompositorModifierData *modifier_data = + reinterpret_cast(strip_modifier_data); + modifier_data->node_group = nullptr; +} + +static bool ensure_linear_float_buffer(ImBuf *ibuf) +{ + if (!ibuf) { + return false; + } + + /* Already have scene linear float pixels, nothing to do. */ + if (ibuf->float_buffer.data && + IMB_colormanagement_space_is_scene_linear(ibuf->float_buffer.colorspace)) + { + return true; + } + + if (ibuf->float_buffer.data == nullptr) { + IMB_float_from_byte(ibuf); + } + else { + const char *from_colorspace = IMB_colormanagement_get_float_colorspace(ibuf); + const char *to_colorspace = IMB_colormanagement_role_colorspace_name_get( + COLOR_ROLE_SCENE_LINEAR); + IMB_colormanagement_transform_float(ibuf->float_buffer.data, + ibuf->x, + ibuf->y, + ibuf->channels, + from_colorspace, + to_colorspace, + true); + IMB_colormanagement_assign_float_colorspace(ibuf, to_colorspace); + } + return false; +} + +static void compositor_modifier_apply(const RenderData *render_data, + const StripScreenQuad & /*quad*/, + StripModifierData *strip_modifier_data, + ImBuf *image_buffer, + ImBuf *mask) +{ + const SequencerCompositorModifierData *modifier_data = + reinterpret_cast(strip_modifier_data); + if (!modifier_data->node_group) { + return; + } + + ensure_linear_float_buffer(mask); + const bool was_float_linear = ensure_linear_float_buffer(image_buffer); + const bool was_byte = image_buffer->float_buffer.data == nullptr; + + CompositorContext context(*render_data, modifier_data, image_buffer, mask); + compositor::Evaluator evaluator(context); + evaluator.evaluate(); + + if (was_float_linear) { + return; + } + + if (was_byte) { + IMB_byte_from_float(image_buffer); + IMB_free_float_pixels(image_buffer); + } + else { + seq_imbuf_to_sequencer_space(render_data->scene, image_buffer, true); + } +} + +static void compositor_modifier_panel_draw(const bContext *C, Panel *panel) +{ + uiLayout *layout = panel->layout; + PointerRNA *ptr = UI_panel_custom_data_get(panel); + + layout->use_property_split_set(true); + + uiTemplateID(layout, + C, + ptr, + "node_group", + "NODE_OT_new_compositor_sequencer_node_group", + nullptr, + nullptr); + + if (uiLayout *mask_input_layout = layout->panel_prop( + C, ptr, "open_mask_input_panel", IFACE_("Mask Input"))) + { + draw_mask_input_type_settings(C, mask_input_layout, ptr); + } +} + +static void compositor_modifier_register(ARegionType *region_type) +{ + modifier_panel_register( + region_type, eSeqModifierType_Compositor, compositor_modifier_panel_draw); +} + +StripModifierTypeInfo seqModifierType_Compositor = { + /*idname*/ "Compositor", + /*name*/ CTX_N_(BLT_I18NCONTEXT_ID_SEQUENCE, "Compositor"), + /*struct_name*/ "SequencerCompositorModifierData", + /*struct_size*/ sizeof(SequencerCompositorModifierData), + /*init_data*/ compositor_modifier_init_data, + /*free_data*/ nullptr, + /*copy_data*/ nullptr, + /*apply*/ compositor_modifier_apply, + /*panel_register*/ compositor_modifier_register, +}; + +}; // namespace blender::seq diff --git a/source/blender/sequencer/intern/modifiers/MOD_curves.cc b/source/blender/sequencer/intern/modifiers/MOD_curves.cc index 52e6945e633..465d200cda3 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_curves.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_curves.cc @@ -65,7 +65,8 @@ struct CurvesApplyOp { } }; -static void curves_apply(const StripScreenQuad & /*quad*/, +static void curves_apply(const RenderData * /*render_data*/, + const StripScreenQuad & /*quad*/, StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) diff --git a/source/blender/sequencer/intern/modifiers/MOD_hue_correct.cc b/source/blender/sequencer/intern/modifiers/MOD_hue_correct.cc index 82c855d472d..177dd0feb08 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_hue_correct.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_hue_correct.cc @@ -100,7 +100,8 @@ struct HueCorrectApplyOp { } }; -static void hue_correct_apply(const StripScreenQuad & /*quad*/, +static void hue_correct_apply(const RenderData * /*render_data*/, + const StripScreenQuad & /*quad*/, StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) diff --git a/source/blender/sequencer/intern/modifiers/MOD_mask.cc b/source/blender/sequencer/intern/modifiers/MOD_mask.cc index c9499d7bfc7..2474fe626f0 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_mask.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_mask.cc @@ -63,7 +63,8 @@ struct MaskApplyOp { } }; -static void maskmodifier_apply(const StripScreenQuad & /*quad*/, +static void maskmodifier_apply(const RenderData * /*render_data*/, + const StripScreenQuad & /*quad*/, StripModifierData * /*smd*/, ImBuf *ibuf, ImBuf *mask) diff --git a/source/blender/sequencer/intern/modifiers/MOD_tonemap.cc b/source/blender/sequencer/intern/modifiers/MOD_tonemap.cc index 3c97ba1fb5c..f4660f9d5eb 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_tonemap.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_tonemap.cc @@ -280,7 +280,8 @@ static AreaLuminance tonemap_calc_input_luminance(const StripScreenQuad &quad, c return lum; } -static void tonemapmodifier_apply(const StripScreenQuad &quad, +static void tonemapmodifier_apply(const RenderData * /*render_data*/, + const StripScreenQuad &quad, StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) diff --git a/source/blender/sequencer/intern/modifiers/MOD_white_balance.cc b/source/blender/sequencer/intern/modifiers/MOD_white_balance.cc index 871b1e1b404..e28e1d577c5 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_white_balance.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_white_balance.cc @@ -58,7 +58,8 @@ struct WhiteBalanceApplyOp { } }; -static void whiteBalance_apply(const StripScreenQuad & /*quad*/, +static void whiteBalance_apply(const RenderData * /*render_data*/, + const StripScreenQuad & /*quad*/, StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) diff --git a/source/blender/sequencer/intern/modifiers/modifier.cc b/source/blender/sequencer/intern/modifiers/modifier.cc index 8d20d49443d..1792dc2f675 100644 --- a/source/blender/sequencer/intern/modifiers/modifier.cc +++ b/source/blender/sequencer/intern/modifiers/modifier.cc @@ -371,6 +371,7 @@ static void modifier_types_init(StripModifierTypeInfo *types[]) INIT_TYPE(None); INIT_TYPE(BrightContrast); INIT_TYPE(ColorBalance); + INIT_TYPE(Compositor); INIT_TYPE(Curves); INIT_TYPE(HueCorrect); INIT_TYPE(Mask); @@ -528,7 +529,7 @@ void modifier_apply_stack(const RenderData *context, } ImBuf *mask = modifier_mask_get(smd, context, timeline_frame, frame_offset); - smti->apply(quad, smd, ibuf, mask); + smti->apply(context, quad, smd, ibuf, mask); if (mask) { IMB_freeImBuf(mask); } diff --git a/source/blender/sequencer/intern/strip_lookup.cc b/source/blender/sequencer/intern/strip_lookup.cc index 3be6171ca23..2681e1048ce 100644 --- a/source/blender/sequencer/intern/strip_lookup.cc +++ b/source/blender/sequencer/intern/strip_lookup.cc @@ -10,6 +10,7 @@ #include "sequencer.hh" #include "DNA_listBase.h" +#include "DNA_node_types.h" #include "DNA_scene_types.h" #include "DNA_sequence_types.h" @@ -29,6 +30,7 @@ static Mutex lookup_lock; struct StripLookup { blender::Map strip_by_name; blender::Map> strips_by_scene; + blender::Map> strips_by_compositor_node_group; blender::Map meta_by_strip; blender::Map> effects_by_strip; blender::Map owner_by_channel; @@ -55,6 +57,25 @@ static void strip_by_scene_lookup_build(Strip *strip, StripLookup *lookup) strips.add(strip); } +static void strip_by_compositor_node_group_lookup_build(Strip *strip, StripLookup *lookup) +{ + LISTBASE_FOREACH (StripModifierData *, modifier, &strip->modifiers) { + if (modifier->type != eSeqModifierType_Compositor) { + continue; + } + + const SequencerCompositorModifierData *modifier_data = + reinterpret_cast(modifier); + if (!modifier_data->node_group) { + continue; + } + + VectorSet &strips = lookup->strips_by_compositor_node_group.lookup_or_add_default( + modifier_data->node_group); + strips.add(strip); + } +} + static void strip_lookup_build_effect(Strip *strip, StripLookup *lookup) { if (!strip->is_effect()) { @@ -80,6 +101,7 @@ static void strip_lookup_build_from_seqbase(Strip *parent_meta, lookup->meta_by_strip.add(strip, parent_meta); strip_lookup_build_effect(strip, lookup); strip_by_scene_lookup_build(strip, lookup); + strip_by_compositor_node_group_lookup_build(strip, lookup); if (strip->type == STRIP_TYPE_META) { strip_lookup_build_from_seqbase(strip, &strip->seqbase, lookup); @@ -150,6 +172,18 @@ Span lookup_strips_by_scene(Editing *ed, const Scene *key) return strips.as_span(); } +Span lookup_strips_by_compositor_node_group(Editing *ed, const bNodeTree *key) +{ + BLI_assert(ed != nullptr); + BLI_assert(key->type == NTREE_COMPOSIT); + + std::lock_guard lock(lookup_lock); + strip_lookup_update_if_needed(ed, &ed->runtime.strip_lookup); + StripLookup *lookup = ed->runtime.strip_lookup; + VectorSet &strips = lookup->strips_by_compositor_node_group.lookup_or_add_default(key); + return strips.as_span(); +} + Strip *lookup_meta_by_strip(Editing *ed, const Strip *key) { BLI_assert(ed != nullptr); diff --git a/source/blender/sequencer/intern/strip_relations.cc b/source/blender/sequencer/intern/strip_relations.cc index 96dfb2bf35a..d46d47ef1f1 100644 --- a/source/blender/sequencer/intern/strip_relations.cc +++ b/source/blender/sequencer/intern/strip_relations.cc @@ -172,6 +172,17 @@ void relations_invalidate_scene_strips(const Main *bmain, const Scene *scene_tar } } +void relations_invalidate_compositor_modifiers(const Main *bmain, const bNodeTree *node_tree) +{ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->ed != nullptr) { + for (Strip *strip : lookup_strips_by_compositor_node_group(editing_get(scene), node_tree)) { + relations_invalidate_cache(scene, strip); + } + } + } +} + static void invalidate_movieclip_strips(Scene *scene, MovieClip *clip_target, ListBase *seqbase) { for (Strip *strip = static_cast(seqbase->first); strip != nullptr; strip = strip->next)