GPv3: Python API for frame, drawing and drawing attributes #124787

Merged
Falk David merged 22 commits from filedescriptor/blender:gpv3-drawing-python-api into main 2024-07-26 16:30:21 +02:00
110 changed files with 2221 additions and 3981 deletions
Showing only changes of commit 35f0102c36 - Show all commits

View File

@ -37,7 +37,7 @@ ccl_device void svm_vector_math(ccl_private float *value,
*vector = reflect(a, b);
break;
case NODE_VECTOR_MATH_REFRACT:
*vector = refract(a, normalize(b), param1);
*vector = refract(a, safe_normalize(b), param1);
break;
case NODE_VECTOR_MATH_FACEFORWARD:
*vector = faceforward(a, b, c);

@ -1 +1 @@
Subproject commit 4d2e4b4ba301e0a911d94dca83b99b88e5c579fe
Subproject commit 53ee259cea0132e8dc141f19d543b0a934ade9ac

@ -1 +1 @@
Subproject commit 181d15c1c640d826c593b54ce097894a1710977e
Subproject commit a0b9f2878577e4d6922e469eaef5e268ffa1f29f

View File

@ -245,15 +245,17 @@ class DOPESHEET_HT_editor_buttons:
layout.template_ID(st, "action", new="action.new", unlink="action.unlink")
# context.space_data.action comes from the active object.
adt = context.object and context.object.animation_data
if adt and st.action and st.action.is_action_layered:
layout.template_search(
adt, "action_slot",
adt, "action_slots",
new="",
unlink="anim.slot_unassign_object",
)
# Show slot selector.
if context.preferences.experimental.use_animation_baklava:
# context.space_data.action comes from the active object.
adt = context.object and context.object.animation_data
if adt and st.action and st.action.is_action_layered:
layout.template_search(
adt, "action_slot",
adt, "action_slots",
new="",
unlink="anim.slot_unassign_object",
)
# Layer management
if st.mode == 'GPENCIL':

View File

@ -35,6 +35,7 @@ from bl_ui.space_toolsystem_common import (
from bpy.app.translations import (
contexts as i18n_contexts,
pgettext_iface as iface_,
)
@ -791,10 +792,13 @@ class IMAGE_HT_header(Header):
tool_settings = context.tool_settings
# Snap.
snap_uv_element = tool_settings.snap_uv_element
try:
act_snap_icon = tool_settings.bl_rna.properties["snap_uv_element"].enum_items[snap_uv_element].icon
except KeyError:
snap_uv_elements = tool_settings.snap_uv_element
if len(snap_uv_elements) == 1:
text = ""
elem = next(iter(snap_uv_elements))
act_snap_icon = tool_settings.bl_rna.properties["snap_uv_element"].enum_items[elem].icon
else:
text = iface_("Mix", i18n_contexts.id_image)
act_snap_icon = 'NONE'
row = layout.row(align=True)
@ -804,7 +808,7 @@ class IMAGE_HT_header(Header):
sub.popover(
panel="IMAGE_PT_snapping",
icon=act_snap_icon,
text="",
text=text,
)
# Proportional Editing
@ -1016,10 +1020,10 @@ class IMAGE_PT_snapping(Panel):
col.label(text="Snap Target")
col.prop(tool_settings, "snap_uv_element", expand=True)
if tool_settings.snap_uv_element != 'INCREMENT':
col.label(text="Snap Base")
row = col.row(align=True)
row.prop(tool_settings, "snap_target", expand=True)
col.label(text="Snap Base")
row = col.row(align=True)
row.active = bool(tool_settings.snap_uv_element.difference({'INCREMENT', 'GRID'}))
row.prop(tool_settings, "snap_target", expand=True)
col.separator()

View File

@ -6022,6 +6022,10 @@ class VIEW3D_MT_edit_greasepencil_animation(Menu):
layout.operator("grease_pencil.insert_blank_frame", text="Insert Blank Keyframe (Active Layer)")
layout.operator("grease_pencil.insert_blank_frame", text="Insert Blank Keyframe (All Layers)").all_layers = True
layout.separator()
layout.operator("grease_pencil.frame_duplicate", text="Duplicate Active Keyframe (Active Layer)").all = False
layout.operator("grease_pencil.frame_duplicate", text="Duplicate Active Keyframe (All Layer)").all = True
class VIEW3D_MT_edit_gpencil_transform(Menu):
bl_label = "Transform"

View File

@ -9,15 +9,145 @@
*/
#pragma once
#include "BLI_map.hh"
#include "BLI_string_ref.hh"
#include "DNA_anim_types.h"
#include "RNA_access.hh"
#include "ANIM_action.hh"
struct AnimationEvalContext;
struct PointerRNA;
namespace blender::animrig {
/* Identifies the property that an evaluated animation value is for.
*
* This could be replaced with either `FCurveIdentifier` or `RNAPath`. However,
* `FCurveIdentifier` is semantically meant to represent an fcurve itself rather
* than the property an fcurve might be for, and moreover not all animation will
* necessarily come from fcurves in the future anyway. `RNAPath` would be more
* semantically appropriate, but it stores a full copy of the string component
* of the path, and here we want to be lighter than that and use a string
* reference.
*/
class PropIdentifier {
public:
/**
* Reference to the RNA path of the property.
*
* This string is typically owned by the FCurve that animates the property.
*/
StringRefNull rna_path;
int array_index;
PropIdentifier() = default;
PropIdentifier(const StringRefNull rna_path, const int array_index)
: rna_path(rna_path), array_index(array_index)
{
}
bool operator==(const PropIdentifier &other) const
{
return rna_path == other.rna_path && array_index == other.array_index;
}
bool operator!=(const PropIdentifier &other) const
{
return !(*this == other);
}
uint64_t hash() const
{
return get_default_hash(rna_path, array_index);
}
};
/**
* The evaluated value for an animated property, along with its RNA pointer.
*/
class AnimatedProperty {
public:
float value;
PathResolvedRNA prop_rna;
AnimatedProperty(const float value, const PathResolvedRNA &prop_rna)
: value(value), prop_rna(prop_rna)
{
}
};
/* Result of FCurve evaluation for an action slot.
* Mapping from property identifier to its float value.
*
* Can be fed to the evaluation of the next layer, mixed with another strip, or
* used to modify actual RNA properties.
*
* TODO: see if this is efficient, and contains enough info, for mixing. For now
* this just captures the FCurve evaluation result, but doesn't have any info
* about how to do the mixing (LERP, quaternion SLERP, etc.).
*/
class EvaluationResult {
protected:
using EvaluationMap = Map<PropIdentifier, AnimatedProperty>;
EvaluationMap result_;
public:
EvaluationResult() = default;
EvaluationResult(const EvaluationResult &other) = default;
~EvaluationResult() = default;
public:
operator bool() const
{
return !this->is_empty();
}
bool is_empty() const
{
return result_.is_empty();
}
void store(const StringRefNull rna_path,
const int array_index,
const float value,
const PathResolvedRNA &prop_rna)
{
PropIdentifier key(rna_path, array_index);
AnimatedProperty anim_prop(value, prop_rna);
result_.add_overwrite(key, anim_prop);
}
AnimatedProperty value(const StringRefNull rna_path, const int array_index) const
{
PropIdentifier key(rna_path, array_index);
return result_.lookup(key);
}
const AnimatedProperty *lookup_ptr(const PropIdentifier &key) const
{
return result_.lookup_ptr(key);
}
AnimatedProperty *lookup_ptr(const PropIdentifier &key)
{
return result_.lookup_ptr(key);
}
EvaluationMap::ItemIterator items() const
{
return result_.items();
}
};
/**
* Evaluate the given action for the given slot and animated ID.
*
* This does *not* apply the resulting values to the ID. Instead, it returns
* the resulting values in an `EvaluationResult`.
*/
EvaluationResult evaluate_action(PointerRNA &animated_id_ptr,
Action &action,
slot_handle_t slot_handle,
const AnimationEvalContext &anim_eval_context);
/**
* Top level animation evaluation function.
*

View File

@ -189,31 +189,16 @@ bool insert_keyframe_direct(ReportList *reports,
* Will perform checks just in case.
* \return The number of key-frames deleted.
*/
int delete_keyframe(Main *bmain,
ReportList *reports,
ID *id,
bAction *act,
const char rna_path[],
int array_index,
float cfra);
int delete_keyframe(Main *bmain, ReportList *reports, ID *id, const RNAPath &path, float cfra);
/**
* Main Keyframing API call:
* Use this when validation of necessary animation data isn't necessary as it
* already exists. It will clear the current buttons fcurve(s).
*
* The flag argument is used for special settings that alter the behavior of
* the keyframe deletion. These include the quick refresh options.
*
* \return The number of f-curves removed.
*/
int clear_keyframe(Main *bmain,
ReportList *reports,
ID *id,
bAction *act,
const char rna_path[],
int array_index,
eInsertKeyFlags /*flag*/);
int clear_keyframe(Main *bmain, ReportList *reports, ID *id, const RNAPath &rna_path);
/** Check if a flag is set for keyframing (per scene takes precedence). */
bool is_keying_flag(const Scene *scene, eKeying_Flag flag);

View File

@ -64,6 +64,9 @@ set(LIB
PRIVATE bf::intern::clog
)
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_ANIM_BAKLAVA)
endif()
blender_add_lib(bf_animrig "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
add_library(bf::animrig ALIAS bf_animrig)

View File

@ -35,10 +35,10 @@ void apply_evaluation_result(const EvaluationResult &evaluation_result,
PointerRNA &animated_id_ptr,
bool flush_to_original);
static EvaluationResult evaluate_action(PointerRNA &animated_id_ptr,
Action &action,
const slot_handle_t slot_handle,
const AnimationEvalContext &anim_eval_context)
EvaluationResult evaluate_action(PointerRNA &animated_id_ptr,
Action &action,
const slot_handle_t slot_handle,
const AnimationEvalContext &anim_eval_context)
{
EvaluationResult last_result;

View File

@ -4,117 +4,8 @@
#pragma once
#include "BLI_map.hh"
#include "BLI_string_ref.hh"
#include "RNA_access.hh"
namespace blender::animrig::internal {
class PropIdentifier {
public:
/**
* Reference to the RNA path of the property.
*
* This string is typically owned by the FCurve that animates the property.
*/
StringRefNull rna_path;
int array_index;
PropIdentifier() = default;
PropIdentifier(const StringRefNull rna_path, const int array_index)
: rna_path(rna_path), array_index(array_index)
{
}
bool operator==(const PropIdentifier &other) const
{
return rna_path == other.rna_path && array_index == other.array_index;
}
bool operator!=(const PropIdentifier &other) const
{
return !(*this == other);
}
uint64_t hash() const
{
return get_default_hash(rna_path, array_index);
}
};
class AnimatedProperty {
public:
float value;
PathResolvedRNA prop_rna;
AnimatedProperty(const float value, const PathResolvedRNA &prop_rna)
: value(value), prop_rna(prop_rna)
{
}
};
/* Evaluated FCurves for some action slot.
* Mapping from property identifier to its float value.
*
* Can be fed to the evaluation of the next layer, mixed with another strip, or
* used to modify actual RNA properties.
*
* TODO: see if this is efficient, and contains enough info, for mixing. For now
* this just captures the FCurve evaluation result, but doesn't have any info
* about how to do the mixing (LERP, quaternion SLERP, etc.).
*/
class EvaluationResult {
protected:
using EvaluationMap = Map<PropIdentifier, AnimatedProperty>;
EvaluationMap result_;
public:
EvaluationResult() = default;
EvaluationResult(const EvaluationResult &other) = default;
~EvaluationResult() = default;
public:
operator bool() const
{
return !this->is_empty();
}
bool is_empty() const
{
return result_.is_empty();
}
void store(const StringRefNull rna_path,
const int array_index,
const float value,
const PathResolvedRNA &prop_rna)
{
PropIdentifier key(rna_path, array_index);
AnimatedProperty anim_prop(value, prop_rna);
result_.add_overwrite(key, anim_prop);
}
AnimatedProperty value(const StringRefNull rna_path, const int array_index) const
{
PropIdentifier key(rna_path, array_index);
return result_.lookup(key);
}
const AnimatedProperty *lookup_ptr(const PropIdentifier &key) const
{
return result_.lookup_ptr(key);
}
AnimatedProperty *lookup_ptr(const PropIdentifier &key)
{
return result_.lookup_ptr(key);
}
EvaluationMap::ItemIterator items() const
{
return result_.items();
}
};
/**
* Evaluate the animation data on the given layer, for the given slot. This
* just returns the evaluation result, without taking any other layers,

View File

@ -627,15 +627,8 @@ static void deg_tag_after_keyframe_delete(Main *bmain, ID *id, AnimData *adt)
}
}
int delete_keyframe(Main *bmain,
ReportList *reports,
ID *id,
bAction *act,
const char rna_path[],
int array_index,
float cfra)
int delete_keyframe(Main *bmain, ReportList *reports, ID *id, const RNAPath &rna_path, float cfra)
{
BLI_assert(rna_path != nullptr);
AnimData *adt = BKE_animdata_from_id(id);
if (ELEM(nullptr, id, adt)) {
@ -646,33 +639,27 @@ int delete_keyframe(Main *bmain,
PointerRNA ptr;
PropertyRNA *prop;
PointerRNA id_ptr = RNA_id_pointer_create(id);
if (RNA_path_resolve_property(&id_ptr, rna_path, &ptr, &prop) == false) {
if (RNA_path_resolve_property(&id_ptr, rna_path.path.c_str(), &ptr, &prop) == false) {
BKE_reportf(
reports,
RPT_ERROR,
"Could not delete keyframe, as RNA path is invalid for the given ID (ID = %s, path = %s)",
id->name,
rna_path);
rna_path.path.c_str());
return 0;
}
if (act == nullptr) {
if (adt->action) {
act = adt->action;
cfra = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
}
else {
BKE_reportf(reports, RPT_ERROR, "No action to delete keyframes from for ID = %s", id->name);
return 0;
}
if (!adt->action) {
BKE_reportf(reports, RPT_ERROR, "No action to delete keyframes from for ID = %s", id->name);
return 0;
}
bAction *act = adt->action;
cfra = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
int array_index = rna_path.index.value_or(0);
int array_index_max = array_index + 1;
if (array_index == -1) {
array_index = 0;
if (!rna_path.index.has_value()) {
array_index_max = RNA_property_array_length(&ptr, prop);
/* For single properties, increase max_index so that the property itself gets included,
* but don't do this for standard arrays since that can cause corruption issues
* (extra unused curves).
@ -695,7 +682,7 @@ int delete_keyframe(Main *bmain,
* paths. In the future when legacy actions are removed, we can restructure
* it to be clearer. */
for (; array_index < array_index_max; array_index++) {
FCurve *fcurve = fcurve_find(fcurves, {rna_path, array_index});
FCurve *fcurve = fcurve_find(fcurves, {rna_path.path, array_index});
if (fcurve == nullptr) {
continue;
}
@ -708,7 +695,7 @@ int delete_keyframe(Main *bmain,
/* Will only loop once unless the array index was -1. */
int key_count = 0;
for (; array_index < array_index_max; array_index++) {
FCurve *fcu = action_fcurve_find(act, {rna_path, array_index});
FCurve *fcu = action_fcurve_find(act, {rna_path.path, array_index});
if (fcu == nullptr) {
continue;
@ -736,16 +723,8 @@ int delete_keyframe(Main *bmain,
/* ************************************************** */
/* KEYFRAME CLEAR */
int clear_keyframe(Main *bmain,
ReportList *reports,
ID *id,
bAction *act,
const char rna_path[],
int array_index,
eInsertKeyFlags /*flag*/)
int clear_keyframe(Main *bmain, ReportList *reports, ID *id, const RNAPath &rna_path)
{
BLI_assert(rna_path != nullptr);
AnimData *adt = BKE_animdata_from_id(id);
if (ELEM(nullptr, id, adt)) {
@ -756,29 +735,25 @@ int clear_keyframe(Main *bmain,
PointerRNA ptr;
PropertyRNA *prop;
PointerRNA id_ptr = RNA_id_pointer_create(id);
if (RNA_path_resolve_property(&id_ptr, rna_path, &ptr, &prop) == false) {
if (RNA_path_resolve_property(&id_ptr, rna_path.path.c_str(), &ptr, &prop) == false) {
BKE_reportf(
reports,
RPT_ERROR,
"Could not clear keyframe, as RNA path is invalid for the given ID (ID = %s, path = %s)",
id->name,
rna_path);
rna_path.path.c_str());
return 0;
}
if (act == nullptr) {
if (adt->action) {
act = adt->action;
}
else {
BKE_reportf(reports, RPT_ERROR, "No action to delete keyframes from for ID = %s", id->name);
return 0;
}
if (!adt->action) {
BKE_reportf(reports, RPT_ERROR, "No action to delete keyframes from for ID = %s", id->name);
return 0;
}
bAction *act = adt->action;
int array_index = rna_path.index.value_or(0);
int array_index_max = array_index + 1;
if (array_index == -1) {
array_index = 0;
if (!rna_path.index.has_value()) {
array_index_max = RNA_property_array_length(&ptr, prop);
/* For single properties, increase max_index so that the property itself gets included,
@ -793,7 +768,7 @@ int clear_keyframe(Main *bmain,
int key_count = 0;
/* Will only loop once unless the array index was -1. */
for (; array_index < array_index_max; array_index++) {
FCurve *fcu = action_fcurve_find(act, {rna_path, array_index});
FCurve *fcu = action_fcurve_find(act, {rna_path.path, array_index});
if (fcu == nullptr) {
continue;

View File

@ -1,39 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup asset_system
*
* \brief Information to uniquely identify and locate an asset.
*
* https://developer.blender.org/docs/features/asset_system/backend/#asset-identifier
*/
#pragma once
#include <memory>
#include <string>
#include "BLI_string_ref.hh"
struct AssetWeakReference;
namespace blender::asset_system {
class AssetIdentifier {
std::shared_ptr<std::string> library_root_path_;
std::string relative_asset_path_;
public:
AssetIdentifier(std::shared_ptr<std::string> library_root_path, std::string relative_asset_path);
AssetIdentifier(AssetIdentifier &&) = default;
AssetIdentifier(const AssetIdentifier &) = default;
StringRefNull library_relative_identifier() const;
std::string full_path() const;
std::string full_library_path() const;
};
} // namespace blender::asset_system

View File

@ -8,7 +8,6 @@
#pragma once
#include <functional>
#include <memory>
#include <mutex>
@ -30,9 +29,7 @@ class IDRemapper;
namespace blender::asset_system {
class AssetIdentifier;
class AssetRepresentation;
class AssetStorage;
/**
* AssetLibrary provides access to an asset library's data.
@ -54,9 +51,9 @@ class AssetLibrary {
std::shared_ptr<std::string> root_path_;
/**
* Storage for assets (better said their representations) that are considered to be part of this
* library. Assets are not automatically loaded into this when loading an asset library. Assets
* have to be loaded externally and added to this storage via #add_external_asset() or
* AssetStorage for assets (better said their representations) that are considered to be part of
* this library. Assets are not automatically loaded into this when loading an asset library.
* Assets have to be loaded externally and added to this storage via #add_external_asset() or
* #add_local_id_asset(). So this really is arbitrary storage as far as #AssetLibrary is
* concerned (allowing the API user to manage partial library storage and partial loading, so
* only relevant parts of a library are kept in memory).
@ -67,7 +64,17 @@ class AssetLibrary {
* already in memory and which not. Neither do we keep track of how many parts of Blender are
* using an asset or an asset library, which is needed to know when assets can be freed.
*/
std::unique_ptr<AssetStorage> asset_storage_;
struct AssetStorage {
/* Uses shared pointers so the UI can acquire weak pointers. It can then ensure pointers are
* not dangling before accessing. */
Set<std::shared_ptr<AssetRepresentation>> external_assets;
/* Store local ID assets separately for efficient lookups.
* TODO(Julian): A [ID *, asset] or even [ID.session_uid, asset] map would be preferable for
* faster lookups. Not possible until each asset is only represented once in the storage. */
Set<std::shared_ptr<AssetRepresentation>> local_id_assets;
};
AssetStorage asset_storage_;
protected:
/* Changing this pointer should be protected using #catalog_service_mutex_. Note that changes
@ -169,12 +176,6 @@ class AssetLibrary {
void on_blend_save_post(Main *bmain, PointerRNA **pointers, int num_pointers);
/**
* Create an asset identifier from the root path of this asset library and the given relative
* asset path (relative to the asset library root directory).
*/
AssetIdentifier asset_identifier_from_library(StringRef relative_asset_path);
std::string resolve_asset_weak_reference_to_full_path(const AssetWeakReference &asset_reference);
eAssetLibraryType library_type() const;

View File

@ -23,8 +23,6 @@
#include "DNA_ID_enums.h"
#include "DNA_asset_types.h"
#include "AS_asset_identifier.hh"
struct AssetMetaData;
struct ID;
@ -33,14 +31,18 @@ namespace blender::asset_system {
class AssetLibrary;
class AssetRepresentation : NonCopyable, NonMovable {
AssetIdentifier identifier_;
/** Pointer back to the asset library that owns this asset representation. */
const AssetLibrary &owner_asset_library_;
/**
* Uniquely identifies the asset within the asset library. Currently this is always a path (path
* within the asset library).
*/
std::string relative_identifier_;
/**
* Indicate if this is a local or external asset, and as such, which of the union members below
* should be used.
*/
const bool is_local_id_ = false;
/** Asset library that owns this asset representation. */
const AssetLibrary &owner_asset_library_;
struct ExternalAsset {
std::string name;
@ -52,11 +54,11 @@ class AssetRepresentation : NonCopyable, NonMovable {
ID *local_asset_id_ = nullptr; /* Non-owning. */
};
friend class AssetStorage;
friend class AssetLibrary;
public:
/** Constructs an asset representation for an external ID. The asset will not be editable. */
AssetRepresentation(AssetIdentifier &&identifier,
AssetRepresentation(StringRef relative_asset_path,
StringRef name,
int id_type,
std::unique_ptr<AssetMetaData> metadata,
@ -65,13 +67,11 @@ class AssetRepresentation : NonCopyable, NonMovable {
* Constructs an asset representation for an ID stored in the current file. This makes the asset
* local and fully editable.
*/
AssetRepresentation(AssetIdentifier &&identifier,
AssetRepresentation(StringRef relative_asset_path,
ID &id,
const AssetLibrary &owner_asset_library);
~AssetRepresentation();
const AssetIdentifier &get_identifier() const;
/**
* Create a weak reference for this asset that can be written to files, but can break under a
* number of conditions.
@ -82,6 +82,11 @@ class AssetRepresentation : NonCopyable, NonMovable {
StringRefNull get_name() const;
ID_Type get_id_type() const;
AssetMetaData &get_metadata() const;
StringRefNull library_relative_identifier() const;
std::string full_path() const;
std::string full_library_path() const;
/**
* Get the import method to use for this asset. A different one may be used if
* #may_override_import_method() returns true, otherwise, the returned value must be used. If

View File

@ -17,7 +17,6 @@ set(SRC
intern/asset_catalog_definition_file.cc
intern/asset_catalog_path.cc
intern/asset_catalog_tree.cc
intern/asset_identifier.cc
intern/asset_library.cc
intern/asset_library_all.cc
intern/asset_library_essentials.cc
@ -26,13 +25,11 @@ set(SRC
intern/asset_library_runtime.cc
intern/asset_library_service.cc
intern/asset_representation.cc
intern/asset_storage.cc
intern/utils.cc
AS_asset_catalog.hh
AS_asset_catalog_path.hh
AS_asset_catalog_tree.hh
AS_asset_identifier.hh
AS_asset_library.hh
AS_asset_representation.hh
AS_essentials_library.hh
@ -44,7 +41,6 @@ set(SRC
intern/asset_library_on_disk.hh
intern/asset_library_runtime.hh
intern/asset_library_service.hh
intern/asset_storage.hh
intern/utils.hh
)

View File

@ -1,53 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup asset_system
*/
#include <string>
#include "BKE_blendfile.hh"
#include "BLI_path_util.h"
#include "BLI_path_util.h"
#include "AS_asset_identifier.hh"
namespace blender::asset_system {
AssetIdentifier::AssetIdentifier(std::shared_ptr<std::string> library_root_path,
std::string relative_asset_path)
: library_root_path_(std::move(library_root_path)),
relative_asset_path_(std::move(relative_asset_path))
{
}
StringRefNull AssetIdentifier::library_relative_identifier() const
{
return relative_asset_path_;
}
std::string AssetIdentifier::full_path() const
{
char filepath[FILE_MAX];
BLI_path_join(
filepath, sizeof(filepath), library_root_path_->c_str(), relative_asset_path_.c_str());
return filepath;
}
std::string AssetIdentifier::full_library_path() const
{
std::string asset_path = full_path();
char blend_path[1090 /*FILE_MAX_LIBEXTRA*/];
if (!BKE_blendfile_library_path_explode(asset_path.c_str(), blend_path, nullptr, nullptr)) {
return {};
}
return blend_path;
}
} // namespace blender::asset_system

View File

@ -9,7 +9,6 @@
#include <memory>
#include "AS_asset_catalog_tree.hh"
#include "AS_asset_identifier.hh"
#include "AS_asset_library.hh"
#include "AS_asset_representation.hh"
@ -27,7 +26,6 @@
#include "asset_catalog_collection.hh"
#include "asset_catalog_definition_file.hh"
#include "asset_library_service.hh"
#include "asset_storage.hh"
#include "utils.hh"
using namespace blender;
@ -168,7 +166,6 @@ AssetLibrary::AssetLibrary(eAssetLibraryType library_type, StringRef name, Strin
: library_type_(library_type),
name_(name),
root_path_(std::make_shared<std::string>(utils::normalize_directory_path(root_path))),
asset_storage_(std::make_unique<AssetStorage>()),
catalog_service_(std::make_unique<AssetCatalogService>())
{
}
@ -208,26 +205,45 @@ std::weak_ptr<AssetRepresentation> AssetLibrary::add_external_asset(
const int id_type,
std::unique_ptr<AssetMetaData> metadata)
{
AssetIdentifier identifier = this->asset_identifier_from_library(relative_asset_path);
return asset_storage_->add_external_asset(
std::move(identifier), name, id_type, std::move(metadata), *this);
return asset_storage_.external_assets.lookup_key_or_add(std::make_shared<AssetRepresentation>(
relative_asset_path, name, id_type, std::move(metadata), *this));
}
std::weak_ptr<AssetRepresentation> AssetLibrary::add_local_id_asset(StringRef relative_asset_path,
ID &id)
{
AssetIdentifier identifier = this->asset_identifier_from_library(relative_asset_path);
return asset_storage_->add_local_id_asset(std::move(identifier), id, *this);
return asset_storage_.local_id_assets.lookup_key_or_add(
std::make_shared<AssetRepresentation>(relative_asset_path, id, *this));
}
bool AssetLibrary::remove_asset(AssetRepresentation &asset)
{
return asset_storage_->remove_asset(asset);
if (asset_storage_.local_id_assets.remove_as(&asset)) {
return true;
}
return asset_storage_.external_assets.remove_as(&asset);
}
void AssetLibrary::remap_ids_and_remove_invalid(const bke::id::IDRemapper &mappings)
{
asset_storage_->remap_ids_and_remove_invalid(mappings);
Set<AssetRepresentation *> removed_assets;
for (auto &asset_ptr : asset_storage_.local_id_assets) {
AssetRepresentation &asset = *asset_ptr;
BLI_assert(asset.is_local_id());
const IDRemapperApplyResult result = mappings.apply(&asset.local_asset_id_,
ID_REMAP_APPLY_DEFAULT);
/* Entirely remove assets whose ID is unset. We don't want assets with a null ID pointer. */
if (result == ID_REMAP_RESULT_SOURCE_UNASSIGNED) {
removed_assets.add(&asset);
}
}
for (AssetRepresentation *asset : removed_assets) {
this->remove_asset(*asset);
}
}
namespace {
@ -269,11 +285,6 @@ void AssetLibrary::on_blend_save_post(Main *main,
}
}
AssetIdentifier AssetLibrary::asset_identifier_from_library(StringRef relative_asset_path)
{
return AssetIdentifier(root_path_, relative_asset_path);
}
std::string AssetLibrary::resolve_asset_weak_reference_to_full_path(
const AssetWeakReference &asset_reference)
{

View File

@ -8,23 +8,26 @@
#include <stdexcept>
#include "BLI_path_util.h"
#include "BKE_blendfile.hh"
#include "DNA_ID.h"
#include "DNA_asset_types.h"
#include "AS_asset_identifier.hh"
#include "AS_asset_library.hh"
#include "AS_asset_representation.hh"
namespace blender::asset_system {
AssetRepresentation::AssetRepresentation(AssetIdentifier &&identifier,
AssetRepresentation::AssetRepresentation(StringRef relative_path,
StringRef name,
const int id_type,
std::unique_ptr<AssetMetaData> metadata,
const AssetLibrary &owner_asset_library)
: identifier_(std::move(identifier)),
: owner_asset_library_(owner_asset_library),
relative_identifier_(relative_path),
is_local_id_(false),
owner_asset_library_(owner_asset_library),
external_asset_()
{
external_asset_.name = name;
@ -32,12 +35,12 @@ AssetRepresentation::AssetRepresentation(AssetIdentifier &&identifier,
external_asset_.metadata_ = std::move(metadata);
}
AssetRepresentation::AssetRepresentation(AssetIdentifier &&identifier,
AssetRepresentation::AssetRepresentation(StringRef relative_path,
ID &id,
const AssetLibrary &owner_asset_library)
: identifier_(std::move(identifier)),
: owner_asset_library_(owner_asset_library),
relative_identifier_(relative_path),
is_local_id_(true),
owner_asset_library_(owner_asset_library),
local_asset_id_(&id)
{
if (!id.asset_data) {
@ -52,14 +55,9 @@ AssetRepresentation::~AssetRepresentation()
}
}
const AssetIdentifier &AssetRepresentation::get_identifier() const
{
return identifier_;
}
AssetWeakReference AssetRepresentation::make_weak_reference() const
{
return AssetWeakReference::make_reference(owner_asset_library_, identifier_);
return AssetWeakReference::make_reference(owner_asset_library_, relative_identifier_);
}
StringRefNull AssetRepresentation::get_name() const
@ -85,6 +83,33 @@ AssetMetaData &AssetRepresentation::get_metadata() const
return is_local_id_ ? *local_asset_id_->asset_data : *external_asset_.metadata_;
}
StringRefNull AssetRepresentation::library_relative_identifier() const
{
return relative_identifier_;
}
std::string AssetRepresentation::full_path() const
{
char filepath[FILE_MAX];
BLI_path_join(filepath,
sizeof(filepath),
owner_asset_library_.root_path().c_str(),
relative_identifier_.c_str());
return filepath;
}
std::string AssetRepresentation::full_library_path() const
{
std::string asset_path = full_path();
char blend_path[1090 /*FILE_MAX_LIBEXTRA*/];
if (!BKE_blendfile_library_path_explode(asset_path.c_str(), blend_path, nullptr, nullptr)) {
return {};
}
return blend_path;
}
std::optional<eAssetImportMethod> AssetRepresentation::get_import_method() const
{
return owner_asset_library_.import_method_;

View File

@ -1,68 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup asset_system
*/
#include "AS_asset_representation.hh"
#include "DNA_ID.h"
#include "DNA_asset_types.h"
#include "BKE_lib_remap.hh"
#include "asset_storage.hh"
namespace blender::asset_system {
std::weak_ptr<AssetRepresentation> AssetStorage::add_local_id_asset(
AssetIdentifier &&identifier, ID &id, const AssetLibrary &owner_asset_library)
{
return local_id_assets_.lookup_key_or_add(
std::make_shared<AssetRepresentation>(std::move(identifier), id, owner_asset_library));
}
std::weak_ptr<AssetRepresentation> AssetStorage::add_external_asset(
AssetIdentifier &&identifier,
StringRef name,
const int id_type,
std::unique_ptr<AssetMetaData> metadata,
const AssetLibrary &owner_asset_library)
{
return external_assets_.lookup_key_or_add(std::make_shared<AssetRepresentation>(
std::move(identifier), name, id_type, std::move(metadata), owner_asset_library));
}
bool AssetStorage::remove_asset(AssetRepresentation &asset)
{
if (local_id_assets_.remove_as(&asset)) {
return true;
}
return external_assets_.remove_as(&asset);
}
void AssetStorage::remap_ids_and_remove_invalid(const bke::id::IDRemapper &mappings)
{
Set<AssetRepresentation *> removed_assets;
for (auto &asset_ptr : local_id_assets_) {
AssetRepresentation &asset = *asset_ptr;
BLI_assert(asset.is_local_id());
const IDRemapperApplyResult result = mappings.apply(&asset.local_asset_id_,
ID_REMAP_APPLY_DEFAULT);
/* Entirely remove assets whose ID is unset. We don't want assets with a null ID pointer. */
if (result == ID_REMAP_RESULT_SOURCE_UNASSIGNED) {
removed_assets.add(&asset);
}
}
for (AssetRepresentation *asset : removed_assets) {
this->remove_asset(*asset);
}
}
} // namespace blender::asset_system

View File

@ -1,61 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup asset_system
*
* The database implementation for asset representations within a library. Not part of the public
* API, storage access happens via the #AssetLibrary API.
*/
#pragma once
#include <memory>
#include "BLI_set.hh"
struct AssetMetaData;
struct ID;
namespace blender::bke::id {
class IDRemapper;
}
namespace blender::asset_system {
class AssetIdentifier;
class AssetLibrary;
class AssetRepresentation;
class AssetStorage {
/* Uses shared pointers so the UI can acquire weak pointers. It can then ensure pointers are not
* dangling before accessing. */
using StorageT = Set<std::shared_ptr<AssetRepresentation>>;
StorageT external_assets_;
/* Store local ID assets separately for efficient lookups.
* TODO(Julian): A [ID *, asset] or even [ID.session_uid, asset] map would be preferable for
* faster lookups. Not possible until each asset is only represented once in the storage. */
StorageT local_id_assets_;
public:
/** See #AssetLibrary::add_external_asset(). */
std::weak_ptr<AssetRepresentation> add_external_asset(AssetIdentifier &&identifier,
StringRef name,
int id_type,
std::unique_ptr<AssetMetaData> metadata,
const AssetLibrary &owner_asset_library);
/** See #AssetLibrary::add_external_asset(). */
std::weak_ptr<AssetRepresentation> add_local_id_asset(AssetIdentifier &&identifier,
ID &id,
const AssetLibrary &owner_asset_library);
/** See #AssetLibrary::remove_asset(). */
bool remove_asset(AssetRepresentation &asset);
/** See #AssetLibrary::remap_ids_and_remove_nulled(). */
void remap_ids_and_remove_invalid(const blender::bke::id::IDRemapper &mappings);
};
} // namespace blender::asset_system

View File

@ -17,7 +17,6 @@
#include "DNA_asset_types.h"
#include "DNA_space_types.h"