Files
test/source/blender/blenloader/intern/versioning_500.cc
hogan.mastanduno dde9d21b91 OpenXR: VR Advanced Locomotion Phase 1
Includes the following changes to the existing Locomotion system for VR Scene Inspection:
 * new VR Navigation Preferences and VR Session Settings
 * changes to XR raycast logic and its visualization
 * new XR vignette that appears when moving
 * snap turning

Pull Request: https://projects.blender.org/blender/blender/pulls/144241
2025-10-01 22:16:12 +02:00

3797 lines
148 KiB
C++

/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blenloader
*/
#define DNA_DEPRECATED_ALLOW
#include <fmt/format.h>
#include "MEM_guardedalloc.h"
/* Define macros in `DNA_genfile.h`. */
#define DNA_GENFILE_VERSIONING_MACROS
#include "DNA_ID.h"
#include "DNA_brush_types.h"
#include "DNA_camera_types.h"
#include "DNA_curves_types.h"
#include "DNA_genfile.h"
#include "DNA_grease_pencil_types.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_sequence_types.h"
#include "DNA_windowmanager_types.h"
#include "DNA_workspace_types.h"
#include "DNA_world_types.h"
#include "BLI_function_ref.hh"
#include "BLI_listbase.h"
#include "BLI_math_color.h"
#include "BLI_math_numbers.hh"
#include "BLI_math_vector.h"
#include "BLI_math_vector.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_set.hh"
#include "BLI_string_utf8.h"
#include "BLI_string_utils.hh"
#include "BLI_sys_types.h"
#include "BKE_animsys.h"
#include "BKE_armature.hh"
#include "BKE_attribute_legacy_convert.hh"
#include "BKE_colortools.hh"
#include "BKE_curves.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_idprop.hh"
#include "BKE_image_format.hh"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
#include "BKE_mesh_legacy_convert.hh"
#include "BKE_node.hh"
#include "BKE_node_legacy_types.hh"
#include "BKE_node_runtime.hh"
#include "BKE_paint.hh"
#include "BKE_pointcache.h"
#include "BKE_report.hh"
#include "BLT_translation.hh"
#include "BLO_read_write.hh"
#include "SEQ_edit.hh"
#include "SEQ_effects.hh"
#include "SEQ_iterator.hh"
#include "SEQ_modifier.hh"
#include "SEQ_relations.hh"
#include "SEQ_sequencer.hh"
#include "SEQ_utils.hh"
#include "WM_api.hh"
#include "AS_asset_library.hh"
#include "readfile.hh"
#include "versioning_common.hh"
// #include "CLG_log.h"
// static CLG_LogRef LOG = {"blend.doversion"};
static void idprops_process(IDProperty *idprops, IDProperty **system_idprops)
{
BLI_assert(*system_idprops == nullptr);
if (idprops) {
/* Other ID pointers have not yet been relinked, do not try to access them for refcounting. */
*system_idprops = IDP_CopyProperty_ex(idprops, LIB_ID_CREATE_NO_USER_REFCOUNT);
}
}
void version_system_idprops_generate(Main *bmain)
{
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
idprops_process(id_iter->properties, &id_iter->system_properties);
}
FOREACH_MAIN_ID_END;
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
idprops_process(view_layer->id_properties, &view_layer->system_properties);
}
if (scene->ed != nullptr) {
blender::seq::for_each_callback(&scene->ed->seqbase, [](Strip *strip) -> bool {
idprops_process(strip->prop, &strip->system_properties);
return true;
});
}
}
LISTBASE_FOREACH (Object *, object, &bmain->objects) {
if (!object->pose) {
continue;
}
LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) {
idprops_process(pchan->prop, &pchan->system_properties);
}
}
LISTBASE_FOREACH (bArmature *, armature, &bmain->armatures) {
for (BoneCollection *bcoll : armature->collections_span()) {
idprops_process(bcoll->prop, &bcoll->system_properties);
}
LISTBASE_FOREACH (Bone *, bone, &armature->bonebase) {
idprops_process(bone->prop, &bone->system_properties);
}
}
}
/* Separate callback for nodes, because they had the split implemented later. */
void version_system_idprops_nodes_generate(Main *bmain)
{
FOREACH_NODETREE_BEGIN (bmain, node_tree, id_owner) {
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
idprops_process(node->prop, &node->system_properties);
}
}
FOREACH_NODETREE_END;
}
static CustomDataLayer *find_old_seam_layer(CustomData &custom_data, const blender::StringRef name)
{
for (CustomDataLayer &layer : blender::MutableSpan(custom_data.layers, custom_data.totlayer)) {
if (layer.name == name) {
return &layer;
}
}
return nullptr;
}
static void rename_mesh_uv_seam_attribute(Mesh &mesh)
{
using namespace blender;
CustomDataLayer *old_seam_layer = find_old_seam_layer(mesh.edge_data, ".uv_seam");
if (!old_seam_layer) {
return;
}
Set<StringRef> names;
for (const CustomDataLayer &layer : Span(mesh.vert_data.layers, mesh.vert_data.totlayer)) {
if (layer.type & CD_MASK_PROP_ALL) {
names.add(layer.name);
}
}
for (const CustomDataLayer &layer : Span(mesh.edge_data.layers, mesh.edge_data.totlayer)) {
if (layer.type & CD_MASK_PROP_ALL) {
names.add(layer.name);
}
}
for (const CustomDataLayer &layer : Span(mesh.face_data.layers, mesh.face_data.totlayer)) {
if (layer.type & CD_MASK_PROP_ALL) {
names.add(layer.name);
}
}
for (const CustomDataLayer &layer : Span(mesh.corner_data.layers, mesh.corner_data.totlayer)) {
if (layer.type & CD_MASK_PROP_ALL) {
names.add(layer.name);
}
}
LISTBASE_FOREACH (const bDeformGroup *, vertex_group, &mesh.vertex_group_names) {
names.add(vertex_group->name);
}
/* If the new UV name is already taken, still rename the attribute so it becomes visible in the
* list. Then the user can deal with the name conflict themselves. */
const std::string new_name = BLI_uniquename_cb(
[&](const StringRef name) { return names.contains(name); }, '.', "uv_seam");
STRNCPY_UTF8(old_seam_layer->name, new_name.c_str());
}
static void update_brush_sizes(Main &bmain)
{
/* This conversion was originally done in 582c7d94b8, between subversion 1 (84bee96757) and
* subversion 2 (fa03c53d4a). The original change should have come with a subversion bump to be
* filled in later, but since it didn't, the best we can do is use subversion 1 for this check.
* Thankfully, this only results in a single day window in which a user would have had to
* download the build where this versioning was not correctly applied. */
LISTBASE_FOREACH (Brush *, brush, &bmain.brushes) {
brush->size *= 2;
brush->unprojected_size *= 2.0f;
}
auto apply_to_paint = [&](Paint *paint) {
if (paint == nullptr) {
return;
}
UnifiedPaintSettings &ups = paint->unified_paint_settings;
ups.size *= 2;
ups.unprojected_size *= 2.0f;
};
LISTBASE_FOREACH (Scene *, scene, &bmain.scenes) {
scene->toolsettings->unified_paint_settings.size *= 2;
scene->toolsettings->unified_paint_settings.unprojected_size *= 2.0f;
apply_to_paint(reinterpret_cast<Paint *>(scene->toolsettings->vpaint));
apply_to_paint(reinterpret_cast<Paint *>(scene->toolsettings->wpaint));
apply_to_paint(reinterpret_cast<Paint *>(scene->toolsettings->sculpt));
apply_to_paint(reinterpret_cast<Paint *>(scene->toolsettings->gp_paint));
apply_to_paint(reinterpret_cast<Paint *>(scene->toolsettings->gp_vertexpaint));
apply_to_paint(reinterpret_cast<Paint *>(scene->toolsettings->gp_sculptpaint));
apply_to_paint(reinterpret_cast<Paint *>(scene->toolsettings->gp_weightpaint));
apply_to_paint(reinterpret_cast<Paint *>(scene->toolsettings->curves_sculpt));
apply_to_paint(reinterpret_cast<Paint *>(&scene->toolsettings->imapaint));
}
}
static void initialize_closure_input_structure_types(bNodeTree &ntree)
{
LISTBASE_FOREACH (bNode *, node, &ntree.nodes) {
if (node->type_legacy == NODE_EVALUATE_CLOSURE) {
auto *storage = static_cast<NodeEvaluateClosure *>(node->storage);
if (!storage) {
/* Can happen with certain files saved in 4.5 which did not officially support closures
* yet. */
continue;
}
for (const int i : blender::IndexRange(storage->input_items.items_num)) {
NodeEvaluateClosureInputItem &item = storage->input_items.items[i];
if (item.structure_type == NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) {
item.structure_type = NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_DYNAMIC;
}
}
for (const int i : blender::IndexRange(storage->output_items.items_num)) {
NodeEvaluateClosureOutputItem &item = storage->output_items.items[i];
if (item.structure_type == NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) {
item.structure_type = NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_DYNAMIC;
}
}
}
}
}
static void versioning_replace_legacy_combined_and_separate_color_nodes(bNodeTree *ntree)
{
/* In geometry nodes, replace shader combine/separate color nodes with function nodes */
if (ntree->type == NTREE_GEOMETRY) {
version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "R", "Red");
version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "G", "Green");
version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "B", "Blue");
version_node_output_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "Image", "Color");
version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "R", "Red");
version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "G", "Green");
version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "B", "Blue");
version_node_input_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "Image", "Color");
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
switch (node->type_legacy) {
case SH_NODE_COMBRGB_LEGACY: {
node->type_legacy = FN_NODE_COMBINE_COLOR;
NodeCombSepColor *storage = MEM_callocN<NodeCombSepColor>(__func__);
storage->mode = NODE_COMBSEP_COLOR_RGB;
STRNCPY_UTF8(node->idname, "FunctionNodeCombineColor");
node->storage = storage;
break;
}
case SH_NODE_SEPRGB_LEGACY: {
node->type_legacy = FN_NODE_SEPARATE_COLOR;
NodeCombSepColor *storage = MEM_callocN<NodeCombSepColor>(__func__);
storage->mode = NODE_COMBSEP_COLOR_RGB;
STRNCPY_UTF8(node->idname, "FunctionNodeSeparateColor");
node->storage = storage;
break;
}
}
}
}
/* In compositing nodes, replace combine/separate RGBA/HSVA/YCbCrA/YCCA nodes with
* combine/separate color */
if (ntree->type == NTREE_COMPOSIT) {
version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "R", "Red");
version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "G", "Green");
version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "B", "Blue");
version_node_input_socket_name(ntree, CMP_NODE_COMBRGBA_LEGACY, "A", "Alpha");
version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "H", "Red");
version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "S", "Green");
version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "V", "Blue");
version_node_input_socket_name(ntree, CMP_NODE_COMBHSVA_LEGACY, "A", "Alpha");
version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "Y", "Red");
version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "Cb", "Green");
version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "Cr", "Blue");
version_node_input_socket_name(ntree, CMP_NODE_COMBYCCA_LEGACY, "A", "Alpha");
version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "Y", "Red");
version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "U", "Green");
version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "V", "Blue");
version_node_input_socket_name(ntree, CMP_NODE_COMBYUVA_LEGACY, "A", "Alpha");
version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "R", "Red");
version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "G", "Green");
version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "B", "Blue");
version_node_output_socket_name(ntree, CMP_NODE_SEPRGBA_LEGACY, "A", "Alpha");
version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "H", "Red");
version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "S", "Green");
version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "V", "Blue");
version_node_output_socket_name(ntree, CMP_NODE_SEPHSVA_LEGACY, "A", "Alpha");
version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "Y", "Red");
version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "Cb", "Green");
version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "Cr", "Blue");
version_node_output_socket_name(ntree, CMP_NODE_SEPYCCA_LEGACY, "A", "Alpha");
version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "Y", "Red");
version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "U", "Green");
version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "V", "Blue");
version_node_output_socket_name(ntree, CMP_NODE_SEPYUVA_LEGACY, "A", "Alpha");
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
switch (node->type_legacy) {
case CMP_NODE_COMBRGBA_LEGACY: {
node->type_legacy = CMP_NODE_COMBINE_COLOR;
NodeCMPCombSepColor *storage = MEM_callocN<NodeCMPCombSepColor>(__func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_RGB;
STRNCPY_UTF8(node->idname, "CompositorNodeCombineColor");
node->storage = storage;
break;
}
case CMP_NODE_COMBHSVA_LEGACY: {
node->type_legacy = CMP_NODE_COMBINE_COLOR;
NodeCMPCombSepColor *storage = MEM_callocN<NodeCMPCombSepColor>(__func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_HSV;
STRNCPY_UTF8(node->idname, "CompositorNodeCombineColor");
node->storage = storage;
break;
}
case CMP_NODE_COMBYCCA_LEGACY: {
node->type_legacy = CMP_NODE_COMBINE_COLOR;
NodeCMPCombSepColor *storage = MEM_callocN<NodeCMPCombSepColor>(__func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_YCC;
storage->ycc_mode = node->custom1;
STRNCPY_UTF8(node->idname, "CompositorNodeCombineColor");
node->storage = storage;
break;
}
case CMP_NODE_COMBYUVA_LEGACY: {
node->type_legacy = CMP_NODE_COMBINE_COLOR;
NodeCMPCombSepColor *storage = MEM_callocN<NodeCMPCombSepColor>(__func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_YUV;
STRNCPY_UTF8(node->idname, "CompositorNodeCombineColor");
node->storage = storage;
break;
}
case CMP_NODE_SEPRGBA_LEGACY: {
node->type_legacy = CMP_NODE_SEPARATE_COLOR;
NodeCMPCombSepColor *storage = MEM_callocN<NodeCMPCombSepColor>(__func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_RGB;
STRNCPY_UTF8(node->idname, "CompositorNodeSeparateColor");
node->storage = storage;
break;
}
case CMP_NODE_SEPHSVA_LEGACY: {
node->type_legacy = CMP_NODE_SEPARATE_COLOR;
NodeCMPCombSepColor *storage = MEM_callocN<NodeCMPCombSepColor>(__func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_HSV;
STRNCPY_UTF8(node->idname, "CompositorNodeSeparateColor");
node->storage = storage;
break;
}
case CMP_NODE_SEPYCCA_LEGACY: {
node->type_legacy = CMP_NODE_SEPARATE_COLOR;
NodeCMPCombSepColor *storage = MEM_callocN<NodeCMPCombSepColor>(__func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_YCC;
storage->ycc_mode = node->custom1;
STRNCPY_UTF8(node->idname, "CompositorNodeSeparateColor");
node->storage = storage;
break;
}
case CMP_NODE_SEPYUVA_LEGACY: {
node->type_legacy = CMP_NODE_SEPARATE_COLOR;
NodeCMPCombSepColor *storage = MEM_callocN<NodeCMPCombSepColor>(__func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_YUV;
STRNCPY_UTF8(node->idname, "CompositorNodeSeparateColor");
node->storage = storage;
break;
}
}
}
}
/* In texture nodes, replace combine/separate RGBA with combine/separate color */
if (ntree->type == NTREE_TEXTURE) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
switch (node->type_legacy) {
case TEX_NODE_COMPOSE_LEGACY: {
node->type_legacy = TEX_NODE_COMBINE_COLOR;
node->custom1 = NODE_COMBSEP_COLOR_RGB;
STRNCPY_UTF8(node->idname, "TextureNodeCombineColor");
break;
}
case TEX_NODE_DECOMPOSE_LEGACY: {
node->type_legacy = TEX_NODE_SEPARATE_COLOR;
node->custom1 = NODE_COMBSEP_COLOR_RGB;
STRNCPY_UTF8(node->idname, "TextureNodeSeparateColor");
break;
}
}
}
}
/* In shader nodes, replace combine/separate RGB/HSV with combine/separate color */
if (ntree->type == NTREE_SHADER) {
version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "R", "Red");
version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "G", "Green");
version_node_input_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "B", "Blue");
version_node_output_socket_name(ntree, SH_NODE_COMBRGB_LEGACY, "Image", "Color");
version_node_input_socket_name(ntree, SH_NODE_COMBHSV_LEGACY, "H", "Red");
version_node_input_socket_name(ntree, SH_NODE_COMBHSV_LEGACY, "S", "Green");
version_node_input_socket_name(ntree, SH_NODE_COMBHSV_LEGACY, "V", "Blue");
version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "R", "Red");
version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "G", "Green");
version_node_output_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "B", "Blue");
version_node_input_socket_name(ntree, SH_NODE_SEPRGB_LEGACY, "Image", "Color");
version_node_output_socket_name(ntree, SH_NODE_SEPHSV_LEGACY, "H", "Red");
version_node_output_socket_name(ntree, SH_NODE_SEPHSV_LEGACY, "S", "Green");
version_node_output_socket_name(ntree, SH_NODE_SEPHSV_LEGACY, "V", "Blue");
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
switch (node->type_legacy) {
case SH_NODE_COMBRGB_LEGACY: {
node->type_legacy = SH_NODE_COMBINE_COLOR;
NodeCombSepColor *storage = MEM_callocN<NodeCombSepColor>(__func__);
storage->mode = NODE_COMBSEP_COLOR_RGB;
STRNCPY_UTF8(node->idname, "ShaderNodeCombineColor");
node->storage = storage;
break;
}
case SH_NODE_COMBHSV_LEGACY: {
node->type_legacy = SH_NODE_COMBINE_COLOR;
NodeCombSepColor *storage = MEM_callocN<NodeCombSepColor>(__func__);
storage->mode = NODE_COMBSEP_COLOR_HSV;
STRNCPY_UTF8(node->idname, "ShaderNodeCombineColor");
node->storage = storage;
break;
}
case SH_NODE_SEPRGB_LEGACY: {
node->type_legacy = SH_NODE_SEPARATE_COLOR;
NodeCombSepColor *storage = MEM_callocN<NodeCombSepColor>(__func__);
storage->mode = NODE_COMBSEP_COLOR_RGB;
STRNCPY_UTF8(node->idname, "ShaderNodeSeparateColor");
node->storage = storage;
break;
}
case SH_NODE_SEPHSV_LEGACY: {
node->type_legacy = SH_NODE_SEPARATE_COLOR;
NodeCombSepColor *storage = MEM_callocN<NodeCombSepColor>(__func__);
storage->mode = NODE_COMBSEP_COLOR_HSV;
STRNCPY_UTF8(node->idname, "ShaderNodeSeparateColor");
node->storage = storage;
break;
}
}
}
}
}
/* "Use Nodes" was removed. */
static void do_version_scene_remove_use_nodes(Scene *scene)
{
if (scene->nodetree == nullptr && scene->compositing_node_group == nullptr) {
/* scene->use_nodes is set to false by default. Files saved without compositing node trees
* should not disable compositing. */
return;
}
if (scene->use_nodes == false && scene->r.scemode & R_DOCOMP) {
/* A compositing node tree exists but users explicitly disabled compositing. */
scene->r.scemode &= ~R_DOCOMP;
}
/* Ignore use_nodes otherwise. */
}
/* The Dot output of the Normal node was removed, so replace it with a dot product vector math
* node, noting that the Dot output was actually negative the dot product of the normalized
* node vector with the input. */
static void do_version_normal_node_dot_product(bNodeTree *node_tree, bNode *node)
{
bNodeSocket *normal_input = blender::bke::node_find_socket(*node, SOCK_IN, "Normal");
bNodeSocket *normal_output = blender::bke::node_find_socket(*node, SOCK_OUT, "Normal");
bNodeSocket *dot_output = blender::bke::node_find_socket(*node, SOCK_OUT, "Dot");
/* Find the links going into and out from the node. */
bNodeLink *normal_input_link = nullptr;
bool is_normal_ontput_needed = false;
bool is_dot_output_used = false;
LISTBASE_FOREACH (bNodeLink *, link, &node_tree->links) {
if (link->tosock == normal_input) {
normal_input_link = link;
}
if (link->fromsock == normal_output) {
is_normal_ontput_needed = true;
}
if (link->fromsock == dot_output) {
is_dot_output_used = true;
}
}
/* The dot output is unused, nothing to do. */
if (!is_dot_output_used) {
return;
}
/* Take the dot product with negative the node normal. */
bNode *dot_product_node = blender::bke::node_add_node(
nullptr, *node_tree, "ShaderNodeVectorMath");
dot_product_node->custom1 = NODE_VECTOR_MATH_DOT_PRODUCT;
dot_product_node->flag |= NODE_COLLAPSED;
dot_product_node->parent = node->parent;
dot_product_node->location[0] = node->location[0];
dot_product_node->location[1] = node->location[1];
bNodeSocket *dot_product_a_input = blender::bke::node_find_socket(
*dot_product_node, SOCK_IN, "Vector");
bNodeSocket *dot_product_b_input = blender::bke::node_find_socket(
*dot_product_node, SOCK_IN, "Vector_001");
bNodeSocket *dot_product_output = blender::bke::node_find_socket(
*dot_product_node, SOCK_OUT, "Value");
copy_v3_v3(static_cast<bNodeSocketValueVector *>(dot_product_a_input->default_value)->value,
static_cast<bNodeSocketValueVector *>(normal_input->default_value)->value);
if (normal_input_link) {
version_node_add_link(*node_tree,
*normal_input_link->fromnode,
*normal_input_link->fromsock,
*dot_product_node,
*dot_product_a_input);
blender::bke::node_remove_link(node_tree, *normal_input_link);
}
/* Notice that we normalize and take the negative to reproduce the same behavior as the old
* Normal node. */
const blender::float3 node_normal =
normal_output->default_value_typed<bNodeSocketValueVector>()->value;
const blender::float3 normalized_node_normal = -blender::math::normalize(node_normal);
copy_v3_v3(static_cast<bNodeSocketValueVector *>(dot_product_b_input->default_value)->value,
normalized_node_normal);
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &node_tree->links) {
if (link->fromsock != dot_output) {
continue;
}
version_node_add_link(
*node_tree, *dot_product_node, *dot_product_output, *link->tonode, *link->tosock);
blender::bke::node_remove_link(node_tree, *link);
}
/* If only the Dot output was used, remove the node, making sure to initialize the node types to
* allow removal. */
if (!is_normal_ontput_needed) {
blender::bke::node_tree_set_type(*node_tree);
version_node_remove(*node_tree, *node);
}
}
static void do_version_transform_geometry_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Mode")) {
return;
}
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Mode");
socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
}
static void do_version_points_to_volume_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Resolution Mode")) {
return;
}
const NodeGeometryPointsToVolume &storage = *static_cast<NodeGeometryPointsToVolume *>(
node.storage);
bNodeSocket &socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Resolution Mode");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.resolution_mode;
}
static void do_version_triangulate_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (!blender::bke::node_find_socket(node, SOCK_IN, "Quad Method")) {
bNodeSocket &socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Quad Method");
socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
}
if (!blender::bke::node_find_socket(node, SOCK_IN, "N-gon Method")) {
bNodeSocket &socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "N-gon Method");
socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom2;
}
}
static void do_version_volume_to_mesh_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Resolution Mode")) {
return;
}
const NodeGeometryVolumeToMesh &storage = *static_cast<NodeGeometryVolumeToMesh *>(node.storage);
bNodeSocket &socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Resolution Mode");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.resolution_mode;
}
static void do_version_match_string_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Operation")) {
return;
}
bNodeSocket &socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Operation");
socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
}
static void do_version_fill_curve_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Mode")) {
return;
}
const auto &storage = *static_cast<NodeGeometryCurveFill *>(node.storage);
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Mode");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.mode;
}
static void do_version_fillet_curve_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Mode")) {
return;
}
const auto &storage = *static_cast<NodeGeometryCurveFillet *>(node.storage);
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Mode");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.mode;
}
static void do_version_resample_curve_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Mode")) {
return;
}
const auto &storage = *static_cast<NodeGeometryCurveResample *>(node.storage);
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Mode");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.mode;
}
static void do_version_distribute_points_in_volume_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Mode")) {
return;
}
const auto &storage = *static_cast<NodeGeometryDistributePointsInVolume *>(node.storage);
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Mode");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.mode;
}
static void do_version_merge_by_distance_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Mode")) {
return;
}
const auto &storage = *static_cast<NodeGeometryMergeByDistance *>(node.storage);
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Mode");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.mode;
}
static void do_version_mesh_to_volume_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Resolution Mode")) {
return;
}
const auto &storage = *static_cast<NodeGeometryMeshToVolume *>(node.storage);
bNodeSocket &socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Resolution Mode");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.resolution_mode;
}
static void do_version_raycast_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Interpolation")) {
return;
}
const auto &storage = *static_cast<NodeGeometryRaycast *>(node.storage);
bNodeSocket &socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Interpolation");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.mapping;
}
static void do_version_remove_attribute_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Pattern Mode")) {
return;
}
bNodeSocket &socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Pattern Mode");
socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
}
static void do_version_sample_grid_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Interpolation")) {
return;
}
bNodeSocket &socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Interpolation");
socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom2;
}
static void do_version_scale_elements_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Scale Mode")) {
return;
}
bNodeSocket &socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Scale Mode");
socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom2;
}
static void do_version_set_curve_normal_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Mode")) {
return;
}
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Mode");
socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
}
static void do_version_subdivision_surface_options_to_inputs(bNodeTree &ntree, bNode &node)
{
auto &storage = *static_cast<NodeGeometrySubdivisionSurface *>(node.storage);
if (!blender::bke::node_find_socket(node, SOCK_IN, "UV Smooth")) {
bNodeSocket &socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "UV Smooth");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.uv_smooth;
}
if (!blender::bke::node_find_socket(node, SOCK_IN, "Boundary Smooth")) {
bNodeSocket &socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Boundary Smooth");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.boundary_smooth;
}
}
static void do_version_uv_pack_islands_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Method")) {
return;
}
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Method");
socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
}
static void do_version_uv_unwrap_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Method")) {
return;
}
const auto &storage = *static_cast<NodeGeometryUVUnwrap *>(node.storage);
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Method");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.method;
}
static void version_seq_text_from_legacy(Main *bmain)
{
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (scene->ed != nullptr) {
blender::seq::for_each_callback(&scene->ed->seqbase, [&](Strip *strip) -> bool {
if (strip->type == STRIP_TYPE_TEXT && strip->effectdata != nullptr) {
TextVars *data = static_cast<TextVars *>(strip->effectdata);
if (data->text_ptr == nullptr) {
data->text_ptr = BLI_strdup(data->text_legacy);
data->text_len_bytes = strlen(data->text_ptr);
}
}
return true;
});
}
}
}
static void for_each_mode_paint_settings(
Scene &scene, blender::FunctionRef<void(Scene &scene, Paint *paint)> func)
{
func(scene, reinterpret_cast<Paint *>(scene.toolsettings->vpaint));
func(scene, reinterpret_cast<Paint *>(scene.toolsettings->wpaint));
func(scene, reinterpret_cast<Paint *>(scene.toolsettings->sculpt));
func(scene, reinterpret_cast<Paint *>(scene.toolsettings->gp_paint));
func(scene, reinterpret_cast<Paint *>(scene.toolsettings->gp_vertexpaint));
func(scene, reinterpret_cast<Paint *>(scene.toolsettings->gp_sculptpaint));
func(scene, reinterpret_cast<Paint *>(scene.toolsettings->gp_weightpaint));
func(scene, reinterpret_cast<Paint *>(scene.toolsettings->curves_sculpt));
func(scene, reinterpret_cast<Paint *>(&scene.toolsettings->imapaint));
}
static void copy_unified_paint_settings(Scene &scene, Paint *paint)
{
if (paint == nullptr) {
return;
}
const UnifiedPaintSettings &scene_ups = scene.toolsettings->unified_paint_settings;
UnifiedPaintSettings &ups = paint->unified_paint_settings;
ups.size = scene_ups.size;
ups.unprojected_size = scene_ups.unprojected_size;
ups.alpha = scene_ups.alpha;
ups.weight = scene_ups.weight;
copy_v3_v3(ups.color, scene_ups.color);
copy_v3_v3(ups.rgb, scene_ups.rgb);
copy_v3_v3(ups.secondary_color, scene_ups.secondary_color);
copy_v3_v3(ups.secondary_rgb, scene_ups.secondary_rgb);
ups.color_jitter_flag = scene_ups.color_jitter_flag;
copy_v3_v3(ups.hsv_jitter, scene_ups.hsv_jitter);
BLI_assert(ups.curve_rand_hue == nullptr);
BLI_assert(ups.curve_rand_saturation == nullptr);
BLI_assert(ups.curve_rand_value == nullptr);
ups.curve_rand_hue = BKE_curvemapping_copy(scene_ups.curve_rand_hue);
ups.curve_rand_saturation = BKE_curvemapping_copy(scene_ups.curve_rand_saturation);
ups.curve_rand_value = BKE_curvemapping_copy(scene_ups.curve_rand_value);
ups.flag = scene_ups.flag;
}
/* The Use Alpha option is does not exist in the new generic Mix node, it essentially just
* multiplied the factor by the alpha of the second input. */
static void do_version_mix_color_use_alpha(bNodeTree *node_tree, bNode *node)
{
if (!(node->custom2 & SHD_MIXRGB_USE_ALPHA)) {
return;
}
blender::bke::node_tree_set_type(*node_tree);
bNodeSocket *factor_input = blender::bke::node_find_socket(*node, SOCK_IN, "Factor_Float");
bNodeSocket *b_input = blender::bke::node_find_socket(*node, SOCK_IN, "B_Color");
/* Find the links going into the factor and B input of the Mix node. */
bNodeLink *factor_link = nullptr;
bNodeLink *b_link = nullptr;
LISTBASE_FOREACH (bNodeLink *, link, &node_tree->links) {
if (link->tosock == factor_input) {
factor_link = link;
}
else if (link->tosock == b_input) {
b_link = link;
}
}
/* If neither sockets are connected, just multiply the factor by the alpha of the B input. */
if (!factor_link && !b_link) {
static_cast<bNodeSocketValueFloat *>(factor_input->default_value)->value *=
static_cast<bNodeSocketValueRGBA *>(b_input->default_value)->value[3];
return;
}
/* Otherwise, add a multiply node to do the multiplication. */
bNode *multiply_node = blender::bke::node_add_static_node(nullptr, *node_tree, SH_NODE_MATH);
multiply_node->parent = node->parent;
multiply_node->custom1 = NODE_MATH_MULTIPLY;
multiply_node->location[0] = node->location[0] - node->width - 20.0f;
multiply_node->location[1] = node->location[1];
multiply_node->flag |= NODE_COLLAPSED;
bNodeSocket *multiply_input_a = static_cast<bNodeSocket *>(
BLI_findlink(&multiply_node->inputs, 0));
bNodeSocket *multiply_input_b = static_cast<bNodeSocket *>(
BLI_findlink(&multiply_node->inputs, 1));
bNodeSocket *multiply_output = blender::bke::node_find_socket(*multiply_node, SOCK_OUT, "Value");
/* Connect the output of the multiply node to the math node. */
version_node_add_link(*node_tree, *multiply_node, *multiply_output, *node, *factor_input);
if (factor_link) {
/* The factor input is linked, so connect its origin to the first input of the multiply and
* remove the original link. */
version_node_add_link(*node_tree,
*factor_link->fromnode,
*factor_link->fromsock,
*multiply_node,
*multiply_input_a);
blender::bke::node_remove_link(node_tree, *factor_link);
}
else {
/* Otherwise, the factor is unlinked and we just copy the factor value to the first input in
* the multiply. */
static_cast<bNodeSocketValueFloat *>(multiply_input_a->default_value)->value =
static_cast<bNodeSocketValueFloat *>(factor_input->default_value)->value;
}
if (b_link) {
/* The B input is linked, so extract the alpha of its origin and connect it to the second input
* of the multiply and remove the original link. */
bNode *separate_color_node = blender::bke::node_add_static_node(
nullptr, *node_tree, CMP_NODE_SEPARATE_COLOR);
separate_color_node->parent = node->parent;
separate_color_node->location[0] = multiply_node->location[0] - multiply_node->width - 20.0f;
separate_color_node->location[1] = multiply_node->location[1];
separate_color_node->flag |= NODE_COLLAPSED;
bNodeSocket *image_input = blender::bke::node_find_socket(
*separate_color_node, SOCK_IN, "Image");
bNodeSocket *alpha_output = blender::bke::node_find_socket(
*separate_color_node, SOCK_OUT, "Alpha");
version_node_add_link(
*node_tree, *b_link->fromnode, *b_link->fromsock, *separate_color_node, *image_input);
version_node_add_link(
*node_tree, *separate_color_node, *alpha_output, *multiply_node, *multiply_input_b);
}
else {
/* Otherwise, the B input is unlinked and we just copy the alpha value to the second input in
* the multiply. */
static_cast<bNodeSocketValueFloat *>(multiply_input_b->default_value)->value =
static_cast<bNodeSocketValueRGBA *>(b_input->default_value)->value[3];
}
version_socket_update_is_used(node_tree);
}
/* The Map Value node is now deprecated and should be replaced by other nodes. The node essentially
* just computes (value + offset) * size and clamps based on min and max. */
static void do_version_map_value_node(bNodeTree *node_tree, bNode *node)
{
blender::bke::node_tree_set_type(*node_tree);
const TexMapping &texture_mapping = *static_cast<TexMapping *>(node->storage);
const bool use_min = texture_mapping.flag & TEXMAP_CLIP_MIN;
const bool use_max = texture_mapping.flag & TEXMAP_CLIP_MAX;
const float offset = texture_mapping.loc[0];
const float size = texture_mapping.size[0];
const float min = texture_mapping.min[0];
const float max = texture_mapping.max[0];
bNodeSocket *value_input = blender::bke::node_find_socket(*node, SOCK_IN, "Value");
/* Find the link going into the value input Map Value node. */
bNodeLink *value_link = nullptr;
LISTBASE_FOREACH (bNodeLink *, link, &node_tree->links) {
if (link->tosock == value_input) {
value_link = link;
}
}
bNode *frame = blender::bke::node_add_static_node(nullptr, *node_tree, NODE_FRAME);
frame->parent = node->parent;
STRNCPY(frame->label, RPT_("Versioning: Map Value node was removed"));
NodeFrame *frame_data = static_cast<NodeFrame *>(frame->storage);
frame_data->label_size = 10;
/* If the value input is not connected, add a value node with the computed value. */
if (!value_link) {
const float value = static_cast<bNodeSocketValueFloat *>(value_input->default_value)->value;
const float mapped_value = (value + offset) * size;
const float min_clamped_value = use_min ? blender::math::max(mapped_value, min) : mapped_value;
const float clamped_value = use_max ? blender::math::min(min_clamped_value, max) :
min_clamped_value;
bNode &value_node = version_node_add_empty(*node_tree, "ShaderNodeValue");
bNodeSocket &value_output = version_node_add_socket(
*node_tree, value_node, SOCK_OUT, "NodeSocketFloat", "Value");
value_node.parent = frame;
value_node.location[0] = node->location[0];
value_node.location[1] = node->location[1];
static_cast<bNodeSocketValueFloat *>(value_output.default_value)->value = clamped_value;
/* Relink from the Map Value node to the value node. */
LISTBASE_FOREACH_BACKWARD_MUTABLE (bNodeLink *, link, &node_tree->links) {
if (link->fromnode != node) {
continue;
}
version_node_add_link(*node_tree, value_node, value_output, *link->tonode, *link->tosock);
blender::bke::node_remove_link(node_tree, *link);
}
MEM_freeN(&texture_mapping);
node->storage = nullptr;
version_node_remove(*node_tree, *node);
version_socket_update_is_used(node_tree);
return;
}
/* Otherwise, add math nodes to do the computation, starting with an add node to add the offset
* of the range. */
bNode &add_node = version_node_add_empty(*node_tree, "ShaderNodeMath");
bNodeSocket &add_input_a = version_node_add_socket(
*node_tree, add_node, SOCK_IN, "NodeSocketFloat", "Value");
bNodeSocket &add_input_b = version_node_add_socket(
*node_tree, add_node, SOCK_IN, "NodeSocketFloat", "Value_001");
version_node_add_socket(*node_tree, add_node, SOCK_IN, "NodeSocketFloat", "Value_002");
bNodeSocket &add_output = version_node_add_socket(
*node_tree, add_node, SOCK_OUT, "NodeSocketFloat", "Value");
add_node.parent = frame;
add_node.custom1 = NODE_MATH_ADD;
add_node.location[0] = node->location[0];
add_node.location[1] = node->location[1];
add_node.flag |= NODE_COLLAPSED;
/* Connect the origin of the node to the first input of the add node and remove the original
* link. */
version_node_add_link(
*node_tree, *value_link->fromnode, *value_link->fromsock, add_node, add_input_a);
blender::bke::node_remove_link(node_tree, *value_link);
/* Set the offset to the second input of the add node. */
static_cast<bNodeSocketValueFloat *>(add_input_b.default_value)->value = offset;
/* Add a multiply node to multiply by the size. */
bNode &multiply_node = version_node_add_empty(*node_tree, "ShaderNodeMath");
multiply_node.parent = frame;
multiply_node.custom1 = NODE_MATH_MULTIPLY;
multiply_node.location[0] = add_node.location[0];
multiply_node.location[1] = add_node.location[1] - 40.0f;
multiply_node.flag |= NODE_COLLAPSED;
bNodeSocket &multiply_input_a = version_node_add_socket(
*node_tree, multiply_node, SOCK_IN, "NodeSocketFloat", "Value");
bNodeSocket &multiply_input_b = version_node_add_socket(
*node_tree, multiply_node, SOCK_IN, "NodeSocketFloat", "Value_001");
version_node_add_socket(*node_tree, multiply_node, SOCK_IN, "NodeSocketFloat", "Value_002");
bNodeSocket &multiply_output = version_node_add_socket(
*node_tree, multiply_node, SOCK_OUT, "NodeSocketFloat", "Value");
/* Connect the output of the add node to the first input of the multiply node. */
version_node_add_link(*node_tree, add_node, add_output, multiply_node, multiply_input_a);
/* Set the size to the second input of the multiply node. */
static_cast<bNodeSocketValueFloat *>(multiply_input_b.default_value)->value = size;
bNode *final_node = &multiply_node;
bNodeSocket *final_output = &multiply_output;
if (use_min) {
/* Add a maximum node to clamp by the minimum. */
bNode &max_node = version_node_add_empty(*node_tree, "ShaderNodeMath");
max_node.parent = frame;
max_node.custom1 = NODE_MATH_MAXIMUM;
max_node.location[0] = final_node->location[0];
max_node.location[1] = final_node->location[1] - 40.0f;
max_node.flag |= NODE_COLLAPSED;
bNodeSocket &max_input_a = version_node_add_socket(
*node_tree, max_node, SOCK_IN, "NodeSocketFloat", "Value");
bNodeSocket &max_input_b = version_node_add_socket(
*node_tree, max_node, SOCK_IN, "NodeSocketFloat", "Value_001");
version_node_add_socket(*node_tree, max_node, SOCK_IN, "NodeSocketFloat", "Value_002");
bNodeSocket &max_output = version_node_add_socket(
*node_tree, max_node, SOCK_OUT, "NodeSocketFloat", "Value");
/* Connect the output of the final node to the first input of the maximum node. */
version_node_add_link(*node_tree, *final_node, *final_output, max_node, max_input_a);
/* Set the minimum to the second input of the maximum node. */
static_cast<bNodeSocketValueFloat *>(max_input_b.default_value)->value = min;
final_node = &max_node;
final_output = &max_output;
}
if (use_max) {
/* Add a minimum node to clamp by the maximum. */
bNode &min_node = version_node_add_empty(*node_tree, "ShaderNodeMath");
min_node.parent = frame;
min_node.custom1 = NODE_MATH_MINIMUM;
min_node.location[0] = final_node->location[0];
min_node.location[1] = final_node->location[1] - 40.0f;
min_node.flag |= NODE_COLLAPSED;
bNodeSocket &min_input_a = version_node_add_socket(
*node_tree, min_node, SOCK_IN, "NodeSocketFloat", "Value");
bNodeSocket &min_input_b = version_node_add_socket(
*node_tree, min_node, SOCK_IN, "NodeSocketFloat", "Value_001");
version_node_add_socket(*node_tree, min_node, SOCK_IN, "NodeSocketFloat", "Value_002");
bNodeSocket &min_output = version_node_add_socket(
*node_tree, min_node, SOCK_OUT, "NodeSocketFloat", "Value");
/* Connect the output of the final node to the first input of the minimum node. */
version_node_add_link(*node_tree, *final_node, *final_output, min_node, min_input_a);
/* Set the maximum to the second input of the minimum node. */
static_cast<bNodeSocketValueFloat *>(min_input_b.default_value)->value = max;
final_node = &min_node;
final_output = &min_output;
}
/* Relink from the Map Value node to the final node. */
LISTBASE_FOREACH_BACKWARD_MUTABLE (bNodeLink *, link, &node_tree->links) {
if (link->fromnode != node) {
continue;
}
version_node_add_link(*node_tree, *final_node, *final_output, *link->tonode, *link->tosock);
blender::bke::node_remove_link(node_tree, *link);
}
MEM_freeN(&texture_mapping);
node->storage = nullptr;
version_node_remove(*node_tree, *node);
version_socket_update_is_used(node_tree);
}
/* The compositor Value, Color Ramp, Mix Color, Map Range, Map Value, Math, Combine XYZ, Separate
* XYZ, and Vector Curves nodes are now deprecated and should be replaced by their generic Shader
* node counterpart. */
static void do_version_convert_to_generic_nodes(bNodeTree *node_tree)
{
LISTBASE_FOREACH_MUTABLE (bNode *, node, &node_tree->nodes) {
switch (node->type_legacy) {
case CMP_NODE_VALUE_DEPRECATED:
node->type_legacy = SH_NODE_VALUE;
STRNCPY_UTF8(node->idname, "ShaderNodeValue");
break;
case CMP_NODE_MATH_DEPRECATED:
node->type_legacy = SH_NODE_MATH;
STRNCPY_UTF8(node->idname, "ShaderNodeMath");
break;
case CMP_NODE_COMBINE_XYZ_DEPRECATED:
node->type_legacy = SH_NODE_COMBXYZ;
STRNCPY_UTF8(node->idname, "ShaderNodeCombineXYZ");
break;
case CMP_NODE_SEPARATE_XYZ_DEPRECATED:
node->type_legacy = SH_NODE_SEPXYZ;
STRNCPY_UTF8(node->idname, "ShaderNodeSeparateXYZ");
break;
case CMP_NODE_CURVE_VEC_DEPRECATED:
node->type_legacy = SH_NODE_CURVE_VEC;
STRNCPY_UTF8(node->idname, "ShaderNodeVectorCurve");
break;
case CMP_NODE_VALTORGB_DEPRECATED: {
node->type_legacy = SH_NODE_VALTORGB;
STRNCPY_UTF8(node->idname, "ShaderNodeValToRGB");
/* Compositor node uses "Image" as the output name while the shader node uses "Color" as
* the output name. */
bNodeSocket *image_output = blender::bke::node_find_socket(*node, SOCK_OUT, "Image");
STRNCPY_UTF8(image_output->identifier, "Color");
STRNCPY_UTF8(image_output->name, "Color");
break;
}
case CMP_NODE_MAP_RANGE_DEPRECATED: {
node->type_legacy = SH_NODE_MAP_RANGE;
STRNCPY_UTF8(node->idname, "ShaderNodeMapRange");
/* Transfer options from node to NodeMapRange storage. */
NodeMapRange *data = MEM_callocN<NodeMapRange>(__func__);
data->clamp = node->custom1;
data->data_type = CD_PROP_FLOAT;
data->interpolation_type = NODE_MAP_RANGE_LINEAR;
node->storage = data;
/* Compositor node uses "Value" as the output name while the shader node uses "Result" as
* the output name. */
bNodeSocket *value_output = blender::bke::node_find_socket(*node, SOCK_OUT, "Value");
STRNCPY_UTF8(value_output->identifier, "Result");
STRNCPY_UTF8(value_output->name, "Result");
break;
}
case CMP_NODE_MIX_RGB_DEPRECATED: {
node->type_legacy = SH_NODE_MIX;
STRNCPY_UTF8(node->idname, "ShaderNodeMix");
/* Transfer options from node to NodeShaderMix storage. */
NodeShaderMix *data = MEM_callocN<NodeShaderMix>(__func__);
data->data_type = SOCK_RGBA;
data->factor_mode = NODE_MIX_MODE_UNIFORM;
data->clamp_factor = 0;
data->clamp_result = node->custom2 & SHD_MIXRGB_CLAMP ? 1 : 0;
data->blend_type = node->custom1;
node->storage = data;
/* Compositor node uses "Fac", "Image", and ("Image" "Image_001") as socket names and
* identifiers while the shader node uses ("Factor", "Factor_Float"), ("A", "A_Color"),
* ("B", "B_Color"), and ("Result", "Result_Color") as socket names and identifiers. */
bNodeSocket *factor_input = blender::bke::node_find_socket(*node, SOCK_IN, "Fac");
STRNCPY_UTF8(factor_input->identifier, "Factor_Float");
STRNCPY_UTF8(factor_input->name, "Factor");
bNodeSocket *first_input = blender::bke::node_find_socket(*node, SOCK_IN, "Image");
STRNCPY_UTF8(first_input->identifier, "A_Color");
STRNCPY_UTF8(first_input->name, "A");
bNodeSocket *second_input = blender::bke::node_find_socket(*node, SOCK_IN, "Image_001");
STRNCPY_UTF8(second_input->identifier, "B_Color");
STRNCPY_UTF8(second_input->name, "B");
bNodeSocket *image_output = blender::bke::node_find_socket(*node, SOCK_OUT, "Image");
STRNCPY_UTF8(image_output->identifier, "Result_Color");
STRNCPY_UTF8(image_output->name, "Result");
do_version_mix_color_use_alpha(node_tree, node);
break;
}
case CMP_NODE_MAP_VALUE_DEPRECATED: {
do_version_map_value_node(node_tree, node);
break;
}
default:
break;
}
}
}
/* Equivalent to do_version_convert_to_generic_nodes but performed after linking for handing things
* like animation or node construction. */
static void do_version_convert_to_generic_nodes_after_linking(Main *bmain,
bNodeTree *node_tree,
ID *id)
{
LISTBASE_FOREACH_MUTABLE (bNode *, node, &node_tree->nodes) {
char escaped_node_name[sizeof(node->name) * 2 + 1];
BLI_str_escape(escaped_node_name, node->name, sizeof(escaped_node_name));
const std::string rna_path_prefix = fmt::format("nodes[\"{}\"].inputs", escaped_node_name);
switch (node->type_legacy) {
/* Notice that we use the shader type because the node is already converted in versioning
* before linking. */
case SH_NODE_CURVE_VEC: {
/* The node gained a new Factor input as a first socket, so the vector socket moved to be
* the second socket and we need to transfer its animation as well. */
BKE_animdata_fix_paths_rename_all_ex(
bmain, id, rna_path_prefix.c_str(), nullptr, nullptr, 0, 1, false);
break;
}
/* Notice that we use the shader type because the node is already converted in versioning
* before linking. */
case SH_NODE_MIX: {
/* The node gained multiple new sockets after the factor socket, so the second and third
* sockets moved to be the 7th and 8th sockets. */
BKE_animdata_fix_paths_rename_all_ex(
bmain, id, rna_path_prefix.c_str(), nullptr, nullptr, 1, 6, false);
BKE_animdata_fix_paths_rename_all_ex(
bmain, id, rna_path_prefix.c_str(), nullptr, nullptr, 2, 7, false);
break;
}
default:
break;
}
}
}
static void do_version_split_node_rotation(bNodeTree *node_tree, bNode *node)
{
using namespace blender;
bNodeSocket *factor_input = bke::node_find_socket(*node, SOCK_IN, "Factor");
float factor = factor_input->default_value_typed<bNodeSocketValueFloat>()->value;
bNodeSocket *rotation_input = bke::node_find_socket(*node, SOCK_IN, "Rotation");
if (!rotation_input) {
rotation_input = bke::node_add_static_socket(
*node_tree, *node, SOCK_IN, SOCK_FLOAT, PROP_ANGLE, "Rotation", "Rotation");
}
bNodeSocket *position_input = bke::node_find_socket(*node, SOCK_IN, "Position");
if (!position_input) {
position_input = bke::node_add_static_socket(
*node_tree, *node, SOCK_IN, SOCK_VECTOR, PROP_FACTOR, "Position", "Position");
}
constexpr int CMP_NODE_SPLIT_HORIZONTAL = 0;
constexpr int CMP_NODE_SPLIT_VERTICAL = 1;
switch (node->custom2) {
case CMP_NODE_SPLIT_HORIZONTAL: {
rotation_input->default_value_typed<bNodeSocketValueFloat>()->value =
-math::numbers::pi_v<float> / 2.0f;
position_input->default_value_typed<bNodeSocketValueVector>()->value[0] = factor;
/* The y-coordinate doesn't matter in this case, so set the value to 0.5 so that the gizmo
* appears nicely at the center. */
position_input->default_value_typed<bNodeSocketValueVector>()->value[1] = 0.5f;
break;
}
case CMP_NODE_SPLIT_VERTICAL: {
rotation_input->default_value_typed<bNodeSocketValueFloat>()->value = 0.0f;
position_input->default_value_typed<bNodeSocketValueVector>()->value[0] = 0.5f;
position_input->default_value_typed<bNodeSocketValueVector>()->value[1] = factor;
break;
}
}
}
static void do_version_remove_lzo_and_lzma_compression(FileData *fd, Object *object)
{
ListBase pidlist;
BKE_ptcache_ids_from_object(&pidlist, object, nullptr, 0);
LISTBASE_FOREACH (PTCacheID *, pid, &pidlist) {
bool found_incompatible_cache = false;
if (ELEM(pid->cache->compression,
PTCACHE_COMPRESS_LZO_DEPRECATED,
PTCACHE_COMPRESS_LZMA_DEPRECATED))
{
pid->cache->compression = PTCACHE_COMPRESS_ZSTD_FILTERED;
found_incompatible_cache = true;
}
if (pid->type == PTCACHE_TYPE_DYNAMICPAINT) {
/* Dynamicpaint was hardcoded to use LZO. */
found_incompatible_cache = true;
}
if (!found_incompatible_cache) {
continue;
}
std::string cache_type;
switch (pid->type) {
case PTCACHE_TYPE_SOFTBODY:
cache_type = RPT_("Softbody");
break;
case PTCACHE_TYPE_PARTICLES:
cache_type = RPT_("Particle");
break;
case PTCACHE_TYPE_CLOTH:
cache_type = RPT_("Cloth");
break;
case PTCACHE_TYPE_SMOKE_DOMAIN:
cache_type = RPT_("Smoke Domain");
break;
case PTCACHE_TYPE_SMOKE_HIGHRES:
cache_type = RPT_("Smoke");
break;
case PTCACHE_TYPE_DYNAMICPAINT:
cache_type = RPT_("Dynamic Paint");
break;
case PTCACHE_TYPE_RIGIDBODY:
/* Rigidbody caches shouldn't have any disk caches, but keep it here just in case. */
cache_type = RPT_("Rigidbody");
break;
}
BLO_reportf_wrap(
fd->reports,
RPT_WARNING,
RPT_("%s Cache in object %s can not be read because it uses an "
"outdated compression method. You need to delete the caches and re-bake."),
cache_type.c_str(),
pid->owner_id->name + 2);
}
BLI_freelistN(&pidlist);
}
static void do_version_convert_gp_jitter_values(Brush *brush)
{
/* Because this change is backported into the 4.5 branch, we need to avoid performing versioning
* in case the user updated their custom brush assets between using 4.5 and 5.0 to avoid
* overwriting their changes.
*
* See #142104
*/
if ((brush->flag2 & BRUSH_JITTER_COLOR) != 0 || !is_zero_v3(brush->hsv_jitter)) {
return;
}
BrushGpencilSettings *settings = brush->gpencil_settings;
float old_hsv_jitter[3] = {
settings->random_hue, settings->random_saturation, settings->random_value};
if (!is_zero_v3(old_hsv_jitter)) {
brush->flag2 |= BRUSH_JITTER_COLOR;
}
copy_v3_v3(brush->hsv_jitter, old_hsv_jitter);
if (brush->curve_rand_hue) {
BKE_curvemapping_free_data(brush->curve_rand_hue);
BKE_curvemapping_copy_data(brush->curve_rand_hue, settings->curve_rand_hue);
}
else {
brush->curve_rand_hue = BKE_curvemapping_copy(settings->curve_rand_hue);
}
if (brush->curve_rand_saturation) {
BKE_curvemapping_free_data(brush->curve_rand_saturation);
BKE_curvemapping_copy_data(brush->curve_rand_saturation, settings->curve_rand_saturation);
}
else {
brush->curve_rand_saturation = BKE_curvemapping_copy(settings->curve_rand_saturation);
}
if (brush->curve_rand_value) {
BKE_curvemapping_free_data(brush->curve_rand_value);
BKE_curvemapping_copy_data(brush->curve_rand_value, settings->curve_rand_value);
}
else {
brush->curve_rand_value = BKE_curvemapping_copy(settings->curve_rand_value);
}
}
/* The Sun beams node was removed and the Glare node should be used instead, so we need to
* make the replacement. */
static void do_version_sun_beams(bNodeTree &node_tree, bNode &node)
{
blender::bke::node_tree_set_type(node_tree);
bNodeSocket *old_image_input = blender::bke::node_find_socket(node, SOCK_IN, "Image");
bNodeSocket *old_source_input = blender::bke::node_find_socket(node, SOCK_IN, "Source");
bNodeSocket *old_length_input = blender::bke::node_find_socket(node, SOCK_IN, "Length");
bNodeSocket *old_image_output = blender::bke::node_find_socket(node, SOCK_OUT, "Image");
bNode *glare_node = blender::bke::node_add_node(nullptr, node_tree, "CompositorNodeGlare");
glare_node->parent = node.parent;
glare_node->location[0] = node.location[0];
glare_node->location[1] = node.location[1];
bNodeSocket *image_input = blender::bke::node_find_socket(*glare_node, SOCK_IN, "Image");
bNodeSocket *type_input = blender::bke::node_find_socket(*glare_node, SOCK_IN, "Type");
bNodeSocket *quality_input = blender::bke::node_find_socket(*glare_node, SOCK_IN, "Quality");
bNodeSocket *threshold_input = blender::bke::node_find_socket(
*glare_node, SOCK_IN, "Highlights Threshold");
bNodeSocket *size_input = blender::bke::node_find_socket(*glare_node, SOCK_IN, "Size");
bNodeSocket *source_input = blender::bke::node_find_socket(*glare_node, SOCK_IN, "Sun Position");
bNodeSocket *glare_output = blender::bke::node_find_socket(*glare_node, SOCK_OUT, "Glare");
type_input->default_value_typed<bNodeSocketValueMenu>()->value = CMP_NODE_GLARE_SUN_BEAMS;
quality_input->default_value_typed<bNodeSocketValueMenu>()->value = CMP_NODE_GLARE_QUALITY_HIGH;
copy_v4_v4(image_input->default_value_typed<bNodeSocketValueRGBA>()->value,
old_image_input->default_value_typed<bNodeSocketValueRGBA>()->value);
threshold_input->default_value_typed<bNodeSocketValueFloat>()->value = 0.0f;
size_input->default_value_typed<bNodeSocketValueFloat>()->value =
old_length_input->default_value_typed<bNodeSocketValueFloat>()->value;
copy_v2_v2(source_input->default_value_typed<bNodeSocketValueVector>()->value,
old_source_input->default_value_typed<bNodeSocketValueVector>()->value);
LISTBASE_FOREACH_BACKWARD_MUTABLE (bNodeLink *, link, &node_tree.links) {
if (link->tosock == old_image_input) {
version_node_add_link(
node_tree, *link->fromnode, *link->fromsock, *glare_node, *image_input);
blender::bke::node_remove_link(&node_tree, *link);
continue;
}
if (link->tosock == old_source_input) {
version_node_add_link(
node_tree, *link->fromnode, *link->fromsock, *glare_node, *source_input);
blender::bke::node_remove_link(&node_tree, *link);
continue;
}
if (link->tosock == old_length_input) {
version_node_add_link(node_tree, *link->fromnode, *link->fromsock, *glare_node, *size_input);
blender::bke::node_remove_link(&node_tree, *link);
continue;
}
if (link->fromsock == old_image_output) {
version_node_add_link(node_tree, *link->tonode, *link->tosock, *glare_node, *glare_output);
blender::bke::node_remove_link(&node_tree, *link);
}
}
version_node_remove(node_tree, node);
}
/* The Composite node was removed and a Group Output node should be used instead, so we need to
* make the replacement. But first note that the Group Output node relies on the node tree
* interface, so we ensure a default interface with a single input and output. This is only for
* root trees used as scene compositing node groups, for other node trees, we remove all composite
* nodes since they are no longer supported inside groups. */
static void do_version_composite_node_in_scene_tree(bNodeTree &node_tree, bNode &node)
{
blender::bke::node_tree_set_type(node_tree);
/* Remove inactive nodes. */
if (!(node.flag & NODE_DO_OUTPUT)) {
version_node_remove(node_tree, node);
return;
}
bNodeSocket *old_image_input = blender::bke::node_find_socket(node, SOCK_IN, "Image");
/* Find the link going into the Image input of the Composite node. */
bNodeLink *image_link = nullptr;
LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
if (link->tosock == old_image_input) {
image_link = link;
}
}
bNode *group_output_node = blender::bke::node_add_node(nullptr, node_tree, "NodeGroupOutput");
group_output_node->parent = node.parent;
group_output_node->location[0] = node.location[0];
group_output_node->location[1] = node.location[1];
bNodeSocket *image_input = static_cast<bNodeSocket *>(group_output_node->inputs.first);
BLI_assert(blender::StringRef(image_input->name) == "Image");
copy_v4_v4(image_input->default_value_typed<bNodeSocketValueRGBA>()->value,
old_image_input->default_value_typed<bNodeSocketValueRGBA>()->value);
if (image_link) {
version_node_add_link(
node_tree, *image_link->fromnode, *image_link->fromsock, *group_output_node, *image_input);
blender::bke::node_remove_link(&node_tree, *image_link);
}
version_node_remove(node_tree, node);
}
/* The file output node started using item accessors, so we need to free socket storage and copy
* them to the new items members. Additionally, the base path was split into a directory and a file
* name, so we need to split it. */
static void do_version_file_output_node(bNode &node)
{
if (node.storage == nullptr) {
return;
}
NodeCompositorFileOutput *data = static_cast<NodeCompositorFileOutput *>(node.storage);
/* The directory previously stored both the directory and the file name. */
char directory[FILE_MAX] = "";
char file_name[FILE_MAX] = "";
BLI_path_split_dir_file(data->directory, directory, FILE_MAX, file_name, FILE_MAX);
STRNCPY(data->directory, directory);
data->file_name = BLI_strdup_null(file_name);
data->items_count = BLI_listbase_count(&node.inputs);
data->items = MEM_calloc_arrayN<NodeCompositorFileOutputItem>(data->items_count, __func__);
int i = 0;
LISTBASE_FOREACH_INDEX (bNodeSocket *, input, &node.inputs, i) {
NodeImageMultiFileSocket *old_item_data = static_cast<NodeImageMultiFileSocket *>(
input->storage);
NodeCompositorFileOutputItem *item_data = &data->items[i];
item_data->identifier = i;
BKE_image_format_copy(&item_data->format, &old_item_data->format);
item_data->save_as_render = old_item_data->save_as_render;
item_data->override_node_format = !bool(old_item_data->use_node_format);
item_data->socket_type = input->type;
if (item_data->socket_type == SOCK_VECTOR) {
item_data->vector_socket_dimensions =
input->default_value_typed<bNodeSocketValueVector>()->dimensions;
}
if (data->format.imtype == R_IMF_IMTYPE_MULTILAYER) {
item_data->name = BLI_strdup(old_item_data->layer);
}
else {
item_data->name = BLI_strdup(old_item_data->path);
}
const std::string identifier = "Item_" + std::to_string(item_data->identifier);
STRNCPY(input->identifier, identifier.c_str());
BKE_image_format_free(&old_item_data->format);
MEM_freeN(old_item_data);
input->storage = nullptr;
}
}
/* Updates the media type of the given format to match its imtype. */
static void update_format_media_type(ImageFormatData *format)
{
if (BKE_imtype_is_image(format->imtype)) {
format->media_type = MEDIA_TYPE_IMAGE;
}
else if (BKE_imtype_is_multi_layer_image(format->imtype)) {
format->media_type = MEDIA_TYPE_MULTI_LAYER_IMAGE;
}
else if (BKE_imtype_is_movie(format->imtype)) {
format->media_type = MEDIA_TYPE_VIDEO;
}
else {
BLI_assert_unreachable();
}
}
static void do_version_world_remove_use_nodes(Main *bmain, World *world)
{
if (world->use_nodes) {
return;
}
/* Users defined a world node tree, but deactivated it by disabling "Use Nodes". So we
* simulate the same effect by creating a new World Output node and setting it to active. */
bNodeTree *ntree = world->nodetree;
if (ntree == nullptr) {
/* In case the world was defined through Python API it might have been missing a node tree. */
ntree = blender::bke::node_tree_add_tree_embedded(
bmain, &world->id, "World Node Tree Versioning", "ShaderNodeTree");
}
bNode *old_output = nullptr;
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (STREQ(node->idname, "ShaderNodeOutputWorld") && (node->flag & NODE_DO_OUTPUT)) {
old_output = node;
old_output->flag &= ~NODE_DO_OUTPUT;
}
}
bNode &new_output = version_node_add_empty(*ntree, "ShaderNodeOutputWorld");
bNodeSocket &output_surface_input = version_node_add_socket(
*ntree, new_output, SOCK_IN, "NodeSocketShader", "Surface");
version_node_add_socket(*ntree, new_output, SOCK_IN, "NodeSocketShader", "Volume");
new_output.flag |= NODE_DO_OUTPUT;
bNode &background = version_node_add_empty(*ntree, "ShaderNodeBackground");
bNodeSocket &background_color_output = version_node_add_socket(
*ntree, background, SOCK_OUT, "NodeSocketShader", "Background");
bNodeSocket &background_color_input = version_node_add_socket(
*ntree, background, SOCK_IN, "NodeSocketColor", "Color");
bNodeSocket &background_strength_input = version_node_add_socket(
*ntree, background, SOCK_IN, "NodeSocketFloat", "Strength");
bNodeSocket &background_weight_input = version_node_add_socket(
*ntree, background, SOCK_IN, "NodeSocketFloat", "Weight");
background_weight_input.flag |= SOCK_UNAVAIL;
version_node_add_link(
*ntree, background, background_color_output, new_output, output_surface_input);
bNodeSocketValueRGBA *rgba = background_color_input.default_value_typed<bNodeSocketValueRGBA>();
rgba->value[0] = world->horr;
rgba->value[1] = world->horg;
rgba->value[2] = world->horb;
rgba->value[3] = 1.0f;
background_strength_input.default_value_typed<bNodeSocketValueFloat>()->value = 1.0f;
if (old_output != nullptr) {
/* Position the newly created node after the old output. Assume the old output node is at
* the far right of the node tree. */
background.location[0] = old_output->location[0] + 1.5f * old_output->width;
background.location[1] = old_output->location[1];
}
new_output.location[0] = background.location[0] + 2.0f * background.width;
new_output.location[1] = background.location[1];
bNode *frame = blender::bke::node_add_static_node(nullptr, *ntree, NODE_FRAME);
STRNCPY(frame->label, RPT_("Versioning: Use Nodes was removed"));
background.parent = frame;
new_output.parent = frame;
}
static void do_version_blur_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Type")) {
return;
}
const auto &storage = *static_cast<NodeBlurData *>(node.storage);
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Type");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.filtertype;
}
static void do_version_filter_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Type")) {
return;
}
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Type");
socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
}
static void do_version_levels_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Channel")) {
return;
}
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Channel");
socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
}
static void do_version_dilate_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Type")) {
return;
}
bNodeSocket &type_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Type");
type_socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
const auto &storage = *static_cast<NodeDilateErode *>(node.storage);
bNodeSocket &falloff_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Falloff");
falloff_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.falloff;
}
static void do_version_tone_map_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Type")) {
return;
}
const auto &storage = *static_cast<NodeTonemap *>(node.storage);
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Type");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.type;
}
static void do_version_lens_distortion_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Type")) {
return;
}
const auto &storage = *static_cast<NodeLensDist *>(node.storage);
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Type");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.distortion_type;
}
static void do_version_kuwahara_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Type")) {
return;
}
const auto &storage = *static_cast<NodeKuwaharaData *>(node.storage);
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Type");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.variation;
}
static void do_version_denoise_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Prefilter")) {
return;
}
const auto &storage = *static_cast<NodeDenoise *>(node.storage);
bNodeSocket &prefilter_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Prefilter");
prefilter_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.prefilter;
bNodeSocket &quality_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Quality");
quality_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.quality;
}
static void do_version_translate_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Interpolation")) {
return;
}
const auto &storage = *static_cast<NodeTranslateData *>(node.storage);
bNodeSocket &interpolation_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Interpolation");
interpolation_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.interpolation;
bNodeSocket &extension_x_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Extension X");
extension_x_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.extension_x;
bNodeSocket &extension_y_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Extension Y");
extension_y_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.extension_y;
}
static void do_version_transform_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Interpolation")) {
return;
}
const auto &storage = *static_cast<NodeTransformData *>(node.storage);
bNodeSocket &interpolation_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Interpolation");
interpolation_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.interpolation;
bNodeSocket &extension_x_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Extension X");
extension_x_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.extension_x;
bNodeSocket &extension_y_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Extension Y");
extension_y_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.extension_y;
}
static void do_version_corner_pin_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Interpolation")) {
return;
}
const auto &storage = *static_cast<NodeCornerPinData *>(node.storage);
bNodeSocket &interpolation_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Interpolation");
interpolation_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.interpolation;
bNodeSocket &extension_x_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Extension X");
extension_x_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.extension_x;
bNodeSocket &extension_y_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Extension Y");
extension_y_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.extension_y;
}
static void do_version_map_uv_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Interpolation")) {
return;
}
const auto &storage = *static_cast<NodeMapUVData *>(node.storage);
bNodeSocket &interpolation_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Interpolation");
interpolation_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.interpolation;
bNodeSocket &extension_x_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Extension X");
extension_x_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.extension_x;
bNodeSocket &extension_y_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Extension Y");
extension_y_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.extension_y;
}
static void do_version_scale_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Interpolation")) {
return;
}
bNodeSocket &type_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Type");
type_socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
bNodeSocket &frame_type_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Frame Type");
frame_type_socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom2;
const auto &storage = *static_cast<NodeScaleData *>(node.storage);
bNodeSocket &interpolation_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Interpolation");
interpolation_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.interpolation;
bNodeSocket &extension_x_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Extension X");
extension_x_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.extension_x;
bNodeSocket &extension_y_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Extension Y");
extension_y_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.extension_y;
}
static void do_version_rotate_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Interpolation")) {
return;
}
const auto &storage = *static_cast<NodeRotateData *>(node.storage);
bNodeSocket &interpolation_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Interpolation");
interpolation_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.interpolation;
bNodeSocket &extension_x_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Extension X");
extension_x_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.extension_x;
bNodeSocket &extension_y_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Extension Y");
extension_y_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.extension_y;
}
static void do_version_displace_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Interpolation")) {
return;
}
const auto &storage = *static_cast<NodeDisplaceData *>(node.storage);
bNodeSocket &interpolation_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Interpolation");
interpolation_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.interpolation;
bNodeSocket &extension_x_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Extension X");
extension_x_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.extension_x;
bNodeSocket &extension_y_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Extension Y");
extension_y_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.extension_y;
}
static void do_version_stabilize_2d_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Interpolation")) {
return;
}
bNodeSocket &interpolation_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Interpolation");
interpolation_socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
}
static void do_version_box_mask_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Operation")) {
return;
}
bNodeSocket &operation_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Operation");
operation_socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
}
static void do_version_ellipse_mask_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Operation")) {
return;
}
bNodeSocket &operation_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Operation");
operation_socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
}
static void do_version_track_position_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Mode")) {
return;
}
bNodeSocket &mode_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Mode");
mode_socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
bNodeSocket &frame_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketInt", "Frame");
frame_socket.default_value_typed<bNodeSocketValueInt>()->value = node.custom2;
}
static void do_version_keying_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Feather Falloff")) {
return;
}
const auto &storage = *static_cast<NodeKeyingData *>(node.storage);
bNodeSocket &feather_falloff_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Feather Falloff");
feather_falloff_socket.default_value_typed<bNodeSocketValueMenu>()->value =
storage.feather_falloff;
}
static void do_version_mask_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Size Source")) {
return;
}
bNodeSocket &size_source_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Size Source");
size_source_socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
}
static void do_version_movie_distortion_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Type")) {
return;
}
bNodeSocket &type_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Type");
type_socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
}
static void do_version_glare_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Type")) {
return;
}
const auto &storage = *static_cast<NodeGlare *>(node.storage);
bNodeSocket &type_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Type");
type_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.type;
bNodeSocket &quality_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Quality");
quality_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.quality;
}
static void initialize_missing_closure_and_bundle_node_storage(bNodeTree &ntree)
{
/* When opening and saving 5.0 files with bundle/closure nodes in 4.5, the storage is lost, since
* Blender 4.5 does not officially support these features yet (they were experimental features
* though). This versioning code just adds back the storage so that it does not crash further
* down the line. */
LISTBASE_FOREACH (bNode *, node, &ntree.nodes) {
if (node->storage) {
continue;
}
switch (node->type_legacy) {
case NODE_CLOSURE_INPUT: {
node->storage = MEM_callocN<NodeClosureInput>(__func__);
break;
}
case NODE_CLOSURE_OUTPUT: {
node->storage = MEM_callocN<NodeClosureOutput>(__func__);
break;
}
case NODE_EVALUATE_CLOSURE: {
node->storage = MEM_callocN<NodeEvaluateClosure>(__func__);
break;
}
case NODE_COMBINE_BUNDLE: {
node->storage = MEM_callocN<NodeCombineBundle>(__func__);
break;
}
case NODE_SEPARATE_BUNDLE: {
node->storage = MEM_callocN<NodeSeparateBundle>(__func__);
break;
}
}
}
}
static void do_version_material_remove_use_nodes(Main *bmain, Material *material)
{
if (material->use_nodes) {
return;
}
/* Users defined a material node tree, but deactivated it by disabling "Use Nodes". So we
* simulate the same effect by creating a new Material Output node and setting it to active. */
bNodeTree *ntree = material->nodetree;
if (ntree == nullptr) {
/* In case the material was created in Python API it might have been missing a node tree. */
ntree = blender::bke::node_tree_add_tree_embedded(
bmain, &material->id, "Material Node Tree Versioning", "ShaderNodeTree");
}
bNode *old_output = nullptr;
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (STREQ(node->idname, "ShaderNodeOutputMaterial") && (node->flag & NODE_DO_OUTPUT)) {
old_output = node;
old_output->flag &= ~NODE_DO_OUTPUT;
}
}
bNode *frame = blender::bke::node_add_static_node(nullptr, *ntree, NODE_FRAME);
STRNCPY(frame->label, RPT_("Versioning: Use Nodes was removed"));
{
/* For EEVEE, we use a principled BSDF shader because we need to recreate the metallic,
* specular and roughness properties of the material for use_nodes = false.*/
bNode &new_output_eevee = version_node_add_empty(*ntree, "ShaderNodeOutputMaterial");
bNodeSocket &output_surface_input = version_node_add_socket(
*ntree, new_output_eevee, SOCK_IN, "NodeSocketShader", "Surface");
version_node_add_socket(*ntree, new_output_eevee, SOCK_IN, "NodeSocketShader", "Volume");
version_node_add_socket(*ntree, new_output_eevee, SOCK_IN, "NodeSocketVector", "Displacement");
version_node_add_socket(*ntree, new_output_eevee, SOCK_IN, "NodeSocketFloat", "Thickness");
version_node_add_socket(*ntree, new_output_eevee, SOCK_IN, "NodeSocketShader", "Volume");
new_output_eevee.flag |= NODE_DO_OUTPUT;
new_output_eevee.custom1 = SHD_OUTPUT_EEVEE;
bNode &shader_eevee = *blender::bke::node_add_static_node(
nullptr, *ntree, SH_NODE_BSDF_PRINCIPLED);
bNodeSocket &shader_bsdf_output = *blender::bke::node_find_socket(
shader_eevee, SOCK_OUT, "BSDF");
bNodeSocket &shader_color_input = *blender::bke::node_find_socket(
shader_eevee, SOCK_IN, "Base Color");
bNodeSocket &specular_input = *blender::bke::node_find_socket(
shader_eevee, SOCK_IN, "Specular IOR Level");
bNodeSocket &metallic_input = *blender::bke::node_find_socket(
shader_eevee, SOCK_IN, "Metallic");
bNodeSocket &roughness_input = *blender::bke::node_find_socket(
shader_eevee, SOCK_IN, "Roughness");
version_node_add_link(
*ntree, shader_eevee, shader_bsdf_output, new_output_eevee, output_surface_input);
bNodeSocketValueRGBA *rgba = shader_color_input.default_value_typed<bNodeSocketValueRGBA>();
rgba->value[0] = material->r;
rgba->value[1] = material->g;
rgba->value[2] = material->b;
rgba->value[3] = material->a;
roughness_input.default_value_typed<bNodeSocketValueFloat>()->value = material->roughness;
metallic_input.default_value_typed<bNodeSocketValueFloat>()->value = material->metallic;
specular_input.default_value_typed<bNodeSocketValueFloat>()->value = material->spec;
if (old_output != nullptr) {
/* Position the newly created node after the old output. Assume the old output node is at
* the far right of the node tree. */
shader_eevee.location[0] = old_output->location[0] + 1.5f * old_output->width;
shader_eevee.location[1] = old_output->location[1];
}
new_output_eevee.location[0] = shader_eevee.location[0] + 2.0f * shader_eevee.width;
new_output_eevee.location[1] = shader_eevee.location[1];
shader_eevee.parent = frame;
new_output_eevee.parent = frame;
}
{
/* For Cycles, a simple diffuse BSDF is sufficient. */
bNode &new_output_cycles = version_node_add_empty(*ntree, "ShaderNodeOutputMaterial");
bNodeSocket &output_surface_input = version_node_add_socket(
*ntree, new_output_cycles, SOCK_IN, "NodeSocketShader", "Surface");
version_node_add_socket(*ntree, new_output_cycles, SOCK_IN, "NodeSocketShader", "Volume");
version_node_add_socket(
*ntree, new_output_cycles, SOCK_IN, "NodeSocketVector", "Displacement");
version_node_add_socket(*ntree, new_output_cycles, SOCK_IN, "NodeSocketFloat", "Thickness");
/* We don't activate the output explicitly to avoid having two active outputs. We assume
* `node_tree.get_output_node('Cycles')` will return this node. */
new_output_cycles.custom1 = SHD_OUTPUT_CYCLES;
bNode &shader_cycles = *blender::bke::node_add_static_node(
nullptr, *ntree, SH_NODE_BSDF_DIFFUSE);
bNodeSocket &shader_bsdf_output = *blender::bke::node_find_socket(
shader_cycles, SOCK_OUT, "BSDF");
bNodeSocket &shader_color_input = *blender::bke::node_find_socket(
shader_cycles, SOCK_IN, "Color");
version_node_add_link(
*ntree, shader_cycles, shader_bsdf_output, new_output_cycles, output_surface_input);
bNodeSocketValueRGBA *rgba = shader_color_input.default_value_typed<bNodeSocketValueRGBA>();
rgba->value[0] = material->r;
rgba->value[1] = material->g;
rgba->value[2] = material->b;
rgba->value[3] = material->a;
if (old_output != nullptr) {
shader_cycles.location[0] = old_output->location[0] + 1.5f * old_output->width;
shader_cycles.location[1] = old_output->location[1] + 2.0f * old_output->height;
}
new_output_cycles.location[0] = shader_cycles.location[0] + 3.0f * shader_cycles.width;
new_output_cycles.location[1] = shader_cycles.location[1];
shader_cycles.parent = frame;
new_output_cycles.parent = frame;
}
}
static void do_version_set_alpha_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Type")) {
return;
}
const auto &storage = *static_cast<NodeSetAlpha *>(node.storage);
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Type");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.mode;
}
static void do_version_channel_matte_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Color Space")) {
return;
}
const auto &storage = *static_cast<NodeChroma *>(node.storage);
bNodeSocket &color_space_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Color Space");
color_space_socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1 - 1;
bNodeSocket &rgb_key_channel_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "RGB Key Channel");
rgb_key_channel_socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom2 - 1;
bNodeSocket &hsv_key_channel_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "HSV Key Channel");
hsv_key_channel_socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom2 - 1;
bNodeSocket &yuv_key_channel_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "YUV Key Channel");
yuv_key_channel_socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom2 - 1;
bNodeSocket &ycc_key_channel_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "YCbCr Key Channel");
ycc_key_channel_socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom2 - 1;
bNodeSocket &limit_method_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Limit Method");
limit_method_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.algorithm;
bNodeSocket &rgb_limit_channel_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "RGB Limit Channel");
rgb_limit_channel_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.channel -
1;
bNodeSocket &hsv_limit_channel_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "HSV Limit Channel");
hsv_limit_channel_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.channel -
1;
bNodeSocket &yuv_limit_channel_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "YUV Limit Channel");
yuv_limit_channel_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.channel -
1;
bNodeSocket &ycc_limit_channel_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "YCbCr Limit Channel");
ycc_limit_channel_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.channel -
1;
}
static void do_version_color_balance_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Type")) {
return;
}
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Type");
socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
}
static void do_version_convert_alpha_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Type")) {
return;
}
bNodeSocket &socket = version_node_add_socket(ntree, node, SOCK_IN, "NodeSocketMenu", "Type");
socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1;
}
static void do_version_distance_matte_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Color Space")) {
return;
}
auto &storage = *static_cast<NodeChroma *>(node.storage);
bNodeSocket &socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Color Space");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.channel - 1;
}
static void do_version_color_spill_menus_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Spill Channel")) {
return;
}
bNodeSocket &spill_channel_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Spill Channel");
spill_channel_socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom1 - 1;
bNodeSocket &limit_method_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Limit Method");
limit_method_socket.default_value_typed<bNodeSocketValueMenu>()->value = node.custom2;
auto &storage = *static_cast<NodeColorspill *>(node.storage);
bNodeSocket &limit_channel_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketMenu", "Limit Channel");
limit_channel_socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.limchan;
}
static void do_version_double_edge_mask_options_to_inputs(bNodeTree &ntree, bNode &node)
{
if (blender::bke::node_find_socket(node, SOCK_IN, "Image Edges")) {
return;
}
bNodeSocket &image_edges_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketBool", "Image Edges");
image_edges_socket.default_value_typed<bNodeSocketValueBoolean>()->value = bool(node.custom2);
bNodeSocket &only_inside_outer_socket = version_node_add_socket(
ntree, node, SOCK_IN, "NodeSocketBool", "Only Inside Outer");
only_inside_outer_socket.default_value_typed<bNodeSocketValueBoolean>()->value = bool(
node.custom1);
}
static void version_dynamic_viewer_node_items(bNodeTree &ntree)
{
LISTBASE_FOREACH (bNode *, node, &ntree.nodes) {
if (node->type_legacy != GEO_NODE_VIEWER) {
continue;
}
NodeGeometryViewer *storage = static_cast<NodeGeometryViewer *>(node->storage);
const int input_sockets_num = BLI_listbase_count(&node->inputs);
if (input_sockets_num == storage->items_num + 1) {
/* Make versioning idempotent. */
continue;
}
storage->items_num = 2;
storage->items = MEM_calloc_arrayN<NodeGeometryViewerItem>(2, __func__);
NodeGeometryViewerItem &geometry_item = storage->items[0];
geometry_item.name = BLI_strdup("Geometry");
geometry_item.socket_type = SOCK_GEOMETRY;
geometry_item.identifier = 0;
NodeGeometryViewerItem &value_item = storage->items[1];
value_item.name = BLI_strdup("Value");
value_item.socket_type = blender::bke::custom_data_type_to_socket_type(
eCustomDataType(storage->data_type_legacy))
.value_or(SOCK_FLOAT);
value_item.identifier = 1;
storage->next_identifier = 2;
}
}
static void do_version_displace_node_remove_xy_scale(bNodeTree &node_tree, bNode &node)
{
blender::bke::node_tree_set_type(node_tree);
bNodeSocket *displacement_input = blender::bke::node_find_socket(node, SOCK_IN, "Displacement");
bNodeSocket *x_scale_input = blender::bke::node_find_socket(node, SOCK_IN, "X Scale");
bNodeSocket *y_scale_input = blender::bke::node_find_socket(node, SOCK_IN, "Y Scale");
/* Find the link going into the inputs of the node. */
bNodeLink *displacement_link = nullptr;
bNodeLink *x_scale_link = nullptr;
bNodeLink *y_scale_link = nullptr;
LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
if (link->tosock == displacement_input) {
displacement_link = link;
}
if (link->tosock == x_scale_input) {
x_scale_link = link;
}
if (link->tosock == y_scale_input) {
y_scale_link = link;
}
}
bNode *multiply_node = blender::bke::node_add_node(nullptr, node_tree, "ShaderNodeVectorMath");
multiply_node->parent = node.parent;
multiply_node->location[0] = node.location[0] - node.width - 20.0f;
multiply_node->location[1] = node.location[1];
multiply_node->custom1 = NODE_VECTOR_MATH_MULTIPLY;
bNodeSocket *multiply_a_input = blender::bke::node_find_socket(
*multiply_node, SOCK_IN, "Vector");
bNodeSocket *multiply_b_input = blender::bke::node_find_socket(
*multiply_node, SOCK_IN, "Vector_001");
bNodeSocket *multiply_output = blender::bke::node_find_socket(
*multiply_node, SOCK_OUT, "Vector");
copy_v2_v2(multiply_a_input->default_value_typed<bNodeSocketValueVector>()->value,
displacement_input->default_value_typed<bNodeSocketValueVector>()->value);
if (displacement_link) {
version_node_add_link(node_tree,
*displacement_link->fromnode,
*displacement_link->fromsock,
*multiply_node,
*multiply_a_input);
blender::bke::node_remove_link(&node_tree, *displacement_link);
}
version_node_add_link(node_tree, *multiply_node, *multiply_output, node, *displacement_input);
bNode *combine_node = blender::bke::node_add_node(nullptr, node_tree, "ShaderNodeCombineXYZ");
combine_node->parent = node.parent;
combine_node->location[0] = multiply_node->location[0] - multiply_node->width - 20.0f;
combine_node->location[1] = multiply_node->location[1];
bNodeSocket *combine_x_input = blender::bke::node_find_socket(*combine_node, SOCK_IN, "X");
bNodeSocket *combine_y_input = blender::bke::node_find_socket(*combine_node, SOCK_IN, "Y");
bNodeSocket *combine_output = blender::bke::node_find_socket(*combine_node, SOCK_OUT, "Vector");
version_node_add_link(
node_tree, *combine_node, *combine_output, *multiply_node, *multiply_b_input);
combine_x_input->default_value_typed<bNodeSocketValueFloat>()->value =
x_scale_input->default_value_typed<bNodeSocketValueFloat>()->value;
if (x_scale_link) {
version_node_add_link(node_tree,
*x_scale_link->fromnode,
*x_scale_link->fromsock,
*combine_node,
*combine_x_input);
blender::bke::node_remove_link(&node_tree, *x_scale_link);
}
combine_y_input->default_value_typed<bNodeSocketValueFloat>()->value =
y_scale_input->default_value_typed<bNodeSocketValueFloat>()->value;
if (y_scale_link) {
version_node_add_link(node_tree,
*y_scale_link->fromnode,
*y_scale_link->fromsock,
*combine_node,
*combine_y_input);
blender::bke::node_remove_link(&node_tree, *y_scale_link);
}
}
/* The Size input is now in pixels, while previously, it was relative to 0.01 of the greater image
* dimension. */
static void do_version_bokeh_blur_pixel_size(bNodeTree &node_tree, bNode &node)
{
blender::bke::node_tree_set_type(node_tree);
bNodeSocket *image_input = blender::bke::node_find_socket(node, SOCK_IN, "Image");
bNodeSocket *size_input = blender::bke::node_find_socket(node, SOCK_IN, "Size");
/* Find the link going into the inputs of the node. */
bNodeLink *image_link = nullptr;
bNodeLink *size_link = nullptr;
LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
if (link->tosock == size_input) {
size_link = link;
}
if (link->tosock == image_input) {
image_link = link;
}
}
bNode &multiply_node = version_node_add_empty(node_tree, "ShaderNodeMath");
multiply_node.parent = node.parent;
multiply_node.location[0] = node.location[0] - node.width - 20.0f;
multiply_node.location[1] = node.location[1];
multiply_node.custom1 = NODE_MATH_MULTIPLY;
bNodeSocket &multiply_a_input = version_node_add_socket(
node_tree, multiply_node, SOCK_IN, "NodeSocketFloat", "Value");
bNodeSocket &multiply_b_input = version_node_add_socket(
node_tree, multiply_node, SOCK_IN, "NodeSocketFloat", "Value_001");
bNodeSocket &multiply_output = version_node_add_socket(
node_tree, multiply_node, SOCK_OUT, "NodeSocketFloat", "Value");
multiply_a_input.default_value_typed<bNodeSocketValueFloat>()->value =
size_input->default_value_typed<bNodeSocketValueFloat>()->value;
if (size_link) {
version_node_add_link(
node_tree, *size_link->fromnode, *size_link->fromsock, multiply_node, multiply_a_input);
blender::bke::node_remove_link(&node_tree, *size_link);
}
version_node_add_link(node_tree, multiply_node, multiply_output, node, *size_input);
bNode *relative_to_pixel_node = blender::bke::node_add_node(
nullptr, node_tree, "CompositorNodeRelativeToPixel");
relative_to_pixel_node->parent = node.parent;
relative_to_pixel_node->location[0] = multiply_node.location[0] - multiply_node.width - 20.0f;
relative_to_pixel_node->location[1] = multiply_node.location[1];
relative_to_pixel_node->custom1 = CMP_NODE_RELATIVE_TO_PIXEL_DATA_TYPE_FLOAT;
relative_to_pixel_node->custom2 = CMP_NODE_RELATIVE_TO_PIXEL_REFERENCE_DIMENSION_GREATER;
bNodeSocket *relative_to_pixel_image_input = blender::bke::node_find_socket(
*relative_to_pixel_node, SOCK_IN, "Image");
bNodeSocket *relative_to_pixel_value_input = blender::bke::node_find_socket(
*relative_to_pixel_node, SOCK_IN, "Float Value");
bNodeSocket *relative_to_pixel_value_output = blender::bke::node_find_socket(
*relative_to_pixel_node, SOCK_OUT, "Float Value");
version_node_add_link(node_tree,
*relative_to_pixel_node,
*relative_to_pixel_value_output,
multiply_node,
multiply_b_input);
relative_to_pixel_value_input->default_value_typed<bNodeSocketValueFloat>()->value = 0.01f;
if (image_link) {
version_node_add_link(node_tree,
*image_link->fromnode,
*image_link->fromsock,
*relative_to_pixel_node,
*relative_to_pixel_image_input);
}
}
static bool window_has_sequence_editor_open(const wmWindow *win)
{
bScreen *screen = WM_window_get_active_screen(win);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_SEQ) {
return true;
}
}
}
return false;
}
/* Merge transform effect properties with strip transform. Because this effect could use modifiers,
* change its type to gaussian blur with 0 radius. */
static void sequencer_substitute_transform_effects(Scene *scene)
{
blender::seq::for_each_callback(&scene->ed->seqbase, [&](Strip *strip) -> bool {
if (strip->type == STRIP_TYPE_TRANSFORM_LEGACY && strip->effectdata != nullptr) {
TransformVarsLegacy *tv = static_cast<TransformVarsLegacy *>(strip->effectdata);
StripTransform *transform = strip->data->transform;
blender::float2 offset(tv->xIni, tv->yIni);
if (tv->percent == 1) {
blender::float2 scene_resolution(scene->r.xsch, scene->r.ysch);
offset *= scene_resolution;
}
transform->xofs += offset.x;
transform->yofs += offset.y;
transform->scale_x *= tv->ScalexIni;
transform->scale_y *= tv->ScaleyIni;
transform->rotation += tv->rotIni;
blender::seq::EffectHandle sh = blender::seq::strip_effect_handle_get(strip);
sh.free(strip, true);
strip->type = STRIP_TYPE_GAUSSIAN_BLUR;
sh = blender::seq::strip_effect_handle_get(strip);
sh.init(strip);
GaussianBlurVars *gv = static_cast<GaussianBlurVars *>(strip->effectdata);
gv->size_x = gv->size_y = 0.0f;
blender::seq::edit_strip_name_set(scene, strip, "Transform Placeholder (Migrated)");
blender::seq::ensure_unique_name(strip, scene);
}
return true;
});
}
void do_versions_after_linking_500(FileData *fd, Main *bmain)
{
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 9)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (STREQ(scene->r.engine, RE_engine_id_BLENDER_EEVEE_NEXT)) {
STRNCPY_UTF8(scene->r.engine, RE_engine_id_BLENDER_EEVEE);
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 27)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_COMPOSIT) {
do_version_convert_to_generic_nodes_after_linking(bmain, ntree, id);
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 37)) {
LISTBASE_FOREACH (Object *, object, &bmain->objects) {
do_version_remove_lzo_and_lzma_compression(fd, object);
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 41)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
bNodeTree *node_tree = version_get_scene_compositor_node_tree(bmain, scene);
if (node_tree) {
/* Add a default interface for the node tree. See the versioning function below for more
* details. */
node_tree->tree_interface.clear_items();
node_tree->tree_interface.add_socket(
DATA_("Image"), "", "NodeSocketColor", NODE_INTERFACE_SOCKET_INPUT, nullptr);
node_tree->tree_interface.add_socket(
DATA_("Image"), "", "NodeSocketColor", NODE_INTERFACE_SOCKET_OUTPUT, nullptr);
LISTBASE_FOREACH_BACKWARD_MUTABLE (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == CMP_NODE_COMPOSITE_DEPRECATED) {
do_version_composite_node_in_scene_tree(*node_tree, *node);
}
}
}
}
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
blender::bke::node_tree_set_type(*node_tree);
if (node_tree->type == NTREE_COMPOSIT) {
LISTBASE_FOREACH_BACKWARD_MUTABLE (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == CMP_NODE_COMPOSITE_DEPRECATED) {
/* See do_version_composite_node_in_scene_tree. */
version_node_remove(*node_tree, *node);
}
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 54)) {
LISTBASE_FOREACH (Object *, object, &bmain->objects) {
if (object->type != OB_ARMATURE || !object->data) {
continue;
}
BKE_pose_rebuild(nullptr, object, static_cast<bArmature *>(object->data), false);
LISTBASE_FOREACH (bPoseChannel *, pose_bone, &object->pose->chanbase) {
if (pose_bone->bone->flag & BONE_HIDDEN_P) {
pose_bone->drawflag |= PCHAN_DRAW_HIDDEN;
}
else {
pose_bone->drawflag &= ~PCHAN_DRAW_HIDDEN;
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 57)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
LISTBASE_FOREACH_BACKWARD_MUTABLE (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == CMP_NODE_SUNBEAMS_DEPRECATED) {
do_version_sun_beams(*node_tree, *node);
}
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 63)) {
LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
if (window_has_sequence_editor_open(win)) {
Scene *scene = WM_window_get_active_scene(win);
if (scene->ed != nullptr) {
WorkSpace *workspace = WM_window_get_active_workspace(win);
workspace->sequencer_scene = scene;
}
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 97)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (scene->ed != nullptr) {
sequencer_substitute_transform_effects(scene);
}
}
}
/**
* 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.
*/
}
static void remove_in_and_out_node_panel_recursive(bNodeTreeInterfacePanel &panel)
{
using namespace blender;
const Span old_sockets(panel.items_array, panel.items_num);
Vector<bNodeTreeInterfaceItem *> new_sockets;
for (bNodeTreeInterfaceItem *item : old_sockets) {
if (item->item_type == NODE_INTERFACE_PANEL) {
remove_in_and_out_node_panel_recursive(*reinterpret_cast<bNodeTreeInterfacePanel *>(item));
continue;
}
bNodeTreeInterfaceSocket *socket = reinterpret_cast<bNodeTreeInterfaceSocket *>(item);
constexpr int in_and_out = NODE_INTERFACE_SOCKET_INPUT | NODE_INTERFACE_SOCKET_OUTPUT;
if ((socket->flag & in_and_out) != in_and_out) {
continue;
}
bNodeTreeInterfaceSocket *new_output = MEM_callocN<bNodeTreeInterfaceSocket>(__func__);
new_output->item.item_type = NODE_INTERFACE_SOCKET;
new_output->name = BLI_strdup_null(socket->name);
new_output->description = BLI_strdup_null(socket->description);
new_output->socket_type = BLI_strdup_null(socket->socket_type);
new_output->flag = socket->flag & ~NODE_INTERFACE_SOCKET_INPUT;
new_output->attribute_domain = socket->attribute_domain;
new_output->default_input = socket->default_input;
new_output->default_attribute_name = BLI_strdup_null(socket->default_attribute_name);
new_output->identifier = BLI_strdup(socket->identifier);
if (socket->properties) {
new_output->properties = IDP_CopyProperty_ex(socket->properties,
LIB_ID_CREATE_NO_USER_REFCOUNT);
}
new_output->structure_type = socket->structure_type;
new_sockets.append(reinterpret_cast<bNodeTreeInterfaceItem *>(new_output));
socket->flag &= ~NODE_INTERFACE_SOCKET_OUTPUT;
}
if (new_sockets.is_empty()) {
return;
}
new_sockets.extend(old_sockets);
VectorData new_socket_data = new_sockets.release();
MEM_freeN(panel.items_array);
panel.items_array = new_socket_data.data;
panel.items_num = new_socket_data.size;
}
/**
* Fix node interface sockest that could become both inputs and outputs before the current design
* was settled on.
*/
static void remove_in_and_out_node_interface(bNodeTree &node_tree)
{
remove_in_and_out_node_panel_recursive(node_tree.tree_interface.root_panel);
}
static void repair_node_link_node_pointers(FileData &fd, bNodeTree &node_tree)
{
using namespace blender;
Map<bNodeSocket *, bNode *> socket_to_node;
LISTBASE_FOREACH (bNode *, node, &node_tree.nodes) {
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
socket_to_node.add(socket, node);
}
LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
socket_to_node.add(socket, node);
}
}
LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
bool fixed = false;
bNode *to_node = socket_to_node.lookup(link->tosock);
if (to_node != link->tonode) {
link->tonode = to_node;
fixed = true;
}
bNode *from_node = socket_to_node.lookup(link->fromsock);
if (from_node != link->fromnode) {
link->fromnode = from_node;
fixed = true;
}
if (fixed) {
BLO_reportf_wrap(fd.reports,
RPT_WARNING,
"Repairing invalid state in node link from %s:%s to %s:%s",
link->fromnode->name,
link->fromsock->identifier,
link->tonode->name,
link->tosock->identifier);
}
}
}
static void sequencer_remove_listbase_pointers(Scene &scene)
{
Editing *ed = scene.ed;
if (!ed) {
return;
}
const MetaStack *last_meta_stack = blender::seq::meta_stack_active_get(ed);
if (!last_meta_stack) {
return;
}
ed->current_meta_strip = last_meta_stack->parent_strip;
blender::seq::meta_stack_set(&scene, last_meta_stack->parent_strip);
}
static void do_version_adaptive_subdivision(Main *bmain)
{
/* Move cycles properties natively into subdivision surface modifier. */
bool experimental_features = false;
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
IDProperty *idprop = version_cycles_properties_from_ID(&scene->id);
if (idprop) {
experimental_features |= version_cycles_property_boolean(idprop, "feature_set", false);
}
}
LISTBASE_FOREACH (Object *, object, &bmain->objects) {
bool use_adaptive_subdivision = false;
float dicing_rate = 1.0f;
IDProperty *idprop = version_cycles_properties_from_ID(&object->id);
if (idprop) {
if (experimental_features) {
use_adaptive_subdivision = version_cycles_property_boolean(
idprop, "use_adaptive_subdivision", false);
}
dicing_rate = version_cycles_property_float(idprop, "dicing_rate", 1.0f);
}
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->type == eModifierType_Subsurf) {
SubsurfModifierData *smd = (SubsurfModifierData *)md;
if (use_adaptive_subdivision) {
smd->flags |= eSubsurfModifierFlag_UseAdaptiveSubdivision;
smd->adaptive_space = SUBSURF_ADAPTIVE_SPACE_PIXEL;
smd->adaptive_pixel_size = dicing_rate;
smd->adaptive_object_edge_length = 0.01f;
}
}
}
}
}
void blo_do_versions_500(FileData *fd, Library * /*lib*/, Main *bmain)
{
using namespace blender;
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 1)) {
LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) {
bke::mesh_sculpt_mask_to_generic(*mesh);
bke::mesh_custom_normals_to_generic(*mesh);
rename_mesh_uv_seam_attribute(*mesh);
}
update_brush_sizes(*bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 2)) {
LISTBASE_FOREACH (PointCloud *, pointcloud, &bmain->pointclouds) {
blender::bke::pointcloud_convert_customdata_to_storage(*pointcloud);
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 3)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_GEOMETRY) {
initialize_closure_input_structure_types(*ntree);
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 7)) {
const int uv_select_island = 1 << 3;
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *ts = scene->toolsettings;
if (ts->uv_selectmode & uv_select_island) {
ts->uv_selectmode = UV_SELECT_VERT;
ts->uv_flag |= UV_FLAG_SELECT_ISLAND;
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 8)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type != NTREE_COMPOSIT) {
continue;
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type_legacy != CMP_NODE_DISPLACE) {
continue;
}
if (node->storage != nullptr) {
continue;
}
NodeDisplaceData *data = MEM_callocN<NodeDisplaceData>(__func__);
data->interpolation = CMP_NODE_INTERPOLATION_ANISOTROPIC;
node->storage = data;
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 10)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
view_layer->eevee.ambient_occlusion_distance = scene->eevee.gtao_distance;
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 13)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_COMPOSIT) {
version_node_socket_name(ntree, CMP_NODE_VIEW_LEVELS, "Std Dev", "Standard Deviation");
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 14)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
versioning_replace_legacy_combined_and_separate_color_nodes(ntree);
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 15)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_COMPOSIT) {
version_node_socket_name(ntree, CMP_NODE_ROTATE, "Degr", "Angle");
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 17)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
do_version_scene_remove_use_nodes(scene);
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 20)) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (ELEM(sl->spacetype, SPACE_ACTION, SPACE_GRAPH, SPACE_NLA, SPACE_SEQ)) {
ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
&sl->regionbase;
ARegion *new_footer = do_versions_add_region_if_not_found(
regionbase, RGN_TYPE_FOOTER, "footer for animation editors", RGN_TYPE_HEADER);
if (new_footer != nullptr) {
new_footer->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_TOP :
RGN_ALIGN_BOTTOM;
new_footer->flag |= RGN_FLAG_HIDDEN;
}
}
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 21)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
LISTBASE_FOREACH_MUTABLE (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == CMP_NODE_NORMAL) {
do_version_normal_node_dot_product(node_tree, node);
}
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 25)) {
version_seq_text_from_legacy(bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 26)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
for_each_mode_paint_settings(*scene, copy_unified_paint_settings);
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 27)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_COMPOSIT) {
do_version_convert_to_generic_nodes(ntree);
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 28)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == CMP_NODE_SPLIT) {
do_version_split_node_rotation(node_tree, node);
}
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 30)) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype != SPACE_FILE) {
continue;
}
SpaceFile *sfile = reinterpret_cast<SpaceFile *>(sl);
if (sfile->browse_mode != FILE_BROWSE_MODE_ASSETS) {
continue;
}
sfile->asset_params->base_params.filter_id |= FILTER_ID_SCE;
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 32)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type != NTREE_COMPOSIT) {
continue;
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type_legacy != CMP_NODE_TRANSLATE) {
continue;
}
if (node->storage == nullptr) {
continue;
}
NodeTranslateData *data = static_cast<NodeTranslateData *>(node->storage);
/* Map old wrap axis to new extension mode. */
switch (data->wrap_axis) {
case CMP_NODE_TRANSLATE_REPEAT_AXIS_NONE:
data->extension_x = CMP_NODE_EXTENSION_MODE_CLIP;
data->extension_y = CMP_NODE_EXTENSION_MODE_CLIP;
break;
case CMP_NODE_TRANSLATE_REPEAT_AXIS_X:
data->extension_x = CMP_NODE_EXTENSION_MODE_REPEAT;
data->extension_y = CMP_NODE_EXTENSION_MODE_CLIP;
break;
case CMP_NODE_TRANSLATE_REPEAT_AXIS_Y:
data->extension_x = CMP_NODE_EXTENSION_MODE_CLIP;
data->extension_y = CMP_NODE_EXTENSION_MODE_REPEAT;
break;
case CMP_NODE_TRANSLATE_REPEAT_AXIS_XY:
data->extension_x = CMP_NODE_EXTENSION_MODE_REPEAT;
data->extension_y = CMP_NODE_EXTENSION_MODE_REPEAT;
break;
}
}
FOREACH_NODETREE_END;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 32)) {
LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) {
mesh->radial_symmetry[0] = 1;
mesh->radial_symmetry[1] = 1;
mesh->radial_symmetry[2] = 1;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 33)) {
LISTBASE_FOREACH (Curves *, curves, &bmain->hair_curves) {
blender::bke::curves_convert_customdata_to_storage(curves->geometry.wrap());
}
LISTBASE_FOREACH (GreasePencil *, grease_pencil, &bmain->grease_pencils) {
blender::bke::grease_pencil_convert_customdata_to_storage(*grease_pencil);
for (const int i : IndexRange(grease_pencil->drawing_array_num)) {
GreasePencilDrawingBase *drawing_base = grease_pencil->drawing_array[i];
if (drawing_base->type == GP_DRAWING) {
GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(drawing_base);
blender::bke::curves_convert_customdata_to_storage(drawing->geometry.wrap());
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 34)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type != NTREE_COMPOSIT) {
continue;
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type_legacy != CMP_NODE_SCALE) {
continue;
}
if (node->storage == nullptr) {
continue;
}
NodeScaleData *data = static_cast<NodeScaleData *>(node->storage);
data->extension_x = CMP_NODE_EXTENSION_MODE_CLIP;
data->extension_y = CMP_NODE_EXTENSION_MODE_CLIP;
}
FOREACH_NODETREE_END;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 35)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type != NTREE_COMPOSIT) {
continue;
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type_legacy != CMP_NODE_TRANSFORM) {
continue;
}
if (node->storage != nullptr) {
continue;
}
NodeTransformData *data = MEM_callocN<NodeTransformData>(__func__);
data->interpolation = node->custom1;
data->extension_x = CMP_NODE_EXTENSION_MODE_CLIP;
data->extension_y = CMP_NODE_EXTENSION_MODE_CLIP;
node->storage = data;
}
FOREACH_NODETREE_END;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 36)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_COMPOSIT) {
version_node_input_socket_name(ntree, CMP_NODE_ZCOMBINE, "Image", "A");
version_node_input_socket_name(ntree, CMP_NODE_ZCOMBINE, "Image_001", "B");
version_node_input_socket_name(ntree, CMP_NODE_ZCOMBINE, "Z", "Depth A");
version_node_input_socket_name(ntree, CMP_NODE_ZCOMBINE, "Z_001", "Depth B");
version_node_output_socket_name(ntree, CMP_NODE_ZCOMBINE, "Image", "Result");
version_node_output_socket_name(ntree, CMP_NODE_ZCOMBINE, "Z", "Depth");
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 38)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_GEOMETRY) {
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == GEO_NODE_TRANSFORM_GEOMETRY) {
do_version_transform_geometry_options_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == GEO_NODE_POINTS_TO_VOLUME) {
do_version_points_to_volume_options_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == GEO_NODE_TRIANGULATE) {
do_version_triangulate_options_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == GEO_NODE_VOLUME_TO_MESH) {
do_version_volume_to_mesh_options_to_inputs(*node_tree, *node);
}
else if (STREQ(node->idname, "FunctionNodeMatchString")) {
do_version_match_string_options_to_inputs(*node_tree, *node);
}
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 39)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
Editing *ed = seq::editing_get(scene);
if (ed != nullptr) {
seq::for_each_callback(&ed->seqbase, [](Strip *strip) -> bool {
LISTBASE_FOREACH (StripModifierData *, smd, &strip->modifiers) {
seq::modifier_persistent_uid_init(*strip, *smd);
}
return true;
});
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 40)) {
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
if (brush->gpencil_settings) {
do_version_convert_gp_jitter_values(brush);
}
}
}
/* ImageFormatData gained a new media type which we need to be set according to the
* existing imtype. */
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 42)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
update_format_media_type(&scene->r.im_format);
}
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type != NTREE_COMPOSIT) {
continue;
}
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
if (node->type_legacy != CMP_NODE_OUTPUT_FILE) {
continue;
}
NodeCompositorFileOutput *storage = static_cast<NodeCompositorFileOutput *>(node->storage);
update_format_media_type(&storage->format);
LISTBASE_FOREACH (bNodeSocket *, input, &node->inputs) {
NodeImageMultiFileSocket *input_storage = static_cast<NodeImageMultiFileSocket *>(
input->storage);
update_format_media_type(&input_storage->format);
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 43)) {
LISTBASE_FOREACH (World *, world, &bmain->worlds) {
do_version_world_remove_use_nodes(bmain, world);
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 45)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_GEOMETRY) {
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == GEO_NODE_FILL_CURVE) {
do_version_fill_curve_options_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == GEO_NODE_FILLET_CURVE) {
do_version_fillet_curve_options_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == GEO_NODE_RESAMPLE_CURVE) {
do_version_resample_curve_options_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME) {
do_version_distribute_points_in_volume_options_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == GEO_NODE_MERGE_BY_DISTANCE) {
do_version_merge_by_distance_options_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == GEO_NODE_MESH_TO_VOLUME) {
do_version_mesh_to_volume_options_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == GEO_NODE_RAYCAST) {
do_version_raycast_options_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == GEO_NODE_REMOVE_ATTRIBUTE) {
do_version_remove_attribute_options_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == GEO_NODE_SAMPLE_GRID) {
do_version_sample_grid_options_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == GEO_NODE_SCALE_ELEMENTS) {
do_version_scale_elements_options_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == GEO_NODE_SET_CURVE_NORMAL) {
do_version_set_curve_normal_options_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == GEO_NODE_SUBDIVISION_SURFACE) {
do_version_subdivision_surface_options_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == GEO_NODE_UV_PACK_ISLANDS) {
do_version_uv_pack_islands_options_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == GEO_NODE_UV_UNWRAP) {
do_version_uv_unwrap_options_to_inputs(*node_tree, *node);
}
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 46)) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype != SPACE_NODE) {
continue;
}
const SpaceNode *snode = reinterpret_cast<SpaceNode *>(sl);
if (!STREQ(snode->tree_idname, "CompositorNodeTree")) {
continue;
}
ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
&sl->regionbase;
if (ARegion *new_shelf_region = do_versions_add_region_if_not_found(
regionbase,
RGN_TYPE_ASSET_SHELF,
"Asset shelf for compositing (versioning)",
RGN_TYPE_HEADER))
{
new_shelf_region->alignment = RGN_ALIGN_BOTTOM;
}
if (ARegion *new_shelf_header = do_versions_add_region_if_not_found(
regionbase,
RGN_TYPE_ASSET_SHELF_HEADER,
"Asset shelf header for compositing (versioning)",
RGN_TYPE_ASSET_SHELF))
{
new_shelf_header->alignment = RGN_ALIGN_BOTTOM | RGN_ALIGN_HIDE_WITH_PREV;
}
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 48)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type != NTREE_COMPOSIT) {
continue;
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type_legacy != CMP_NODE_ROTATE) {
continue;
}
if (node->storage != nullptr) {
continue;
}
NodeRotateData *data = MEM_callocN<NodeRotateData>(__func__);
data->interpolation = node->custom1;
data->extension_x = CMP_NODE_EXTENSION_MODE_CLIP;
data->extension_y = CMP_NODE_EXTENSION_MODE_CLIP;
node->storage = data;
}
FOREACH_NODETREE_END;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 49)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type != NTREE_COMPOSIT) {
continue;
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type_legacy != CMP_NODE_DISPLACE) {
continue;
}
if (node->storage == nullptr) {
continue;
}
NodeDisplaceData *data = static_cast<NodeDisplaceData *>(node->storage);
data->extension_x = CMP_NODE_EXTENSION_MODE_CLIP;
data->extension_y = CMP_NODE_EXTENSION_MODE_CLIP;
}
FOREACH_NODETREE_END;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 50)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type != NTREE_COMPOSIT) {
continue;
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type_legacy != CMP_NODE_MAP_UV) {
continue;
}
if (node->storage != nullptr) {
continue;
}
NodeMapUVData *data = MEM_callocN<NodeMapUVData>(__func__);
data->interpolation = node->custom2;
data->extension_x = CMP_NODE_EXTENSION_MODE_CLIP;
data->extension_y = CMP_NODE_EXTENSION_MODE_CLIP;
node->storage = data;
}
FOREACH_NODETREE_END;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 51)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type != NTREE_COMPOSIT) {
continue;
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type_legacy != CMP_NODE_CORNERPIN) {
continue;
}
if (node->storage != nullptr) {
continue;
}
NodeCornerPinData *data = MEM_callocN<NodeCornerPinData>(__func__);
data->interpolation = node->custom1;
data->extension_x = CMP_NODE_EXTENSION_MODE_CLIP;
data->extension_y = CMP_NODE_EXTENSION_MODE_CLIP;
node->storage = data;
}
FOREACH_NODETREE_END;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 54)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type != NTREE_COMPOSIT) {
continue;
}
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == CMP_NODE_OUTPUT_FILE) {
do_version_file_output_node(*node);
}
}
FOREACH_NODETREE_END;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 58)) {
LISTBASE_FOREACH (Object *, object, &bmain->objects) {
LISTBASE_FOREACH (ModifierData *, modifier, &object->modifiers) {
if (modifier->type != eModifierType_GreasePencilLineart) {
continue;
}
GreasePencilLineartModifierData *lmd = reinterpret_cast<GreasePencilLineartModifierData *>(
modifier);
if (lmd->radius != 0.0f) {
continue;
}
lmd->radius = float(lmd->thickness_legacy) *
bke::greasepencil::LEGACY_RADIUS_CONVERSION_FACTOR;
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 61)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type != NTREE_COMPOSIT) {
continue;
}
version_node_input_socket_name(ntree, CMP_NODE_GAMMA_DEPRECATED, "Image", "Color");
version_node_output_socket_name(ntree, CMP_NODE_GAMMA_DEPRECATED, "Image", "Color");
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type_legacy == CMP_NODE_GAMMA_DEPRECATED) {
node->type_legacy = SH_NODE_GAMMA;
STRNCPY_UTF8(node->idname, "ShaderNodeGamma");
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 63)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (scene->r.bake_flag & R_BAKE_MULTIRES) {
scene->r.bake.type = scene->r.bake_mode;
scene->r.bake.flag |= (scene->r.bake_flag & (R_BAKE_MULTIRES | R_BAKE_LORES_MESH));
scene->r.bake.margin_type = scene->r.bake_margin_type;
scene->r.bake.margin = scene->r.bake_margin;
}
else {
scene->r.bake.type = R_BAKE_NORMALS;
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 62)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
scene->r.bake.displacement_space = R_BAKE_SPACE_OBJECT;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 64)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
remove_in_and_out_node_interface(*node_tree);
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 65)) {
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
srgb_to_linearrgb_v3_v3(brush->color, brush->rgb);
srgb_to_linearrgb_v3_v3(brush->secondary_color, brush->secondary_rgb);
}
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
UnifiedPaintSettings &ups = scene->toolsettings->unified_paint_settings;
srgb_to_linearrgb_v3_v3(ups.color, ups.rgb);
srgb_to_linearrgb_v3_v3(ups.secondary_color, ups.secondary_rgb);
for_each_mode_paint_settings(*scene, [](Scene & /*scene*/, Paint *paint) {
if (paint != nullptr) {
UnifiedPaintSettings &ups = paint->unified_paint_settings;
srgb_to_linearrgb_v3_v3(ups.color, ups.rgb);
srgb_to_linearrgb_v3_v3(ups.secondary_color, ups.secondary_rgb);
}
});
}
LISTBASE_FOREACH (Palette *, palette, &bmain->palettes) {
LISTBASE_FOREACH (PaletteColor *, color, &palette->colors) {
srgb_to_linearrgb_v3_v3(color->color, color->rgb);
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 66)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type != NTREE_COMPOSIT) {
continue;
}
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == CMP_NODE_BLUR) {
do_version_blur_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_FILTER) {
do_version_filter_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_VIEW_LEVELS) {
do_version_levels_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_DILATEERODE) {
do_version_dilate_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_TONEMAP) {
do_version_tone_map_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_LENSDIST) {
do_version_lens_distortion_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_KUWAHARA) {
do_version_kuwahara_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_DENOISE) {
do_version_denoise_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_TRANSLATE) {
do_version_translate_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_TRANSFORM) {
do_version_transform_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_CORNERPIN) {
do_version_corner_pin_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_MAP_UV) {
do_version_map_uv_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_SCALE) {
do_version_scale_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_ROTATE) {
do_version_rotate_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_DISPLACE) {
do_version_displace_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_STABILIZE2D) {
do_version_stabilize_2d_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_MASK_BOX) {
do_version_box_mask_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_MASK_ELLIPSE) {
do_version_ellipse_mask_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_TRACKPOS) {
do_version_track_position_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_KEYING) {
do_version_keying_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_MASK) {
do_version_mask_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_MOVIEDISTORTION) {
do_version_movie_distortion_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_GLARE) {
do_version_glare_menus_to_inputs(*node_tree, *node);
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 67)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
initialize_missing_closure_and_bundle_node_storage(*ntree);
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 68)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
sequencer_remove_listbase_pointers(*scene);
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 69)) {
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
repair_node_link_node_pointers(*fd, *ntree);
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 71)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
scene->toolsettings->uvsculpt.size *= 2;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 72)) {
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
if (brush->curve_size == nullptr) {
brush->curve_size = BKE_paint_default_curve();
}
if (brush->curve_strength == nullptr) {
brush->curve_strength = BKE_paint_default_curve();
}
if (brush->curve_jitter == nullptr) {
brush->curve_jitter = BKE_paint_default_curve();
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 73)) {
/* Old files created on WIN32 use `\r`. */
LISTBASE_FOREACH (Curve *, cu, &bmain->curves) {
if (cu->str) {
BLI_string_replace_char(cu->str, '\r', '\n');
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 74)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (scene->ed != nullptr) {
/* Set the first strip modifier as the active one and uncollapse the root panel. */
blender::seq::for_each_callback(&scene->ed->seqbase, [&](Strip *strip) -> bool {
seq::modifier_set_active(strip,
static_cast<StripModifierData *>(strip->modifiers.first));
LISTBASE_FOREACH (StripModifierData *, smd, &strip->modifiers) {
smd->layout_panel_open_flag |= UI_PANEL_DATA_EXPAND_ROOT;
}
return true;
});
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 75)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_COMPOSIT) {
version_node_socket_name(ntree, CMP_NODE_RGB, "RGBA", "Color");
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 81)) {
LISTBASE_FOREACH (Material *, material, &bmain->materials) {
do_version_material_remove_use_nodes(bmain, material);
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 82)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type != NTREE_COMPOSIT) {
continue;
}
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == CMP_NODE_SETALPHA) {
do_version_set_alpha_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_CHANNEL_MATTE) {
do_version_channel_matte_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_COLORBALANCE) {
do_version_color_balance_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_PREMULKEY) {
do_version_convert_alpha_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_DIST_MATTE) {
do_version_distance_matte_menus_to_inputs(*node_tree, *node);
}
else if (node->type_legacy == CMP_NODE_COLOR_SPILL) {
do_version_color_spill_menus_to_inputs(*node_tree, *node);
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 83)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type != NTREE_COMPOSIT) {
continue;
}
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == CMP_NODE_DOUBLEEDGEMASK) {
do_version_double_edge_mask_options_to_inputs(*node_tree, *node);
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 84)) {
/* Add sidebar to the preferences editor. */
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_USERPREF) {
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 preferences", RGN_TYPE_HEADER);
if (new_sidebar != nullptr) {
new_sidebar->alignment = RGN_ALIGN_LEFT;
new_sidebar->flag &= ~RGN_FLAG_HIDDEN;
}
}
}
}
}
}
if (MAIN_VERSION_FILE_ATLEAST(bmain, 500, 23) && !MAIN_VERSION_FILE_ATLEAST(bmain, 500, 85)) {
/* Old sky textures were temporarily removed and restored. */
/* Change default Sky Texture to Nishita (after removal of old sky models) */
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_SHADER) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type_legacy == SH_NODE_TEX_SKY && node->storage) {
NodeTexSky *tex = (NodeTexSky *)node->storage;
if (tex->sky_model == 0) {
tex->sky_model = SHD_SKY_SINGLE_SCATTERING;
}
if (tex->sky_model == 1) {
tex->sky_model = SHD_SKY_MULTIPLE_SCATTERING;
}
}
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 86)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type != NTREE_GEOMETRY) {
continue;
}
version_dynamic_viewer_node_items(*ntree);
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 87)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type != NTREE_COMPOSIT) {
continue;
}
version_node_input_socket_name(node_tree, CMP_NODE_ALPHAOVER, "Image_001", "Foreground");
version_node_input_socket_name(node_tree, CMP_NODE_ALPHAOVER, "Image", "Background");
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 88)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type != NTREE_COMPOSIT) {
continue;
}
version_node_input_socket_name(node_tree, CMP_NODE_DISPLACE, "Vector", "Displacement");
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == CMP_NODE_DISPLACE) {
do_version_displace_node_remove_xy_scale(*node_tree, *node);
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 89)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type != NTREE_COMPOSIT) {
continue;
}
version_node_input_socket_name(node_tree, CMP_NODE_BOKEHBLUR, "Bounding box", "Mask");
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
if (node->type_legacy == CMP_NODE_BOKEHBLUR) {
do_version_bokeh_blur_pixel_size(*node_tree, *node);
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 89)) {
/* Node Editor: toggle overlays on. */
if (!DNA_struct_exists(fd->filesdna, "SpaceClipOverlay")) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) {
if (space->spacetype == SPACE_CLIP) {
SpaceClip *sclip = (SpaceClip *)space;
sclip->overlay.flag |= SC_SHOW_OVERLAYS;
sclip->overlay.flag |= SC_SHOW_CURSOR;
}
}
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 92)) {
do_version_adaptive_subdivision(bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 95)) {
LISTBASE_FOREACH (Camera *, camera, &bmain->cameras) {
float default_col[4] = {0.5f, 0.5f, 0.5f, 1.0f};
copy_v4_v4(camera->composition_guide_color, default_col);
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 97)) {
/* Enable new "Optional Label" setting for all menu sockets. This was implicit before. */
FOREACH_NODETREE_BEGIN (bmain, tree, id) {
tree->tree_interface.foreach_item([&](bNodeTreeInterfaceItem &item) {
if (item.item_type != NODE_INTERFACE_SOCKET) {
return true;
}
auto &socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(item);
if (!STREQ(socket.socket_type, "NodeSocketMenu")) {
return true;
}
socket.flag |= NODE_INTERFACE_SOCKET_OPTIONAL_LABEL;
return true;
});
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 98)) {
/* For a brief period of time, these values were not properly versioned, so it is possible for
* files to be in an odd state. This versioning was formerly run in 4.2 subversion 23. */
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
UvSculpt &uvsculpt = scene->toolsettings->uvsculpt;
if (uvsculpt.size == 0 || uvsculpt.curve_distance_falloff == nullptr) {
uvsculpt.size = 100;
uvsculpt.strength = 1.0f;
uvsculpt.curve_distance_falloff_preset = BRUSH_CURVE_SMOOTH;
if (uvsculpt.curve_distance_falloff == nullptr) {
uvsculpt.curve_distance_falloff = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 99)) {
LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
wm->xr.session_settings.fly_speed = 3.0f;
}
}
/**
* 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.
*/
/* Keep this versioning always enabled at the bottom of the function; it can only be moved
* behind a subversion bump when the file format is changed. */
LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) {
bke::mesh_freestyle_marks_to_generic(*mesh);
}
/* TODO: Can be moved to subversion bump. */
AS_asset_library_import_method_ensure_valid(*bmain);
}