Asset System: "Mark Asset" & "Clear Asset" operators and UI integration

This makes it possible to turn data-blocks into assets and back into normal
data-blocks. A core design decision made for the asset system is that not every
data-block should be an asset, because not every data-block is made for reuse.
Users have to explicitly mark data-blocks as assets.

Exposes "Mark Asset" and "Clear Asset" in Outliner context menus (currently ID
Data submenu) and button context menus. We are still not too happy with the
names, they may change.

This uses the new context members to pass data-blocks to operators, added in
af008f5532 and 0c1d476923.

Part of the first Asset Browser milestone. Check the #asset_browser_milestone_1
project milestone on developer.blender.org.

Differential Revision: https://developer.blender.org/D9717

Reviewed by: Brecht Van Lommel
This commit is contained in:
Julian Eisel 2020-12-11 23:16:29 +01:00
parent b71eb3a105
commit c25e031049
Notes: blender-bot 2023-02-14 10:37:50 +01:00
Referenced by issue #82819, Merge Asset Browser Milestone 1 (Not Necessarily Feature-Complete)
18 changed files with 453 additions and 1 deletions

View File

@ -121,6 +121,10 @@
* \ingroup editors
*/
/** \defgroup edasset asset
* \ingroup editors
*/
/** \defgroup edcurve curve
* \ingroup editors
*/

View File

@ -46,6 +46,9 @@ struct AssetTagEnsureResult BKE_asset_metadata_tag_ensure(struct AssetMetaData *
const char *name);
void BKE_asset_metadata_tag_remove(struct AssetMetaData *asset_data, struct AssetTag *tag);
struct PreviewImage *BKE_asset_metadata_preview_get_from_id(const struct AssetMetaData *asset_data,
const struct ID *owner_id);
void BKE_asset_metadata_write(struct BlendWriter *writer, struct AssetMetaData *asset_data);
void BKE_asset_metadata_read(struct BlendDataReader *reader, struct AssetMetaData *asset_data);

View File

@ -129,6 +129,8 @@ void BKE_previewimg_clear_single(struct PreviewImage *prv, enum eIconSizes size)
/* get the preview from any pointer */
struct PreviewImage **BKE_previewimg_id_get_p(const struct ID *id);
struct PreviewImage *BKE_previewimg_id_get(const struct ID *id);
/* Trigger deferred loading of a custom image file into the preview buffer. */
void BKE_previewimg_id_custom_set(struct ID *id, const char *path);

View File

@ -298,6 +298,8 @@ void BKE_id_tag_clear_atomic(struct ID *id, int tag);
bool BKE_id_is_in_global_main(struct ID *id);
bool BKE_id_can_be_asset(const struct ID *id);
void BKE_id_ordered_list(struct ListBase *ordered_lb, const struct ListBase *lb);
void BKE_id_reorder(const struct ListBase *lb, struct ID *id, struct ID *relative, bool after);

View File

@ -110,6 +110,14 @@ void BKE_asset_metadata_tag_remove(AssetMetaData *asset_data, AssetTag *tag)
BLI_assert(BLI_listbase_count(&asset_data->tags) == asset_data->tot_tags);
}
/* Queries -------------------------------------------- */
PreviewImage *BKE_asset_metadata_preview_get_from_id(const AssetMetaData *UNUSED(asset_data),
const ID *id)
{
return BKE_previewimg_id_get(id);
}
/* .blend file API -------------------------------------------- */
void BKE_asset_metadata_write(BlendWriter *writer, AssetMetaData *asset_data)

View File

@ -344,7 +344,7 @@ PreviewImage **BKE_previewimg_id_get_p(const ID *id)
ID_PRV_CASE(ID_LA, Light);
ID_PRV_CASE(ID_IM, Image);
ID_PRV_CASE(ID_BR, Brush);
ID_PRV_CASE(ID_OB, Object);
// ID_PRV_CASE(ID_OB, Object);
ID_PRV_CASE(ID_GR, Collection);
ID_PRV_CASE(ID_SCE, Scene);
ID_PRV_CASE(ID_SCR, bScreen);
@ -356,6 +356,12 @@ PreviewImage **BKE_previewimg_id_get_p(const ID *id)
return NULL;
}
PreviewImage *BKE_previewimg_id_get(const ID *id)
{
PreviewImage **prv_p = BKE_previewimg_id_get_p(id);
return prv_p ? *prv_p : NULL;
}
void BKE_previewimg_id_free(ID *id)
{
PreviewImage **prv_p = BKE_previewimg_id_get_p(id);

View File

@ -2266,6 +2266,12 @@ bool BKE_id_is_in_global_main(ID *id)
return (id == NULL || BLI_findindex(which_libbase(G_MAIN, GS(id->name)), id) != -1);
}
bool BKE_id_can_be_asset(const ID *id)
{
return !ID_IS_LINKED(id) && !ID_IS_OVERRIDE_LIBRARY(id) &&
BKE_idtype_idcode_is_linkable(GS(id->name));
}
/************************* Datablock order in UI **************************/
static int *id_order_get(ID *id)

View File

@ -22,6 +22,7 @@ if(WITH_BLENDER)
add_subdirectory(animation)
add_subdirectory(armature)
add_subdirectory(asset)
add_subdirectory(curve)
add_subdirectory(geometry)
add_subdirectory(gizmo_library)

View File

@ -0,0 +1,39 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# ***** END GPL LICENSE BLOCK *****
set(INC
../include
../../blenlib
../../blenkernel
../../makesdna
../../makesrna
../../windowmanager
../../../../intern/guardedalloc
)
set(INC_SYS
)
set(SRC
asset_edit.c
asset_ops.c
)
set(LIB
)
blender_add_lib(bf_editor_asset "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")

View File

@ -0,0 +1,69 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup edasset
*/
#include "BKE_asset.h"
#include "BKE_context.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
#include "DNA_ID.h"
#include "DNA_asset_types.h"
#include "UI_interface_icons.h"
#include "RNA_access.h"
#include "ED_asset.h"
bool ED_asset_mark_id(const bContext *C, ID *id)
{
if (id->asset_data) {
return false;
}
if (!BKE_id_can_be_asset(id)) {
return false;
}
id_fake_user_set(id);
id->asset_data = BKE_asset_metadata_create();
UI_icon_render_id(C, NULL, id, true, true);
return true;
}
bool ED_asset_clear_id(ID *id)
{
if (!id->asset_data) {
return false;
}
BKE_asset_metadata_free(&id->asset_data);
/* Don't clear fake user here, there's no guarantee that it was actually set by
* #ED_asset_mark_id(), it might have been something/someone else. */
return true;
}
bool ED_asset_can_make_single_from_context(const bContext *C)
{
/* Context needs a "id" pointer to be set for #ASSET_OT_mark()/#ASSET_OT_clear() to use. */
return CTX_data_pointer_get_type_silent(C, "id", &RNA_ID).data != NULL;
}

View File

@ -0,0 +1,238 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup edasset
*/
#include <string.h>
#include "BKE_asset.h"
#include "BKE_context.h"
#include "BKE_report.h"
#include "BLI_listbase.h"
#include "BLI_string_utils.h"
#include "DNA_asset_types.h"
#include "ED_asset.h"
#include "MEM_guardedalloc.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "WM_api.h"
#include "WM_types.h"
/* -------------------------------------------------------------------- */
struct AssetMarkResultStats {
int tot_created;
int tot_already_asset;
ID *last_id;
};
/**
* Return the IDs to operate on as list of #CollectionPointerLink links. Needs freeing.
*/
static ListBase /* CollectionPointerLink */ asset_operation_get_ids_from_context(const bContext *C)
{
ListBase list = {0};
PointerRNA idptr = CTX_data_pointer_get_type(C, "id", &RNA_ID);
if (idptr.data) {
CollectionPointerLink *ctx_link = MEM_callocN(sizeof(*ctx_link), __func__);
ctx_link->ptr = idptr;
BLI_addtail(&list, ctx_link);
}
else {
CTX_data_selected_ids(C, &list);
}
return list;
}
static void asset_mark_for_idptr_list(const bContext *C,
const ListBase /* CollectionPointerLink */ *ids,
struct AssetMarkResultStats *r_stats)
{
memset(r_stats, 0, sizeof(*r_stats));
LISTBASE_FOREACH (CollectionPointerLink *, ctx_id, ids) {
BLI_assert(RNA_struct_is_ID(ctx_id->ptr.type));
ID *id = ctx_id->ptr.data;
if (id->asset_data) {
r_stats->tot_already_asset++;
continue;
}
if (ED_asset_mark_id(C, id)) {
r_stats->last_id = id;
r_stats->tot_created++;
}
}
}
static bool asset_mark_results_report(const struct AssetMarkResultStats *stats,
ReportList *reports)
{
/* User feedback on failure. */
if ((stats->tot_created < 1) && (stats->tot_already_asset > 0)) {
BKE_report(reports,
RPT_ERROR,
"Selected data-blocks are already assets (or do not support use as assets)");
return false;
}
if (stats->tot_created < 1) {
BKE_report(reports,
RPT_ERROR,
"No data-blocks to create assets for found (or do not support use as assets)");
return false;
}
/* User feedback on success. */
if (stats->tot_created == 1) {
/* If only one data-block: Give more useful message by printing asset name. */
BKE_reportf(reports, RPT_INFO, "Data-block '%s' is now an asset", stats->last_id->name + 2);
}
else {
BKE_reportf(reports, RPT_INFO, "%i data-blocks are now assets", stats->tot_created);
}
return true;
}
static int asset_mark_exec(bContext *C, wmOperator *op)
{
ListBase ids = asset_operation_get_ids_from_context(C);
struct AssetMarkResultStats stats;
asset_mark_for_idptr_list(C, &ids, &stats);
BLI_freelistN(&ids);
if (!asset_mark_results_report(&stats, op->reports)) {
return OPERATOR_CANCELLED;
}
WM_main_add_notifier(NC_ID | NA_EDITED, NULL);
WM_main_add_notifier(NC_ASSET | NA_ADDED, NULL);
return OPERATOR_FINISHED;
}
static void ASSET_OT_mark(wmOperatorType *ot)
{
ot->name = "Mark Asset";
ot->description =
"Enable easier reuse of selected data-blocks through the Asset Browser, with the help of "
"customizable metadata (like previews, descriptions and tags)";
ot->idname = "ASSET_OT_mark";
ot->exec = asset_mark_exec;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* -------------------------------------------------------------------- */
struct AssetClearResultStats {
int tot_removed;
ID *last_id;
};
static void asset_clear_from_idptr_list(const ListBase /* CollectionPointerLink */ *ids,
struct AssetClearResultStats *r_stats)
{
memset(r_stats, 0, sizeof(*r_stats));
LISTBASE_FOREACH (CollectionPointerLink *, ctx_id, ids) {
BLI_assert(RNA_struct_is_ID(ctx_id->ptr.type));
ID *id = ctx_id->ptr.data;
if (!id->asset_data) {
continue;
}
if (ED_asset_clear_id(id)) {
r_stats->tot_removed++;
r_stats->last_id = id;
}
}
}
static bool asset_clear_result_report(const struct AssetClearResultStats *stats,
ReportList *reports)
{
if (stats->tot_removed < 1) {
BKE_report(reports, RPT_ERROR, "No asset data-blocks selected/focused");
return false;
}
if (stats->tot_removed == 1) {
/* If only one data-block: Give more useful message by printing asset name. */
BKE_reportf(
reports, RPT_INFO, "Data-block '%s' is no asset anymore", stats->last_id->name + 2);
}
else {
BKE_reportf(reports, RPT_INFO, "%i data-blocks are no assets anymore", stats->tot_removed);
}
return true;
}
static int asset_clear_exec(bContext *C, wmOperator *op)
{
ListBase ids = asset_operation_get_ids_from_context(C);
struct AssetClearResultStats stats;
asset_clear_from_idptr_list(&ids, &stats);
BLI_freelistN(&ids);
if (!asset_clear_result_report(&stats, op->reports)) {
return OPERATOR_CANCELLED;
}
WM_main_add_notifier(NC_ID | NA_EDITED, NULL);
WM_main_add_notifier(NC_ASSET | NA_REMOVED, NULL);
return OPERATOR_FINISHED;
}
static void ASSET_OT_clear(wmOperatorType *ot)
{
ot->name = "Clear Asset";
ot->description =
"Delete all asset metadata and turn the selected asset data-blocks back into normal "
"data-blocks";
ot->idname = "ASSET_OT_clear";
ot->exec = asset_clear_exec;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* -------------------------------------------------------------------- */
void ED_operatortypes_asset(void)
{
WM_operatortype_append(ASSET_OT_mark);
WM_operatortype_append(ASSET_OT_clear);
}

View File

@ -0,0 +1,39 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup editors
*/
#ifndef __ED_ASSET_H__
#define __ED_ASSET_H__
#ifdef __cplusplus
extern "C" {
#endif
bool ED_asset_mark_id(const struct bContext *C, struct ID *id);
bool ED_asset_clear_id(struct ID *id);
bool ED_asset_can_make_single_from_context(const struct bContext *C);
void ED_operatortypes_asset(void);
#ifdef __cplusplus
}
#endif
#endif /* __ED_ASSET_H__ */

View File

@ -38,6 +38,7 @@
#include "BKE_idprop.h"
#include "BKE_screen.h"
#include "ED_asset.h"
#include "ED_keyframing.h"
#include "ED_screen.h"
@ -952,6 +953,22 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
}
}
/* If the button reprents an id, it can set the "id" context pointer. */
if (ED_asset_can_make_single_from_context(C)) {
ID *id = CTX_data_pointer_get_type(C, "id", &RNA_ID).data;
/* Gray out items depending on if data-block is an asset. Preferably this could be done via
* operator poll, but that doesn't work since the operator also works with "selected_ids",
* which isn't cheap to check. */
uiLayout *sub = uiLayoutColumn(layout, true);
uiLayoutSetEnabled(sub, !id->asset_data);
uiItemO(sub, NULL, ICON_NONE, "ASSET_OT_mark");
sub = uiLayoutColumn(layout, true);
uiLayoutSetEnabled(sub, id->asset_data);
uiItemO(sub, NULL, ICON_NONE, "ASSET_OT_clear");
uiItemS(layout);
}
/* Pointer properties and string properties with
* prop_search support jumping to target object/bone. */
if (but->rnapoin.data && but->rnaprop) {

View File

@ -40,6 +40,7 @@
#include "ED_anim_api.h"
#include "ED_armature.h"
#include "ED_asset.h"
#include "ED_clip.h"
#include "ED_curve.h"
#include "ED_fileselect.h"
@ -105,6 +106,7 @@ void ED_spacetypes_init(void)
ED_operatortypes_screen();
ED_operatortypes_anim();
ED_operatortypes_animchannels();
ED_operatortypes_asset();
ED_operatortypes_gpencil();
ED_operatortypes_object();
ED_operatortypes_lattice();

View File

@ -1685,6 +1685,8 @@ typedef enum eOutlinerIdOpTypes {
OUTLINER_IDOP_INVALID = 0,
OUTLINER_IDOP_UNLINK,
OUTLINER_IDOP_MARK_ASSET,
OUTLINER_IDOP_CLEAR_ASSET,
OUTLINER_IDOP_LOCAL,
OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE,
OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY,
@ -1710,6 +1712,8 @@ typedef enum eOutlinerIdOpTypes {
/* TODO: implement support for changing the ID-block used. */
static const EnumPropertyItem prop_id_op_types[] = {
{OUTLINER_IDOP_UNLINK, "UNLINK", 0, "Unlink", ""},
{OUTLINER_IDOP_MARK_ASSET, "MARK_ASSET", 0, "Mark Asset", ""},
{OUTLINER_IDOP_CLEAR_ASSET, "CLEAR_ASSET", 0, "Clear Asset", ""},
{OUTLINER_IDOP_LOCAL, "LOCAL", 0, "Make Local", ""},
{OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""},
{OUTLINER_IDOP_DELETE, "DELETE", ICON_X, "Delete", ""},
@ -1915,6 +1919,14 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
break;
}
case OUTLINER_IDOP_MARK_ASSET: {
WM_operator_name_call(C, "ASSET_OT_mark", WM_OP_EXEC_DEFAULT, NULL);
break;
}
case OUTLINER_IDOP_CLEAR_ASSET: {
WM_operator_name_call(C, "ASSET_OT_clear", WM_OP_EXEC_DEFAULT, NULL);
break;
}
case OUTLINER_IDOP_LOCAL: {
/* make local */
outliner_do_libdata_operation(

View File

@ -47,6 +47,7 @@ set(SRC
../include/BIF_glutil.h
../include/ED_anim_api.h
../include/ED_armature.h
../include/ED_asset.h
../include/ED_buttons.h
../include/ED_clip.h
../include/ED_curve.h

View File

@ -428,6 +428,7 @@ set(LIB
bf_editor_animation
bf_editor_armature
bf_editor_asset
bf_editor_curve
bf_editor_gizmo_library
bf_editor_gpencil

View File

@ -298,6 +298,8 @@ typedef struct wmNotifier {
#define NC_LINESTYLE (23 << 24)
#define NC_CAMERA (24 << 24)
#define NC_LIGHTPROBE (25 << 24)
/* Changes to asset data in the current .blend. */
#define NC_ASSET (26 << 24)
/* data type, 256 entries is enough, it can overlap */
#define NOTE_DATA 0x00FF0000