VSE: Add scene assets through strip add menu

Adds scene assets (including the catalogue tree) to the `Add` > `Scene` menu.

When selecting a scene asset, the asset import setting is used:
* Link: Scene is linked (cannot be edited, but will be used by the strip)
* Append: Appends the full scene including all IDs to the file.
* Append/Reuse: Appends the scene but will reuse IDs (like mesh data, materials, etc.).
   Objects, collections, and the scene itself are not reused.

Part of #144063.

Pull Request: https://projects.blender.org/blender/blender/pulls/141213
This commit is contained in:
Falk David
2025-08-13 13:11:47 +02:00
committed by Falk David
parent fbbd79aa50
commit 2f852d4f00
7 changed files with 331 additions and 3 deletions

View File

@@ -801,13 +801,15 @@ class SEQUENCER_MT_add_scene(Menu):
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("sequencer.scene_strip_add_new", text="Empty Scene", icon='ADD').type = 'EMPTY'
layout.menu_contents("SEQUENCER_MT_scene_add_root_catalogs")
bpy_data_scenes_len = len(bpy.data.scenes)
if bpy_data_scenes_len > 10:
layout.separator()
layout.label(text="Scenes", icon='NONE')
layout.operator_context = 'INVOKE_DEFAULT'
layout.operator("sequencer.scene_strip_add", text="Scene...", icon='SCENE_DATA')
elif bpy_data_scenes_len > 1:
layout.separator()
layout.label(text="Scenes", icon='NONE')
scene = context.scene
for sc_item in bpy.data.scenes:
if sc_item == scene:

View File

@@ -15,6 +15,7 @@ set(INC_SYS
set(SRC
sequencer_add.cc
sequencer_add_menu_scene_assets.cc
sequencer_buttons.cc
sequencer_channels_draw.cc
sequencer_channels_edit.cc
@@ -45,6 +46,7 @@ set(SRC
)
set(LIB
PRIVATE bf::asset_system
PRIVATE bf::blenfont
PRIVATE bf::blenkernel
PRIVATE bf::blenlib

View File

@@ -10,6 +10,8 @@
#include <cstdlib>
#include <cstring>
#include "AS_asset_representation.hh"
#include "DNA_sequence_types.h"
#include "MEM_guardedalloc.h"
@@ -49,8 +51,9 @@
#include "SEQ_time.hh"
#include "SEQ_transform.hh"
#include "ED_asset.hh"
#include "ED_asset_menu_utils.hh"
#include "ED_scene.hh"
/* For menu, popup, icons, etc. */
#include "ED_screen.hh"
#include "ED_sequencer.hh"
#include "ED_time_scrub_ui.hh"
@@ -1931,4 +1934,101 @@ void SEQUENCER_OT_effect_strip_add(wmOperatorType *ot)
RNA_def_property_subtype(prop, PROP_COLOR_GAMMA);
}
static Scene *sequencer_add_scene_asset(const bContext &C,
const asset_system::AssetRepresentation &asset,
ReportList & /*reports*/)
{
Main &bmain = *CTX_data_main(&C);
Scene *scene_asset = reinterpret_cast<Scene *>(
asset::asset_local_id_ensure_imported(bmain, asset));
return scene_asset;
}
static wmOperatorStatus sequencer_add_scene_asset_invoke(bContext *C,
wmOperator *op,
const wmEvent *event)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_sequencer_scene(C);
if (!scene) {
return OPERATOR_CANCELLED;
}
Editing *ed = seq::editing_ensure(scene);
BLI_assert(ed != nullptr);
sequencer_disable_one_time_properties(C, op);
sequencer_generic_invoke_xy__internal(C, op, 0, STRIP_TYPE_SCENE, event);
const asset_system::AssetRepresentation *asset =
asset::operator_asset_reference_props_get_asset_from_all_library(*C, *op->ptr, op->reports);
if (!asset) {
return OPERATOR_CANCELLED;
}
Scene *scene_asset = sequencer_add_scene_asset(*C, *asset, *op->reports);
if (!scene_asset) {
return OPERATOR_CANCELLED;
}
const char *error_msg;
if (!have_free_channels(C, op, 1, &error_msg)) {
BKE_report(op->reports, RPT_ERROR, error_msg);
return OPERATOR_CANCELLED;
}
if (RNA_boolean_get(op->ptr, "replace_sel")) {
deselect_all_strips(scene);
}
seq::LoadData load_data;
load_data_init_from_operator(&load_data, C, op);
load_data.scene = scene_asset;
Strip *strip = seq::add_scene_strip(scene, ed->seqbasep, &load_data);
seq_load_apply_generic_options(C, op, strip);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
DEG_relations_tag_update(bmain);
sequencer_select_do_updates(C, scene);
if (RNA_boolean_get(op->ptr, "move_strips")) {
move_strips(C);
}
return OPERATOR_FINISHED;
}
static std::string sequencer_add_scene_asset_get_description(bContext *C,
wmOperatorType * /*ot*/,
PointerRNA *ptr)
{
const asset_system::AssetRepresentation *asset =
asset::operator_asset_reference_props_get_asset_from_all_library(*C, *ptr, nullptr);
if (!asset) {
return "";
}
const AssetMetaData &asset_data = asset->get_metadata();
if (!asset_data.description) {
return "";
}
return TIP_(asset_data.description);
}
void SEQUENCER_OT_add_scene_strip_from_scene_asset(wmOperatorType *ot)
{
ot->name = "Add Scene Asset";
ot->description = "Add a scene strip from a scene asset";
ot->idname = "SEQUENCER_OT_add_scene_strip_from_scene_asset";
ot->invoke = sequencer_add_scene_asset_invoke;
ot->poll = ED_operator_sequencer_active_editable;
ot->get_description = sequencer_add_scene_asset_get_description;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_MOVE);
asset::operator_asset_reference_props_register(*ot->srna);
}
} // namespace blender::ed::vse

View File

@@ -0,0 +1,207 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "AS_asset_catalog.hh"
#include "AS_asset_catalog_tree.hh"
#include "AS_asset_library.hh"
#include "AS_asset_representation.hh"
#include "BLI_multi_value_map.hh"
#include "BLI_string.h"
#include "DNA_space_types.h"
#include "DNA_workspace_types.h"
#include "BKE_asset.hh"
#include "BKE_idprop.hh"
#include "BKE_screen.hh"
#include "BLT_translation.hh"
#include "RNA_access.hh"
#include "ED_asset.hh"
#include "ED_asset_menu_utils.hh"
#include "ED_sequencer.hh"
#include "UI_interface.hh"
#include "UI_interface_layout.hh"
#include "sequencer_intern.hh"
namespace blender::ed::vse {
static bool sequencer_add_menu_poll(const bContext *C, MenuType * /*mt*/)
{
/* Add menu is not accessible from the VSE preview. */
SpaceSeq *sseq = CTX_wm_space_seq(C);
return sseq && ELEM(sseq->view, SEQ_VIEW_SEQUENCE, SEQ_VIEW_SEQUENCE_PREVIEW);
}
static bool all_loading_finished()
{
AssetLibraryReference all_library_ref = asset_system::all_library_reference();
return asset::list::is_loaded(&all_library_ref);
}
static asset::AssetItemTree build_catalog_tree(const bContext &C)
{
asset::AssetFilterSettings type_filter{};
type_filter.id_types = FILTER_ID_SCE;
const AssetLibraryReference library = asset_system::all_library_reference();
asset_system::all_library_reload_catalogs_if_dirty();
return asset::build_filtered_all_catalog_tree(library, C, type_filter, {});
}
static void sequencer_add_catalog_assets_draw(const bContext *C, Menu *menu)
{
SpaceSeq *sseq = CTX_wm_space_seq(C);
if (!sseq || !ELEM(sseq->view, SEQ_VIEW_SEQUENCE, SEQ_VIEW_SEQUENCE_PREVIEW)) {
return;
}
if (!sseq->runtime->assets_for_menu) {
sseq->runtime->assets_for_menu = std::make_shared<asset::AssetItemTree>(
build_catalog_tree(*C));
return;
}
asset::AssetItemTree &tree = *sseq->runtime->assets_for_menu;
const std::optional<blender::StringRefNull> menu_path = CTX_data_string_get(
C, "asset_catalog_path");
if (!menu_path) {
return;
}
const Span<asset_system::AssetRepresentation *> assets = tree.assets_per_path.lookup(
menu_path->c_str());
const asset_system::AssetCatalogTreeItem *catalog_item = tree.catalogs.find_item(
menu_path->c_str());
BLI_assert(catalog_item != nullptr);
if (assets.is_empty() && !catalog_item->has_children()) {
return;
}
uiLayout *layout = menu->layout;
bool add_separator = true;
for (const asset_system::AssetRepresentation *asset : assets) {
if (add_separator) {
layout->separator();
add_separator = false;
}
PointerRNA op_ptr = layout->op("SEQUENCER_OT_add_scene_strip_from_scene_asset",
IFACE_(asset->get_name()),
ICON_NONE,
wm::OpCallContext::InvokeRegionWin,
UI_ITEM_NONE);
asset::operator_asset_reference_props_set(*asset, op_ptr);
}
catalog_item->foreach_child([&](const asset_system::AssetCatalogTreeItem &item) {
if (add_separator) {
layout->separator();
add_separator = false;
}
asset::draw_menu_for_catalog(item, "SEQUENCER_MT_scene_add_catalog_assets", *layout);
});
}
static void sequencer_add_unassigned_assets_draw(const bContext *C, Menu *menu)
{
SpaceSeq *sseq = CTX_wm_space_seq(C);
if (!sseq || !ELEM(sseq->view, SEQ_VIEW_SEQUENCE, SEQ_VIEW_SEQUENCE_PREVIEW)) {
return;
}
if (!sseq->runtime->assets_for_menu) {
sseq->runtime->assets_for_menu = std::make_shared<asset::AssetItemTree>(
build_catalog_tree(*C));
return;
}
asset::AssetItemTree &tree = *sseq->runtime->assets_for_menu;
for (const asset_system::AssetRepresentation *asset : tree.unassigned_assets) {
PointerRNA op_ptr = menu->layout->op("SEQUENCER_OT_add_scene_strip_from_scene_asset",
IFACE_(asset->get_name()),
ICON_NONE,
wm::OpCallContext::InvokeRegionWin,
UI_ITEM_NONE);
BLI_assert(op_ptr.data != nullptr);
asset::operator_asset_reference_props_set(*asset, op_ptr);
}
}
static void sequencer_add_root_catalogs_draw(const bContext *C, Menu *menu)
{
SpaceSeq *sseq = CTX_wm_space_seq(C);
if (!sseq || !ELEM(sseq->view, SEQ_VIEW_SEQUENCE, SEQ_VIEW_SEQUENCE_PREVIEW)) {
return;
}
sseq->runtime->assets_for_menu = std::make_shared<asset::AssetItemTree>(build_catalog_tree(*C));
const bool loading_finished = all_loading_finished();
asset::AssetItemTree &tree = *sseq->runtime->assets_for_menu;
if (tree.catalogs.is_empty() && loading_finished && tree.unassigned_assets.is_empty()) {
return;
}
uiLayout *layout = menu->layout;
layout->separator();
layout->label(IFACE_("Assets"), ICON_NONE);
if (!loading_finished) {
layout->label(IFACE_("Loading Asset Libraries"), ICON_INFO);
}
tree.catalogs.foreach_root_item([&](const asset_system::AssetCatalogTreeItem &item) {
asset::draw_menu_for_catalog(item, "SEQUENCER_MT_scene_add_catalog_assets", *layout);
});
if (!tree.unassigned_assets.is_empty()) {
layout->separator();
layout->menu(
"SEQUENCER_MT_scene_add_unassigned_assets", IFACE_("Unassigned"), ICON_FILE_HIDDEN);
}
/* We expect this to be drawn before another section in the menu. */
layout->separator();
}
MenuType add_catalog_assets_menu_type()
{
MenuType type{};
STRNCPY(type.idname, "SEQUENCER_MT_scene_add_catalog_assets");
type.poll = sequencer_add_menu_poll;
type.draw = sequencer_add_catalog_assets_draw;
type.listener = asset::list::asset_reading_region_listen_fn;
type.flag = MenuTypeFlag::ContextDependent;
return type;
}
MenuType add_unassigned_assets_menu_type()
{
MenuType type{};
STRNCPY(type.idname, "SEQUENCER_MT_scene_add_unassigned_assets");
type.poll = sequencer_add_menu_poll;
type.draw = sequencer_add_unassigned_assets_draw;
type.listener = asset::list::asset_reading_region_listen_fn;
type.flag = MenuTypeFlag::ContextDependent;
type.description = N_(
"Scene assets not assigned to a catalog.\n"
"Catalogs can be assigned in the Asset Browser");
return type;
}
MenuType add_root_catalogs_menu_type()
{
MenuType type{};
STRNCPY(type.idname, "SEQUENCER_MT_scene_add_root_catalogs");
type.poll = sequencer_add_menu_poll;
type.draw = sequencer_add_root_catalogs_draw;
type.listener = asset::list::asset_reading_region_listen_fn;
return type;
}
} // namespace blender::ed::vse

View File

@@ -47,6 +47,10 @@ struct ScrArea;
struct Editing;
struct ListBase;
namespace blender::ed::asset {
struct AssetItemTree;
}
namespace blender::ed::vse {
class SeqQuadsBatch;
@@ -60,6 +64,8 @@ struct SpaceSeq_Runtime : public NonCopyable {
SeqScopes scopes;
std::shared_ptr<asset::AssetItemTree> assets_for_menu;
SpaceSeq_Runtime() = default;
~SpaceSeq_Runtime();
};
@@ -286,6 +292,7 @@ void SEQUENCER_OT_mask_strip_add(wmOperatorType *ot);
void SEQUENCER_OT_sound_strip_add(wmOperatorType *ot);
void SEQUENCER_OT_image_strip_add(wmOperatorType *ot);
void SEQUENCER_OT_effect_strip_add(wmOperatorType *ot);
void SEQUENCER_OT_add_scene_strip_from_scene_asset(wmOperatorType *ot);
/* `sequencer_drag_drop.cc` */
@@ -398,4 +405,9 @@ blender::Vector<Strip *> sequencer_visible_strips_get(const Scene *scene, const
wmOperatorStatus sequencer_clipboard_copy_exec(bContext *C, wmOperator *op);
wmOperatorStatus sequencer_clipboard_paste_exec(bContext *C, wmOperator *op);
/* `sequencer_add_menu_scene_assets.cc` */
MenuType add_catalog_assets_menu_type();
MenuType add_unassigned_assets_menu_type();
MenuType add_root_catalogs_menu_type();
} // namespace blender::ed::vse

View File

@@ -116,6 +116,7 @@ void sequencer_operatortypes()
WM_operatortype_append(SEQUENCER_OT_sound_strip_add);
WM_operatortype_append(SEQUENCER_OT_image_strip_add);
WM_operatortype_append(SEQUENCER_OT_effect_strip_add);
WM_operatortype_append(SEQUENCER_OT_add_scene_strip_from_scene_asset);
/* sequencer_modifiers.c */
WM_operatortype_append(SEQUENCER_OT_strip_modifier_add);

View File

@@ -1244,6 +1244,10 @@ void ED_spacetype_sequencer()
art = ED_area_type_hud(st->spaceid);
BLI_addhead(&st->regiontypes, art);
WM_menutype_add(MEM_dupallocN<MenuType>(__func__, add_catalog_assets_menu_type()));
WM_menutype_add(MEM_dupallocN<MenuType>(__func__, add_unassigned_assets_menu_type()));
WM_menutype_add(MEM_dupallocN<MenuType>(__func__, add_root_catalogs_menu_type()));
BKE_spacetype_register(std::move(st));
/* Set the sequencer callback when not in background mode. */