WIP: Brush assets project #106303
|
@ -80,15 +80,3 @@ void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data)
|
|||
|
||||
void BKE_asset_weak_reference_write(BlendWriter *writer, const AssetWeakReference *weak_ref);
|
||||
void BKE_asset_weak_reference_read(BlendDataReader *reader, AssetWeakReference *weak_ref);
|
||||
|
||||
/**
|
||||
* Database of assets that are weakly reference by scene data,
|
||||
* currently used for brush assets and their dependencies.
|
||||
*/
|
||||
Main *BKE_asset_weak_reference_main(const ID *id);
|
||||
ID *BKE_asset_weak_reference_ensure(Main &global_main,
|
||||
ID_Type id_type,
|
||||
const AssetWeakReference &weak_ref);
|
||||
void BKE_asset_weak_reference_main_reload(Main &global_main, Main &main);
|
||||
void BKE_asset_weak_reference_main_free(Main &global_main, Main &asset_main);
|
||||
void BKE_asset_weak_reference_main_free_all();
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Editing of datablocks from asset libraries, separate from the current open
|
||||
* blend file.
|
||||
*
|
||||
* Each asset blend file is loaded into a separate main database, including the
|
||||
* asset datablocks and their dependencies. These datablocks are all tagged with
|
||||
* LIB_TAG_ASSET_MAIN. These can not be linked with other datablocks in the
|
||||
* current blend file.
|
||||
*
|
||||
* For editable assets in user asset libraries, each asset is stored in its own
|
||||
* blend file. This way the blend file can be easily saved, reloaded and deleted.
|
||||
*
|
||||
* This mechanism is currently only used for brush assets.
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "AS_asset_catalog.hh"
|
||||
|
||||
#include "DNA_ID_enums.h"
|
||||
|
||||
struct bUserAssetLibrary;
|
||||
struct AssetWeakReference;
|
||||
struct ID;
|
||||
struct Main;
|
||||
struct ReportList;
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
/** Get datablock from weak reference, loading the blend file as needed. */
|
||||
ID *asset_edit_id_from_weak_reference(Main &global_main,
|
||||
ID_Type id_type,
|
||||
const AssetWeakReference &weak_ref);
|
||||
|
||||
/** Get main database that a given asset datablock corresponds to. */
|
||||
Main *asset_edit_main(const ID *id);
|
||||
|
||||
/** Asset editing operations. */
|
||||
|
||||
bool asset_edit_id_is_editable(const ID *id);
|
||||
|
||||
std::optional<std::string> asset_edit_id_save_as(
|
||||
Main &global_main,
|
||||
const ID *id,
|
||||
const char *name,
|
||||
std::optional<blender::asset_system::CatalogID> catalog_id,
|
||||
std::optional<std::string> catalog_simple_name,
|
||||
const bUserAssetLibrary *user_library,
|
||||
ReportList *reports);
|
||||
|
||||
bool asset_edit_id_save(Main &global_main, const ID *id, ReportList *reports);
|
||||
bool asset_edit_id_revert(Main &global_main, const ID *id, ReportList *reports);
|
||||
bool asset_edit_id_delete(Main &global_main, const ID *id, ReportList *reports);
|
||||
|
||||
/** Clean up on exit. */
|
||||
void asset_edit_main_free_all();
|
||||
|
||||
} // namespace blender::bke
|
|
@ -65,6 +65,7 @@ set(SRC
|
|||
intern/armature_update.cc
|
||||
intern/asset.cc
|
||||
intern/asset_weak_reference.cc
|
||||
intern/asset_edit.cc
|
||||
intern/attribute.cc
|
||||
intern/attribute_access.cc
|
||||
intern/attribute_math.cc
|
||||
|
@ -333,6 +334,7 @@ set(SRC
|
|||
BKE_appdir.hh
|
||||
BKE_armature.hh
|
||||
BKE_asset.hh
|
||||
BKE_asset_edit.hh
|
||||
BKE_attribute.h
|
||||
BKE_attribute.hh
|
||||
BKE_attribute_math.hh
|
||||
|
|
|
@ -0,0 +1,518 @@
|
|||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "AS_asset_identifier.hh"
|
||||
#include "AS_asset_library.hh"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_asset_edit.hh"
|
||||
#include "BKE_blendfile.hh"
|
||||
#include "BKE_blendfile_link_append.hh"
|
||||
#include "BKE_idtype.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_lib_remap.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_preferences.h"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
#include "BLO_readfile.hh"
|
||||
#include "BLO_writefile.hh"
|
||||
|
||||
#include "DNA_asset_types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
/**
|
||||
* Asset library blend file, with editable contents.
|
||||
*/
|
||||
struct AssetEditBlend {
|
||||
std::string filepath;
|
||||
Main *main;
|
||||
bool is_editable = false;
|
||||
|
||||
AssetEditBlend(const std::string &filepath);
|
||||
~AssetEditBlend();
|
||||
|
||||
AssetEditBlend(const AssetEditBlend &) = delete;
|
||||
AssetEditBlend(AssetEditBlend &&other);
|
||||
AssetEditBlend &operator=(AssetEditBlend &&other);
|
||||
|
||||
ID *ensure_id(ID_Type id_type, const char *asset_name);
|
||||
void reload(Main &global_main);
|
||||
void clear_users(Main &global_main);
|
||||
};
|
||||
|
||||
AssetEditBlend::AssetEditBlend(const std::string &filepath)
|
||||
: filepath(std::move(filepath)), main(BKE_main_new())
|
||||
{
|
||||
this->main->is_asset_weak_reference_main = true;
|
||||
BLI_assert(!BLI_path_is_rel(filepath.c_str()));
|
||||
|
||||
/* Fairly simple check based on filepath only.
|
||||
* - Ends with `.asset.bend` extensions.
|
||||
* - Is located in user asset library.
|
||||
*
|
||||
* TODO?
|
||||
* - Check file contents.
|
||||
* - Check file is writable.
|
||||
*/
|
||||
this->is_editable = StringRef(filepath).endswith(BLENDER_ASSET_FILE_SUFFIX) &&
|
||||
BKE_preferences_asset_library_containing_path(&U, filepath.c_str());
|
||||
}
|
||||
|
||||
AssetEditBlend::~AssetEditBlend()
|
||||
{
|
||||
if (main) {
|
||||
BKE_main_free(main);
|
||||
}
|
||||
}
|
||||
|
||||
AssetEditBlend::AssetEditBlend(AssetEditBlend &&other)
|
||||
: filepath(std::exchange(other.filepath, "")), main(std::exchange(other.main, nullptr))
|
||||
{
|
||||
}
|
||||
AssetEditBlend &AssetEditBlend::operator=(AssetEditBlend &&other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
this->filepath = std::exchange(other.filepath, "");
|
||||
this->main = std::exchange(other.main, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ID *AssetEditBlend::ensure_id(const ID_Type id_type, const char *asset_name)
|
||||
{
|
||||
/* Check if we have the asset already. */
|
||||
ID *local_asset = BKE_libblock_find_name(this->main, id_type, asset_name);
|
||||
if (local_asset) {
|
||||
BLI_assert(ID_IS_ASSET(local_asset));
|
||||
return local_asset;
|
||||
}
|
||||
|
||||
/* Load asset from asset library. */
|
||||
LibraryLink_Params lapp_params{};
|
||||
lapp_params.bmain = this->main;
|
||||
BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new(&lapp_params);
|
||||
BKE_blendfile_link_append_context_flag_set(lapp_context, BLO_LIBLINK_FORCE_INDIRECT, true);
|
||||
BKE_blendfile_link_append_context_flag_set(lapp_context, 0, true);
|
||||
|
||||
BKE_blendfile_link_append_context_library_add(lapp_context, filepath.c_str(), nullptr);
|
||||
|
||||
BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add(
|
||||
lapp_context, asset_name, id_type, nullptr);
|
||||
BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0);
|
||||
|
||||
BKE_blendfile_link(lapp_context, nullptr);
|
||||
BKE_blendfile_append(lapp_context, nullptr);
|
||||
|
||||
local_asset = BKE_blendfile_link_append_context_item_newid_get(lapp_context, lapp_item);
|
||||
|
||||
BKE_blendfile_link_append_context_free(lapp_context);
|
||||
|
||||
BKE_main_id_tag_all(this->main, LIB_TAG_ASSET_MAIN, true);
|
||||
|
||||
/* Verify that the name matches. It must for referencing the same asset again to work. */
|
||||
BLI_assert(local_asset == nullptr || STREQ(local_asset->name + 2, asset_name));
|
||||
|
||||
return local_asset;
|
||||
}
|
||||
|
||||
static std::string asset_root_path_for_save(const bUserAssetLibrary &user_library,
|
||||
const ID_Type id_type)
|
||||
{
|
||||
BLI_assert(user_library.dirpath[0] != '\0');
|
||||
|
||||
char libpath[FILE_MAX];
|
||||
BLI_strncpy(libpath, user_library.dirpath, sizeof(libpath));
|
||||
BLI_path_slash_native(libpath);
|
||||
BLI_path_normalize(libpath);
|
||||
|
||||
/* Capitalize folder name. Ideally this would already available in
|
||||
* the type info to work correctly with multiple words. */
|
||||
const IDTypeInfo *id_type_info = BKE_idtype_get_info_from_idcode(id_type);
|
||||
std::string name = id_type_info->name_plural;
|
||||
name[0] = BLI_toupper_ascii(name[0]);
|
||||
|
||||
return std::string(libpath) + SEP + "Saved" + SEP + name;
|
||||
}
|
||||
|
||||
static std::string asset_blendfile_path_for_save(ReportList *reports,
|
||||
const bUserAssetLibrary &user_library,
|
||||
const StringRefNull base_name,
|
||||
const ID_Type id_type)
|
||||
{
|
||||
std::string root_path = asset_root_path_for_save(user_library, id_type);
|
||||
BLI_assert(!root_path.empty());
|
||||
|
||||
if (!BLI_dir_create_recursive(root_path.c_str())) {
|
||||
BKE_report(reports, RPT_ERROR, "Failed to create asset library directory to save brush");
|
||||
return "";
|
||||
}
|
||||
|
||||
char base_name_filesafe[FILE_MAXFILE];
|
||||
BLI_strncpy(base_name_filesafe, base_name.c_str(), sizeof(base_name_filesafe));
|
||||
BLI_path_make_safe_filename(base_name_filesafe);
|
||||
|
||||
const std::string filepath = root_path + SEP + base_name_filesafe + BLENDER_ASSET_FILE_SUFFIX;
|
||||
if (!BLI_is_file(filepath.c_str())) {
|
||||
return filepath;
|
||||
}
|
||||
|
||||
for (int i = 1;; i++) {
|
||||
const std::string filepath = root_path + SEP + base_name_filesafe + "_" + std::to_string(i++) +
|
||||
BLENDER_ASSET_FILE_SUFFIX;
|
||||
if (!BLI_is_file((filepath.c_str()))) {
|
||||
return filepath;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static bool asset_write_in_library(Main *bmain,
|
||||
const ID *id_const,
|
||||
const char *name,
|
||||
const StringRefNull filepath,
|
||||
const std::optional<asset_system::CatalogID> catalog,
|
||||
const std::optional<StringRefNull> catalog_simple_name,
|
||||
std::string &final_full_file_path,
|
||||
ReportList *reports)
|
||||
{
|
||||
/* XXX
|
||||
* FIXME
|
||||
*
|
||||
* This code is _pure evil_. It does in-place manipulation on IDs in global Main database,
|
||||
* temporarilly remove them and add them back...
|
||||
*
|
||||
* Use it as-is for now (in a similar way as python API or copy-to-buffer works). Nut the whole
|
||||
* 'BKE_blendfile_write_partial' code needs to be completely refactored.
|
||||
*
|
||||
* Ideas:
|
||||
* - Have `BKE_blendfile_write_partial_begin` return a new temp Main.
|
||||
* - Replace `BKE_blendfile_write_partial_tag_ID` by API to add IDs to this temp Main.
|
||||
* + This should _duplicate_ the ID, not remove the original one from the source Main!
|
||||
* - Have API to automatically also duplicate dependencies into temp Main.
|
||||
* + Have options to e.g. make all duplicated IDs 'local' (i.e. remove their library data).
|
||||
* - `BKE_blendfile_write_partial` then simply write the given temp main.
|
||||
* - `BKE_blendfile_write_partial_end` frees the temp Main.
|
||||
*/
|
||||
|
||||
ID *id = const_cast<ID *>(id_const);
|
||||
|
||||
const short prev_flag = id->flag;
|
||||
const int prev_tag = id->tag;
|
||||
const int prev_us = id->us;
|
||||
const std::string prev_name = id->name + 2;
|
||||
IDOverrideLibrary *prev_liboverride = id->override_library;
|
||||
AssetMetaData *asset_data = id->asset_data;
|
||||
const int write_flags = 0; /* Could use #G_FILE_COMPRESS ? */
|
||||
const eBLO_WritePathRemap remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE;
|
||||
|
||||
BKE_blendfile_write_partial_begin(bmain);
|
||||
|
||||
id->flag |= LIB_FAKEUSER;
|
||||
id->tag &= ~LIB_TAG_RUNTIME;
|
||||
id->us = 1;
|
||||
BLI_strncpy(id->name + 2, name, sizeof(id->name) - 2);
|
||||
if (!ID_IS_ASSET(id)) {
|
||||
id->asset_data = id->override_library->reference->asset_data;
|
||||
}
|
||||
id->override_library = nullptr;
|
||||
|
||||
if (catalog) {
|
||||
id->asset_data->catalog_id = *catalog;
|
||||
}
|
||||
if (catalog_simple_name) {
|
||||
STRNCPY(id->asset_data->catalog_simple_name, catalog_simple_name->c_str());
|
||||
}
|
||||
|
||||
BKE_blendfile_write_partial_tag_ID(id, true);
|
||||
|
||||
/* TODO: check overwriting existing file. */
|
||||
/* TODO: ensure filepath contains only valid characters for file system. */
|
||||
const bool sucess = BKE_blendfile_write_partial(
|
||||
bmain, filepath.c_str(), write_flags, remap_mode, reports);
|
||||
|
||||
if (sucess) {
|
||||
final_full_file_path = std::string(filepath) + SEP + "Brush" + SEP + name;
|
||||
}
|
||||
|
||||
BKE_blendfile_write_partial_end(bmain);
|
||||
|
||||
BKE_blendfile_write_partial_tag_ID(id, false);
|
||||
id->flag = prev_flag;
|
||||
id->tag = prev_tag;
|
||||
id->us = prev_us;
|
||||
BLI_strncpy(id->name + 2, prev_name.c_str(), sizeof(id->name) - 2);
|
||||
id->override_library = prev_liboverride;
|
||||
id->asset_data = asset_data;
|
||||
|
||||
return sucess;
|
||||
}
|
||||
|
||||
void AssetEditBlend::reload(Main &global_main)
|
||||
{
|
||||
Main *old_main = this->main;
|
||||
this->main = BKE_main_new();
|
||||
this->main->is_asset_weak_reference_main = true;
|
||||
|
||||
/* Fill fresh main database with same datablock as before. */
|
||||
LibraryLink_Params lapp_params{};
|
||||
lapp_params.bmain = this->main;
|
||||
BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new(&lapp_params);
|
||||
BKE_blendfile_link_append_context_flag_set(lapp_context, BLO_LIBLINK_FORCE_INDIRECT, true);
|
||||
BKE_blendfile_link_append_context_flag_set(lapp_context, 0, true);
|
||||
|
||||
BKE_blendfile_link_append_context_library_add(lapp_context, this->filepath.c_str(), nullptr);
|
||||
|
||||
/* Requests all existing datablocks to be appended again. */
|
||||
ID *old_id;
|
||||
FOREACH_MAIN_ID_BEGIN (old_main, old_id) {
|
||||
ID_Type old_id_code = GS(old_id->name);
|
||||
if (BKE_idtype_idcode_is_linkable(old_id_code)) {
|
||||
BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add(
|
||||
lapp_context, old_id->name + 2, old_id_code, nullptr);
|
||||
BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0);
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
BKE_blendfile_link(lapp_context, nullptr);
|
||||
BKE_blendfile_append(lapp_context, nullptr);
|
||||
|
||||
BKE_blendfile_link_append_context_free(lapp_context);
|
||||
|
||||
BKE_main_id_tag_all(this->main, LIB_TAG_ASSET_MAIN, true);
|
||||
|
||||
/* Remap old to new. */
|
||||
bke::id::IDRemapper mappings;
|
||||
FOREACH_MAIN_ID_BEGIN (old_main, old_id) {
|
||||
ID *new_id = BKE_libblock_find_name(this->main, GS(old_id->name), old_id->name + 2);
|
||||
mappings.add(old_id, new_id);
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
BKE_libblock_remap_multiple(&global_main, mappings, 0);
|
||||
|
||||
/* Free old database. */
|
||||
BKE_main_free(old_main);
|
||||
}
|
||||
|
||||
void AssetEditBlend::clear_users(Main &global_main)
|
||||
{
|
||||
/* Remap old to null pointer. */
|
||||
bke::id::IDRemapper mappings;
|
||||
ID *old_id;
|
||||
FOREACH_MAIN_ID_BEGIN (this->main, old_id) {
|
||||
mappings.add(old_id, nullptr);
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
BKE_libblock_remap_multiple(&global_main, mappings, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public API
|
||||
*/
|
||||
|
||||
static Vector<AssetEditBlend> &asset_edit_blend_get_all()
|
||||
{
|
||||
static Vector<AssetEditBlend> mains;
|
||||
return mains;
|
||||
}
|
||||
|
||||
static AssetEditBlend *asset_edit_blend_from_id(const ID *id)
|
||||
{
|
||||
BLI_assert(id->tag & LIB_TAG_ASSET_MAIN);
|
||||
|
||||
for (AssetEditBlend &asset_blend : asset_edit_blend_get_all()) {
|
||||
/* TODO: Look into make this whole thing more efficient. */
|
||||
ListBase *lb = which_libbase(asset_blend.main, GS(id->name));
|
||||
LISTBASE_FOREACH (ID *, other_id, lb) {
|
||||
if (id == other_id) {
|
||||
return &asset_blend;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Main *asset_edit_main(const ID *id)
|
||||
{
|
||||
const AssetEditBlend *asset_blend = asset_edit_blend_from_id(id);
|
||||
return (asset_blend) ? asset_blend->main : nullptr;
|
||||
}
|
||||
|
||||
static AssetEditBlend &asset_edit_blend_file_ensure(const StringRef filepath)
|
||||
{
|
||||
for (AssetEditBlend &asset_blend : asset_edit_blend_get_all()) {
|
||||
if (asset_blend.filepath == filepath) {
|
||||
return asset_blend;
|
||||
}
|
||||
}
|
||||
|
||||
asset_edit_blend_get_all().append_as(filepath);
|
||||
return asset_edit_blend_get_all().last();
|
||||
}
|
||||
|
||||
std::optional<std::string> asset_edit_id_save_as(Main &global_main,
|
||||
const ID *id,
|
||||
const char *name,
|
||||
std::optional<asset_system::CatalogID> catalog_id,
|
||||
std::optional<std::string> catalog_simple_name,
|
||||
const bUserAssetLibrary *user_library,
|
||||
ReportList *reports)
|
||||
{
|
||||
const std::string filepath = asset_blendfile_path_for_save(
|
||||
reports, *user_library, name, GS(id->name));
|
||||
|
||||
/* Save to asset library. */
|
||||
Main *asset_main = BKE_main_from_id(&global_main, id);
|
||||
|
||||
std::string final_full_asset_filepath;
|
||||
const bool success = asset_write_in_library(asset_main,
|
||||
id,
|
||||
name,
|
||||
filepath,
|
||||
catalog_id,
|
||||
catalog_simple_name,
|
||||
final_full_asset_filepath,
|
||||
reports);
|
||||
|
||||
if (!success) {
|
||||
BKE_report(reports, RPT_ERROR, "Failed to write to asset library");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
BKE_reportf(reports, RPT_INFO, "Saved \"%s\"", filepath.c_str());
|
||||
|
||||
return final_full_asset_filepath;
|
||||
}
|
||||
|
||||
bool asset_edit_id_save(Main & /*global_main*/, const ID *id, ReportList *reports)
|
||||
{
|
||||
AssetEditBlend *asset_blend = asset_edit_blend_from_id(id);
|
||||
if (asset_blend == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string final_full_asset_filepath;
|
||||
const bool success = asset_write_in_library(asset_blend->main,
|
||||
id,
|
||||
id->name + 2,
|
||||
asset_blend->filepath.c_str(),
|
||||
std::nullopt,
|
||||
std::nullopt,
|
||||
final_full_asset_filepath,
|
||||
reports);
|
||||
|
||||
if (!success) {
|
||||
BKE_report(reports, RPT_ERROR, "Failed to write to asset library");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asset_edit_id_revert(Main &global_main, const ID *id, ReportList * /*reports*/)
|
||||
{
|
||||
AssetEditBlend *asset_blend = asset_edit_blend_from_id(id);
|
||||
if (asset_blend == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Reload entire main, including texture dependencies. This relies on there
|
||||
* being only a single brush asset per blend file. */
|
||||
asset_blend->reload(global_main);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asset_edit_id_delete(Main &global_main, const ID *id, ReportList *reports)
|
||||
{
|
||||
AssetEditBlend *asset_blend = asset_edit_blend_from_id(id);
|
||||
if (asset_blend == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BLI_delete(asset_blend->filepath.c_str(), false, false) != 0) {
|
||||
BKE_report(reports, RPT_ERROR, "Failed to delete asset library file");
|
||||
return false;
|
||||
}
|
||||
|
||||
asset_blend->clear_users(global_main);
|
||||
|
||||
int index = 0;
|
||||
for (AssetEditBlend &asset_blend_iter : asset_edit_blend_get_all()) {
|
||||
if (&asset_blend_iter == asset_blend) {
|
||||
asset_edit_blend_get_all().remove(index);
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ID *asset_edit_id_from_weak_reference(Main &global_main,
|
||||
const ID_Type id_type,
|
||||
const AssetWeakReference &weak_ref)
|
||||
{
|
||||
char asset_full_path_buffer[FILE_MAX_LIBEXTRA];
|
||||
char *asset_lib_path, *asset_group, *asset_name;
|
||||
|
||||
AS_asset_full_path_explode_from_weak_ref(
|
||||
&weak_ref, asset_full_path_buffer, &asset_lib_path, &asset_group, &asset_name);
|
||||
if (asset_lib_path == nullptr && asset_group == nullptr && asset_name == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLI_assert(asset_name != nullptr);
|
||||
|
||||
/* Find asset in current blend file. */
|
||||
if (asset_lib_path == nullptr) {
|
||||
ID *local_asset = BKE_libblock_find_name(&global_main, id_type, asset_name);
|
||||
BLI_assert(local_asset == nullptr || ID_IS_ASSET(local_asset));
|
||||
return local_asset;
|
||||
}
|
||||
|
||||
/* If weak reference resolves to a null library path, assume we are in local asset case. */
|
||||
AssetEditBlend &asset_blend = asset_edit_blend_file_ensure(asset_lib_path);
|
||||
return asset_blend.ensure_id(id_type, asset_name);
|
||||
}
|
||||
|
||||
bool asset_edit_id_is_editable(const ID *id)
|
||||
{
|
||||
if (!(id->tag & LIB_TAG_ASSET_MAIN)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const AssetEditBlend *asset_blend = asset_edit_blend_from_id(id);
|
||||
return (asset_blend) ? asset_blend->is_editable : false;
|
||||
}
|
||||
|
||||
void asset_edit_main_free_all()
|
||||
{
|
||||
asset_edit_blend_get_all().clear_and_shrink();
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
|
@ -129,225 +129,3 @@ void BKE_asset_weak_reference_read(BlendDataReader *reader, AssetWeakReference *
|
|||
BLO_read_data_address(reader, &weak_ref->asset_library_identifier);
|
||||
BLO_read_data_address(reader, &weak_ref->relative_asset_identifier);
|
||||
}
|
||||
|
||||
/* Main database for storing assets that are weak referenced.
|
||||
*
|
||||
* This avoids mixing asset datablocks in the regular main, which leads to naming conflicts and
|
||||
* confusing user interface. */
|
||||
|
||||
struct AssetWeakReferenceMain {
|
||||
std::string filepath;
|
||||
Main *main;
|
||||
|
||||
AssetWeakReferenceMain(std::string filepath)
|
||||
: filepath(std::move(filepath)), main(BKE_main_new())
|
||||
{
|
||||
main->is_asset_weak_reference_main = true;
|
||||
BLI_assert(!BLI_path_is_rel(filepath.c_str()));
|
||||
}
|
||||
AssetWeakReferenceMain(const AssetWeakReferenceMain &) = delete;
|
||||
AssetWeakReferenceMain(AssetWeakReferenceMain &&other)
|
||||
: filepath(std::exchange(other.filepath, "")), main(std::exchange(other.main, nullptr))
|
||||
{
|
||||
}
|
||||
AssetWeakReferenceMain &operator=(AssetWeakReferenceMain &&other)
|
||||
{
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
this->filepath = std::exchange(other.filepath, "");
|
||||
this->main = std::exchange(other.main, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~AssetWeakReferenceMain()
|
||||
{
|
||||
if (main) {
|
||||
BKE_main_free(main);
|
||||
}
|
||||
}
|
||||
|
||||
void reload(Main &global_main);
|
||||
void clear_users(Main &global_main);
|
||||
};
|
||||
|
||||
void AssetWeakReferenceMain::reload(Main &global_main)
|
||||
{
|
||||
Main *old_main = this->main;
|
||||
this->main = BKE_main_new();
|
||||
this->main->is_asset_weak_reference_main = true;
|
||||
|
||||
/* Fill fresh main database with same datablock as before. */
|
||||
LibraryLink_Params lapp_params{};
|
||||
lapp_params.bmain = this->main;
|
||||
BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new(&lapp_params);
|
||||
BKE_blendfile_link_append_context_flag_set(lapp_context, BLO_LIBLINK_FORCE_INDIRECT, true);
|
||||
BKE_blendfile_link_append_context_flag_set(lapp_context, 0, true);
|
||||
|
||||
BKE_blendfile_link_append_context_library_add(lapp_context, this->filepath.c_str(), nullptr);
|
||||
|
||||
/* Requests all existing datablocks to be appended again. */
|
||||
ID *old_id;
|
||||
FOREACH_MAIN_ID_BEGIN (old_main, old_id) {
|
||||
ID_Type old_id_code = GS(old_id->name);
|
||||
if (BKE_idtype_idcode_is_linkable(old_id_code)) {
|
||||
BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add(
|
||||
lapp_context, old_id->name + 2, old_id_code, nullptr);
|
||||
BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0);
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
BKE_blendfile_link(lapp_context, nullptr);
|
||||
BKE_blendfile_append(lapp_context, nullptr);
|
||||
|
||||
BKE_blendfile_link_append_context_free(lapp_context);
|
||||
|
||||
BKE_main_id_tag_all(this->main, LIB_TAG_ASSET_MAIN, true);
|
||||
|
||||
/* Remap old to new. */
|
||||
bke::id::IDRemapper mappings;
|
||||
FOREACH_MAIN_ID_BEGIN (old_main, old_id) {
|
||||
ID *new_id = BKE_libblock_find_name(this->main, GS(old_id->name), old_id->name + 2);
|
||||
mappings.add(old_id, new_id);
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
BKE_libblock_remap_multiple(&global_main, mappings, 0);
|
||||
|
||||
/* Free old database. */
|
||||
BKE_main_free(old_main);
|
||||
}
|
||||
|
||||
void AssetWeakReferenceMain::clear_users(Main &global_main)
|
||||
{
|
||||
/* Remap old to null pointer. */
|
||||
bke::id::IDRemapper mappings;
|
||||
ID *old_id;
|
||||
FOREACH_MAIN_ID_BEGIN (this->main, old_id) {
|
||||
mappings.add(old_id, nullptr);
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
BKE_libblock_remap_multiple(&global_main, mappings, 0);
|
||||
}
|
||||
|
||||
static Vector<AssetWeakReferenceMain> &get_weak_reference_mains()
|
||||
{
|
||||
static Vector<AssetWeakReferenceMain> mains;
|
||||
return mains;
|
||||
}
|
||||
|
||||
Main *BKE_asset_weak_reference_main(const ID *id)
|
||||
{
|
||||
BLI_assert(id->tag & LIB_TAG_ASSET_MAIN);
|
||||
|
||||
for (const AssetWeakReferenceMain &weak_ref_main : get_weak_reference_mains()) {
|
||||
/* TODO: Look into make this whole thing more efficient. */
|
||||
ListBase *lb = which_libbase(weak_ref_main.main, GS(id->name));
|
||||
LISTBASE_FOREACH (ID *, other_id, lb) {
|
||||
if (id == other_id) {
|
||||
return weak_ref_main.main;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static Main &asset_weak_reference_main_ensure(const StringRef filepath)
|
||||
{
|
||||
for (const AssetWeakReferenceMain &weak_ref_main : get_weak_reference_mains()) {
|
||||
if (weak_ref_main.filepath == filepath) {
|
||||
return *weak_ref_main.main;
|
||||
}
|
||||
}
|
||||
|
||||
get_weak_reference_mains().append_as(filepath);
|
||||
return *get_weak_reference_mains().last().main;
|
||||
}
|
||||
|
||||
void BKE_asset_weak_reference_main_reload(Main &global_main, Main &asset_main)
|
||||
{
|
||||
for (AssetWeakReferenceMain &weak_ref_main : get_weak_reference_mains()) {
|
||||
if (weak_ref_main.main == &asset_main) {
|
||||
weak_ref_main.reload(global_main);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
void BKE_asset_weak_reference_main_free(Main &global_main, Main &asset_main)
|
||||
{
|
||||
int index = 0;
|
||||
for (AssetWeakReferenceMain &weak_ref_main : get_weak_reference_mains()) {
|
||||
if (weak_ref_main.main == &asset_main) {
|
||||
weak_ref_main.clear_users(global_main);
|
||||
get_weak_reference_mains().remove(index);
|
||||
return;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
void BKE_asset_weak_reference_main_free_all()
|
||||
{
|
||||
get_weak_reference_mains().clear_and_shrink();
|
||||
}
|
||||
|
||||
ID *BKE_asset_weak_reference_ensure(Main &global_main,
|
||||
const ID_Type id_type,
|
||||
const AssetWeakReference &weak_ref)
|
||||
{
|
||||
char asset_full_path_buffer[FILE_MAX_LIBEXTRA];
|
||||
char *asset_lib_path, *asset_group, *asset_name;
|
||||
|
||||
AS_asset_full_path_explode_from_weak_ref(
|
||||
&weak_ref, asset_full_path_buffer, &asset_lib_path, &asset_group, &asset_name);
|
||||
|
||||
if (asset_lib_path == nullptr && asset_group == nullptr && asset_name == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLI_assert(asset_name != nullptr);
|
||||
|
||||
/* If weak reference resolves to a null library path, assume we are in local asset case. */
|
||||
Main &bmain = asset_lib_path ? asset_weak_reference_main_ensure(asset_lib_path) : global_main;
|
||||
|
||||
/* Check if we have the asset already, or if it's global main and there is nothing we can add. */
|
||||
ID *local_asset = BKE_libblock_find_name(&bmain, id_type, asset_name);
|
||||
if (local_asset || asset_lib_path == nullptr) {
|
||||
BLI_assert(local_asset == nullptr || ID_IS_ASSET(local_asset));
|
||||
return local_asset;
|
||||
}
|
||||
|
||||
/* Load asset from asset library. */
|
||||
LibraryLink_Params lapp_params{};
|
||||
lapp_params.bmain = &bmain;
|
||||
BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new(&lapp_params);
|
||||
BKE_blendfile_link_append_context_flag_set(lapp_context, BLO_LIBLINK_FORCE_INDIRECT, true);
|
||||
BKE_blendfile_link_append_context_flag_set(lapp_context, 0, true);
|
||||
|
||||
BKE_blendfile_link_append_context_library_add(lapp_context, asset_lib_path, nullptr);
|
||||
|
||||
BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add(
|
||||
lapp_context, asset_name, id_type, nullptr);
|
||||
BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0);
|
||||
|
||||
BKE_blendfile_link(lapp_context, nullptr);
|
||||
BKE_blendfile_append(lapp_context, nullptr);
|
||||
|
||||
local_asset = BKE_blendfile_link_append_context_item_newid_get(lapp_context, lapp_item);
|
||||
|
||||
BKE_blendfile_link_append_context_free(lapp_context);
|
||||
|
||||
BKE_main_id_tag_all(&bmain, LIB_TAG_ASSET_MAIN, true);
|
||||
|
||||
/* Verify that the name matches. It must for referencing the same asset again to work. */
|
||||
BLI_assert(local_asset == nullptr || STREQ(local_asset->name + 2, asset_name));
|
||||
|
||||
return local_asset;
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "IMB_moviecache.hh"
|
||||
|
||||
#include "BKE_addon.h"
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_asset_edit.hh"
|
||||
#include "BKE_blender.hh" /* own include */
|
||||
#include "BKE_blender_user_menu.hh" /* own include */
|
||||
#include "BKE_blender_version.h" /* own include */
|
||||
|
@ -64,7 +64,7 @@ void BKE_blender_free()
|
|||
/* Free separate data-bases after #BKE_blender_globals_clear in case any data-blocks in the
|
||||
* global main use pointers to asset main data-blocks when they're freed. That generally
|
||||
* shouldn't happen but it's better to be safe. */
|
||||
BKE_asset_weak_reference_main_free_all();
|
||||
blender::bke::asset_edit_main_free_all();
|
||||
|
||||
if (G.log.file != nullptr) {
|
||||
fclose(static_cast<FILE *>(G.log.file));
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
#include "DNA_ID.h"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_asset_edit.hh"
|
||||
#include "BKE_bpath.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_idtype.hh"
|
||||
|
@ -972,7 +972,7 @@ Main *BKE_main_from_id(Main *global_main, const ID *id, const bool verify)
|
|||
return nullptr;
|
||||
}
|
||||
if (id->tag & LIB_TAG_ASSET_MAIN) {
|
||||
return BKE_asset_weak_reference_main(id);
|
||||
return blender::bke::asset_edit_main(id);
|
||||
}
|
||||
|
||||
if (verify) {
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "BLT_translation.hh"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_asset_edit.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_brush.hh"
|
||||
#include "BKE_ccg.h"
|
||||
|
@ -620,8 +621,8 @@ static bool paint_brush_set_from_asset_reference(Main *bmain, Paint *paint)
|
|||
return false;
|
||||
}
|
||||
|
||||
Brush *brush = reinterpret_cast<Brush *>(
|
||||
BKE_asset_weak_reference_ensure(*bmain, ID_BR, *paint->brush_asset_reference));
|
||||
Brush *brush = reinterpret_cast<Brush *>(blender::bke::asset_edit_id_from_weak_reference(
|
||||
*bmain, ID_BR, *paint->brush_asset_reference));
|
||||
BLI_assert(brush == nullptr || (brush->id.tag & LIB_TAG_ASSET_MAIN));
|
||||
|
||||
/* Ensure we have a brush with appropriate mode to assign.
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "BLO_writefile.hh"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_asset_edit.hh"
|
||||
#include "BKE_blendfile.hh"
|
||||
#include "BKE_brush.hh"
|
||||
#include "BKE_context.hh"
|
||||
|
@ -739,7 +740,7 @@ static int brush_asset_select_exec(bContext *C, wmOperator *op)
|
|||
|
||||
AssetWeakReference brush_asset_reference = asset->make_weak_reference();
|
||||
Brush *brush = reinterpret_cast<Brush *>(
|
||||
BKE_asset_weak_reference_ensure(*bmain, ID_BR, brush_asset_reference));
|
||||
blender::bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, brush_asset_reference));
|
||||
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
|
||||
|
@ -771,7 +772,7 @@ static void BRUSH_OT_asset_select(wmOperatorType *ot)
|
|||
* This needs to be properly implemented in assetlib code.
|
||||
*/
|
||||
static AssetWeakReference brush_asset_create_weakref_hack(const bUserAssetLibrary *user_asset_lib,
|
||||
std::string &file_path)
|
||||
const std::string &file_path)
|
||||
{
|
||||
AssetWeakReference asset_weak_ref{};
|
||||
|
||||
|
@ -819,127 +820,6 @@ static void refresh_asset_library(const bContext *C, const bUserAssetLibrary &us
|
|||
}
|
||||
}
|
||||
|
||||
static std::string brush_asset_root_path_for_save(const bUserAssetLibrary &user_library)
|
||||
{
|
||||
if (user_library.dirpath[0] == '\0') {
|
||||
return "";
|
||||
}
|
||||
|
||||
char libpath[FILE_MAX];
|
||||
BLI_strncpy(libpath, user_library.dirpath, sizeof(libpath));
|
||||
BLI_path_slash_native(libpath);
|
||||
BLI_path_normalize(libpath);
|
||||
|
||||
return std::string(libpath) + SEP + "Saved" + SEP + "Brushes";
|
||||
}
|
||||
|
||||
static std::string brush_asset_blendfile_path_for_save(ReportList *reports,
|
||||
const bUserAssetLibrary &user_library,
|
||||
const StringRefNull base_name)
|
||||
{
|
||||
std::string root_path = brush_asset_root_path_for_save(user_library);
|
||||
BLI_assert(!root_path.empty());
|
||||
|
||||
if (!BLI_dir_create_recursive(root_path.c_str())) {
|
||||
BKE_report(reports, RPT_ERROR, "Failed to create asset library directory to save brush");
|
||||
return "";
|
||||
}
|
||||
|
||||
char base_name_filesafe[FILE_MAXFILE];
|
||||
BLI_strncpy(base_name_filesafe, base_name.c_str(), sizeof(base_name_filesafe));
|
||||
BLI_path_make_safe_filename(base_name_filesafe);
|
||||
|
||||
if (!BLI_is_file((root_path + SEP + base_name_filesafe + BLENDER_ASSET_FILE_SUFFIX).c_str())) {
|
||||
return root_path + SEP + base_name_filesafe + BLENDER_ASSET_FILE_SUFFIX;
|
||||
}
|
||||
int i = 1;
|
||||
while (BLI_is_file((root_path + SEP + base_name_filesafe + "_" + std::to_string(i++) +
|
||||
BLENDER_ASSET_FILE_SUFFIX)
|
||||
.c_str()))
|
||||
;
|
||||
return root_path + SEP + base_name_filesafe + "_" + std::to_string(i - 1) +
|
||||
BLENDER_ASSET_FILE_SUFFIX;
|
||||
}
|
||||
|
||||
static bool brush_asset_write_in_library(Main *bmain,
|
||||
Brush *brush,
|
||||
const char *name,
|
||||
const StringRefNull filepath,
|
||||
const std::optional<asset_system::CatalogID> catalog,
|
||||
const std::optional<StringRefNull> catalog_simple_name,
|
||||
std::string &final_full_file_path,
|
||||
ReportList *reports)
|
||||
{
|
||||
/* XXX
|
||||
* FIXME
|
||||
*
|
||||
* This code is _pure evil_. It does in-place manipulation on IDs in global Main database,
|
||||
* temporarilly remove them and add them back...
|
||||
*
|
||||
* Use it as-is for now (in a similar way as python API or copy-to-buffer works). Nut the whole
|
||||
* 'BKE_blendfile_write_partial' code needs to be completely refactored.
|
||||
*
|
||||
* Ideas:
|
||||
* - Have `BKE_blendfile_write_partial_begin` return a new temp Main.
|
||||
* - Replace `BKE_blendfile_write_partial_tag_ID` by API to add IDs to this temp Main.
|
||||
* + This should _duplicate_ the ID, not remove the original one from the source Main!
|
||||
* - Have API to automatically also duplicate dependencies into temp Main.
|
||||
* + Have options to e.g. make all duplicated IDs 'local' (i.e. remove their library data).
|
||||
* - `BKE_blendfile_write_partial` then simply write the given temp main.
|
||||
* - `BKE_blendfile_write_partial_end` frees the temp Main.
|
||||
*/
|
||||
|
||||
const short brush_flag = brush->id.flag;
|
||||
const int brush_tag = brush->id.tag;
|
||||
const int brush_us = brush->id.us;
|
||||
const std::string brush_name = brush->id.name + 2;
|
||||
IDOverrideLibrary *brush_liboverride = brush->id.override_library;
|
||||
AssetMetaData *brush_asset_data = brush->id.asset_data;
|
||||
const int write_flags = 0; /* Could use #G_FILE_COMPRESS ? */
|
||||
const eBLO_WritePathRemap remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE;
|
||||
|
||||
BKE_blendfile_write_partial_begin(bmain);
|
||||
|
||||
brush->id.flag |= LIB_FAKEUSER;
|
||||
brush->id.tag &= ~LIB_TAG_RUNTIME;
|
||||
brush->id.us = 1;
|
||||
BLI_strncpy(brush->id.name + 2, name, sizeof(brush->id.name) - 2);
|
||||
if (!ID_IS_ASSET(&brush->id)) {
|
||||
brush->id.asset_data = brush->id.override_library->reference->asset_data;
|
||||
}
|
||||
brush->id.override_library = nullptr;
|
||||
|
||||
if (catalog) {
|
||||
brush->id.asset_data->catalog_id = *catalog;
|
||||
}
|
||||
if (catalog_simple_name) {
|
||||
STRNCPY(brush->id.asset_data->catalog_simple_name, catalog_simple_name->c_str());
|
||||
}
|
||||
|
||||
BKE_blendfile_write_partial_tag_ID(&brush->id, true);
|
||||
|
||||
/* TODO: check overwriting existing file. */
|
||||
/* TODO: ensure filepath contains only valid characters for file system. */
|
||||
const bool sucess = BKE_blendfile_write_partial(
|
||||
bmain, filepath.c_str(), write_flags, remap_mode, reports);
|
||||
|
||||
if (sucess) {
|
||||
final_full_file_path = std::string(filepath) + SEP + "Brush" + SEP + name;
|
||||
}
|
||||
|
||||
BKE_blendfile_write_partial_end(bmain);
|
||||
|
||||
BKE_blendfile_write_partial_tag_ID(&brush->id, false);
|
||||
brush->id.flag = brush_flag;
|
||||
brush->id.tag = brush_tag;
|
||||
brush->id.us = brush_us;
|
||||
BLI_strncpy(brush->id.name + 2, brush_name.c_str(), sizeof(brush->id.name) - 2);
|
||||
brush->id.override_library = brush_liboverride;
|
||||
brush->id.asset_data = brush_asset_data;
|
||||
|
||||
return sucess;
|
||||
}
|
||||
|
||||
static bool brush_asset_save_as_poll(bContext *C)
|
||||
{
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
|
@ -956,38 +836,6 @@ static bool brush_asset_save_as_poll(bContext *C)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool asset_is_editable(const AssetWeakReference &asset_weak_ref)
|
||||
{
|
||||
/* Fairly simple checks, based on filepath only:
|
||||
* - The blendlib filepath ends up with the `.asset.blend` extension.
|
||||
* - The blendlib is located in the expected sub-directory of the editable asset library.
|
||||
*
|
||||
* TODO: Right now no check is done on file content, e.g. to ensure that the blendlib file has
|
||||
* not been manually edited by the user (that it does not have any UI IDs e.g.). */
|
||||
|
||||
char path_buffer[FILE_MAX_LIBEXTRA];
|
||||
char *dir, *group, *name;
|
||||
AS_asset_full_path_explode_from_weak_ref(&asset_weak_ref, path_buffer, &dir, &group, &name);
|
||||
|
||||
if (!StringRef(dir).endswith(BLENDER_ASSET_FILE_SUFFIX)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bUserAssetLibrary *library = BKE_preferences_asset_library_find_by_name(
|
||||
&U, asset_weak_ref.asset_library_identifier);
|
||||
if (!library) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string root_path_for_save = brush_asset_root_path_for_save(*library);
|
||||
if (root_path_for_save.empty() || !StringRef(dir).startswith(root_path_for_save)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* TODO: Do we want more checks here? E.g. check actual content of the file? */
|
||||
return true;
|
||||
}
|
||||
|
||||
static const bUserAssetLibrary *get_asset_library_from_prop(PointerRNA &ptr)
|
||||
{
|
||||
const int enum_value = RNA_enum_get(&ptr, "asset_library_reference");
|
||||
|
@ -1048,18 +896,6 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op)
|
|||
if (!user_library) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
const std::string filepath = brush_asset_blendfile_path_for_save(
|
||||
op->reports, *user_library, name);
|
||||
if (filepath.empty()) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Turn brush into asset if it isn't yet. */
|
||||
if (!ID_IS_ASSET(brush)) {
|
||||
asset::mark_id(&brush->id);
|
||||
asset::generate_preview(C, &brush->id);
|
||||
}
|
||||
BLI_assert(ID_IS_ASSET(brush));
|
||||
|
||||
asset_system::AssetLibrary *library = AS_asset_library_load(
|
||||
CTX_data_main(C), user_library_to_library_ref(*user_library));
|
||||
|
@ -1068,6 +904,13 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Turn brush into asset if it isn't yet. */
|
||||
if (!ID_IS_ASSET(&brush->id)) {
|
||||
asset::mark_id(&brush->id);
|
||||
asset::generate_preview(C, &brush->id);
|
||||
}
|
||||
BLI_assert(ID_IS_ASSET(&brush->id));
|
||||
|
||||
/* Add asset to catalog. */
|
||||
char catalog_path[MAX_NAME];
|
||||
RNA_string_get(op->ptr, "catalog_path", catalog_path);
|
||||
|
@ -1082,33 +925,20 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op)
|
|||
catalog_simple_name = catalog.simple_name;
|
||||
}
|
||||
|
||||
library->catalog_service().write_to_disk(filepath);
|
||||
const std::optional<std::string> final_full_asset_filepath = blender::bke::asset_edit_id_save_as(
|
||||
*bmain, &brush->id, name, catalog_id, catalog_simple_name, user_library, op->reports);
|
||||
|
||||
/* Save to asset library. */
|
||||
Main *asset_main = BKE_main_from_id(bmain, &brush->id);
|
||||
|
||||
std::string final_full_asset_filepath;
|
||||
const bool success = brush_asset_write_in_library(asset_main,
|
||||
brush,
|
||||
name,
|
||||
filepath,
|
||||
catalog_id,
|
||||
catalog_simple_name,
|
||||
final_full_asset_filepath,
|
||||
op->reports);
|
||||
|
||||
if (!success) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Failed to write to asset library");
|
||||
if (!final_full_asset_filepath) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
AssetWeakReference new_brush_weak_ref = brush_asset_create_weakref_hack(
|
||||
user_library, final_full_asset_filepath);
|
||||
library->catalog_service().write_to_disk(*final_full_asset_filepath);
|
||||
|
||||
BKE_reportf(op->reports, RPT_INFO, "Saved \"%s\"", filepath.c_str());
|
||||
AssetWeakReference new_brush_weak_ref = brush_asset_create_weakref_hack(
|
||||
user_library, *final_full_asset_filepath);
|
||||
|
||||
brush = reinterpret_cast<Brush *>(
|
||||
BKE_asset_weak_reference_ensure(*bmain, ID_BR, new_brush_weak_ref));
|
||||
blender::bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, new_brush_weak_ref));
|
||||
|
||||
if (!BKE_paint_brush_asset_set(paint, brush, new_brush_weak_ref)) {
|
||||
/* Note brush sset was still saved in editable asset library, so was not a no-op. */
|
||||
|
@ -1219,7 +1049,7 @@ static bool brush_asset_delete_poll(bContext *C)
|
|||
|
||||
/* Asset brush, check if belongs to an editable blend file. */
|
||||
if (paint->brush_asset_reference && ID_IS_ASSET(brush)) {
|
||||
if (!asset_is_editable(*paint->brush_asset_reference)) {
|
||||
if (!blender::bke::asset_edit_id_is_editable(&brush->id)) {
|
||||
CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable");
|
||||
return false;
|
||||
}
|
||||
|
@ -1233,7 +1063,6 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op)
|
|||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Main *asset_main = BKE_main_from_id(bmain, &brush->id);
|
||||
|
||||
bUserAssetLibrary *library = BKE_preferences_asset_library_find_by_name(
|
||||
&U, paint->brush_asset_reference->asset_library_identifier);
|
||||
|
@ -1241,21 +1070,7 @@ static int brush_asset_delete_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (paint->brush_asset_reference && ID_IS_ASSET(brush)) {
|
||||
/* Delete from asset library on disk. */
|
||||
char path_buffer[FILE_MAX_LIBEXTRA];
|
||||
char *filepath;
|
||||
AS_asset_full_path_explode_from_weak_ref(
|
||||
paint->brush_asset_reference, path_buffer, &filepath, nullptr, nullptr);
|
||||
|
||||
if (BLI_delete(filepath, false, false) != 0) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Failed to delete asset library file");
|
||||
}
|
||||
}
|
||||
|
||||
if (asset_main != bmain) {
|
||||
BKE_asset_weak_reference_main_free(*bmain, *asset_main);
|
||||
}
|
||||
blender::bke::asset_edit_id_delete(*bmain, &brush->id, op->reports);
|
||||
|
||||
refresh_asset_library(C, *library);
|
||||
|
||||
|
@ -1306,7 +1121,7 @@ static bool brush_asset_update_poll(bContext *C)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!asset_is_editable(*paint->brush_asset_reference)) {
|
||||
if (!blender::bke::asset_edit_id_is_editable(&brush->id)) {
|
||||
CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable");
|
||||
return false;
|
||||
}
|
||||
|
@ -1327,23 +1142,9 @@ static int brush_asset_update_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
char path_buffer[FILE_MAX_LIBEXTRA];
|
||||
char *filepath;
|
||||
AS_asset_full_path_explode_from_weak_ref(
|
||||
asset_weak_ref, path_buffer, &filepath, nullptr, nullptr);
|
||||
|
||||
BLI_assert(ID_IS_ASSET(brush));
|
||||
|
||||
Main *asset_main = BKE_main_from_id(bmain, &brush->id);
|
||||
std::string final_full_asset_filepath;
|
||||
brush_asset_write_in_library(asset_main,
|
||||
brush,
|
||||
brush->id.name + 2,
|
||||
filepath,
|
||||
std::nullopt,
|
||||
std::nullopt,
|
||||
final_full_asset_filepath,
|
||||
op->reports);
|
||||
blender::bke::asset_edit_id_save(*bmain, &brush->id, op->reports);
|
||||
|
||||
refresh_asset_library(C, *user_library);
|
||||
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr);
|
||||
|
@ -1373,16 +1174,13 @@ static bool brush_asset_revert_poll(bContext *C)
|
|||
return paint->brush_asset_reference && (brush->id.tag & LIB_TAG_ASSET_MAIN);
|
||||
}
|
||||
|
||||
static int brush_asset_revert_exec(bContext *C, wmOperator * /*op*/)
|
||||
static int brush_asset_revert_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
Main *asset_main = BKE_main_from_id(bmain, &brush->id);
|
||||
|
||||
/* Reload entire main, including texture dependencies. This relies on there
|
||||
* being only a single brush asset per blend file. */
|
||||
BKE_asset_weak_reference_main_reload(*bmain, *asset_main);
|
||||
blender::bke::asset_edit_id_revert(*bmain, &brush->id, op->reports);
|
||||
|
||||
WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr);
|
||||
WM_main_add_notifier(NC_TEXTURE | ND_NODES, nullptr);
|
||||
|
|
Loading…
Reference in New Issue