This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/blenloader/intern/versioning_300.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

2433 lines
90 KiB
C
Raw Normal View History

/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blenloader
*/
/* allow readfile to use deprecated functionality */
#define DNA_DEPRECATED_ALLOW
Refactor IDProperty UI data storage The storage of IDProperty UI data (min, max, default value, etc) is quite complicated. For every property, retrieving a single one of these values involves three string lookups. First for the "_RNA_UI" group property, then another for a group with the property's name, then for the data value name. Not only is this inefficient, it's hard to reason about, unintuitive, and not at all self-explanatory. This commit replaces that system with a UI data struct directly in the IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond storing the description, name, and RNA subtype, derived structs are used to store type specific UI data like min and max. Note that this means that addons using (abusing) the `_RNA_UI` custom property will have to be changed. A few places in the addons repository will be changed after this commit with D9919. **Before** Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group, then the subgroup for the original property, then specific UI data is accessed like any other IDProperty. ``` prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True) prop["min"] = 1.0 ``` **After** After, the `id_properties_ui` function for RNA structs returns a python object specifically for managing an IDProperty's UI data. ``` ui_data = idproperties_owner.id_properties_ui("prop_name") ui_data.update(min=1.0) ``` In addition to `update`, there are now other functions: - `as_dict`: Returns a dictionary of the property's UI data. - `clear`: Removes the property's UI data. - `update_from`: Copy UI data between properties, even if they have different owners. Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
#include <string.h>
#include "CLG_log.h"
Refactor IDProperty UI data storage The storage of IDProperty UI data (min, max, default value, etc) is quite complicated. For every property, retrieving a single one of these values involves three string lookups. First for the "_RNA_UI" group property, then another for a group with the property's name, then for the data value name. Not only is this inefficient, it's hard to reason about, unintuitive, and not at all self-explanatory. This commit replaces that system with a UI data struct directly in the IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond storing the description, name, and RNA subtype, derived structs are used to store type specific UI data like min and max. Note that this means that addons using (abusing) the `_RNA_UI` custom property will have to be changed. A few places in the addons repository will be changed after this commit with D9919. **Before** Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group, then the subgroup for the original property, then specific UI data is accessed like any other IDProperty. ``` prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True) prop["min"] = 1.0 ``` **After** After, the `id_properties_ui` function for RNA structs returns a python object specifically for managing an IDProperty's UI data. ``` ui_data = idproperties_owner.id_properties_ui("prop_name") ui_data.update(min=1.0) ``` In addition to `update`, there are now other functions: - `as_dict`: Returns a dictionary of the property's UI data. - `clear`: Removes the property's UI data. - `update_from`: Copy UI data between properties, even if they have different owners. Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
#include "MEM_guardedalloc.h"
2021-04-22 16:33:37 +10:00
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_string_utils.h"
2021-04-22 16:33:37 +10:00
#include "BLI_utildefines.h"
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_brush_types.h"
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
2021-04-22 16:33:37 +10:00
#include "DNA_genfile.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_lineart_types.h"
#include "DNA_listBase.h"
#include "DNA_material_types.h"
2021-04-22 16:33:37 +10:00
#include "DNA_modifier_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "DNA_text_types.h"
#include "DNA_workspace_types.h"
2021-04-22 16:33:37 +10:00
#include "BKE_action.h"
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
#include "BKE_armature.h"
#include "BKE_asset.h"
#include "BKE_collection.h"
#include "BKE_curve.h"
#include "BKE_deform.h"
#include "BKE_fcurve.h"
#include "BKE_fcurve_driver.h"
Refactor IDProperty UI data storage The storage of IDProperty UI data (min, max, default value, etc) is quite complicated. For every property, retrieving a single one of these values involves three string lookups. First for the "_RNA_UI" group property, then another for a group with the property's name, then for the data value name. Not only is this inefficient, it's hard to reason about, unintuitive, and not at all self-explanatory. This commit replaces that system with a UI data struct directly in the IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond storing the description, name, and RNA subtype, derived structs are used to store type specific UI data like min and max. Note that this means that addons using (abusing) the `_RNA_UI` custom property will have to be changed. A few places in the addons repository will be changed after this commit with D9919. **Before** Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group, then the subgroup for the original property, then specific UI data is accessed like any other IDProperty. ``` prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True) prop["min"] = 1.0 ``` **After** After, the `id_properties_ui` function for RNA structs returns a python object specifically for managing an IDProperty's UI data. ``` ui_data = idproperties_owner.id_properties_ui("prop_name") ui_data.update(min=1.0) ``` In addition to `update`, there are now other functions: - `as_dict`: Returns a dictionary of the property's UI data. - `clear`: Removes the property's UI data. - `update_from`: Copy UI data between properties, even if they have different owners. Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
#include "BKE_idprop.h"
UDIM: Support virtual filenames This implements the design detailed in T92696 to support virtual filenames for UDIM textures. Currently, the following 2 substitution tokens are supported: | Token | Meaning | | ----- | ---- | | <UDIM> | 1001 + u-tile + v-tile * 10 | | <UVTILE> | Equivalent to u<u-tile + 1>_v<v-tile + 1> | Example for u-tile of 3 and v-tile of 1: filename.<UDIM>_ver0023.png --> filename.1014_ver0023.png filename.<UVTILE>_ver0023.png --> filename.u4_v2_ver0023.png For image loading, the existing workflow is unchanged. A user can select one or more image files, belonging to one or more UDIM tile sets, and have Blender load them all as it does today. Now the <UVTILE> format is "guessed" just as the <UDIM> format was guessed before. If guessing fails, the user can simply go into the Image Editor and type the proper substitution in the filename. Once typing is complete, Blender will reload the files and correctly fill the tiles. This workflow is new as attempting to fix the guessing in current versions did not really work, and the user was often stuck with a confusing situation. For image saving, the existing workflow is changed slightly. Currently, when saving, a user has to be sure to type the filename of the first tile (e.g. filename.1001.png) to save the entire UDIM set. The number could differ if they start at a different tile etc. This is confusing. Now, the user should type a filename containing the appropriate substitution token. By default Blender will fill in a default name using the <UDIM> token but the user is free to save out images using <UVTILE> if they wish. Differential Revision: https://developer.blender.org/D13057
2021-12-30 22:06:23 -08:00
#include "BKE_image.h"
#include "BKE_lib_id.h"
#include "BKE_lib_override.h"
#include "BKE_main.h"
#include "BKE_modifier.h"
#include "BKE_node.h"
Refactor IDProperty UI data storage The storage of IDProperty UI data (min, max, default value, etc) is quite complicated. For every property, retrieving a single one of these values involves three string lookups. First for the "_RNA_UI" group property, then another for a group with the property's name, then for the data value name. Not only is this inefficient, it's hard to reason about, unintuitive, and not at all self-explanatory. This commit replaces that system with a UI data struct directly in the IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond storing the description, name, and RNA subtype, derived structs are used to store type specific UI data like min and max. Note that this means that addons using (abusing) the `_RNA_UI` custom property will have to be changed. A few places in the addons repository will be changed after this commit with D9919. **Before** Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group, then the subgroup for the original property, then specific UI data is accessed like any other IDProperty. ``` prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True) prop["min"] = 1.0 ``` **After** After, the `id_properties_ui` function for RNA structs returns a python object specifically for managing an IDProperty's UI data. ``` ui_data = idproperties_owner.id_properties_ui("prop_name") ui_data.update(min=1.0) ``` In addition to `update`, there are now other functions: - `as_dict`: Returns a dictionary of the property's UI data. - `clear`: Removes the property's UI data. - `update_from`: Copy UI data between properties, even if they have different owners. Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
#include "RNA_access.h"
#include "RNA_enum_types.h"
#include "RNA_prototypes.h"
Refactor IDProperty UI data storage The storage of IDProperty UI data (min, max, default value, etc) is quite complicated. For every property, retrieving a single one of these values involves three string lookups. First for the "_RNA_UI" group property, then another for a group with the property's name, then for the data value name. Not only is this inefficient, it's hard to reason about, unintuitive, and not at all self-explanatory. This commit replaces that system with a UI data struct directly in the IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond storing the description, name, and RNA subtype, derived structs are used to store type specific UI data like min and max. Note that this means that addons using (abusing) the `_RNA_UI` custom property will have to be changed. A few places in the addons repository will be changed after this commit with D9919. **Before** Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group, then the subgroup for the original property, then specific UI data is accessed like any other IDProperty. ``` prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True) prop["min"] = 1.0 ``` **After** After, the `id_properties_ui` function for RNA structs returns a python object specifically for managing an IDProperty's UI data. ``` ui_data = idproperties_owner.id_properties_ui("prop_name") ui_data.update(min=1.0) ``` In addition to `update`, there are now other functions: - `as_dict`: Returns a dictionary of the property's UI data. - `clear`: Removes the property's UI data. - `update_from`: Copy UI data between properties, even if they have different owners. Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
#include "BLO_readfile.h"
#include "MEM_guardedalloc.h"
#include "readfile.h"
#include "SEQ_iterator.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
#include "RNA_access.h"
#include "versioning_common.h"
static CLG_LogRef LOG = {"blo.readfile.doversion"};
Refactor IDProperty UI data storage The storage of IDProperty UI data (min, max, default value, etc) is quite complicated. For every property, retrieving a single one of these values involves three string lookups. First for the "_RNA_UI" group property, then another for a group with the property's name, then for the data value name. Not only is this inefficient, it's hard to reason about, unintuitive, and not at all self-explanatory. This commit replaces that system with a UI data struct directly in the IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond storing the description, name, and RNA subtype, derived structs are used to store type specific UI data like min and max. Note that this means that addons using (abusing) the `_RNA_UI` custom property will have to be changed. A few places in the addons repository will be changed after this commit with D9919. **Before** Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group, then the subgroup for the original property, then specific UI data is accessed like any other IDProperty. ``` prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True) prop["min"] = 1.0 ``` **After** After, the `id_properties_ui` function for RNA structs returns a python object specifically for managing an IDProperty's UI data. ``` ui_data = idproperties_owner.id_properties_ui("prop_name") ui_data.update(min=1.0) ``` In addition to `update`, there are now other functions: - `as_dict`: Returns a dictionary of the property's UI data. - `clear`: Removes the property's UI data. - `update_from`: Copy UI data between properties, even if they have different owners. Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
static IDProperty *idproperty_find_ui_container(IDProperty *idprop_group)
{
LISTBASE_FOREACH (IDProperty *, prop, &idprop_group->data.group) {
if (prop->type == IDP_GROUP && STREQ(prop->name, "_RNA_UI")) {
return prop;
}
}
return NULL;
}
static void version_idproperty_move_data_int(IDPropertyUIDataInt *ui_data,
const IDProperty *prop_ui_data)
{
IDProperty *min = IDP_GetPropertyFromGroup(prop_ui_data, "min");
if (min != NULL) {
ui_data->min = ui_data->soft_min = IDP_coerce_to_int_or_zero(min);
}
IDProperty *max = IDP_GetPropertyFromGroup(prop_ui_data, "max");
if (max != NULL) {
ui_data->max = ui_data->soft_max = IDP_coerce_to_int_or_zero(max);
}
IDProperty *soft_min = IDP_GetPropertyFromGroup(prop_ui_data, "soft_min");
if (soft_min != NULL) {
ui_data->soft_min = IDP_coerce_to_int_or_zero(soft_min);
ui_data->soft_min = MIN2(ui_data->soft_min, ui_data->min);
}
IDProperty *soft_max = IDP_GetPropertyFromGroup(prop_ui_data, "soft_max");
if (soft_max != NULL) {
ui_data->soft_max = IDP_coerce_to_int_or_zero(soft_max);
ui_data->soft_max = MAX2(ui_data->soft_max, ui_data->max);
}
IDProperty *step = IDP_GetPropertyFromGroup(prop_ui_data, "step");
if (step != NULL) {
ui_data->step = IDP_coerce_to_int_or_zero(soft_max);
}
IDProperty *default_value = IDP_GetPropertyFromGroup(prop_ui_data, "default");
if (default_value != NULL) {
if (default_value->type == IDP_ARRAY) {
if (default_value->subtype == IDP_INT) {
ui_data->default_array = MEM_malloc_arrayN(default_value->len, sizeof(int), __func__);
memcpy(ui_data->default_array, IDP_Array(default_value), sizeof(int) * default_value->len);
Refactor IDProperty UI data storage The storage of IDProperty UI data (min, max, default value, etc) is quite complicated. For every property, retrieving a single one of these values involves three string lookups. First for the "_RNA_UI" group property, then another for a group with the property's name, then for the data value name. Not only is this inefficient, it's hard to reason about, unintuitive, and not at all self-explanatory. This commit replaces that system with a UI data struct directly in the IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond storing the description, name, and RNA subtype, derived structs are used to store type specific UI data like min and max. Note that this means that addons using (abusing) the `_RNA_UI` custom property will have to be changed. A few places in the addons repository will be changed after this commit with D9919. **Before** Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group, then the subgroup for the original property, then specific UI data is accessed like any other IDProperty. ``` prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True) prop["min"] = 1.0 ``` **After** After, the `id_properties_ui` function for RNA structs returns a python object specifically for managing an IDProperty's UI data. ``` ui_data = idproperties_owner.id_properties_ui("prop_name") ui_data.update(min=1.0) ``` In addition to `update`, there are now other functions: - `as_dict`: Returns a dictionary of the property's UI data. - `clear`: Removes the property's UI data. - `update_from`: Copy UI data between properties, even if they have different owners. Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
ui_data->default_array_len = default_value->len;
}
}
else if (default_value->type == IDP_INT) {
ui_data->default_value = IDP_coerce_to_int_or_zero(default_value);
}
}
}
static void version_idproperty_move_data_float(IDPropertyUIDataFloat *ui_data,
const IDProperty *prop_ui_data)
{
IDProperty *min = IDP_GetPropertyFromGroup(prop_ui_data, "min");
if (min != NULL) {
ui_data->min = ui_data->soft_min = IDP_coerce_to_double_or_zero(min);
}
IDProperty *max = IDP_GetPropertyFromGroup(prop_ui_data, "max");
if (max != NULL) {
ui_data->max = ui_data->soft_max = IDP_coerce_to_double_or_zero(max);
Refactor IDProperty UI data storage The storage of IDProperty UI data (min, max, default value, etc) is quite complicated. For every property, retrieving a single one of these values involves three string lookups. First for the "_RNA_UI" group property, then another for a group with the property's name, then for the data value name. Not only is this inefficient, it's hard to reason about, unintuitive, and not at all self-explanatory. This commit replaces that system with a UI data struct directly in the IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond storing the description, name, and RNA subtype, derived structs are used to store type specific UI data like min and max. Note that this means that addons using (abusing) the `_RNA_UI` custom property will have to be changed. A few places in the addons repository will be changed after this commit with D9919. **Before** Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group, then the subgroup for the original property, then specific UI data is accessed like any other IDProperty. ``` prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True) prop["min"] = 1.0 ``` **After** After, the `id_properties_ui` function for RNA structs returns a python object specifically for managing an IDProperty's UI data. ``` ui_data = idproperties_owner.id_properties_ui("prop_name") ui_data.update(min=1.0) ``` In addition to `update`, there are now other functions: - `as_dict`: Returns a dictionary of the property's UI data. - `clear`: Removes the property's UI data. - `update_from`: Copy UI data between properties, even if they have different owners. Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
}
IDProperty *soft_min = IDP_GetPropertyFromGroup(prop_ui_data, "soft_min");
if (soft_min != NULL) {
ui_data->soft_min = IDP_coerce_to_double_or_zero(soft_min);
ui_data->soft_min = MAX2(ui_data->soft_min, ui_data->min);
}
IDProperty *soft_max = IDP_GetPropertyFromGroup(prop_ui_data, "soft_max");
if (soft_max != NULL) {
ui_data->soft_max = IDP_coerce_to_double_or_zero(soft_max);
ui_data->soft_max = MIN2(ui_data->soft_max, ui_data->max);
}
IDProperty *step = IDP_GetPropertyFromGroup(prop_ui_data, "step");
if (step != NULL) {
ui_data->step = IDP_coerce_to_float_or_zero(step);
}
IDProperty *precision = IDP_GetPropertyFromGroup(prop_ui_data, "precision");
if (precision != NULL) {
ui_data->precision = IDP_coerce_to_int_or_zero(precision);
}
IDProperty *default_value = IDP_GetPropertyFromGroup(prop_ui_data, "default");
if (default_value != NULL) {
if (default_value->type == IDP_ARRAY) {
const int array_len = default_value->len;
ui_data->default_array_len = array_len;
if (default_value->subtype == IDP_FLOAT) {
ui_data->default_array = MEM_malloc_arrayN(array_len, sizeof(double), __func__);
const float *old_default_array = IDP_Array(default_value);
for (int i = 0; i < ui_data->default_array_len; i++) {
ui_data->default_array[i] = (double)old_default_array[i];
}
}
else if (default_value->subtype == IDP_DOUBLE) {
ui_data->default_array = MEM_malloc_arrayN(array_len, sizeof(double), __func__);
memcpy(ui_data->default_array, IDP_Array(default_value), sizeof(double) * array_len);
Refactor IDProperty UI data storage The storage of IDProperty UI data (min, max, default value, etc) is quite complicated. For every property, retrieving a single one of these values involves three string lookups. First for the "_RNA_UI" group property, then another for a group with the property's name, then for the data value name. Not only is this inefficient, it's hard to reason about, unintuitive, and not at all self-explanatory. This commit replaces that system with a UI data struct directly in the IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond storing the description, name, and RNA subtype, derived structs are used to store type specific UI data like min and max. Note that this means that addons using (abusing) the `_RNA_UI` custom property will have to be changed. A few places in the addons repository will be changed after this commit with D9919. **Before** Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group, then the subgroup for the original property, then specific UI data is accessed like any other IDProperty. ``` prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True) prop["min"] = 1.0 ``` **After** After, the `id_properties_ui` function for RNA structs returns a python object specifically for managing an IDProperty's UI data. ``` ui_data = idproperties_owner.id_properties_ui("prop_name") ui_data.update(min=1.0) ``` In addition to `update`, there are now other functions: - `as_dict`: Returns a dictionary of the property's UI data. - `clear`: Removes the property's UI data. - `update_from`: Copy UI data between properties, even if they have different owners. Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
}
}
else if (ELEM(default_value->type, IDP_DOUBLE, IDP_FLOAT)) {
ui_data->default_value = IDP_coerce_to_double_or_zero(default_value);
}
}
}
static void version_idproperty_move_data_string(IDPropertyUIDataString *ui_data,
const IDProperty *prop_ui_data)
{
IDProperty *default_value = IDP_GetPropertyFromGroup(prop_ui_data, "default");
if (default_value != NULL && default_value->type == IDP_STRING) {
ui_data->default_value = BLI_strdup(IDP_String(default_value));
}
}
static void version_idproperty_ui_data(IDProperty *idprop_group)
{
if (idprop_group == NULL) { /* NULL check here to reduce verbosity of calls to this function. */
return;
}
IDProperty *ui_container = idproperty_find_ui_container(idprop_group);
if (ui_container == NULL) {
return;
}
LISTBASE_FOREACH (IDProperty *, prop, &idprop_group->data.group) {
IDProperty *prop_ui_data = IDP_GetPropertyFromGroup(ui_container, prop->name);
if (prop_ui_data == NULL) {
continue;
}
if (!IDP_ui_data_supported(prop)) {
continue;
}
IDPropertyUIData *ui_data = IDP_ui_data_ensure(prop);
IDProperty *subtype = IDP_GetPropertyFromGroup(prop_ui_data, "subtype");
if (subtype != NULL && subtype->type == IDP_STRING) {
const char *subtype_string = IDP_String(subtype);
int result = PROP_NONE;
RNA_enum_value_from_id(rna_enum_property_subtype_items, subtype_string, &result);
ui_data->rna_subtype = result;
}
IDProperty *description = IDP_GetPropertyFromGroup(prop_ui_data, "description");
if (description != NULL && description->type == IDP_STRING) {
ui_data->description = BLI_strdup(IDP_String(description));
}
/* Type specific data. */
switch (IDP_ui_data_type(prop)) {
case IDP_UI_DATA_TYPE_STRING:
version_idproperty_move_data_string((IDPropertyUIDataString *)ui_data, prop_ui_data);
break;
case IDP_UI_DATA_TYPE_ID:
break;
case IDP_UI_DATA_TYPE_INT:
version_idproperty_move_data_int((IDPropertyUIDataInt *)ui_data, prop_ui_data);
break;
case IDP_UI_DATA_TYPE_FLOAT:
version_idproperty_move_data_float((IDPropertyUIDataFloat *)ui_data, prop_ui_data);
break;
case IDP_UI_DATA_TYPE_UNSUPPORTED:
BLI_assert_unreachable();
break;
}
IDP_FreeFromGroup(ui_container, prop_ui_data);
}
IDP_FreeFromGroup(idprop_group, ui_container);
}
static void do_versions_idproperty_bones_recursive(Bone *bone)
{
version_idproperty_ui_data(bone->prop);
LISTBASE_FOREACH (Bone *, child_bone, &bone->childbase) {
do_versions_idproperty_bones_recursive(child_bone);
}
}
static void do_versions_idproperty_seq_recursive(ListBase *seqbase)
{
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
version_idproperty_ui_data(seq->prop);
if (seq->type == SEQ_TYPE_META) {
do_versions_idproperty_seq_recursive(&seq->seqbase);
}
}
}
Refactor IDProperty UI data storage The storage of IDProperty UI data (min, max, default value, etc) is quite complicated. For every property, retrieving a single one of these values involves three string lookups. First for the "_RNA_UI" group property, then another for a group with the property's name, then for the data value name. Not only is this inefficient, it's hard to reason about, unintuitive, and not at all self-explanatory. This commit replaces that system with a UI data struct directly in the IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond storing the description, name, and RNA subtype, derived structs are used to store type specific UI data like min and max. Note that this means that addons using (abusing) the `_RNA_UI` custom property will have to be changed. A few places in the addons repository will be changed after this commit with D9919. **Before** Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group, then the subgroup for the original property, then specific UI data is accessed like any other IDProperty. ``` prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True) prop["min"] = 1.0 ``` **After** After, the `id_properties_ui` function for RNA structs returns a python object specifically for managing an IDProperty's UI data. ``` ui_data = idproperties_owner.id_properties_ui("prop_name") ui_data.update(min=1.0) ``` In addition to `update`, there are now other functions: - `as_dict`: Returns a dictionary of the property's UI data. - `clear`: Removes the property's UI data. - `update_from`: Copy UI data between properties, even if they have different owners. Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
/**
* For every data block that supports them, initialize the new IDProperty UI data struct based on
* the old more complicated storage. Assumes only the top level of IDProperties below the parent
* group had UI data in a "_RNA_UI" group.
*
* \note The following IDProperty groups in DNA aren't exposed in the UI or are runtime-only, so
* they don't have UI data: wmOperator, bAddon, bUserMenuItem_Op, wmKeyMapItem, wmKeyConfigPref,
* uiList, FFMpegCodecData, View3DShading, bToolRef, TimeMarker, ViewLayer, bPoseChannel.
*/
static void do_versions_idproperty_ui_data(Main *bmain)
{
/* ID data. */
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
IDProperty *idprop_group = IDP_GetProperties(id, false);
version_idproperty_ui_data(idprop_group);
}
FOREACH_MAIN_ID_END;
/* Bones. */
LISTBASE_FOREACH (bArmature *, armature, &bmain->armatures) {
LISTBASE_FOREACH (Bone *, bone, &armature->bonebase) {
do_versions_idproperty_bones_recursive(bone);
}
}
/* Nodes and node sockets. */
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
version_idproperty_ui_data(node->prop);
}
LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs) {
version_idproperty_ui_data(socket->prop);
}
LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs) {
version_idproperty_ui_data(socket->prop);
}
}
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
/* The UI data from exposed node modifier properties is just copied from the corresponding node
* group, but the copying only runs when necessary, so we still need to version data here. */
Refactor IDProperty UI data storage The storage of IDProperty UI data (min, max, default value, etc) is quite complicated. For every property, retrieving a single one of these values involves three string lookups. First for the "_RNA_UI" group property, then another for a group with the property's name, then for the data value name. Not only is this inefficient, it's hard to reason about, unintuitive, and not at all self-explanatory. This commit replaces that system with a UI data struct directly in the IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond storing the description, name, and RNA subtype, derived structs are used to store type specific UI data like min and max. Note that this means that addons using (abusing) the `_RNA_UI` custom property will have to be changed. A few places in the addons repository will be changed after this commit with D9919. **Before** Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group, then the subgroup for the original property, then specific UI data is accessed like any other IDProperty. ``` prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True) prop["min"] = 1.0 ``` **After** After, the `id_properties_ui` function for RNA structs returns a python object specifically for managing an IDProperty's UI data. ``` ui_data = idproperties_owner.id_properties_ui("prop_name") ui_data.update(min=1.0) ``` In addition to `update`, there are now other functions: - `as_dict`: Returns a dictionary of the property's UI data. - `clear`: Removes the property's UI data. - `update_from`: Copy UI data between properties, even if they have different owners. Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type == eModifierType_Nodes) {
NodesModifierData *nmd = (NodesModifierData *)md;
version_idproperty_ui_data(nmd->settings.properties);
}
}
/* Object post bones. */
if (ob->type == OB_ARMATURE && ob->pose != NULL) {
LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
version_idproperty_ui_data(pchan->prop);
}
}
Refactor IDProperty UI data storage The storage of IDProperty UI data (min, max, default value, etc) is quite complicated. For every property, retrieving a single one of these values involves three string lookups. First for the "_RNA_UI" group property, then another for a group with the property's name, then for the data value name. Not only is this inefficient, it's hard to reason about, unintuitive, and not at all self-explanatory. This commit replaces that system with a UI data struct directly in the IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond storing the description, name, and RNA subtype, derived structs are used to store type specific UI data like min and max. Note that this means that addons using (abusing) the `_RNA_UI` custom property will have to be changed. A few places in the addons repository will be changed after this commit with D9919. **Before** Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group, then the subgroup for the original property, then specific UI data is accessed like any other IDProperty. ``` prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True) prop["min"] = 1.0 ``` **After** After, the `id_properties_ui` function for RNA structs returns a python object specifically for managing an IDProperty's UI data. ``` ui_data = idproperties_owner.id_properties_ui("prop_name") ui_data.update(min=1.0) ``` In addition to `update`, there are now other functions: - `as_dict`: Returns a dictionary of the property's UI data. - `clear`: Removes the property's UI data. - `update_from`: Copy UI data between properties, even if they have different owners. Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
}
/* Sequences. */
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (scene->ed != NULL) {
do_versions_idproperty_seq_recursive(&scene->ed->seqbase);
Refactor IDProperty UI data storage The storage of IDProperty UI data (min, max, default value, etc) is quite complicated. For every property, retrieving a single one of these values involves three string lookups. First for the "_RNA_UI" group property, then another for a group with the property's name, then for the data value name. Not only is this inefficient, it's hard to reason about, unintuitive, and not at all self-explanatory. This commit replaces that system with a UI data struct directly in the IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond storing the description, name, and RNA subtype, derived structs are used to store type specific UI data like min and max. Note that this means that addons using (abusing) the `_RNA_UI` custom property will have to be changed. A few places in the addons repository will be changed after this commit with D9919. **Before** Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group, then the subgroup for the original property, then specific UI data is accessed like any other IDProperty. ``` prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True) prop["min"] = 1.0 ``` **After** After, the `id_properties_ui` function for RNA structs returns a python object specifically for managing an IDProperty's UI data. ``` ui_data = idproperties_owner.id_properties_ui("prop_name") ui_data.update(min=1.0) ``` In addition to `update`, there are now other functions: - `as_dict`: Returns a dictionary of the property's UI data. - `clear`: Removes the property's UI data. - `update_from`: Copy UI data between properties, even if they have different owners. Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
}
}
}
static void sort_linked_ids(Main *bmain)
{
ListBase *lb;
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
ListBase temp_list;
BLI_listbase_clear(&temp_list);
LISTBASE_FOREACH_MUTABLE (ID *, id, lb) {
if (ID_IS_LINKED(id)) {
BLI_remlink(lb, id);
BLI_addtail(&temp_list, id);
id_sort_by_name(&temp_list, id, NULL);
}
}
BLI_movelisttolist(lb, &temp_list);
}
FOREACH_MAIN_LISTBASE_END;
}
static void assert_sorted_ids(Main *bmain)
{
#ifndef NDEBUG
ListBase *lb;
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
ID *id_prev = NULL;
LISTBASE_FOREACH (ID *, id, lb) {
if (id_prev == NULL) {
continue;
}
BLI_assert(id_prev->lib != id->lib || BLI_strcasecmp(id_prev->name, id->name) < 0);
}
}
FOREACH_MAIN_LISTBASE_END;
#else
UNUSED_VARS_NDEBUG(bmain);
#endif
}
static void move_vertex_group_names_to_object_data(Main *bmain)
{
LISTBASE_FOREACH (Object *, object, &bmain->objects) {
if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) {
ListBase *new_defbase = BKE_object_defgroup_list_mutable(object);
/* Choose the longest vertex group name list among all linked duplicates. */
if (BLI_listbase_count(&object->defbase) < BLI_listbase_count(new_defbase)) {
BLI_freelistN(&object->defbase);
}
else {
/* Clear the list in case the it was already assigned from another object. */
BLI_freelistN(new_defbase);
*new_defbase = object->defbase;
BKE_object_defgroup_active_index_set(object, object->actdef);
}
}
}
}
static void do_versions_sequencer_speed_effect_recursive(Scene *scene, const ListBase *seqbase)
{
/* Old SpeedControlVars->flags. */
#define SEQ_SPEED_INTEGRATE (1 << 0)
#define SEQ_SPEED_COMPRESS_IPO_Y (1 << 2)
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
if (seq->type == SEQ_TYPE_SPEED) {
SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
const char *substr = NULL;
float globalSpeed = v->globalSpeed;
if (seq->flag & SEQ_USE_EFFECT_DEFAULT_FADE) {
if (globalSpeed == 1.0f) {
v->speed_control_type = SEQ_SPEED_STRETCH;
}
else {
v->speed_control_type = SEQ_SPEED_MULTIPLY;
v->speed_fader = globalSpeed *
((float)seq->seq1->len /
max_ff((float)(seq->seq1->enddisp - seq->seq1->start), 1.0f));
}
}
else if (v->flags & SEQ_SPEED_INTEGRATE) {
v->speed_control_type = SEQ_SPEED_MULTIPLY;
v->speed_fader = seq->speed_fader * globalSpeed;
}
else if (v->flags & SEQ_SPEED_COMPRESS_IPO_Y) {
globalSpeed *= 100.0f;
v->speed_control_type = SEQ_SPEED_LENGTH;
v->speed_fader_length = seq->speed_fader * globalSpeed;
substr = "speed_length";
}
else {
v->speed_control_type = SEQ_SPEED_FRAME_NUMBER;
v->speed_fader_frame_number = (int)(seq->speed_fader * globalSpeed);
substr = "speed_frame_number";
}
v->flags &= ~(SEQ_SPEED_INTEGRATE | SEQ_SPEED_COMPRESS_IPO_Y);
if (substr || globalSpeed != 1.0f) {
FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_factor", 0, NULL);
if (fcu) {
if (globalSpeed != 1.0f) {
for (int i = 0; i < fcu->totvert; i++) {
BezTriple *bezt = &fcu->bezt[i];
bezt->vec[0][1] *= globalSpeed;
bezt->vec[1][1] *= globalSpeed;
bezt->vec[2][1] *= globalSpeed;
}
}
if (substr) {
char *new_path = BLI_str_replaceN(fcu->rna_path, "speed_factor", substr);
MEM_freeN(fcu->rna_path);
fcu->rna_path = new_path;
}
}
}
}
else if (seq->type == SEQ_TYPE_META) {
do_versions_sequencer_speed_effect_recursive(scene, &seq->seqbase);
}
}
#undef SEQ_SPEED_INTEGRATE
#undef SEQ_SPEED_COMPRESS_IPO_Y
}
static bool do_versions_sequencer_color_tags(Sequence *seq, void *UNUSED(user_data))
{
seq->color_tag = SEQUENCE_COLOR_NONE;
return true;
}
static bool do_versions_sequencer_color_balance_sop(Sequence *seq, void *UNUSED(user_data))
{
LISTBASE_FOREACH (SequenceModifierData *, smd, &seq->modifiers) {
if (smd->type == seqModifierType_ColorBalance) {
StripColorBalance *cb = &((ColorBalanceModifierData *)smd)->color_balance;
cb->method = SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN;
for (int i = 0; i < 3; i++) {
copy_v3_fl(cb->slope, 1.0f);
copy_v3_fl(cb->offset, 1.0f);
copy_v3_fl(cb->power, 1.0f);
}
}
}
return true;
}
static bNodeLink *find_connected_link(bNodeTree *ntree, bNodeSocket *in_socket)
{
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
if (link->tosock == in_socket) {
return link;
}
}
return NULL;
}
static void add_realize_instances_before_socket(bNodeTree *ntree,
bNode *node,
bNodeSocket *geometry_socket)
{
BLI_assert(geometry_socket->type == SOCK_GEOMETRY);
bNodeLink *link = find_connected_link(ntree, geometry_socket);
if (link == NULL) {
return;
}
/* If the realize instances node is already before this socket, no need to continue. */
if (link->fromnode->type == GEO_NODE_REALIZE_INSTANCES) {
return;
}
bNode *realize_node = nodeAddStaticNode(NULL, ntree, GEO_NODE_REALIZE_INSTANCES);
realize_node->parent = node->parent;
realize_node->locx = node->locx - 100;
realize_node->locy = node->locy;
nodeAddLink(ntree, link->fromnode, link->fromsock, realize_node, realize_node->inputs.first);
link->fromnode = realize_node;
link->fromsock = realize_node->outputs.first;
}
/**
* If a node used to realize instances implicitly and will no longer do so in 3.0, add a "Realize
* Instances" node in front of it to avoid changing behavior. Don't do this if the node will be
* replaced anyway though.
*/
static void version_geometry_nodes_add_realize_instance_nodes(bNodeTree *ntree)
{
LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) {
if (ELEM(node->type,
GEO_NODE_CAPTURE_ATTRIBUTE,
GEO_NODE_SEPARATE_COMPONENTS,
GEO_NODE_CONVEX_HULL,
GEO_NODE_CURVE_LENGTH,
GEO_NODE_MESH_BOOLEAN,
GEO_NODE_FILLET_CURVE,
GEO_NODE_RESAMPLE_CURVE,
GEO_NODE_CURVE_TO_MESH,
GEO_NODE_TRIM_CURVE,
GEO_NODE_REPLACE_MATERIAL,
GEO_NODE_SUBDIVIDE_MESH,
GEO_NODE_TRIANGULATE)) {
bNodeSocket *geometry_socket = node->inputs.first;
add_realize_instances_before_socket(ntree, node, geometry_socket);
}
/* Also realize instances for the profile input of the curve to mesh node. */
if (node->type == GEO_NODE_CURVE_TO_MESH) {
bNodeSocket *profile_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
add_realize_instances_before_socket(ntree, node, profile_socket);
}
}
}
/**
* The geometry nodes modifier used to realize instances for the next modifier implicitly. Now it
* is done with the realize instances node. It also used to convert meshes to point clouds
* automatically, which is also now done with a specific node.
*/
static bNodeTree *add_realize_node_tree(Main *bmain)
{
bNodeTree *node_tree = ntreeAddTree(bmain, "Realize Instances 2.93 Legacy", "GeometryNodeTree");
ntreeAddSocketInterface(node_tree, SOCK_IN, "NodeSocketGeometry", "Geometry");
ntreeAddSocketInterface(node_tree, SOCK_OUT, "NodeSocketGeometry", "Geometry");
bNode *group_input = nodeAddStaticNode(NULL, node_tree, NODE_GROUP_INPUT);
group_input->locx = -400.0f;
bNode *group_output = nodeAddStaticNode(NULL, node_tree, NODE_GROUP_OUTPUT);
group_output->locx = 500.0f;
group_output->flag |= NODE_DO_OUTPUT;
bNode *join = nodeAddStaticNode(NULL, node_tree, GEO_NODE_JOIN_GEOMETRY);
join->locx = group_output->locx - 175.0f;
join->locy = group_output->locy;
bNode *conv = nodeAddStaticNode(NULL, node_tree, GEO_NODE_POINTS_TO_VERTICES);
conv->locx = join->locx - 175.0f;
conv->locy = join->locy - 70.0;
bNode *separate = nodeAddStaticNode(NULL, node_tree, GEO_NODE_SEPARATE_COMPONENTS);
separate->locx = join->locx - 350.0f;
separate->locy = join->locy + 50.0f;
bNode *realize = nodeAddStaticNode(NULL, node_tree, GEO_NODE_REALIZE_INSTANCES);
realize->locx = separate->locx - 200.0f;
realize->locy = join->locy;
nodeAddLink(node_tree, group_input, group_input->outputs.first, realize, realize->inputs.first);
nodeAddLink(node_tree, realize, realize->outputs.first, separate, separate->inputs.first);
nodeAddLink(node_tree, conv, conv->outputs.first, join, join->inputs.first);
nodeAddLink(node_tree, separate, BLI_findlink(&separate->outputs, 3), join, join->inputs.first);
nodeAddLink(node_tree, separate, BLI_findlink(&separate->outputs, 1), conv, conv->inputs.first);
nodeAddLink(node_tree, separate, BLI_findlink(&separate->outputs, 2), join, join->inputs.first);
nodeAddLink(node_tree, separate, separate->outputs.first, join, join->inputs.first);
nodeAddLink(node_tree, join, join->outputs.first, group_output, group_output->inputs.first);
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
nodeSetSelected(node, false);
}
Nodes: refactor node tree update handling Goals of this refactor: * More unified approach to updating everything that needs to be updated after a change in a node tree. * The updates should happen in the correct order and quadratic or worse algorithms should be avoided. * Improve detection of changes to the output to avoid tagging the depsgraph when it's not necessary. * Move towards a more declarative style of defining nodes by having a more centralized update procedure. The refactor consists of two main parts: * Node tree tagging and update refactor. * Generally, when changes are done to a node tree, it is tagged dirty until a global update function is called that updates everything in the correct order. * The tagging is more fine-grained compared to before, to allow for more precise depsgraph update tagging. * Depsgraph changes. * The shading specific depsgraph node for node trees as been removed. * Instead, there is a new `NTREE_OUTPUT` depsgrap node, which is only tagged when the output of the node tree changed (e.g. the Group Output or Material Output node). * The copy-on-write relation from node trees to the data block they are embedded in is now non-flushing. This avoids e.g. triggering a material update after the shader node tree changed in unrelated ways. Instead the material has a flushing relation to the new `NTREE_OUTPUT` node now. * The depsgraph no longer reports data block changes through to cycles through `Depsgraph.updates` when only the node tree changed in ways that do not affect the output. Avoiding unnecessary updates seems to work well for geometry nodes and cycles. The situation is a bit worse when there are drivers on the node tree, but that could potentially be improved separately in the future. Avoiding updates in eevee and the compositor is more tricky, but also less urgent. * Eevee updates are triggered by calling `DRW_notify_view_update` in `ED_render_view3d_update` indirectly from `DEG_editors_update`. * Compositor updates are triggered by `ED_node_composite_job` in `node_area_refresh`. This is triggered by calling `ED_area_tag_refresh` in `node_area_listener`. Removing updates always has the risk of breaking some dependency that no one was aware of. It's not unlikely that this will happen here as well. Adding back missing updates should be quite a bit easier than getting rid of unnecessary updates though. Differential Revision: https://developer.blender.org/D13246
2021-12-21 15:18:56 +01:00
version_socket_update_is_used(node_tree);
return node_tree;
}
void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
{
if (MAIN_VERSION_ATLEAST(bmain, 300, 0) && !MAIN_VERSION_ATLEAST(bmain, 300, 1)) {
/* Set zero user text objects to have a fake user. */
LISTBASE_FOREACH (Text *, text, &bmain->texts) {
if (text->id.us == 0) {
id_fake_user_set(&text->id);
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 3)) {
sort_linked_ids(bmain);
assert_sorted_ids(bmain);
}
if (MAIN_VERSION_ATLEAST(bmain, 300, 3)) {
assert_sorted_ids(bmain);
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 11)) {
move_vertex_group_names_to_object_data(bmain);
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 13)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (scene->ed != NULL) {
do_versions_sequencer_speed_effect_recursive(scene, &scene->ed->seqbase);
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 25)) {
version_node_socket_index_animdata(bmain, NTREE_SHADER, SH_NODE_BSDF_PRINCIPLED, 4, 2, 25);
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 26)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *tool_settings = scene->toolsettings;
ImagePaintSettings *imapaint = &tool_settings->imapaint;
if (imapaint->canvas != NULL &&
ELEM(imapaint->canvas->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
imapaint->canvas = NULL;
}
if (imapaint->stencil != NULL &&
ELEM(imapaint->stencil->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
imapaint->stencil = NULL;
}
if (imapaint->clone != NULL &&
ELEM(imapaint->clone->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
imapaint->clone = NULL;
}
}
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
2021-09-24 10:45:36 +02:00
if (brush->clone.image != NULL &&
ELEM(brush->clone.image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
brush->clone.image = NULL;
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 28)) {
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type == NTREE_GEOMETRY) {
version_geometry_nodes_add_realize_instance_nodes(ntree);
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 30)) {
do_versions_idproperty_ui_data(bmain);
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 32)) {
/* Update Switch Node Non-Fields switch input to Switch_001. */
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type != NTREE_GEOMETRY) {
continue;
}
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
if (link->tonode->type == GEO_NODE_SWITCH) {
if (STREQ(link->tosock->identifier, "Switch")) {
bNode *to_node = link->tonode;
uint8_t mode = ((NodeSwitch *)to_node->storage)->input_type;
if (ELEM(mode,
SOCK_GEOMETRY,
SOCK_OBJECT,
SOCK_COLLECTION,
SOCK_TEXTURE,
SOCK_MATERIAL)) {
link->tosock = link->tosock->next;
}
}
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 33)) {
/* This was missing from #move_vertex_group_names_to_object_data. */
LISTBASE_FOREACH (Object *, object, &bmain->objects) {
if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) {
/* This uses the fact that the active vertex group index starts counting at 1. */
if (BKE_object_defgroup_active_index_get(object) == 0) {
BKE_object_defgroup_active_index_set(object, object->actdef);
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 35)) {
/* Add a new modifier to realize instances from previous modifiers.
* Previously that was done automatically by geometry nodes. */
bNodeTree *realize_instances_node_tree = NULL;
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
LISTBASE_FOREACH_MUTABLE (ModifierData *, md, &ob->modifiers) {
if (md->type != eModifierType_Nodes) {
continue;
}
if (md->next == NULL) {
break;
}
if (md->next->type == eModifierType_Nodes) {
continue;
}
NodesModifierData *nmd = (NodesModifierData *)md;
if (nmd->node_group == NULL) {
continue;
}
NodesModifierData *new_nmd = (NodesModifierData *)BKE_modifier_new(eModifierType_Nodes);
STRNCPY(new_nmd->modifier.name, "Realize Instances 2.93 Legacy");
BKE_modifier_unique_name(&ob->modifiers, &new_nmd->modifier);
BLI_insertlinkafter(&ob->modifiers, md, new_nmd);
if (realize_instances_node_tree == NULL) {
realize_instances_node_tree = add_realize_node_tree(bmain);
}
new_nmd->node_group = realize_instances_node_tree;
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 37)) {
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type == NTREE_GEOMETRY) {
LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) {
if (node->type == GEO_NODE_BOUNDING_BOX) {
bNodeSocket *geometry_socket = node->inputs.first;
add_realize_instances_before_socket(ntree, node, geometry_socket);
}
}
}
}
}
2022-01-28 11:11:11 +01:00
if (!MAIN_VERSION_ATLEAST(bmain, 301, 6)) {
{ /* Ensure driver variable names are unique within the driver. */
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
AnimData *adt = BKE_animdata_from_id(id);
if (adt == NULL) {
continue;
}
LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) {
ChannelDriver *driver = fcu->driver;
/* Ensure the uniqueness front to back. Given a list of identically
* named variables, the last one gets to keep its original name. This
* matches the evaluation order, and thus shouldn't change the evaluated
* value of the driver expression. */
LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
BLI_uniquename(&driver->variables,
dvar,
dvar->name,
'_',
offsetof(DriverVar, name),
sizeof(dvar->name));
}
}
}
FOREACH_MAIN_ID_END;
}
UDIM: Support virtual filenames This implements the design detailed in T92696 to support virtual filenames for UDIM textures. Currently, the following 2 substitution tokens are supported: | Token | Meaning | | ----- | ---- | | <UDIM> | 1001 + u-tile + v-tile * 10 | | <UVTILE> | Equivalent to u<u-tile + 1>_v<v-tile + 1> | Example for u-tile of 3 and v-tile of 1: filename.<UDIM>_ver0023.png --> filename.1014_ver0023.png filename.<UVTILE>_ver0023.png --> filename.u4_v2_ver0023.png For image loading, the existing workflow is unchanged. A user can select one or more image files, belonging to one or more UDIM tile sets, and have Blender load them all as it does today. Now the <UVTILE> format is "guessed" just as the <UDIM> format was guessed before. If guessing fails, the user can simply go into the Image Editor and type the proper substitution in the filename. Once typing is complete, Blender will reload the files and correctly fill the tiles. This workflow is new as attempting to fix the guessing in current versions did not really work, and the user was often stuck with a confusing situation. For image saving, the existing workflow is changed slightly. Currently, when saving, a user has to be sure to type the filename of the first tile (e.g. filename.1001.png) to save the entire UDIM set. The number could differ if they start at a different tile etc. This is confusing. Now, the user should type a filename containing the appropriate substitution token. By default Blender will fill in a default name using the <UDIM> token but the user is free to save out images using <UVTILE> if they wish. Differential Revision: https://developer.blender.org/D13057
2021-12-30 22:06:23 -08:00
/* Ensure tiled image sources contain a UDIM token. */
LISTBASE_FOREACH (Image *, ima, &bmain->images) {
if (ima->source == IMA_SRC_TILED) {
char *filename = (char *)BLI_path_basename(ima->filepath);
BKE_image_ensure_tile_token(filename);
UDIM: Support virtual filenames This implements the design detailed in T92696 to support virtual filenames for UDIM textures. Currently, the following 2 substitution tokens are supported: | Token | Meaning | | ----- | ---- | | <UDIM> | 1001 + u-tile + v-tile * 10 | | <UVTILE> | Equivalent to u<u-tile + 1>_v<v-tile + 1> | Example for u-tile of 3 and v-tile of 1: filename.<UDIM>_ver0023.png --> filename.1014_ver0023.png filename.<UVTILE>_ver0023.png --> filename.u4_v2_ver0023.png For image loading, the existing workflow is unchanged. A user can select one or more image files, belonging to one or more UDIM tile sets, and have Blender load them all as it does today. Now the <UVTILE> format is "guessed" just as the <UDIM> format was guessed before. If guessing fails, the user can simply go into the Image Editor and type the proper substitution in the filename. Once typing is complete, Blender will reload the files and correctly fill the tiles. This workflow is new as attempting to fix the guessing in current versions did not really work, and the user was often stuck with a confusing situation. For image saving, the existing workflow is changed slightly. Currently, when saving, a user has to be sure to type the filename of the first tile (e.g. filename.1001.png) to save the entire UDIM set. The number could differ if they start at a different tile etc. This is confusing. Now, the user should type a filename containing the appropriate substitution token. By default Blender will fill in a default name using the <UDIM> token but the user is free to save out images using <UVTILE> if they wish. Differential Revision: https://developer.blender.org/D13057
2021-12-30 22:06:23 -08:00
}
}
}
2022-01-28 11:11:11 +01:00
/**
* Versioning code until next subversion bump goes here.
*
* \note Be sure to check when bumping the version:
* - #blo_do_versions_300 in this file.
* - "versioning_userdef.c", #blo_do_versions_userdef
* - "versioning_userdef.c", #do_versions_theme
*
* \note Keep this message at the bottom of the function.
*/
{
/* Keep this block, even when empty. */
}
}
static void version_switch_node_input_prefix(Main *bmain)
{
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_GEOMETRY) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == GEO_NODE_SWITCH) {
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
/* Skip the "switch" socket. */
if (socket == node->inputs.first) {
continue;
}
strcpy(socket->name, socket->name[0] == 'A' ? "False" : "True");
/* Replace "A" and "B", but keep the unique number suffix at the end. */
char number_suffix[8];
BLI_strncpy(number_suffix, socket->identifier + 1, sizeof(number_suffix));
strcpy(socket->identifier, socket->name);
strcat(socket->identifier, number_suffix);
}
}
}
}
}
FOREACH_NODETREE_END;
}
static bool replace_bbone_len_scale_rnapath(char **p_old_path, int *p_index)
{
char *old_path = *p_old_path;
if (old_path == NULL) {
return false;
}
int len = strlen(old_path);
if (BLI_str_endswith(old_path, ".bbone_curveiny") ||
BLI_str_endswith(old_path, ".bbone_curveouty")) {
old_path[len - 1] = 'z';
return true;
}
if (BLI_str_endswith(old_path, ".bbone_scaleinx") ||
BLI_str_endswith(old_path, ".bbone_scaleiny") ||
BLI_str_endswith(old_path, ".bbone_scaleoutx") ||
BLI_str_endswith(old_path, ".bbone_scaleouty")) {
int index = (old_path[len - 1] == 'y' ? 2 : 0);
old_path[len - 1] = 0;
if (p_index) {
*p_index = index;
}
else {
*p_old_path = BLI_sprintfN("%s[%d]", old_path, index);
MEM_freeN(old_path);
}
return true;
}
return false;
}
static void do_version_bbone_len_scale_fcurve_fix(FCurve *fcu)
{
/* Update driver variable paths. */
if (fcu->driver) {
LISTBASE_FOREACH (DriverVar *, dvar, &fcu->driver->variables) {
DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
replace_bbone_len_scale_rnapath(&dtar->rna_path, NULL);
}
DRIVER_TARGETS_LOOPER_END;
}
}
/* Update F-Curve's path. */
replace_bbone_len_scale_rnapath(&fcu->rna_path, &fcu->array_index);
}
static void do_version_bbone_len_scale_animdata_cb(ID *UNUSED(id),
AnimData *adt,
void *UNUSED(wrapper_data))
{
LISTBASE_FOREACH_MUTABLE (FCurve *, fcu, &adt->drivers) {
do_version_bbone_len_scale_fcurve_fix(fcu);
}
}
static void do_version_bones_bbone_len_scale(ListBase *lb)
{
LISTBASE_FOREACH (Bone *, bone, lb) {
if (bone->flag & BONE_ADD_PARENT_END_ROLL) {
bone->bbone_flag |= BBONE_ADD_PARENT_END_ROLL;
}
copy_v3_fl3(bone->scale_in, bone->scale_in_x, 1.0f, bone->scale_in_z);
copy_v3_fl3(bone->scale_out, bone->scale_out_x, 1.0f, bone->scale_out_z);
do_version_bones_bbone_len_scale(&bone->childbase);
}
}
static void do_version_constraints_spline_ik_joint_bindings(ListBase *lb)
{
/* Binding array data could be freed without properly resetting its size data. */
LISTBASE_FOREACH (bConstraint *, con, lb) {
if (con->type == CONSTRAINT_TYPE_SPLINEIK) {
bSplineIKConstraint *data = (bSplineIKConstraint *)con->data;
if (data->points == NULL) {
data->numpoints = 0;
}
}
}
}
static bNodeSocket *do_version_replace_float_size_with_vector(bNodeTree *ntree,
bNode *node,
bNodeSocket *socket)
{
const bNodeSocketValueFloat *socket_value = (const bNodeSocketValueFloat *)socket->default_value;
const float old_value = socket_value->value;
nodeRemoveSocket(ntree, node, socket);
bNodeSocket *new_socket = nodeAddSocket(
ntree, node, SOCK_IN, nodeStaticSocketType(SOCK_VECTOR, PROP_TRANSLATION), "Size", "Size");
bNodeSocketValueVector *value_vector = (bNodeSocketValueVector *)new_socket->default_value;
copy_v3_fl(value_vector->value, old_value);
return new_socket;
}
static bool seq_transform_origin_set(Sequence *seq, void *UNUSED(user_data))
{
StripTransform *transform = seq->strip->transform;
if (seq->strip->transform != NULL) {
transform->origin[0] = transform->origin[1] = 0.5f;
}
return true;
}
static bool seq_transform_filter_set(Sequence *seq, void *UNUSED(user_data))
{
StripTransform *transform = seq->strip->transform;
if (seq->strip->transform != NULL) {
transform->filter = SEQ_TRANSFORM_FILTER_BILINEAR;
}
return true;
}
static void do_version_subsurface_methods(bNode *node)
{
if (node->type == SH_NODE_SUBSURFACE_SCATTERING) {
if (!ELEM(node->custom1, SHD_SUBSURFACE_BURLEY, SHD_SUBSURFACE_RANDOM_WALK)) {
node->custom1 = SHD_SUBSURFACE_RANDOM_WALK_FIXED_RADIUS;
}
}
else if (node->type == SH_NODE_BSDF_PRINCIPLED) {
if (!ELEM(node->custom2, SHD_SUBSURFACE_BURLEY, SHD_SUBSURFACE_RANDOM_WALK)) {
node->custom2 = SHD_SUBSURFACE_RANDOM_WALK_FIXED_RADIUS;
}
}
}
static void version_geometry_nodes_add_attribute_input_settings(NodesModifierData *nmd)
{
/* Before versioning the properties, make sure it hasn't been done already. */
LISTBASE_FOREACH (const IDProperty *, property, &nmd->settings.properties->data.group) {
if (strstr(property->name, "_use_attribute") || strstr(property->name, "_attribute_name")) {
return;
}
}
LISTBASE_FOREACH_MUTABLE (IDProperty *, property, &nmd->settings.properties->data.group) {
if (!ELEM(property->type, IDP_FLOAT, IDP_INT, IDP_ARRAY)) {
continue;
}
if (strstr(property->name, "_use_attribute") || strstr(property->name, "_attribute_name")) {
continue;
}
char use_attribute_prop_name[MAX_IDPROP_NAME];
BLI_snprintf(use_attribute_prop_name,
sizeof(use_attribute_prop_name),
"%s%s",
property->name,
"_use_attribute");
IDPropertyTemplate idprop = {0};
IDProperty *use_attribute_prop = IDP_New(IDP_INT, &idprop, use_attribute_prop_name);
IDP_AddToGroup(nmd->settings.properties, use_attribute_prop);
char attribute_name_prop_name[MAX_IDPROP_NAME];
BLI_snprintf(attribute_name_prop_name,
sizeof(attribute_name_prop_name),
"%s%s",
property->name,
"_attribute_name");
IDProperty *attribute_prop = IDP_New(IDP_STRING, &idprop, attribute_name_prop_name);
IDP_AddToGroup(nmd->settings.properties, attribute_prop);
}
}
/* Copy of the function before the fixes. */
static void legacy_vec_roll_to_mat3_normalized(const float nor[3],
const float roll,
float r_mat[3][3])
{
const float SAFE_THRESHOLD = 1.0e-5f; /* theta above this value has good enough precision. */
const float CRITICAL_THRESHOLD = 1.0e-9f; /* above this is safe under certain conditions. */
const float THRESHOLD_SQUARED = CRITICAL_THRESHOLD * CRITICAL_THRESHOLD;
const float x = nor[0];
const float y = nor[1];
const float z = nor[2];
const float theta = 1.0f + y; /* remapping Y from [-1,+1] to [0,2]. */
const float theta_alt = x * x + z * z; /* Helper value for matrix calculations. */
float rMatrix[3][3], bMatrix[3][3];
BLI_ASSERT_UNIT_V3(nor);
/* When theta is close to zero (nor is aligned close to negative Y Axis),
* we have to check we do have non-null X/Z components as well.
* Also, due to float precision errors, nor can be (0.0, -0.99999994, 0.0) which results
* in theta being close to zero. This will cause problems when theta is used as divisor.
*/
if (theta > SAFE_THRESHOLD || (theta > CRITICAL_THRESHOLD && theta_alt > THRESHOLD_SQUARED)) {
/* nor is *not* aligned to negative Y-axis (0,-1,0). */
bMatrix[0][1] = -x;
bMatrix[1][0] = x;
bMatrix[1][1] = y;
bMatrix[1][2] = z;
bMatrix[2][1] = -z;
if (theta > SAFE_THRESHOLD) {
/* nor differs significantly from negative Y axis (0,-1,0): apply the general case. */
bMatrix[0][0] = 1 - x * x / theta;
bMatrix[2][2] = 1 - z * z / theta;
bMatrix[2][0] = bMatrix[0][2] = -x * z / theta;
}
else {
/* nor is close to negative Y axis (0,-1,0): apply the special case. */
bMatrix[0][0] = (x + z) * (x - z) / -theta_alt;
bMatrix[2][2] = -bMatrix[0][0];
bMatrix[2][0] = bMatrix[0][2] = 2.0f * x * z / theta_alt;
}
}
else {
/* nor is very close to negative Y axis (0,-1,0): use simple symmetry by Z axis. */
unit_m3(bMatrix);
bMatrix[0][0] = bMatrix[1][1] = -1.0;
}
/* Make Roll matrix */
axis_angle_normalized_to_mat3(rMatrix, nor, roll);
/* Combine and output result */
mul_m3_m3m3(r_mat, rMatrix, bMatrix);
}
static void correct_bone_roll_value(const float head[3],
const float tail[3],
const float check_x_axis[3],
const float check_y_axis[3],
float *r_roll)
{
const float SAFE_THRESHOLD = 1.0e-5f;
float vec[3], bone_mat[3][3], vec2[3];
/* Compute the Y axis vector. */
sub_v3_v3v3(vec, tail, head);
normalize_v3(vec);
/* Only correct when in the danger zone. */
if (1.0f + vec[1] < SAFE_THRESHOLD * 2 && (vec[0] || vec[2])) {
/* Use the armature matrix to double-check if adjustment is needed.
* This should minimize issues if the file is bounced back and forth between
* 2.92 and 2.91, provided Edit Mode isn't entered on the armature in 2.91. */
vec_roll_to_mat3(vec, *r_roll, bone_mat);
UNUSED_VARS_NDEBUG(check_y_axis);
BLI_assert(dot_v3v3(bone_mat[1], check_y_axis) > 0.999f);
if (dot_v3v3(bone_mat[0], check_x_axis) < 0.999f) {
/* Recompute roll using legacy code to interpret the old value. */
legacy_vec_roll_to_mat3_normalized(vec, *r_roll, bone_mat);
mat3_to_vec_roll(bone_mat, vec2, r_roll);
BLI_assert(compare_v3v3(vec, vec2, 0.001f));
}
}
}
/* Update the armature Bone roll fields for bones very close to -Y direction. */
static void do_version_bones_roll(ListBase *lb)
{
LISTBASE_FOREACH (Bone *, bone, lb) {
/* Parent-relative orientation (used for posing). */
correct_bone_roll_value(
bone->head, bone->tail, bone->bone_mat[0], bone->bone_mat[1], &bone->roll);
/* Absolute orientation (used for Edit mode). */
correct_bone_roll_value(
bone->arm_head, bone->arm_tail, bone->arm_mat[0], bone->arm_mat[1], &bone->arm_roll);
do_version_bones_roll(&bone->childbase);
}
}
static void version_geometry_nodes_set_position_node_offset(bNodeTree *ntree)
{
/* Add the new Offset socket. */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type != GEO_NODE_SET_POSITION) {
continue;
}
if (BLI_listbase_count(&node->inputs) < 4) {
/* The offset socket didn't exist in the file yet. */
return;
}
bNodeSocket *old_offset_socket = BLI_findlink(&node->inputs, 3);
if (old_offset_socket->type == SOCK_VECTOR) {
/* Versioning happened already. */
return;
}
/* Change identifier of old socket, so that the there is no name collision. */
STRNCPY(old_offset_socket->identifier, "Offset_old");
nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_VECTOR, PROP_TRANSLATION, "Offset", "Offset");
}
/* Relink links that were connected to Position while Offset was enabled. */
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
if (link->tonode->type != GEO_NODE_SET_POSITION) {
continue;
}
if (!STREQ(link->tosock->identifier, "Position")) {
continue;
}
bNodeSocket *old_offset_socket = BLI_findlink(&link->tonode->inputs, 3);
/* This assumes that the offset is not linked to something else. That seems to be a reasonable
* assumption, because the node is probably only ever used in one or the other mode. */
const bool offset_enabled =
((bNodeSocketValueBoolean *)old_offset_socket->default_value)->value;
if (offset_enabled) {
/* Relink to new offset socket. */
link->tosock = old_offset_socket->next;
}
}
/* Remove old Offset socket. */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type != GEO_NODE_SET_POSITION) {
continue;
}
bNodeSocket *old_offset_socket = BLI_findlink(&node->inputs, 3);
nodeRemoveSocket(ntree, node, old_offset_socket);
}
}
static void version_node_tree_socket_id_delim(bNodeTree *ntree)
{
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
version_node_socket_id_delim(socket);
}
LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
version_node_socket_id_delim(socket);
}
}
}
static bool version_fix_seq_meta_range(Sequence *seq, void *user_data)
{
Scene *scene = (Scene *)user_data;
if (seq->type == SEQ_TYPE_META) {
SEQ_time_update_meta_strip_range(scene, seq);
}
return true;
}
/* Those `version_liboverride_rnacollections_*` functions mimic the old, pre-3.0 code to find
* anchor and source items in the given list of modifiers, constraints etc., using only the
* `subitem_local` data of the override property operation.
*
* Then they convert it into the new, proper `subitem_reference` data for the anchor, and
* `subitem_local` for the source.
*
* NOTE: Here only the stored override ID is available, unlike in the `override_apply` functions.
*/
static void version_liboverride_rnacollections_insertion_object_constraints(
ListBase *constraints, IDOverrideLibraryProperty *op)
{
LISTBASE_FOREACH_MUTABLE (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
if (opop->operation != IDOVERRIDE_LIBRARY_OP_INSERT_AFTER) {
continue;
}
bConstraint *constraint_anchor = BLI_listbase_string_or_index_find(constraints,
opop->subitem_local_name,
offsetof(bConstraint, name),
opop->subitem_local_index);
bConstraint *constraint_src = constraint_anchor != NULL ? constraint_anchor->next :
constraints->first;
if (constraint_src == NULL) {
/* Invalid case, just remove that override property operation. */
CLOG_ERROR(&LOG, "Could not find source constraint in stored override data");
BKE_lib_override_library_property_operation_delete(op, opop);
continue;
}
opop->subitem_reference_name = opop->subitem_local_name;
opop->subitem_local_name = BLI_strdup(constraint_src->name);
opop->subitem_reference_index = opop->subitem_local_index;
opop->subitem_local_index++;
}
}
static void version_liboverride_rnacollections_insertion_object(Object *object)
{
IDOverrideLibrary *liboverride = object->id.override_library;
IDOverrideLibraryProperty *op;
op = BKE_lib_override_library_property_find(liboverride, "modifiers");
if (op != NULL) {
LISTBASE_FOREACH_MUTABLE (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
if (opop->operation != IDOVERRIDE_LIBRARY_OP_INSERT_AFTER) {
continue;
}
ModifierData *mod_anchor = BLI_listbase_string_or_index_find(&object->modifiers,
opop->subitem_local_name,
offsetof(ModifierData, name),
opop->subitem_local_index);
ModifierData *mod_src = mod_anchor != NULL ? mod_anchor->next : object->modifiers.first;
if (mod_src == NULL) {
/* Invalid case, just remove that override property operation. */
CLOG_ERROR(&LOG, "Could not find source modifier in stored override data");
BKE_lib_override_library_property_operation_delete(op, opop);
continue;
}
opop->subitem_reference_name = opop->subitem_local_name;
opop->subitem_local_name = BLI_strdup(mod_src->name);
opop->subitem_reference_index = opop->subitem_local_index;
opop->subitem_local_index++;
}
}
op = BKE_lib_override_library_property_find(liboverride, "grease_pencil_modifiers");
if (op != NULL) {
LISTBASE_FOREACH_MUTABLE (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
if (opop->operation != IDOVERRIDE_LIBRARY_OP_INSERT_AFTER) {
continue;
}
GpencilModifierData *gp_mod_anchor = BLI_listbase_string_or_index_find(
&object->greasepencil_modifiers,
opop->subitem_local_name,
offsetof(GpencilModifierData, name),
opop->subitem_local_index);
GpencilModifierData *gp_mod_src = gp_mod_anchor != NULL ?
gp_mod_anchor->next :
object->greasepencil_modifiers.first;
if (gp_mod_src == NULL) {
/* Invalid case, just remove that override property operation. */
CLOG_ERROR(&LOG, "Could not find source GP modifier in stored override data");
BKE_lib_override_library_property_operation_delete(op, opop);
continue;
}
opop->subitem_reference_name = opop->subitem_local_name;
opop->subitem_local_name = BLI_strdup(gp_mod_src->name);
opop->subitem_reference_index = opop->subitem_local_index;
opop->subitem_local_index++;
}
}
op = BKE_lib_override_library_property_find(liboverride, "constraints");
if (op != NULL) {
version_liboverride_rnacollections_insertion_object_constraints(&object->constraints, op);
}
if (object->pose != NULL) {
LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) {
char rna_path[FILE_MAXFILE];
BLI_snprintf(rna_path, sizeof(rna_path), "pose.bones[\"%s\"].constraints", pchan->name);
op = BKE_lib_override_library_property_find(liboverride, rna_path);
if (op != NULL) {
version_liboverride_rnacollections_insertion_object_constraints(&pchan->constraints, op);
}
}
}
}
static void version_liboverride_rnacollections_insertion_animdata(ID *id)
{
AnimData *anim_data = BKE_animdata_from_id(id);
if (anim_data == NULL) {
return;
}
IDOverrideLibrary *liboverride = id->override_library;
IDOverrideLibraryProperty *op;
op = BKE_lib_override_library_property_find(liboverride, "animation_data.nla_tracks");
if (op != NULL) {
LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
if (opop->operation != IDOVERRIDE_LIBRARY_OP_INSERT_AFTER) {
continue;
}
/* NLA tracks are only referenced by index, which limits possibilities, basically they are
* always added at the end of the list, see #rna_NLA_tracks_override_apply.
*
* This makes things simple here. */
opop->subitem_reference_name = opop->subitem_local_name;
opop->subitem_local_name = NULL;
opop->subitem_reference_index = opop->subitem_local_index;
opop->subitem_local_index++;
}
}
}
/* NOLINTNEXTLINE: readability-function-size */
2021-04-22 16:33:37 +10:00
void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
{
/* The #SCE_SNAP_SEQ flag has been removed in favor of the #SCE_SNAP which can be used for each
* snap_flag member individually. */
enum { SCE_SNAP_SEQ = (1 << 7) };
if (!MAIN_VERSION_ATLEAST(bmain, 300, 1)) {
2021-04-22 16:33:37 +10:00
/* Set default value for the new bisect_threshold parameter in the mirror modifier. */
if (!DNA_struct_elem_find(fd->filesdna, "MirrorModifierData", "float", "bisect_threshold")) {
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type == eModifierType_Mirror) {
MirrorModifierData *mmd = (MirrorModifierData *)md;
/* This was the previous hard-coded value. */
mmd->bisect_threshold = 0.001f;
}
}
}
}
/* Grease Pencil: Set default value for dilate pixels. */
if (!DNA_struct_elem_find(fd->filesdna, "BrushGpencilSettings", "int", "dilate_pixels")) {
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
if (brush->gpencil_settings) {
brush->gpencil_settings->dilate_pixels = 1;
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 2)) {
version_switch_node_input_prefix(bmain);
if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "float", "custom_scale_xyz[3]")) {
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
if (ob->pose == NULL) {
continue;
}
LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
copy_v3_fl(pchan->custom_scale_xyz, pchan->custom_scale);
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 4)) {
/* Add a properties sidebar to the spreadsheet editor. */
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_SPREADSHEET) {
ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
&sl->regionbase;
ARegion *new_sidebar = do_versions_add_region_if_not_found(
regionbase, RGN_TYPE_UI, "sidebar for spreadsheet", RGN_TYPE_FOOTER);
if (new_sidebar != NULL) {
new_sidebar->alignment = RGN_ALIGN_RIGHT;
new_sidebar->flag |= RGN_FLAG_HIDDEN;
}
}
}
}
}
/* Enable spreadsheet filtering in old files without row filters. */
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_SPREADSHEET) {
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
sspreadsheet->filter_flag |= SPREADSHEET_FILTER_ENABLE;
}
}
}
}
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_GEOMETRY) {
version_node_socket_name(ntree, GEO_NODE_BOUNDING_BOX, "Mesh", "Bounding Box");
}
}
FOREACH_NODETREE_END;
if (!DNA_struct_elem_find(fd->filesdna, "FileAssetSelectParams", "short", "import_type")) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_FILE) {
SpaceFile *sfile = (SpaceFile *)sl;
if (sfile->asset_params) {
sfile->asset_params->import_type = FILE_ASSET_IMPORT_APPEND;
}
}
}
}
}
}
/* Initialize length-wise scale B-Bone settings. */
if (!DNA_struct_elem_find(fd->filesdna, "Bone", "int", "bbone_flag")) {
/* Update armature data and pose channels. */
LISTBASE_FOREACH (bArmature *, arm, &bmain->armatures) {
do_version_bones_bbone_len_scale(&arm->bonebase);
}
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
if (ob->pose) {
LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
copy_v3_fl3(pchan->scale_in, pchan->scale_in_x, 1.0f, pchan->scale_in_z);
copy_v3_fl3(pchan->scale_out, pchan->scale_out_x, 1.0f, pchan->scale_out_z);
}
}
}
/* Update action curves and drivers. */
LISTBASE_FOREACH (bAction *, act, &bmain->actions) {
LISTBASE_FOREACH_MUTABLE (FCurve *, fcu, &act->curves) {
do_version_bbone_len_scale_fcurve_fix(fcu);
}
}
BKE_animdata_main_cb(bmain, do_version_bbone_len_scale_animdata_cb, NULL);
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 5)) {
/* Add a dataset sidebar to the spreadsheet editor. */
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_SPREADSHEET) {
ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
&sl->regionbase;
ARegion *spreadsheet_dataset_region = do_versions_add_region_if_not_found(
regionbase, RGN_TYPE_CHANNELS, "spreadsheet dataset region", RGN_TYPE_FOOTER);
if (spreadsheet_dataset_region) {
spreadsheet_dataset_region->alignment = RGN_ALIGN_LEFT;
spreadsheet_dataset_region->v2d.scroll = (V2D_SCROLL_RIGHT | V2D_SCROLL_BOTTOM);
}
}
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 6)) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) {
/* Disable View Layers filter. */
if (space->spacetype == SPACE_OUTLINER) {
SpaceOutliner *space_outliner = (SpaceOutliner *)space;
space_outliner->filter |= SO_FILTER_NO_VIEW_LAYERS;
}
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 7)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *tool_settings = scene->toolsettings;
tool_settings->snap_flag |= SCE_SNAP_SEQ;
short snap_mode = tool_settings->snap_mode;
short snap_node_mode = tool_settings->snap_node_mode;
short snap_uv_mode = tool_settings->snap_uv_mode;
tool_settings->snap_mode &= ~((1 << 4) | (1 << 5) | (1 << 6));
tool_settings->snap_node_mode &= ~((1 << 5) | (1 << 6));
tool_settings->snap_uv_mode &= ~(1 << 4);
if (snap_mode & (1 << 4)) {
tool_settings->snap_mode |= (1 << 6); /* SCE_SNAP_MODE_INCREMENT */
}
if (snap_mode & (1 << 5)) {
tool_settings->snap_mode |= (1 << 4); /* SCE_SNAP_MODE_EDGE_MIDPOINT */
}
if (snap_mode & (1 << 6)) {
tool_settings->snap_mode |= (1 << 5); /* SCE_SNAP_MODE_EDGE_PERPENDICULAR */
}
if (snap_node_mode & (1 << 5)) {
tool_settings->snap_node_mode |= (1 << 0); /* SCE_SNAP_MODE_NODE_X */
}
if (snap_node_mode & (1 << 6)) {
tool_settings->snap_node_mode |= (1 << 1); /* SCE_SNAP_MODE_NODE_Y */
}
if (snap_uv_mode & (1 << 4)) {
tool_settings->snap_uv_mode |= (1 << 6); /* SCE_SNAP_MODE_INCREMENT */
}
SequencerToolSettings *sequencer_tool_settings = SEQ_tool_settings_ensure(scene);
sequencer_tool_settings->snap_mode = SEQ_SNAP_TO_STRIPS | SEQ_SNAP_TO_CURRENT_FRAME |
SEQ_SNAP_TO_STRIP_HOLD;
sequencer_tool_settings->snap_distance = 15;
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 8)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (scene->master_collection != NULL) {
BLI_strncpy(scene->master_collection->id.name + 2,
BKE_SCENE_COLLECTION_NAME,
sizeof(scene->master_collection->id.name) - 2);
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 9)) {
/* Fix a bug where reordering FCurves and bActionGroups could cause some corruption. Just
* reconstruct all the action groups & ensure that the FCurves of a group are continuously
* stored (i.e. not mixed with other groups) to be sure. See T89435. */
LISTBASE_FOREACH (bAction *, act, &bmain->actions) {
BKE_action_groups_reconstruct(act);
}
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_GEOMETRY) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == GEO_NODE_SUBDIVIDE_MESH) {
strcpy(node->idname, "GeometryNodeMeshSubdivide");
}
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 10)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *tool_settings = scene->toolsettings;
if (tool_settings->snap_uv_mode & (1 << 4)) {
tool_settings->snap_uv_mode |= (1 << 6); /* SCE_SNAP_MODE_INCREMENT */
tool_settings->snap_uv_mode &= ~(1 << 4);
}
}
LISTBASE_FOREACH (Material *, mat, &bmain->materials) {
if (!(mat->lineart.flags & LRT_MATERIAL_CUSTOM_OCCLUSION_EFFECTIVENESS)) {
mat->lineart.mat_occlusion = 1;
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 13)) {
/* Convert Surface Deform to sparse-capable bind structure. */
if (!DNA_struct_elem_find(
fd->filesdna, "SurfaceDeformModifierData", "int", "num_mesh_verts")) {
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type == eModifierType_SurfaceDeform) {
SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
if (smd->num_bind_verts && smd->verts) {
smd->num_mesh_verts = smd->num_bind_verts;
for (unsigned int i = 0; i < smd->num_bind_verts; i++) {
smd->verts[i].vertex_idx = i;
}
}
}
}
if (ob->type == OB_GPENCIL) {
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
if (md->type == eGpencilModifierType_Lineart) {
LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
lmd->flags |= LRT_GPENCIL_USE_CACHE;
lmd->chain_smooth_tolerance = 0.2f;
}
}
}
}
}
if (!DNA_struct_elem_find(
fd->filesdna, "WorkSpace", "AssetLibraryReference", "asset_library")) {
LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) {
BKE_asset_library_reference_init_default(&workspace->asset_library_ref);
}
}
if (!DNA_struct_elem_find(
fd->filesdna, "FileAssetSelectParams", "AssetLibraryReference", "asset_library_ref")) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) {
if (space->spacetype == SPACE_FILE) {
SpaceFile *sfile = (SpaceFile *)space;
if (sfile->browse_mode != FILE_BROWSE_MODE_ASSETS) {
continue;
}
BKE_asset_library_reference_init_default(&sfile->asset_params->asset_library_ref);
}
}
}
}
}
/* Set default 2D annotation placement. */
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *ts = scene->toolsettings;
ts->gpencil_v2d_align = GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR;
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 14)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *tool_settings = scene->toolsettings;
tool_settings->snap_flag &= ~SCE_SNAP_SEQ;
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 15)) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_SEQ) {
SpaceSeq *sseq = (SpaceSeq *)sl;
sseq->flag |= SEQ_TIMELINE_SHOW_GRID;
}
}
}
}
}
2021-08-04 13:13:02 +10:00
/* Font names were copied directly into ID names, see: T90417. */
if (!MAIN_VERSION_ATLEAST(bmain, 300, 16)) {
ListBase *lb = which_libbase(bmain, ID_VF);
BKE_main_id_repair_duplicate_names_listbase(lb);
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 17)) {
if (!DNA_struct_elem_find(
fd->filesdna, "View3DOverlay", "float", "normals_constant_screen_size")) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_VIEW3D) {
View3D *v3d = (View3D *)sl;
v3d->overlay.normals_constant_screen_size = 7.0f;
}
}
}
}
}
/* Fix SplineIK constraint's inconsistency between binding points array and its stored size.
*/
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
/* NOTE: Objects should never have SplineIK constraint, so no need to apply this fix on
* their constraints. */
if (ob->pose) {
LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
do_version_constraints_spline_ik_joint_bindings(&pchan->constraints);
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 18)) {
if (!DNA_struct_elem_find(
fd->filesdna, "WorkSpace", "AssetLibraryReference", "asset_library_ref")) {
LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) {
BKE_asset_library_reference_init_default(&workspace->asset_library_ref);
}
}
if (!DNA_struct_elem_find(
fd->filesdna, "FileAssetSelectParams", "AssetLibraryReference", "asset_library_ref")) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) {
if (space->spacetype != SPACE_FILE) {
continue;
}
SpaceFile *sfile = (SpaceFile *)space;
if (sfile->browse_mode != FILE_BROWSE_MODE_ASSETS) {
continue;
}
BKE_asset_library_reference_init_default(&sfile->asset_params->asset_library_ref);
}
}
}
}
/* Previously, only text ending with `.py` would run, apply this logic
* to existing files so text that happens to have the "Register" enabled
* doesn't suddenly start running code on startup that was previously ignored. */
LISTBASE_FOREACH (Text *, text, &bmain->texts) {
if ((text->flags & TXT_ISSCRIPT) && !BLI_path_extension_check(text->id.name + 2, ".py")) {
text->flags &= ~TXT_ISSCRIPT;
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 19)) {
/* Disable Fade Inactive Overlay by default as it is redundant after introducing flash on
* mode transfer. */
for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_VIEW3D) {
View3D *v3d = (View3D *)sl;
v3d->overlay.flag &= ~V3D_OVERLAY_FADE_INACTIVE;
}
}
}
}
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
SequencerToolSettings *sequencer_tool_settings = SEQ_tool_settings_ensure(scene);
sequencer_tool_settings->overlap_mode = SEQ_OVERLAP_SHUFFLE;
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 20)) {
/* Use new vector Size socket in Cube Mesh Primitive node. */
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type != NTREE_GEOMETRY) {
continue;
}
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
if (link->tonode->type == GEO_NODE_MESH_PRIMITIVE_CUBE) {
bNode *node = link->tonode;
if (STREQ(link->tosock->identifier, "Size") && link->tosock->type == SOCK_FLOAT) {
bNode *link_fromnode = link->fromnode;
bNodeSocket *link_fromsock = link->fromsock;
bNodeSocket *socket = link->tosock;
BLI_assert(socket);
bNodeSocket *new_socket = do_version_replace_float_size_with_vector(
ntree, node, socket);
nodeAddLink(ntree, link_fromnode, link_fromsock, node, new_socket);
}
}
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type != GEO_NODE_MESH_PRIMITIVE_CUBE) {
continue;
}
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
if (STREQ(socket->identifier, "Size") && (socket->type == SOCK_FLOAT)) {
do_version_replace_float_size_with_vector(ntree, node, socket);
break;
}
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 22)) {
if (!DNA_struct_elem_find(
fd->filesdna, "LineartGpencilModifierData", "bool", "use_crease_on_smooth")) {
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
if (ob->type == OB_GPENCIL) {
LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
if (md->type == eGpencilModifierType_Lineart) {
LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md;
lmd->calculation_flags |= LRT_USE_CREASE_ON_SMOOTH_SURFACES;
}
}
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 23)) {
for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_FILE) {
SpaceFile *sfile = (SpaceFile *)sl;
if (sfile->asset_params) {
sfile->asset_params->base_params.recursion_level = FILE_SELECT_MAX_RECURSIONS;
}
}
}
}
}
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_SEQ) {
SpaceSeq *sseq = (SpaceSeq *)sl;
int seq_show_safe_margins = (sseq->flag & SEQ_PREVIEW_SHOW_SAFE_MARGINS);
int seq_show_gpencil = (sseq->flag & SEQ_PREVIEW_SHOW_GPENCIL);
int seq_show_fcurves = (sseq->flag & SEQ_TIMELINE_SHOW_FCURVES);
int seq_show_safe_center = (sseq->flag & SEQ_PREVIEW_SHOW_SAFE_CENTER);
int seq_show_metadata = (sseq->flag & SEQ_PREVIEW_SHOW_METADATA);
int seq_show_strip_name = (sseq->flag & SEQ_TIMELINE_SHOW_STRIP_NAME);
int seq_show_strip_source = (sseq->flag & SEQ_TIMELINE_SHOW_STRIP_SOURCE);
int seq_show_strip_duration = (sseq->flag & SEQ_TIMELINE_SHOW_STRIP_DURATION);
int seq_show_grid = (sseq->flag & SEQ_TIMELINE_SHOW_GRID);
int show_strip_offset = (sseq->draw_flag & SEQ_TIMELINE_SHOW_STRIP_OFFSETS);
sseq->preview_overlay.flag = (seq_show_safe_margins | seq_show_gpencil |
seq_show_safe_center | seq_show_metadata);
sseq->timeline_overlay.flag = (seq_show_fcurves | seq_show_strip_name |
seq_show_strip_source | seq_show_strip_duration |
seq_show_grid | show_strip_offset);
}
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 24)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
SequencerToolSettings *sequencer_tool_settings = SEQ_tool_settings_ensure(scene);
sequencer_tool_settings->pivot_point = V3D_AROUND_CENTER_MEDIAN;
if (scene->ed != NULL) {
SEQ_for_each_callback(&scene->ed->seqbase, seq_transform_origin_set, NULL);
}
}
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_SEQ) {
SpaceSeq *sseq = (SpaceSeq *)sl;
sseq->preview_overlay.flag |= SEQ_PREVIEW_SHOW_OUTLINE_SELECTED;
}
}
}
}
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_SEQ) {
ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
&sl->regionbase;
LISTBASE_FOREACH (ARegion *, region, regionbase) {
if (region->regiontype == RGN_TYPE_WINDOW) {
region->v2d.min[1] = 4.0f;
}
}
}
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 25)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_SHADER) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
do_version_subsurface_methods(node);
}
}
}
FOREACH_NODETREE_END;
enum {
R_EXR_TILE_FILE = (1 << 10),
R_FULL_SAMPLE = (1 << 15),
};
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
scene->r.scemode &= ~(R_EXR_TILE_FILE | R_FULL_SAMPLE);
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 26)) {
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type == eModifierType_Nodes) {
version_geometry_nodes_add_attribute_input_settings((NodesModifierData *)md);
}
}
}
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
switch (sl->spacetype) {
case SPACE_FILE: {
SpaceFile *sfile = (SpaceFile *)sl;
if (sfile->params) {
sfile->params->flag &= ~(FILE_PARAMS_FLAG_UNUSED_1 | FILE_PARAMS_FLAG_UNUSED_2 |
UDIM: Support virtual filenames This implements the design detailed in T92696 to support virtual filenames for UDIM textures. Currently, the following 2 substitution tokens are supported: | Token | Meaning | | ----- | ---- | | <UDIM> | 1001 + u-tile + v-tile * 10 | | <UVTILE> | Equivalent to u<u-tile + 1>_v<v-tile + 1> | Example for u-tile of 3 and v-tile of 1: filename.<UDIM>_ver0023.png --> filename.1014_ver0023.png filename.<UVTILE>_ver0023.png --> filename.u4_v2_ver0023.png For image loading, the existing workflow is unchanged. A user can select one or more image files, belonging to one or more UDIM tile sets, and have Blender load them all as it does today. Now the <UVTILE> format is "guessed" just as the <UDIM> format was guessed before. If guessing fails, the user can simply go into the Image Editor and type the proper substitution in the filename. Once typing is complete, Blender will reload the files and correctly fill the tiles. This workflow is new as attempting to fix the guessing in current versions did not really work, and the user was often stuck with a confusing situation. For image saving, the existing workflow is changed slightly. Currently, when saving, a user has to be sure to type the filename of the first tile (e.g. filename.1001.png) to save the entire UDIM set. The number could differ if they start at a different tile etc. This is confusing. Now, the user should type a filename containing the appropriate substitution token. By default Blender will fill in a default name using the <UDIM> token but the user is free to save out images using <UVTILE> if they wish. Differential Revision: https://developer.blender.org/D13057
2021-12-30 22:06:23 -08:00
FILE_PARAMS_FLAG_UNUSED_3 | FILE_PATH_TOKENS_ALLOW);
}
/* New default import type: Append with reuse. */
if (sfile->asset_params) {
sfile->asset_params->import_type = FILE_ASSET_IMPORT_APPEND_REUSE;
}
break;
}
default:
break;
}
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 29)) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
switch (sl->spacetype) {
case SPACE_SEQ: {
ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
&sl->regionbase;
LISTBASE_FOREACH (ARegion *, region, regionbase) {
if (region->regiontype == RGN_TYPE_WINDOW) {
region->v2d.max[1] = MAXSEQ;
}
}
break;
}
case SPACE_IMAGE: {
SpaceImage *sima = (SpaceImage *)sl;
sima->custom_grid_subdiv = 10;
break;
}
}
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 31)) {
/* Swap header with the tool header so the regular header is always on the edge. */
for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
&sl->regionbase;
ARegion *region_tool = NULL, *region_head = NULL;
int region_tool_index = -1, region_head_index = -1, i;
LISTBASE_FOREACH_INDEX (ARegion *, region, regionbase, i) {
if (region->regiontype == RGN_TYPE_TOOL_HEADER) {
region_tool = region;
region_tool_index = i;
}
else if (region->regiontype == RGN_TYPE_HEADER) {
region_head = region;
region_head_index = i;
}
}
if ((region_tool && region_head) && (region_head_index > region_tool_index)) {
BLI_listbase_swaplinks(regionbase, region_tool, region_head);
}
}
}
}
/* Set strip color tags to SEQUENCE_COLOR_NONE. */
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (scene->ed != NULL) {
SEQ_for_each_callback(&scene->ed->seqbase, do_versions_sequencer_color_tags, NULL);
}
}
2021-10-03 12:06:06 +11:00
/* Show sequencer color tags by default. */
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_SEQ) {
SpaceSeq *sseq = (SpaceSeq *)sl;
sseq->timeline_overlay.flag |= SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG;
}
}
}
}
/* Set defaults for new color balance modifier parameters. */
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (scene->ed != NULL) {
SEQ_for_each_callback(&scene->ed->seqbase, do_versions_sequencer_color_balance_sop, NULL);
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 33)) {
for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
switch (sl->spacetype) {
case SPACE_SEQ: {
SpaceSeq *sseq = (SpaceSeq *)sl;
enum { SEQ_DRAW_SEQUENCE = 0 };
if (sseq->mainb == SEQ_DRAW_SEQUENCE) {
sseq->mainb = SEQ_DRAW_IMG_IMBUF;
}
break;
}
case SPACE_TEXT: {
SpaceText *st = (SpaceText *)sl;
st->flags &= ~ST_FLAG_UNUSED_4;
break;
}
}
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 36)) {
2021-10-18 11:16:24 +11:00
/* Update the `idnames` for renamed geometry and function nodes. */
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type != NTREE_GEOMETRY) {
continue;
}
version_node_id(ntree, FN_NODE_COMPARE, "FunctionNodeCompareFloats");
version_node_id(ntree, GEO_NODE_CAPTURE_ATTRIBUTE, "GeometryNodeCaptureAttribute");
version_node_id(ntree, GEO_NODE_MESH_BOOLEAN, "GeometryNodeMeshBoolean");
version_node_id(ntree, GEO_NODE_FILL_CURVE, "GeometryNodeFillCurve");
version_node_id(ntree, GEO_NODE_FILLET_CURVE, "GeometryNodeFilletCurve");
version_node_id(ntree, GEO_NODE_REVERSE_CURVE, "GeometryNodeReverseCurve");
version_node_id(ntree, GEO_NODE_SAMPLE_CURVE, "GeometryNodeSampleCurve");
version_node_id(ntree, GEO_NODE_RESAMPLE_CURVE, "GeometryNodeResampleCurve");
version_node_id(ntree, GEO_NODE_SUBDIVIDE_CURVE, "GeometryNodeSubdivideCurve");
version_node_id(ntree, GEO_NODE_TRIM_CURVE, "GeometryNodeTrimCurve");
version_node_id(ntree, GEO_NODE_REPLACE_MATERIAL, "GeometryNodeReplaceMaterial");
version_node_id(ntree, GEO_NODE_SUBDIVIDE_MESH, "GeometryNodeSubdivideMesh");
version_node_id(ntree, GEO_NODE_SET_MATERIAL, "GeometryNodeSetMaterial");
version_node_id(ntree, GEO_NODE_SPLIT_EDGES, "GeometryNodeSplitEdges");
}
/* Update bone roll after a fix to vec_roll_to_mat3_normalized. */
LISTBASE_FOREACH (bArmature *, arm, &bmain->armatures) {
do_version_bones_roll(&arm->bonebase);
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 37)) {
/* Node Editor: toggle overlays on. */
if (!DNA_struct_find(fd->filesdna, "SpaceNodeOverlay")) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) {
if (space->spacetype == SPACE_NODE) {
SpaceNode *snode = (SpaceNode *)space;
snode->overlay.flag |= SN_OVERLAY_SHOW_OVERLAYS;
snode->overlay.flag |= SN_OVERLAY_SHOW_WIRE_COLORS;
}
}
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 38)) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) {
if (space->spacetype == SPACE_FILE) {
SpaceFile *sfile = (SpaceFile *)space;
FileAssetSelectParams *asset_params = sfile->asset_params;
if (asset_params) {
asset_params->base_params.filter_id = FILTER_ID_ALL;
}
}
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 39)) {
LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
wm->xr.session_settings.base_scale = 1.0f;
wm->xr.session_settings.draw_flags |= (V3D_OFSDRAW_SHOW_SELECTION |
V3D_OFSDRAW_XR_SHOW_CONTROLLERS |
V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS);
}
}
UI: Theme refresh for Blender v3.0 {F11548100, size=full} To celebrate the beginning of a new series, it feels like the right time to give the theme a fresh look while improving on what already works. The aim of this refresh is to keep a familiar look but with polishing touches here and there. Like new paint on the walls of your well known house. The theme for Blender 2.8 was well received but presented a few flaws. * Transparency on menus and tooltips reduce readability * Mismatch on certain colors, especially outlines of connected widgets * Active/open menus highlight was not prominent enough * Header background mismatch in some editors At the same time we can make use of new features in 3.0: * Make panels look like panels again (like in v2.3!) * Make use of roundness in more widgets * Since nodes are no longer hard-coded to be transparent, tweak opacity and saturation * Tweak colors for the new dot grid This update does not include: * Meshes in edit mode to match greenish object-data color. This needs tweaks in the code to improve contrast. * A copy of the Blender 2.8x legacy theme. This could be added to the community themes (shouldn't cost much maintenance, I hope) There will be certainly small tweaks to do here and there, I've been working using this theme for months but there can be areas that are missing update. The overall style is presented here. This commit bumps the file subversion. Reviewed By: #user_interface, Severin Differential Revision: https://developer.blender.org/D13008
2021-10-27 18:20:40 +02:00
if (!MAIN_VERSION_ATLEAST(bmain, 300, 40)) {
/* Update the `idnames` for renamed geometry and function nodes. */
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type != NTREE_GEOMETRY) {
continue;
}
version_node_id(ntree, FN_NODE_SLICE_STRING, "FunctionNodeSliceString");
version_geometry_nodes_set_position_node_offset(ntree);
}
/* Add storage to viewer node. */
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type != NTREE_GEOMETRY) {
continue;
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == GEO_NODE_VIEWER) {
if (node->storage == NULL) {
NodeGeometryViewer *data = (NodeGeometryViewer *)MEM_callocN(
sizeof(NodeGeometryViewer), __func__);
data->data_type = CD_PROP_FLOAT;
node->storage = data;
}
}
}
}
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type == NTREE_GEOMETRY) {
version_node_input_socket_name(
ntree, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, "Geometry", "Mesh");
version_node_input_socket_name(ntree, GEO_NODE_POINTS_TO_VOLUME, "Geometry", "Points");
version_node_output_socket_name(ntree, GEO_NODE_POINTS_TO_VOLUME, "Geometry", "Volume");
version_node_socket_name(ntree, GEO_NODE_SUBDIVISION_SURFACE, "Geometry", "Mesh");
version_node_socket_name(ntree, GEO_NODE_RESAMPLE_CURVE, "Geometry", "Curve");
version_node_socket_name(ntree, GEO_NODE_SUBDIVIDE_CURVE, "Geometry", "Curve");
version_node_socket_name(ntree, GEO_NODE_SET_CURVE_RADIUS, "Geometry", "Curve");
version_node_socket_name(ntree, GEO_NODE_SET_CURVE_TILT, "Geometry", "Curve");
version_node_socket_name(ntree, GEO_NODE_SET_CURVE_HANDLES, "Geometry", "Curve");
version_node_socket_name(ntree, GEO_NODE_TRANSLATE_INSTANCES, "Geometry", "Instances");
version_node_socket_name(ntree, GEO_NODE_ROTATE_INSTANCES, "Geometry", "Instances");
version_node_socket_name(ntree, GEO_NODE_SCALE_INSTANCES, "Geometry", "Instances");
version_node_output_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry", "Mesh");
version_node_input_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry 1", "Mesh 1");
version_node_input_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry 2", "Mesh 2");
version_node_socket_name(ntree, GEO_NODE_SUBDIVIDE_MESH, "Geometry", "Mesh");
version_node_socket_name(ntree, GEO_NODE_TRIANGULATE, "Geometry", "Mesh");
version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_CONE, "Geometry", "Mesh");
version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_CUBE, "Geometry", "Mesh");
version_node_output_socket_name(
ntree, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Geometry", "Mesh");
version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_GRID, "Geometry", "Mesh");
version_node_output_socket_name(
ntree, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, "Geometry", "Mesh");
version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_CIRCLE, "Geometry", "Mesh");
version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_LINE, "Geometry", "Mesh");
version_node_output_socket_name(
ntree, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, "Geometry", "Mesh");
version_node_socket_name(ntree, GEO_NODE_SET_POINT_RADIUS, "Geometry", "Points");
}
}
}
UI: Theme refresh for Blender v3.0 {F11548100, size=full} To celebrate the beginning of a new series, it feels like the right time to give the theme a fresh look while improving on what already works. The aim of this refresh is to keep a familiar look but with polishing touches here and there. Like new paint on the walls of your well known house. The theme for Blender 2.8 was well received but presented a few flaws. * Transparency on menus and tooltips reduce readability * Mismatch on certain colors, especially outlines of connected widgets * Active/open menus highlight was not prominent enough * Header background mismatch in some editors At the same time we can make use of new features in 3.0: * Make panels look like panels again (like in v2.3!) * Make use of roundness in more widgets * Since nodes are no longer hard-coded to be transparent, tweak opacity and saturation * Tweak colors for the new dot grid This update does not include: * Meshes in edit mode to match greenish object-data color. This needs tweaks in the code to improve contrast. * A copy of the Blender 2.8x legacy theme. This could be added to the community themes (shouldn't cost much maintenance, I hope) There will be certainly small tweaks to do here and there, I've been working using this theme for months but there can be areas that are missing update. The overall style is presented here. This commit bumps the file subversion. Reviewed By: #user_interface, Severin Differential Revision: https://developer.blender.org/D13008
2021-10-27 18:20:40 +02:00
if (!MAIN_VERSION_ATLEAST(bmain, 300, 42)) {
/* Use consistent socket identifiers for the math node.
* The code to make unique identifiers from the names was inconsistent. */
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type != NTREE_CUSTOM) {
version_node_tree_socket_id_delim(ntree);
}
}
FOREACH_NODETREE_END;
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_SEQ) {
ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
&sl->regionbase;
LISTBASE_FOREACH (ARegion *, region, regionbase) {
if (region->regiontype == RGN_TYPE_WINDOW) {
region->v2d.min[1] = 1.0f;
}
}
}
}
}
}
/* Change minimum zoom to 0.05f in the node editor. */
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_NODE) {
ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
&sl->regionbase;
LISTBASE_FOREACH (ARegion *, region, regionbase) {
if (region->regiontype == RGN_TYPE_WINDOW) {
if (region->v2d.minzoom > 0.05f) {
region->v2d.minzoom = 0.05f;
}
}
}
}
}
}
}
2021-11-19 06:22:47 +01:00
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
Editing *ed = SEQ_editing_get(scene);
/* Make sure range of meta strips is correct.
* It was possible to save .blend file with incorrect state of meta strip
* range. The root cause is expected to be fixed, but need to ensure files
* with invalid meta strip range are corrected. */
if (ed != NULL) {
SEQ_for_each_callback(&ed->seqbase, version_fix_seq_meta_range, scene);
}
}
UI: Theme refresh for Blender v3.0 {F11548100, size=full} To celebrate the beginning of a new series, it feels like the right time to give the theme a fresh look while improving on what already works. The aim of this refresh is to keep a familiar look but with polishing touches here and there. Like new paint on the walls of your well known house. The theme for Blender 2.8 was well received but presented a few flaws. * Transparency on menus and tooltips reduce readability * Mismatch on certain colors, especially outlines of connected widgets * Active/open menus highlight was not prominent enough * Header background mismatch in some editors At the same time we can make use of new features in 3.0: * Make panels look like panels again (like in v2.3!) * Make use of roundness in more widgets * Since nodes are no longer hard-coded to be transparent, tweak opacity and saturation * Tweak colors for the new dot grid This update does not include: * Meshes in edit mode to match greenish object-data color. This needs tweaks in the code to improve contrast. * A copy of the Blender 2.8x legacy theme. This could be added to the community themes (shouldn't cost much maintenance, I hope) There will be certainly small tweaks to do here and there, I've been working using this theme for months but there can be areas that are missing update. The overall style is presented here. This commit bumps the file subversion. Reviewed By: #user_interface, Severin Differential Revision: https://developer.blender.org/D13008
2021-10-27 18:20:40 +02:00
}
/* Special case to handle older in-development 3.1 files, before change from 3.0 branch gets
* merged in master. */
if (!MAIN_VERSION_ATLEAST(bmain, 300, 42) ||
(bmain->versionfile == 301 && !MAIN_VERSION_ATLEAST(bmain, 301, 3))) {
/* Update LibOverride operations regarding insertions in RNA collections (i.e. modifiers,
* constraints and NLA tracks). */
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
if (ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) {
version_liboverride_rnacollections_insertion_animdata(id_iter);
if (GS(id_iter->name) == ID_OB) {
version_liboverride_rnacollections_insertion_object((Object *)id_iter);
}
}
}
FOREACH_MAIN_ID_END;
}
if (!MAIN_VERSION_ATLEAST(bmain, 301, 4)) {
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type != NTREE_GEOMETRY) {
continue;
}
version_node_id(ntree, GEO_NODE_CURVE_SPLINE_PARAMETER, "GeometryNodeSplineParameter");
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == GEO_NODE_CURVE_SPLINE_PARAMETER) {
version_node_add_socket_if_not_exist(
ntree, node, SOCK_OUT, SOCK_INT, PROP_NONE, "Index", "Index");
}
/* Convert float compare into a more general compare node. */
if (node->type == FN_NODE_COMPARE) {
if (node->storage == NULL) {
NodeFunctionCompare *data = (NodeFunctionCompare *)MEM_callocN(
sizeof(NodeFunctionCompare), __func__);
data->data_type = SOCK_FLOAT;
data->operation = node->custom1;
strcpy(node->idname, "FunctionNodeCompare");
node->storage = data;
}
}
}
}
/* Add a toggle for the breadcrumbs overlay in the node editor. */
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) {
if (space->spacetype == SPACE_NODE) {
SpaceNode *snode = (SpaceNode *)space;
snode->overlay.flag |= SN_OVERLAY_SHOW_PATH;
}
}
}
}
}
Geometry Nodes: support instance attributes when realizing instances This patch refactors the instance-realization code and adds new functionality. * Named and anonymous attributes are propagated from instances to the realized geometry. If the same attribute exists on the geometry and on an instance, the attribute on the geometry has precedence. * The id attribute has special handling to avoid creating the same id on many output points. This is necessary to make e.g. the Random Value node work as expected afterwards. Realizing instance attributes has an effect on existing files, especially due to the id attribute. To avoid breaking existing files, the Realize Instances node now has a legacy option that is enabled for all already existing Realize Instances nodes. Removing this legacy behavior does affect some existing files (although not many). We can decide whether it's worth to remove the old behavior as a separate step. This refactor also improves performance when realizing instances. That is mainly due to multi-threading. See D13446 to get the file used for benchmarking. The curve code is not as optimized as it could be yet. That's mainly because the storage for these attributes might change soonish and it wasn't worth optimizing for the current storage format right now. ``` 1,000,000 x mesh vertex: 530 ms -> 130 ms 1,000,000 x simple cube: 1290 ms -> 190 ms 1,000,000 x point: 1000 ms -> 150 ms 1,000,000 x curve spiral: 1740 ms -> 330 ms 1,000,000 x curve line: 1110 ms -> 210 ms 10,000 x subdivided cylinder: 170 ms -> 40 ms 10 x subdivided spiral: 180 ms -> 180 ms ``` Differential Revision: https://developer.blender.org/D13446
2021-12-14 15:57:58 +01:00
if (!MAIN_VERSION_ATLEAST(bmain, 301, 5)) {
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type != NTREE_GEOMETRY) {
continue;
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type != GEO_NODE_REALIZE_INSTANCES) {
continue;
}
node->custom1 |= GEO_NODE_REALIZE_INSTANCES_LEGACY_BEHAVIOR;
}
}
}
2022-01-28 11:11:11 +01:00
if (!MAIN_VERSION_ATLEAST(bmain, 301, 6)) {
/* Add node storage for map range node. */
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == SH_NODE_MAP_RANGE) {
if (node->storage == NULL) {
NodeMapRange *data = MEM_callocN(sizeof(NodeMapRange), __func__);
data->clamp = node->custom1;
data->data_type = CD_PROP_FLOAT;
data->interpolation_type = node->custom2;
node->storage = data;
}
}
}
}
FOREACH_NODETREE_END;
/* Update spreadsheet data set region type. */
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_SPREADSHEET) {
ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
&sl->regionbase;
LISTBASE_FOREACH (ARegion *, region, regionbase) {
if (region->regiontype == RGN_TYPE_CHANNELS) {
region->regiontype = RGN_TYPE_TOOLS;
}
}
}
}
}
}
/* Initialize the bone wireframe opacity setting. */
if (!DNA_struct_elem_find(fd->filesdna, "View3DOverlay", "float", "bone_wire_alpha")) {
for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_VIEW3D) {
View3D *v3d = (View3D *)sl;
v3d->overlay.bone_wire_alpha = 1.0f;
}
}
}
}
}
/* Rename sockets on multiple nodes */
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type == NTREE_GEOMETRY) {
version_node_output_socket_name(
ntree, GEO_NODE_STRING_TO_CURVES, "Curves", "Curve Instances");
version_node_output_socket_name(
ntree, GEO_NODE_INPUT_MESH_EDGE_ANGLE, "Angle", "Unsigned Angle");
version_node_output_socket_name(
ntree, GEO_NODE_INPUT_MESH_ISLAND, "Index", "Island Index");
version_node_input_socket_name(ntree, GEO_NODE_TRANSFER_ATTRIBUTE, "Target", "Source");
}
}
}
2022-01-28 11:11:11 +01:00
if (!MAIN_VERSION_ATLEAST(bmain, 301, 7) ||
(bmain->versionfile == 302 && !MAIN_VERSION_ATLEAST(bmain, 302, 4))) {
/* Duplicate value for two flags that mistakenly had the same numeric value. */
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type == eModifierType_WeightVGProximity) {
WeightVGProximityModifierData *wpmd = (WeightVGProximityModifierData *)md;
if (wpmd->proximity_flags & MOD_WVG_PROXIMITY_INVERT_VGROUP_MASK) {
wpmd->proximity_flags |= MOD_WVG_PROXIMITY_WEIGHTS_NORMALIZE;
}
}
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 302, 2)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (scene->ed != NULL) {
SEQ_for_each_callback(&scene->ed->seqbase, seq_transform_filter_set, NULL);
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 302, 6)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *ts = scene->toolsettings;
if (ts->uv_relax_method == 0) {
ts->uv_relax_method = UV_SCULPT_TOOL_RELAX_LAPLACIAN;
}
}
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *tool_settings = scene->toolsettings;
tool_settings->snap_flag_seq = tool_settings->snap_flag & ~(SCE_SNAP | SCE_SNAP_SEQ);
if (tool_settings->snap_flag & SCE_SNAP_SEQ) {
tool_settings->snap_flag_seq |= SCE_SNAP;
tool_settings->snap_flag &= ~SCE_SNAP_SEQ;
}
tool_settings->snap_flag_node = tool_settings->snap_flag;
tool_settings->snap_uv_flag |= tool_settings->snap_flag & SCE_SNAP;
}
/* Alter NURBS knot mode flags to fit new modes. */
LISTBASE_FOREACH (Curve *, curve, &bmain->curves) {
LISTBASE_FOREACH (Nurb *, nurb, &curve->nurb) {
/* Previously other flags were ignored if CU_NURB_CYCLIC is set. */
if (nurb->flagu & CU_NURB_CYCLIC) {
nurb->flagu = CU_NURB_CYCLIC;
}
/* CU_NURB_BEZIER and CU_NURB_ENDPOINT were ignored if combined. */
else if (nurb->flagu & CU_NURB_BEZIER && nurb->flagu & CU_NURB_ENDPOINT) {
nurb->flagu &= ~(CU_NURB_BEZIER | CU_NURB_ENDPOINT);
BKE_nurb_knot_calc_u(nurb);
}
/* Bezier NURBS of order 3 were clamped to first control point. */
else if (nurb->orderu == 3 && (nurb->flagu & CU_NURB_BEZIER)) {
nurb->flagu |= CU_NURB_ENDPOINT;
}
/* Previously other flags were ignored if CU_NURB_CYCLIC is set. */
if (nurb->flagv & CU_NURB_CYCLIC) {
nurb->flagv = CU_NURB_CYCLIC;
}
/* CU_NURB_BEZIER and CU_NURB_ENDPOINT were ignored if used together. */
else if (nurb->flagv & CU_NURB_BEZIER && nurb->flagv & CU_NURB_ENDPOINT) {
nurb->flagv &= ~(CU_NURB_BEZIER | CU_NURB_ENDPOINT);
BKE_nurb_knot_calc_v(nurb);
}
/* Bezier NURBS of order 3 were clamped to first control point. */
else if (nurb->orderv == 3 && (nurb->flagv & CU_NURB_BEZIER)) {
nurb->flagv |= CU_NURB_ENDPOINT;
}
}
}
}
/**
* Versioning code until next subversion bump goes here.
*
* \note Be sure to check when bumping the version:
* - "versioning_userdef.c", #blo_do_versions_userdef
* - "versioning_userdef.c", #do_versions_theme
*
* \note Keep this message at the bottom of the function.
*/
{
/* Keep this block, even when empty. */
2022-01-28 11:11:11 +01:00
}
}