Files
test/source/blender/blenloader/intern/versioning_510.cc
Omar Emara 28f84831c3 Fix #144815: Assert when loading certain files
Th compositor asserts when loading certain files. Those file were saved
with an invalid active viewer key, possibly due to a bug that was fixed
already in c8cb24121f, but blend files were never updated.

So starting in 5.1, we fix those files by essentially doing what
ED_node_set_active_viewer_key is supposed to do at load time during
versioning.

Pull Request: https://projects.blender.org/blender/blender/pulls/148047
2025-10-14 14:39:27 +02:00

283 lines
12 KiB
C++

/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blenloader
*/
#define DNA_DEPRECATED_ALLOW
#include "DNA_ID.h"
#include "DNA_material_types.h"
#include "DNA_node_types.h"
#include "DNA_screen_types.h"
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_sys_types.h"
#include "BKE_main.hh"
#include "BKE_node.hh"
#include "BKE_node_legacy_types.hh"
#include "BKE_node_runtime.hh"
#include "readfile.hh"
#include "versioning_common.hh"
// #include "CLG_log.h"
// static CLG_LogRef LOG = {"blend.doversion"};
/* The Mix mode of the Mix node previously assumed the alpha of the first input as opposed to
* mixing the alpha as well. So we add a separate color node to get the alpha of the first input
* and set it to the result using a set alpha node. */
static void do_version_mix_node_mix_mode_compositor(bNodeTree &node_tree, bNode &node)
{
const NodeShaderMix *data = reinterpret_cast<NodeShaderMix *>(node.storage);
if (data->data_type != SOCK_RGBA) {
return;
}
if (data->blend_type != MA_RAMP_BLEND) {
return;
}
bNodeSocket *first_input = blender::bke::node_find_socket(node, SOCK_IN, "A_Color");
bNodeSocket *output = blender::bke::node_find_socket(node, SOCK_OUT, "Result_Color");
/* Find the link going into the inputs of the node. */
bNodeLink *first_link = nullptr;
LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
if (link->tosock == first_input) {
first_link = link;
}
}
bNode &separate_node = version_node_add_empty(node_tree, "CompositorNodeSeparateColor");
separate_node.parent = node.parent;
separate_node.location[0] = node.location[0] - 10.0f;
separate_node.location[1] = node.location[1];
NodeCMPCombSepColor *storage = MEM_callocN<NodeCMPCombSepColor>(__func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_RGB;
separate_node.storage = storage;
bNodeSocket &separate_input = version_node_add_socket(
node_tree, separate_node, SOCK_IN, "NodeSocketColor", "Image");
bNodeSocket &separate_alpha_output = version_node_add_socket(
node_tree, separate_node, SOCK_OUT, "NodeSocketFloat", "Alpha");
copy_v4_v4(separate_input.default_value_typed<bNodeSocketValueRGBA>()->value,
first_input->default_value_typed<bNodeSocketValueRGBA>()->value);
if (first_link) {
version_node_add_link(
node_tree, *first_link->fromnode, *first_link->fromsock, separate_node, separate_input);
}
bNode &set_alpha_node = version_node_add_empty(node_tree, "CompositorNodeSetAlpha");
set_alpha_node.parent = node.parent;
set_alpha_node.location[0] = node.location[0] - 10.0f;
set_alpha_node.location[1] = node.location[1];
set_alpha_node.storage = MEM_callocN<NodeCMPCombSepColor>(__func__);
bNodeSocket &set_alpha_image_input = version_node_add_socket(
node_tree, set_alpha_node, SOCK_IN, "NodeSocketColor", "Image");
bNodeSocket &set_alpha_alpha_input = version_node_add_socket(
node_tree, set_alpha_node, SOCK_IN, "NodeSocketFloat", "Alpha");
bNodeSocket &set_alpha_type_input = version_node_add_socket(
node_tree, set_alpha_node, SOCK_IN, "NodeSocketMenu", "Type");
bNodeSocket &set_alpha_output = version_node_add_socket(
node_tree, set_alpha_node, SOCK_OUT, "NodeSocketColor", "Image");
set_alpha_type_input.default_value_typed<bNodeSocketValueMenu>()->value =
CMP_NODE_SETALPHA_MODE_REPLACE_ALPHA;
version_node_add_link(node_tree, node, *output, set_alpha_node, set_alpha_image_input);
version_node_add_link(
node_tree, separate_node, separate_alpha_output, set_alpha_node, set_alpha_alpha_input);
LISTBASE_FOREACH_BACKWARD_MUTABLE (bNodeLink *, link, &node_tree.links) {
if (link->fromsock == output && link->tonode != &set_alpha_node) {
version_node_add_link(
node_tree, set_alpha_node, set_alpha_output, *link->tonode, *link->tosock);
blender::bke::node_remove_link(&node_tree, *link);
}
}
}
/* The Mix mode of the Mix node previously assumed the alpha of the first input as opposed to
* mixing the alpha as well. So we add a separate color node to get the alpha of the first input
* and set it to the result using a pair of separate and combine color nodes. */
static void do_version_mix_node_mix_mode_geometry(bNodeTree &node_tree, bNode &node)
{
const NodeShaderMix *data = reinterpret_cast<NodeShaderMix *>(node.storage);
if (data->data_type != SOCK_RGBA) {
return;
}
if (data->blend_type != MA_RAMP_BLEND) {
return;
}
bNodeSocket *first_input = blender::bke::node_find_socket(node, SOCK_IN, "A_Color");
bNodeSocket *output = blender::bke::node_find_socket(node, SOCK_OUT, "Result_Color");
/* Find the link going into the inputs of the node. */
bNodeLink *first_link = nullptr;
LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
if (link->tosock == first_input) {
first_link = link;
}
}
bNode &separate_alpha_node = version_node_add_empty(node_tree, "FunctionNodeSeparateColor");
separate_alpha_node.parent = node.parent;
separate_alpha_node.location[0] = node.location[0] - 10.0f;
separate_alpha_node.location[1] = node.location[1];
NodeCombSepColor *separate_alpha_storage = MEM_callocN<NodeCombSepColor>(__func__);
separate_alpha_storage->mode = NODE_COMBSEP_COLOR_RGB;
separate_alpha_node.storage = separate_alpha_storage;
bNodeSocket &separate_alpha_input = version_node_add_socket(
node_tree, separate_alpha_node, SOCK_IN, "NodeSocketColor", "Color");
bNodeSocket &separate_alpha_output = version_node_add_socket(
node_tree, separate_alpha_node, SOCK_OUT, "NodeSocketFloat", "Alpha");
copy_v4_v4(separate_alpha_input.default_value_typed<bNodeSocketValueRGBA>()->value,
first_input->default_value_typed<bNodeSocketValueRGBA>()->value);
if (first_link) {
version_node_add_link(node_tree,
*first_link->fromnode,
*first_link->fromsock,
separate_alpha_node,
separate_alpha_input);
}
bNode &separate_color_node = version_node_add_empty(node_tree, "FunctionNodeSeparateColor");
separate_color_node.parent = node.parent;
separate_color_node.location[0] = node.location[0] - 10.0f;
separate_color_node.location[1] = node.location[1];
NodeCombSepColor *separate_color_storage = MEM_callocN<NodeCombSepColor>(__func__);
separate_color_storage->mode = NODE_COMBSEP_COLOR_RGB;
separate_color_node.storage = separate_color_storage;
bNodeSocket &separate_color_input = version_node_add_socket(
node_tree, separate_color_node, SOCK_IN, "NodeSocketColor", "Color");
bNodeSocket &separate_color_red_output = version_node_add_socket(
node_tree, separate_color_node, SOCK_OUT, "NodeSocketFloat", "Red");
bNodeSocket &separate_color_green_output = version_node_add_socket(
node_tree, separate_color_node, SOCK_OUT, "NodeSocketFloat", "Green");
bNodeSocket &separate_color_blue_output = version_node_add_socket(
node_tree, separate_color_node, SOCK_OUT, "NodeSocketFloat", "Blue");
version_node_add_link(node_tree, node, *output, separate_color_node, separate_color_input);
bNode &combine_color_node = version_node_add_empty(node_tree, "FunctionNodeCombineColor");
combine_color_node.parent = node.parent;
combine_color_node.location[0] = node.location[0] - 10.0f;
combine_color_node.location[1] = node.location[1];
NodeCombSepColor *combine_color_storage = MEM_callocN<NodeCombSepColor>(__func__);
combine_color_storage->mode = NODE_COMBSEP_COLOR_RGB;
combine_color_node.storage = combine_color_storage;
bNodeSocket &combine_color_red_input = version_node_add_socket(
node_tree, combine_color_node, SOCK_IN, "NodeSocketFloat", "Red");
bNodeSocket &combine_color_green_input = version_node_add_socket(
node_tree, combine_color_node, SOCK_IN, "NodeSocketFloat", "Green");
bNodeSocket &combine_color_blue_input = version_node_add_socket(
node_tree, combine_color_node, SOCK_IN, "NodeSocketFloat", "Blue");
bNodeSocket &combine_color_alpha_input = version_node_add_socket(
node_tree, combine_color_node, SOCK_IN, "NodeSocketFloat", "Alpha");
bNodeSocket &combine_color_output = version_node_add_socket(
node_tree, combine_color_node, SOCK_OUT, "NodeSocketColor", "Color");
version_node_add_link(node_tree,
separate_color_node,
separate_color_red_output,
combine_color_node,
combine_color_red_input);
version_node_add_link(node_tree,
separate_color_node,
separate_color_green_output,
combine_color_node,
combine_color_green_input);
version_node_add_link(node_tree,
separate_color_node,
separate_color_blue_output,
combine_color_node,
combine_color_blue_input);
version_node_add_link(node_tree,
separate_alpha_node,
separate_alpha_output,
combine_color_node,
combine_color_alpha_input);
LISTBASE_FOREACH_BACKWARD_MUTABLE (bNodeLink *, link, &node_tree.links) {
if (link->fromsock == output && link->tonode != &separate_color_node) {
version_node_add_link(
node_tree, combine_color_node, combine_color_output, *link->tonode, *link->tosock);
blender::bke::node_remove_link(&node_tree, *link);
}
}
}
void do_versions_after_linking_510(FileData * /*fd*/, Main *bmain)
{
/* Some blend files were saved with an invalid active viewer key, possibly due to a bug that was
* fixed already in c8cb24121f, but blend files were never updated. So starting in 5.1, we fix
* those files by essentially doing what ED_node_set_active_viewer_key is supposed to do at load
* time during versioning. Note that the invalid active viewer will just cause a harmless assert,
* so this does not need to exist in previous releases. */
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 0)) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) {
if (space->spacetype == SPACE_NODE) {
SpaceNode *space_node = reinterpret_cast<SpaceNode *>(space);
bNodeTreePath *path = static_cast<bNodeTreePath *>(space_node->treepath.last);
if (space_node->nodetree && path) {
space_node->nodetree->active_viewer_key = path->parent_key;
}
}
}
}
}
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
*
* \note Keep this message at the bottom of the function.
*/
}
void blo_do_versions_510(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
{
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 1)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == SH_NODE_MIX) {
do_version_mix_node_mix_mode_compositor(*node_tree, *node);
}
}
}
else if (node_tree->type == NTREE_GEOMETRY) {
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == SH_NODE_MIX) {
do_version_mix_node_mix_mode_geometry(*node_tree, *node);
}
}
}
}
FOREACH_NODETREE_END;
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
*
* \note Keep this message at the bottom of the function.
*/
}