From 39b8a3306830957132931c2966c3c3afff5cdfb3 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 29 Nov 2017 15:05:03 +0100 Subject: Merge ID static override, part I: core changes in DNA, BKE and .blend read/write. See https://developer.blender.org/D2417 for details. Note that since static overrides rely heavily on RNA, this commit is essentially invisible from user PoV, more in next commits. --- source/blender/blenkernel/BKE_library_override.h | 86 +++ source/blender/blenkernel/BKE_library_query.h | 3 + source/blender/blenkernel/BKE_library_remap.h | 2 + source/blender/blenkernel/CMakeLists.txt | 2 + source/blender/blenkernel/intern/library.c | 10 + .../blender/blenkernel/intern/library_override.c | 680 +++++++++++++++++++++ source/blender/blenkernel/intern/library_query.c | 4 + source/blender/blenkernel/intern/library_remap.c | 12 +- source/blender/blenloader/intern/readfile.c | 51 ++ source/blender/blenloader/intern/writefile.c | 294 +++++---- source/blender/makesdna/DNA_ID.h | 81 ++- 11 files changed, 1097 insertions(+), 128 deletions(-) create mode 100644 source/blender/blenkernel/BKE_library_override.h create mode 100644 source/blender/blenkernel/intern/library_override.c (limited to 'source') diff --git a/source/blender/blenkernel/BKE_library_override.h b/source/blender/blenkernel/BKE_library_override.h new file mode 100644 index 00000000000..1ba009660f2 --- /dev/null +++ b/source/blender/blenkernel/BKE_library_override.h @@ -0,0 +1,86 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 by Blender Foundation. + * All rights reserved. + * + * Contributor(s): Bastien Montagne. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BKE_LIBRARY_OVERRIDE_H__ +#define __BKE_LIBRARY_OVERRIDE_H__ + +/** \file BKE_library_override.h + * \ingroup bke + * \since December 2016 + * \author mont29 + */ + +struct ID; +struct IDOverrideStatic; +struct IDOverrideStaticProperty; +struct IDOverrideStaticPropertyOperation; +struct Main; + +struct IDOverrideStatic *BKE_override_static_init(struct ID *local_id, struct ID *reference_id); +void BKE_override_static_copy(struct ID *dst_id, const struct ID *src_id); +void BKE_override_static_clear(struct IDOverrideStatic *override); +void BKE_override_static_free(struct IDOverrideStatic **override); + +struct ID *BKE_override_static_create_from(struct Main *bmain, struct ID *reference_id); + +struct IDOverrideStaticProperty *BKE_override_static_property_find(struct IDOverrideStatic *override, const char *rna_path); +struct IDOverrideStaticProperty *BKE_override_static_property_get(struct IDOverrideStatic *override, const char *rna_path, bool *r_created); +void BKE_override_static_property_delete(struct IDOverrideStatic *override, struct IDOverrideStaticProperty *override_property); + +struct IDOverrideStaticPropertyOperation *BKE_override_static_property_operation_find( + struct IDOverrideStaticProperty *override_property, + const char *subitem_refname, const char *subitem_locname, + const int subitem_refindex, const int subitem_locindex, const bool strict, bool *r_strict); +struct IDOverrideStaticPropertyOperation *BKE_override_static_property_operation_get( + struct IDOverrideStaticProperty *override_property, const short operation, + const char *subitem_refname, const char *subitem_locname, + const int subitem_refindex, const int subitem_locindex, + const bool strict, bool *r_strict, bool *r_created); +void BKE_override_static_property_operation_delete( + struct IDOverrideStaticProperty *override_property, struct IDOverrideStaticPropertyOperation *override_property_operation); + +bool BKE_override_static_status_check_local(struct ID *local); +bool BKE_override_static_status_check_reference(struct ID *local); + +bool BKE_override_static_operations_create(struct ID *local); +void BKE_main_override_static_operations_create(struct Main *bmain); + +void BKE_override_static_update(struct Main *bmain, struct ID *local); +void BKE_main_override_static_update(struct Main *bmain); + + +/* Storage (.blend file writing) part. */ + +/* For now, we just use a temp main list. */ +typedef struct Main OverrideStaticStorage; + +OverrideStaticStorage *BKE_override_static_operations_store_initialize(void); +struct ID *BKE_override_static_operations_store_start(OverrideStaticStorage *override_storage, struct ID *local); +void BKE_override_static_operations_store_end(OverrideStaticStorage *override_storage, struct ID *local); +void BKE_override_static_operations_store_finalize(OverrideStaticStorage *override_storage); + + + +#endif /* __BKE_LIBRARY_OVERRIDE_H__ */ diff --git a/source/blender/blenkernel/BKE_library_query.h b/source/blender/blenkernel/BKE_library_query.h index d6e7d98f371..b66fc0aab16 100644 --- a/source/blender/blenkernel/BKE_library_query.h +++ b/source/blender/blenkernel/BKE_library_query.h @@ -56,6 +56,9 @@ enum { * How to handle that kind of cases totally depends on what caller code is doing... */ IDWALK_CB_LOOPBACK = (1 << 4), + /** That ID is used as static override's reference by its owner. */ + IDWALK_CB_STATIC_OVERRIDE_REFERENCE = (1 << 5), + /** * Adjusts #ID.us reference-count. * \note keep in sync with 'newlibadr_us' use in readfile.c diff --git a/source/blender/blenkernel/BKE_library_remap.h b/source/blender/blenkernel/BKE_library_remap.h index fd37fd762f4..3425ca011b7 100644 --- a/source/blender/blenkernel/BKE_library_remap.h +++ b/source/blender/blenkernel/BKE_library_remap.h @@ -51,6 +51,8 @@ enum { * This is needed e.g. in reload scenario, since we have to ensure remapping of Armature data of local proxy * is also performed. Usual nightmare... */ ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE = 1 << 4, + /* Do not remap static override pointers. */ + ID_REMAP_SKIP_STATIC_OVERRIDE = 1 << 5, }; /* Note: Requiring new_id to be non-null, this *may* not be the case ultimately, but makes things simpler for now. */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 24b0adb1adb..c99c3d6ffa9 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -124,6 +124,7 @@ set(SRC intern/lattice.c intern/library.c intern/library_idmap.c + intern/library_override.c intern/library_query.c intern/library_remap.c intern/linestyle.c @@ -256,6 +257,7 @@ set(SRC BKE_lattice.h BKE_library.h BKE_library_idmap.h + BKE_library_override.h BKE_library_query.h BKE_library_remap.h BKE_linestyle.h diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index f57691fde3e..0b4ec679f06 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -104,6 +104,7 @@ #include "BKE_lamp.h" #include "BKE_lattice.h" #include "BKE_library.h" +#include "BKE_library_override.h" #include "BKE_library_query.h" #include "BKE_library_remap.h" #include "BKE_linestyle.h" @@ -1411,6 +1412,15 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int fla new_id->properties = IDP_CopyProperty_ex(id->properties, flag); } + /* XXX Again... We need a way to control what we copy in a much more refined way. + * We cannot always copy this, some internal copying will die on it! */ + /* For now, upper level code will have to do that itself when required. */ +#if 0 + if (id->override != NULL) { + BKE_override_copy(new_id, id); + } +#endif + /* the duplicate should get a copy of the animdata */ id_copy_animdata(bmain, new_id, (flag & LIB_ID_COPY_ACTIONS) != 0 && (flag & LIB_ID_CREATE_NO_MAIN) == 0); diff --git a/source/blender/blenkernel/intern/library_override.c b/source/blender/blenkernel/intern/library_override.c new file mode 100644 index 00000000000..bc69b085e57 --- /dev/null +++ b/source/blender/blenkernel/intern/library_override.c @@ -0,0 +1,680 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 by Blender Foundation. + * All rights reserved. + * + * Contributor(s): Bastien Montagne. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/library_override.c + * \ingroup bke + */ + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_ID.h" +#include "DNA_object_types.h" + +#include "DEG_depsgraph.h" +#include "BKE_library.h" +#include "BKE_library_override.h" +#include "BKE_library_remap.h" +#include "BKE_main.h" + +#include "BLI_utildefines.h" +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "RNA_access.h" +#include "RNA_types.h" + +#include "PIL_time.h" +#include "PIL_time_utildefines.h" + +#define OVERRIDE_AUTO_CHECK_DELAY 0.2 /* 200ms between auto-override checks. */ + +static void bke_override_property_copy(IDOverrideStaticProperty *op_dst, IDOverrideStaticProperty *op_src); +static void bke_override_property_operation_copy(IDOverrideStaticPropertyOperation *opop_dst, IDOverrideStaticPropertyOperation *opop_src); + +static void bke_override_property_clear(IDOverrideStaticProperty *op); +static void bke_override_property_operation_clear(IDOverrideStaticPropertyOperation *opop); + +/** Initialize empty overriding of \a reference_id by \a local_id. */ +IDOverrideStatic *BKE_override_static_init(ID *local_id, ID *reference_id) +{ + /* If reference_id is NULL, we are creating an override template for purely local data. + * Else, reference *must* be linked data. */ + BLI_assert(reference_id == NULL || reference_id->lib != NULL); + BLI_assert(local_id->override_static == NULL); + + if (reference_id != NULL && reference_id->override_static != NULL && reference_id->override_static->reference == NULL) { + /* reference ID has an override template, use it! */ + BKE_override_static_copy(local_id, reference_id); + return local_id->override_static; + } + + /* Else, generate new empty override. */ + local_id->override_static = MEM_callocN(sizeof(*local_id->override_static), __func__); + local_id->override_static->reference = reference_id; + id_us_plus(reference_id); + local_id->tag &= ~LIB_TAG_OVERRIDESTATIC_OK; + /* TODO do we want to add tag or flag to referee to mark it as such? */ + return local_id->override_static; +} + +/** Deep copy of a whole override from \a src_id to \a dst_id. */ +void BKE_override_static_copy(ID *dst_id, const ID *src_id) +{ + BLI_assert(src_id->override_static != NULL); + + if (dst_id->override_static != NULL) { + if (src_id->override_static == NULL) { + BKE_override_static_free(&dst_id->override_static); + return; + } + else { + BKE_override_static_clear(dst_id->override_static); + } + } + else if (src_id->override_static == NULL) { + return; + } + else { + BKE_override_static_init(dst_id, NULL); + } + + /* Source is already overriding data, we copy it but reuse its reference for dest ID. + * otherwise, source is only an override template, it then becomes reference of dest ID. */ + dst_id->override_static->reference = src_id->override_static->reference ? src_id->override_static->reference : (ID *)src_id; + id_us_plus(dst_id->override_static->reference); + + BLI_duplicatelist(&dst_id->override_static->properties, &src_id->override_static->properties); + for (IDOverrideStaticProperty *op_dst = dst_id->override_static->properties.first, *op_src = src_id->override_static->properties.first; + op_dst; + op_dst = op_dst->next, op_src = op_src->next) + { + bke_override_property_copy(op_dst, op_src); + } + + dst_id->tag &= ~LIB_TAG_OVERRIDESTATIC_OK; +} + +/** Clear any overriding data from given \a override. */ +void BKE_override_static_clear(IDOverrideStatic *override) +{ + BLI_assert(override != NULL); + + for (IDOverrideStaticProperty *op = override->properties.first; op; op = op->next) { + bke_override_property_clear(op); + } + BLI_freelistN(&override->properties); + + id_us_min(override->reference); + /* override->storage should never be refcounted... */ +} + +/** Free given \a override. */ +void BKE_override_static_free(struct IDOverrideStatic **override) +{ + BLI_assert(*override != NULL); + + BKE_override_static_clear(*override); + MEM_freeN(*override); + *override = NULL; +} + +/** Create an overriden local copy of linked reference. */ +ID *BKE_override_static_create_from(Main *bmain, ID *reference_id) +{ + BLI_assert(reference_id != NULL); + BLI_assert(reference_id->lib != NULL); + + ID *local_id; + + if (!id_copy(bmain, reference_id, (ID **)&local_id, false)) { + return NULL; + } + id_us_min(local_id); + + BKE_override_static_init(local_id, reference_id); + local_id->flag |= LIB_OVERRIDE_STATIC_AUTO; + + /* Remapping, we obviously only want to affect local data (and not our own reference pointer to overriden ID). */ + BKE_libblock_remap(bmain, reference_id, local_id, ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_STATIC_OVERRIDE); + + return local_id; +} + +/** + * Find override property from given RNA path, if it exists. + */ +IDOverrideStaticProperty *BKE_override_static_property_find(IDOverrideStatic *override, const char *rna_path) +{ + /* XXX TODO we'll most likely want a runtime ghash to store that mapping at some point. */ + return BLI_findstring_ptr(&override->properties, rna_path, offsetof(IDOverrideStaticProperty, rna_path)); +} + +/** + * Find override property from given RNA path, or create it if it does not exist. + */ +IDOverrideStaticProperty *BKE_override_static_property_get(IDOverrideStatic *override, const char *rna_path, bool *r_created) +{ + /* XXX TODO we'll most likely want a runtime ghash to store taht mapping at some point. */ + IDOverrideStaticProperty *op = BKE_override_static_property_find(override, rna_path); + + if (op == NULL) { + op = MEM_callocN(sizeof(IDOverrideStaticProperty), __func__); + op->rna_path = BLI_strdup(rna_path); + BLI_addtail(&override->properties, op); + + if (r_created) { + *r_created = true; + } + } + else if (r_created) { + *r_created = false; + } + + return op; +} + +void bke_override_property_copy(IDOverrideStaticProperty *op_dst, IDOverrideStaticProperty *op_src) +{ + op_dst->rna_path = BLI_strdup(op_src->rna_path); + BLI_duplicatelist(&op_dst->operations, &op_src->operations); + + for (IDOverrideStaticPropertyOperation *opop_dst = op_dst->operations.first, *opop_src = op_src->operations.first; + opop_dst; + opop_dst = opop_dst->next, opop_src = opop_src->next) + { + bke_override_property_operation_copy(opop_dst, opop_src); + } +} + +void bke_override_property_clear(IDOverrideStaticProperty *op) +{ + BLI_assert(op->rna_path != NULL); + + MEM_freeN(op->rna_path); + + for (IDOverrideStaticPropertyOperation *opop = op->operations.first; opop; opop = opop->next) { + bke_override_property_operation_clear(opop); + } + BLI_freelistN(&op->operations); +} + +/** + * Remove and free given \a override_property from given ID \a override. + */ +void BKE_override_static_property_delete(IDOverrideStatic *override, IDOverrideStaticProperty *override_property) +{ + bke_override_property_clear(override_property); + BLI_freelinkN(&override->properties, override_property); +} + +/** + * Find override property operation from given sub-item(s), if it exists. + */ +IDOverrideStaticPropertyOperation *BKE_override_static_property_operation_find( + IDOverrideStaticProperty *override_property, + const char *subitem_refname, const char *subitem_locname, + const int subitem_refindex, const int subitem_locindex, const bool strict, bool *r_strict) +{ + IDOverrideStaticPropertyOperation *opop; + const int subitem_defindex = -1; + + if (r_strict) { + *r_strict = true; + } + + if (subitem_locname && + (opop = BLI_findstring_ptr(&override_property->operations, subitem_locname, + offsetof(IDOverrideStaticPropertyOperation, subitem_local_name)))) + { + return opop; + } + + if (subitem_refname && + (opop = BLI_findstring_ptr(&override_property->operations, subitem_refname, + offsetof(IDOverrideStaticPropertyOperation, subitem_reference_name)))) + { + return opop; + } + + if ((opop = BLI_listbase_bytes_find(&override_property->operations, &subitem_locindex, sizeof(subitem_locindex), + offsetof(IDOverrideStaticPropertyOperation, subitem_local_index)))) + { + return opop; + } + + if ((opop = BLI_listbase_bytes_find(&override_property->operations, &subitem_refindex, sizeof(subitem_refindex), + offsetof(IDOverrideStaticPropertyOperation, subitem_reference_index)))) + { + return opop; + } + + /* index == -1 means all indices, that is valid fallback in case we requested specific index. */ + if (!strict && (subitem_locindex != subitem_defindex) && + (opop = BLI_listbase_bytes_find(&override_property->operations, &subitem_defindex, sizeof(subitem_defindex), + offsetof(IDOverrideStaticPropertyOperation, subitem_local_index)))) + { + if (r_strict) { + *r_strict = false; + } + return opop; + } + + return NULL; +} + +/** + * Find override property operation from given sub-item(s), or create it if it does not exist. + */ +IDOverrideStaticPropertyOperation *BKE_override_static_property_operation_get( + IDOverrideStaticProperty *override_property, const short operation, + const char *subitem_refname, const char *subitem_locname, + const int subitem_refindex, const int subitem_locindex, + const bool strict, bool *r_strict, bool *r_created) +{ + IDOverrideStaticPropertyOperation *opop = BKE_override_static_property_operation_find(override_property, + subitem_refname, subitem_locname, + subitem_refindex, subitem_locindex, + strict, r_strict); + + if (opop == NULL) { + opop = MEM_callocN(sizeof(IDOverrideStaticPropertyOperation), __func__); + opop->operation = operation; + if (subitem_locname) { + opop->subitem_local_name = BLI_strdup(subitem_locname); + } + if (subitem_refname) { + opop->subitem_reference_name = BLI_strdup(subitem_refname); + } + opop->subitem_local_index = subitem_locindex; + opop->subitem_reference_index = subitem_refindex; + + BLI_addtail(&override_property->operations, opop); + + if (r_created) { + *r_created = true; + } + } + else if (r_created) { + *r_created = false; + } + + return opop; +} + +void bke_override_property_operation_copy(IDOverrideStaticPropertyOperation *opop_dst, IDOverrideStaticPropertyOperation *opop_src) +{ + if (opop_src->subitem_reference_name) { + opop_dst->subitem_reference_name = BLI_strdup(opop_src->subitem_reference_name); + } + if (opop_src->subitem_local_name) { + opop_dst->subitem_local_name = BLI_strdup(opop_src->subitem_local_name); + } +} + +void bke_override_property_operation_clear(IDOverrideStaticPropertyOperation *opop) +{ + if (opop->subitem_reference_name) { + MEM_freeN(opop->subitem_reference_name); + } + if (opop->subitem_local_name) { + MEM_freeN(opop->subitem_local_name); + } +} + +/** + * Remove and free given \a override_property_operation from given ID \a override_property. + */ +void BKE_override_static_property_operation_delete( + IDOverrideStaticProperty *override_property, IDOverrideStaticPropertyOperation *override_property_operation) +{ + bke_override_property_operation_clear(override_property_operation); + BLI_freelinkN(&override_property->operations, override_property_operation); +} + +/** + * Check that status of local data-block is still valid against current reference one. + * + * It means that all overridable, but not overridden, properties' local values must be equal to reference ones. + * Clears LIB_TAG_OVERRIDE_OK if they do not. + * + * This is typically used to detect whether some property has been changed in local and a new IDOverrideProperty + * (of IDOverridePropertyOperation) has to be added. + * + * \return true if status is OK, false otherwise. */ +bool BKE_override_static_status_check_local(ID *local) +{ + BLI_assert(local->override_static != NULL); + + ID *reference = local->override_static->reference; + + if (reference == NULL) { + /* This is an override template, local status is always OK! */ + return true; + } + + BLI_assert(GS(local->name) == GS(reference->name)); + + /* Note that reference is assumed always valid, caller has to ensure that itself. */ + + PointerRNA rnaptr_local, rnaptr_reference; + RNA_id_pointer_create(local, &rnaptr_local); + RNA_id_pointer_create(reference, &rnaptr_reference); + +// if (!RNA_struct_override_matches(&rnaptr_local, &rnaptr_reference, local->override_static, true, true)) { +// local->tag &= ~LIB_TAG_OVERRIDESTATIC_OK; +// return false; +// } + + return true; +} + +/** + * Check that status of reference data-block is still valid against current local one. + * + * It means that all non-overridden properties' local values must be equal to reference ones. + * Clears LIB_TAG_OVERRIDE_OK if they do not. + * + * This is typically used to detect whether some reference has changed and local needs to be updated against it. + * + * \return true if status is OK, false otherwise. */ +bool BKE_override_static_status_check_reference(ID *local) +{ + BLI_assert(local->override_static != NULL); + + ID *reference = local->override_static->reference; + + if (reference == NULL) { + /* This is an override template, reference is virtual, so its status is always OK! */ + return true; + } + + BLI_assert(GS(local->name) == GS(reference->name)); + + if (reference->override_static && (reference->tag & LIB_TAG_OVERRIDESTATIC_OK) == 0) { + if (!BKE_override_static_status_check_reference(reference)) { + /* If reference is also override of another data-block, and its status is not OK, + * then this override is not OK either. + * Note that this should only happen when reloading libraries... */ + local->tag &= ~LIB_TAG_OVERRIDESTATIC_OK; + return false; + } + } + + PointerRNA rnaptr_local, rnaptr_reference; + RNA_id_pointer_create(local, &rnaptr_local); + RNA_id_pointer_create(reference, &rnaptr_reference); + +// if (!RNA_struct_override_matches(&rnaptr_local, &rnaptr_reference, local->override_static, false, true)) { +// local->tag &= ~LIB_TAG_OVERRIDESTATIC_OK; +// return false; +// } + + return true; +} + +/** + * Compares local and reference data-blocks and create new override operations as needed, + * or reset to reference values if overriding is not allowed. + * + * \note Defining override operations is only mandatory before saving a .blend file on disk (not for undo!). + * Knowing that info at runtime is only useful for UI/UX feedback. + * + * \note This is by far the biggest operation (the more time-consuming) of the three so far, since it has to go over + * all properties in depth (all overridable ones at least). Generating diff values and applying overrides + * are much cheaper. + * + * \return true is new overriding op was created, or some local data was reset. */ +bool BKE_override_static_operations_create(ID *local) +{ + BLI_assert(local->override_static != NULL); + const bool is_template = (local->override_static->reference == NULL); + bool ret = false; + + if (!is_template && local->flag & LIB_OVERRIDE_STATIC_AUTO) { + PointerRNA rnaptr_local, rnaptr_reference; + RNA_id_pointer_create(local, &rnaptr_local); + RNA_id_pointer_create(local->override_static->reference, &rnaptr_reference); + +// ret = RNA_struct_auto_override(&rnaptr_local, &rnaptr_reference, local->override_static, NULL); +#ifndef NDEBUG + if (ret) { + printf("We did generate static override rules for %s\n", local->name); + } + else { + printf("No new static override rules for %s\n", local->name); + } +#endif + } + return ret; +} + +/** Check all overrides from given \a bmain and create/update overriding operations as needed. */ +void BKE_main_override_static_operations_create(Main *bmain) +{ + ListBase *lbarray[MAX_LIBARRAY]; + int base_count, i; + + base_count = set_listbasepointers(bmain, lbarray); + + for (i = 0; i < base_count; i++) { + ListBase *lb = lbarray[i]; + ID *id; + + for (id = lb->first; id; id = id->next) { + /* TODO Maybe we could also add an 'override update' tag e.g. when tagging for DEG update? */ + if (id->lib == NULL && id->override_static != NULL && id->override_static->reference != NULL && (id->flag & LIB_OVERRIDE_STATIC_AUTO)) { + BKE_override_static_operations_create(id); + } + } + } +} + +/** Update given override from its reference (re-applying overriden properties). */ +void BKE_override_static_update(Main *bmain, ID *local) +{ + if (local->override_static == NULL || local->override_static->reference == NULL) { + return; + } + + /* Recursively do 'ancestors' overrides first, if any. */ + if (local->override_static->reference->override_static && (local->override_static->reference->tag & LIB_TAG_OVERRIDESTATIC_OK) == 0) { + BKE_override_static_update(bmain, local->override_static->reference); + } + + /* We want to avoid having to remap here, however creating up-to-date override is much simpler if based + * on reference than on current override. + * So we work on temp copy of reference, and 'swap' its content with local. */ + + /* XXX We need a way to get off-Main copies of IDs (similar to localized mats/texts/ etc.)! + * However, this is whole bunch of code work in itself, so for now plain stupid ID copy will do, + * as innefficient as it is. :/ + * Actually, maybe not! Since we are swapping with original ID's local content, we want to keep + * usercount in correct state when freeing tmp_id (and that usercounts of IDs used by 'new' local data + * also remain correct). */ + /* This would imply change in handling of usercout all over RNA (and possibly all over Blender code). + * Not impossible to do, but would rather see first if extra useless usual user handling is actually + * a (performances) issue here. */ + + ID *tmp_id; + id_copy(bmain, local->override_static->reference, &tmp_id, false); + + if (tmp_id == NULL) { + return; + } + + PointerRNA rnaptr_src, rnaptr_dst, rnaptr_storage_stack, *rnaptr_storage = NULL; + RNA_id_pointer_create(local, &rnaptr_src); + RNA_id_pointer_create(tmp_id, &rnaptr_dst); + if (local->override_static->storage) { + rnaptr_storage = &rnaptr_storage_stack; + RNA_id_pointer_create(local->override_static->storage, rnaptr_storage); + } + +// RNA_struct_override_apply(&rnaptr_dst, &rnaptr_src, rnaptr_storage, local->override_static); + + /* This also transfers all pointers (memory) owned by local to tmp_id, and vice-versa. So when we'll free tmp_id, + * we'll actually free old, outdated data from local. */ + BKE_id_swap(bmain, local, tmp_id); + + /* Again, horribly innefficient in our case, we need something off-Main (aka moar generic nolib copy/free stuff)! */ + /* XXX And crashing in complex cases (e.g. because depsgraph uses same data...). */ + BKE_libblock_free_ex(bmain, tmp_id, true, false); + + if (local->override_static->storage) { + /* We know this datablock is not used anywhere besides local->override->storage. */ + /* XXX For until we get fully shadow copies, we still need to ensure storage releases + * its usage of any ID pointers it may have. */ + BKE_libblock_free_ex(bmain, local->override_static->storage, true, false); + local->override_static->storage = NULL; + } + + local->tag |= LIB_TAG_OVERRIDESTATIC_OK; + + /* Full rebuild of Depsgraph! */ + DEG_on_visible_update(bmain, true); /* XXX Is this actual valid replacement for old DAG_relations_tag_update(bmain) ? */ +} + +/** Update all overrides from given \a bmain. */ +void BKE_main_override_static_update(Main *bmain) +{ + ListBase *lbarray[MAX_LIBARRAY]; + int base_count, i; + + base_count = set_listbasepointers(bmain, lbarray); + + for (i = 0; i < base_count; i++) { + ListBase *lb = lbarray[i]; + ID *id; + + for (id = lb->first; id; id = id->next) { + if (id->override_static != NULL && id->lib == NULL) { + BKE_override_static_update(bmain, id); + } + } + } +} + +/*********************************************************************************************************************** + * Storage (how to wtore overriding data into .blend files). + * + * Basically: + * I) Only 'differential' storage needs special handling here. All others (replacing values or + * inserting/removing items from a collection) can be handled with simply storing current content of local data-block. + * II) We store the differential value into a second 'ghost' data-block, which is an empty ID of same type as local one, + * where we only define values that need differential data. + * + * This avoids us having to modify 'real' data-block at write time (and retoring it afterwards), which is inneficient, + * and potentially dangerous (in case of concurrent access...), while not using much extra memory in typical cases. + * It also ensures stored data-block always contains exact same data as "desired" ones (kind of "baked" data-blocks). + */ + +/** Initialize an override storage. */ +OverrideStaticStorage *BKE_override_static_operations_store_initialize(void) +{ + return BKE_main_new(); +} + +/** + * Generate suitable 'write' data (this only affects differential override operations). + * + * Note that \a local ID is no more modified by this call, all extra data are stored in its temp \a storage_id copy. */ +ID *BKE_override_static_operations_store_start(OverrideStaticStorage *override_storage, ID *local) +{ + BLI_assert(local->override_static != NULL); + BLI_assert(override_storage != NULL); + const bool is_template = (local->override_static->reference == NULL); + + if (is_template) { + /* This is actually purely local data with an override template, nothing to do here! */ + return NULL; + } + + /* Forcefully ensure we know about all needed override operations. */ + BKE_override_static_operations_create(local); + + ID *storage_id; +#ifdef DEBUG_OVERRIDE_TIMEIT + TIMEIT_START_AVERAGED(BKE_override_operations_store_start); +#endif + + /* XXX TODO We may also want a specialized handling of things here too, to avoid copying heavy never-overridable + * data (like Mesh geometry etc.)? And also maybe avoid lib refcounting completely (shallow copy...). */ + /* This would imply change in handling of usercout all over RNA (and possibly all over Blender code). + * Not impossible to do, but would rather see first is extra useless usual user handling is actually + * a (performances) issue here, before doing it. */ + id_copy((Main *)override_storage, local, &storage_id, false); + + if (storage_id != NULL) { + PointerRNA rnaptr_reference, rnaptr_final, rnaptr_storage; + RNA_id_pointer_create(local->override_static->reference, &rnaptr_reference); + RNA_id_pointer_create(local, &rnaptr_final); + RNA_id_pointer_create(storage_id, &rnaptr_storage); + +// if (!RNA_struct_override_store(&rnaptr_final, &rnaptr_reference, &rnaptr_storage, local->override_static)) { +// BKE_libblock_free_ex(override_storage, storage_id, true, false); +// storage_id = NULL; +// } + } + + local->override_static->storage = storage_id; + +#ifdef DEBUG_OVERRIDE_TIMEIT + TIMEIT_END_AVERAGED(BKE_override_operations_store_start); +#endif + return storage_id; +} + +/** Restore given ID modified by \a BKE_override_operations_store_start, to its original state. */ +void BKE_override_static_operations_store_end(OverrideStaticStorage *UNUSED(override_storage), ID *local) +{ + BLI_assert(local->override_static != NULL); + + /* Nothing else to do here really, we need to keep all temp override storage data-blocks in memory until + * whole file is written anyway (otherwise we'd get mem pointers overlap...). */ + local->override_static->storage = NULL; +} + +void BKE_override_static_operations_store_finalize(OverrideStaticStorage *override_storage) +{ + /* We cannot just call BKE_main_free(override_storage), not until we have option to make 'ghost' copies of IDs + * without increasing usercount of used data-blocks... */ + ListBase *lbarray[MAX_LIBARRAY]; + int base_count, i; + + base_count = set_listbasepointers(override_storage, lbarray); + + for (i = 0; i < base_count; i++) { + ListBase *lb = lbarray[i]; + ID *id; + + while ((id = lb->first)) { + BKE_libblock_free_ex(override_storage, id, true, false); + } + } + + BKE_main_free(override_storage); +} diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c index 93bc4be7954..c0698849045 100644 --- a/source/blender/blenkernel/intern/library_query.c +++ b/source/blender/blenkernel/intern/library_query.c @@ -373,6 +373,10 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call #define CALLBACK_INVOKE(check_id_super, cb_flag) \ FOREACH_CALLBACK_INVOKE(&data, check_id_super, cb_flag) + if (id->override_static != NULL) { + CALLBACK_INVOKE_ID(id->override_static->reference, IDWALK_CB_USER | IDWALK_CB_STATIC_OVERRIDE_REFERENCE); + } + for (; id != NULL; id = (flag & IDWALK_RECURSE) ? BLI_LINKSTACK_POP(data.ids_todo) : NULL) { data.self_id = id; data.cb_flag = ID_IS_LINKED(id) ? IDWALK_CB_INDIRECT_USAGE : 0; diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c index e897f3c5330..a3376dff98c 100644 --- a/source/blender/blenkernel/intern/library_remap.c +++ b/source/blender/blenkernel/intern/library_remap.c @@ -87,6 +87,7 @@ #include "BKE_lattice.h" #include "BKE_layer.h" #include "BKE_library.h" +#include "BKE_library_override.h" #include "BKE_library_query.h" #include "BKE_library_remap.h" #include "BKE_linestyle.h" @@ -181,6 +182,7 @@ static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id } if (*id_p && (*id_p == old_id)) { + const bool is_reference = (cb_flag & IDWALK_CB_STATIC_OVERRIDE_REFERENCE) != 0; const bool is_indirect = (cb_flag & IDWALK_CB_INDIRECT_USAGE) != 0; const bool skip_indirect = (id_remap_data->flag & ID_REMAP_SKIP_INDIRECT_USAGE) != 0; /* Note: proxy usage implies LIB_TAG_EXTERN, so on this aspect it is direct, @@ -191,6 +193,7 @@ static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id const bool is_obj_editmode = (is_obj && BKE_object_is_in_editmode((Object *)id)); const bool is_never_null = ((cb_flag & IDWALK_CB_NEVER_NULL) && (new_id == NULL) && (id_remap_data->flag & ID_REMAP_FORCE_NEVER_NULL_USAGE) == 0); + const bool skip_reference = (id_remap_data->flag & ID_REMAP_SKIP_STATIC_OVERRIDE) != 0; const bool skip_never_null = (id_remap_data->flag & ID_REMAP_SKIP_NEVER_NULL_USAGE) != 0; #ifdef DEBUG_PRINT @@ -205,7 +208,8 @@ static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id /* Special hack in case it's Object->data and we are in edit mode (skipped_direct too). */ if ((is_never_null && skip_never_null) || (is_obj_editmode && (((Object *)id)->data == *id_p)) || - (skip_indirect && is_indirect)) + (skip_indirect && is_indirect) || + (is_reference && skip_reference)) { if (is_indirect) { id_remap_data->skipped_indirect++; @@ -218,7 +222,7 @@ static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id } } } - else if (is_never_null || is_obj_editmode) { + else if (is_never_null || is_obj_editmode || is_reference) { id_remap_data->skipped_direct++; } else { @@ -742,6 +746,10 @@ void BKE_libblock_free_data(ID *id, const bool do_id_user) MEM_freeN(id->properties); } + if (id->override_static) { + BKE_override_static_free(&id->override_static); + } + /* XXX TODO remove animdata handling from each type's freeing func, and do it here, like for copy! */ } diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 57dab868752..2d885fcec17 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -136,6 +136,7 @@ #include "BKE_layer.h" #include "BKE_library.h" // for which_libbase #include "BKE_library_idmap.h" +#include "BKE_library_override.h" #include "BKE_library_query.h" #include "BKE_idcode.h" #include "BKE_idprop.h" @@ -2214,6 +2215,42 @@ static PreviewImage *direct_link_preview_image(FileData *fd, PreviewImage *old_p /* ************ READ ID *************** */ +static void lib_link_id(FileData *fd, Main *main) +{ + ListBase *lbarray[MAX_LIBARRAY]; + int base_count, i; + + base_count = set_listbasepointers(main, lbarray); + + for (i = 0; i < base_count; i++) { + ListBase *lb = lbarray[i]; + ID *id; + + for (id = lb->first; id; id = id->next) { + if (id->override_static) { + id->override_static->reference = newlibadr_us(fd, id->lib, id->override_static->reference); + id->override_static->storage = newlibadr_us(fd, id->lib, id->override_static->storage); + } + } + } +} + +static void direct_link_id_override_property_operation_cb(FileData *fd, void *data) +{ + IDOverrideStaticPropertyOperation *opop = data; + + opop->subitem_reference_name = newdataadr(fd, opop->subitem_reference_name); + opop->subitem_local_name = newdataadr(fd, opop->subitem_local_name); +} + +static void direct_link_id_override_property_cb(FileData *fd, void *data) +{ + IDOverrideStaticProperty *op = data; + + op->rna_path = newdataadr(fd, op->rna_path); + link_list_ex(fd, &op->operations, direct_link_id_override_property_operation_cb); +} + static void direct_link_id(FileData *fd, ID *id) { /*link direct data of ID properties*/ @@ -2223,6 +2260,12 @@ static void direct_link_id(FileData *fd, ID *id) IDP_DirectLinkGroup_OrFree(&id->properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); } id->py_instance = NULL; + + /* Link direct data of overrides. */ + if (id->override_static) { + id->override_static = newdataadr(fd, id->override_static); + link_list_ex(fd, &id->override_static->properties, direct_link_id_override_property_cb); + } } /* ************ READ CurveMapping *************** */ @@ -8683,6 +8726,8 @@ static void lib_link_all(FileData *fd, Main *main) { oldnewmap_sort(fd); + lib_link_id(fd, main); + /* No load UI for undo memfiles */ if (fd->memfile == NULL) { lib_link_windowmanager(fd, main); @@ -8908,6 +8953,12 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) BKE_main_id_tag_all(bfd->main, LIB_TAG_NEW, false); + /* Now that all our data-blocks are loaded, we can re-generate overrides from their references. */ + if (fd->memfile == NULL) { + /* Do not apply in undo case! */ + BKE_main_override_static_update(bfd->main); + } + lib_verify_nodetree(bfd->main, true); fix_relpaths_library(fd->relabase, bfd->main); /* make all relative paths, relative to the open blend file */ diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 6c39b0d486a..30ca75d7da4 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -170,6 +170,7 @@ #include "BKE_global.h" // for G #include "BKE_idcode.h" #include "BKE_library.h" // for set_listbasepointers +#include "BKE_library_override.h" #include "BKE_main.h" #include "BKE_node.h" #include "BKE_report.h" @@ -683,6 +684,25 @@ static void write_iddata(void *wd, const ID *id) if (id->properties && !ELEM(GS(id->name), ID_WM)) { IDP_WriteProperty(id->properties, wd); } + + if (id->override_static) { + writestruct(wd, DATA, IDOverrideStatic, 1, id->override_static); + + writelist(wd, DATA, IDOverrideStaticProperty, &id->override_static->properties); + for (IDOverrideStaticProperty *op = id->override_static->properties.first; op; op = op->next) { + writedata(wd, DATA, strlen(op->rna_path) + 1, op->rna_path); + + writelist(wd, DATA, IDOverrideStaticPropertyOperation, &op->operations); + for (IDOverrideStaticPropertyOperation *opop = op->operations.first; opop; opop = opop->next) { + if (opop->subitem_reference_name) { + writedata(wd, DATA, strlen(opop->subitem_reference_name) + 1, opop->subitem_reference_name); + } + if (opop->subitem_local_name) { + writedata(wd, DATA, strlen(opop->subitem_local_name) + 1, opop->subitem_local_name); + } + } + } + } } static void write_previews(WriteData *wd, const PreviewImage *prv_orig) @@ -3789,6 +3809,8 @@ static void write_libraries(WriteData *wd, Main *main) /* XXX needs rethink, just like save UI in undo files now - would be nice to append things only for the] * quit.blend and temp saves */ if (found_one) { + /* Not overridable. */ + writestruct(wd, ID_LI, Library, 1, main->curlib); write_iddata(wd, &main->curlib->id); @@ -3923,137 +3945,159 @@ static bool write_file_handle( * avoid thumbnail detecting changes because of this. */ mywrite_flush(wd); - ListBase *lbarray[MAX_LIBARRAY]; - int a = set_listbasepointers(mainvar, lbarray); - while (a--) { - ID *id = lbarray[a]->first; + OverrideStaticStorage *override_storage = !wd->current ? BKE_override_static_operations_store_initialize() : NULL; - if (id && GS(id->name) == ID_LI) { - continue; /* Libraries are handled separately below. */ - } + /* This outer loop allows to save first datablocks from real mainvar, then the temp ones from override process, + * if needed, without duplicating whole code. */ + Main *bmain = mainvar; + do { + ListBase *lbarray[MAX_LIBARRAY]; + int a = set_listbasepointers(bmain, lbarray); + while (a--) { + ID *id = lbarray[a]->first; - for (; id; id = id->next) { - /* We should never attempt to write non-regular IDs (i.e. all kind of temp/runtime ones). */ - BLI_assert((id->tag & (LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT | LIB_TAG_NOT_ALLOCATED)) == 0); + if (id && GS(id->name) == ID_LI) { + continue; /* Libraries are handled separately below. */ + } - switch ((ID_Type)GS(id->name)) { - case ID_WM: - write_windowmanager(wd, (wmWindowManager *)id); - break; - case ID_WS: - write_workspace(wd, (WorkSpace *)id); - break; - case ID_SCR: - write_screen(wd, (bScreen *)id); - break; - case ID_MC: - write_movieclip(wd, (MovieClip *)id); - break; - case ID_MSK: - write_mask(wd, (Mask *)id); - break; - case ID_SCE: - write_scene(wd, (Scene *)id); - break; - case ID_CU: - write_curve(wd, (Curve *)id); - break; - case ID_MB: - write_mball(wd, (MetaBall *)id); - break; - case ID_IM: - write_image(wd, (Image *)id); - break; - case ID_CA: - write_camera(wd, (Camera *)id); - break; - case ID_LA: - write_lamp(wd, (Lamp *)id); - break; - case ID_LT: - write_lattice(wd, (Lattice *)id); - break; - case ID_VF: - write_vfont(wd, (VFont *)id); - break; - case ID_KE: - write_key(wd, (Key *)id); - break; - case ID_WO: - write_world(wd, (World *)id); - break; - case ID_TXT: - write_text(wd, (Text *)id); - break; - case ID_SPK: - write_speaker(wd, (Speaker *)id); - break; - case ID_LP: - write_probe(wd, (LightProbe *)id); - break; - case ID_SO: - write_sound(wd, (bSound *)id); - break; - case ID_GR: - write_group(wd, (Group *)id); - break; - case ID_AR: - write_armature(wd, (bArmature *)id); - break; - case ID_AC: - write_action(wd, (bAction *)id); - break; - case ID_OB: - write_object(wd, (Object *)id); - break; - case ID_MA: - write_material(wd, (Material *)id); - break; - case ID_TE: - write_texture(wd, (Tex *)id); - break; - case ID_ME: - write_mesh(wd, (Mesh *)id); - break; - case ID_PA: - write_particlesettings(wd, (ParticleSettings *)id); - break; - case ID_NT: - write_nodetree(wd, (bNodeTree *)id); - break; - case ID_BR: - write_brush(wd, (Brush *)id); - break; - case ID_PAL: - write_palette(wd, (Palette *)id); - break; - case ID_PC: - write_paintcurve(wd, (PaintCurve *)id); - break; - case ID_GD: - write_gpencil(wd, (bGPdata *)id); - break; - case ID_LS: - write_linestyle(wd, (FreestyleLineStyle *)id); - break; - case ID_CF: - write_cachefile(wd, (CacheFile *)id); - break; - case ID_LI: - /* Do nothing, handled below - and should never be reached. */ - BLI_assert(0); - break; - case ID_IP: - /* Do nothing, deprecated. */ - break; - default: - /* Should never be reached. */ - BLI_assert(0); - break; + for (; id; id = id->next) { + /* We should never attempt to write non-regular IDs (i.e. all kind of temp/runtime ones). */ + BLI_assert((id->tag & (LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT | LIB_TAG_NOT_ALLOCATED)) == 0); + + const bool do_override = !ELEM(override_storage, NULL, bmain) && id->override_static; + + if (do_override) { + BKE_override_static_operations_store_start(override_storage, id); + } + + switch ((ID_Type)GS(id->name)) { + case ID_WM: + write_windowmanager(wd, (wmWindowManager *)id); + break; + case ID_WS: + write_workspace(wd, (WorkSpace *)id); + break; + case ID_SCR: + write_screen(wd, (bScreen *)id); + break; + case ID_MC: + write_movieclip(wd, (MovieClip *)id); + break; + case ID_MSK: + write_mask(wd, (Mask *)id); + break; + case ID_SCE: + write_scene(wd, (Scene *)id); + break; + case ID_CU: + write_curve(wd,(Curve *)id); + break; + case ID_MB: + write_mball(wd, (MetaBall *)id); + break; + case ID_IM: + write_image(wd, (Image *)id); + break; + case ID_CA: + write_camera(wd, (Camera *)id); + break; + case ID_LA: + write_lamp(wd, (Lamp *)id); + break; + case ID_LT: + write_lattice(wd, (Lattice *)id); + break; + case ID_VF: + write_vfont(wd, (VFont *)id); + break; + case ID_KE: + write_key(wd, (Key *)id); + break; + case ID_WO: + write_world(wd, (World *)id); + break; + case ID_TXT: + write_text(wd, (Text *)id); + break; + case ID_SPK: + write_speaker(wd, (Speaker *)id); + break; + case ID_LP: + write_probe(wd, (LightProbe *)id); + break; + case ID_SO: + write_sound(wd, (bSound *)id); + break; + case ID_GR: + write_group(wd, (Group *)id); + break; + case ID_AR: + write_armature(wd, (bArmature *)id); + break; + case ID_AC: + write_action(wd, (bAction *)id); + break; + case ID_OB: + write_object(wd, (Object *)id); + break; + case ID_MA: + write_material(wd, (Material *)id); + break; + case ID_TE: + write_texture(wd, (Tex *)id); + break; + case ID_ME: + write_mesh(wd, (Mesh *)id); + break; + case ID_PA: + write_particlesettings(wd, (ParticleSettings *)id); + break; + case ID_NT: + write_nodetree(wd, (bNodeTree *)id); + break; + case ID_BR: + write_brush(wd, (Brush *)id); + break; + case ID_PAL: + write_palette(wd, (Palette *)id); + break; + case ID_PC: + write_paintcurve(wd, (PaintCurve *)id); + break; + case ID_GD: + write_gpencil(wd, (bGPdata *)id); + break; + case ID_LS: + write_linestyle(wd, (FreestyleLineStyle *)id); + break; + case ID_CF: + write_cachefile(wd, (CacheFile *)id); + break; + case ID_LI: + /* Do nothing, handled below - and should never be reached. */ + BLI_assert(0); + break; + case ID_IP: + /* Do nothing, deprecated. */ + break; + default: + /* Should never be reached. */ + BLI_assert(0); + break; + } + + if (do_override) { + BKE_override_static_operations_store_end(override_storage, id); + } } + + mywrite_flush(wd); } + } while ((bmain != override_storage) && (bmain = override_storage)); - mywrite_flush(wd); + if (override_storage) { + BKE_override_static_operations_store_finalize(override_storage); + override_storage = NULL; } /* Special handling, operating over split Mains... */ diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 30d56a3d91d..4e1161148ea 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -115,6 +115,76 @@ enum { /* add any future new id property types here.*/ + +/* Static ID override structs. */ + +typedef struct IDOverrideStaticPropertyOperation { + struct IDOverrideStaticPropertyOperation *next, *prev; + + /* Type of override. */ + short operation; + short flag; + short pad_s1[2]; + + /* Sub-item references, if needed (for arrays or collections only). + * We need both reference and local values to allow e.g. insertion into collections (constraints, modifiers...). + * In collection case, if names are defined, they are used in priority. + * Names are pointers (instead of char[64]) to save some space, NULL when unset. + * Indices are -1 when unset. */ + char *subitem_reference_name; + char *subitem_local_name; + int subitem_reference_index; + int subitem_local_index; +} IDOverrideStaticPropertyOperation; + +/* IDOverridePropertyOperation->operation. */ +enum { + /* Basic operations. */ + IDOVERRIDESTATIC_OP_NOOP = 0, /* Special value, forbids any overriding. */ + + IDOVERRIDESTATIC_OP_REPLACE = 1, /* Fully replace local value by reference one. */ + + /* Numeric-only operations. */ + IDOVERRIDESTATIC_OP_ADD = 101, /* Add local value to reference one. */ + /* Subtract local value from reference one (needed due to unsigned values etc.). */ + IDOVERRIDESTATIC_OP_SUBTRACT = 102, + /* Multiply reference value by local one (more useful than diff for scales and the like). */ + IDOVERRIDESTATIC_OP_MULTIPLY = 103, + + /* Collection-only operations. */ + IDOVERRIDESTATIC_OP_INSERT_AFTER = 201, /* Insert after given reference's subitem. */ + IDOVERRIDESTATIC_OP_INSERT_BEFORE = 202, /* Insert before given reference's subitem. */ + /* We can add more if needed (move, delete, ...). */ +}; + +/* IDOverridePropertyOperation->flag. */ +enum { + IDOVERRIDESTATIC_FLAG_MANDATORY = 1 << 0, /* User cannot remove that override operation. */ + IDOVERRIDESTATIC_FLAG_LOCKED = 1 << 1, /* User cannot change that override operation. */ +}; + +/* A single overriden property, contain all operations on this one. */ +typedef struct IDOverrideStaticProperty { + struct IDOverrideStaticProperty *next, *prev; + + /* Path from ID to overridden property. *Does not* include indices/names for final arrays/collections items. */ + char *rna_path; + + ListBase operations; /* List of overriding operations (IDOverridePropertyOperation) applied to this property. */ +} IDOverrideStaticProperty; + +/* Main container for all overriding data info of a data-block. */ +typedef struct IDOverrideStatic { + struct ID *reference; /* Reference linked ID which this one overrides. */ + ListBase properties; /* List of IDOverrideProperty structs. */ + + /* Read/write data. */ + /* Temp ID storing extra override data (used for differential operations only currently). + * Always NULL outside of read/write context. */ + struct ID *storage; +} IDOverrideStatic; + + /* watch it: Sequence has identical beginning. */ /** * ID is the first thing included in all serializable types. It @@ -142,7 +212,12 @@ typedef struct ID { int us; int icon_id; IDProperty *properties; + + IDOverrideStatic *override_static; /* Reference linked ID which this one overrides. */ + void *py_instance; + + void *pad1; } ID; /** @@ -309,7 +384,8 @@ typedef enum ID_Type { /* id->flag (persitent). */ enum { - LIB_FAKEUSER = 1 << 9, + LIB_OVERRIDE_STATIC_AUTO = 1 << 0, /* Allow automatic generation of overriding rules. */ + LIB_FAKEUSER = 1 << 9, }; /** @@ -344,6 +420,9 @@ enum { /* RESET_NEVER tag datablock as a place-holder (because the real one could not be linked from its library e.g.). */ LIB_TAG_MISSING = 1 << 6, + /* RESET_NEVER tag datablock as being up-to-date regarding its reference. */ + LIB_TAG_OVERRIDESTATIC_OK = 1 << 9, + /* tag datablock has having an extra user. */ LIB_TAG_EXTRAUSER = 1 << 2, /* tag datablock has having actually increased usercount for the extra virtual user. */ -- cgit v1.2.3