mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
VSE: Compositor based effects/transitions
Implements compositor-based Effects in VSE (#150636). Compositor effects can be of all currently existing effect kinds: Two input effects (transitions): - Similar built-in effects: Cross fade, Wipe. - Created on two selected strips. - When creating a node group for them, the group input has "Input 1", "Input 2" and a float "Effect Fader" (0..1 over strip range) inputs. - Expected use case: transitions or fancy combination of two strips. Single input effects: - Similar built-in effects: Blur, Glow. - Created on one selected strip. - When creating a node group for them, the group input has "Input" and a float "Effect Fader" (0..1 over strip range) inputs. - Expected use case: post-processing of input in screen-space (different from strip modifiers, which operate in local space before strip transform is applied). Zero-input effects: - Similar built-in effects: Solid Color, Text. - Created when no strips are selected. - When creating a node group for them, the group input only has a float "Effect Fader" (0..1 over strip range) input. - Expected use case: gradients, procedural textures. General notes: Menu item to create a Compositor strip is in top level menu; next to Adjustment strip. It is not under "effect" or "transition" since compositor strip can be 1 or 0 inputs. Properties inspector for compositor strips displays their inputs (as for any other effect), and the compositor node tree selector. Note that this PR does not change anything in how effect inputs are selected within VSE - the new Compositor effect works just like any other effect that already exists. You create a new strip with none, one or two strips selected, and that are the inputs of the effect. These can be swapped (for two input effects) or reassigned later with the usual operators. Implementation notes: The actual compositor effect code is very small; most of the work inside the PR was: - Changes across VSE codebase to allow non-fixed amount of inputs. Since Compositor strip type can have either 0, 1 or 2 inputs now. - Various places related to depsgraph & co, where it was handling strip Compositor Modifiers; very similar code has been added to also handle Compositor Effects. - Just like compositor modifier, the compositor effect right now only works on the CPU. Getting both to use GPU is future task. Pull Request: https://projects.blender.org/blender/blender/pulls/150694
This commit is contained in:
parent
a66c520538
commit
04aea5a4e9
52 changed files with 784 additions and 289 deletions
|
|
@ -62,7 +62,7 @@ class STRIP_PT_strip(StripButtonsPanel, Panel):
|
|||
}:
|
||||
icon_header = 'SHADERFX'
|
||||
elif strip_type in {
|
||||
'CROSS', 'GAMMA_CROSS', 'WIPE',
|
||||
'CROSS', 'GAMMA_CROSS', 'WIPE', 'COMPOSITOR',
|
||||
}:
|
||||
icon_header = 'ARROW_LEFTRIGHT'
|
||||
elif strip_type == 'SCENE':
|
||||
|
|
@ -146,6 +146,7 @@ class STRIP_PT_effect(StripButtonsPanel, Panel):
|
|||
'ALPHA_UNDER',
|
||||
'CROSS',
|
||||
'GAMMA_CROSS',
|
||||
'COMPOSITOR',
|
||||
'MULTIPLY',
|
||||
'WIPE',
|
||||
'GLOW',
|
||||
|
|
@ -165,6 +166,11 @@ class STRIP_PT_effect(StripButtonsPanel, Panel):
|
|||
|
||||
layout.active = not strip.mute
|
||||
|
||||
strip_type = strip.type
|
||||
|
||||
if strip_type == 'COMPOSITOR':
|
||||
layout.template_ID(strip, "node_group", new="node.new_compositor_sequencer_node_group")
|
||||
|
||||
if strip.input_count > 0:
|
||||
col = layout.column()
|
||||
row = col.row()
|
||||
|
|
@ -176,8 +182,6 @@ class STRIP_PT_effect(StripButtonsPanel, Panel):
|
|||
row.prop(strip, "input_2")
|
||||
row.operator("sequencer.swap_inputs", text="", icon='SORT_DESC')
|
||||
|
||||
strip_type = strip.type
|
||||
|
||||
if strip_type == 'COLOR':
|
||||
layout.template_color_picker(strip, "color", value_slider=True, cubic=True)
|
||||
layout.prop(strip, "color", text="")
|
||||
|
|
@ -261,7 +265,7 @@ class STRIP_PT_effect(StripButtonsPanel, Panel):
|
|||
layout.prop(strip, "wrap_width", text="Wrap Width")
|
||||
|
||||
col = layout.column(align=True)
|
||||
if strip_type in {'CROSS', 'GAMMA_CROSS', 'WIPE', 'ALPHA_OVER', 'ALPHA_UNDER'}:
|
||||
if strip_type in {'CROSS', 'GAMMA_CROSS', 'WIPE', 'ALPHA_OVER', 'ALPHA_UNDER', 'COMPOSITOR'}:
|
||||
col.prop(strip, "use_default_fade", text="Default Fade")
|
||||
if not strip.use_default_fade:
|
||||
col.prop(strip, "effect_fader", text="Effect Fader")
|
||||
|
|
@ -965,7 +969,7 @@ class STRIP_PT_adjust_video(StripButtonsPanel, Panel):
|
|||
return strip.type in {
|
||||
'MOVIE', 'IMAGE', 'SCENE', 'MOVIECLIP', 'MASK',
|
||||
'META', 'ADD', 'SUBTRACT', 'ALPHA_OVER',
|
||||
'ALPHA_UNDER', 'CROSS', 'GAMMA_CROSS', 'MULTIPLY',
|
||||
'ALPHA_UNDER', 'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'COMPOSITOR',
|
||||
'WIPE', 'GLOW', 'COLOR', 'MULTICAM', 'SPEED', 'ADJUSTMENT', 'COLORMIX',
|
||||
}
|
||||
|
||||
|
|
@ -997,7 +1001,7 @@ class STRIP_PT_adjust_color(StripButtonsPanel, Panel):
|
|||
return strip.type in {
|
||||
'MOVIE', 'IMAGE', 'SCENE', 'MOVIECLIP', 'MASK',
|
||||
'META', 'ADD', 'SUBTRACT', 'ALPHA_OVER',
|
||||
'ALPHA_UNDER', 'CROSS', 'GAMMA_CROSS', 'MULTIPLY',
|
||||
'ALPHA_UNDER', 'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'COMPOSITOR',
|
||||
'WIPE', 'GLOW', 'COLOR', 'MULTICAM', 'SPEED', 'ADJUSTMENT', 'COLORMIX',
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -158,20 +158,27 @@ class NODE_HT_header(Header):
|
|||
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:
|
||||
if active_modifier.node_group:
|
||||
row.template_ID(active_modifier,
|
||||
"node_group",
|
||||
new="node.duplicate_compositing_modifier_node_group")
|
||||
else:
|
||||
row.template_ID(
|
||||
active_modifier,
|
||||
if active_strip:
|
||||
active_modifier = active_strip.modifiers.active
|
||||
is_compositor_modifier_active = active_modifier and active_modifier.type == 'COMPOSITOR'
|
||||
is_compositor_effect_active = active_strip.type == 'COMPOSITOR'
|
||||
if is_compositor_effect_active and not snode.pin:
|
||||
row.template_ID( # @TODO: duplicate operator
|
||||
active_strip,
|
||||
"node_group",
|
||||
new="node.new_compositor_sequencer_node_group")
|
||||
elif active_strip and active_strip.type != 'SOUND':
|
||||
row.template_ID(snode, "node_tree", new="node.new_compositor_sequencer_node_group")
|
||||
elif is_compositor_modifier_active and not snode.pin:
|
||||
if active_modifier.node_group:
|
||||
row.template_ID(active_modifier,
|
||||
"node_group",
|
||||
new="node.duplicate_compositing_modifier_node_group")
|
||||
else:
|
||||
row.template_ID(
|
||||
active_modifier,
|
||||
"node_group",
|
||||
new="node.new_compositor_sequencer_node_group")
|
||||
elif active_strip.type != 'SOUND':
|
||||
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="")
|
||||
|
|
|
|||
|
|
@ -628,7 +628,7 @@ class SEQUENCER_MT_change(Menu):
|
|||
layout.operator_context = 'INVOKE_DEFAULT'
|
||||
if strip and strip.type in {
|
||||
'CROSS', 'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
|
||||
'GAMMA_CROSS', 'MULTIPLY', 'WIPE', 'GLOW',
|
||||
'GAMMA_CROSS', 'COMPOSITOR', 'MULTIPLY', 'WIPE', 'GLOW',
|
||||
'SPEED', 'MULTICAM', 'ADJUSTMENT', 'GAUSSIAN_BLUR',
|
||||
}:
|
||||
layout.menu("SEQUENCER_MT_strip_effect_change")
|
||||
|
|
@ -727,14 +727,16 @@ class SEQUENCER_MT_add(Menu):
|
|||
layout.operator("sequencer.effect_strip_add", text="Text", icon='FONT_DATA').type = 'TEXT'
|
||||
|
||||
layout.separator()
|
||||
total, nonsound = selected_strips_count(context)
|
||||
|
||||
layout.operator("sequencer.effect_strip_add", text="Adjustment Layer", icon='COLOR').type = 'ADJUSTMENT'
|
||||
col = layout.column()
|
||||
col.operator("sequencer.effect_strip_add", text="Compositor", icon='NODE_COMPOSITING').type = 'COMPOSITOR'
|
||||
col.enabled = nonsound < 3
|
||||
|
||||
layout.operator_context = 'INVOKE_DEFAULT'
|
||||
layout.menu("SEQUENCER_MT_add_effect", icon='SHADERFX')
|
||||
|
||||
total, nonsound = selected_strips_count(context)
|
||||
|
||||
col = layout.column()
|
||||
col.menu("SEQUENCER_MT_add_transitions", icon='ARROW_LEFTRIGHT')
|
||||
# Enable for video transitions or sound cross-fade.
|
||||
|
|
@ -1023,6 +1025,10 @@ class SEQUENCER_MT_strip_effect_change(Menu):
|
|||
|
||||
strip = context.active_strip
|
||||
|
||||
col = layout.column()
|
||||
col.operator("sequencer.change_effect_type", text="Compositor").type = 'COMPOSITOR'
|
||||
layout.separator()
|
||||
|
||||
col = layout.column()
|
||||
col.operator("sequencer.change_effect_type", text="Adjustment Layer").type = 'ADJUSTMENT'
|
||||
col.operator("sequencer.change_effect_type", text="Multicam Selector").type = 'MULTICAM'
|
||||
|
|
@ -1163,7 +1169,7 @@ class SEQUENCER_MT_strip(Menu):
|
|||
|
||||
if strip_type in {
|
||||
'CROSS', 'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
|
||||
'GAMMA_CROSS', 'MULTIPLY', 'WIPE', 'GLOW',
|
||||
'GAMMA_CROSS', 'COMPOSITOR', 'MULTIPLY', 'WIPE', 'GLOW',
|
||||
'SPEED', 'MULTICAM', 'ADJUSTMENT', 'GAUSSIAN_BLUR',
|
||||
}:
|
||||
layout.separator()
|
||||
|
|
@ -1346,7 +1352,7 @@ class SEQUENCER_MT_context_menu(Menu):
|
|||
|
||||
if strip_type in {
|
||||
'CROSS', 'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER',
|
||||
'GAMMA_CROSS', 'MULTIPLY', 'WIPE', 'GLOW',
|
||||
'GAMMA_CROSS', 'COMPOSITOR', 'MULTIPLY', 'WIPE', 'GLOW',
|
||||
'SPEED', 'MULTICAM', 'ADJUSTMENT', 'GAUSSIAN_BLUR',
|
||||
}:
|
||||
layout.separator()
|
||||
|
|
|
|||
|
|
@ -829,6 +829,10 @@ static bool strip_foreach_member_id_cb(Strip *strip, void *user_data)
|
|||
IDP_foreach_property(strip->system_properties, IDP_TYPE_FILTER_ID, [&](IDProperty *prop) {
|
||||
BKE_lib_query_idpropertiesForeachIDLink_callback(prop, data);
|
||||
});
|
||||
if (strip->type == STRIP_TYPE_COMPOSITOR && strip->effectdata) {
|
||||
CompositorEffectVars *comp_data = static_cast<CompositorEffectVars *>(strip->effectdata);
|
||||
FOREACHID_PROCESS_IDSUPER(data, comp_data->node_group, IDWALK_CB_USER);
|
||||
}
|
||||
/* TODO: This could use `seq::foreach_strip_modifier_id`, but because `FOREACHID_PROCESS_IDSUPER`
|
||||
* doesn't take IDs but "ID supers", it makes it a bit more cumbersome. */
|
||||
for (StripModifierData &smd : strip->modifiers) {
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@
|
|||
#include "readfile.hh"
|
||||
|
||||
#include "SEQ_channels.hh"
|
||||
#include "SEQ_effects.hh"
|
||||
#include "SEQ_iterator.hh"
|
||||
#include "SEQ_retiming.hh"
|
||||
#include "SEQ_sequencer.hh"
|
||||
|
|
@ -1765,9 +1764,7 @@ static bool version_merge_still_offsets(Strip *strip, void * /*user_data*/)
|
|||
|
||||
static bool version_set_seq_single_frame_content(Strip *strip, void * /*user_data*/)
|
||||
{
|
||||
if ((strip->len == 1) && (strip->type == STRIP_TYPE_IMAGE ||
|
||||
(strip->is_effect() && seq::effect_get_num_inputs(strip->type) == 0)))
|
||||
{
|
||||
if ((strip->len == 1) && (strip->type == STRIP_TYPE_IMAGE || !strip->is_effect_with_inputs())) {
|
||||
strip->flag |= SEQ_SINGLE_FRAME_CONTENT;
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -2354,6 +2354,13 @@ 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);
|
||||
}
|
||||
|
||||
if (strip->type == STRIP_TYPE_COMPOSITOR && strip->effectdata) {
|
||||
CompositorEffectVars *comp_data = static_cast<CompositorEffectVars *>(strip->effectdata);
|
||||
if (comp_data->node_group) {
|
||||
nb->build_nodetree(comp_data->node_group);
|
||||
}
|
||||
}
|
||||
for (StripModifierData &modifier : strip->modifiers) {
|
||||
if (modifier.type != eSeqModifierType_Compositor) {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -3467,6 +3467,15 @@ 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);
|
||||
}
|
||||
if (strip->type == STRIP_TYPE_COMPOSITOR && strip->effectdata) {
|
||||
const CompositorEffectVars *comp_data = static_cast<CompositorEffectVars *>(strip->effectdata);
|
||||
if (comp_data->node_group) {
|
||||
cd->builder->build_nodetree(comp_data->node_group);
|
||||
OperationKey node_tree_key(
|
||||
&comp_data->node_group->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT);
|
||||
cd->builder->add_relation(node_tree_key, cd->sequencer_key, "Effect's Node Group");
|
||||
}
|
||||
}
|
||||
for (StripModifierData &modifier : strip->modifiers) {
|
||||
if (modifier.type != eSeqModifierType_Compositor) {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -3447,10 +3447,16 @@ static size_t animdata_filter_dopesheet_scene(bAnimContext *ac,
|
|||
ac, &tmp_data, reinterpret_cast<ID *>(sce), ntree, filter_mode);
|
||||
}
|
||||
|
||||
/* Strip modifier node trees. */
|
||||
/* VSE strip node trees. */
|
||||
if (ed && !(ac->filters.flag & ADS_FILTER_NONTREE)) {
|
||||
VectorSet<ID *> node_trees;
|
||||
seq::foreach_strip(&ed->seqbase, [&](Strip *strip) {
|
||||
if (strip->type == STRIP_TYPE_COMPOSITOR && strip->effectdata) {
|
||||
CompositorEffectVars *comp_data = static_cast<CompositorEffectVars *>(strip->effectdata);
|
||||
if (comp_data->node_group) {
|
||||
node_trees.add(reinterpret_cast<ID *>(comp_data->node_group));
|
||||
}
|
||||
}
|
||||
seq::foreach_strip_modifier_id(strip, [&](ID *id) {
|
||||
if (GS(id->name) == ID_NT) {
|
||||
node_trees.add(id);
|
||||
|
|
|
|||
|
|
@ -327,11 +327,11 @@ static void update_sequencer(const DEGEditorUpdateContext *update_ctx, Main *bma
|
|||
}
|
||||
}
|
||||
|
||||
/* Invalidate cache for strips that use this compositing tree as a modifier. */
|
||||
/* Invalidate cache for strips that use this compositing tree. */
|
||||
if (GS(id->name) == ID_NT) {
|
||||
const bNodeTree *node_tree = reinterpret_cast<const bNodeTree *>(id);
|
||||
if (node_tree->type == NTREE_COMPOSIT) {
|
||||
seq::relations_invalidate_compositor_modifiers(bmain, node_tree);
|
||||
seq::relations_invalidate_compositor_users(bmain, node_tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1850,15 +1850,36 @@ void NODE_OT_duplicate_compositing_modifier_node_group(wmOperatorType *ot)
|
|||
/** \name New Compositor Sequencer Node Group Operator
|
||||
* \{ */
|
||||
|
||||
static void initialize_compositor_sequencer_node_group(const bContext *C, bNodeTree &ntree)
|
||||
static void initialize_compositor_sequencer_node_group(const bContext *C,
|
||||
bNodeTree &ntree,
|
||||
bool for_effect,
|
||||
int effect_input_count)
|
||||
{
|
||||
BLI_assert(ntree.type == NTREE_COMPOSIT);
|
||||
BLI_assert(BLI_listbase_count(&ntree.nodes) == 0);
|
||||
|
||||
ntree.tree_interface.add_socket(
|
||||
"Image", "", "NodeSocketColor", NODE_INTERFACE_SOCKET_INPUT, nullptr);
|
||||
ntree.tree_interface.add_socket(
|
||||
"Mask", "", "NodeSocketColor", NODE_INTERFACE_SOCKET_INPUT, nullptr);
|
||||
if (for_effect) {
|
||||
/* Effect: Input 1, Input 2, Fader depending on input count. */
|
||||
if (effect_input_count == 2) {
|
||||
ntree.tree_interface.add_socket(
|
||||
"Input 1", "", "NodeSocketColor", NODE_INTERFACE_SOCKET_INPUT, nullptr);
|
||||
ntree.tree_interface.add_socket(
|
||||
"Input 2", "", "NodeSocketColor", NODE_INTERFACE_SOCKET_INPUT, nullptr);
|
||||
}
|
||||
else if (effect_input_count == 1) {
|
||||
ntree.tree_interface.add_socket(
|
||||
"Input", "", "NodeSocketColor", NODE_INTERFACE_SOCKET_INPUT, nullptr);
|
||||
}
|
||||
ntree.tree_interface.add_socket(
|
||||
"Effect Fader", "", "NodeSocketFloat", NODE_INTERFACE_SOCKET_INPUT, nullptr);
|
||||
}
|
||||
else {
|
||||
/* Modifier: Image, Mask. */
|
||||
ntree.tree_interface.add_socket(
|
||||
"Image", "", "NodeSocketColor", NODE_INTERFACE_SOCKET_INPUT, nullptr);
|
||||
ntree.tree_interface.add_socket(
|
||||
"Mask", "", "NodeSocketColor", NODE_INTERFACE_SOCKET_INPUT, nullptr);
|
||||
}
|
||||
ntree.tree_interface.add_socket(
|
||||
"Image", "", "NodeSocketColor", NODE_INTERFACE_SOCKET_OUTPUT, nullptr);
|
||||
|
||||
|
|
@ -1908,23 +1929,40 @@ static wmOperatorStatus new_compositor_sequencer_node_group_exec(bContext *C, wm
|
|||
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);
|
||||
|
||||
Strip *strip = seq::select_active_get(scene);
|
||||
const bool is_effect_active = strip != nullptr && strip->type == STRIP_TYPE_COMPOSITOR;
|
||||
int effect_input_count = 0;
|
||||
if (is_effect_active) {
|
||||
effect_input_count = (strip->input1 && strip->input2) ? 2 : (strip->input1 ? 1 : 0);
|
||||
}
|
||||
|
||||
bNodeTree *ntree = new_node_tree_impl(C, tree_name, "CompositorNodeTree");
|
||||
initialize_compositor_sequencer_node_group(C, *ntree, is_effect_active, effect_input_count);
|
||||
|
||||
/* Add modifier and assign node tree when the strip has no active compositor modifier. */
|
||||
if (strip != nullptr && strip->type != STRIP_TYPE_SOUND) {
|
||||
bool assigned_node_tree = false;
|
||||
|
||||
/* If strip is a compositor effect: assign the node tree. */
|
||||
if (strip->type == STRIP_TYPE_COMPOSITOR && strip->effectdata) {
|
||||
CompositorEffectVars *comp_data = static_cast<CompositorEffectVars *>(strip->effectdata);
|
||||
comp_data->node_group = ntree;
|
||||
assigned_node_tree = true;
|
||||
}
|
||||
|
||||
/* Otherwise, if there's no active compositor modifier: create one and assign the node tree. */
|
||||
StripModifierData *active_smd = seq::modifier_get_active(strip);
|
||||
if (!active_smd || active_smd->type != eSeqModifierType_Compositor) {
|
||||
if (!assigned_node_tree && (!active_smd || active_smd->type != eSeqModifierType_Compositor)) {
|
||||
StripModifierData *smd = seq::modifier_new(strip, nullptr, eSeqModifierType_Compositor);
|
||||
seq::modifier_persistent_uid_init(*strip, *smd);
|
||||
|
||||
SequencerCompositorModifierData *modifier_data =
|
||||
reinterpret_cast<SequencerCompositorModifierData *>(smd);
|
||||
modifier_data->node_group = ntree;
|
||||
seq::relations_invalidate_cache(scene, strip);
|
||||
assigned_node_tree = true;
|
||||
}
|
||||
|
||||
if (assigned_node_tree) {
|
||||
seq::relations_invalidate_cache(scene, strip);
|
||||
/* Tag depsgraph relations for an update since the modifier should now be referencing a
|
||||
* different node tree. */
|
||||
DEG_relations_tag_update(bmain);
|
||||
|
|
|
|||
|
|
@ -169,52 +169,44 @@ static void get_context_path_node_compositor(const bContext &C,
|
|||
SpaceNode &snode,
|
||||
Vector<ui::ContextPathItem> &path)
|
||||
{
|
||||
bool skip_base = false;
|
||||
if (snode.flag & SNODE_PIN) {
|
||||
context_path_add_node_tree_and_node_groups(snode, path);
|
||||
/* Pinned: nothing extra; will be handled below. */
|
||||
}
|
||||
else if (snode.node_tree_sub_type == SNODE_COMPOSITOR_SEQUENCER) {
|
||||
Scene *sequencer_scene = CTX_data_sequencer_scene(&C);
|
||||
if (sequencer_scene) {
|
||||
ui::context_path_add_generic(path, *RNA_Scene, sequencer_scene, ICON_SCENE);
|
||||
Strip *strip = seq::select_active_get(sequencer_scene);
|
||||
if (strip) {
|
||||
ui::context_path_add_generic(path, *RNA_Strip, strip, ICON_SEQ_STRIP_DUPLICATE);
|
||||
bNodeTree *node_group = nullptr;
|
||||
if (strip->type == STRIP_TYPE_COMPOSITOR && strip->effectdata) {
|
||||
CompositorEffectVars *comp_data = static_cast<CompositorEffectVars *>(strip->effectdata);
|
||||
node_group = comp_data->node_group;
|
||||
}
|
||||
else {
|
||||
StripModifierData *smd = seq::modifier_get_active(strip);
|
||||
if (smd && smd->type == eSeqModifierType_Compositor) {
|
||||
SequencerCompositorModifierData *scmd =
|
||||
reinterpret_cast<SequencerCompositorModifierData *>(smd);
|
||||
node_group = scmd->node_group;
|
||||
}
|
||||
}
|
||||
|
||||
if (node_group != nullptr) {
|
||||
context_path_add_top_level_shader_node_tree(snode, path, *RNA_NodeTree, node_group);
|
||||
skip_base = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
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;
|
||||
}
|
||||
ui::context_path_add_generic(path, *RNA_Scene, sequencer_scene, ICON_SCENE);
|
||||
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;
|
||||
}
|
||||
ui::context_path_add_generic(path, *RNA_Strip, strip, ICON_SEQ_STRIP_DUPLICATE);
|
||||
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<SequencerCompositorModifierData *>(
|
||||
smd);
|
||||
if (scmd->node_group == nullptr) {
|
||||
context_path_add_node_tree_and_node_groups(snode, path);
|
||||
return;
|
||||
}
|
||||
context_path_add_top_level_shader_node_tree(snode, 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);
|
||||
}
|
||||
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, skip_base);
|
||||
}
|
||||
|
||||
static void get_context_path_node_geometry(const bContext &C,
|
||||
|
|
|
|||
|
|
@ -2815,6 +2815,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
|
|||
case STRIP_TYPE_CROSS:
|
||||
case STRIP_TYPE_GAMCROSS:
|
||||
case STRIP_TYPE_WIPE:
|
||||
case STRIP_TYPE_COMPOSITOR:
|
||||
data.icon = ICON_ARROW_LEFTRIGHT;
|
||||
break;
|
||||
case STRIP_TYPE_META:
|
||||
|
|
|
|||
|
|
@ -1969,14 +1969,16 @@ static wmOperatorStatus sequencer_add_effect_strip_exec(bContext *C, wmOperator
|
|||
Editing *ed = seq::editing_ensure(scene);
|
||||
|
||||
StripType effect_type = StripType(RNA_enum_get(op->ptr, "type"));
|
||||
const int num_inputs = seq::effect_get_num_inputs(effect_type);
|
||||
const int min_inputs = seq::effect_type_get_min_num_inputs(effect_type);
|
||||
|
||||
VectorSet<Strip *> inputs = strip_effect_get_new_inputs(scene, num_inputs);
|
||||
StringRef error_msg = effect_inputs_validate(inputs, num_inputs);
|
||||
|
||||
if (!error_msg.is_empty()) {
|
||||
BKE_report(op->reports, RPT_ERROR, error_msg.data());
|
||||
return OPERATOR_CANCELLED;
|
||||
VectorSet<Strip *> inputs = strip_effect_get_new_inputs(
|
||||
scene, effect_type == STRIP_TYPE_COMPOSITOR ? 2 : min_inputs);
|
||||
if (effect_type != STRIP_TYPE_COMPOSITOR) {
|
||||
const char *error_msg = effect_inputs_validate(inputs.size(), min_inputs);
|
||||
if (error_msg != nullptr) {
|
||||
BKE_report(op->reports, RPT_ERROR, error_msg);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
seq::LoadData load_data;
|
||||
|
|
@ -2011,12 +2013,9 @@ static wmOperatorStatus sequencer_add_effect_strip_exec(bContext *C, wmOperator
|
|||
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
|
||||
sequencer_select_do_updates(C, scene);
|
||||
|
||||
/* It's reasonable to add effects with inputs directly above the input. */
|
||||
if (ELEM(load_data.effect.type,
|
||||
STRIP_TYPE_COLOR,
|
||||
STRIP_TYPE_TEXT,
|
||||
STRIP_TYPE_ADJUSTMENT,
|
||||
STRIP_TYPE_MULTICAM))
|
||||
/* Place generator effects; others are automatically placed above their inputs. */
|
||||
if (seq::effect_type_get_min_num_inputs(load_data.effect.type) == 0 &&
|
||||
(load_data.effect.type != STRIP_TYPE_COMPOSITOR || input1 == nullptr))
|
||||
{
|
||||
move_strips(C, op);
|
||||
}
|
||||
|
|
@ -2024,23 +2023,34 @@ static wmOperatorStatus sequencer_add_effect_strip_exec(bContext *C, wmOperator
|
|||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static bool is_op_for_effect_with_inputs(const Scene *scene, StripType type)
|
||||
{
|
||||
if (seq::effect_type_get_min_num_inputs(type) != 0) {
|
||||
return true;
|
||||
}
|
||||
/* Adding compositor effect with any selected strip; assuming it will be for that strip. */
|
||||
if (type == STRIP_TYPE_COMPOSITOR) {
|
||||
if (seq::select_has_any(scene)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static wmOperatorStatus sequencer_add_effect_strip_invoke(bContext *C,
|
||||
wmOperator *op,
|
||||
const wmEvent *event)
|
||||
{
|
||||
bool is_type_set = RNA_struct_property_is_set(op->ptr, "type");
|
||||
int type = -1;
|
||||
int prop_flag = SEQPROP_LENGTH;
|
||||
|
||||
if (!is_type_set) {
|
||||
BKE_report(op->reports, RPT_ERROR_INVALID_INPUT, "Strip type is not set.");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
type = RNA_enum_get(op->ptr, "type");
|
||||
|
||||
int prop_flag = SEQPROP_LENGTH;
|
||||
/* When invoking an effect strip which uses inputs, skip guessing of the channel. */
|
||||
if (seq::effect_get_num_inputs(type) != 0) {
|
||||
StripType type = StripType(RNA_enum_get(op->ptr, "type"));
|
||||
if (is_op_for_effect_with_inputs(CTX_data_sequencer_scene(C), type)) {
|
||||
prop_flag |= SEQPROP_NOCHAN;
|
||||
}
|
||||
|
||||
|
|
@ -2049,15 +2059,15 @@ static wmOperatorStatus sequencer_add_effect_strip_invoke(bContext *C,
|
|||
return sequencer_add_effect_strip_exec(C, op);
|
||||
}
|
||||
|
||||
static bool sequencer_add_effect_strip_poll_property(const bContext * /*C*/,
|
||||
static bool sequencer_add_effect_strip_poll_property(const bContext *C,
|
||||
wmOperator *op,
|
||||
const PropertyRNA *prop)
|
||||
{
|
||||
const char *prop_id = RNA_property_identifier(prop);
|
||||
int type = RNA_enum_get(op->ptr, "type");
|
||||
StripType type = StripType(RNA_enum_get(op->ptr, "type"));
|
||||
|
||||
/* Hide start frame and length for effect strips that are locked to their parents' location. */
|
||||
if (seq::effect_get_num_inputs(type) != 0) {
|
||||
if (is_op_for_effect_with_inputs(CTX_data_sequencer_scene(C), type)) {
|
||||
if (STR_ELEM(prop_id, "frame_start", "length")) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2114,6 +2124,8 @@ static std::string sequencer_add_effect_strip_get_description(bContext * /*C*/,
|
|||
return TIP_("Add a text strip to the sequencer");
|
||||
case STRIP_TYPE_COLORMIX:
|
||||
return TIP_("Add a color mix effect strip to the sequencer");
|
||||
case STRIP_TYPE_COMPOSITOR:
|
||||
return TIP_("Add a compositor based effect strip for zero, one, or two selected inputs");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -311,8 +311,7 @@ static bool sequencer_swap_inputs_poll(bContext *C)
|
|||
}
|
||||
Scene *scene = CTX_data_sequencer_scene(C);
|
||||
Strip *active_strip = seq::select_active_get(scene);
|
||||
|
||||
if (sequencer_effect_poll(C) && seq::effect_get_num_inputs(active_strip->type) == 2) {
|
||||
if (active_strip && active_strip->effect_num_inputs_get() == 2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1607,18 +1606,18 @@ void SEQUENCER_OT_refresh_all(wmOperatorType *ot)
|
|||
/** \name Reassign Inputs Operator
|
||||
* \{ */
|
||||
|
||||
StringRef effect_inputs_validate(const VectorSet<Strip *> &inputs, int num_inputs)
|
||||
const char *effect_inputs_validate(int have_inputs, int num_inputs)
|
||||
{
|
||||
if (inputs.size() > 2) {
|
||||
if (have_inputs > 2) {
|
||||
return "Cannot apply effect to more than 2 strips with video content";
|
||||
}
|
||||
if (num_inputs == 2 && inputs.size() != 2) {
|
||||
if (num_inputs == 2 && have_inputs != 2) {
|
||||
return "Exactly 2 selected strips with video content are needed";
|
||||
}
|
||||
if (num_inputs == 1 && inputs.size() != 1) {
|
||||
if (num_inputs == 1 && have_inputs != 1) {
|
||||
return "Exactly one selected strip with video content is needed";
|
||||
}
|
||||
return "";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VectorSet<Strip *> strip_effect_get_new_inputs(const Scene *scene,
|
||||
|
|
@ -1657,7 +1656,7 @@ static wmOperatorStatus sequencer_reassign_inputs_exec(bContext *C, wmOperator *
|
|||
{
|
||||
Scene *scene = CTX_data_sequencer_scene(C);
|
||||
Strip *active_strip = seq::select_active_get(scene);
|
||||
const int num_inputs = seq::effect_get_num_inputs(active_strip->type);
|
||||
const int num_inputs = active_strip->effect_num_inputs_get();
|
||||
|
||||
if (num_inputs == 0) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Cannot reassign inputs: strip has no inputs");
|
||||
|
|
@ -1665,7 +1664,7 @@ static wmOperatorStatus sequencer_reassign_inputs_exec(bContext *C, wmOperator *
|
|||
}
|
||||
|
||||
VectorSet<Strip *> inputs = strip_effect_get_new_inputs(scene, num_inputs, true);
|
||||
StringRef error_msg = effect_inputs_validate(inputs, num_inputs);
|
||||
StringRef error_msg = effect_inputs_validate(inputs.size(), num_inputs);
|
||||
|
||||
if (!error_msg.is_empty()) {
|
||||
BKE_report(op->reports, RPT_ERROR, error_msg.data());
|
||||
|
|
@ -1739,7 +1738,7 @@ static wmOperatorStatus sequencer_swap_inputs_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (seq::effect_get_num_inputs(active_strip->type) != 2 || active_strip->input1 == nullptr ||
|
||||
if (active_strip->effect_num_inputs_get() != 2 || active_strip->input1 == nullptr ||
|
||||
active_strip->input2 == nullptr)
|
||||
{
|
||||
BKE_report(op->reports, RPT_ERROR, "Strip needs two inputs to swap");
|
||||
|
|
@ -3125,12 +3124,10 @@ static wmOperatorStatus sequencer_swap_exec(bContext *C, wmOperator *op)
|
|||
if (strip) {
|
||||
|
||||
/* Disallow effect strips. */
|
||||
if (seq::effect_get_num_inputs(strip->type) >= 1 &&
|
||||
(strip->effectdata || strip->input1 || strip->input2))
|
||||
{
|
||||
if (strip->is_effect_with_inputs() && (strip->effectdata || strip->input1 || strip->input2)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
if ((seq::effect_get_num_inputs(active_strip->type) >= 1) &&
|
||||
if (active_strip->is_effect_with_inputs() &&
|
||||
(active_strip->effectdata || active_strip->input1 || active_strip->input2))
|
||||
{
|
||||
return OPERATOR_CANCELLED;
|
||||
|
|
@ -3393,6 +3390,7 @@ const EnumPropertyItem sequencer_prop_effect_types[] = {
|
|||
{STRIP_TYPE_GAUSSIAN_BLUR, "GAUSSIAN_BLUR", 0, "Gaussian Blur", "Soften details along axes"},
|
||||
{STRIP_TYPE_TEXT, "TEXT", 0, "Text", "Add a simple text strip"},
|
||||
{STRIP_TYPE_COLORMIX, "COLORMIX", 0, "Color Mix", "Combine two strips using blend modes"},
|
||||
{STRIP_TYPE_COMPOSITOR, "COMPOSITOR", 0, "Compositor", "Compositor based effect"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
|
|
@ -3400,16 +3398,20 @@ static wmOperatorStatus sequencer_change_effect_type_exec(bContext *C, wmOperato
|
|||
{
|
||||
Scene *scene = CTX_data_sequencer_scene(C);
|
||||
Strip *strip = seq::select_active_get(scene);
|
||||
const int old_type = strip->type;
|
||||
const int new_type = RNA_enum_get(op->ptr, "type");
|
||||
const StripType old_type = StripType(strip->type);
|
||||
const int have_inputs = strip->effect_num_inputs_get();
|
||||
const StripType new_type = StripType(RNA_enum_get(op->ptr, "type"));
|
||||
|
||||
if (!strip->is_effect()) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (seq::effect_get_num_inputs(strip->type) != seq::effect_get_num_inputs(new_type)) {
|
||||
BKE_report(op->reports, RPT_ERROR, "New effect takes less or more inputs");
|
||||
return OPERATOR_CANCELLED;
|
||||
/* Note: allow changing to compositor effect; it can have any number of inputs. */
|
||||
if (new_type != STRIP_TYPE_COMPOSITOR) {
|
||||
if (have_inputs != seq::effect_type_get_min_num_inputs(new_type)) {
|
||||
BKE_report(op->reports, RPT_ERROR, "New effect takes less or more inputs");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free previous effect. */
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ void slip_modal_keymap(wmKeyConfig *keyconf);
|
|||
VectorSet<Strip *> strip_effect_get_new_inputs(const Scene *scene,
|
||||
int num_inputs,
|
||||
bool ignore_active = false);
|
||||
StringRef effect_inputs_validate(const VectorSet<Strip *> &inputs, int num_inputs);
|
||||
const char *effect_inputs_validate(int have_inputs, int num_inputs);
|
||||
|
||||
/* Operator helpers. */
|
||||
bool sequencer_edit_poll(bContext *C);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@
|
|||
|
||||
#include "SEQ_channels.hh"
|
||||
#include "SEQ_connect.hh"
|
||||
#include "SEQ_effects.hh"
|
||||
#include "SEQ_iterator.hh"
|
||||
#include "SEQ_relations.hh"
|
||||
#include "SEQ_retiming.hh"
|
||||
|
|
@ -969,7 +968,7 @@ static float inner_clickable_handle_size_get(const Scene *scene,
|
|||
|
||||
bool can_select_handle(const Scene *scene, const Strip *strip, const View2D *v2d)
|
||||
{
|
||||
if (seq::effect_get_num_inputs(strip->type) > 0) {
|
||||
if (strip->is_effect_with_inputs()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -344,6 +344,7 @@ static void color3ubv_from_seq(const Scene *curscene,
|
|||
case STRIP_TYPE_CROSS:
|
||||
case STRIP_TYPE_GAMCROSS:
|
||||
case STRIP_TYPE_WIPE:
|
||||
case STRIP_TYPE_COMPOSITOR:
|
||||
ui::theme::get_color_3ubv(TH_SEQ_TRANSITION, r_col);
|
||||
|
||||
/* Slightly offset hue to distinguish different transition types. */
|
||||
|
|
@ -353,6 +354,9 @@ static void color3ubv_from_seq(const Scene *curscene,
|
|||
else if (strip->type == STRIP_TYPE_WIPE) {
|
||||
rgb_byte_set_hue_float_offset(r_col, 0.06);
|
||||
}
|
||||
else if (strip->type == STRIP_TYPE_COMPOSITOR) {
|
||||
rgb_byte_set_hue_float_offset(r_col, -0.03f);
|
||||
}
|
||||
break;
|
||||
|
||||
/* Effects. */
|
||||
|
|
@ -1323,7 +1327,8 @@ static void draw_strips_background(const TimelineDrawContext &ctx,
|
|||
|
||||
/* Transition state. */
|
||||
if (show_overlay && strip.can_draw_strip_content &&
|
||||
seq::effect_is_transition(StripType(strip.strip->type)))
|
||||
seq::effect_is_transition(StripType(strip.strip->type)) && strip.strip->input1 &&
|
||||
strip.strip->input2)
|
||||
{
|
||||
data.flags |= GPU_SEQ_FLAG_TRANSITION;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
#include "SEQ_animation.hh"
|
||||
#include "SEQ_channels.hh"
|
||||
#include "SEQ_edit.hh"
|
||||
#include "SEQ_effects.hh"
|
||||
#include "SEQ_iterator.hh"
|
||||
#include "SEQ_relations.hh"
|
||||
#include "SEQ_sequencer.hh"
|
||||
|
|
@ -480,10 +479,10 @@ static void create_trans_seq_clamp_data(TransInfo *t, const Scene *scene)
|
|||
|
||||
VectorSet<Strip *> strips = seq::query_selected_strips(seq::active_seqbase_get(ed));
|
||||
for (Strip *strip : strips) {
|
||||
if (!strip->is_effect() || seq::effect_get_num_inputs(strip->type) == 0) {
|
||||
if (!strip->is_effect_with_inputs()) {
|
||||
continue;
|
||||
}
|
||||
/* If there is an effect strip with no inputs selected, prevent any x-direction movement,
|
||||
/* If there is an effect strip without its inputs selected, prevent any x-direction movement,
|
||||
* since these strips are tied to their inputs and can only move up and down. */
|
||||
if (!(strip->input1->flag & SEQ_SELECT) &&
|
||||
(!strip->input2 || !(strip->input2->flag & SEQ_SELECT)))
|
||||
|
|
|
|||
|
|
@ -214,9 +214,8 @@ static VectorSet<Strip *> query_snap_targets_timeline(Scene *scene,
|
|||
* have to be selected. Remove such strips from `snap_targets` collection. */
|
||||
VectorSet effects_of_snap_sources = snap_sources;
|
||||
seq::iterator_set_expand(scene, seqbase, effects_of_snap_sources, query_strip_effects_fn);
|
||||
effects_of_snap_sources.remove_if([&](Strip *strip) {
|
||||
return strip->is_effect() && seq::effect_get_num_inputs(strip->type) == 0;
|
||||
});
|
||||
effects_of_snap_sources.remove_if(
|
||||
[&](Strip *strip) { return strip->is_effect() && !strip->is_effect_with_inputs(); });
|
||||
|
||||
VectorSet<Strip *> snap_targets;
|
||||
for (Strip &strip : *seqbase) {
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ enum StripType {
|
|||
STRIP_TYPE_MUL = 14,
|
||||
/* Removed (behavior was the same as alpha-over), only used when reading old files. */
|
||||
STRIP_TYPE_OVERDROP_REMOVED = 15,
|
||||
STRIP_TYPE_COMPOSITOR = 16,
|
||||
/* STRIP_TYPE_PLUGIN = 24, */ /* Removed. */
|
||||
STRIP_TYPE_WIPE = 25,
|
||||
STRIP_TYPE_GLOW = 26,
|
||||
|
|
@ -459,6 +460,11 @@ struct Strip {
|
|||
|
||||
#ifdef __cplusplus
|
||||
bool is_effect() const;
|
||||
int effect_num_inputs_get() const;
|
||||
bool is_effect_with_inputs() const
|
||||
{
|
||||
return this->effect_num_inputs_get() != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get timeline frame where strip content starts.
|
||||
|
|
@ -846,6 +852,10 @@ struct ColorMixVars {
|
|||
float factor = 0;
|
||||
};
|
||||
|
||||
struct CompositorEffectVars {
|
||||
struct bNodeTree *node_group = nullptr;
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
|||
|
|
@ -704,7 +704,7 @@ static int rna_Strip_time_editable(const PointerRNA *ptr, const char ** /*r_info
|
|||
{
|
||||
Strip *strip = static_cast<Strip *>(ptr->data);
|
||||
/* Effect strips' start frame and length must be readonly! */
|
||||
return (seq::effect_get_num_inputs(strip->type)) ? PropertyFlag(0) : PROP_EDITABLE;
|
||||
return strip->is_effect_with_inputs() ? PropertyFlag(0) : PROP_EDITABLE;
|
||||
}
|
||||
|
||||
static void rna_Strip_channel_set(PointerRNA *ptr, int value)
|
||||
|
|
@ -970,6 +970,8 @@ static StructRNA *rna_Strip_refine(PointerRNA *ptr)
|
|||
return RNA_AlphaUnderStrip;
|
||||
case STRIP_TYPE_GAMCROSS:
|
||||
return RNA_GammaCrossStrip;
|
||||
case STRIP_TYPE_COMPOSITOR:
|
||||
return RNA_CompositorStrip;
|
||||
case STRIP_TYPE_MUL:
|
||||
return RNA_MultiplyStrip;
|
||||
case STRIP_TYPE_MULTICAM:
|
||||
|
|
@ -1146,8 +1148,7 @@ static void rna_Strip_pan_range(
|
|||
static int rna_Strip_input_count_get(PointerRNA *ptr)
|
||||
{
|
||||
Strip *strip = static_cast<Strip *>(ptr->data);
|
||||
|
||||
return seq::effect_get_num_inputs(strip->type);
|
||||
return strip->effect_num_inputs_get();
|
||||
}
|
||||
|
||||
static void rna_Strip_input_set(PointerRNA *ptr,
|
||||
|
|
@ -1856,7 +1857,7 @@ 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)
|
||||
static bool rna_Compositor_node_group_poll(PointerRNA * /*ptr*/, PointerRNA value)
|
||||
{
|
||||
const bNodeTree *node_tree = value.data_as<bNodeTree>();
|
||||
if (node_tree->type != NTREE_COMPOSIT) {
|
||||
|
|
@ -1865,11 +1866,9 @@ static bool rna_CompositorModifier_node_group_poll(PointerRNA * /*ptr*/, Pointer
|
|||
return true;
|
||||
}
|
||||
|
||||
static void rna_CompositorModifier_node_group_update(Main *bmain, Scene *scene, PointerRNA *ptr)
|
||||
static void strip_compositor_node_group_update(Main *bmain, PointerRNA *ptr)
|
||||
{
|
||||
rna_StripModifier_update(bmain, scene, ptr);
|
||||
|
||||
/* Tag depsgraph relations for an update since the modifier could now be referencing a different
|
||||
/* Tag depsgraph relations for an update since the strip could now be referencing a different
|
||||
* node tree. */
|
||||
DEG_relations_tag_update(bmain);
|
||||
|
||||
|
|
@ -1878,11 +1877,23 @@ static void rna_CompositorModifier_node_group_update(Main *bmain, Scene *scene,
|
|||
Scene *strip_scene = reinterpret_cast<Scene *>(ptr->owner_id);
|
||||
Editing *ed = 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. */
|
||||
/* The sequencer stores a cached mapping between compositor node trees and strips that use them,
|
||||
* so we need to invalidate the cache since the node tree changed. */
|
||||
seq::strip_lookup_invalidate(ed);
|
||||
}
|
||||
|
||||
static void rna_CompositorEffect_node_group_update(Main *bmain, Scene *scene, PointerRNA *ptr)
|
||||
{
|
||||
rna_Strip_invalidate_raw_update(bmain, scene, ptr);
|
||||
strip_compositor_node_group_update(bmain, ptr);
|
||||
}
|
||||
|
||||
static void rna_CompositorModifier_node_group_update(Main *bmain, Scene *scene, PointerRNA *ptr)
|
||||
{
|
||||
rna_StripModifier_update(bmain, scene, ptr);
|
||||
strip_compositor_node_group_update(bmain, ptr);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
|
||||
#else
|
||||
|
|
@ -2391,6 +2402,7 @@ static void rna_def_strip(BlenderRNA *brna)
|
|||
{STRIP_TYPE_ALPHAOVER, "ALPHA_OVER", 0, "Alpha Over", ""},
|
||||
{STRIP_TYPE_ALPHAUNDER, "ALPHA_UNDER", 0, "Alpha Under", ""},
|
||||
{STRIP_TYPE_GAMCROSS, "GAMMA_CROSS", 0, "Gamma Crossfade", ""},
|
||||
{STRIP_TYPE_COMPOSITOR, "COMPOSITOR", 0, "Compositor", ""},
|
||||
{STRIP_TYPE_MUL, "MULTIPLY", 0, "Multiply", ""},
|
||||
{STRIP_TYPE_WIPE, "WIPE", 0, "Wipe", ""},
|
||||
{STRIP_TYPE_GLOW, "GLOW", 0, "Glow", ""},
|
||||
|
|
@ -3613,6 +3625,17 @@ static void rna_def_glow(StructRNA *srna)
|
|||
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Strip_invalidate_raw_update");
|
||||
}
|
||||
|
||||
static void rna_def_compositor_effect(StructRNA *srna)
|
||||
{
|
||||
RNA_def_struct_sdna_from(srna, "CompositorEffectVars", "effectdata");
|
||||
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 effect does");
|
||||
RNA_def_property_pointer_funcs(
|
||||
prop, nullptr, nullptr, nullptr, "rna_Compositor_node_group_poll");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_CompositorEffect_node_group_update");
|
||||
}
|
||||
|
||||
static void rna_def_solid_color(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
|
@ -3941,6 +3964,7 @@ static EffectInfo def_effects[] = {
|
|||
0},
|
||||
{"CrossStrip", "Crossfade Strip", "Crossfade Strip", nullptr, 2},
|
||||
{"GammaCrossStrip", "Gamma Crossfade Strip", "Gamma Crossfade Strip", nullptr, 2},
|
||||
{"CompositorStrip", "Compositor Strip", "Compositor Strip", rna_def_compositor_effect, 2},
|
||||
{"GlowStrip", "Glow Strip", "Sequence strip creating a glow effect", rna_def_glow, 1},
|
||||
{"MulticamStrip",
|
||||
"Multicam Select Strip",
|
||||
|
|
@ -4288,7 +4312,7 @@ static void rna_def_compositor_modifier(BlenderRNA *brna)
|
|||
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");
|
||||
prop, nullptr, nullptr, nullptr, "rna_Compositor_node_group_poll");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_update(
|
||||
prop, NC_SCENE | ND_SEQUENCER, "rna_CompositorModifier_node_group_update");
|
||||
|
|
|
|||
|
|
@ -489,13 +489,11 @@ static Strip *rna_Strips_new_effect(ID *id,
|
|||
Strip *input1,
|
||||
Strip *input2)
|
||||
{
|
||||
Scene *scene = id_cast<Scene *>(id);
|
||||
Strip *strip;
|
||||
const int num_inputs = seq::effect_get_num_inputs(type);
|
||||
|
||||
switch (num_inputs) {
|
||||
const int min_inputs = blender::seq::effect_type_get_min_num_inputs(StripType(type));
|
||||
const bool compositor_with_inputs = type == STRIP_TYPE_COMPOSITOR && input1 != nullptr;
|
||||
switch (min_inputs) {
|
||||
case 0:
|
||||
if (length <= 0) {
|
||||
if (length <= 0 && !compositor_with_inputs) {
|
||||
BKE_report(reports, RPT_ERROR, "Strips.new_effect: invalid length");
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -517,17 +515,17 @@ static Strip *rna_Strips_new_effect(ID *id,
|
|||
reports,
|
||||
RPT_ERROR,
|
||||
"Strips.new_effect: effect expects more than 2 inputs (%d, should never happen!)",
|
||||
num_inputs);
|
||||
min_inputs);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
seq::LoadData load_data;
|
||||
seq::add_load_data_init(&load_data, name, nullptr, frame_start, channel);
|
||||
load_data.effect.length = length;
|
||||
load_data.effect.type = StripType(type);
|
||||
load_data.effect.input1 = input1;
|
||||
load_data.effect.input2 = input2;
|
||||
strip = seq::add_effect_strip(scene, seqbase, &load_data);
|
||||
Scene *scene = id_cast<Scene *>(id);
|
||||
Strip *strip = seq::add_effect_strip(scene, seqbase, &load_data);
|
||||
|
||||
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
|
||||
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene);
|
||||
|
|
@ -842,6 +840,7 @@ void RNA_api_strips(StructRNA *srna, const bool metastrip)
|
|||
0,
|
||||
"Gamma Crossfade",
|
||||
"Crossfade with color correction"},
|
||||
{STRIP_TYPE_COMPOSITOR, "COMPOSITOR", 0, "Compositor", "Compositor based effect"},
|
||||
{STRIP_TYPE_MUL, "MULTIPLY", 0, "Multiply", "Multiply color channels from two videos"},
|
||||
{STRIP_TYPE_WIPE, "WIPE", 0, "Wipe", "Sweep a transition line across the frame"},
|
||||
{STRIP_TYPE_GLOW, "GLOW", 0, "Glow", "Add blur and brightness to light areas"},
|
||||
|
|
|
|||
|
|
@ -44,35 +44,35 @@ static void composite_get_from_context(const bContext *C,
|
|||
{
|
||||
const SpaceNode *snode = CTX_wm_space_node(C);
|
||||
if (snode->node_tree_sub_type == SNODE_COMPOSITOR_SEQUENCER) {
|
||||
*r_ntree = nullptr;
|
||||
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;
|
||||
|
||||
bNodeTree *node_group = nullptr;
|
||||
if (strip->type == STRIP_TYPE_COMPOSITOR && strip->effectdata) {
|
||||
CompositorEffectVars *comp_data = static_cast<CompositorEffectVars *>(strip->effectdata);
|
||||
node_group = comp_data->node_group;
|
||||
}
|
||||
if (smd->type != eSeqModifierType_Compositor) {
|
||||
*r_ntree = nullptr;
|
||||
return;
|
||||
else {
|
||||
StripModifierData *smd = seq::modifier_get_active(strip);
|
||||
if (smd && smd->type == eSeqModifierType_Compositor) {
|
||||
SequencerCompositorModifierData *scmd =
|
||||
reinterpret_cast<SequencerCompositorModifierData *>(smd);
|
||||
node_group = scmd->node_group;
|
||||
}
|
||||
}
|
||||
|
||||
if (node_group) {
|
||||
*r_from = nullptr;
|
||||
*r_id = &sequencer_scene->id;
|
||||
*r_ntree = node_group;
|
||||
}
|
||||
SequencerCompositorModifierData *scmd = reinterpret_cast<SequencerCompositorModifierData *>(
|
||||
smd);
|
||||
*r_from = nullptr;
|
||||
*r_id = &sequencer_scene->id;
|
||||
*r_ntree = scmd->node_group;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -947,41 +947,44 @@ static void node_group_input_extra_info(nodes::NodeExtraInfoParams ¶meters)
|
|||
return;
|
||||
}
|
||||
|
||||
blender::Span<const bNodeSocket *> group_inputs = parameters.node.output_sockets().drop_back(1);
|
||||
bool added_warning_for_unsupported_inputs = false;
|
||||
Span<const bNodeSocket *> group_inputs = parameters.node.output_sockets().drop_back(1);
|
||||
int color_count = 0;
|
||||
int float_count = 0;
|
||||
int other_count = 0;
|
||||
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));
|
||||
}
|
||||
if (input->type == SOCK_RGBA) {
|
||||
color_count++;
|
||||
}
|
||||
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 (input->type == SOCK_FLOAT) {
|
||||
float_count++;
|
||||
}
|
||||
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;
|
||||
other_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (color_count > 2) {
|
||||
nodes::NodeExtraInfoRow row;
|
||||
row.text = IFACE_("Unsupported Inputs");
|
||||
row.icon = ICON_WARNING_LARGE;
|
||||
row.tooltip = TIP_("Sequencer supports up to two Image inputs, the rest will return zero");
|
||||
parameters.rows.append(std::move(row));
|
||||
}
|
||||
if (float_count > 1) {
|
||||
nodes::NodeExtraInfoRow row;
|
||||
row.text = IFACE_("Unsupported Inputs");
|
||||
row.icon = ICON_WARNING_LARGE;
|
||||
row.tooltip = TIP_("Sequencer supports one Float input, the rest will return zero");
|
||||
parameters.rows.append(std::move(row));
|
||||
}
|
||||
if (other_count > 0) {
|
||||
nodes::NodeExtraInfoRow row;
|
||||
row.text = IFACE_("Unsupported Inputs");
|
||||
row.icon = ICON_WARNING_LARGE;
|
||||
row.tooltip = TIP_(
|
||||
"Sequencer supports only Color and Float inputs, the rest will return zero");
|
||||
parameters.rows.append(std::move(row));
|
||||
}
|
||||
}
|
||||
|
||||
void register_node_type_group_input()
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ set(SRC
|
|||
intern/effects/vse_effect_add_sub_mul.cc
|
||||
intern/effects/vse_effect_adjustment.cc
|
||||
intern/effects/vse_effect_blend.cc
|
||||
intern/effects/vse_effect_compositor.cc
|
||||
intern/effects/vse_effect_cross.cc
|
||||
intern/effects/vse_effect_gaussian_blur.cc
|
||||
intern/effects/vse_effect_glow.cc
|
||||
|
|
|
|||
|
|
@ -23,7 +23,12 @@ namespace seq {
|
|||
|
||||
void effect_ensure_initialized(Strip *strip);
|
||||
void effect_free(Strip *strip);
|
||||
int effect_get_num_inputs(int strip_type);
|
||||
|
||||
/* Returns the minimum number of inputs needed by the effect type.
|
||||
* Note: some effects (compositor) will return zero; they can
|
||||
* take variable number of inputs. */
|
||||
int effect_type_get_min_num_inputs(StripType type);
|
||||
bool strip_type_is_effect(StripType type);
|
||||
bool effect_is_transition(StripType type);
|
||||
void effect_text_font_set(Strip *strip, VFont *font);
|
||||
bool effects_can_render_text(const Strip *strip);
|
||||
|
|
|
|||
|
|
@ -60,9 +60,9 @@ 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.
|
||||
* Invalidates the cache for all strips that uses the given compositor node tree.
|
||||
*/
|
||||
void relations_invalidate_compositor_modifiers(const Main *bmain, const bNodeTree *node_tree);
|
||||
void relations_invalidate_compositor_users(const Main *bmain, const bNodeTree *node_tree);
|
||||
|
||||
void relations_invalidate_movieclip_strips(Main *bmain, MovieClip *clip_target);
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ namespace seq {
|
|||
Strip *select_active_get(const Scene *scene);
|
||||
bool select_active_get_pair(Scene *scene, Strip **r_strip_act, Strip **r_strip_other);
|
||||
void select_active_set(Scene *scene, Strip *strip);
|
||||
bool select_has_any(const Scene *scene);
|
||||
|
||||
} // namespace seq
|
||||
} // namespace blender
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ Span<Strip *> lookup_strips_by_scene(Editing *ed, const Scene *key);
|
|||
Map<const Scene *, VectorSet<Strip *>> &lookup_strips_by_scene_map_get(Editing *ed);
|
||||
|
||||
/**
|
||||
* Find all strips using provided compositor node tree as a modifier
|
||||
* Find all strips using provided compositor node tree
|
||||
*
|
||||
* \param ed: Editing that owns lookup hash
|
||||
* \param key: Node tree pointer
|
||||
|
|
|
|||
|
|
@ -103,11 +103,6 @@ Array<float> make_gaussian_blur_kernel(float rad, int size)
|
|||
|
||||
static void init_noop(Strip * /*strip*/) {}
|
||||
|
||||
static int num_inputs_default()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
static void copy_effect_default(Strip *dst, const Strip *src, const int /*flag*/)
|
||||
{
|
||||
dst->effectdata = MEM_dupalloc_void(src->effectdata);
|
||||
|
|
@ -169,7 +164,6 @@ EffectHandle effect_handle_get(StripType strip_type)
|
|||
EffectHandle rval;
|
||||
|
||||
rval.init = init_noop;
|
||||
rval.num_inputs = num_inputs_default;
|
||||
rval.free = nullptr;
|
||||
rval.early_out = early_out_noop;
|
||||
rval.execute = nullptr;
|
||||
|
|
@ -182,6 +176,9 @@ EffectHandle effect_handle_get(StripType strip_type)
|
|||
case STRIP_TYPE_GAMCROSS:
|
||||
gamma_cross_effect_get_handle(rval);
|
||||
break;
|
||||
case STRIP_TYPE_COMPOSITOR:
|
||||
compositor_effect_get_handle(rval);
|
||||
break;
|
||||
case STRIP_TYPE_ADD:
|
||||
add_effect_get_handle(rval);
|
||||
break;
|
||||
|
|
@ -236,7 +233,6 @@ static EffectHandle effect_handle_for_blend_mode_get(StripBlendMode blend)
|
|||
EffectHandle rval;
|
||||
|
||||
rval.init = init_noop;
|
||||
rval.num_inputs = num_inputs_default;
|
||||
rval.free = nullptr;
|
||||
rval.early_out = early_out_noop;
|
||||
rval.execute = nullptr;
|
||||
|
|
@ -312,7 +308,15 @@ EffectHandle strip_blend_mode_handle_get(Strip *strip)
|
|||
static float transition_fader_calc(const Scene *scene, const Strip *strip, float timeline_frame)
|
||||
{
|
||||
float fac = float(timeline_frame - strip->left_handle());
|
||||
fac /= strip->length(scene);
|
||||
/* Compositor with no inputs can have strip->len not be updated,
|
||||
* since most of existing editing code assumes no-input effects never need the length.
|
||||
* So for the fader, just calculated it here directly. */
|
||||
if (strip->type == STRIP_TYPE_COMPOSITOR) {
|
||||
fac /= strip->enddisp - strip->startdisp;
|
||||
}
|
||||
else {
|
||||
fac /= strip->length(scene);
|
||||
}
|
||||
fac = math::clamp(fac, 0.0f, 1.0f);
|
||||
return fac;
|
||||
}
|
||||
|
|
@ -334,18 +338,43 @@ float effect_fader_calc(Scene *scene, Strip *strip, float timeline_frame)
|
|||
return strip->effect_fader;
|
||||
}
|
||||
|
||||
int effect_get_num_inputs(int strip_type)
|
||||
int effect_type_get_min_num_inputs(StripType type)
|
||||
{
|
||||
EffectHandle rval = effect_handle_get(StripType(strip_type));
|
||||
if (rval.execute == nullptr) {
|
||||
if (!strip_type_is_effect(type)) {
|
||||
return 0;
|
||||
}
|
||||
return rval.num_inputs();
|
||||
|
||||
/* Zero input effects. Note: compositor is here too, but it supports
|
||||
* any input count. */
|
||||
if (ELEM(type,
|
||||
STRIP_TYPE_ADJUSTMENT,
|
||||
STRIP_TYPE_MULTICAM,
|
||||
STRIP_TYPE_COLOR,
|
||||
STRIP_TYPE_TEXT,
|
||||
STRIP_TYPE_COMPOSITOR))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* One input effects. */
|
||||
if (ELEM(type, STRIP_TYPE_GAUSSIAN_BLUR, STRIP_TYPE_GLOW, STRIP_TYPE_SPEED)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Others are two inputs. */
|
||||
return 2;
|
||||
}
|
||||
|
||||
bool strip_type_is_effect(StripType type)
|
||||
{
|
||||
return (type >= STRIP_TYPE_CROSS && type <= STRIP_TYPE_COMPOSITOR) ||
|
||||
(type >= STRIP_TYPE_WIPE && type <= STRIP_TYPE_ADJUSTMENT) ||
|
||||
(type >= STRIP_TYPE_GAUSSIAN_BLUR && type <= STRIP_TYPE_COLORMIX);
|
||||
}
|
||||
|
||||
bool effect_is_transition(StripType type)
|
||||
{
|
||||
return ELEM(type, STRIP_TYPE_CROSS, STRIP_TYPE_GAMCROSS, STRIP_TYPE_WIPE);
|
||||
return ELEM(type, STRIP_TYPE_CROSS, STRIP_TYPE_GAMCROSS, STRIP_TYPE_WIPE, STRIP_TYPE_COMPOSITOR);
|
||||
}
|
||||
|
||||
} // namespace blender::seq
|
||||
|
|
|
|||
|
|
@ -41,9 +41,6 @@ struct EffectHandle {
|
|||
/* #init is only called on first creation, or when changing effect type. */
|
||||
void (*init)(Strip *strip);
|
||||
|
||||
/* Number of input strips needed for this effect. */
|
||||
int (*num_inputs)();
|
||||
|
||||
/* duplicate */
|
||||
void (*copy)(Strip *dst, const Strip *src, int flag);
|
||||
|
||||
|
|
@ -123,6 +120,7 @@ void alpha_over_effect_get_handle(EffectHandle &rval);
|
|||
void alpha_under_effect_get_handle(EffectHandle &rval);
|
||||
void blend_mode_effect_get_handle(EffectHandle &rval);
|
||||
void color_mix_effect_get_handle(EffectHandle &rval);
|
||||
void compositor_effect_get_handle(EffectHandle &rval);
|
||||
void cross_effect_get_handle(EffectHandle &rval);
|
||||
void gamma_cross_effect_get_handle(EffectHandle &rval);
|
||||
void gaussian_blur_effect_get_handle(EffectHandle &rval);
|
||||
|
|
|
|||
|
|
@ -18,12 +18,6 @@
|
|||
|
||||
namespace blender::seq {
|
||||
|
||||
/* No effect inputs for adjustment, we use #give_ibuf_seq. */
|
||||
static int num_inputs_adjustment()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static StripEarlyOut early_out_adjustment(const Strip * /*strip*/, float /*fac*/)
|
||||
{
|
||||
return StripEarlyOut::NoInput;
|
||||
|
|
@ -85,7 +79,6 @@ static ImBuf *do_adjustment(const RenderData *context,
|
|||
|
||||
void adjustment_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.num_inputs = num_inputs_adjustment;
|
||||
rval.early_out = early_out_adjustment;
|
||||
rval.execute = do_adjustment;
|
||||
}
|
||||
|
|
|
|||
342
source/blender/sequencer/intern/effects/vse_effect_compositor.cc
Normal file
342
source/blender/sequencer/intern/effects/vse_effect_compositor.cc
Normal file
|
|
@ -0,0 +1,342 @@
|
|||
/* SPDX-FileCopyrightText: 2026 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup sequencer
|
||||
*/
|
||||
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
#include "COM_context.hh"
|
||||
#include "COM_domain.hh"
|
||||
#include "COM_node_group_operation.hh"
|
||||
#include "COM_realize_on_domain_operation.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "DNA_sequence_types.h"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
#include "IMB_imbuf.hh"
|
||||
|
||||
#include "SEQ_render.hh"
|
||||
|
||||
#include "effects.hh"
|
||||
#include "render.hh"
|
||||
|
||||
namespace blender::seq {
|
||||
|
||||
class CompositorEffectContext : public compositor::Context {
|
||||
const RenderData &render_data_;
|
||||
bNodeTree *node_group_;
|
||||
|
||||
ImBuf *input_1_;
|
||||
ImBuf *input_2_;
|
||||
ImBuf *output_;
|
||||
float factor_;
|
||||
float2 result_translation_ = float2(0, 0);
|
||||
const Strip *strip_;
|
||||
/* Identifies if the output of the viewer was written. */
|
||||
bool viewer_was_written_ = false;
|
||||
|
||||
public:
|
||||
CompositorEffectContext(compositor::StaticCacheManager &cache_manager,
|
||||
const RenderData &render_data,
|
||||
bNodeTree *node_tree,
|
||||
ImBuf *input_1,
|
||||
ImBuf *input_2,
|
||||
ImBuf *output,
|
||||
float factor,
|
||||
const Strip &strip)
|
||||
: compositor::Context(cache_manager),
|
||||
render_data_(render_data),
|
||||
node_group_(node_tree),
|
||||
input_1_(input_1),
|
||||
input_2_(input_2),
|
||||
output_(output),
|
||||
factor_(factor),
|
||||
strip_(&strip)
|
||||
{
|
||||
}
|
||||
|
||||
float2 get_result_translation() const
|
||||
{
|
||||
return result_translation_;
|
||||
}
|
||||
|
||||
const Scene &get_scene() const override
|
||||
{
|
||||
return *render_data_.scene;
|
||||
}
|
||||
|
||||
bool treat_viewer_as_group_output() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
compositor::Domain get_compositing_domain() const override
|
||||
{
|
||||
return compositor::Domain(int2(this->output_->x, this->output_->y));
|
||||
}
|
||||
|
||||
void write_output(const compositor::Result &result)
|
||||
{
|
||||
/* Do not write the output if the viewer output was already written. */
|
||||
if (viewer_was_written_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.is_single_value()) {
|
||||
IMB_rectfill(this->output_, result.get_single_value<compositor::Color>());
|
||||
return;
|
||||
}
|
||||
|
||||
result_translation_ = result.domain().transformation.location();
|
||||
std::memcpy(this->output_->float_buffer.data,
|
||||
result.cpu_data().data(),
|
||||
IMB_get_pixel_count(this->output_) * sizeof(float) * 4);
|
||||
}
|
||||
|
||||
void write_viewer(compositor::Result &result) override
|
||||
{
|
||||
/* Within compositor effect, output and viewer output function the same. */
|
||||
this->write_output(result);
|
||||
viewer_was_written_ = true;
|
||||
}
|
||||
|
||||
const Strip *get_strip() const override
|
||||
{
|
||||
return strip_;
|
||||
}
|
||||
|
||||
bool use_gpu() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
compositor::NodeGroupOutputTypes needed_outputs() const
|
||||
{
|
||||
compositor::NodeGroupOutputTypes needed_outputs =
|
||||
compositor::NodeGroupOutputTypes::GroupOutputNode;
|
||||
if (!render_data_.render) {
|
||||
needed_outputs |= compositor::NodeGroupOutputTypes::ViewerNode;
|
||||
}
|
||||
return needed_outputs;
|
||||
}
|
||||
|
||||
void evaluate()
|
||||
{
|
||||
using namespace compositor;
|
||||
const bNodeTree &node_group = *DEG_get_evaluated<bNodeTree>(render_data_.depsgraph,
|
||||
node_group_);
|
||||
NodeGroupOperation node_group_operation(*this,
|
||||
node_group,
|
||||
this->needed_outputs(),
|
||||
nullptr,
|
||||
node_group.active_viewer_key,
|
||||
bke::NODE_INSTANCE_KEY_BASE);
|
||||
|
||||
/* Set the reference count for the outputs, only the first color output is actually needed,
|
||||
* while the rest are ignored. */
|
||||
node_group.ensure_interface_cache();
|
||||
for (const bNodeTreeInterfaceSocket *output_socket : node_group.interface_outputs()) {
|
||||
const bool is_first_output = output_socket == node_group.interface_outputs().first();
|
||||
Result &output_result = node_group_operation.get_result(output_socket->identifier);
|
||||
const bool is_color = output_result.type() == ResultType::Color;
|
||||
output_result.set_reference_count(is_first_output && is_color ? 1 : 0);
|
||||
}
|
||||
|
||||
/* Map the inputs to the operation. */
|
||||
Vector<std::unique_ptr<Result>> inputs;
|
||||
int float_counter = 0;
|
||||
int color_counter = 0;
|
||||
for (const bNodeTreeInterfaceSocket *input_socket : node_group.interface_inputs()) {
|
||||
const bke::bNodeSocketType *typeinfo = input_socket->socket_typeinfo();
|
||||
Result *input_result = nullptr;
|
||||
if (typeinfo && typeinfo->type == SOCK_FLOAT && float_counter == 0) {
|
||||
/* First float input is factor. */
|
||||
input_result = new Result(this->create_result(ResultType::Float, ResultPrecision::Full));
|
||||
input_result->allocate_single_value();
|
||||
input_result->set_single_value(this->factor_);
|
||||
float_counter++;
|
||||
}
|
||||
else if (color_counter == 0 && this->input_1_) {
|
||||
/* First input image. */
|
||||
input_result = new Result(this->create_result(ResultType::Color, ResultPrecision::Full));
|
||||
input_result->wrap_external(this->input_1_->float_buffer.data,
|
||||
int2(this->input_1_->x, this->input_1_->y));
|
||||
color_counter++;
|
||||
}
|
||||
else if (color_counter == 1 && this->input_2_) {
|
||||
/* Second input image. */
|
||||
input_result = new Result(this->create_result(ResultType::Color, ResultPrecision::Full));
|
||||
input_result->wrap_external(this->input_2_->float_buffer.data,
|
||||
int2(this->input_2_->x, this->input_2_->y));
|
||||
color_counter++;
|
||||
}
|
||||
else {
|
||||
/* Unsupported sockets. */
|
||||
input_result = new Result(this->create_result(ResultType::Color, ResultPrecision::Full));
|
||||
input_result->allocate_invalid();
|
||||
}
|
||||
|
||||
node_group_operation.map_input_to_result(input_socket->identifier, input_result);
|
||||
inputs.append(std::unique_ptr<Result>(input_result));
|
||||
}
|
||||
|
||||
node_group_operation.evaluate();
|
||||
|
||||
/* Write the outputs of the operation. */
|
||||
for (const bNodeTreeInterfaceSocket *output_socket : node_group.interface_outputs()) {
|
||||
Result &output_result = node_group_operation.get_result(output_socket->identifier);
|
||||
if (!output_result.should_compute()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Realize the output transforms if needed. */
|
||||
const InputDescriptor input_descriptor = {ResultType::Color,
|
||||
InputRealizationMode::OperationDomain};
|
||||
SimpleOperation *realization_operation = RealizeOnDomainOperation::construct_if_needed(
|
||||
*this, output_result, input_descriptor, output_result.domain());
|
||||
if (realization_operation) {
|
||||
realization_operation->map_input_to_result(&output_result);
|
||||
realization_operation->evaluate();
|
||||
Result &realized_output_result = realization_operation->get_result();
|
||||
this->write_output(realized_output_result);
|
||||
realized_output_result.release();
|
||||
delete realization_operation;
|
||||
continue;
|
||||
}
|
||||
|
||||
this->write_output(output_result);
|
||||
output_result.release();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static bool is_linear_float_buffer(const ImBuf *image)
|
||||
{
|
||||
return image->float_buffer.data &&
|
||||
IMB_colormanagement_space_is_scene_linear(image->float_buffer.colorspace);
|
||||
}
|
||||
|
||||
static ImBuf *make_linear_float_buffer(ImBuf *src)
|
||||
{
|
||||
if (!src) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Already have scene linear float pixels, return same buffer. */
|
||||
if (is_linear_float_buffer(src)) {
|
||||
return src;
|
||||
}
|
||||
|
||||
ImBuf *dst = IMB_allocImBuf(
|
||||
src->x, src->y, src->planes, IB_float_data | IB_uninitialized_pixels);
|
||||
const char *to_colorspace = IMB_colormanagement_role_colorspace_name_get(
|
||||
COLOR_ROLE_SCENE_LINEAR);
|
||||
if (src->float_buffer.data == nullptr) {
|
||||
const char *from_colorspace = IMB_colormanagement_get_rect_colorspace(src);
|
||||
IMB_colormanagement_transform_byte_to_float(dst->float_buffer.data,
|
||||
src->byte_buffer.data,
|
||||
src->x,
|
||||
src->y,
|
||||
src->channels,
|
||||
from_colorspace,
|
||||
to_colorspace);
|
||||
}
|
||||
else {
|
||||
const char *from_colorspace = IMB_colormanagement_get_float_colorspace(src);
|
||||
//@TODO: src->dst transform would be faster instead of copy + transform in-place
|
||||
memcpy(dst->float_buffer.data,
|
||||
src->float_buffer.data,
|
||||
IMB_get_pixel_count(src) * src->channels * sizeof(float));
|
||||
IMB_colormanagement_transform_float(dst->float_buffer.data,
|
||||
dst->x,
|
||||
dst->y,
|
||||
dst->channels,
|
||||
from_colorspace,
|
||||
to_colorspace,
|
||||
true);
|
||||
}
|
||||
IMB_colormanagement_assign_float_colorspace(dst, to_colorspace);
|
||||
return dst;
|
||||
}
|
||||
|
||||
static ImBuf *do_compositor_effect(const RenderData *context,
|
||||
SeqRenderState * /*state*/,
|
||||
Strip *strip,
|
||||
float /*timeline_frame*/,
|
||||
float fac,
|
||||
ImBuf *src1,
|
||||
ImBuf *src2)
|
||||
{
|
||||
const int x = context->rectx;
|
||||
const int y = context->recty;
|
||||
ImBuf *out = IMB_allocImBuf(x, y, 32, IB_float_data | IB_uninitialized_pixels);
|
||||
IMB_colormanagement_assign_float_colorspace(
|
||||
out, IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR));
|
||||
|
||||
CompositorEffectVars *data = static_cast<CompositorEffectVars *>(strip->effectdata);
|
||||
if (!data || !data->node_group) {
|
||||
IMB_rectfill(out, float4(0, 0, 0, 1));
|
||||
}
|
||||
else {
|
||||
ImBuf *linear_src1 = make_linear_float_buffer(src1);
|
||||
ImBuf *linear_src2 = make_linear_float_buffer(src2);
|
||||
|
||||
/* TODO: Should be persistent across evaluations. */
|
||||
compositor::StaticCacheManager cache_manager;
|
||||
CompositorEffectContext com_context(
|
||||
cache_manager, *context, data->node_group, linear_src1, linear_src2, out, fac, *strip);
|
||||
com_context.evaluate();
|
||||
com_context.cache_manager().reset();
|
||||
// context.result_translation += com_context.get_result_translation(); //@TODO?
|
||||
|
||||
if (linear_src1 != src1) {
|
||||
IMB_freeImBuf(linear_src1);
|
||||
}
|
||||
if (linear_src2 != src2) {
|
||||
IMB_freeImBuf(linear_src2);
|
||||
}
|
||||
seq_imbuf_to_sequencer_space(context->scene, out, true);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static void init_compositor_effect(Strip *strip)
|
||||
{
|
||||
CompositorEffectVars *data = MEM_new<CompositorEffectVars>(__func__);
|
||||
strip->effectdata = data;
|
||||
}
|
||||
|
||||
static void free_compositor_effect(Strip *strip, const bool /*do_id_user*/)
|
||||
{
|
||||
if (strip->effectdata) {
|
||||
CompositorEffectVars *data = static_cast<CompositorEffectVars *>(strip->effectdata);
|
||||
MEM_delete(data);
|
||||
strip->effectdata = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static StripEarlyOut early_out_compositor(const Strip *strip, float /*fac*/)
|
||||
{
|
||||
/* No inputs: compositor generates the result. */
|
||||
if (strip->input1 == nullptr) {
|
||||
return StripEarlyOut::NoInput;
|
||||
}
|
||||
|
||||
/* One or two inputs: do the effect. */
|
||||
return StripEarlyOut::DoEffect;
|
||||
}
|
||||
|
||||
void compositor_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.init = init_compositor_effect;
|
||||
rval.free = free_compositor_effect;
|
||||
rval.execute = do_compositor_effect;
|
||||
rval.early_out = early_out_compositor;
|
||||
}
|
||||
|
||||
} // namespace blender::seq
|
||||
|
|
@ -36,11 +36,6 @@ static void free_gaussian_blur_effect(Strip *strip, const bool /*do_id_user*/)
|
|||
}
|
||||
}
|
||||
|
||||
static int num_inputs_gaussian_blur()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static StripEarlyOut early_out_gaussian_blur(const Strip *strip, float /*fac*/)
|
||||
{
|
||||
GaussianBlurVars *data = static_cast<GaussianBlurVars *>(strip->effectdata);
|
||||
|
|
@ -221,7 +216,6 @@ static ImBuf *do_gaussian_blur_effect(const RenderData *context,
|
|||
void gaussian_blur_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.init = init_gaussian_blur_effect;
|
||||
rval.num_inputs = num_inputs_gaussian_blur;
|
||||
rval.free = free_gaussian_blur_effect;
|
||||
rval.early_out = early_out_gaussian_blur;
|
||||
rval.execute = do_gaussian_blur_effect;
|
||||
|
|
|
|||
|
|
@ -142,11 +142,6 @@ static void free_glow_effect(Strip *strip, const bool /*do_id_user*/)
|
|||
}
|
||||
}
|
||||
|
||||
static int num_inputs_glow()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void do_glow_effect_byte(Strip *strip,
|
||||
int render_size,
|
||||
float fac,
|
||||
|
|
@ -250,7 +245,6 @@ static ImBuf *do_glow_effect(const RenderData *context,
|
|||
void glow_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.init = init_glow_effect;
|
||||
rval.num_inputs = num_inputs_glow;
|
||||
rval.free = free_glow_effect;
|
||||
rval.execute = do_glow_effect;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,6 @@
|
|||
|
||||
namespace blender::seq {
|
||||
|
||||
/* No effect inputs for multi-camera, we use #give_ibuf_seq. */
|
||||
static int num_inputs_multicam()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static StripEarlyOut early_out_multicam(const Strip * /*strip*/, float /*fac*/)
|
||||
{
|
||||
return StripEarlyOut::NoInput;
|
||||
|
|
@ -63,7 +57,6 @@ static ImBuf *do_multicam(const RenderData *context,
|
|||
|
||||
void multi_camera_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.num_inputs = num_inputs_multicam;
|
||||
rval.early_out = early_out_multicam;
|
||||
rval.execute = do_multicam;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,11 +31,6 @@ static void free_solid_color(Strip *strip, const bool /*do_id_user*/)
|
|||
}
|
||||
}
|
||||
|
||||
static int num_inputs_color()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static StripEarlyOut early_out_color(const Strip * /*strip*/, float /*fac*/)
|
||||
{
|
||||
return StripEarlyOut::NoInput;
|
||||
|
|
@ -92,7 +87,6 @@ static ImBuf *do_solid_color(const RenderData *context,
|
|||
void solid_color_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.init = init_solid_color;
|
||||
rval.num_inputs = num_inputs_color;
|
||||
rval.free = free_solid_color;
|
||||
rval.early_out = early_out_color;
|
||||
rval.execute = do_solid_color;
|
||||
|
|
|
|||
|
|
@ -45,11 +45,6 @@ static void free_speed_effect(Strip *strip, const bool /*do_id_user*/)
|
|||
}
|
||||
}
|
||||
|
||||
static int num_inputs_speed()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void copy_speed_effect(Strip *dst, const Strip *src, const int /*flag*/)
|
||||
{
|
||||
SpeedControlVars *v = MEM_dupalloc(static_cast<SpeedControlVars *>(src->effectdata));
|
||||
|
|
@ -202,7 +197,6 @@ static ImBuf *do_speed_effect(const RenderData *context,
|
|||
void speed_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.init = init_speed_effect;
|
||||
rval.num_inputs = num_inputs_speed;
|
||||
rval.free = free_speed_effect;
|
||||
rval.copy = copy_speed_effect;
|
||||
rval.execute = do_speed_effect;
|
||||
|
|
|
|||
|
|
@ -299,11 +299,6 @@ static void copy_text_effect(Strip *dst, const Strip *src, const int flag)
|
|||
dst->effectdata = data;
|
||||
}
|
||||
|
||||
static int num_inputs_text()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static StripEarlyOut early_out_text(const Strip *strip, float /*fac*/)
|
||||
{
|
||||
if (!effects_can_render_text(strip)) {
|
||||
|
|
@ -1106,7 +1101,6 @@ static ImBuf *do_text_effect(const RenderData *context,
|
|||
|
||||
void text_effect_get_handle(EffectHandle &rval)
|
||||
{
|
||||
rval.num_inputs = num_inputs_text;
|
||||
rval.init = init_text_effect;
|
||||
rval.free = free_text_effect;
|
||||
rval.copy = copy_text_effect;
|
||||
|
|
|
|||
|
|
@ -169,11 +169,6 @@ static void free_wipe_effect(Strip *strip, const bool /*do_id_user*/)
|
|||
}
|
||||
}
|
||||
|
||||
static int num_inputs_wipe()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void do_wipe_effect(
|
||||
const Strip *strip, float fac, int width, int height, const T *rect1, const T *rect2, T *out)
|
||||
|
|
@ -246,7 +241,6 @@ void wipe_effect_get_handle(EffectHandle &rval)
|
|||
{
|
||||
rval.init = init_wipe_effect;
|
||||
rval.free = free_wipe_effect;
|
||||
rval.num_inputs = num_inputs_wipe;
|
||||
rval.early_out = early_out_fade;
|
||||
rval.execute = do_wipe_effect;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
#include "BLI_listbase.h"
|
||||
|
||||
#include "SEQ_connect.hh"
|
||||
#include "SEQ_effects.hh"
|
||||
#include "SEQ_iterator.hh"
|
||||
#include "SEQ_relations.hh"
|
||||
#include "SEQ_render.hh"
|
||||
|
|
@ -200,7 +199,7 @@ bool must_render_strip(const VectorSet<Strip *> &strip_stack, Strip *target_stri
|
|||
}
|
||||
|
||||
/* All effects with inputs are rendered assuming they pass the above checks. */
|
||||
if (target_strip->is_effect() && effect_get_num_inputs(target_strip->type) != 0) {
|
||||
if (target_strip->is_effect_with_inputs()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -711,9 +711,8 @@ static ImBuf *seq_render_preprocess_ibuf(const RenderData *context,
|
|||
}
|
||||
|
||||
/* Proxies and non-generator effect strips are not stored in cache. */
|
||||
const bool is_effect_with_inputs = strip->is_effect() &&
|
||||
(effect_get_num_inputs(strip->type) != 0 ||
|
||||
(strip->type == STRIP_TYPE_ADJUSTMENT));
|
||||
const bool is_effect_with_inputs = strip->is_effect_with_inputs() ||
|
||||
strip->type == STRIP_TYPE_ADJUSTMENT;
|
||||
if (!is_proxy_image && !is_effect_with_inputs) {
|
||||
Scene *orig_scene = prefetch_get_original_scene(context);
|
||||
if (orig_scene->ed->cache_flag & SEQ_CACHE_STORE_RAW) {
|
||||
|
|
@ -783,7 +782,7 @@ static ImBuf *seq_render_effect_strip_impl(const RenderData *context,
|
|||
}
|
||||
}
|
||||
|
||||
if (ibuf[0] && (ibuf[1] || effect_get_num_inputs(strip->type) == 1)) {
|
||||
if (ibuf[0] && (ibuf[1] || strip->effect_num_inputs_get() == 1)) {
|
||||
out = sh.execute(context, state, strip, timeline_frame, fac, ibuf[0], ibuf[1]);
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -900,6 +900,9 @@ static bool strip_write_data_cb(Strip *strip, void *userdata)
|
|||
case STRIP_TYPE_COLORMIX:
|
||||
writer->write_struct_cast<ColorMixVars>(strip->effectdata);
|
||||
break;
|
||||
case STRIP_TYPE_COMPOSITOR:
|
||||
writer->write_struct_cast<CompositorEffectVars>(strip->effectdata);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -999,6 +1002,9 @@ static bool strip_read_data_cb(Strip *strip, void *user_data)
|
|||
case STRIP_TYPE_COLORMIX:
|
||||
BLO_read_struct(reader, ColorMixVars, &strip->effectdata);
|
||||
break;
|
||||
case STRIP_TYPE_COMPOSITOR:
|
||||
BLO_read_struct(reader, CompositorEffectVars, &strip->effectdata);
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
strip->effectdata = nullptr;
|
||||
|
|
@ -1290,9 +1296,16 @@ ListBaseT<SeqTimelineChannel> *Editing::current_channels() const
|
|||
|
||||
bool Strip::is_effect() const
|
||||
{
|
||||
return (this->type >= STRIP_TYPE_CROSS && this->type <= STRIP_TYPE_OVERDROP_REMOVED) ||
|
||||
(this->type >= STRIP_TYPE_WIPE && this->type <= STRIP_TYPE_ADJUSTMENT) ||
|
||||
(this->type >= STRIP_TYPE_GAUSSIAN_BLUR && this->type <= STRIP_TYPE_COLORMIX);
|
||||
return blender::seq::strip_type_is_effect(StripType(this->type));
|
||||
}
|
||||
|
||||
int Strip::effect_num_inputs_get() const
|
||||
{
|
||||
/* Compositor can have varying amount of inputs; return based on assigned inputs. */
|
||||
if (this->type == STRIP_TYPE_COMPOSITOR) {
|
||||
return this->input1 && this->input2 ? 2 : this->input1 ? 1 : 0;
|
||||
}
|
||||
return blender::seq::effect_type_get_min_num_inputs(StripType(this->type));
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
|
|
|
|||
|
|
@ -168,12 +168,13 @@ Strip *add_effect_strip(Scene *scene, ListBaseT<Strip> *seqbase, LoadData *load_
|
|||
strip->flag |= SEQ_USE_EFFECT_DEFAULT_FADE;
|
||||
effect_ensure_initialized(strip);
|
||||
|
||||
if (effect_get_num_inputs(strip->type) != 0) {
|
||||
const int min_inputs = effect_type_get_min_num_inputs(load_data->effect.type);
|
||||
if (min_inputs != 0 || load_data->effect.type == STRIP_TYPE_COMPOSITOR) {
|
||||
strip->input1 = load_data->effect.input1;
|
||||
strip->input2 = load_data->effect.input2;
|
||||
}
|
||||
|
||||
if (effect_get_num_inputs(strip->type) == 1) {
|
||||
if (min_inputs == 1) {
|
||||
strip->blend_mode = strip->input1->blend_mode;
|
||||
strip->blend_opacity = strip->input1->blend_opacity;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ bool edit_strip_swap(Scene *scene, Strip *strip_a, Strip *strip_b, const char **
|
|||
}
|
||||
|
||||
if (strip_a->is_effect() && strip_b->is_effect()) {
|
||||
if (effect_get_num_inputs(strip_a->type) != effect_get_num_inputs(strip_b->type)) {
|
||||
if (strip_a->effect_num_inputs_get() != strip_b->effect_num_inputs_get()) {
|
||||
*r_error_str = N_("Strips must have the same number of inputs");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -391,7 +391,7 @@ static bool seq_edit_split_operation_permitted_check(const Scene *scene,
|
|||
if (!seq_edit_split_intersect_check(scene, strip, timeline_frame)) {
|
||||
continue;
|
||||
}
|
||||
if (effect_get_num_inputs(strip->type) <= 1) {
|
||||
if (strip->effect_num_inputs_get() <= 1) {
|
||||
continue;
|
||||
}
|
||||
if (effect_is_transition(StripType(strip->type))) {
|
||||
|
|
|
|||
|
|
@ -57,6 +57,15 @@ static void strip_by_scene_lookup_build(Strip *strip, StripLookup *lookup)
|
|||
|
||||
static void strip_by_compositor_node_group_lookup_build(Strip *strip, StripLookup *lookup)
|
||||
{
|
||||
if (strip->type == STRIP_TYPE_COMPOSITOR && strip->effectdata) {
|
||||
const CompositorEffectVars *comp_data = static_cast<CompositorEffectVars *>(strip->effectdata);
|
||||
if (comp_data->node_group) {
|
||||
VectorSet<Strip *> &strips = lookup->strips_by_compositor_node_group.lookup_or_add_default(
|
||||
comp_data->node_group);
|
||||
strips.add(strip);
|
||||
}
|
||||
}
|
||||
|
||||
for (StripModifierData &modifier : strip->modifiers) {
|
||||
if (modifier.type != eSeqModifierType_Compositor) {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -166,6 +166,11 @@ void relations_invalidate_cache(Scene *scene, Strip *strip)
|
|||
strip_effect_speed_rebuild_map(scene, strip);
|
||||
}
|
||||
|
||||
/* Zero-input compositor effect source caches also need to be invalidated. */
|
||||
if (strip->type == STRIP_TYPE_COMPOSITOR && !strip->is_effect_with_inputs()) {
|
||||
source_image_cache_invalidate_strip(scene, strip);
|
||||
}
|
||||
|
||||
invalidate_final_cache_strip_range(scene, strip);
|
||||
intra_frame_cache_invalidate(scene, strip);
|
||||
preview_cache_invalidate(scene);
|
||||
|
|
@ -187,7 +192,7 @@ void relations_invalidate_scene_strips(const Main *bmain, const Scene *scene_tar
|
|||
}
|
||||
}
|
||||
|
||||
void relations_invalidate_compositor_modifiers(const Main *bmain, const bNodeTree *node_tree)
|
||||
void relations_invalidate_compositor_users(const Main *bmain, const bNodeTree *node_tree)
|
||||
{
|
||||
for (Scene &scene : bmain->scenes) {
|
||||
if (scene.ed != nullptr) {
|
||||
|
|
|
|||
|
|
@ -65,4 +65,17 @@ bool select_active_get_pair(Scene *scene, Strip **r_strip_act, Strip **r_strip_o
|
|||
return (*r_strip_other != nullptr);
|
||||
}
|
||||
|
||||
bool select_has_any(const Scene *scene)
|
||||
{
|
||||
Editing *ed = editing_get(scene);
|
||||
if (ed != nullptr) {
|
||||
for (Strip &strip : *ed->current_strips()) {
|
||||
if (strip.flag & SEQ_SELECT) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace blender::seq
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
#include "SEQ_animation.hh"
|
||||
#include "SEQ_channels.hh"
|
||||
#include "SEQ_edit.hh"
|
||||
#include "SEQ_effects.hh"
|
||||
#include "SEQ_iterator.hh"
|
||||
#include "SEQ_relations.hh"
|
||||
#include "SEQ_sequencer.hh"
|
||||
|
|
@ -45,7 +44,7 @@ bool transform_single_image_check(const Strip *strip)
|
|||
|
||||
bool transform_strip_can_be_translated(const Strip *strip)
|
||||
{
|
||||
return !strip->is_effect() || (effect_get_num_inputs(strip->type) == 0);
|
||||
return !strip->is_effect_with_inputs();
|
||||
}
|
||||
|
||||
bool transform_test_overlap(const Scene *scene, Strip *strip1, Strip *strip2)
|
||||
|
|
@ -433,7 +432,7 @@ static void strip_transform_handle_overwrite_trim(Scene *scene,
|
|||
|
||||
/* Trim all non effects, that have influence on effect length which is overlapping. */
|
||||
for (Strip *strip : targets) {
|
||||
if (strip->is_effect() && effect_get_num_inputs(strip->type) > 0) {
|
||||
if (strip->is_effect_with_inputs()) {
|
||||
continue;
|
||||
}
|
||||
if (overlap == STRIP_OVERLAP_LEFT_SIDE) {
|
||||
|
|
|
|||
|
|
@ -127,6 +127,8 @@ const char *get_default_stripname_by_type(int type)
|
|||
return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Crossfade");
|
||||
case STRIP_TYPE_GAMCROSS:
|
||||
return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Gamma Crossfade");
|
||||
case STRIP_TYPE_COMPOSITOR:
|
||||
return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Compositor");
|
||||
case STRIP_TYPE_ADD:
|
||||
return CTX_DATA_(BLT_I18NCONTEXT_ID_SEQUENCE, "Add");
|
||||
case STRIP_TYPE_SUB:
|
||||
|
|
|
|||
3
tests/files/sequence_editing/effects/fx_comp.blend
Normal file
3
tests/files/sequence_editing/effects/fx_comp.blend
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d5f7836d7cb43277b4e29291eece9fd59abb00b43eb701605012a230f77bc9d6
|
||||
size 85549
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:13dd3ff3f251a23b1cdeced0a1799dbcba6d86e441da1f0f119840d90341b93a
|
||||
size 67267
|
||||
Loading…
Add table
Add a link
Reference in a new issue