Refactor: Move asset editing code into own file and refactor #119888
|
@ -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"
|
||||
|
||||
brecht marked this conversation as resolved
|
||||
struct bUserAssetLibrary;
|
||||
struct AssetWeakReference;
|
||||
struct ID;
|
||||
struct Main;
|
||||
struct ReportList;
|
||||
|
||||
namespace blender::bke {
|
||||
brecht marked this conversation as resolved
Outdated
Julian Eisel
commented
Would make these doxygen comments. Would make these doxygen comments.
|
||||
|
||||
/** 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,
|
||||
brecht marked this conversation as resolved
Hans Goudey
commented
Not sure Not sure `const` is meaningful here, since it's passed by value
|
||||
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"
|
||||
|
||||
brecht marked this conversation as resolved
Hans Goudey
commented
I'd rather put all the new code in the Could also be done separately from this commit though I'd rather put all the new code in the `blender::bke` namespace (replacing the `BKE_` prefix on the functions. It could even be a nested namespace like `blender::bke::asset_edit`, though I don't feel strongly about that.
Could also be done separately from this commit though
|
||||
namespace blender::bke {
|
||||
|
||||
/**
|
||||
* Asset library blend file, with editable contents.
|
||||
*/
|
||||
brecht marked this conversation as resolved
Hans Goudey
commented
Unnecessary newline Unnecessary newline
|
||||
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;
|
||||
brecht marked this conversation as resolved
Outdated
Julian Eisel
commented
The guidelines are to always use braces. To make this more readable, I'd suggest this:
Also, I'd hope that the compiler optimizes this, but this could potentially allocate multiple strings for each iteration, so maybe better to create some The guidelines are to [always use braces](https://developer.blender.org/docs/handbook/guidelines/c_cpp/#always-use-braces). To make this more readable, I'd suggest this:
```c
int i = 1;
while (...) {
i++;
}
```
Also, I'd hope that the compiler optimizes this, but this could potentially allocate multiple strings for each iteration, so maybe better to create some `const` base path string.
|
||||
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;
|
||||
brecht marked this conversation as resolved
Hans Goudey
commented
`/* global_main */` -> `/*global_main*/`
|
||||
}
|
||||
|
||||
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
Don't think this is needed?