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:
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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. */
|
||||
|
||||
Reference in New Issue
Block a user