Cleanup: Remove LibOverride Template experimental feature.

This was added at the start of the project, as a way for the source
library to control what would be overridable by the user of the data
(production files).

The implementation never went beyond the experimental area, and only
exposed in the python API then. The idea has been superseeded by the
'cherry-pick' design, so there is no point in keeping this code any
longer.
This commit is contained in:
Bastien Montagne 2023-12-20 17:53:34 +01:00
parent 399e8264e6
commit 25ab53a4af
8 changed files with 89 additions and 233 deletions

View File

@ -203,10 +203,7 @@ bool BKE_lib_override_library_create(Main *bmain,
ID *id_instance_hint,
ID **r_id_root_override,
const bool do_fully_editable);
/**
* Create a library override template.
*/
bool BKE_lib_override_library_template_create(ID *id);
/**
* Convert a given proxy object into a library override.
*

View File

@ -148,35 +148,16 @@ IDOverrideLibrary *BKE_lib_override_library_get(Main *bmain,
IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id)
{
/* If reference_id is nullptr, we are creating an override template for purely local data.
* Else, reference *must* be linked data. */
BLI_assert(reference_id == nullptr || ID_IS_LINKED(reference_id));
/* The `reference_id` *must* be linked data. */
BLI_assert(!reference_id || ID_IS_LINKED(reference_id));
BLI_assert(local_id->override_library == nullptr);
ID *ancestor_id;
for (ancestor_id = reference_id;
ancestor_id != nullptr && ancestor_id->override_library != nullptr &&
ancestor_id->override_library->reference != nullptr;
ancestor_id = ancestor_id->override_library->reference)
{
/* pass */
}
if (ancestor_id != nullptr && ancestor_id->override_library != nullptr) {
/* Original ID has a template, use it! */
BKE_lib_override_library_copy(local_id, ancestor_id, true);
if (local_id->override_library->reference != reference_id) {
id_us_min(local_id->override_library->reference);
local_id->override_library->reference = reference_id;
id_us_plus(local_id->override_library->reference);
}
return local_id->override_library;
}
/* Else, generate new empty override. */
local_id->override_library = MEM_cnew<IDOverrideLibrary>(__func__);
local_id->override_library->reference = reference_id;
id_us_plus(local_id->override_library->reference);
if (reference_id) {
id_us_plus(local_id->override_library->reference);
}
local_id->tag &= ~LIB_TAG_LIBOVERRIDE_REFOK;
/* By default initialized liboverrides are 'system overrides', higher-level code is responsible
* to unset this flag for specific IDs. */
@ -187,7 +168,7 @@ IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id)
void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_full_copy)
{
BLI_assert(ID_IS_OVERRIDE_LIBRARY(src_id) || ID_IS_OVERRIDE_LIBRARY_TEMPLATE(src_id));
BLI_assert(ID_IS_OVERRIDE_LIBRARY(src_id));
if (dst_id->override_library != nullptr) {
if (src_id->override_library == nullptr) {
@ -205,12 +186,8 @@ void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_f
BKE_lib_override_library_init(dst_id, nullptr);
}
/* If source is already overriding data, we copy it but reuse its reference for destination ID.
* Otherwise, source is only an override template, it then becomes reference of destination ID.
*/
dst_id->override_library->reference = src_id->override_library->reference ?
src_id->override_library->reference :
const_cast<ID *>(src_id);
/* Reuse the source's reference for destination ID. */
dst_id->override_library->reference = src_id->override_library->reference;
id_us_plus(dst_id->override_library->reference);
dst_id->override_library->hierarchy_root = src_id->override_library->hierarchy_root;
@ -274,8 +251,7 @@ static ID *lib_override_library_create_from(Main *bmain,
ID *reference_id,
const int lib_id_copy_flags)
{
/* NOTE: We do not want to copy possible override data from reference here (whether it is an
* override template, or already an override of some other ref data). */
/* NOTE: do not copy possible override data from the reference here. */
ID *local_id = BKE_id_copy_ex(bmain,
reference_id,
nullptr,
@ -1676,19 +1652,6 @@ bool BKE_lib_override_library_create(Main *bmain,
return success;
}
bool BKE_lib_override_library_template_create(ID *id)
{
if (ID_IS_LINKED(id)) {
return false;
}
if (ID_IS_OVERRIDE_LIBRARY(id)) {
return false;
}
BKE_lib_override_library_init(id, nullptr);
return true;
}
static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int *r_best_level)
{
if (curr_level > 1000) {
@ -4203,29 +4166,36 @@ void BKE_lib_override_library_validate(Main * /*bmain*/, ID *id, ReportList *rep
return;
}
if (id->override_library->reference == nullptr) {
/* This is a template ID, could be linked or local, not an override. */
/* This (probably) used to be a template ID, could be linked or local, not an override. */
BKE_reportf(reports,
RPT_WARNING,
"Library override templates have been removed: removing all override data from "
"the data-block '%s'",
id->name);
BKE_lib_override_library_free(&id->override_library, true);
return;
}
if (id->override_library->reference == id) {
/* Very serious data corruption, cannot do much about it besides removing the reference
* (therefore making the id a local override template one only). */
/* Very serious data corruption, cannot do much about it besides removing the liboverride data.
*/
BKE_reportf(reports,
RPT_ERROR,
"Data corruption: data-block '%s' is using itself as library override reference",
"Data corruption: data-block '%s' is using itself as library override reference, "
"removing all override data",
id->name);
id->override_library->reference = nullptr;
BKE_lib_override_library_free(&id->override_library, true);
return;
}
if (!ID_IS_LINKED(id->override_library->reference)) {
/* Very serious data corruption, cannot do much about it besides removing the reference
* (therefore making the id a local override template one only). */
/* Very serious data corruption, cannot do much about it besides removing the liboverride data.
*/
BKE_reportf(reports,
RPT_ERROR,
"Data corruption: data-block '%s' is using another local data-block ('%s') as "
"library override reference",
"library override reference, removing all override data",
id->name,
id->override_library->reference->name);
id->override_library->reference = nullptr;
BKE_lib_override_library_free(&id->override_library, true);
return;
}
}
@ -4248,11 +4218,7 @@ bool BKE_lib_override_library_status_check_local(Main *bmain, ID *local)
ID *reference = local->override_library->reference;
if (reference == nullptr) {
/* This is an override template, local status is always OK! */
return true;
}
BLI_assert(reference);
BLI_assert(GS(local->name) == GS(reference->name));
if (GS(local->name) == ID_OB) {
@ -4298,11 +4264,7 @@ bool BKE_lib_override_library_status_check_reference(Main *bmain, ID *local)
ID *reference = local->override_library->reference;
if (reference == nullptr) {
/* This is an override template, reference is virtual, so its status is always OK! */
return true;
}
BLI_assert(reference);
BLI_assert(GS(local->name) == GS(reference->name));
if (reference->override_library && (reference->tag & LIB_TAG_LIBOVERRIDE_REFOK) == 0) {
@ -4354,63 +4316,60 @@ static void lib_override_library_operations_create(Main *bmain,
eRNAOverrideMatchResult *r_report_flags)
{
BLI_assert(!ID_IS_LINKED(local));
BLI_assert(local->override_library != nullptr);
const bool is_template = (local->override_library->reference == nullptr);
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(local));
if (!is_template) {
/* Do not attempt to generate overriding rules from an empty place-holder generated by link
* code when it cannot find the actual library/ID. Much better to keep the local data-block as
* is in the file in that case, until broken lib is fixed. */
if (ID_MISSING(local->override_library->reference)) {
return;
}
/* Do not attempt to generate overriding rules from an empty place-holder generated by link
* code when it cannot find the actual library/ID. Much better to keep the local data-block as
* is in the file in that case, until broken lib is fixed. */
if (ID_MISSING(local->override_library->reference)) {
return;
}
if (GS(local->name) == ID_OB) {
/* Our beloved pose's bone cross-data pointers. Usually, depsgraph evaluation would
* ensure this is valid, but in some situations (like hidden collections etc.) this won't
* be the case, so we need to take care of this ourselves. */
Object *ob_local = reinterpret_cast<Object *>(local);
if (ob_local->type == OB_ARMATURE) {
Object *ob_reference = reinterpret_cast<Object *>(local->override_library->reference);
BLI_assert(ob_local->data != nullptr);
BLI_assert(ob_reference->data != nullptr);
BKE_pose_ensure(bmain, ob_local, static_cast<bArmature *>(ob_local->data), true);
BKE_pose_ensure(bmain, ob_reference, static_cast<bArmature *>(ob_reference->data), true);
}
if (GS(local->name) == ID_OB) {
/* Our beloved pose's bone cross-data pointers. Usually, depsgraph evaluation would
* ensure this is valid, but in some situations (like hidden collections etc.) this won't
* be the case, so we need to take care of this ourselves. */
Object *ob_local = reinterpret_cast<Object *>(local);
if (ob_local->type == OB_ARMATURE) {
Object *ob_reference = reinterpret_cast<Object *>(local->override_library->reference);
BLI_assert(ob_local->data != nullptr);
BLI_assert(ob_reference->data != nullptr);
BKE_pose_ensure(bmain, ob_local, static_cast<bArmature *>(ob_local->data), true);
BKE_pose_ensure(bmain, ob_reference, static_cast<bArmature *>(ob_reference->data), true);
}
}
PointerRNA rnaptr_local = RNA_id_pointer_create(local);
PointerRNA rnaptr_reference = RNA_id_pointer_create(local->override_library->reference);
PointerRNA rnaptr_local = RNA_id_pointer_create(local);
PointerRNA rnaptr_reference = RNA_id_pointer_create(local->override_library->reference);
eRNAOverrideMatchResult local_report_flags = RNA_OVERRIDE_MATCH_RESULT_INIT;
RNA_struct_override_matches(bmain,
&rnaptr_local,
&rnaptr_reference,
nullptr,
0,
local->override_library,
liboverride_match_flags,
&local_report_flags);
eRNAOverrideMatchResult local_report_flags = RNA_OVERRIDE_MATCH_RESULT_INIT;
RNA_struct_override_matches(bmain,
&rnaptr_local,
&rnaptr_reference,
nullptr,
0,
local->override_library,
liboverride_match_flags,
&local_report_flags);
if (local_report_flags & RNA_OVERRIDE_MATCH_RESULT_RESTORED) {
CLOG_INFO(&LOG, 2, "We did restore some properties of %s from its reference", local->name);
}
if (local_report_flags & RNA_OVERRIDE_MATCH_RESULT_RESTORE_TAGGED) {
CLOG_INFO(&LOG,
2,
"We did tag some properties of %s for restoration from its reference",
local->name);
}
if (local_report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) {
CLOG_INFO(&LOG, 2, "We did generate library override rules for %s", local->name);
}
else {
CLOG_INFO(&LOG, 2, "No new library override rules for %s", local->name);
}
if (local_report_flags & RNA_OVERRIDE_MATCH_RESULT_RESTORED) {
CLOG_INFO(&LOG, 2, "We did restore some properties of %s from its reference", local->name);
}
if (local_report_flags & RNA_OVERRIDE_MATCH_RESULT_RESTORE_TAGGED) {
CLOG_INFO(&LOG,
2,
"We did tag some properties of %s for restoration from its reference",
local->name);
}
if (local_report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) {
CLOG_INFO(&LOG, 2, "We did generate library override rules for %s", local->name);
}
else {
CLOG_INFO(&LOG, 2, "No new library override rules for %s", local->name);
}
if (r_report_flags != nullptr) {
*r_report_flags = static_cast<eRNAOverrideMatchResult>(*r_report_flags | local_report_flags);
}
if (r_report_flags != nullptr) {
*r_report_flags = static_cast<eRNAOverrideMatchResult>(*r_report_flags | local_report_flags);
}
}
void BKE_lib_override_library_operations_create(Main *bmain, ID *local, int *r_report_flags)
@ -5114,10 +5073,9 @@ ID *BKE_lib_override_library_operations_store_start(Main *bmain,
OverrideLibraryStorage *liboverride_storage,
ID *local)
{
if (ID_IS_OVERRIDE_LIBRARY_TEMPLATE(local) || ID_IS_OVERRIDE_LIBRARY_VIRTUAL(local)) {
/* This is actually purely local data with an override template, or one of those embedded IDs
* (root node trees, master collections or shape-keys) that cannot have their own override.
* Nothing to do here! */
if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(local)) {
/* This is actually one of these embedded IDs (root node trees, master collections or
* shape-keys) that cannot have their own override. Nothing to do here! */
return nullptr;
}

View File

@ -685,16 +685,11 @@ static int override_remove_button_exec(bContext *C, wmOperator *op)
BLI_assert(oprop != nullptr);
BLI_assert(id != nullptr && id->override_library != nullptr);
const bool is_template = ID_IS_OVERRIDE_LIBRARY_TEMPLATE(id);
/* We need source (i.e. linked data) to restore values of deleted overrides...
* If this is an override template, we obviously do not need to restore anything. */
if (!is_template) {
PropertyRNA *src_prop;
PointerRNA id_refptr = RNA_id_pointer_create(id->override_library->reference);
if (!RNA_path_resolve_property(&id_refptr, oprop->rna_path, &src, &src_prop)) {
BLI_assert_msg(0, "Failed to create matching source (linked data) RNA pointer");
}
/* The source (i.e. linked data) is required to restore values of deleted overrides. */
PropertyRNA *src_prop;
PointerRNA id_refptr = RNA_id_pointer_create(id->override_library->reference);
if (!RNA_path_resolve_property(&id_refptr, oprop->rna_path, &src, &src_prop)) {
BLI_assert_msg(0, "Failed to create matching source (linked data) RNA pointer");
}
if (!all && index != -1) {
@ -716,9 +711,7 @@ static int override_remove_button_exec(bContext *C, wmOperator *op)
}
}
BKE_lib_override_library_property_operation_delete(oprop, opop);
if (!is_template) {
RNA_property_copy(bmain, &ptr, &src, prop, index);
}
RNA_property_copy(bmain, &ptr, &src, prop, index);
if (BLI_listbase_is_empty(&oprop->operations)) {
BKE_lib_override_library_property_delete(id->override_library, oprop);
}
@ -726,9 +719,7 @@ static int override_remove_button_exec(bContext *C, wmOperator *op)
else {
/* Just remove whole generic override operation of this property. */
BKE_lib_override_library_property_delete(id->override_library, oprop);
if (!is_template) {
RNA_property_copy(bmain, &ptr, &src, prop, -1);
}
RNA_property_copy(bmain, &ptr, &src, prop, -1);
}
/* Outliner e.g. has to be aware of this change. */

View File

@ -695,9 +695,6 @@ typedef struct PreviewImage {
(!ID_IS_OVERRIDE_LIBRARY_REAL(_id) || \
((ID *)(_id))->override_library->hierarchy_root == ((ID *)(_id)))
#define ID_IS_OVERRIDE_LIBRARY_TEMPLATE(_id) \
(((ID *)(_id))->override_library != NULL && ((ID *)(_id))->override_library->reference == NULL)
#define ID_IS_ASSET(_id) (((const ID *)(_id))->asset_data != NULL)
/* Check whether datablock type is covered by copy-on-write. */

View File

@ -708,7 +708,6 @@ typedef struct UserDef_Experimental {
char use_full_frame_compositor;
char use_sculpt_tools_tilt;
char use_extended_asset_browser;
char use_override_templates;
char use_sculpt_texture_paint;
char use_grease_pencil_version3;
char enable_overlay_next;
@ -716,7 +715,7 @@ typedef struct UserDef_Experimental {
char use_shader_node_previews;
char use_extension_repos;
char _pad[3];
char _pad[4];
/** `makesdna` does not allow empty structs. */
} UserDef_Experimental;

View File

@ -824,26 +824,6 @@ static ID *rna_ID_override_hierarchy_create(ID *id,
return id_root_override;
}
static void rna_ID_override_template_create(ID *id, ReportList *reports)
{
if (!U.experimental.use_override_templates) {
BKE_report(reports, RPT_ERROR, "Override template experimental feature is disabled");
return;
}
if (ID_IS_LINKED(id)) {
BKE_report(reports, RPT_ERROR, "Unable to create override template for linked data-blocks");
return;
}
if (ID_IS_OVERRIDE_LIBRARY(id)) {
BKE_report(
reports, RPT_ERROR, "Unable to create override template for overridden data-blocks");
return;
}
BKE_lib_override_library_template_create(id);
WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, nullptr);
}
static void rna_ID_override_library_operations_update(ID *id,
IDOverrideLibrary * /*override_library*/,
Main *bmain,
@ -2406,10 +2386,6 @@ static void rna_def_ID(BlenderRNA *brna)
"Make all library overrides generated by this call fully editable by the user "
"(none will be 'system overrides')");
func = RNA_def_function(srna, "override_template_create", "rna_ID_override_template_create");
RNA_def_function_ui_description(func, "Create an override template for this ID");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
func = RNA_def_function(srna, "user_clear", "rna_ID_user_clear");
RNA_def_function_ui_description(func,
"Clear the user count of a data-block so its not saved, "

View File

@ -7009,11 +7009,6 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
"completely reread assets from disk");
RNA_def_property_update(prop, 0, "rna_userdef_ui_update");
prop = RNA_def_property(srna, "use_override_templates", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "use_override_templates", 1);
RNA_def_property_ui_text(
prop, "Override Templates", "Enable library override template in the Python API");
prop = RNA_def_property(srna, "use_grease_pencil_version3", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "use_grease_pencil_version3", 1);
RNA_def_property_ui_text(prop, "Grease Pencil 3.0", "Enable the new grease pencil 3.0 codebase");

View File

@ -41,9 +41,6 @@ class TestLibraryOverrides(TestHelper, unittest.TestCase):
mesh = bpy.data.meshes.new(TestLibraryOverrides.MESH_LIBRARY_PERMISSIVE)
obj = bpy.data.objects.new(TestLibraryOverrides.OBJECT_LIBRARY_PERMISSIVE, object_data=mesh)
bpy.context.collection.objects.link(obj)
obj.override_template_create()
prop = obj.override_library.properties.add(rna_path='scale')
prop.operations.add(operation='NOOP')
bpy.ops.wm.save_as_mainfile(filepath=str(self.output_path), check_existing=False, compress=False)
@ -104,12 +101,6 @@ class TestLibraryOverrides(TestHelper, unittest.TestCase):
assert bpy.data.objects.get((local_id_name, None), None) is None
def test_link_permissive(self):
"""
Linked assets with a permissive template.
- Checks if the NOOP is properly handled.
- Checks if the correct properties and operations are created/updated.
"""
bpy.ops.wm.read_homefile(use_empty=True, use_factory_startup=True)
bpy.data.orphans_purge()
@ -117,38 +108,19 @@ class TestLibraryOverrides(TestHelper, unittest.TestCase):
bpy.ops.wm.link(directory=str(link_dir), filename=TestLibraryOverrides.OBJECT_LIBRARY_PERMISSIVE)
obj = bpy.data.objects[TestLibraryOverrides.OBJECT_LIBRARY_PERMISSIVE]
self.assertIsNotNone(obj.override_library)
self.assertIsNone(obj.override_library)
local_id = obj.override_create()
self.assertIsNotNone(local_id.override_library)
self.assertIsNone(local_id.data.override_library)
assert len(local_id.override_library.properties) == 1
override_prop = local_id.override_library.properties[0]
assert override_prop.rna_path == "scale"
assert len(override_prop.operations) == 1
override_operation = override_prop.operations[0]
assert override_operation.operation == 'NOOP'
assert override_operation.subitem_local_index == -1
assert len(local_id.override_library.properties) == 0
local_id.location.y = 1.0
local_id.scale.x = 0.5
# `scale.x` will apply, but will be reverted when the library overrides
# are updated. This is by design so python scripts can still alter the
# properties locally what is a typical usecase in productions.
assert local_id.scale.x == 0.5
assert local_id.location.y == 1.0
local_id.override_library.operations_update()
assert local_id.scale.x == 1.0
assert local_id.location.y == 1.0
assert len(local_id.override_library.properties) == 2
assert len(local_id.override_library.properties) == 1
override_prop = local_id.override_library.properties[0]
assert override_prop.rna_path == "scale"
assert len(override_prop.operations) == 1
override_operation = override_prop.operations[0]
assert override_operation.operation == 'NOOP'
assert override_operation.subitem_local_index == -1
override_prop = local_id.override_library.properties[1]
assert override_prop.rna_path == "location"
assert len(override_prop.operations) == 1
override_operation = override_prop.operations[0]
@ -156,33 +128,6 @@ class TestLibraryOverrides(TestHelper, unittest.TestCase):
assert override_operation.subitem_local_index == -1
class TestLibraryTemplate(TestHelper, unittest.TestCase):
MESH_LIBRARY_PERMISSIVE = "LibMeshPermissive"
OBJECT_LIBRARY_PERMISSIVE = "LibMeshPermissive"
def __init__(self, args):
pass
def test_permissive_template(self):
"""
Test setting up a permissive template.
"""
bpy.ops.wm.read_homefile(use_empty=True, use_factory_startup=True)
mesh = bpy.data.meshes.new(TestLibraryTemplate.MESH_LIBRARY_PERMISSIVE)
obj = bpy.data.objects.new(TestLibraryTemplate.OBJECT_LIBRARY_PERMISSIVE, object_data=mesh)
bpy.context.collection.objects.link(obj)
assert obj.override_library is None
obj.override_template_create()
assert obj.override_library is not None
assert len(obj.override_library.properties) == 0
prop = obj.override_library.properties.add(rna_path='scale')
assert len(obj.override_library.properties) == 1
assert len(prop.operations) == 0
operation = prop.operations.add(operation='NOOP')
assert len(prop.operations) == 1
assert operation.operation == 'NOOP'
class TestLibraryOverridesComplex(TestHelper, unittest.TestCase):
# Test resync, recursive resync, overrides of overrides, ID names collision handling, and multiple overrides.
@ -868,7 +813,6 @@ class TestLibraryOverridesFromProxies(TestHelper, unittest.TestCase):
TESTS = (
TestLibraryOverrides,
TestLibraryTemplate,
TestLibraryOverridesComplex,
TestLibraryOverridesFromProxies,
)
@ -903,7 +847,6 @@ def main():
# Don't write thumbnails into the home directory.
bpy.context.preferences.filepaths.file_preview_type = 'NONE'
bpy.context.preferences.experimental.use_override_templates = True
for Test in TESTS:
Test(args).run_all_tests()