This commit adds low-level logic in BKE to support three behaviors in case of name conflict when renaming an ID: 1. Always tweak new name of the renamed ID (never modify the other ID name). 2. Always set requested name in renamed ID, modifying as needed the other ID name. 3. Only modify the other ID name if it shares the same root name with the current renamed ID's name. It also adds quite some changes to IDTemplate, Outliner code, and RNA-defined UILayout code, and the lower-level UI button API, to allow for the new behavior defined in the design (i.e. option three from above list). When renaming from the UI either 'fails' (falls back to adjusted name) or forces renaming another ID, an INFO report is displayed. This commit also fixes several issues in existing code, especially regarding undo handling in rename operations (which could lead to saving the wrong name in undo step, and/or over-generating undo steps). API wise, the bahavior when directly assigning a name to the `ID.name` property remains unchanged (option one from the list above). But a new API call `ID.rename` has been added, which offers all three behaviors. Unittests were added to cover the new implemented behaviors (both at BKE level, and the RNA/Py API). This commit implements #119139 design. Pull Request: https://projects.blender.org/blender/blender/pulls/126996
671 lines
22 KiB
C++
671 lines
22 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup blenloader
|
|
*/
|
|
/* allow readfile to use deprecated functionality */
|
|
#define DNA_DEPRECATED_ALLOW
|
|
|
|
#include <cstring>
|
|
|
|
#include "DNA_node_types.h"
|
|
#include "DNA_screen_types.h"
|
|
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_map.hh"
|
|
#include "BLI_string.h"
|
|
#include "BLI_string_ref.hh"
|
|
#include "BLI_string_utf8.h"
|
|
|
|
#include "BKE_animsys.h"
|
|
#include "BKE_grease_pencil_legacy_convert.hh"
|
|
#include "BKE_idprop.hh"
|
|
#include "BKE_ipo.h"
|
|
#include "BKE_lib_id.hh"
|
|
#include "BKE_lib_override.hh"
|
|
#include "BKE_main.hh"
|
|
#include "BKE_main_namemap.hh"
|
|
#include "BKE_mesh_legacy_convert.hh"
|
|
#include "BKE_node.hh"
|
|
#include "BKE_node_runtime.hh"
|
|
#include "BKE_node_tree_update.hh"
|
|
|
|
#include "NOD_socket.hh"
|
|
|
|
#include "BLT_translation.hh"
|
|
|
|
#include "SEQ_sequencer.hh"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLO_readfile.hh"
|
|
#include "readfile.hh"
|
|
#include "versioning_common.hh"
|
|
|
|
using blender::Map;
|
|
using blender::StringRef;
|
|
|
|
ARegion *do_versions_add_region_if_not_found(ListBase *regionbase,
|
|
int region_type,
|
|
const char *allocname,
|
|
int link_after_region_type)
|
|
{
|
|
ARegion *link_after_region = nullptr;
|
|
LISTBASE_FOREACH (ARegion *, region, regionbase) {
|
|
if (region->regiontype == region_type) {
|
|
return nullptr;
|
|
}
|
|
if (region->regiontype == link_after_region_type) {
|
|
link_after_region = region;
|
|
}
|
|
}
|
|
|
|
ARegion *new_region = static_cast<ARegion *>(MEM_callocN(sizeof(ARegion), allocname));
|
|
new_region->regiontype = region_type;
|
|
BLI_insertlinkafter(regionbase, link_after_region, new_region);
|
|
return new_region;
|
|
}
|
|
|
|
ARegion *do_versions_ensure_region(ListBase *regionbase,
|
|
int region_type,
|
|
const char *allocname,
|
|
int link_after_region_type)
|
|
{
|
|
ARegion *link_after_region = nullptr;
|
|
LISTBASE_FOREACH (ARegion *, region, regionbase) {
|
|
if (region->regiontype == region_type) {
|
|
return region;
|
|
}
|
|
if (region->regiontype == link_after_region_type) {
|
|
link_after_region = region;
|
|
}
|
|
}
|
|
|
|
ARegion *new_region = MEM_cnew<ARegion>(allocname);
|
|
new_region->regiontype = region_type;
|
|
BLI_insertlinkafter(regionbase, link_after_region, new_region);
|
|
return new_region;
|
|
}
|
|
|
|
ID *do_versions_rename_id(Main *bmain,
|
|
const short id_type,
|
|
const char *name_src,
|
|
const char *name_dst)
|
|
{
|
|
/* We can ignore libraries */
|
|
ListBase *lb = which_libbase(bmain, id_type);
|
|
ID *id = nullptr;
|
|
LISTBASE_FOREACH (ID *, idtest, lb) {
|
|
if (!ID_IS_LINKED(idtest)) {
|
|
if (STREQ(idtest->name + 2, name_src)) {
|
|
id = idtest;
|
|
}
|
|
if (STREQ(idtest->name + 2, name_dst)) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
}
|
|
if (id != nullptr) {
|
|
BKE_libblock_rename(*bmain, *id, name_dst);
|
|
}
|
|
return id;
|
|
}
|
|
|
|
static void change_node_socket_name(ListBase *sockets, const char *old_name, const char *new_name)
|
|
{
|
|
LISTBASE_FOREACH (bNodeSocket *, socket, sockets) {
|
|
if (STREQ(socket->name, old_name)) {
|
|
STRNCPY(socket->name, new_name);
|
|
}
|
|
if (STREQ(socket->identifier, old_name)) {
|
|
STRNCPY(socket->identifier, new_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool version_node_socket_is_used(bNodeSocket *sock)
|
|
{
|
|
return sock->flag & SOCK_IS_LINKED;
|
|
}
|
|
|
|
void version_node_socket_id_delim(bNodeSocket *socket)
|
|
{
|
|
StringRef name = socket->name;
|
|
StringRef id = socket->identifier;
|
|
|
|
if (!id.startswith(name)) {
|
|
/* We only need to affect the case where the identifier starts with the name. */
|
|
return;
|
|
}
|
|
|
|
StringRef id_number = id.drop_known_prefix(name);
|
|
if (id_number.is_empty()) {
|
|
/* The name was already unique, and didn't need numbers at the end for the id. */
|
|
return;
|
|
}
|
|
|
|
if (id_number.startswith(".")) {
|
|
socket->identifier[name.size()] = '_';
|
|
}
|
|
}
|
|
|
|
void version_node_socket_name(bNodeTree *ntree,
|
|
const int node_type,
|
|
const char *old_name,
|
|
const char *new_name)
|
|
{
|
|
for (bNode *node : ntree->all_nodes()) {
|
|
if (node->type == node_type) {
|
|
change_node_socket_name(&node->inputs, old_name, new_name);
|
|
change_node_socket_name(&node->outputs, old_name, new_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
void version_node_input_socket_name(bNodeTree *ntree,
|
|
const int node_type,
|
|
const char *old_name,
|
|
const char *new_name)
|
|
{
|
|
for (bNode *node : ntree->all_nodes()) {
|
|
if (node->type == node_type) {
|
|
change_node_socket_name(&node->inputs, old_name, new_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
void version_node_output_socket_name(bNodeTree *ntree,
|
|
const int node_type,
|
|
const char *old_name,
|
|
const char *new_name)
|
|
{
|
|
for (bNode *node : ntree->all_nodes()) {
|
|
if (node->type == node_type) {
|
|
change_node_socket_name(&node->outputs, old_name, new_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
bNode &version_node_add_empty(bNodeTree &ntree, const char *idname)
|
|
{
|
|
blender::bke::bNodeType *ntype = blender::bke::node_type_find(idname);
|
|
|
|
bNode *node = MEM_cnew<bNode>(__func__);
|
|
node->runtime = MEM_new<blender::bke::bNodeRuntime>(__func__);
|
|
BLI_addtail(&ntree.nodes, node);
|
|
blender::bke::node_unique_id(&ntree, node);
|
|
|
|
STRNCPY(node->idname, idname);
|
|
STRNCPY_UTF8(node->name, DATA_(ntype->ui_name));
|
|
blender::bke::node_unique_name(&ntree, node);
|
|
|
|
node->flag = NODE_SELECT | NODE_OPTIONS | NODE_INIT;
|
|
node->width = ntype->width;
|
|
node->height = ntype->height;
|
|
node->color[0] = node->color[1] = node->color[2] = 0.608;
|
|
|
|
node->type = ntype->type;
|
|
|
|
BKE_ntree_update_tag_node_new(&ntree, node);
|
|
return *node;
|
|
}
|
|
|
|
bNodeSocket &version_node_add_socket(bNodeTree &ntree,
|
|
bNode &node,
|
|
const eNodeSocketInOut in_out,
|
|
const char *idname,
|
|
const char *identifier)
|
|
{
|
|
blender::bke::bNodeSocketType *stype = blender::bke::node_socket_type_find(idname);
|
|
|
|
bNodeSocket *socket = MEM_cnew<bNodeSocket>(__func__);
|
|
socket->runtime = MEM_new<blender::bke::bNodeSocketRuntime>(__func__);
|
|
socket->in_out = in_out;
|
|
socket->limit = (in_out == SOCK_IN ? 1 : 0xFFF);
|
|
socket->type = stype->type;
|
|
|
|
STRNCPY(socket->idname, idname);
|
|
STRNCPY(socket->identifier, identifier);
|
|
STRNCPY(socket->name, identifier);
|
|
|
|
if (in_out == SOCK_IN) {
|
|
BLI_addtail(&node.inputs, socket);
|
|
}
|
|
else {
|
|
BLI_addtail(&node.outputs, socket);
|
|
}
|
|
|
|
node_socket_init_default_value_data(
|
|
eNodeSocketDatatype(stype->type), stype->subtype, &socket->default_value);
|
|
|
|
BKE_ntree_update_tag_socket_new(&ntree, socket);
|
|
return *socket;
|
|
}
|
|
|
|
bNodeLink &version_node_add_link(
|
|
bNodeTree &ntree, bNode &node_a, bNodeSocket &socket_a, bNode &node_b, bNodeSocket &socket_b)
|
|
{
|
|
BLI_assert(socket_a.in_out != socket_b.in_out);
|
|
if (socket_a.in_out == SOCK_IN) {
|
|
return version_node_add_link(ntree, node_b, socket_b, node_a, socket_a);
|
|
}
|
|
bNode &node_from = node_a;
|
|
bNodeSocket &socket_from = socket_a;
|
|
bNode &node_to = node_b;
|
|
bNodeSocket &socket_to = socket_b;
|
|
|
|
bNodeLink *link = MEM_cnew<bNodeLink>(__func__);
|
|
link->fromnode = &node_from;
|
|
link->fromsock = &socket_from;
|
|
link->tonode = &node_to;
|
|
link->tosock = &socket_to;
|
|
|
|
BLI_addtail(&ntree.links, link);
|
|
|
|
BKE_ntree_update_tag_link_added(&ntree, link);
|
|
return *link;
|
|
}
|
|
|
|
bNodeSocket *version_node_add_socket_if_not_exist(bNodeTree *ntree,
|
|
bNode *node,
|
|
int in_out,
|
|
int type,
|
|
int subtype,
|
|
const char *identifier,
|
|
const char *name)
|
|
{
|
|
bNodeSocket *sock = blender::bke::node_find_socket(node, eNodeSocketInOut(in_out), identifier);
|
|
if (sock != nullptr) {
|
|
return sock;
|
|
}
|
|
return blender::bke::node_add_static_socket(
|
|
ntree, node, eNodeSocketInOut(in_out), type, subtype, identifier, name);
|
|
}
|
|
|
|
void version_node_id(bNodeTree *ntree, const int node_type, const char *new_name)
|
|
{
|
|
for (bNode *node : ntree->all_nodes()) {
|
|
if (node->type == node_type) {
|
|
if (!STREQ(node->idname, new_name)) {
|
|
STRNCPY(node->idname, new_name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void version_node_socket_index_animdata(Main *bmain,
|
|
const int node_tree_type,
|
|
const int node_type,
|
|
const int socket_index_orig,
|
|
const int socket_index_offset,
|
|
const int total_number_of_sockets)
|
|
{
|
|
|
|
/* The for loop for the input ids is at the top level otherwise we lose the animation
|
|
* keyframe data. Not sure what causes that, so I (Sybren) moved the code here from
|
|
* versioning_290.cc as-is (structure-wise). */
|
|
for (int input_index = total_number_of_sockets - 1; input_index >= socket_index_orig;
|
|
input_index--)
|
|
{
|
|
FOREACH_NODETREE_BEGIN (bmain, ntree, owner_id) {
|
|
if (ntree->type != node_tree_type) {
|
|
continue;
|
|
}
|
|
|
|
for (bNode *node : ntree->all_nodes()) {
|
|
if (node->type != node_type) {
|
|
continue;
|
|
}
|
|
|
|
const size_t node_name_length = strlen(node->name);
|
|
const size_t node_name_escaped_max_length = (node_name_length * 2);
|
|
char *node_name_escaped = (char *)MEM_mallocN(node_name_escaped_max_length + 1,
|
|
"escaped name");
|
|
BLI_str_escape(node_name_escaped, node->name, node_name_escaped_max_length);
|
|
char *rna_path_prefix = BLI_sprintfN("nodes[\"%s\"].inputs", node_name_escaped);
|
|
|
|
const int new_index = input_index + socket_index_offset;
|
|
BKE_animdata_fix_paths_rename_all_ex(
|
|
bmain, owner_id, rna_path_prefix, nullptr, nullptr, input_index, new_index, false);
|
|
MEM_freeN(rna_path_prefix);
|
|
MEM_freeN(node_name_escaped);
|
|
}
|
|
}
|
|
FOREACH_NODETREE_END;
|
|
}
|
|
}
|
|
|
|
void version_socket_update_is_used(bNodeTree *ntree)
|
|
{
|
|
for (bNode *node : ntree->all_nodes()) {
|
|
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
|
|
socket->flag &= ~SOCK_IS_LINKED;
|
|
}
|
|
LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
|
|
socket->flag &= ~SOCK_IS_LINKED;
|
|
}
|
|
}
|
|
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
|
|
link->fromsock->flag |= SOCK_IS_LINKED;
|
|
link->tosock->flag |= SOCK_IS_LINKED;
|
|
}
|
|
}
|
|
|
|
ARegion *do_versions_add_region(int regiontype, const char *name)
|
|
{
|
|
ARegion *region = (ARegion *)MEM_callocN(sizeof(ARegion), name);
|
|
region->regiontype = regiontype;
|
|
return region;
|
|
}
|
|
|
|
void node_tree_relink_with_socket_id_map(bNodeTree &ntree,
|
|
bNode &old_node,
|
|
bNode &new_node,
|
|
const Map<std::string, std::string> &map)
|
|
{
|
|
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree.links) {
|
|
if (link->tonode == &old_node) {
|
|
bNodeSocket *old_socket = link->tosock;
|
|
if (old_socket->is_available()) {
|
|
if (const std::string *new_identifier = map.lookup_ptr_as(old_socket->identifier)) {
|
|
bNodeSocket *new_socket = blender::bke::node_find_socket(
|
|
&new_node, SOCK_IN, *new_identifier);
|
|
link->tonode = &new_node;
|
|
link->tosock = new_socket;
|
|
old_socket->link = nullptr;
|
|
}
|
|
}
|
|
}
|
|
if (link->fromnode == &old_node) {
|
|
bNodeSocket *old_socket = link->fromsock;
|
|
if (old_socket->is_available()) {
|
|
if (const std::string *new_identifier = map.lookup_ptr_as(old_socket->identifier)) {
|
|
bNodeSocket *new_socket = blender::bke::node_find_socket(
|
|
&new_node, SOCK_OUT, *new_identifier);
|
|
link->fromnode = &new_node;
|
|
link->fromsock = new_socket;
|
|
old_socket->link = nullptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static blender::Vector<bNodeLink *> find_connected_links(bNodeTree *ntree, bNodeSocket *in_socket)
|
|
{
|
|
blender::Vector<bNodeLink *> links;
|
|
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
|
|
if (link->tosock == in_socket) {
|
|
links.append(link);
|
|
}
|
|
}
|
|
return links;
|
|
}
|
|
|
|
void add_realize_instances_before_socket(bNodeTree *ntree,
|
|
bNode *node,
|
|
bNodeSocket *geometry_socket)
|
|
{
|
|
BLI_assert(geometry_socket->type == SOCK_GEOMETRY);
|
|
blender::Vector<bNodeLink *> links = find_connected_links(ntree, geometry_socket);
|
|
for (bNodeLink *link : links) {
|
|
/* 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 = blender::bke::node_add_static_node(
|
|
nullptr, ntree, GEO_NODE_REALIZE_INSTANCES);
|
|
realize_node->parent = node->parent;
|
|
realize_node->locx = node->locx - 100;
|
|
realize_node->locy = node->locy;
|
|
blender::bke::node_add_link(ntree,
|
|
link->fromnode,
|
|
link->fromsock,
|
|
realize_node,
|
|
static_cast<bNodeSocket *>(realize_node->inputs.first));
|
|
link->fromnode = realize_node;
|
|
link->fromsock = static_cast<bNodeSocket *>(realize_node->outputs.first);
|
|
}
|
|
}
|
|
|
|
float *version_cycles_node_socket_float_value(bNodeSocket *socket)
|
|
{
|
|
bNodeSocketValueFloat *socket_data = static_cast<bNodeSocketValueFloat *>(socket->default_value);
|
|
return &socket_data->value;
|
|
}
|
|
|
|
float *version_cycles_node_socket_rgba_value(bNodeSocket *socket)
|
|
{
|
|
bNodeSocketValueRGBA *socket_data = static_cast<bNodeSocketValueRGBA *>(socket->default_value);
|
|
return socket_data->value;
|
|
}
|
|
|
|
float *version_cycles_node_socket_vector_value(bNodeSocket *socket)
|
|
{
|
|
bNodeSocketValueVector *socket_data = static_cast<bNodeSocketValueVector *>(
|
|
socket->default_value);
|
|
return socket_data->value;
|
|
}
|
|
|
|
IDProperty *version_cycles_properties_from_ID(ID *id)
|
|
{
|
|
IDProperty *idprop = IDP_GetProperties(id);
|
|
return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles", IDP_GROUP) : nullptr;
|
|
}
|
|
|
|
IDProperty *version_cycles_properties_from_view_layer(ViewLayer *view_layer)
|
|
{
|
|
IDProperty *idprop = view_layer->id_properties;
|
|
return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles", IDP_GROUP) : nullptr;
|
|
}
|
|
|
|
IDProperty *version_cycles_properties_from_render_layer(SceneRenderLayer *render_layer)
|
|
{
|
|
IDProperty *idprop = render_layer->prop;
|
|
return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles", IDP_GROUP) : nullptr;
|
|
}
|
|
|
|
float version_cycles_property_float(IDProperty *idprop, const char *name, float default_value)
|
|
{
|
|
IDProperty *prop = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_FLOAT);
|
|
return (prop) ? IDP_Float(prop) : default_value;
|
|
}
|
|
|
|
int version_cycles_property_int(IDProperty *idprop, const char *name, int default_value)
|
|
{
|
|
IDProperty *prop = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_INT);
|
|
return (prop) ? IDP_Int(prop) : default_value;
|
|
}
|
|
|
|
void version_cycles_property_int_set(IDProperty *idprop, const char *name, int value)
|
|
{
|
|
if (IDProperty *prop = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_INT)) {
|
|
IDP_Int(prop) = value;
|
|
}
|
|
else {
|
|
IDP_AddToGroup(idprop, blender::bke::idprop::create(name, value).release());
|
|
}
|
|
}
|
|
|
|
bool version_cycles_property_boolean(IDProperty *idprop, const char *name, bool default_value)
|
|
{
|
|
return version_cycles_property_int(idprop, name, default_value);
|
|
}
|
|
|
|
void version_cycles_property_boolean_set(IDProperty *idprop, const char *name, bool value)
|
|
{
|
|
version_cycles_property_int_set(idprop, name, value);
|
|
}
|
|
|
|
IDProperty *version_cycles_visibility_properties_from_ID(ID *id)
|
|
{
|
|
IDProperty *idprop = IDP_GetProperties(id);
|
|
return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles_visibility", IDP_GROUP) : nullptr;
|
|
}
|
|
|
|
void version_update_node_input(
|
|
bNodeTree *ntree,
|
|
FunctionRef<bool(bNode *)> check_node,
|
|
const char *socket_identifier,
|
|
FunctionRef<void(bNode *, bNodeSocket *)> update_input,
|
|
FunctionRef<void(bNode *, bNodeSocket *, bNode *, bNodeSocket *)> update_input_link)
|
|
{
|
|
bool need_update = false;
|
|
|
|
/* Iterate backwards from end so we don't encounter newly added links. */
|
|
LISTBASE_FOREACH_BACKWARD_MUTABLE (bNodeLink *, link, &ntree->links) {
|
|
/* Detect link to replace. */
|
|
bNode *fromnode = link->fromnode;
|
|
bNodeSocket *fromsock = link->fromsock;
|
|
bNode *tonode = link->tonode;
|
|
bNodeSocket *tosock = link->tosock;
|
|
|
|
if (!(tonode != nullptr && check_node(tonode) && STREQ(tosock->identifier, socket_identifier)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* Replace links with updated equivalent */
|
|
blender::bke::node_remove_link(ntree, link);
|
|
update_input_link(fromnode, fromsock, tonode, tosock);
|
|
|
|
need_update = true;
|
|
}
|
|
|
|
/* Update sockets and/or their default values.
|
|
* Do this after the link update in case it changes the identifier. */
|
|
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
|
if (check_node(node)) {
|
|
bNodeSocket *input = blender::bke::node_find_socket(node, SOCK_IN, socket_identifier);
|
|
if (input != nullptr) {
|
|
update_input(node, input);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (need_update) {
|
|
version_socket_update_is_used(ntree);
|
|
}
|
|
}
|
|
|
|
bNode *version_eevee_output_node_get(bNodeTree *ntree, int16_t node_type)
|
|
{
|
|
bNode *output_node = nullptr;
|
|
/* NOTE: duplicated from `ntreeShaderOutputNode` with small adjustments so it can be called
|
|
* during versioning. */
|
|
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
|
if (node->type != node_type) {
|
|
continue;
|
|
}
|
|
if (node->custom1 == SHD_OUTPUT_ALL) {
|
|
if (output_node == nullptr) {
|
|
output_node = node;
|
|
}
|
|
else if (output_node->custom1 == SHD_OUTPUT_ALL) {
|
|
if ((node->flag & NODE_DO_OUTPUT) && !(output_node->flag & NODE_DO_OUTPUT)) {
|
|
output_node = node;
|
|
}
|
|
}
|
|
}
|
|
else if (node->custom1 == SHD_OUTPUT_EEVEE) {
|
|
if (output_node == nullptr) {
|
|
output_node = node;
|
|
}
|
|
else if ((node->flag & NODE_DO_OUTPUT) && !(output_node->flag & NODE_DO_OUTPUT)) {
|
|
output_node = node;
|
|
}
|
|
}
|
|
}
|
|
|
|
return output_node;
|
|
}
|
|
|
|
static bool blendfile_or_libraries_versions_atleast(Main *bmain,
|
|
const short versionfile,
|
|
const short subversionfile)
|
|
{
|
|
if (!MAIN_VERSION_FILE_ATLEAST(bmain, versionfile, subversionfile)) {
|
|
return false;
|
|
}
|
|
|
|
LISTBASE_FOREACH (Library *, library, &bmain->libraries) {
|
|
if (!LIBRARY_VERSION_FILE_ATLEAST(library, versionfile, subversionfile)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void do_versions_after_setup(Main *new_bmain,
|
|
BlendfileLinkAppendContext *lapp_context,
|
|
BlendFileReadReport *reports)
|
|
{
|
|
/* WARNING: The code below may add IDs. These IDs _will_ be (by definition) conforming to current
|
|
* code's version already, and _must not_ be 'versionned' again.
|
|
*
|
|
* This means that when adding code here, _extreme_ care must be taken that it will not badly
|
|
* affect these 'modern' IDs potentially added by already existing processing.
|
|
*
|
|
* Adding code here should only be done in exceptional cases.
|
|
*
|
|
* Some further points to keep in mind:
|
|
* - While typically versioning order should be respected in code below (i.e. versioning
|
|
* affecting older versions should be done first), _this is not a hard rule_. And it should
|
|
* not be assumed older code must not be checked when adding newer code.
|
|
* - Do not rely strongly on versioning numbers here. This code may be run on data from
|
|
* different Blender versions (through the usage of linked data), and all existing data have
|
|
* already been processed through the whole do_version during blendfile reading itself. So
|
|
* decision to apply some versioning on some data should mostly rely on the data itself.
|
|
* - Unlike the regular do_version code, this one should _not_ be assumed as 'valid forever'.
|
|
* It is closer to the Editing or BKE code in that respect, changes to the logic or data
|
|
* model of an ID will require a careful update here as well.
|
|
*
|
|
* Another critical weakness of this code is that it is currently _not_ performed on data linked
|
|
* during an editing session, but only on data linked while reading a whole blendfile. This will
|
|
* have to be fixed at some point.
|
|
*/
|
|
|
|
/* NOTE: Version number is checked against Main version (i.e. current blend file version), AND
|
|
* the versions of all the linked libraries. */
|
|
|
|
if (!blendfile_or_libraries_versions_atleast(new_bmain, 250, 0)) {
|
|
do_versions_ipos_to_animato(new_bmain);
|
|
}
|
|
|
|
if (!blendfile_or_libraries_versions_atleast(new_bmain, 250, 0)) {
|
|
LISTBASE_FOREACH (Scene *, scene, &new_bmain->scenes) {
|
|
if (scene->ed) {
|
|
SEQ_doversion_250_sound_proxy_update(new_bmain, scene->ed);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!blendfile_or_libraries_versions_atleast(new_bmain, 302, 1)) {
|
|
BKE_lib_override_library_main_proxy_convert(new_bmain, reports);
|
|
/* Currently liboverride code can generate invalid namemap. This is a known issue, requires
|
|
* #107847 to be properly fixed. */
|
|
BKE_main_namemap_validate_and_fix(new_bmain);
|
|
}
|
|
|
|
if (!blendfile_or_libraries_versions_atleast(new_bmain, 302, 3)) {
|
|
/* Does not add any new IDs, but needs the full Main data-base. */
|
|
BKE_lib_override_library_main_hierarchy_root_ensure(new_bmain);
|
|
}
|
|
|
|
if (!blendfile_or_libraries_versions_atleast(new_bmain, 402, 22)) {
|
|
/* Initial auto smooth versioning started at (401, 2), but a bug caused the legacy flag to not
|
|
* be cleared, so it is re-run in a later version when the bug is fixed and the versioning has
|
|
* been made idempotent. */
|
|
BKE_main_mesh_legacy_convert_auto_smooth(*new_bmain);
|
|
}
|
|
|
|
if (!blendfile_or_libraries_versions_atleast(new_bmain, 403, 3)) {
|
|
/* Convert all the legacy grease pencil objects. This does not touch annotations. */
|
|
blender::bke::greasepencil::convert::legacy_main(*new_bmain, lapp_context, *reports);
|
|
}
|
|
}
|