From 638afb9bd428fb697fb5136e8bb5f31b05458db7 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 29 Nov 2017 15:31:31 +0100 Subject: ID Static Override, part II: RNA changes. This is essentially a huge refactor/extension of our existing RNA compare & copy code, since static override needs more advanced handling here. Note that not all new features are implemented yet, advanced things like collections insertion/deletion are still TODO (medium priority). This completes the ground work for overrides, remaining commits will be about UI and some basic/testing activation of overrides for a limited set of data-blocks & properties. For details see https://developer.blender.org/D2417 --- .../blender/blenkernel/intern/library_override.c | 28 +- source/blender/makesrna/RNA_access.h | 57 +- source/blender/makesrna/RNA_define.h | 2 + source/blender/makesrna/RNA_types.h | 5 +- source/blender/makesrna/intern/makesrna.c | 7 +- source/blender/makesrna/intern/rna_ID.c | 30 + source/blender/makesrna/intern/rna_access.c | 694 +++++++++----- source/blender/makesrna/intern/rna_define.c | 31 + source/blender/makesrna/intern/rna_internal.h | 36 + .../blender/makesrna/intern/rna_internal_types.h | 62 ++ source/blender/makesrna/intern/rna_rna.c | 1009 ++++++++++++++++++++ 11 files changed, 1712 insertions(+), 249 deletions(-) (limited to 'source') diff --git a/source/blender/blenkernel/intern/library_override.c b/source/blender/blenkernel/intern/library_override.c index bc69b085e57..22896cff64a 100644 --- a/source/blender/blenkernel/intern/library_override.c +++ b/source/blender/blenkernel/intern/library_override.c @@ -384,10 +384,10 @@ bool BKE_override_static_status_check_local(ID *local) 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; -// } + if (!RNA_struct_override_matches(&rnaptr_local, &rnaptr_reference, local->override_static, true, true)) { + local->tag &= ~LIB_TAG_OVERRIDESTATIC_OK; + return false; + } return true; } @@ -428,10 +428,10 @@ bool BKE_override_static_status_check_reference(ID *local) 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; -// } + if (!RNA_struct_override_matches(&rnaptr_local, &rnaptr_reference, local->override_static, false, true)) { + local->tag &= ~LIB_TAG_OVERRIDESTATIC_OK; + return false; + } return true; } @@ -459,7 +459,7 @@ bool BKE_override_static_operations_create(ID *local) 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); + 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); @@ -534,7 +534,7 @@ void BKE_override_static_update(Main *bmain, ID *local) RNA_id_pointer_create(local->override_static->storage, rnaptr_storage); } -// RNA_struct_override_apply(&rnaptr_dst, &rnaptr_src, rnaptr_storage, local->override_static); + 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. */ @@ -634,10 +634,10 @@ ID *BKE_override_static_operations_store_start(OverrideStaticStorage *override_s 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; -// } + 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; diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 60c2480a2ff..cab6e938048 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -39,6 +39,9 @@ extern "C" { struct bContext; struct ID; +struct IDOverrideStatic; +struct IDOverrideStaticProperty; +struct IDOverrideStaticPropertyOperation; struct ListBase; struct Main; struct ReportList; @@ -1042,7 +1045,7 @@ char *RNA_path_property_py(struct PointerRNA *ptr, struct PropertyRNA *prop, int * call RNA_struct_find_property. The names have to exist as RNA properties * for the type in the pointer, if they do not exist an error will be printed. * - * There is no support for pointers and collections here yet, these can be + * There is no support for pointers and collections here yet, these can be * added when ID properties support them. */ int RNA_boolean_get(PointerRNA *ptr, const char *name); @@ -1234,17 +1237,51 @@ StructRNA *ID_code_to_RNA_type(short idcode); void _RNA_warning(const char *format, ...) ATTR_PRINTF_FORMAT(1, 2); -/* Equals test (skips pointers and collections) - * is_strict false assumes uninitialized properties are equal */ +/* Equals test. */ -typedef enum eRNAEqualsMode { - RNA_EQ_STRICT, /* set/unset ignored */ - RNA_EQ_UNSET_MATCH_ANY, /* unset property matches anything */ - RNA_EQ_UNSET_MATCH_NONE /* unset property never matches set property */ -} eRNAEqualsMode; +/* Note: In practice, EQ_STRICT and EQ_COMPARE have same behavior currently, and will yield same result. */ +typedef enum eRNACompareMode { + /* Only care about equality, not full comparison. */ + RNA_EQ_STRICT, /* set/unset ignored */ + RNA_EQ_UNSET_MATCH_ANY, /* unset property matches anything */ + RNA_EQ_UNSET_MATCH_NONE, /* unset property never matches set property */ + /* Full comparison. */ + RNA_EQ_COMPARE, +} eRNACompareMode; -bool RNA_property_equals(struct PointerRNA *a, struct PointerRNA *b, struct PropertyRNA *prop, eRNAEqualsMode mode); -bool RNA_struct_equals(struct PointerRNA *a, struct PointerRNA *b, eRNAEqualsMode mode); +bool RNA_property_equals(struct PointerRNA *a, struct PointerRNA *b, struct PropertyRNA *prop, eRNACompareMode mode); +bool RNA_struct_equals(struct PointerRNA *a, struct PointerRNA *b, eRNACompareMode mode); + +/* Override. */ + +bool RNA_struct_override_matches(struct PointerRNA *local, struct PointerRNA *reference, + struct IDOverrideStatic *override, const bool ignore_non_overridable, const bool ignore_overridden); + +bool RNA_struct_override_store( + struct PointerRNA *local, struct PointerRNA *reference, PointerRNA *storage, struct IDOverrideStatic *override); + +void RNA_property_override_apply( + struct PointerRNA *dst, struct PointerRNA *src, struct PointerRNA *storage, struct PropertyRNA *prop, + struct IDOverrideStaticProperty *op); +void RNA_struct_override_apply( + struct PointerRNA *dst, struct PointerRNA *src, struct PointerRNA *storage, + struct IDOverrideStatic *override); + +bool RNA_struct_auto_override( + struct PointerRNA *local, struct PointerRNA *reference, struct IDOverrideStatic *override, const char *root_path); + +struct IDOverrideStaticProperty *RNA_property_override_property_find(PointerRNA *ptr, PropertyRNA *prop); +struct IDOverrideStaticProperty *RNA_property_override_property_get(PointerRNA *ptr, PropertyRNA *prop, bool *r_created); + +struct IDOverrideStaticPropertyOperation *RNA_property_override_property_operation_find( + PointerRNA *ptr, PropertyRNA *prop, const int index, const bool strict, bool *r_strict); +struct IDOverrideStaticPropertyOperation *RNA_property_override_property_operation_get( + PointerRNA *ptr, PropertyRNA *prop, const short operation, const int index, + const bool strict, bool *r_strict, bool *r_created); + +void RNA_property_override_status( + PointerRNA *ptr, PropertyRNA *prop, const int index, + bool *r_overridable, bool *r_overridden, bool *r_mandatory, bool *r_locked); #ifdef __cplusplus } diff --git a/source/blender/makesrna/RNA_define.h b/source/blender/makesrna/RNA_define.h index e9e4276b235..4e32651d356 100644 --- a/source/blender/makesrna/RNA_define.h +++ b/source/blender/makesrna/RNA_define.h @@ -177,6 +177,8 @@ void RNA_def_property_update(PropertyRNA *prop, int noteflag, const char *update void RNA_def_property_editable_func(PropertyRNA *prop, const char *editable); void RNA_def_property_editable_array_func(PropertyRNA *prop, const char *editable); +void RNA_def_property_override_funcs(PropertyRNA *prop, const char *diff, const char *store, const char *apply); + void RNA_def_property_update_runtime(PropertyRNA *prop, const void *func); void RNA_def_property_poll_runtime(PropertyRNA *prop, const void *func); diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h index 4a8df4d6a7f..a3bba1984c1 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -158,7 +158,7 @@ typedef enum PropertySubType { /* Make sure enums are updated with these */ /* HIGHEST FLAG IN USE: 1 << 31 - * FREE FLAGS: 2, 3, 7, 9, 11, 13, 14, 15, 30 */ + * FREE FLAGS: 3, 7, 9, 11, 13, 14, 15, 30 */ typedef enum PropertyFlag { /* editable means the property is editable in the user * interface, properties are editable by default except @@ -176,6 +176,9 @@ typedef enum PropertyFlag { * and collections */ PROP_ANIMATABLE = (1 << 1), + /* Means the property can be overriden by a local 'proxy' of some linked datablock. */ + PROP_OVERRIDABLE = (1 << 2), + /* This flag means when the property's widget is in 'textedit' mode, it will be updated * after every typed char, instead of waiting final validation. Used e.g. for text searchbox. * It will also cause UI_BUT_VALUE_CLEAR to be set for text buttons. We could add an own flag diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index b927daf2ac0..19f66109665 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -3025,12 +3025,15 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr prop->arraylength[1], prop->arraylength[2], prop->totarraylength); - fprintf(f, "\t%s%s, %d, %s, %s,\n", + fprintf(f, "\t%s%s, %d, %s, %s, %s, %s, %s,\n", (prop->flag & PROP_CONTEXT_UPDATE) ? "(UpdateFunc)" : "", rna_function_string(prop->update), prop->noteflag, rna_function_string(prop->editable), - rna_function_string(prop->itemeditable)); + rna_function_string(prop->itemeditable), + rna_function_string(prop->override_diff), + rna_function_string(prop->override_store), + rna_function_string(prop->override_apply)); if (prop->flag_internal & PROP_INTERN_RAW_ACCESS) rna_set_raw_offset(f, srna, prop); else fprintf(f, "\t0, -1"); diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index c519c61e9e9..44df8916796 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -97,6 +97,7 @@ const EnumPropertyItem rna_enum_id_type_items[] = { #include "BKE_idprop.h" #include "BKE_library.h" #include "BKE_library_query.h" +#include "BKE_library_override.h" #include "BKE_library_remap.h" #include "BKE_animsys.h" #include "BKE_material.h" @@ -311,6 +312,15 @@ static ID *rna_ID_copy(ID *id, Main *bmain) return NULL; } +static ID *rna_ID_override_create(ID *id, Main *bmain) +{ + if (id->lib == NULL) { + return NULL; + } + + return BKE_override_static_create_from(bmain, id); +} + static void rna_ID_update_tag(ID *id, ReportList *reports, int flag) { /* XXX, new function for this! */ @@ -762,6 +772,14 @@ static PointerRNA rna_IDPreview_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_ImagePreview, prv_img); } +static PointerRNA rna_ID_override_reference_get(PointerRNA *ptr) +{ + ID *id = (ID *)ptr->data; + ID *reference = (id && id->override_static) ? id->override_static->reference : NULL; + + return reference ? rna_pointer_inherit_refine(ptr, ID_code_to_RNA_type(GS(reference->name)), reference) : PointerRNA_NULL; +} + #else static void rna_def_ID_properties(BlenderRNA *brna) @@ -1024,6 +1042,12 @@ static void rna_def_ID(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Library", "Library file the data-block is linked from"); + prop = RNA_def_pointer(srna, "override_static_reference", "ID", + "Override Reference", "Reference linked data-block overridden by this one"); + RNA_def_property_pointer_sdna(prop, NULL, "override_static->reference"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_pointer_funcs(prop, "rna_ID_override_reference_get", NULL, NULL, NULL); + prop = RNA_def_pointer(srna, "preview", "ImagePreview", "Preview", "Preview image and icon of this data-block (None if not supported for this type of data)"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -1036,6 +1060,12 @@ static void rna_def_ID(BlenderRNA *brna) parm = RNA_def_pointer(func, "id", "ID", "", "New copy of the ID"); RNA_def_function_return(func, parm); + func = RNA_def_function(srna, "override_create", "rna_ID_override_create"); + RNA_def_function_ui_description(func, "Create an overridden local copy of this linked data-block (not supported for all data-blocks)"); + RNA_def_function_flag(func, FUNC_USE_MAIN); + parm = RNA_def_pointer(func, "id", "ID", "", "New overridden local copy of the ID"); + RNA_def_function_return(func, parm); + 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, " "on reload the data will be removed"); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 49b50858f93..a1f059a9487 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -51,6 +51,7 @@ #include "BKE_idprop.h" #include "BKE_fcurve.h" #include "BKE_library.h" +#include "BKE_library_override.h" #include "BKE_main.h" #include "BKE_report.h" @@ -1817,7 +1818,8 @@ bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop) return ((flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0 && - (!id || !ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION))); + (!id || ((!ID_IS_LINKED(id) || (prop->flag & PROP_LIB_EXCEPTION)) && + (!id->override_static || (prop->flag & PROP_OVERRIDABLE))))); } /** @@ -1843,11 +1845,19 @@ bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char * } /* property from linked data-block */ - if (id && ID_IS_LINKED(id) && (prop->flag & PROP_LIB_EXCEPTION) == 0) { - if (!(*r_info)[0]) { - *r_info = "Can't edit this property from a linked data-block."; + if (id) { + if (ID_IS_LINKED(id) && (prop->flag & PROP_LIB_EXCEPTION) == 0) { + if (!(*r_info)[0]) { + *r_info = "Can't edit this property from a linked data-block."; + } + return false; + } + if (id->override_static != NULL && (prop->flag & PROP_OVERRIDABLE) == 0) { + if (!(*r_info)[0]) { + *r_info = "Can't edit this property from an override data-block."; + } + return false; } - return false; } return ((flag & PROP_EDITABLE) && (flag & PROP_REGISTER) == 0); @@ -6959,120 +6969,22 @@ bool RNA_property_reset(PointerRNA *ptr, PropertyRNA *prop, int index) } } +static bool rna_property_override_operation_apply( + PointerRNA *ptr_local, PointerRNA *ptr_reference, PointerRNA *ptr_storage, PropertyRNA *prop_local, + IDOverrideStaticPropertyOperation *opop); + bool RNA_property_copy(PointerRNA *ptr, PointerRNA *fromptr, PropertyRNA *prop, int index) { - int len, fromlen; - PropertyRNA *fromprop = prop; - - if (prop->magic != RNA_MAGIC) { - /* In case of IDProperty, we have to find the *real* idprop of ptr, - * since prop in this case is just a fake wrapper around actual IDProp data, and not a 'real' PropertyRNA. */ - prop = (PropertyRNA *)rna_idproperty_find(ptr, ((IDProperty *)fromprop)->name); - - /* its possible the custom-prop doesn't exist on this data-block */ - if (prop == NULL) { - return false; - } - - /* Even though currently we now prop will always be the 'fromprop', this might not be the case in the future. */ - if (prop == fromprop) { - fromprop = (PropertyRNA *)rna_idproperty_find(fromptr, ((IDProperty *)prop)->name); - } - } - - /* get the length of the array to work with */ - len = RNA_property_array_length(ptr, prop); - fromlen = RNA_property_array_length(fromptr, fromprop); - - if (len != fromlen) + if (!RNA_property_editable(ptr, prop)) { return false; - - /* get and set the default values as appropriate for the various types */ - switch (RNA_property_type(prop)) { - case PROP_BOOLEAN: - if (len) { - if (index == -1) { - int *tmparray = MEM_callocN(sizeof(int) * len, "copy - boolean"); - - RNA_property_boolean_get_array(fromptr, fromprop, tmparray); - RNA_property_boolean_set_array(ptr, prop, tmparray); - - MEM_freeN(tmparray); - } - else { - int value = RNA_property_boolean_get_index(fromptr, fromprop, index); - RNA_property_boolean_set_index(ptr, prop, index, value); - } - } - else { - int value = RNA_property_boolean_get(fromptr, fromprop); - RNA_property_boolean_set(ptr, prop, value); - } - return true; - case PROP_INT: - if (len) { - if (index == -1) { - int *tmparray = MEM_callocN(sizeof(int) * len, "copy - int"); - - RNA_property_int_get_array(fromptr, fromprop, tmparray); - RNA_property_int_set_array(ptr, prop, tmparray); - - MEM_freeN(tmparray); - } - else { - int value = RNA_property_int_get_index(fromptr, fromprop, index); - RNA_property_int_set_index(ptr, prop, index, value); - } - } - else { - int value = RNA_property_int_get(fromptr, fromprop); - RNA_property_int_set(ptr, prop, value); - } - return true; - case PROP_FLOAT: - if (len) { - if (index == -1) { - float *tmparray = MEM_callocN(sizeof(float) * len, "copy - float"); - - RNA_property_float_get_array(fromptr, fromprop, tmparray); - RNA_property_float_set_array(ptr, prop, tmparray); - - MEM_freeN(tmparray); - } - else { - float value = RNA_property_float_get_index(fromptr, fromprop, index); - RNA_property_float_set_index(ptr, prop, index, value); - } - } - else { - float value = RNA_property_float_get(fromptr, fromprop); - RNA_property_float_set(ptr, prop, value); - } - return true; - case PROP_ENUM: - { - int value = RNA_property_enum_get(fromptr, fromprop); - RNA_property_enum_set(ptr, prop, value); - return true; - } - case PROP_POINTER: - { - PointerRNA value = RNA_property_pointer_get(fromptr, fromprop); - RNA_property_pointer_set(ptr, prop, value); - return true; - } - case PROP_STRING: - { - char *value = RNA_property_string_get_alloc(fromptr, fromprop, NULL, 0, NULL); - RNA_property_string_set(ptr, prop, value); - MEM_freeN(value); - return true; - } - default: - return false; } - return false; + IDOverrideStaticPropertyOperation opop = { + .operation = IDOVERRIDESTATIC_OP_REPLACE, + .subitem_reference_index = index, + .subitem_local_index = index + }; + return rna_property_override_operation_apply(ptr, fromptr, NULL, prop, &opop); } /* use RNA_warning macro which includes __func__ suffix */ @@ -7097,176 +7009,514 @@ void _RNA_warning(const char *format, ...) #endif } -bool RNA_property_equals(PointerRNA *a, PointerRNA *b, PropertyRNA *prop, eRNAEqualsMode mode) +static int rna_property_override_diff( + PointerRNA *a, PointerRNA *b, PropertyRNA *prop, eRNACompareMode mode, + IDOverrideStatic *override, const char *rna_path, bool *r_override_changed, const int flags); + +bool RNA_property_equals(PointerRNA *a, PointerRNA *b, PropertyRNA *prop, eRNACompareMode mode) +{ + BLI_assert(ELEM(mode, RNA_EQ_STRICT, RNA_EQ_UNSET_MATCH_ANY, RNA_EQ_UNSET_MATCH_NONE)); + + return (rna_property_override_diff(a, b, prop, mode, NULL, NULL, NULL, 0) != 0); +} + +bool RNA_struct_equals(PointerRNA *a, PointerRNA *b, eRNACompareMode mode) +{ + CollectionPropertyIterator iter; + PropertyRNA *iterprop; + bool equals = true; + + if (a == NULL && b == NULL) + return true; + else if (a == NULL || b == NULL) + return false; + else if (a->type != b->type) + return false; + + iterprop = RNA_struct_iterator_property(a->type); + + RNA_property_collection_begin(a, iterprop, &iter); + for (; iter.valid; RNA_property_collection_next(&iter)) { + PropertyRNA *prop = iter.ptr.data; + + if (!RNA_property_equals(a, b, prop, mode)) { + equals = false; + break; + } + } + RNA_property_collection_end(&iter); + + return equals; +} + +/* Low-level functions, also used by non-override RNA API like copy or equality check. */ +#include "PIL_time_utildefines.h" +static int rna_property_override_diff( + PointerRNA *ptr_a, PointerRNA *ptr_b, PropertyRNA *prop_a, eRNACompareMode mode, + IDOverrideStatic *override, const char *rna_path, bool *r_override_changed, const int flags) { - int len, fromlen; + int len_a, len_b; + + PropertyRNA *prop_b = prop_a; + + if (prop_a->magic != RNA_MAGIC) { + /* In case of IDProperty, we have to find the *real* idprop of ptr, + * since prop in this case is just a fake wrapper around actual IDProp data, and not a 'real' PropertyRNA. */ + /* XXX TODO this is ugly, we already get correct prop in upcalling code, whould just pass them to this func! */ + prop_a = (PropertyRNA *)rna_idproperty_find(ptr_a, ((IDProperty *)prop_a)->name); + prop_b = (PropertyRNA *)rna_idproperty_find(ptr_b, ((IDProperty *)prop_b)->name); + + if (ELEM(NULL, prop_a, prop_b)) { + return 1; + } + } + + BLI_assert(prop_a->override_diff == prop_b->override_diff && prop_a->override_diff != NULL); if (mode == RNA_EQ_UNSET_MATCH_ANY) { /* uninitialized properties are assumed to match anything */ - if (!RNA_property_is_set(a, prop) || !RNA_property_is_set(b, prop)) - return true; + if (!RNA_property_is_set(ptr_a, prop_a) || !RNA_property_is_set(ptr_b, prop_b)) + return 0; } else if (mode == RNA_EQ_UNSET_MATCH_NONE) { /* unset properties never match set properties */ - if (RNA_property_is_set(a, prop) != RNA_property_is_set(b, prop)) - return false; + if (RNA_property_is_set(ptr_a, prop_a) != RNA_property_is_set(ptr_b, prop_b)) + return 1; } /* get the length of the array to work with */ - len = RNA_property_array_length(a, prop); - fromlen = RNA_property_array_length(b, prop); + len_a = RNA_property_array_length(ptr_a, prop_a); + len_b = RNA_property_array_length(ptr_b, prop_b); - if (len != fromlen) - return false; + if (len_a != len_b) { + /* Do not handle override in that case, we do not support insertion/deletion from arrays for now. */ + return len_a > len_b ? 1 : -1; + } - /* get and set the default values as appropriate for the various types */ - switch (RNA_property_type(prop)) { - case PROP_BOOLEAN: - { - if (len) { - int fixed_a[16], fixed_b[16]; - int *array_a, *array_b; - bool equals; + return prop_a->override_diff( + ptr_a, ptr_b, prop_a, prop_b, len_a, len_b, mode, override, rna_path, flags, r_override_changed); +} + +/* Modify local data-block to make it ready for override application (only needed for diff operations, where we use + * the local data-block's data as second operand). */ +static bool rna_property_override_operation_store( + PointerRNA *ptr_local, PointerRNA *ptr_reference, PointerRNA *ptr_storage, PropertyRNA *prop_local, + IDOverrideStaticProperty *op) +{ + int len_local, len_reference, len_storage = 0; + PropertyRNA *prop_reference = prop_local; + PropertyRNA *prop_storage = prop_local; + bool changed = false; - array_a = (len > 16) ? MEM_mallocN(sizeof(int) * len, "RNA equals") : fixed_a; - array_b = (len > 16) ? MEM_mallocN(sizeof(int) * len, "RNA equals") : fixed_b; + if (!ptr_storage) { + return changed; + } - RNA_property_boolean_get_array(a, prop, array_a); - RNA_property_boolean_get_array(b, prop, array_b); + if (prop_local->magic != RNA_MAGIC) { + /* In case of IDProperty, we have to find the *real* idprop of ptr, + * since prop in this case is just a fake wrapper around actual IDProp data, and not a 'real' PropertyRNA. */ + /* XXX TODO this is ugly, we already get correct prop in upcalling code, should just pass them to this func! */ + prop_local = (PropertyRNA *)rna_idproperty_find(ptr_local, ((IDProperty *)prop_local)->name); + prop_reference = (PropertyRNA *)rna_idproperty_find(ptr_reference, ((IDProperty *)prop_reference)->name); + if (ptr_storage) { + prop_storage = (PropertyRNA *)rna_idproperty_find(ptr_storage, ((IDProperty *)prop_storage)->name); + } - equals = memcmp(array_a, array_b, sizeof(int) * len) == 0; + /* its possible the custom-prop doesn't exist on this data-block */ + if (prop_local == NULL) { + return changed; + } + } - if (array_a != fixed_a) MEM_freeN(array_a); - if (array_b != fixed_b) MEM_freeN(array_b); + /* get the length of the array to work with */ + len_local = RNA_property_array_length(ptr_local, prop_local); + len_reference = RNA_property_array_length(ptr_reference, prop_reference); + if (prop_storage) { + len_storage = RNA_property_array_length(ptr_storage, prop_storage); + } - return equals; - } - else { - int value = RNA_property_boolean_get(a, prop); - return value == RNA_property_boolean_get(b, prop); - } + if (len_local != len_reference || len_local != len_storage) { + /* Do not handle override in that case, we do not support insertion/deletion from arrays for now. */ + return changed; + } + + BLI_assert(prop_local->override_store == prop_reference->override_store && + (!ptr_storage || prop_local->override_store == prop_storage->override_store) && + prop_local->override_store != NULL); + + for (IDOverrideStaticPropertyOperation *opop = op->operations.first; opop; opop = opop->next) { + /* Only needed for diff operations. */ + if (!ELEM(opop->operation, IDOVERRIDESTATIC_OP_ADD, IDOVERRIDESTATIC_OP_SUBTRACT, IDOVERRIDESTATIC_OP_MULTIPLY)) { + continue; } - case PROP_INT: + if (prop_local->override_store( + ptr_local, ptr_reference, ptr_storage, prop_local, prop_reference, prop_storage, + len_local, len_reference, len_storage, opop)) { - if (len) { - int fixed_a[16], fixed_b[16]; - int *array_a, *array_b; - bool equals; + changed = true; + } + } - array_a = (len > 16) ? MEM_mallocN(sizeof(int) * len, "RNA equals") : fixed_a; - array_b = (len > 16) ? MEM_mallocN(sizeof(int) * len, "RNA equals") : fixed_b; + return changed; +} - RNA_property_int_get_array(a, prop, array_a); - RNA_property_int_get_array(b, prop, array_b); +static bool rna_property_override_operation_apply( + PointerRNA *ptr_local, PointerRNA *ptr_reference, PointerRNA *ptr_storage, PropertyRNA *prop_local, + IDOverrideStaticPropertyOperation *opop) +{ + int len_local, len_reference, len_storage = 0; + PropertyRNA *prop_reference = prop_local; + PropertyRNA *prop_storage = prop_local; - equals = memcmp(array_a, array_b, sizeof(int) * len) == 0; + const short override_op = opop->operation; - if (array_a != fixed_a) MEM_freeN(array_a); - if (array_b != fixed_b) MEM_freeN(array_b); + if (override_op == IDOVERRIDESTATIC_OP_NOOP) { + return true; + } - return equals; - } - else { - int value = RNA_property_int_get(a, prop); - return value == RNA_property_int_get(b, prop); - } + if (ELEM(override_op, IDOVERRIDESTATIC_OP_ADD, IDOVERRIDESTATIC_OP_SUBTRACT, IDOVERRIDESTATIC_OP_MULTIPLY) && !ptr_storage) { + /* We cannot apply 'diff' override operations without some refference storage. + * This should typically only happen at read time of .blend file... */ + return false; + } + + if (prop_local->magic != RNA_MAGIC) { + /* In case of IDProperty, we have to find the *real* idprop of ptr, + * since prop in this case is just a fake wrapper around actual IDProp data, and not a 'real' PropertyRNA. */ + /* XXX TODO this is ugly, we already get correct prop in upcalling code, whould just pass them to this func! */ + prop_local = (PropertyRNA *)rna_idproperty_find(ptr_local, ((IDProperty *)prop_local)->name); + prop_reference = (PropertyRNA *)rna_idproperty_find(ptr_reference, ((IDProperty *)prop_reference)->name); + if (ptr_storage) { + prop_storage = (PropertyRNA *)rna_idproperty_find(ptr_storage, ((IDProperty *)prop_storage)->name); } - case PROP_FLOAT: - { - if (len) { - float fixed_a[16], fixed_b[16]; - float *array_a, *array_b; - bool equals; + /* its possible the custom-prop doesn't exist on this data-block */ + if (prop_local == NULL) { + return false; + } + } + + if (ELEM(override_op, IDOVERRIDESTATIC_OP_ADD, IDOVERRIDESTATIC_OP_SUBTRACT, IDOVERRIDESTATIC_OP_MULTIPLY) && !prop_storage) { + /* We cannot apply 'diff' override operations without some refference storage. + * This should typically only happen at read time of .blend file... */ + return false; + } - array_a = (len > 16) ? MEM_mallocN(sizeof(float) * len, "RNA equals") : fixed_a; - array_b = (len > 16) ? MEM_mallocN(sizeof(float) * len, "RNA equals") : fixed_b; + /* get the length of the array to work with */ + len_local = RNA_property_array_length(ptr_local, prop_local); + len_reference = RNA_property_array_length(ptr_reference, prop_reference); + if (ptr_storage) { + len_storage = RNA_property_array_length(ptr_storage, prop_storage); + } - RNA_property_float_get_array(a, prop, array_a); - RNA_property_float_get_array(b, prop, array_b); + if (len_local != len_reference || (ptr_storage && len_local != len_storage)) { + /* Do not handle override in that case, we do not support insertion/deletion from arrays for now. */ + return false; + } - equals = memcmp(array_a, array_b, sizeof(float) * len) == 0; + BLI_assert(prop_local->override_apply == prop_reference->override_apply && + (!ptr_storage || prop_local->override_apply == prop_storage->override_apply) && + prop_local->override_apply != NULL); - if (array_a != fixed_a) MEM_freeN(array_a); - if (array_b != fixed_b) MEM_freeN(array_b); + /* get and set the default values as appropriate for the various types */ + return prop_local->override_apply( + ptr_local, ptr_reference, ptr_storage, + prop_local, prop_reference, prop_storage, + len_local, len_reference, len_storage, + opop); +} - return equals; - } - else { - float value = RNA_property_float_get(a, prop); - return value == RNA_property_float_get(b, prop); + +/** + * Check whether reference and local overriden data match (are the same), + * with respect to given restrictive sets of properties. */ +bool RNA_struct_override_matches( + PointerRNA *local, PointerRNA *reference, + IDOverrideStatic *override, const bool ignore_non_overridable, const bool ignore_overridden) +{ + CollectionPropertyIterator iter; + PropertyRNA *iterprop; + bool equals = true; + + BLI_assert(local->type == reference->type); + + iterprop = RNA_struct_iterator_property(local->type); + + RNA_property_collection_begin(local, iterprop, &iter); + for (; iter.valid; RNA_property_collection_next(&iter)) { + PropertyRNA *prop = iter.ptr.data; + + if (ignore_non_overridable && !(prop->flag & PROP_OVERRIDABLE)) { + continue; + } + + if (ignore_overridden) { + /* XXX TODO this will have to be refined to handle collections insertions, and array items */ + char *rna_path = RNA_path_from_ID_to_property(local, prop); + if (BKE_override_static_property_find(override, rna_path) != NULL) { + MEM_SAFE_FREE(rna_path); + continue; } + MEM_SAFE_FREE(rna_path); } - case PROP_ENUM: - { - int value = RNA_property_enum_get(a, prop); - return value == RNA_property_enum_get(b, prop); + int flag = 0; + if (ignore_non_overridable) { + flag |= RNA_OVERRIDE_COMPARE_IGNORE_NON_OVERRIDABLE; + } + if (ignore_overridden) { + flag |= RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN; } + if (rna_property_override_diff(local, reference, prop, RNA_EQ_STRICT, override, NULL, NULL, flag) != 0) { + equals = false; + break; + } + } + RNA_property_collection_end(&iter); - case PROP_STRING: - { - char fixed_a[128], fixed_b[128]; - int len_a, len_b; - char *value_a = RNA_property_string_get_alloc(a, prop, fixed_a, sizeof(fixed_a), &len_a); - char *value_b = RNA_property_string_get_alloc(b, prop, fixed_b, sizeof(fixed_b), &len_b); - bool equals = STREQ(value_a, value_b); + return equals; +} - if (value_a != fixed_a) MEM_freeN(value_a); - if (value_b != fixed_b) MEM_freeN(value_b); +/** Store needed second operands into \a storage data-block for differential override operations. */ +bool RNA_struct_override_store(PointerRNA *local, PointerRNA *reference, PointerRNA *storage, IDOverrideStatic *override) +{ + bool changed = false; - return equals; - } +#ifdef DEBUG_OVERRIDE_TIMEIT + TIMEIT_START_AVERAGED(RNA_struct_override_store); +#endif + for (IDOverrideStaticProperty *op = override->properties.first; op; op = op->next) { + /* Simplified for now! */ + PointerRNA src_data, dst_data; + PropertyRNA *src_prop, *dst_prop; - case PROP_POINTER: + if (RNA_path_resolve_property(reference, op->rna_path, &src_data, &src_prop) && + RNA_path_resolve_property(local, op->rna_path, &dst_data, &dst_prop)) { - if (!STREQ(RNA_property_identifier(prop), "rna_type")) { - PointerRNA propptr_a = RNA_property_pointer_get(a, prop); - PointerRNA propptr_b = RNA_property_pointer_get(b, prop); - return RNA_struct_equals(&propptr_a, &propptr_b, mode); + PointerRNA storage_data; + PropertyRNA *storage_prop = NULL; + + /* It is totally OK if this does not success, only a subset of override operations actually need storage. */ + if (storage && (storage->id.data != NULL)) { + RNA_path_resolve_property(storage, op->rna_path, &storage_data, &storage_prop); + } + + if (rna_property_override_operation_store(&dst_data, &src_data, &storage_data, src_prop, op)) { + changed = true; } - break; } + } +#ifdef DEBUG_OVERRIDE_TIMEIT + TIMEIT_END_AVERAGED(RNA_struct_override_store); +#endif - default: - break; + return changed; +} + +/** Apply given \a op override property operations on \a dst, using \a src as source. */ +void RNA_property_override_apply( + PointerRNA *dst, PointerRNA *src, PointerRNA *storage, PropertyRNA *prop, IDOverrideStaticProperty *op) +{ + for (IDOverrideStaticPropertyOperation *opop = op->operations.first; opop; opop = opop->next) { + if (!rna_property_override_operation_apply(dst, src, storage, prop, opop)) + { + BLI_assert(0); + } } +} - return true; +/** Apply given \a override operations on \a dst, using \a src as source. */ +void RNA_struct_override_apply(PointerRNA *dst, PointerRNA *src, PointerRNA *storage, IDOverrideStatic *override) +{ +#ifdef DEBUG_OVERRIDE_TIMEIT + TIMEIT_START_AVERAGED(RNA_struct_override_apply); +#endif + for (IDOverrideStaticProperty *op = override->properties.first; op; op = op->next) { + /* Simplified for now! */ + PointerRNA src_data, dst_data; + PropertyRNA *src_prop, *dst_prop; + + if (RNA_path_resolve_property(src, op->rna_path, &src_data, &src_prop) && + RNA_path_resolve_property(dst, op->rna_path, &dst_data, &dst_prop)) + { + PointerRNA storage_data; + PropertyRNA *storage_prop = NULL; + + /* It is totally OK if this does not success, only a subset of override operations actually need storage. */ + if (storage && (storage->id.data != NULL)) { + RNA_path_resolve_property(storage, op->rna_path, &storage_data, &storage_prop); + } + + /* Note that src and dst props are the same, unless they are IDProperties... */ + RNA_property_override_apply(&dst_data, &src_data, storage_prop ? &storage_data : NULL, src_prop, op); + } +#ifndef NDEBUG + else { + printf("Failed to apply static override operation to '%s.%s' (could not resolve some properties)\n", + ((ID *)src->id.data)->name, op->rna_path); + } +#endif + } +#ifdef DEBUG_OVERRIDE_TIMEIT + TIMEIT_END_AVERAGED(RNA_struct_override_apply); +#endif } -bool RNA_struct_equals(PointerRNA *a, PointerRNA *b, eRNAEqualsMode mode) +/** Automatically define override rules by comparing \a local and \a reference RNA structs. */ +bool RNA_struct_auto_override(PointerRNA *local, PointerRNA *reference, IDOverrideStatic *override, const char *root_path) { CollectionPropertyIterator iter; -// CollectionPropertyRNA *citerprop; /* UNUSED */ PropertyRNA *iterprop; - bool equals = true; + bool changed = false; - if (a == NULL && b == NULL) - return true; - else if (a == NULL || b == NULL) - return false; - else if (a->type != b->type) - return false; + BLI_assert(local->type == reference->type); + BLI_assert(local->id.data && reference->id.data); - iterprop = RNA_struct_iterator_property(a->type); -// citerprop = (CollectionPropertyRNA *)rna_ensure_property(iterprop); /* UNUSED */ + if ((((ID *)local->id.data)->flag & LIB_OVERRIDE_STATIC_AUTO) == 0) { + return changed; + } - RNA_property_collection_begin(a, iterprop, &iter); - for (; iter.valid; RNA_property_collection_next(&iter)) { +#ifdef DEBUG_OVERRIDE_TIMEIT + static float _sum_time = 0.0f; + static float _num_time = 0.0f; + double _timeit_time; + if (!root_path) { + _timeit_time = PIL_check_seconds_timer(); + } +#endif + + iterprop = RNA_struct_iterator_property(local->type); + + for (RNA_property_collection_begin(local, iterprop, &iter); iter.valid; RNA_property_collection_next(&iter)) { PropertyRNA *prop = iter.ptr.data; - if (!RNA_property_equals(a, b, prop, mode)) { - equals = false; - break; + if (!(prop->flag & PROP_OVERRIDABLE)) { + continue; + } + if (RNA_property_animated(local, prop)) { + continue; } + + /* XXX TODO this will have to be refined to handle collections insertions, and array items */ + char *rna_path; + if (root_path) { + /* Inlined building, much much more efficient. */ + rna_path = BLI_sprintfN("%s.%s", root_path, RNA_property_identifier(prop)); + } + else { + rna_path = RNA_path_from_ID_to_property(local, prop); + } + if (rna_path == NULL) { + continue; + } + + rna_property_override_diff(local, reference, prop, RNA_EQ_STRICT, override, rna_path, &changed, + RNA_OVERRIDE_COMPARE_IGNORE_NON_OVERRIDABLE | RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN); + + MEM_SAFE_FREE(rna_path); } RNA_property_collection_end(&iter); - return equals; +#ifdef DEBUG_OVERRIDE_TIMEIT + if (!root_path) { + const float _delta_time = (float)(PIL_check_seconds_timer() - _timeit_time); + _sum_time += _delta_time; + _num_time++; + printf("ID: %s\n", ((ID *)local->id.data)->name); + printf("time end (%s): %.6f\n", __func__, _delta_time); + printf("time averaged (%s): %.6f (total: %.6f, in %d runs)\n", __func__, (_sum_time / _num_time), _sum_time, (int)_num_time); + } +#endif + + return changed; +} + +IDOverrideStaticProperty *RNA_property_override_property_find(PointerRNA *ptr, PropertyRNA *prop) +{ + ID *id = ptr->id.data; + + if (!id || !id->override_static) { + return NULL; + } + + char *rna_path = RNA_path_from_ID_to_property(ptr, prop); + if (rna_path) { + IDOverrideStaticProperty *op = BKE_override_static_property_find(id->override_static, rna_path); + MEM_freeN(rna_path); + return op; + } + return NULL; +} + +IDOverrideStaticProperty *RNA_property_override_property_get(PointerRNA *ptr, PropertyRNA *prop, bool *r_created) +{ + ID *id = ptr->id.data; + + if (!id || !id->override_static) { + return NULL; + } + + char *rna_path = RNA_path_from_ID_to_property(ptr, prop); + if (rna_path) { + IDOverrideStaticProperty *op = BKE_override_static_property_get(id->override_static, rna_path, r_created); + MEM_freeN(rna_path); + return op; + } + return NULL; +} + +IDOverrideStaticPropertyOperation *RNA_property_override_property_operation_find( + PointerRNA *ptr, PropertyRNA *prop, const int index, const bool strict, bool *r_strict) +{ + IDOverrideStaticProperty *op = RNA_property_override_property_find(ptr, prop); + + if (!op) { + return NULL; + } + + return BKE_override_static_property_operation_find(op, NULL, NULL, index, index, strict, r_strict); } +IDOverrideStaticPropertyOperation *RNA_property_override_property_operation_get( + PointerRNA *ptr, PropertyRNA *prop, const short operation, const int index, + const bool strict, bool *r_strict, bool *r_created) +{ + IDOverrideStaticProperty *op = RNA_property_override_property_get(ptr, prop, NULL); + + if (!op) { + return NULL; + } + + return BKE_override_static_property_operation_get(op, operation, NULL, NULL, index, index, strict, r_strict, r_created); +} + +void RNA_property_override_status( + PointerRNA *ptr, PropertyRNA *prop, const int index, + bool *r_overridable, bool *r_overridden, bool *r_mandatory, bool *r_locked) +{ +#define SET_RET(_name, _val) if (_name != NULL) *_name = (_val) + + SET_RET(r_overridable, false); + SET_RET(r_overridden, false); + SET_RET(r_mandatory, false); + SET_RET(r_locked, false); + + if (!ptr || !prop || !ptr->id.data || !((ID *)ptr->id.data)->override_static) { + return; + } + + SET_RET(r_overridable, (prop->flag & PROP_OVERRIDABLE) && (prop->flag & PROP_EDITABLE)); + + if (r_overridden || r_mandatory || r_locked) { + IDOverrideStaticPropertyOperation *opop = RNA_property_override_property_operation_find(ptr, prop, index, false, NULL); + SET_RET(r_overridden, opop != NULL); + SET_RET(r_mandatory, (opop->flag & IDOVERRIDESTATIC_FLAG_MANDATORY) != 0); + SET_RET(r_locked, (opop->flag & IDOVERRIDESTATIC_FLAG_LOCKED) != 0); + } +} + + bool RNA_path_resolved_create( PointerRNA *ptr, struct PropertyRNA *prop, diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index c8a6a503fd9..03e58f8f78e 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -1270,6 +1270,14 @@ PropertyRNA *RNA_def_property(StructOrFunctionRNA *cont_, const char *identifier #endif } + /* Override handling. */ + if (DefRNA.preprocess) { + prop->override_diff = (RNAPropOverrideDiff)"rna_property_override_diff_default"; + prop->override_store = (RNAPropOverrideStore)"rna_property_override_store_default"; + prop->override_apply = (RNAPropOverrideApply)"rna_property_override_apply_default"; + } + /* TODO: do we want that for runtime-defined stuff too? I’d say no, but... maybe yes :/ */ + rna_addtail(&cont->properties, prop); return prop; @@ -2222,6 +2230,29 @@ void RNA_def_property_editable_array_func(PropertyRNA *prop, const char *editabl if (editable) prop->itemeditable = (ItemEditableFunc)editable; } +/** + * Set custom callbacks for override operations handling. + * + * \note \a diff callback will also be used by RNA comparison/equality functions. + */ +void RNA_def_property_override_funcs(PropertyRNA *prop, const char *diff, const char *store, const char *apply) +{ + if (!DefRNA.preprocess) { + fprintf(stderr, "%s: only during preprocessing.\n", __func__); + return; + } + + if (diff) { + prop->override_diff = (RNAPropOverrideDiff)diff; + } + if (store) { + prop->override_store = (RNAPropOverrideStore)store; + } + if (apply) { + prop->override_apply = (RNAPropOverrideApply)apply; + } +} + void RNA_def_property_update(PropertyRNA *prop, int noteflag, const char *func) { if (!DefRNA.preprocess) { diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 23a34918d33..7dffab4eefb 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -37,6 +37,9 @@ struct FreestyleSettings; struct ID; +struct IDOverrideStatic; +struct IDOverrideStaticProperty; +struct IDOverrideStaticPropertyOperation; struct IDProperty; struct Main; struct Mesh; @@ -201,6 +204,12 @@ void RNA_def_mask(struct BlenderRNA *brna); void rna_def_animdata_common(struct StructRNA *srna); +bool rna_AnimaData_override_apply( + struct PointerRNA *ptr_local, struct PointerRNA *ptr_reference, struct PointerRNA *ptr_storage, + struct PropertyRNA *prop_local, struct PropertyRNA *prop_reference, struct PropertyRNA *prop_storage, + const int len_local, const int len_reference, const int len_storage, + struct IDOverrideStaticPropertyOperation *opop); + void rna_def_animviz_common(struct StructRNA *srna); void rna_def_motionpath_common(struct StructRNA *srna); @@ -391,6 +400,33 @@ extern StructRNA RNA_PropertyGroup; struct IDProperty *rna_idproperty_check(struct PropertyRNA **prop, struct PointerRNA *ptr); +/* Override default callbacks. */ +/* Default override callbacks for all types. */ +/* TODO: Maybe at some point we'll want to write that in direct RNA-generated code instead + * (like we do for default get/set/etc.)? + * Not obvious though, those are fairly more complicated than basic SDNA access. + */ +int rna_property_override_diff_default( + struct PointerRNA *ptr_a, struct PointerRNA *ptr_b, + struct PropertyRNA *prop_a, struct PropertyRNA *prop_b, + const int len_a, const int len_b, + const int mode, + struct IDOverrideStatic *override, const char *rna_path, + const int flags, bool *r_override_changed); + +bool rna_property_override_store_default( + struct PointerRNA *ptr_local, struct PointerRNA *ptr_reference, struct PointerRNA *ptr_storage, + struct PropertyRNA *prop_local, struct PropertyRNA *prop_reference, struct PropertyRNA *prop_storage, + const int len_local, const int len_reference, const int len_storage, + struct IDOverrideStaticPropertyOperation *opop); + +bool rna_property_override_apply_default( + struct PointerRNA *ptr_dst, struct PointerRNA *ptr_src, struct PointerRNA *ptr_storage, + struct PropertyRNA *prop_dst, struct PropertyRNA *prop_src, struct PropertyRNA *prop_storage, + const int len_dst, const int len_src, const int len_storage, + struct IDOverrideStaticPropertyOperation *opop); + + /* Builtin Property Callbacks */ void rna_builtin_properties_begin(struct CollectionPropertyIterator *iter, struct PointerRNA *ptr); diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h index b43423464e9..fd0b655e41e 100644 --- a/source/blender/makesrna/intern/rna_internal_types.h +++ b/source/blender/makesrna/intern/rna_internal_types.h @@ -40,6 +40,9 @@ struct PointerRNA; struct FunctionRNA; struct CollectionPropertyIterator; struct bContext; +struct IDOverrideStatic; +struct IDOverrideStaticProperty; +struct IDOverrideStaticPropertyOperation; struct IDProperty; struct GHash; struct Main; @@ -117,6 +120,60 @@ typedef void (*PropStringSetFuncEx)(struct PointerRNA *ptr, struct PropertyRNA * typedef int (*PropEnumGetFuncEx)(struct PointerRNA *ptr, struct PropertyRNA *prop); typedef void (*PropEnumSetFuncEx)(struct PointerRNA *ptr, struct PropertyRNA *prop, int value); +/* Handling override operations, and also comparison. */ +enum { + /* Do not compare properties that are not overridable. */ + RNA_OVERRIDE_COMPARE_IGNORE_NON_OVERRIDABLE = 1 << 0, + /* Do not compare properties that are already overridden. */ + RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN = 1 << 1, +}; + +/** + * If \a override is NULL, merely do comparison between prop_a from ptr_a and prop_b from ptr_b, + * following comparison mode given. + * If \a override and \a rna_path are not NULL, it will add a new override operation for overridable properties + * that differ and have not yet been overridden (and set accordingly \a r_override_changed if given). + * + * \note Given PropertyRNA are final (in case of IDProps...). + * \note In non-array cases, \a len values are 0. + * \note \a override, \a rna_path and \a r_override_changed may be NULL pointers. + */ +typedef int (*RNAPropOverrideDiff)( + struct PointerRNA *ptr_a, struct PointerRNA *ptr_b, + struct PropertyRNA *prop_a, struct PropertyRNA *prop_b, + const int len_a, const int len_b, + const int mode, + struct IDOverrideStatic *override, const char *rna_path, + const int flags, bool *r_override_changed); + +/** + * Only used for differential override (add, sub, etc.). + * Store into storage the value needed to transform reference's value into local's value. + * + * \note Given PropertyRNA are final (in case of IDProps...). + * \note In non-array cases, \a len values are 0. + * \note Might change given override operation (e.g. change 'add' one into 'sub'), in case computed storage value + * is out of range (or even change it to basic 'set' operation if nothing else works). + */ +typedef bool (*RNAPropOverrideStore)( + struct PointerRNA *ptr_local, struct PointerRNA *ptr_reference, struct PointerRNA *ptr_storage, + struct PropertyRNA *prop_local, struct PropertyRNA *prop_reference, struct PropertyRNA *prop_storage, + const int len_local, const int len_reference, const int len_storage, + struct IDOverrideStaticPropertyOperation *opop); + +/** + * Apply given override operation from src to dst (using value from storage as second operand + * for differential operations). + * + * \note Given PropertyRNA are final (in case of IDProps...). + * \note In non-array cases, \a len values are 0. + */ +typedef bool (*RNAPropOverrideApply)( + struct PointerRNA *ptr_dst, struct PointerRNA *ptr_src, struct PointerRNA *ptr_storage, + struct PropertyRNA *prop_dst, struct PropertyRNA *prop_src, struct PropertyRNA *prop_storage, + const int len_dst, const int len_src, const int len_storage, + struct IDOverrideStaticPropertyOperation *opop); + /* Container - generic abstracted container of RNA properties */ typedef struct ContainerRNA { void *next, *prev; @@ -194,6 +251,11 @@ struct PropertyRNA { /* callback for testing if array-item editable (if applicable) */ ItemEditableFunc itemeditable; + /* Override handling callbacks (diff is also used for comparison). */ + RNAPropOverrideDiff override_diff; + RNAPropOverrideStore override_store; + RNAPropOverrideApply override_apply; + /* raw access */ int rawoffset; RawPropertyType rawtype; diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c index 507262675b3..3bcdd971ed2 100644 --- a/source/blender/makesrna/intern/rna_rna.c +++ b/source/blender/makesrna/intern/rna_rna.c @@ -107,6 +107,8 @@ const EnumPropertyItem rna_enum_property_unit_items[] = { #include "MEM_guardedalloc.h" #include "BLI_ghash.h" +#include "BKE_library_override.h" + /* Struct */ static void rna_Struct_identifier_get(PointerRNA *ptr, char *value) @@ -564,6 +566,13 @@ static int rna_Property_animatable_get(PointerRNA *ptr) return (prop->flag & PROP_ANIMATABLE) != 0; } +static int rna_Property_overridable_get(PointerRNA *ptr) +{ + PropertyRNA *prop = (PropertyRNA *)ptr->data; + + return (prop->flag & PROP_OVERRIDABLE) != 0; +} + static int rna_Property_use_output_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; @@ -1082,6 +1091,1001 @@ static int rna_BlenderRNA_structs_lookup_string(PointerRNA *ptr, const char *key return false; } +/* Default override (and compare) callbacks. */ + +/* Used for both Pointer and Collection properties. */ +static int rna_property_override_equals_propptr( + PointerRNA *propptr_a, PointerRNA *propptr_b, eRNACompareMode mode, + IDOverrideStatic *override, const char *rna_path, bool *r_override_changed, const int flags) +{ + bool is_id = false; + bool is_type_null = false; + + /* Beware, PointerRNA_NULL has no type and is considered a 'blank page'! */ + if (propptr_a->type == NULL) { + if (propptr_b->type == NULL) { + if (r_override_changed) { + *r_override_changed = false; + } + return 0; + } + is_id = RNA_struct_is_ID(propptr_b->type); + is_type_null = true; + } + else { + is_id = RNA_struct_is_ID(propptr_a->type); + is_type_null = (propptr_b->type == NULL); + } + + if (is_id) { + BLI_assert(propptr_a->data == propptr_a->id.data && propptr_b->data == propptr_b->id.data); + } + + if (override) { + if (rna_path) { + if (is_type_null || is_id) { + /* In case this is an ID (or one of the pointers is NULL), do not compare structs! + * This is a quite safe path to infinite loop. + * Instead, just compare pointers themselves (we assume sub-ID structs cannot loop). */ + const int comp = (propptr_a->data != propptr_b->data); + + if (comp != 0 && rna_path) { + bool created = false; + IDOverrideStaticProperty *op = BKE_override_static_property_get(override, rna_path, &created); + + if (op != NULL && created) { /* If not yet overridden... */ + BKE_override_static_property_operation_get( + op, IDOVERRIDESTATIC_OP_REPLACE, NULL, NULL, -1, -1, true, NULL, NULL); + if (r_override_changed) { + *r_override_changed = created; + } + } + } + + return comp; + } + else { + const bool changed = RNA_struct_auto_override(propptr_a, propptr_b, override, rna_path); + if (r_override_changed) { + *r_override_changed = *r_override_changed || changed; + } + /* XXX Simplification here, if no override was added we assume they are equal, + * this may not be good behavior, time will say. */ + return !changed; + } + } + else { + return !RNA_struct_override_matches( + propptr_a, propptr_b, override, + flags & RNA_OVERRIDE_COMPARE_IGNORE_NON_OVERRIDABLE, + flags & RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN); + } + } + else { + return !RNA_struct_equals(propptr_a, propptr_b, mode); + } +} + +static char *rna_path_collection_prop_item_extend(const char *rna_path_prop, const char *item_name) +{ + const size_t esc_item_name_len = strlen(item_name) * 2; + char *esc_item_name = alloca(sizeof(*esc_item_name) * esc_item_name_len); + BLI_strescape(esc_item_name, item_name, esc_item_name_len); + return BLI_sprintfN("%s[\"%s\"]", rna_path_prop, esc_item_name); +} + +int rna_property_override_diff_default(PointerRNA *ptr_a, PointerRNA *ptr_b, + PropertyRNA *prop_a, PropertyRNA *prop_b, + const int len_a, const int len_b, + const int mode, + IDOverrideStatic *override, const char *rna_path, + const int flags, bool *r_override_changed) +{ + BLI_assert(len_a == len_b); + + switch (RNA_property_type(prop_a)) { + case PROP_BOOLEAN: + { + if (len_a) { + int array_stack_a[RNA_STACK_ARRAY], array_stack_b[RNA_STACK_ARRAY]; + int *array_a, *array_b; + + array_a = (len_a > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(int) * len_a, "RNA equals") : array_stack_a; + array_b = (len_b > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(int) * len_b, "RNA equals") : array_stack_b; + + RNA_property_boolean_get_array(ptr_a, prop_a, array_a); + RNA_property_boolean_get_array(ptr_b, prop_b, array_b); + + const int comp = memcmp(array_a, array_b, sizeof(int) * len_a); + + if (comp != 0 && rna_path) { + /* XXX TODO this will have to be refined to handle array items */ + bool created = false; + IDOverrideStaticProperty *op = BKE_override_static_property_get(override, rna_path, &created); + + if (op != NULL && created) { + BKE_override_static_property_operation_get( + op, IDOVERRIDESTATIC_OP_REPLACE, NULL, NULL, -1, -1, true, NULL, NULL); + if (r_override_changed) { + *r_override_changed = created; + } + } + else { + /* Already overriden prop, we'll have to check arrays items etc. */ + } + } + + if (array_a != array_stack_a) MEM_freeN(array_a); + if (array_b != array_stack_b) MEM_freeN(array_b); + + return comp; + } + else { + const int value_a = RNA_property_boolean_get(ptr_a, prop_a); + const int value_b = RNA_property_boolean_get(ptr_b, prop_b); + const int comp = (value_a < value_b) ? -1 : (value_a > value_b) ? 1 : 0; + + if (comp != 0 && rna_path) { + bool created = false; + IDOverrideStaticProperty *op = BKE_override_static_property_get(override, rna_path, &created); + + if (op != NULL && created) { /* If not yet overridden... */ + BKE_override_static_property_operation_get( + op, IDOVERRIDESTATIC_OP_REPLACE, NULL, NULL, -1, -1, true, NULL, NULL); + if (r_override_changed) { + *r_override_changed = created; + } + } + } + + return comp; + } + } + + case PROP_INT: + { + if (len_a) { + int array_stack_a[RNA_STACK_ARRAY], array_stack_b[RNA_STACK_ARRAY]; + int *array_a, *array_b; + + array_a = (len_a > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(int) * len_a, "RNA equals") : array_stack_a; + array_b = (len_b > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(int) * len_b, "RNA equals") : array_stack_b; + + RNA_property_int_get_array(ptr_a, prop_a, array_a); + RNA_property_int_get_array(ptr_b, prop_b, array_b); + + const int comp = memcmp(array_a, array_b, sizeof(int) * len_a); + + if (comp != 0 && rna_path) { + /* XXX TODO this will have to be refined to handle array items */ + bool created = false; + IDOverrideStaticProperty *op = BKE_override_static_property_get(override, rna_path, &created); + + if (op != NULL && created) { + BKE_override_static_property_operation_get( + op, IDOVERRIDESTATIC_OP_REPLACE, NULL, NULL, -1, -1, true, NULL, NULL); + if (r_override_changed) { + *r_override_changed = created; + } + } + else { + /* Already overriden prop, we'll have to check arrays items etc. */ + } + } + + if (array_a != array_stack_a) MEM_freeN(array_a); + if (array_b != array_stack_b) MEM_freeN(array_b); + + return comp; + } + else { + const int value_a = RNA_property_int_get(ptr_a, prop_a); + const int value_b = RNA_property_int_get(ptr_b, prop_b); + const int comp = (value_a < value_b) ? -1 : (value_a > value_b) ? 1 : 0; + + if (comp != 0 && rna_path) { + bool created = false; + IDOverrideStaticProperty *op = BKE_override_static_property_get(override, rna_path, &created); + + if (op != NULL && created) { /* If not yet overridden... */ + BKE_override_static_property_operation_get( + op, IDOVERRIDESTATIC_OP_REPLACE, NULL, NULL, -1, -1, true, NULL, NULL); + if (r_override_changed) { + *r_override_changed = created; + } + } + } + + return comp; + } + } + + case PROP_FLOAT: + { + const bool is_proportional = (prop_a->flag & PROP_PROPORTIONAL) != 0; + if (len_a) { + float array_stack_a[RNA_STACK_ARRAY], array_stack_b[RNA_STACK_ARRAY]; + float *array_a, *array_b; + + array_a = (len_a > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(float) * len_a, "RNA equals") : array_stack_a; + array_b = (len_b > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(float) * len_b, "RNA equals") : array_stack_b; + + RNA_property_float_get_array(ptr_a, prop_a, array_a); + RNA_property_float_get_array(ptr_b, prop_b, array_b); + + const int comp = memcmp(array_a, array_b, sizeof(float) * len_a); + + if (comp != 0 && rna_path) { + /* XXX TODO this will have to be refined to handle array items */ + bool created = false; + IDOverrideStaticProperty *op = BKE_override_static_property_get(override, rna_path, &created); + + if (op != NULL && created) { + BKE_override_static_property_operation_get( + op, is_proportional ? IDOVERRIDESTATIC_OP_MULTIPLY : IDOVERRIDESTATIC_OP_REPLACE, + NULL, NULL, -1, -1, true, NULL, NULL); + if (r_override_changed) { + *r_override_changed = created; + } + } + else { + /* Already overriden prop, we'll have to check arrays items etc. */ + } + } + + if (array_a != array_stack_a) MEM_freeN(array_a); + if (array_b != array_stack_b) MEM_freeN(array_b); + + return comp; + } + else { + const float value_a = RNA_property_float_get(ptr_a, prop_a); + const float value_b = RNA_property_float_get(ptr_b, prop_b); + const int comp = (value_a < value_b) ? -1 : (value_a > value_b) ? 1 : 0; + + if (comp != 0 && rna_path) { + bool created = false; + IDOverrideStaticProperty *op = BKE_override_static_property_get(override, rna_path, &created); + + if (op != NULL && created) { /* If not yet overridden... */ + BKE_override_static_property_operation_get( + op, is_proportional ? IDOVERRIDESTATIC_OP_MULTIPLY : IDOVERRIDESTATIC_OP_REPLACE, + NULL, NULL, -1, -1, true, NULL, NULL); + if (r_override_changed) { + *r_override_changed = created; + } + } + } + + return comp ; + } + } + + case PROP_ENUM: + { + const int value_a = RNA_property_enum_get(ptr_a, prop_a); + const int value_b = RNA_property_enum_get(ptr_b, prop_b); + const int comp = value_a != value_b; + + if (comp != 0 && rna_path) { + bool created = false; + IDOverrideStaticProperty *op = BKE_override_static_property_get(override, rna_path, &created); + + if (op != NULL && created) { /* If not yet overridden... */ + BKE_override_static_property_operation_get( + op, IDOVERRIDESTATIC_OP_REPLACE, NULL, NULL, -1, -1, true, NULL, NULL); + if (r_override_changed) { + *r_override_changed = created; + } + } + } + + return comp; + } + + case PROP_STRING: + { + char fixed_a[128], fixed_b[128]; + int len_str_a, len_str_b; + char *value_a = RNA_property_string_get_alloc(ptr_a, prop_a, fixed_a, sizeof(fixed_a), &len_str_a); + char *value_b = RNA_property_string_get_alloc(ptr_b, prop_b, fixed_b, sizeof(fixed_b), &len_str_b); + const int comp = strcmp(value_a, value_b); + + if (comp != 0 && rna_path) { + bool created = false; + IDOverrideStaticProperty *op = BKE_override_static_property_get(override, rna_path, &created); + + if (op != NULL && created) { /* If not yet overridden... */ + BKE_override_static_property_operation_get( + op, IDOVERRIDESTATIC_OP_REPLACE, NULL, NULL, -1, -1, true, NULL, NULL); + if (r_override_changed) { + *r_override_changed = created; + } + } + } + + if (value_a != fixed_a) MEM_freeN(value_a); + if (value_b != fixed_b) MEM_freeN(value_b); + + return comp; + } + + case PROP_POINTER: + { + if (STREQ(RNA_property_identifier(prop_a), "rna_type")) { + /* Dummy 'pass' answer, this is a meta-data and must be ignored... */ + return 0; + } + else { + PointerRNA propptr_a = RNA_property_pointer_get(ptr_a, prop_a); + PointerRNA propptr_b = RNA_property_pointer_get(ptr_b, prop_b); + return rna_property_override_equals_propptr( + &propptr_a, &propptr_b, mode, + override, rna_path, r_override_changed, flags); + } + break; + } + + case PROP_COLLECTION: + { + bool equals = true; + int idx = 0; + + CollectionPropertyIterator iter_a, iter_b; + RNA_property_collection_begin(ptr_a, prop_a, &iter_a); + RNA_property_collection_begin(ptr_b, prop_b, &iter_b); + + for (; iter_a.valid && iter_b.valid; + RNA_property_collection_next(&iter_a), RNA_property_collection_next(&iter_b), idx++) + { + char *extended_rna_path = NULL; + + if (iter_a.ptr.type != iter_b.ptr.type) { + /* nothing we can do (for until we support adding/removing from collections), skip it. */ + equals = false; + continue; + } + + PropertyRNA *propname = RNA_struct_name_property(iter_a.ptr.type); + char propname_buff_a[256], propname_buff_b[256]; + char *propname_a = NULL, *propname_b = NULL; + + if (propname != NULL) { + propname_a = RNA_property_string_get_alloc(&iter_a.ptr, propname, propname_buff_a, sizeof(propname_buff_a), NULL); + propname_b = RNA_property_string_get_alloc(&iter_b.ptr, propname, propname_buff_b, sizeof(propname_buff_b), NULL); + if (!STREQ(propname_a, propname_b)) { + /* Same as above, not same structs. */ + equals = false; + } + else if (rna_path) { + extended_rna_path = rna_path_collection_prop_item_extend(rna_path, propname_a); + } + } + else { /* Based on index... */ + if (rna_path) { + extended_rna_path = BLI_sprintfN("%s[%d]", rna_path, idx); + } + } + + if (equals) { + const int eq = rna_property_override_equals_propptr( + &iter_a.ptr, &iter_b.ptr, mode, + override, extended_rna_path, r_override_changed, flags); + equals = equals && eq; + } + + if (propname_a != propname_buff_a) { + MEM_freeN(propname_a); + } + if (propname_b != propname_buff_b) { + MEM_freeN(propname_b); + } + MEM_SAFE_FREE(extended_rna_path); + + if (!rna_path && !equals) { + break; /* Early out in case we do not want to loop over whole collection. */ + } + } + + equals = equals && !(iter_a.valid || iter_b.valid); /* Not same number of items in both collections... */ + RNA_property_collection_end(&iter_a); + RNA_property_collection_end(&iter_b); + + return (equals == false); + } + + default: + break; + } + + return 0; +} + +bool rna_property_override_store_default( + PointerRNA *ptr_local, PointerRNA *ptr_reference, PointerRNA *ptr_storage, + PropertyRNA *prop_local, PropertyRNA *prop_reference, PropertyRNA *prop_storage, + const int len_local, const int len_reference, const int len_storage, + IDOverrideStaticPropertyOperation *opop) +{ + BLI_assert(len_local == len_reference && (!ptr_storage || len_local == len_storage)); + + bool changed = false; + const int index = opop->subitem_reference_index; + + if (!ELEM(opop->operation, IDOVERRIDESTATIC_OP_ADD, IDOVERRIDESTATIC_OP_SUBTRACT, IDOVERRIDESTATIC_OP_MULTIPLY)) { + return changed; + } + + /* XXX TODO About range limits. + * Ideally, it woudl be great to get rid of RNA range in that specific case. + * However, this won't be that easy and will add yet another layer of complexity in generated code, + * not to mention that we could most likely *not* bypass custom setters anyway. + * So for now, if needed second operand value is not in valid range, we simply fall back + * to a mere REPLACE operation. + * Time will say whether this is acceptable limitation or not. */ + switch (RNA_property_type(prop_local)) { + case PROP_BOOLEAN: + /* TODO support boolean ops? Really doubt this would ever be useful though... */ + BLI_assert(0 && "Boolean properties support no override diff operation"); + break; + case PROP_INT: + { + int prop_min, prop_max; + RNA_property_int_range(ptr_local, prop_local, &prop_min, &prop_max); + + if (len_local) { + if (index == -1) { + int array_stack_a[RNA_STACK_ARRAY], array_stack_b[RNA_STACK_ARRAY]; + int *array_a, *array_b; + + array_a = (len_local > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(*array_a) * len_local, __func__) : array_stack_a; + RNA_property_int_get_array(ptr_reference, prop_reference, array_a); + + switch (opop->operation) { + case IDOVERRIDESTATIC_OP_ADD: + case IDOVERRIDESTATIC_OP_SUBTRACT: + { + const int fac = opop->operation == IDOVERRIDESTATIC_OP_ADD ? 1 : -1; + const int other_op = opop->operation == IDOVERRIDESTATIC_OP_ADD ? IDOVERRIDESTATIC_OP_SUBTRACT : IDOVERRIDESTATIC_OP_ADD; + bool do_set = true; + array_b = (len_local > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(*array_b) * len_local, __func__) : array_stack_b; + RNA_property_int_get_array(ptr_local, prop_local, array_b); + for (int i = len_local; i--;) { + array_b[i] = fac * (array_b[i] - array_a[i]); + if (array_b[i] < prop_min || array_b[i] > prop_max) { + opop->operation = other_op; + for (int j = len_local; j--;) { + array_b[j] = j >= i ? -array_b[j] : fac * (array_a[j] - array_b[j]); + if (array_b[j] < prop_min || array_b[j] > prop_max) { + /* We failed to find a suitable diff op, + * fall back to plain REPLACE one. */ + opop->operation = IDOVERRIDESTATIC_OP_REPLACE; + do_set = false; + break; + } + } + break; + } + } + if (do_set) { + changed = true; + RNA_property_int_set_array(ptr_storage, prop_storage, array_b); + } + if (array_b != array_stack_b) MEM_freeN(array_b); + break; + } + default: + BLI_assert(0 && "Unsupported RNA override diff operation on integer"); + break; + } + + if (array_a != array_stack_a) MEM_freeN(array_a); + } + else { + const int value = RNA_property_int_get_index(ptr_reference, prop_reference, index); + + switch (opop->operation) { + case IDOVERRIDESTATIC_OP_ADD: + case IDOVERRIDESTATIC_OP_SUBTRACT: + { + const int fac = opop->operation == IDOVERRIDESTATIC_OP_ADD ? 1 : -1; + const int other_op = opop->operation == IDOVERRIDESTATIC_OP_ADD ? IDOVERRIDESTATIC_OP_SUBTRACT : IDOVERRIDESTATIC_OP_ADD; + int b = fac * (RNA_property_int_get_index(ptr_local, prop_local, index) - value); + if (b < prop_min || b > prop_max) { + opop->operation = other_op; + b = -b; + if (b < prop_min || b > prop_max) { + opop->operation = IDOVERRIDESTATIC_OP_REPLACE; + break; + } + } + changed = true; + RNA_property_int_set_index(ptr_storage, prop_storage, index, b); + break; + } + default: + BLI_assert(0 && "Unsupported RNA override diff operation on integer"); + break; + } + } + } + else { + const int value = RNA_property_int_get(ptr_reference, prop_reference); + + switch (opop->operation) { + case IDOVERRIDESTATIC_OP_ADD: + case IDOVERRIDESTATIC_OP_SUBTRACT: + { + const int fac = opop->operation == IDOVERRIDESTATIC_OP_ADD ? 1 : -1; + const int other_op = opop->operation == IDOVERRIDESTATIC_OP_ADD ? IDOVERRIDESTATIC_OP_SUBTRACT : IDOVERRIDESTATIC_OP_ADD; + int b = fac * (RNA_property_int_get(ptr_local, prop_local) - value); + if (b < prop_min || b > prop_max) { + opop->operation = other_op; + b = -b; + if (b < prop_min || b > prop_max) { + opop->operation = IDOVERRIDESTATIC_OP_REPLACE; + break; + } + } + changed = true; + RNA_property_int_set(ptr_storage, prop_storage, b); + break; + } + default: + BLI_assert(0 && "Unsupported RNA override diff operation on integer"); + break; + } + } + break; + } + case PROP_FLOAT: + { + float prop_min, prop_max; + RNA_property_float_range(ptr_local, prop_local, &prop_min, &prop_max); + + if (len_local) { + if (index == -1) { + float array_stack_a[RNA_STACK_ARRAY], array_stack_b[RNA_STACK_ARRAY]; + float *array_a, *array_b; + + array_a = (len_local > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(*array_a) * len_local, __func__) : array_stack_a; + + RNA_property_float_get_array(ptr_reference, prop_reference, array_a); + switch (opop->operation) { + case IDOVERRIDESTATIC_OP_ADD: + case IDOVERRIDESTATIC_OP_SUBTRACT: + { + const float fac = opop->operation == IDOVERRIDESTATIC_OP_ADD ? 1.0 : -1.0; + const int other_op = opop->operation == IDOVERRIDESTATIC_OP_ADD ? IDOVERRIDESTATIC_OP_SUBTRACT : IDOVERRIDESTATIC_OP_ADD; + bool do_set = true; + array_b = (len_local > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(*array_b) * len_local, __func__) : array_stack_b; + RNA_property_float_get_array(ptr_local, prop_local, array_b); + for (int i = len_local; i--;) { + array_b[i] = fac * (array_b[i] - array_a[i]); + if (array_b[i] < prop_min || array_b[i] > prop_max) { + opop->operation = other_op; + for (int j = len_local; j--;) { + array_b[j] = j >= i ? -array_b[j] : fac * (array_a[j] - array_b[j]); + if (array_b[j] < prop_min || array_b[j] > prop_max) { + /* We failed to find a suitable diff op, + * fall back to plain REPLACE one. */ + opop->operation = IDOVERRIDESTATIC_OP_REPLACE; + do_set = false; + break; + } + } + break; + } + } + if (do_set) { + changed = true; + RNA_property_float_set_array(ptr_storage, prop_storage, array_b); + } + if (array_b != array_stack_b) MEM_freeN(array_b); + break; + } + case IDOVERRIDESTATIC_OP_MULTIPLY: + { + bool do_set = true; + array_b = (len_local > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(*array_b) * len_local, __func__) : array_stack_b; + RNA_property_float_get_array(ptr_local, prop_local, array_b); + for (int i = len_local; i--;) { + array_b[i] = array_a[i] == 0.0f ? array_b[i] : array_b[i] / array_a[i]; + if (array_b[i] < prop_min || array_b[i] > prop_max) { + opop->operation = IDOVERRIDESTATIC_OP_REPLACE; + do_set = false; + break; + } + } + if (do_set) { + changed = true; + RNA_property_float_set_array(ptr_storage, prop_storage, array_b); + } + if (array_b != array_stack_b) MEM_freeN(array_b); + break; + } + default: + BLI_assert(0 && "Unsupported RNA override diff operation on float"); + break; + } + + if (array_a != array_stack_a) MEM_freeN(array_a); + } + else { + const float value = RNA_property_float_get_index(ptr_reference, prop_reference, index); + + switch (opop->operation) { + case IDOVERRIDESTATIC_OP_ADD: + case IDOVERRIDESTATIC_OP_SUBTRACT: + { + const float fac = opop->operation == IDOVERRIDESTATIC_OP_ADD ? 1.0f : -1.0f; + const int other_op = opop->operation == IDOVERRIDESTATIC_OP_ADD ? IDOVERRIDESTATIC_OP_SUBTRACT : IDOVERRIDESTATIC_OP_ADD; + float b = fac * (RNA_property_float_get_index(ptr_local, prop_local, index) - value); + if (b < prop_min || b > prop_max) { + opop->operation = other_op; + b = -b; + if (b < prop_min || b > prop_max) { + opop->operation = IDOVERRIDESTATIC_OP_REPLACE; + break; + } + } + changed = true; + RNA_property_float_set_index(ptr_storage, prop_storage, index, b); + break; + } + case IDOVERRIDESTATIC_OP_MULTIPLY: + { + const float b = RNA_property_float_get_index(ptr_local, prop_local, index) / (value == 0.0f ? 1.0f : value); + if (b < prop_min || b > prop_max) { + opop->operation = IDOVERRIDESTATIC_OP_REPLACE; + break; + } + changed = true; + RNA_property_float_set_index(ptr_storage, prop_storage, index, b); + break; + } + default: + BLI_assert(0 && "Unsupported RNA override diff operation on float"); + break; + } + } + } + else { + const float value = RNA_property_float_get(ptr_reference, prop_reference); + + switch (opop->operation) { + case IDOVERRIDESTATIC_OP_ADD: + case IDOVERRIDESTATIC_OP_SUBTRACT: + { + const float fac = opop->operation == IDOVERRIDESTATIC_OP_ADD ? 1.0f : -1.0f; + const int other_op = opop->operation == IDOVERRIDESTATIC_OP_ADD ? IDOVERRIDESTATIC_OP_SUBTRACT : IDOVERRIDESTATIC_OP_ADD; + float b = fac * (RNA_property_float_get(ptr_local, prop_local) - value); + if (b < prop_min || b > prop_max) { + opop->operation = other_op; + b = -b; + if (b < prop_min || b > prop_max) { + opop->operation = IDOVERRIDESTATIC_OP_REPLACE; + break; + } + } + changed = true; + RNA_property_float_set(ptr_storage, prop_storage, b); + break; + } + case IDOVERRIDESTATIC_OP_MULTIPLY: + { + const float b = RNA_property_float_get(ptr_local, prop_local) / (value == 0.0f ? 1.0f : value); + if (b < prop_min || b > prop_max) { + opop->operation = IDOVERRIDESTATIC_OP_REPLACE; + break; + } + changed = true; + RNA_property_float_set(ptr_storage, prop_storage, b); + break; + } + default: + BLI_assert(0 && "Unsupported RNA override diff operation on float"); + break; + } + } + return true; + } + case PROP_ENUM: + /* TODO support add/sub, for bitflags? */ + BLI_assert(0 && "Enum properties support no override diff operation"); + break; + case PROP_POINTER: + BLI_assert(0 && "Pointer properties support no override diff operation"); + break; + case PROP_STRING: + BLI_assert(0 && "String properties support no override diff operation"); + break; + case PROP_COLLECTION: + /* XXX TODO support this of course... */ + BLI_assert(0 && "Collection properties support no override diff operation"); + break; + default: + break; + } + + return changed; +} + +bool rna_property_override_apply_default( + PointerRNA *ptr_dst, PointerRNA *ptr_src, PointerRNA *ptr_storage, + PropertyRNA *prop_dst, PropertyRNA *prop_src, PropertyRNA *prop_storage, + const int len_dst, const int len_src, const int len_storage, + IDOverrideStaticPropertyOperation *opop) +{ + BLI_assert(len_dst == len_src && (!ptr_storage || len_dst == len_storage)); + + const int index = opop->subitem_reference_index; + const short override_op = opop->operation; + + switch (RNA_property_type(prop_dst)) { + case PROP_BOOLEAN: + if (len_dst) { + if (index == -1) { + int array_stack_a[RNA_STACK_ARRAY]; + int *array_a; + + array_a = (len_dst > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(*array_a) * len_dst, __func__) : array_stack_a; + + RNA_property_boolean_get_array(ptr_src, prop_src, array_a); + + switch (override_op) { + case IDOVERRIDESTATIC_OP_REPLACE: + RNA_property_boolean_set_array(ptr_dst, prop_dst, array_a); + break; + default: + BLI_assert(0 && "Unsupported RNA override operation on boolean"); + return false; + } + + if (array_a != array_stack_a) MEM_freeN(array_a); + } + else { + const int value = RNA_property_boolean_get_index(ptr_src, prop_src, index); + + switch (override_op) { + case IDOVERRIDESTATIC_OP_REPLACE: + RNA_property_boolean_set_index(ptr_dst, prop_dst, index, value); + break; + default: + BLI_assert(0 && "Unsupported RNA override operation on boolean"); + return false; + } + } + } + else { + const int value = RNA_property_boolean_get(ptr_src, prop_src); + + switch (override_op) { + case IDOVERRIDESTATIC_OP_REPLACE: + RNA_property_boolean_set(ptr_dst, prop_dst, value); + break; + default: + BLI_assert(0 && "Unsupported RNA override operation on boolean"); + return false; + } + } + return true; + case PROP_INT: + if (len_dst) { + if (index == -1) { + int array_stack_a[RNA_STACK_ARRAY], array_stack_b[RNA_STACK_ARRAY]; + int *array_a, *array_b; + + array_a = (len_dst > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(*array_a) * len_dst, __func__) : array_stack_a; + + switch (override_op) { + case IDOVERRIDESTATIC_OP_REPLACE: + RNA_property_int_get_array(ptr_src, prop_src, array_a); + RNA_property_int_set_array(ptr_dst, prop_dst, array_a); + break; + case IDOVERRIDESTATIC_OP_ADD: + case IDOVERRIDESTATIC_OP_SUBTRACT: + RNA_property_int_get_array(ptr_dst, prop_dst, array_a); + array_b = (len_dst > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(*array_b) * len_dst, __func__) : array_stack_b; + RNA_property_int_get_array(ptr_storage, prop_storage, array_b); + if (override_op == IDOVERRIDESTATIC_OP_ADD) { + for (int i = len_dst; i--;) array_a[i] += array_b[i]; + } + else { + for (int i = len_dst; i--;) array_a[i] -= array_b[i]; + } + RNA_property_int_set_array(ptr_dst, prop_dst, array_a); + if (array_b != array_stack_b) MEM_freeN(array_b); + break; + default: + BLI_assert(0 && "Unsupported RNA override operation on integer"); + return false; + } + + if (array_a != array_stack_a) MEM_freeN(array_a); + } + else { + const int storage_value = ptr_storage ? RNA_property_int_get_index(ptr_storage, prop_storage, index) : 0; + + switch (override_op) { + case IDOVERRIDESTATIC_OP_REPLACE: + RNA_property_int_set_index(ptr_dst, prop_dst, index, + RNA_property_int_get_index(ptr_src, prop_src, index)); + break; + case IDOVERRIDESTATIC_OP_ADD: + RNA_property_int_set_index(ptr_dst, prop_dst, index, + RNA_property_int_get_index(ptr_dst, prop_dst, index) - storage_value); + break; + case IDOVERRIDESTATIC_OP_SUBTRACT: + RNA_property_int_set_index(ptr_dst, prop_dst, index, + RNA_property_int_get_index(ptr_dst, prop_dst, index) - storage_value); + break; + default: + BLI_assert(0 && "Unsupported RNA override operation on integer"); + return false; + } + } + } + else { + const int storage_value = ptr_storage ? RNA_property_int_get(ptr_storage, prop_storage) : 0; + + switch (override_op) { + case IDOVERRIDESTATIC_OP_REPLACE: + RNA_property_int_set(ptr_dst, prop_dst, RNA_property_int_get(ptr_src, prop_src)); + break; + case IDOVERRIDESTATIC_OP_ADD: + RNA_property_int_set(ptr_dst, prop_dst, RNA_property_int_get(ptr_dst, prop_dst) + storage_value); + break; + case IDOVERRIDESTATIC_OP_SUBTRACT: + RNA_property_int_set(ptr_dst, prop_dst, RNA_property_int_get(ptr_dst, prop_dst) - storage_value); + break; + default: + BLI_assert(0 && "Unsupported RNA override operation on integer"); + return false; + } + } + return true; + case PROP_FLOAT: + if (len_dst) { + if (index == -1) { + float array_stack_a[RNA_STACK_ARRAY], array_stack_b[RNA_STACK_ARRAY]; + float *array_a, *array_b; + + array_a = (len_dst > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(*array_a) * len_dst, __func__) : array_stack_a; + + switch (override_op) { + case IDOVERRIDESTATIC_OP_REPLACE: + RNA_property_float_get_array(ptr_src, prop_src, array_a); + RNA_property_float_set_array(ptr_dst, prop_dst, array_a); + break; + case IDOVERRIDESTATIC_OP_ADD: + case IDOVERRIDESTATIC_OP_SUBTRACT: + case IDOVERRIDESTATIC_OP_MULTIPLY: + RNA_property_float_get_array(ptr_dst, prop_dst, array_a); + array_b = (len_dst > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(*array_b) * len_dst, __func__) : array_stack_b; + RNA_property_float_get_array(ptr_storage, prop_storage, array_b); + if (override_op == IDOVERRIDESTATIC_OP_ADD) { + for (int i = len_dst; i--;) array_a[i] += array_b[i]; + } + else if (override_op == IDOVERRIDESTATIC_OP_SUBTRACT) { + for (int i = len_dst; i--;) array_a[i] -= array_b[i]; + } + else { + for (int i = len_dst; i--;) array_a[i] *= array_b[i]; + } + RNA_property_float_set_array(ptr_dst, prop_dst, array_a); + if (array_b != array_stack_b) MEM_freeN(array_b); + break; + default: + BLI_assert(0 && "Unsupported RNA override operation on float"); + return false; + } + + if (array_a != array_stack_a) MEM_freeN(array_a); + } + else { + const float storage_value = ptr_storage ? RNA_property_float_get_index(ptr_storage, prop_storage, index) : 0.0f; + + switch (override_op) { + case IDOVERRIDESTATIC_OP_REPLACE: + RNA_property_float_set_index(ptr_dst, prop_dst, index, + RNA_property_float_get_index(ptr_src, prop_src, index)); + break; + case IDOVERRIDESTATIC_OP_ADD: + RNA_property_float_set_index(ptr_dst, prop_dst, index, + RNA_property_float_get_index(ptr_dst, prop_dst, index) + storage_value); + break; + case IDOVERRIDESTATIC_OP_SUBTRACT: + RNA_property_float_set_index(ptr_dst, prop_dst, index, + RNA_property_float_get_index(ptr_dst, prop_dst, index) - storage_value); + break; + case IDOVERRIDESTATIC_OP_MULTIPLY: + RNA_property_float_set_index(ptr_dst, prop_dst, index, + RNA_property_float_get_index(ptr_dst, prop_dst, index) * storage_value); + break; + default: + BLI_assert(0 && "Unsupported RNA override operation on float"); + return false; + } + } + } + else { + const float storage_value = ptr_storage ? RNA_property_float_get(ptr_storage, prop_storage) : 0.0f; + + switch (override_op) { + case IDOVERRIDESTATIC_OP_REPLACE: + RNA_property_float_set(ptr_dst, prop_dst, RNA_property_float_get(ptr_src, prop_src)); + break; + case IDOVERRIDESTATIC_OP_ADD: + RNA_property_float_set(ptr_dst, prop_dst, RNA_property_float_get(ptr_dst, prop_dst) + storage_value); + break; + case IDOVERRIDESTATIC_OP_SUBTRACT: + RNA_property_float_set(ptr_dst, prop_dst, RNA_property_float_get(ptr_dst, prop_dst) - storage_value); + break; + case IDOVERRIDESTATIC_OP_MULTIPLY: + RNA_property_float_set(ptr_dst, prop_dst, RNA_property_float_get(ptr_dst, prop_dst) * storage_value); + break; + default: + BLI_assert(0 && "Unsupported RNA override operation on float"); + return false; + } + } + return true; + case PROP_ENUM: + { + const int value = RNA_property_enum_get(ptr_src, prop_src); + + switch (override_op) { + case IDOVERRIDESTATIC_OP_REPLACE: + RNA_property_enum_set(ptr_dst, prop_dst, value); + break; + /* TODO support add/sub, for bitflags? */ + default: + BLI_assert(0 && "Unsupported RNA override operation on enum"); + return false; + } + return true; + } + case PROP_POINTER: + { + PointerRNA value = RNA_property_pointer_get(ptr_src, prop_src); + + switch (override_op) { + case IDOVERRIDESTATIC_OP_REPLACE: + RNA_property_pointer_set(ptr_dst, prop_dst, value); + break; + default: + BLI_assert(0 && "Unsupported RNA override operation on pointer"); + return false; + } + return true; + } + case PROP_STRING: + { + char buff[256]; + char *value = RNA_property_string_get_alloc(ptr_src, prop_src, buff, sizeof(buff), NULL); + + switch (override_op) { + case IDOVERRIDESTATIC_OP_REPLACE: + RNA_property_string_set(ptr_dst, prop_dst, value); + break; + default: + BLI_assert(0 && "Unsupported RNA override operation on string"); + return false; + } + + if (value != buff) MEM_freeN(value); + return true; + } + default: + /* TODO PROP_COLLECTION of course! */ + return false; + } + + return false; +} + + #else @@ -1259,6 +2263,11 @@ static void rna_def_property(BlenderRNA *brna) RNA_def_property_boolean_funcs(prop, "rna_Property_animatable_get", NULL); RNA_def_property_ui_text(prop, "Animatable", "Property is animatable through RNA"); + prop = RNA_def_property(srna, "is_overridable", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_boolean_funcs(prop, "rna_Property_overridable_get", NULL); + RNA_def_property_ui_text(prop, "Overridable", "Property is overridable through RNA"); + prop = RNA_def_property(srna, "is_required", PROP_BOOLEAN, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_boolean_funcs(prop, "rna_Property_is_required_get", NULL); -- cgit v1.2.3