Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,278 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blenloader
*
* Utils to check/validate a Main is in sane state,
* only checks relations between data-blocks and libraries for now.
*
* \note Does not *fix* anything, only reports found errors.
*/
#include "CLG_log.h"
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
#include "BLI_linklist.h"
#include "DNA_collection_types.h"
#include "DNA_key_types.h"
#include "DNA_node_types.h"
#include "DNA_windowmanager_types.h"
#include "BKE_key.hh"
#include "BKE_lib_id.hh"
#include "BKE_lib_remap.hh"
#include "BKE_library.hh"
#include "BKE_main.hh"
#include "BKE_node.hh"
#include "BKE_report.hh"
#include "BLO_blend_validate.hh"
#include "BLO_readfile.hh"
#include "readfile.hh"
namespace blender {
static CLG_LogRef LOG = {"blend.validate"};
bool BLO_main_validate_libraries(Main *bmain, ReportList *reports)
{
blo_split_main(bmain);
BLI_assert(bmain->split_mains);
VectorSet<Main *> &split_mains = *bmain->split_mains;
BLI_assert(split_mains[0] == bmain);
bool is_valid = true;
BKE_main_lock(bmain);
MainListsArray lbarray = BKE_main_lists_get(*bmain);
int i = lbarray.size();
while (i--) {
for (ID *id = static_cast<ID *>(lbarray[i]->first); id != nullptr;
id = static_cast<ID *>(id->next))
{
if (ID_IS_LINKED(id)) {
is_valid = false;
BKE_reportf(reports,
RPT_ERROR,
"ID %s is in local database while being linked from library %s!",
id->name,
id->lib->filepath);
}
}
}
for (Main *curmain : split_mains) {
if (curmain == bmain) {
continue;
}
Library *curlib = curmain->curlib;
if (curlib == nullptr) {
BKE_report(reports, RPT_ERROR, "Library database with null library data-block pointer!");
continue;
}
BKE_library_filepath_set(bmain, curlib, curlib->filepath);
BlendFileReadReport bf_reports{};
bf_reports.reports = reports;
BlendHandle *bh = BLO_blendhandle_from_file(curlib->runtime->filepath_abs, &bf_reports);
if (bh == nullptr) {
BKE_reportf(reports,
RPT_ERROR,
"Library ID %s not found at expected path %s!",
curlib->id.name,
curlib->runtime->filepath_abs);
continue;
}
lbarray = BKE_main_lists_get(*curmain);
i = lbarray.size();
while (i--) {
ID *id = static_cast<ID *>(lbarray[i]->first);
if (id == nullptr) {
continue;
}
if (GS(id->name) == ID_LI) {
is_valid = false;
BKE_reportf(reports,
RPT_ERROR,
"Library ID %s in library %s, this should not happen!",
id->name,
curlib->filepath);
continue;
}
int totnames = 0;
LinkNode *names = BLO_blendhandle_get_datablock_names(bh, GS(id->name), false, &totnames);
for (; id != nullptr; id = static_cast<ID *>(id->next)) {
if (!ID_IS_LINKED(id)) {
is_valid = false;
BKE_reportf(reports,
RPT_ERROR,
"ID %s has null lib pointer while being in library %s!",
id->name,
curlib->filepath);
continue;
}
if (id->lib != curlib) {
is_valid = false;
BKE_reportf(reports, RPT_ERROR, "ID %s has mismatched lib pointer!", id->name);
continue;
}
LinkNode *name = names;
for (; name; name = name->next) {
const char *str_name = static_cast<const char *>(name->link);
if (id->name[2] == str_name[0] && STREQ(str_name, id->name + 2)) {
break;
}
}
if (name == nullptr) {
is_valid = false;
BKE_reportf(reports,
RPT_ERROR,
"ID %s not found in library %s anymore!",
id->name,
id->lib->filepath);
continue;
}
}
BLI_linklist_freeN(names);
}
BLO_blendhandle_close(bh);
}
blo_join_main(bmain);
BLI_assert(!bmain->split_mains);
BKE_main_unlock(bmain);
return is_valid;
}
bool BLO_main_validate_shapekeys(Main *bmain, ReportList *reports)
{
ListBaseT<ID> *lb;
ID *id;
bool is_valid = true;
BKE_main_lock(bmain);
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
if (!BKE_key_idtype_support(GS(id->name))) {
break;
}
if (!ID_IS_LINKED(id)) {
/* We assume lib data is valid... */
Key *shapekey = BKE_key_from_id(id);
if (shapekey != nullptr && shapekey->from != id) {
is_valid = false;
BKE_reportf(reports,
RPT_ERROR,
"ID %s uses shapekey %s, but its 'from' pointer is invalid (%p), fixing...",
id->name,
shapekey->id.name,
shapekey->from);
shapekey->from = id;
}
}
}
FOREACH_MAIN_LISTBASE_ID_END;
}
FOREACH_MAIN_LISTBASE_END;
BKE_main_unlock(bmain);
/* NOTE: #BKE_id_delete also locks `bmain`, so we need to do this loop outside of the lock here.
*/
for (Key &shapekey : bmain->shapekeys.items_mutable()) {
if (shapekey.from != nullptr) {
continue;
}
BKE_reportf(reports,
RPT_ERROR,
"ShapeKey %s has an invalid 'from' pointer (%p), it will be deleted",
shapekey.id.name,
shapekey.from);
/* NOTE: also need to remap UI data ID pointers here, since `bmain` is not the current
* `G_MAIN`, default UI-handling remapping callback (defined by call to
* `BKE_library_callback_remap_editor_id_reference_set`) won't work on expected data here. */
BKE_id_delete(bmain, &shapekey, {.extra_remapping_flags = ID_REMAP_FORCE_UI_POINTERS});
}
return is_valid;
}
void BLO_main_validate_embedded_liboverrides(Main *bmain, ReportList * /*reports*/)
{
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
bNodeTree *node_tree = bke::node_tree_from_id(id_iter);
if (node_tree) {
if (node_tree->id.flag & ID_FLAG_EMBEDDED_DATA_LIB_OVERRIDE) {
if (!ID_IS_OVERRIDE_LIBRARY(id_iter)) {
node_tree->id.flag &= ~ID_FLAG_EMBEDDED_DATA_LIB_OVERRIDE;
}
}
}
if (GS(id_iter->name) == ID_SCE) {
Scene *scene = reinterpret_cast<Scene *>(id_iter);
if (scene->master_collection &&
(scene->master_collection->id.flag & ID_FLAG_EMBEDDED_DATA_LIB_OVERRIDE))
{
scene->master_collection->id.flag &= ~ID_FLAG_EMBEDDED_DATA_LIB_OVERRIDE;
}
}
}
FOREACH_MAIN_ID_END;
}
void BLO_main_validate_embedded_flag(Main *bmain, ReportList * /*reports*/)
{
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
if (id_iter->flag & ID_FLAG_EMBEDDED_DATA) {
CLOG_ERROR(
&LOG, "ID %s is flagged as embedded, while existing in Main data-base", id_iter->name);
id_iter->flag &= ~ID_FLAG_EMBEDDED_DATA;
}
bNodeTree *node_tree = bke::node_tree_from_id(id_iter);
if (node_tree) {
if ((node_tree->id.flag & ID_FLAG_EMBEDDED_DATA) == 0) {
CLOG_ERROR(&LOG,
"ID %s has an embedded nodetree which is not flagged as embedded",
id_iter->name);
node_tree->id.flag |= ID_FLAG_EMBEDDED_DATA;
}
}
if (GS(id_iter->name) == ID_SCE) {
Scene *scene = reinterpret_cast<Scene *>(id_iter);
if (scene->master_collection &&
(scene->master_collection->id.flag & ID_FLAG_EMBEDDED_DATA) == 0)
{
CLOG_ERROR(&LOG,
"ID %s has an embedded Collection which is not flagged as embedded",
id_iter->name);
scene->master_collection->id.flag |= ID_FLAG_EMBEDDED_DATA;
}
}
}
FOREACH_MAIN_ID_END;
}
} // namespace blender

View File

@@ -0,0 +1,470 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blenloader
* `.blend` file reading entry point.
*/
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include "MEM_guardedalloc.h"
#include "BLI_ghash.h"
#include "BLI_linklist.h"
#include "BLI_path_utils.hh" /* Only for assertions. */
#include "BLI_set.hh"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "DNA_genfile.h"
#include "BKE_asset.hh"
#include "BKE_idtype.hh"
#include "BKE_main.hh"
#include "BKE_preview_image.hh"
#include "BLO_readfile.hh"
#include "readfile.hh"
#include "BLI_sys_types.h" /* Needed for `intptr_t`. */
namespace blender {
#ifdef WIN32
# include "BLI_winstuff.h"
#endif
/* Access routines used by file-selector. */
void BLO_datablock_info_free(BLODataBlockInfo *datablock_info)
{
if (datablock_info->free_asset_data) {
BKE_asset_metadata_free(&datablock_info->asset_data);
datablock_info->free_asset_data = false;
}
}
void BLO_datablock_info_linklist_free(LinkNode *datablock_infos)
{
BLI_linklist_free(datablock_infos, [](void *link) {
BLODataBlockInfo *datablock_info = static_cast<BLODataBlockInfo *>(link);
BLO_datablock_info_free(datablock_info);
MEM_delete(datablock_info);
});
}
BlendHandle *BLO_blendhandle_from_file(const char *filepath, BlendFileReadReport *reports)
{
BlendHandle *bh;
bh = reinterpret_cast<BlendHandle *>(blo_filedata_from_file(filepath, reports));
return bh;
}
BlendHandle *BLO_blendhandle_from_memory(const void *mem,
int memsize,
BlendFileReadReport *reports)
{
BlendHandle *bh;
bh = reinterpret_cast<BlendHandle *>(blo_filedata_from_memory(mem, memsize, reports));
return bh;
}
int3 BLO_blendhandle_get_version(const BlendHandle *bh)
{
const FileData *fd = reinterpret_cast<const FileData *>(bh);
return int3(fd->fileversion / 100, fd->fileversion % 100, fd->filesubversion);
}
/* Return `false` if the block should be skipped because it is either an invalid block, or it does
* not meet to required conditions. */
static bool blendhandle_load_id_data_and_validate(FileData *fd,
BHead *bhead,
bool use_assets_only,
const char *&r_idname,
short &r_idflag,
AssetMetaData *&r_asset_meta_data,
BLODataBlockInfo::Library *r_library_data)
{
r_idname = blo_bhead_id_name(fd, bhead);
if (!r_idname || r_idname[0] == '\0') {
return false;
}
r_idflag = blo_bhead_id_flag(fd, bhead);
if (r_library_data) {
r_library_data->filepath = blo_bhead_library_filepath(fd, bhead);
r_library_data->flag = blo_bhead_library_flag(fd, bhead);
}
/* Do not list (and therefore allow direct linking of) packed data.
* While supporting this is conceptually possible, it would require significant changes in
* the UI (file browser) and UX (link operation) to convey this concept and handle it
* correctly. */
if (r_idflag & ID_FLAG_LINKED_AND_PACKED) {
return false;
}
r_asset_meta_data = blo_bhead_id_asset_data_address(fd, bhead);
if (use_assets_only && r_asset_meta_data == nullptr) {
return false;
}
return true;
}
LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh,
int ofblocktype,
const bool use_assets_only,
int *r_tot_names)
{
FileData *fd = reinterpret_cast<FileData *>(bh);
LinkNode *names = nullptr;
BHead *bhead;
int tot = 0;
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
if (bhead->code == ofblocktype) {
const char *idname;
short idflag;
AssetMetaData *asset_meta_data;
if (!blendhandle_load_id_data_and_validate(
fd, bhead, use_assets_only, idname, idflag, asset_meta_data, nullptr))
{
continue;
}
BLI_linklist_prepend(&names, BLI_strdup(idname + 2));
tot++;
}
else if (bhead->code == BLO_CODE_ENDB) {
break;
}
}
*r_tot_names = tot;
return names;
}
LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh,
int ofblocktype,
const bool use_assets_only,
int *r_tot_info_items)
{
FileData *fd = reinterpret_cast<FileData *>(bh);
LinkNode *infos = nullptr;
BHead *bhead;
int tot = 0;
const bool is_library = (ofblocktype == ID_LI);
const int sdna_nr_preview_image = DNA_struct_find_with_alias(fd->filesdna, "PreviewImage");
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
if (bhead->code == BLO_CODE_ENDB) {
break;
}
if (bhead->code == ofblocktype) {
BHead *id_bhead = bhead;
const char *idname;
short idflag;
AssetMetaData *asset_meta_data;
BLODataBlockInfo::Library library_data;
if (!blendhandle_load_id_data_and_validate(fd,
id_bhead,
use_assets_only,
idname,
idflag,
asset_meta_data,
is_library ? &library_data : nullptr))
{
continue;
}
const char *name = idname + 2;
BLODataBlockInfo *info = MEM_new<BLODataBlockInfo>(__func__);
if (is_library) {
info->library_data = library_data;
}
/* Lastly, read asset data from the following blocks. */
if (asset_meta_data) {
bhead = blo_read_asset_data_block(fd, bhead, &asset_meta_data);
/* blo_read_asset_data_block() reads all DATA heads and already advances bhead to the
* next non-DATA one. Go back, so the loop doesn't skip the non-DATA head. */
bhead = blo_bhead_prev(fd, bhead);
}
STRNCPY(info->name, name);
info->asset_data = asset_meta_data;
info->free_asset_data = true;
bool has_preview = false;
/* See if we can find a preview in the data of this ID. */
for (BHead *data_bhead = blo_bhead_next(fd, id_bhead); data_bhead->code == BLO_CODE_DATA;
data_bhead = blo_bhead_next(fd, data_bhead))
{
if (data_bhead->SDNAnr == sdna_nr_preview_image) {
has_preview = true;
break;
}
}
info->no_preview_found = !has_preview;
BLI_linklist_prepend(&infos, info);
tot++;
}
}
*r_tot_info_items = tot;
return infos;
}
/**
* Read the preview rects and store in `result`.
*
* `bhead` should point to the block that sourced the `preview_from_file`
* parameter.
* `bhead` parameter is consumed. The correct bhead pointing to the next bhead in the file after
* the preview rects is returned by this function.
* \param fd: The filedata to read the data from.
* \param bhead: should point to the block that sourced the `preview_from_file parameter`.
* bhead is consumed. the new bhead is returned by this function.
* \param result: the Preview Image where the preview rect will be stored.
* \param preview_from_file: The read PreviewImage where the bhead points to. The rects of this
* \return PreviewImage or nullptr when no preview Images have been found. Caller owns the returned
*/
static BHead *blo_blendhandle_read_preview_rects(FileData *fd,
BHead *bhead,
PreviewImage *result,
const PreviewImage *preview_from_file)
{
for (int preview_index = 0; preview_index < NUM_ICON_SIZES; preview_index++) {
if (preview_from_file->rect[preview_index] && preview_from_file->w[preview_index] &&
preview_from_file->h[preview_index])
{
bhead = blo_bhead_next(fd, bhead);
BLI_assert((preview_from_file->w[preview_index] * preview_from_file->h[preview_index] *
sizeof(uint)) == bhead->len);
result->rect[preview_index] = static_cast<uint *>(
BLO_library_read_struct(fd, bhead, "PreviewImage Icon Rect"));
}
else {
/* This should not be needed, but can happen in 'broken' .blend files,
* better handle this gracefully than crashing. */
BLI_assert(preview_from_file->rect[preview_index] == nullptr &&
preview_from_file->w[preview_index] == 0 &&
preview_from_file->h[preview_index] == 0);
result->rect[preview_index] = nullptr;
result->w[preview_index] = result->h[preview_index] = 0;
}
result->flag[preview_index] &= ~PRV_RENDERING;
}
return bhead;
}
PreviewImage *BLO_blendhandle_get_preview_for_id(BlendHandle *bh,
int ofblocktype,
const char *name)
{
FileData *fd = reinterpret_cast<FileData *>(bh);
bool looking = false;
const int sdna_preview_image = DNA_struct_find_with_alias(fd->filesdna, "PreviewImage");
for (BHead *bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
if (bhead->code == BLO_CODE_DATA) {
if (looking && bhead->SDNAnr == sdna_preview_image) {
PreviewImage *preview_from_file = static_cast<PreviewImage *>(
BLO_library_read_struct(fd, bhead, "PreviewImage"));
if (preview_from_file == nullptr) {
break;
}
PreviewImage *result = MEM_dupalloc(preview_from_file);
result->runtime = MEM_new<bke::PreviewImageRuntime>(__func__);
bhead = blo_blendhandle_read_preview_rects(fd, bhead, result, preview_from_file);
MEM_delete(preview_from_file);
return result;
}
}
else if (looking || bhead->code == BLO_CODE_ENDB) {
/* We were looking for a preview image, but didn't find any belonging to block. So it doesn't
* exist. */
break;
}
else if (bhead->code == ofblocktype) {
const char *idname = blo_bhead_id_name(fd, bhead);
if (idname && STREQ(&idname[2], name)) {
looking = true;
}
}
}
return nullptr;
}
LinkNode *BLO_blendhandle_get_linkable_groups(BlendHandle *bh)
{
FileData *fd = reinterpret_cast<FileData *>(bh);
Set<const char *> gathered;
LinkNode *names = nullptr;
BHead *bhead;
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
if (bhead->code == BLO_CODE_ENDB) {
break;
}
if (BKE_idtype_idcode_is_valid(bhead->code)) {
if (BKE_idtype_idcode_is_linkable(bhead->code)) {
const char *str = BKE_idtype_idcode_to_name(bhead->code);
if (gathered.add(str)) {
BLI_linklist_prepend(&names, BLI_strdup(str));
}
}
}
}
return names;
}
void BLO_blendhandle_close(BlendHandle *bh)
{
FileData *fd = reinterpret_cast<FileData *>(bh);
blo_filedata_free(fd);
}
void BLO_read_invalidate_message(BlendHandle *bh, Main *bmain, const char *message)
{
FileData *fd = reinterpret_cast<FileData *>(bh);
blo_readfile_invalidate(fd, bmain, message);
}
/**********/
BlendFileData *BLO_read_from_file(const char *filepath,
eBLOReadSkip skip_flags,
BlendFileReadReport *reports)
{
BLI_assert(!BLI_path_is_rel(filepath));
BLI_assert(BLI_path_is_abs_from_cwd(filepath));
BlendFileData *bfd = nullptr;
FileData *fd;
fd = blo_filedata_from_file(filepath, reports);
if (fd) {
fd->skip_flags = skip_flags;
bfd = blo_read_file_internal(fd, filepath);
blo_filedata_free(fd);
}
return bfd;
}
BlendFileData *BLO_read_from_memory(const void *mem,
int memsize,
eBLOReadSkip skip_flags,
ReportList *reports)
{
BlendFileData *bfd = nullptr;
FileData *fd;
BlendFileReadReport bf_reports{};
bf_reports.reports = reports;
fd = blo_filedata_from_memory(mem, memsize, &bf_reports);
if (fd) {
fd->skip_flags = skip_flags;
bfd = blo_read_file_internal(fd, "");
blo_filedata_free(fd);
}
return bfd;
}
BlendFileData *BLO_read_from_memfile(Main *oldmain,
const char *filepath,
MemFile *memfile,
const BlendFileReadParams *params,
ReportList *reports)
{
BlendFileData *bfd = nullptr;
FileData *fd;
BlendFileReadReport bf_reports{};
bf_reports.reports = reports;
fd = blo_filedata_from_memfile(memfile, params, &bf_reports);
if (fd) {
fd->skip_flags = eBLOReadSkip(params->skip_flags);
STRNCPY(fd->relabase, filepath);
/* Build old ID map for all old IDs. */
blo_make_old_idmap_from_main(fd, oldmain);
/* Separate linked data from old main.
* WARNING: Do not split out packed IDs here, as these are handled similarly as local IDs in
* undo context. */
blo_split_main(oldmain, false);
fd->old_bmain = oldmain;
/* Removed packed data from this trick - it's internal data that needs saves. */
/* Store all existing ID caches pointers into a mapping, to allow restoring them into newly
* read IDs whenever possible.
*
* Note that this is only required for local data, since linked data are always re-used
* 'as-is'. */
blo_cache_storage_init(fd, oldmain);
bfd = blo_read_file_internal(fd, filepath);
/* Ensure relinked caches are not freed together with their old IDs. */
blo_cache_storage_old_bmain_clear(fd, oldmain);
/* Still in-use libraries have already been moved from oldmain to new main
* (fd->bmain->split_mains), but oldmain itself shall *never* be 'transferred' to the new
* split_mains!
*/
BLI_assert(oldmain->split_mains && (*oldmain->split_mains)[0] == oldmain);
/* That way, libraries (aka mains) we did not reuse in new undone/redone state
* will be cleared together with `oldmain`. */
blo_join_main(oldmain);
blo_filedata_free(fd);
}
return bfd;
}
void BLO_blendfiledata_free(BlendFileData *bfd)
{
if (bfd->main) {
BKE_main_free(bfd->main);
}
if (bfd->user) {
MEM_delete(bfd->user);
}
MEM_delete(bfd);
}
void BLO_read_do_version_after_setup(Main *new_bmain,
BlendfileLinkAppendContext *lapp_context,
BlendFileReadReport *reports)
{
do_versions_after_setup(new_bmain, lapp_context, reports);
}
} // namespace blender

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,381 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blenloader
* blenloader readfile private function prototypes.
*/
#pragma once
#include <cstdio> /* IWYU pragma: keep. Include header using off_t before poisoning it below. */
#include <optional>
#ifdef WIN32
# include "BLI_winstuff.h"
#endif
#include "BLI_enum_flags.hh"
#include "BLI_fileops.h"
#include "BLI_filereader.h"
#include "BLI_map.hh"
#include "DNA_sdna_types.h"
#include "DNA_space_types.h"
#include "BLO_core_bhead.hh"
#include "BLO_core_blend_header.hh"
#include "BLO_readfile.hh"
namespace blender {
struct BlendFileData;
struct BlendfileLinkAppendContext;
struct BlendFileReadParams;
struct BlendFileReadReport;
struct BLOCacheStorage;
struct BHeadN;
struct BHeadSort;
struct DNA_ReconstructInfo;
struct IDNameLib_Map;
struct Key;
struct Main;
struct MemFile;
struct Object;
struct OldNewMap;
struct UserDef;
/**
* Store some critical information about the read blend-file.
*/
enum eFileDataFlag {
FD_FLAGS_SWITCH_ENDIAN = 1 << 0,
FD_FLAGS_FILE_POINTSIZE_IS_4 = 1 << 1,
FD_FLAGS_POINTSIZE_DIFFERS = 1 << 2,
FD_FLAGS_FILE_OK = 1 << 3,
FD_FLAGS_IS_MEMFILE = 1 << 4,
/**
* The Blender file is not compatible with current code, but is still likely a blender file
* 'from the future'. Improves report to the user.
*/
FD_FLAGS_FILE_FUTURE = 1 << 5,
/**
* The blend-file has IDs with invalid names (either using the 5.0+ new 'long names', or
* corrupted). I.e. their names have no null char in their first 66 bytes.
*/
FD_FLAGS_HAS_INVALID_ID_NAMES = 1 << 6,
};
ENUM_OPERATORS(eFileDataFlag)
/* Disallow since it's 32bit on ms-windows. */
#ifdef __GNUC__
# pragma GCC poison off_t
#endif
/**
* General data used during a blend-file reading.
*
* Note that this data (and its accesses) are absolutely not thread-safe currently. It should never
* be accessed concurrently.
*/
struct FileData {
/** Linked list of BHeadN's. */
ListBaseT<BHeadN> bhead_list = {};
enum eFileDataFlag flags = eFileDataFlag(0);
bool is_eof = false;
BlenderHeader blender_header = {};
FileReader *file = nullptr;
std::optional<BLI_stat_t> file_stat;
/**
* Whether we are undoing (< 0) or redoing (> 0), used to choose which 'unchanged' flag to use
* to detect unchanged data from memfile.
* #eUndoStepDir.
*/
int undo_direction = 0;
/** Used for relative paths handling.
*
* Typically the actual filepath of the read blend-file, except when recovering
* save-on-exit/autosave files. In the latter case, it will be the path of the file that
* generated the auto-saved one being recovered.
*
* NOTE: Currently expected to be the same path as #BlendFileData.filepath. */
char relabase[FILE_MAX] = {};
/** General reading variables. */
SDNA *filesdna = nullptr;
const SDNA *memsdna = nullptr;
/** Array of #eSDNA_StructCompare. */
const char *compflags = nullptr;
DNA_ReconstructInfo *reconstruct_info = nullptr;
int fileversion = 0;
/**
* Unlike the `fileversion` which is read from the header,
* this is initialized from #read_file_dna.
*/
int filesubversion = 0;
/** Used to retrieve ID names from (bhead+1). */
int id_name_offset = 0;
/**
* Used to retrieve asset data from (bhead+1). NOTE: This may not be available in old files,
* will be -1 then!
*/
int id_asset_data_offset = 0;
int id_flag_offset = 0;
int id_deep_hash_offset = 0;
/**
* Gives access to libraries' filepath and flag, useful for introspection of blendfiles'
* dependencies _without_ having to fully read them.
*/
int library_filepath_offset = 0;
int library_flag_offset = 0;
/** For do_versions patching. */
int globalf = 0;
int fileflags = 0;
/** Optionally skip some data-blocks when they're not needed. */
eBLOReadSkip skip_flags = BLO_READ_SKIP_NONE;
/**
* Tag to apply to all loaded ID data-blocks.
*
* \note This is initialized from #LibraryLink_Params.id_tag_extra since passing it as an
* argument would need an additional argument to be passed around when expanding library data.
*/
int id_tag_extra = 0;
OldNewMap *datamap = nullptr;
OldNewMap *globmap = nullptr;
/** Used to keep track of already loaded packed IDs to avoid loading them multiple times. */
std::shared_ptr<Map<IDHash, ID *>> id_by_deep_hash;
/**
* Store mapping from old ID pointers (the values they have in the .blend file) to new ones,
* typically from value in `bhead->old` to address in memory where the ID was read.
* Used during library-linking process (see #lib_link_all).
*/
OldNewMap *libmap = nullptr;
BLOCacheStorage *cache_storage = nullptr;
BHeadSort *bheadmap = nullptr;
int tot_bheadmap = 0;
std::optional<Map<StringRefNull, BHead *>> bhead_idname_map;
/**
* The root (main, local) Main.
* The Main that will own Library IDs.
*
* When reading libraries, this is typically _not_ the same Main as the one being populated from
* the content of this filedata, see #fd_bmain.
*/
Main *bmain = nullptr;
/** The existing root (main, local) Main, used for undo. */
Main *old_bmain = nullptr;
/**
* The main for the (local) data loaded from this filedata.
*
* This is the same as #bmain when opening a blend-file, but not when reading/loading from
* libraries blend-files.
*/
Main *fd_bmain = nullptr;
/**
* IDMap using UID's as keys of all the old IDs in the old bmain. Used during undo to find a
* matching old data when reading a new ID. */
IDNameLib_Map *old_idmap_uid = nullptr;
/**
* IDMap using uids as keys of the IDs read (or moved) in the new main(s).
*
* Used during undo to ensure that the ID pointers from the 'no undo' IDs remain valid (these
* IDs are re-used from old main even if their content is not the same as in the memfile undo
* step, so they could point e.g. to an ID that does not exist in the newly read undo step).
*
* Also used to find current valid pointers (or none) of these 'no undo' IDs existing in
* read memfile. */
IDNameLib_Map *new_idmap_uid = nullptr;
BlendFileReadReport *reports = nullptr;
/** Opaque handle to the storage system used for non-static allocation strings. */
void *storage_handle = nullptr;
/**
* Set when reading a file from undo with incomplete preview, to trigger restart of preview jobs.
*/
bool need_preview_render_restart = false;
};
/**
* Split a single main into a vector of Mains, each containing only IDs from a given library.
*
* The vector is accessible in all of the split mains through the shared pointer
* #Main::split_mains.
*
* The first Main of the vector is the same as the given `main`, and contains local IDs.
*
* If `do_split_packed_ids` is `false`, packed linked IDs remain in the local (first) main as well.
*/
void blo_split_main(Main *bmain, bool do_split_packed_ids = true);
/**
* Join the set of split mains (found in given `main` #Main::split_mains vector shared pointer)
* back into that 'main' main.
*/
void blo_join_main(Main *bmain);
BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) ATTR_NONNULL(1, 2);
/**
* On each new library added, it now checks for the current #FileData and expands relativeness
*
* cannot be called with relative paths anymore!
*/
FileData *blo_filedata_from_file(const char *filepath, BlendFileReadReport *reports);
FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadReport *reports);
FileData *blo_filedata_from_memfile(MemFile *memfile,
const BlendFileReadParams *params,
BlendFileReadReport *reports);
/**
* Build a #IDNameLib_Map of old main (we only care about local data here,
* so we can do that after #blo_split_main() call).
*/
void blo_make_old_idmap_from_main(FileData *fd, Main *bmain) ATTR_NONNULL(1, 2);
BHead *blo_read_asset_data_block(FileData *fd, BHead *bhead, AssetMetaData **r_asset_data)
ATTR_NONNULL(1, 2);
void blo_cache_storage_init(FileData *fd, Main *bmain) ATTR_NONNULL(1, 2);
void blo_cache_storage_old_bmain_clear(FileData *fd, Main *bmain_old) ATTR_NONNULL(1, 2);
void blo_cache_storage_end(FileData *fd) ATTR_NONNULL(1);
void blo_filedata_free(FileData *fd) ATTR_NONNULL(1);
BHead *blo_bhead_first(FileData *fd) ATTR_NONNULL(1);
BHead *blo_bhead_next(FileData *fd, BHead *thisblock) ATTR_NONNULL(1);
BHead *blo_bhead_prev(FileData *fd, BHead *thisblock) ATTR_NONNULL(1, 2);
/**
* Warning! Caller's responsibility to ensure given bhead **is** an ID one!
*
* Will return `nullptr` if the name is not valid (e.g. because it has no null-char terminator, if
* it was saved in a version of Blender with higher MAX_ID_NAME value).
*/
const char *blo_bhead_id_name(FileData *fd, const BHead *bhead);
/**
* Warning! It's the caller's responsibility to ensure that the given bhead **is** an ID one!
*
* Returns the ID flag value (or `0` if the blendfile is too old and the offset of the ID::flag
* member could not be computed).
*/
short blo_bhead_id_flag(const FileData *fd, const BHead *bhead);
/**
* Warning! Caller's responsibility to ensure given bhead **is** an ID one!
*/
AssetMetaData *blo_bhead_id_asset_data_address(const FileData *fd, const BHead *bhead);
/**
* Return the stored filepath (may be relative) of a library ID.
*
* Warning! Caller's responsibility to ensure that the given bhead **is** a Library ID one!
*/
const char *blo_bhead_library_filepath(const FileData *fd, const BHead *bhead);
/**
* Return the stored flags of a library ID.
*
* Warning! Caller's responsibility to ensure that the given bhead **is** a Library ID one!
*/
LibraryFlag blo_bhead_library_flag(const FileData *fd, const BHead *bhead);
/* do versions stuff */
/**
* Manipulates SDNA before calling #DNA_struct_get_compareflags,
* allowing us to rename structs and struct members.
*
* - This means older versions of Blender won't have access to this data **USE WITH CARE**.
* - These changes are applied on file load (run-time), similar to versioning for compatibility.
*
* \attention ONLY USE THIS KIND OF VERSIONING WHEN `dna_rename_defs.h` ISN'T SUFFICIENT.
*/
void blo_do_versions_dna(SDNA *sdna, int versionfile, int subversionfile);
void blo_do_versions_oldnewmap_insert(OldNewMap *onm, const void *oldaddr, void *newaddr, int nr);
/**
* Only library data.
*/
void *blo_do_versions_newlibadr(FileData *fd,
ID *self_id,
const bool is_linked_only,
const void *adr);
/**
* \note this version patch is intended for versions < 2.52.2,
* but was initially introduced in 2.27 already.
*/
void blo_do_version_old_trackto_to_constraints(Object *ob);
void blo_do_versions_key_uidgen(Key *key);
/**
* Patching #UserDef struct and Themes.
*/
void blo_do_versions_userdef(UserDef *userdef);
void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain);
void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain);
void blo_do_versions_260(FileData *fd, Library *lib, Main *bmain);
void blo_do_versions_270(FileData *fd, Library *lib, Main *bmain);
void blo_do_versions_280(FileData *fd, Library *lib, Main *bmain);
void blo_do_versions_290(FileData *fd, Library *lib, Main *bmain);
void blo_do_versions_300(FileData *fd, Library *lib, Main *bmain);
void blo_do_versions_400(FileData *fd, Library *lib, Main *bmain);
void blo_do_versions_410(FileData *fd, Library *lib, Main *bmain);
void blo_do_versions_420(FileData *fd, Library *lib, Main *bmain);
void blo_do_versions_430(FileData *fd, Library *lib, Main *bmain);
void blo_do_versions_440(FileData *fd, Library *lib, Main *bmain);
void blo_do_versions_450(FileData *fd, Library *lib, Main *bmain);
void blo_do_versions_500(FileData *fd, Library *lib, Main *bmain);
void blo_do_versions_510(FileData *fd, Library *lib, Main *bmain);
void blo_do_versions_520(FileData *fd, Library *lib, Main *bmain);
void do_versions_after_linking_250(Main *bmain);
void do_versions_after_linking_260(Main *bmain);
void do_versions_after_linking_270(Main *bmain);
void do_versions_after_linking_280(FileData *fd, Main *bmain);
void do_versions_after_linking_290(FileData *fd, Main *bmain);
void do_versions_after_linking_300(FileData *fd, Main *bmain);
void do_versions_after_linking_400(FileData *fd, Main *bmain);
void do_versions_after_linking_410(FileData *fd, Main *bmain);
void do_versions_after_linking_420(FileData *fd, Main *bmain);
void do_versions_after_linking_430(FileData *fd, Main *bmain);
void do_versions_after_linking_440(FileData *fd, Main *bmain);
void do_versions_after_linking_450(FileData *fd, Main *bmain);
void do_versions_after_linking_500(FileData *fd, Main *bmain);
void do_versions_after_linking_510(FileData *fd, Main *bmain);
void do_versions_after_linking_520(FileData *fd, Main *bmain);
void do_versions_after_setup(Main *new_bmain,
BlendfileLinkAppendContext *lapp_context,
BlendFileReadReport *reports);
/**
* Direct data-blocks with global linking.
*
* \note This is rather unfortunate to have to expose this here,
* but better use that nasty hack in do_version than readfile itself.
*/
void *blo_read_get_new_globaldata_address(FileData *fd, const void *adr) ATTR_NONNULL(1);
/**
* Mark the Main data as invalid (.blend file reading should be aborted ASAP, and the already read
* data should be discarded). Also add an error report to `fd` including given `message`.
*/
void blo_readfile_invalidate(FileData *fd, Main *bmain, const char *message) ATTR_NONNULL(1, 2, 3);
} // namespace blender

View File

@@ -0,0 +1,55 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blenloader
*/
#include "BLO_readfile.hh"
#include "MEM_guardedalloc.h"
#include "BLI_string.h"
#include "BKE_main.hh"
#include "DNA_ID.h"
namespace blender {
TempLibraryContext *BLO_library_temp_load_id(Main *real_main,
const char *blend_file_path,
const short idcode,
const char *idname,
ReportList *reports)
{
TempLibraryContext *temp_lib_ctx = MEM_new_zeroed<TempLibraryContext>(__func__);
temp_lib_ctx->bmain_base = BKE_main_new();
temp_lib_ctx->bf_reports.reports = reports;
/* Copy the file path so any path remapping is performed properly. */
STRNCPY(temp_lib_ctx->bmain_base->filepath, real_main->filepath);
BlendHandle *blendhandle = BLO_blendhandle_from_file(blend_file_path, &temp_lib_ctx->bf_reports);
LibraryLink_Params lib_link_params;
BLO_library_link_params_init(&lib_link_params, temp_lib_ctx->bmain_base, 0, ID_TAG_TEMP_MAIN);
Main *bmain_lib = BLO_library_link_begin(&blendhandle, blend_file_path, &lib_link_params);
temp_lib_ctx->temp_id = BLO_library_link_named_part(
bmain_lib, &blendhandle, idcode, idname, &lib_link_params);
BLO_library_link_end(bmain_lib, &blendhandle, &lib_link_params, reports);
BLO_blendhandle_close(blendhandle);
return temp_lib_ctx;
}
void BLO_library_temp_free(TempLibraryContext *temp_lib_ctx)
{
BKE_main_free(temp_lib_ctx->bmain_base);
MEM_delete(temp_lib_ctx);
}
} // namespace blender

View File

@@ -0,0 +1,286 @@
/* SPDX-FileCopyrightText: 2004 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blenloader
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
/* open/close */
#ifndef _WIN32
# include <unistd.h>
#else
# include <io.h>
#endif
#include "MEM_guardedalloc.h"
#include "DNA_listBase.h"
#include "BLI_implicit_sharing.hh"
#include "BLI_listbase.h"
#include "BLO_readfile.hh"
#include "BLO_undofile.hh"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
#include "BKE_undo_system.hh"
#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
#include "writefile.hh"
namespace blender {
/* **************** support for memory-write, for undo buffers *************** */
void BLO_memfile_free(MemFile *memfile)
{
while (MemFileChunk *chunk = static_cast<MemFileChunk *>(BLI_pophead(&memfile->chunks))) {
if (chunk->is_identical == false) {
MEM_delete(chunk->buf);
}
MEM_delete(chunk);
}
MEM_SAFE_DELETE(memfile->shared_storage);
memfile->size = 0;
}
MemFileSharedStorage::~MemFileSharedStorage()
{
for (const ImplicitSharingInfoAndData &data : sharing_info_by_address_id.values()) {
/* Removing the user makes sure shared data is freed when the undo step was its last owner. */
data.sharing_info->remove_user_and_delete_if_last();
}
}
void BLO_memfile_merge(MemFile *first, MemFile *second)
{
/* We use this mapping to store the memory buffers from second memfile chunks which are not owned
* by it (i.e. shared with some previous memory steps). */
Map<const char *, MemFileChunk *> buffer_to_second_memchunk;
/* First, detect all memchunks in second memfile that are not owned by it. */
for (MemFileChunk &sc : second->chunks) {
if (sc.is_identical) {
buffer_to_second_memchunk.add(sc.buf, &sc);
}
}
/* Now, check all chunks from first memfile (the one we are removing), and if a memchunk owned by
* it is also used by the second memfile, transfer the ownership. */
for (MemFileChunk &fc : first->chunks) {
if (!fc.is_identical) {
if (MemFileChunk *sc = buffer_to_second_memchunk.lookup_default(fc.buf, nullptr)) {
BLI_assert(sc->is_identical);
sc->is_identical = false;
fc.is_identical = true;
}
/* Note that if the second memfile does not use that chunk, we assume that the first one
* fully owns it without sharing it with any other memfile, and hence it should be freed with
* it. */
}
}
BLO_memfile_free(first);
}
void BLO_memfile_clear_future(MemFile *memfile)
{
for (MemFileChunk &chunk : memfile->chunks) {
chunk.is_identical_future = false;
}
}
void BLO_memfile_write_init(WriteData *wd,
MemFileWriteData *mem_data,
MemFile *written_memfile,
MemFile *reference_memfile)
{
wd->use_memfile = true;
mem_data->written_memfile = written_memfile;
mem_data->reference_memfile = reference_memfile;
mem_data->reference_current_chunk = reference_memfile ? static_cast<MemFileChunk *>(
reference_memfile->chunks.first) :
nullptr;
/* If we have a reference memfile, we generate a mapping between the session_uid's of the
* IDs stored in that previous undo step, and its first matching memchunk. This will allow
* us to easily find the existing undo memory storage of IDs even when some re-ordering in
* current Main data-base broke the order matching with the memchunks from previous step.
*/
if (reference_memfile != nullptr) {
uint current_session_uid = MAIN_ID_SESSION_UID_UNSET;
for (MemFileChunk &mem_chunk : reference_memfile->chunks) {
if (!ELEM(mem_chunk.id_session_uid, MAIN_ID_SESSION_UID_UNSET, current_session_uid)) {
current_session_uid = mem_chunk.id_session_uid;
mem_data->id_session_uid_mapping.add_new(current_session_uid, &mem_chunk);
}
}
}
}
void BLO_memfile_write_finalize(WriteData * /*wd*/, MemFileWriteData *mem_data)
{
mem_data->id_session_uid_mapping.clear();
}
void BLO_memfile_chunk_add(MemFileWriteData *mem_data, const char *buf, size_t size)
{
MemFile *memfile = mem_data->written_memfile;
MemFileChunk **compchunk_step = &mem_data->reference_current_chunk;
MemFileChunk *curchunk = MEM_new_uninitialized<MemFileChunk>("MemFileChunk");
curchunk->size = size;
curchunk->buf = nullptr;
curchunk->is_identical = false;
/* This is unsafe in the sense that an app handler or other code that does not
* perform an undo push may make changes after the last undo push that
* will then not be undo. Though it's not entirely clear that is wrong behavior. */
curchunk->is_identical_future = true;
curchunk->id_session_uid = mem_data->current_id_session_uid;
BLI_addtail(&memfile->chunks, curchunk);
/* we compare compchunk with buf */
if (*compchunk_step != nullptr) {
MemFileChunk *compchunk = *compchunk_step;
if (compchunk->size == curchunk->size) {
if (memcmp(compchunk->buf, buf, size) == 0) {
curchunk->buf = compchunk->buf;
curchunk->is_identical = true;
compchunk->is_identical_future = true;
}
}
*compchunk_step = static_cast<MemFileChunk *>(compchunk->next);
}
/* not equal... */
if (curchunk->buf == nullptr) {
char *buf_new = MEM_new_array_uninitialized<char>(size, "Chunk buffer");
memcpy(buf_new, buf, size);
curchunk->buf = buf_new;
memfile->size += size;
}
}
Main *BLO_memfile_main_get(MemFile *memfile, Main *bmain, Scene **r_scene)
{
Main *bmain_undo = nullptr;
BlendFileReadParams read_params{};
BlendFileData *bfd = BLO_read_from_memfile(
bmain, BKE_main_blendfile_path(bmain), memfile, &read_params, nullptr);
if (bfd) {
bmain_undo = bfd->main;
if (r_scene) {
*r_scene = bfd->curscene;
}
MEM_delete(bfd);
}
return bmain_undo;
}
static int64_t undo_read(FileReader *reader, void *buffer, size_t size)
{
UndoReader *undo = reinterpret_cast<UndoReader *>(reader);
static size_t seek = SIZE_MAX; /* The current position. */
static size_t offset = 0; /* Size of previous chunks. */
static MemFileChunk *chunk = nullptr;
size_t chunkoffset, readsize, totread;
undo->memchunk_identical = true;
if (size == 0) {
return 0;
}
if (seek != size_t(undo->reader.offset)) {
chunk = static_cast<MemFileChunk *>(undo->memfile->chunks.first);
seek = 0;
while (chunk) {
if (seek + chunk->size > size_t(undo->reader.offset)) {
break;
}
seek += chunk->size;
chunk = static_cast<MemFileChunk *>(chunk->next);
}
offset = seek;
seek = size_t(undo->reader.offset);
}
if (chunk) {
totread = 0;
do {
/* First check if it's on the end if current chunk. */
if (seek - offset == chunk->size) {
offset += chunk->size;
chunk = static_cast<MemFileChunk *>(chunk->next);
}
/* Debug, should never happen. */
if (chunk == nullptr) {
printf("illegal read, chunk zero\n");
return 0;
}
chunkoffset = seek - offset;
readsize = size - totread;
/* Data can be spread over multiple chunks, so clamp size
* to within this chunk, and then it will read further in
* the next chunk. */
if (chunkoffset + readsize > chunk->size) {
readsize = chunk->size - chunkoffset;
}
memcpy(POINTER_OFFSET(buffer, totread), chunk->buf + chunkoffset, readsize);
totread += readsize;
undo->reader.offset += off64_t(readsize);
seek += readsize;
/* `is_identical` of current chunk represents whether it changed compared to previous undo
* step. this is fine in redo case, but not in undo case, where we need an extra flag
* defined when saving the next (future) step after the one we want to restore, as we are
* supposed to 'come from' that future undo step, and not the one before current one. */
undo->memchunk_identical &= undo->undo_direction == STEP_REDO ? chunk->is_identical :
chunk->is_identical_future;
} while (totread < size);
return int64_t(totread);
}
return 0;
}
static void undo_close(FileReader *reader)
{
MEM_delete(reader);
}
FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction)
{
UndoReader *undo = MEM_new_zeroed<UndoReader>(__func__);
undo->memfile = memfile;
undo->undo_direction = undo_direction;
undo->reader.read = undo_read;
undo->reader.seek = nullptr;
undo->reader.close = undo_close;
return reinterpret_cast<FileReader *>(undo);
}
} // namespace blender

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,521 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blenloader
*/
#define DNA_DEPRECATED_ALLOW
#include "DNA_brush_types.h"
#include "DNA_camera_types.h"
#include "DNA_collection_types.h"
#include "DNA_curves_types.h"
#include "DNA_modifier_types.h"
#include "DNA_windowmanager_types.h"
#include "DNA_workspace_types.h"
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_string_utf8.h"
#include "BKE_collection.hh"
#include "BKE_context.hh"
#include "BKE_customdata.hh"
#include "BKE_file_handler.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_image_format.hh"
#include "BKE_main.hh"
#include "BKE_node.hh"
#include "BKE_node_legacy_types.hh"
#include "BKE_node_runtime.hh"
#include "BKE_paint.hh"
#include "BKE_screen.hh"
#include "SEQ_sequencer.hh"
#include "BLT_translation.hh"
#include "readfile.hh"
#include "versioning_common.hh"
namespace blender {
void do_versions_after_linking_430(FileData * /*fd*/, Main *bmain)
{
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 6)) {
/* Shift animation data to accommodate the new Diffuse Roughness input. */
version_node_socket_index_animdata(bmain, NTREE_SHADER, SH_NODE_BSDF_PRINCIPLED, 7, 1, 30);
}
}
static void update_paint_modes_for_brush_assets(Main &bmain)
{
/* Replace paint brushes with a reference to the default brush asset for that mode. */
for (Scene &scene : bmain.scenes) {
BKE_paint_brushes_set_default_references(scene.toolsettings);
}
/* Replace persistent tool references with the new single builtin brush tool. */
for (WorkSpace &workspace : bmain.workspaces) {
for (bToolRef &tref : workspace.tools) {
if (tref.space_type == SPACE_IMAGE && tref.mode == SI_MODE_PAINT) {
STRNCPY_UTF8(tref.idname, "builtin.brush");
continue;
}
if (tref.space_type != SPACE_VIEW3D) {
continue;
}
if (!ELEM(tref.mode,
CTX_MODE_SCULPT,
CTX_MODE_PAINT_VERTEX,
CTX_MODE_PAINT_WEIGHT,
CTX_MODE_PAINT_TEXTURE,
CTX_MODE_PAINT_GPENCIL_LEGACY,
CTX_MODE_PAINT_GREASE_PENCIL,
CTX_MODE_SCULPT_GPENCIL_LEGACY,
CTX_MODE_SCULPT_GREASE_PENCIL,
CTX_MODE_WEIGHT_GPENCIL_LEGACY,
CTX_MODE_WEIGHT_GREASE_PENCIL,
CTX_MODE_VERTEX_GREASE_PENCIL,
CTX_MODE_VERTEX_GPENCIL_LEGACY,
CTX_MODE_SCULPT_CURVES))
{
continue;
}
STRNCPY_UTF8(tref.idname, "builtin.brush");
}
}
}
/**
* It was possible that curve attributes were initialized to 0 even if that is not allowed for some
* attributes.
*/
static void fix_built_in_curve_attribute_defaults(Main *bmain)
{
for (Curves &curves : bmain->hair_curves) {
const int curves_num = curves.geometry.curve_num;
if (int *resolutions = static_cast<int *>(CustomData_get_layer_named_for_write(
&curves.geometry.curve_data_legacy, CD_PROP_INT32, "resolution", curves_num)))
{
for (int &resolution : MutableSpan{resolutions, curves_num}) {
resolution = std::max(resolution, 1);
}
}
if (int8_t *nurb_orders = static_cast<int8_t *>(CustomData_get_layer_named_for_write(
&curves.geometry.curve_data_legacy, CD_PROP_INT8, "nurbs_order", curves_num)))
{
for (int8_t &nurbs_order : MutableSpan{nurb_orders, curves_num}) {
nurbs_order = std::max<int8_t>(nurbs_order, 1);
}
}
}
}
static void node_reroute_add_storage(bNodeTree &tree)
{
for (bNode *node : tree.all_nodes()) {
if (node->is_reroute()) {
if (node->storage != nullptr) {
continue;
}
bNodeSocket &input = *static_cast<bNodeSocket *>(node->inputs.first);
bNodeSocket &output = *static_cast<bNodeSocket *>(node->outputs.first);
/* Use uniform identifier for sockets. In old Blender versions (<=2021, up to af0b7925), the
* identifiers were sometimes all lower case. Fixing those wrong socket identifiers is
* important because otherwise they loose links now that the reroute node also uses node
* declarations. */
version_node_socket_identifier_set(input, "Input");
version_node_socket_identifier_set(output, "Output");
NodeReroute *data = MEM_new<NodeReroute>(__func__);
STRNCPY_UTF8(data->type_idname, input.idname);
node->storage = data;
}
}
}
static void add_bevel_modifier_attribute_name_defaults(Main &bmain)
{
for (Object &ob : bmain.objects) {
if (ob.type != OB_MESH) {
continue;
}
for (ModifierData &md : ob.modifiers) {
if (md.type == eModifierType_Bevel) {
BevelModifierData *bmd = reinterpret_cast<BevelModifierData *>(&md);
if (bmd->vertex_weight_name[0] == '\0') {
STRNCPY(bmd->vertex_weight_name, "bevel_weight_vert");
}
if (bmd->edge_weight_name[0] == '\0') {
STRNCPY(bmd->edge_weight_name, "bevel_weight_edge");
}
}
}
}
}
static void hide_simulation_node_skip_socket_value(Main &bmain)
{
for (bNodeTree &tree : bmain.nodetrees) {
for (bNode &node : tree.nodes) {
if (node.type_legacy != GEO_NODE_SIMULATION_OUTPUT) {
continue;
}
bNodeSocket *skip_input = static_cast<bNodeSocket *>(node.inputs.first);
if (!skip_input || !STREQ(skip_input->identifier, "Skip")) {
continue;
}
auto *default_value = static_cast<bNodeSocketValueBoolean *>(skip_input->default_value);
if (!default_value->value) {
continue;
}
bool is_linked = false;
for (bNodeLink &link : tree.links) {
if (link.tosock == skip_input) {
is_linked = true;
}
}
if (is_linked) {
continue;
}
bNode &input_node = version_node_add_empty(tree, "FunctionNodeInputBool");
input_node.parent = node.parent;
input_node.locx_legacy = node.locx_legacy - 25;
input_node.locy_legacy = node.locy_legacy;
NodeInputBool *input_node_storage = MEM_new<NodeInputBool>(__func__);
input_node.storage = input_node_storage;
input_node_storage->boolean = true;
bNodeSocket &input_node_socket = version_node_add_socket(
tree, input_node, SOCK_OUT, "NodeSocketBool", "Boolean");
version_node_add_link(tree, input_node, input_node_socket, node, *skip_input);
/* Change the old socket value so that the versioning code is not run again. */
default_value->value = false;
}
}
}
void blo_do_versions_430(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
{
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 2)) {
for (bScreen &screen : bmain->screens) {
for (ScrArea &area : screen.areabase) {
for (SpaceLink &space_link : area.spacedata) {
if (space_link.spacetype == SPACE_NODE) {
SpaceNode *space_node = reinterpret_cast<SpaceNode *>(&space_link);
space_node->flag &= ~SNODE_FLAG_UNUSED_5;
}
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 3)) {
for (Brush &brush : bmain->brushes) {
if (BrushGpencilSettings *settings = brush.gpencil_settings) {
/* Copy the `draw_strength` value to the `alpha` value. */
brush.alpha = settings->draw_strength;
/* We approximate the simplify pixel threshold by taking the previous threshold (world
* space) and dividing by the legacy radius conversion factor. This should generally give
* reasonable "pixel" threshold values, at least for previous GPv2 defaults. */
settings->simplify_px = settings->simplify_f /
bke::greasepencil::LEGACY_RADIUS_CONVERSION_FACTOR * 0.1f;
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 4)) {
for (Scene &scene : bmain->scenes) {
scene.view_settings.temperature = 6500.0f;
scene.view_settings.tint = 10.0f;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 7)) {
for (Scene &scene : bmain->scenes) {
SequencerToolSettings *sequencer_tool_settings = seq::tool_settings_ensure(&scene);
sequencer_tool_settings->snap_mode |= SEQ_SNAP_TO_PREVIEW_BORDERS |
SEQ_SNAP_TO_PREVIEW_CENTER |
SEQ_SNAP_TO_STRIPS_PREVIEW;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 8)) {
update_paint_modes_for_brush_assets(*bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 9)) {
fix_built_in_curve_attribute_defaults(bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 10)) {
/* Initialize Color Balance node white point settings. */
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type != NTREE_CUSTOM) {
for (bNode &node : ntree->nodes) {
if (node.type_legacy == CMP_NODE_COLORBALANCE) {
if (version_node_ensure_storage_or_invalidate(node)) {
NodeColorBalance *n = static_cast<NodeColorBalance *>(node.storage);
n->input_temperature = n->output_temperature = 6500.0f;
n->input_tint = n->output_tint = 10.0f;
}
}
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 11)) {
for (Curves &curves : bmain->hair_curves) {
curves.geometry.attributes_active_index = curves.attributes_active_index_legacy;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 13)) {
Camera default_cam;
for (Camera &camera : bmain->cameras) {
camera.central_cylindrical_range_u_min = default_cam.central_cylindrical_range_u_min;
camera.central_cylindrical_range_u_max = default_cam.central_cylindrical_range_u_max;
camera.central_cylindrical_range_v_min = default_cam.central_cylindrical_range_v_min;
camera.central_cylindrical_range_v_max = default_cam.central_cylindrical_range_v_max;
camera.central_cylindrical_radius = default_cam.central_cylindrical_radius;
}
}
/* The File Output node now uses the linear color space setting of its stored image formats. So
* we need to ensure the color space value is initialized to some sane default based on the image
* type. Furthermore, the node now gained a new Save As Render option that is global to the node,
* which will be used if Use Node Format is enabled for each input, so we potentially need to
* disable Use Node Format in case inputs had different Save As render options. */
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 14)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type != NTREE_COMPOSIT) {
continue;
}
for (bNode &node : ntree->nodes) {
if (node.type_legacy != CMP_NODE_OUTPUT_FILE) {
continue;
}
if (!version_node_ensure_storage_or_invalidate(node)) {
continue;
}
/* Initialize node format color space if it is not set. */
NodeCompositorFileOutput *storage = static_cast<NodeCompositorFileOutput *>(node.storage);
if (storage->format.linear_colorspace_settings.name[0] == '\0') {
BKE_image_format_update_color_space_for_type(&storage->format);
}
if (node.inputs.is_empty()) {
continue;
}
/* Initialize input formats color space if it is not set. */
for (const bNodeSocket &input : node.inputs) {
NodeImageMultiFileSocket *input_storage = static_cast<NodeImageMultiFileSocket *>(
input.storage);
if (input_storage->format.linear_colorspace_settings.name[0] == '\0') {
BKE_image_format_update_color_space_for_type(&input_storage->format);
}
}
/* EXR images don't use Save As Render. */
if (ELEM(storage->format.imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) {
continue;
}
/* Find out if all inputs have the same Save As Render option. */
const bNodeSocket *first_input = static_cast<bNodeSocket *>(node.inputs.first);
const NodeImageMultiFileSocket *first_input_storage =
static_cast<NodeImageMultiFileSocket *>(first_input->storage);
const bool first_save_as_render = first_input_storage->save_as_render;
bool all_inputs_have_same_save_as_render = true;
for (const bNodeSocket &input : node.inputs) {
const NodeImageMultiFileSocket *input_storage = static_cast<NodeImageMultiFileSocket *>(
input.storage);
if (bool(input_storage->save_as_render) != first_save_as_render) {
all_inputs_have_same_save_as_render = false;
break;
}
}
/* All inputs have the same save as render option, so we set the node Save As Render option
* to that value, and we leave inputs as is. */
if (all_inputs_have_same_save_as_render) {
storage->save_as_render = first_save_as_render;
continue;
}
/* For inputs that have Use Node Format enabled, we need to disabled it because otherwise
* they will use the node's Save As Render option. It follows that we need to copy the
* node's format to the input format. */
for (const bNodeSocket &input : node.inputs) {
NodeImageMultiFileSocket *input_storage = static_cast<NodeImageMultiFileSocket *>(
input.storage);
if (!input_storage->use_node_format) {
continue;
}
input_storage->use_node_format = false;
input_storage->format = storage->format;
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 15)) {
for (Collection &collection : bmain->collections) {
const ListBaseT<CollectionExport> *exporters = &collection.exporters;
for (CollectionExport &data : *exporters) {
/* The name field should be empty at this point. */
BLI_assert(data.name[0] == '\0');
bke::FileHandlerType *fh = bke::file_handler_find(data.fh_idname);
BKE_collection_exporter_name_set(exporters, &data, fh ? fh->label : DATA_("Undefined"));
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 16)) {
for (Scene &scene : bmain->scenes) {
scene.eevee.flag |= SCE_EEVEE_FAST_GI_ENABLED;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 17)) {
FOREACH_NODETREE_BEGIN (bmain, tree, id) {
if (tree->default_group_node_width == 0) {
tree->default_group_node_width = bke::NodeWidth::Default;
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 20)) {
for (bScreen &screen : bmain->screens) {
for (ScrArea &area : screen.areabase) {
for (SpaceLink &sl : area.spacedata) {
if (sl.spacetype == SPACE_SEQ) {
ARegion *region = BKE_area_find_region_type(&area, RGN_TYPE_TOOLS);
if (region != nullptr) {
region->flag &= ~RGN_FLAG_HIDDEN;
}
}
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 21)) {
for (bScreen &screen : bmain->screens) {
for (ScrArea &area : screen.areabase) {
for (SpaceLink &sl : area.spacedata) {
if (sl.spacetype == SPACE_CLIP) {
ARegion *region = BKE_area_find_region_type(&area, RGN_TYPE_WINDOW);
if (region != nullptr) {
View2D *v2d = &region->v2d;
v2d->flag &= ~V2D_VIEWSYNC_SCREEN_TIME;
}
}
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 22)) {
add_bevel_modifier_attribute_name_defaults(*bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 23)) {
for (Object &object : bmain->objects) {
for (ModifierData &md : object.modifiers) {
if (md.type != eModifierType_Nodes) {
continue;
}
NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(&md);
if (nmd.bake_target == NODES_MODIFIER_BAKE_TARGET_INHERIT) {
/* Use disk target for existing modifiers to avoid changing behavior. */
nmd.bake_target = NODES_MODIFIER_BAKE_TARGET_DISK;
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 24)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
node_reroute_add_storage(*ntree);
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 26)) {
hide_simulation_node_skip_socket_value(*bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 28)) {
for (bScreen &screen : bmain->screens) {
for (ScrArea &area : screen.areabase) {
for (SpaceLink &sl : area.spacedata) {
if (sl.spacetype == SPACE_VIEW3D) {
View3D *v3d = reinterpret_cast<View3D *>(&sl);
copy_v3_fl(v3d->overlay.gpencil_grid_color, 0.5f);
copy_v2_fl(v3d->overlay.gpencil_grid_scale, 1.0f);
copy_v2_fl(v3d->overlay.gpencil_grid_offset, 0.0f);
v3d->overlay.gpencil_grid_subdivisions = 4;
}
}
}
}
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type != NTREE_COMPOSIT) {
continue;
}
for (bNode &node : ntree->nodes.items_mutable()) {
if (ELEM(node.type_legacy, CMP_NODE_VIEWER, CMP_NODE_COMPOSITE_DEPRECATED)) {
node.flag &= ~NODE_PREVIEW;
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 29)) {
/* Open warnings panel by default. */
for (Object &object : bmain->objects) {
for (ModifierData &md : object.modifiers) {
if (md.type == eModifierType_Nodes) {
md.layout_panel_open_flag |= 1 << NODES_MODIFIER_PANEL_WARNINGS;
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 403, 31)) {
for (WorkSpace &workspace : bmain->workspaces) {
for (bToolRef &tref : workspace.tools) {
if (tref.space_type != SPACE_SEQ) {
continue;
}
STRNCPY_UTF8(tref.idname, "builtin.select_box");
}
}
}
}
} // namespace blender

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,934 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blenloader
*/
#define DNA_DEPRECATED_ALLOW
#include "DNA_ID.h"
#include "DNA_brush_enums.h"
#include "DNA_brush_types.h"
#include "DNA_light_types.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_node_types.h"
#include "DNA_screen_types.h"
#include "DNA_sequence_types.h"
#include "DNA_windowmanager_types.h"
#include "DNA_workspace_types.h"
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_string.h"
#include "BLI_sys_types.h"
#include "BKE_asset.hh"
#include "BKE_attribute_legacy_convert.hh"
#include "BKE_customdata.hh"
#include "BKE_grease_pencil_legacy_convert.hh"
#include "BKE_idprop.hh"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
#include "BKE_node.hh"
#include "BKE_node_legacy_types.hh"
#include "BKE_node_runtime.hh"
#include "BKE_tracking.hh"
#include "SEQ_iterator.hh"
#include "SEQ_sequencer.hh"
#include "readfile.hh"
#include "versioning_common.hh"
// #include "CLG_log.h"
namespace blender {
// static CLG_LogRef LOG = {"blend.doversion"};
/* The Mix mode of the Mix node previously assumed the alpha of the first input as opposed to
* mixing the alpha as well. So we add a separate color node to get the alpha of the first input
* and set it to the result using a set alpha node. */
static void do_version_mix_node_mix_mode_compositor(bNodeTree &node_tree, bNode &node)
{
if (!version_node_ensure_storage_or_invalidate(node)) {
return;
}
const NodeShaderMix *data = reinterpret_cast<NodeShaderMix *>(node.storage);
if (data->data_type != SOCK_RGBA) {
return;
}
if (data->blend_type != MA_RAMP_BLEND) {
return;
}
bNodeSocket *first_input = bke::node_find_socket(node, SOCK_IN, "A_Color"_ustr);
bNodeSocket *output = bke::node_find_socket(node, SOCK_OUT, "Result_Color"_ustr);
/* Find the link going into the inputs of the node. */
bNodeLink *first_link = nullptr;
for (bNodeLink &link : node_tree.links) {
if (link.tosock == first_input) {
first_link = &link;
}
}
bNode &separate_node = version_node_add_empty(node_tree, "CompositorNodeSeparateColor");
/* Preserve the muted state on the new node so restoring all nodes later behaves the same way. */
SET_FLAG_FROM_TEST(separate_node.flag, node.flag & NODE_MUTED, NODE_MUTED);
separate_node.parent = node.parent;
separate_node.location[0] = node.location[0] - 10.0f;
separate_node.location[1] = node.location[1];
NodeCMPCombSepColor *storage = MEM_new<NodeCMPCombSepColor>(__func__);
storage->mode = CMP_NODE_COMBSEP_COLOR_RGB;
separate_node.storage = storage;
bNodeSocket &separate_input = version_node_add_socket(
node_tree, separate_node, SOCK_IN, "NodeSocketColor", "Image");
bNodeSocket &separate_alpha_output = version_node_add_socket(
node_tree, separate_node, SOCK_OUT, "NodeSocketFloat", "Alpha");
copy_v4_v4(separate_input.default_value_typed<bNodeSocketValueRGBA>()->value,
first_input->default_value_typed<bNodeSocketValueRGBA>()->value);
if (first_link) {
version_node_add_link(
node_tree, *first_link->fromnode, *first_link->fromsock, separate_node, separate_input);
}
bNode &set_alpha_node = version_node_add_empty(node_tree, "CompositorNodeSetAlpha");
SET_FLAG_FROM_TEST(set_alpha_node.flag, node.flag & NODE_MUTED, NODE_MUTED);
set_alpha_node.parent = node.parent;
set_alpha_node.location[0] = node.location[0] - 10.0f;
set_alpha_node.location[1] = node.location[1];
set_alpha_node.storage = MEM_new<NodeCMPCombSepColor>(__func__);
bNodeSocket &set_alpha_image_input = version_node_add_socket(
node_tree, set_alpha_node, SOCK_IN, "NodeSocketColor", "Image");
bNodeSocket &set_alpha_alpha_input = version_node_add_socket(
node_tree, set_alpha_node, SOCK_IN, "NodeSocketFloat", "Alpha");
bNodeSocket &set_alpha_type_input = version_node_add_socket(
node_tree, set_alpha_node, SOCK_IN, "NodeSocketMenu", "Type");
bNodeSocket &set_alpha_output = version_node_add_socket(
node_tree, set_alpha_node, SOCK_OUT, "NodeSocketColor", "Image");
set_alpha_type_input.default_value_typed<bNodeSocketValueMenu>()->value =
CMP_NODE_SETALPHA_MODE_REPLACE_ALPHA;
version_node_add_link(node_tree, node, *output, set_alpha_node, set_alpha_image_input);
version_node_add_link(
node_tree, separate_node, separate_alpha_output, set_alpha_node, set_alpha_alpha_input);
for (bNodeLink &link : node_tree.links.items_reversed_mutable()) {
if (link.fromsock == output && link.tonode != &set_alpha_node) {
version_node_add_link(
node_tree, set_alpha_node, set_alpha_output, *link.tonode, *link.tosock);
bke::node_remove_link(&node_tree, link);
}
}
}
/* The Mix mode of the Mix node previously assumed the alpha of the first input as opposed to
* mixing the alpha as well. So we add a separate color node to get the alpha of the first input
* and set it to the result using a pair of separate and combine color nodes. */
static void do_version_mix_node_mix_mode_geometry(bNodeTree &node_tree, bNode &node)
{
if (!version_node_ensure_storage_or_invalidate(node)) {
return;
}
const NodeShaderMix *data = reinterpret_cast<NodeShaderMix *>(node.storage);
if (data->data_type != SOCK_RGBA) {
return;
}
if (data->blend_type != MA_RAMP_BLEND) {
return;
}
bNodeSocket *first_input = bke::node_find_socket(node, SOCK_IN, "A_Color"_ustr);
bNodeSocket *output = bke::node_find_socket(node, SOCK_OUT, "Result_Color"_ustr);
/* Find the link going into the inputs of the node. */
bNodeLink *first_link = nullptr;
for (bNodeLink &link : node_tree.links) {
if (link.tosock == first_input) {
first_link = &link;
}
}
bNode &separate_alpha_node = version_node_add_empty(node_tree, "FunctionNodeSeparateColor");
separate_alpha_node.parent = node.parent;
separate_alpha_node.location[0] = node.location[0] - 10.0f;
separate_alpha_node.location[1] = node.location[1];
NodeCombSepColor *separate_alpha_storage = MEM_new<NodeCombSepColor>(__func__);
separate_alpha_storage->mode = NODE_COMBSEP_COLOR_RGB;
separate_alpha_node.storage = separate_alpha_storage;
bNodeSocket &separate_alpha_input = version_node_add_socket(
node_tree, separate_alpha_node, SOCK_IN, "NodeSocketColor", "Color");
bNodeSocket &separate_alpha_output = version_node_add_socket(
node_tree, separate_alpha_node, SOCK_OUT, "NodeSocketFloat", "Alpha");
copy_v4_v4(separate_alpha_input.default_value_typed<bNodeSocketValueRGBA>()->value,
first_input->default_value_typed<bNodeSocketValueRGBA>()->value);
if (first_link) {
version_node_add_link(node_tree,
*first_link->fromnode,
*first_link->fromsock,
separate_alpha_node,
separate_alpha_input);
}
bNode &separate_color_node = version_node_add_empty(node_tree, "FunctionNodeSeparateColor");
separate_color_node.parent = node.parent;
separate_color_node.location[0] = node.location[0] - 10.0f;
separate_color_node.location[1] = node.location[1];
NodeCombSepColor *separate_color_storage = MEM_new<NodeCombSepColor>(__func__);
separate_color_storage->mode = NODE_COMBSEP_COLOR_RGB;
separate_color_node.storage = separate_color_storage;
bNodeSocket &separate_color_input = version_node_add_socket(
node_tree, separate_color_node, SOCK_IN, "NodeSocketColor", "Color");
bNodeSocket &separate_color_red_output = version_node_add_socket(
node_tree, separate_color_node, SOCK_OUT, "NodeSocketFloat", "Red");
bNodeSocket &separate_color_green_output = version_node_add_socket(
node_tree, separate_color_node, SOCK_OUT, "NodeSocketFloat", "Green");
bNodeSocket &separate_color_blue_output = version_node_add_socket(
node_tree, separate_color_node, SOCK_OUT, "NodeSocketFloat", "Blue");
version_node_add_link(node_tree, node, *output, separate_color_node, separate_color_input);
bNode &combine_color_node = version_node_add_empty(node_tree, "FunctionNodeCombineColor");
combine_color_node.parent = node.parent;
combine_color_node.location[0] = node.location[0] - 10.0f;
combine_color_node.location[1] = node.location[1];
NodeCombSepColor *combine_color_storage = MEM_new<NodeCombSepColor>(__func__);
combine_color_storage->mode = NODE_COMBSEP_COLOR_RGB;
combine_color_node.storage = combine_color_storage;
bNodeSocket &combine_color_red_input = version_node_add_socket(
node_tree, combine_color_node, SOCK_IN, "NodeSocketFloat", "Red");
bNodeSocket &combine_color_green_input = version_node_add_socket(
node_tree, combine_color_node, SOCK_IN, "NodeSocketFloat", "Green");
bNodeSocket &combine_color_blue_input = version_node_add_socket(
node_tree, combine_color_node, SOCK_IN, "NodeSocketFloat", "Blue");
bNodeSocket &combine_color_alpha_input = version_node_add_socket(
node_tree, combine_color_node, SOCK_IN, "NodeSocketFloat", "Alpha");
bNodeSocket &combine_color_output = version_node_add_socket(
node_tree, combine_color_node, SOCK_OUT, "NodeSocketColor", "Color");
version_node_add_link(node_tree,
separate_color_node,
separate_color_red_output,
combine_color_node,
combine_color_red_input);
version_node_add_link(node_tree,
separate_color_node,
separate_color_green_output,
combine_color_node,
combine_color_green_input);
version_node_add_link(node_tree,
separate_color_node,
separate_color_blue_output,
combine_color_node,
combine_color_blue_input);
version_node_add_link(node_tree,
separate_alpha_node,
separate_alpha_output,
combine_color_node,
combine_color_alpha_input);
for (bNodeLink &link : node_tree.links.items_reversed_mutable()) {
if (link.fromsock == output && link.tonode != &separate_color_node) {
version_node_add_link(
node_tree, combine_color_node, combine_color_output, *link.tonode, *link.tosock);
bke::node_remove_link(&node_tree, link);
}
}
}
static void init_node_tool_operator_idnames(Main &bmain)
{
for (bNodeTree &group : bmain.nodetrees) {
if (group.type != NTREE_GEOMETRY) {
continue;
}
if (!group.geometry_node_asset_traits) {
continue;
}
if (group.geometry_node_asset_traits->node_tool_idname) {
continue;
}
std::string name_str = "geometry.";
for (char c : StringRef(BKE_id_name(group.id))) {
c = tolower(c);
if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_') {
name_str.push_back(c);
}
else {
const bool last_is_underscore = name_str[name_str.size() - 1] == '_';
if (!last_is_underscore) {
name_str.push_back('_');
}
}
}
group.geometry_node_asset_traits->node_tool_idname = BLI_strdupn(name_str.c_str(),
name_str.size());
if (group.id.asset_data) {
auto property = bke::idprop::create(
"node_tool_idname", StringRefNull(group.geometry_node_asset_traits->node_tool_idname));
BKE_asset_metadata_idprop_ensure(group.id.asset_data, property.release());
}
}
}
static void version_realize_instances_to_curve_domain(Main &bmain)
{
for (bNodeTree &node_tree : bmain.nodetrees) {
if (node_tree.type != NTREE_GEOMETRY) {
continue;
}
for (bNode &node : node_tree.nodes) {
if (node.type_legacy != GEO_NODE_REALIZE_INSTANCES) {
continue;
}
node.custom1 |= GEO_NODE_REALIZE_TO_POINT_DOMAIN;
}
}
}
static void version_mesh_uv_map_strings(Main &bmain)
{
for (Mesh &mesh : bmain.meshes) {
const CustomData *data = &mesh.corner_data;
if (!mesh.active_uv_map_attribute) {
if (const char *name = CustomData_get_active_layer_name(data, CD_PROP_FLOAT2)) {
mesh.active_uv_map_attribute = BLI_strdup(name);
}
}
if (!mesh.default_uv_map_attribute) {
if (const char *name = CustomData_get_render_layer_name(data, CD_PROP_FLOAT2)) {
mesh.default_uv_map_attribute = BLI_strdup(name);
}
}
}
}
static void version_clear_unused_strip_flags(Main &bmain)
{
for (Scene &scene : bmain.scenes) {
Editing *ed = seq::editing_get(&scene);
if (ed != nullptr) {
seq::foreach_strip(&ed->seqbase, [&](Strip *strip) {
constexpr int flag_overlap = 1 << 3;
constexpr int flag_ipo_frame_locked = 1 << 8;
constexpr int flag_effect_not_loaded = 1 << 9;
constexpr int flag_delete = 1 << 10;
constexpr int flag_ignore_channel_lock = 1 << 16;
constexpr int flag_show_offsets = 1 << 20;
strip->flag &= ~eStripFlag(flag_overlap | flag_ipo_frame_locked | flag_effect_not_loaded |
flag_delete | flag_ignore_channel_lock | flag_show_offsets);
return true;
});
}
}
}
static void version_string_to_curves_node_inputs(bNodeTree &tree, bNode &node)
{
if (!node.storage) {
return;
}
auto &storage = *reinterpret_cast<NodeGeometryStringToCurves *>(node.storage);
if (!blender::bke::node_find_socket(node, SOCK_IN, "Font"_ustr)) {
bNodeSocket &socket = version_node_add_socket(tree, node, SOCK_IN, "NodeSocketFont", "Font");
socket.default_value_typed<bNodeSocketValueFont>()->value = reinterpret_cast<VFont *>(node.id);
node.id = nullptr;
}
if (!blender::bke::node_find_socket(node, SOCK_IN, "Overflow"_ustr)) {
bNodeSocket &socket = version_node_add_socket(
tree, node, SOCK_IN, "NodeSocketMenu", "Overflow");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.overflow;
}
if (!blender::bke::node_find_socket(node, SOCK_IN, "Align X"_ustr)) {
bNodeSocket &socket = version_node_add_socket(
tree, node, SOCK_IN, "NodeSocketMenu", "Align X");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.align_x;
}
if (!blender::bke::node_find_socket(node, SOCK_IN, "Align Y"_ustr)) {
bNodeSocket &socket = version_node_add_socket(
tree, node, SOCK_IN, "NodeSocketMenu", "Align Y");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.align_y;
}
if (!blender::bke::node_find_socket(node, SOCK_IN, "Pivot Point"_ustr)) {
bNodeSocket &socket = version_node_add_socket(
tree, node, SOCK_IN, "NodeSocketMenu", "Pivot Point");
socket.default_value_typed<bNodeSocketValueMenu>()->value = storage.pivot_mode;
}
}
static const char *legacy_pass_name_to_new_name(const char *name)
{
if (STREQ(name, "DiffDir")) {
return "Diffuse Direct";
}
if (STREQ(name, "DiffInd")) {
return "Diffuse Indirect";
}
if (STREQ(name, "DiffCol")) {
return "Diffuse Color";
}
if (STREQ(name, "GlossDir")) {
return "Glossy Direct";
}
if (STREQ(name, "GlossInd")) {
return "Glossy Indirect";
}
if (STREQ(name, "GlossCol")) {
return "Glossy Color";
}
if (STREQ(name, "TransDir")) {
return "Transmission Direct";
}
if (STREQ(name, "TransInd")) {
return "Transmission Indirect";
}
if (STREQ(name, "TransCol")) {
return "Transmission Color";
}
if (STREQ(name, "VolumeDir")) {
return "Volume Direct";
}
if (STREQ(name, "VolumeInd")) {
return "Volume Indirect";
}
if (STREQ(name, "VolumeCol")) {
return "Volume Color";
}
if (STREQ(name, "AO")) {
return "Ambient Occlusion";
}
if (STREQ(name, "Env")) {
return "Environment";
}
if (STREQ(name, "IndexMA")) {
return "Material Index";
}
if (STREQ(name, "IndexOB")) {
return "Object Index";
}
if (STREQ(name, "GreasePencil")) {
return "Grease Pencil";
}
if (STREQ(name, "Emit")) {
return "Emission";
}
if (STREQ(name, "Z")) {
return "Depth";
}
if (STREQ(name, "Speed")) {
return "Vector";
}
return name;
}
static void do_version_light_remove_use_nodes(Main *bmain, Light *light)
{
if (light->use_nodes) {
return;
}
/* Users defined a light node tree, but deactivated it by disabling "Use Nodes". So we
* simulate the same effect by creating a new Light Output node and setting it to active. */
bNodeTree *ntree = light->nodetree;
if (ntree == nullptr) {
/* In case the light was defined through Python API it might have been missing a node tree.
*/
ntree = bke::node_tree_add_tree_embedded(
bmain, &light->id, "Light Node Tree Versioning", "ShaderNodeTree");
}
bNode *old_output = nullptr;
for (bNode &node : ntree->nodes) {
if (STREQ(node.idname, "ShaderNodeOutputLight") && (node.flag & NODE_DO_OUTPUT)) {
old_output = &node;
old_output->flag &= ~NODE_DO_OUTPUT;
}
}
bNode &new_output = version_node_add_empty(*ntree, "ShaderNodeOutputLight");
bNodeSocket &output_surface_input = version_node_add_socket(
*ntree, new_output, SOCK_IN, "NodeSocketShader", "Surface");
new_output.flag |= NODE_DO_OUTPUT;
bNode &emission = version_node_add_empty(*ntree, "ShaderNodeEmission");
bNodeSocket &emission_color_input = version_node_add_socket(
*ntree, emission, SOCK_IN, "NodeSocketColor", "Color");
bNodeSocket &emission_strength_input = version_node_add_socket(
*ntree, emission, SOCK_IN, "NodeSocketFloat", "Strength");
bNodeSocket &emission_output = version_node_add_socket(
*ntree, emission, SOCK_OUT, "NodeSocketShader", "Emission");
version_node_add_link(*ntree, emission, emission_output, new_output, output_surface_input);
bNodeSocketValueRGBA *rgba = emission_color_input.default_value_typed<bNodeSocketValueRGBA>();
rgba->value[0] = 1.0f;
rgba->value[1] = 1.0f;
rgba->value[2] = 1.0f;
rgba->value[3] = 1.0f;
emission_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. */
emission.location[0] = old_output->location[0] + 1.5f * old_output->width;
emission.location[1] = old_output->location[1];
}
else {
/* Use default position, see #node_tree_shader_default() */
emission.location[0] = -200.0f;
emission.location[1] = 100.0f;
}
new_output.location[0] = emission.location[0] + 2.0f * emission.width;
new_output.location[1] = emission.location[1];
}
/* For cycles, the Denoising Albedo render pass is now registered after the Denoising Normal pass
* to match the compositor Denoise node. So we swap the order of Denoising Albedo and Denoising
* Normal sockets in the Render Layers node that has been saved with the old order. */
static void do_version_render_layers_node_albedo_normal_swap(bNode &node)
{
bNodeSocket *socket_denoise_normal = nullptr;
bNodeSocket *socket_denoise_albedo = nullptr;
for (bNodeSocket &socket : node.outputs) {
if (STREQ(socket.identifier, "Denoising Normal")) {
socket_denoise_normal = &socket;
}
if (STREQ(socket.identifier, "Denoising Albedo")) {
socket_denoise_albedo = &socket;
}
}
if (socket_denoise_albedo && socket_denoise_normal) {
BLI_listbase_swaplinks(&node.outputs, socket_denoise_normal, socket_denoise_albedo);
}
}
/* Some nodes no longer have storage but their storage is still allocated at write time for
* forward compatibility. This only happens during writes from 4.5, so we need to free this
* storage again when loading any file from 4.5. But before this versioning was done, it was
* possible to save a file from 4.5 in 5.0 or 5.1 and it would still have the storage, so we also
* need to include versions up to the current 5.1 subversion. */
static void free_compositor_forward_compatibility_storage(bNode &node)
{
if (!node.storage) {
return;
}
switch (node.type_legacy) {
case CMP_NODE_BOKEHIMAGE:
MEM_delete(static_cast<NodeBokehImage *>(node.storage));
break;
case CMP_NODE_MASK:
MEM_delete(static_cast<NodeMask *>(node.storage));
break;
case CMP_NODE_ANTIALIASING:
MEM_delete(static_cast<NodeAntiAliasingData *>(node.storage));
break;
case CMP_NODE_VECBLUR:
MEM_delete(static_cast<NodeBlurData *>(node.storage));
break;
case CMP_NODE_CHROMA_MATTE:
case CMP_NODE_COLOR_MATTE:
case CMP_NODE_DIFF_MATTE:
case CMP_NODE_LUMA_MATTE:
MEM_delete(static_cast<NodeChroma *>(node.storage));
break;
case CMP_NODE_COLORCORRECTION:
MEM_delete(static_cast<NodeColorCorrection *>(node.storage));
break;
case CMP_NODE_MASK_BOX:
MEM_delete(static_cast<NodeBoxMask *>(node.storage));
break;
case CMP_NODE_MASK_ELLIPSE:
MEM_delete(static_cast<NodeEllipseMask *>(node.storage));
break;
case CMP_NODE_SUNBEAMS_DEPRECATED:
MEM_delete(static_cast<NodeSunBeams *>(node.storage));
break;
case CMP_NODE_DBLUR:
MEM_delete(static_cast<NodeDBlurData *>(node.storage));
break;
case CMP_NODE_BILATERALBLUR:
MEM_delete(static_cast<NodeBilateralBlurData *>(node.storage));
break;
case CMP_NODE_CROP:
MEM_delete(static_cast<NodeTwoXYs *>(node.storage));
break;
case CMP_NODE_COLORBALANCE:
MEM_delete(static_cast<NodeColorBalance *>(node.storage));
break;
default:
return;
}
node.storage = nullptr;
}
static void convert_brush_flags_to_type(Brush &brush)
{
if (brush.flag & BRUSH_UNUSED_1) {
brush.flag &= ~BRUSH_UNUSED_1;
brush.stroke_method = BRUSH_STROKE_AIRBRUSH;
}
else if (brush.flag & BRUSH_UNUSED_2) {
brush.flag &= ~BRUSH_UNUSED_2;
brush.stroke_method = BRUSH_STROKE_ANCHORED;
}
else if (brush.flag & BRUSH_UNUSED_3) {
brush.flag &= ~BRUSH_UNUSED_3;
brush.stroke_method = BRUSH_STROKE_SPACE;
}
else if (brush.flag & BRUSH_UNUSED_4) {
brush.flag &= ~BRUSH_UNUSED_4;
brush.stroke_method = BRUSH_STROKE_DRAG_DOT;
}
else if (brush.flag & BRUSH_UNUSED_5) {
brush.flag &= ~BRUSH_UNUSED_5;
brush.stroke_method = BRUSH_STROKE_LINE;
}
else if (brush.flag & BRUSH_UNUSED_6) {
brush.flag &= ~BRUSH_UNUSED_6;
brush.stroke_method = BRUSH_STROKE_CURVE;
}
else {
brush.stroke_method = BRUSH_STROKE_DOTS;
}
}
void do_versions_after_linking_510(FileData *fd, Main *bmain)
{
/* Some blend files were saved with an invalid active viewer key, possibly due to a bug that
* was fixed already in c8cb24121f, but blend files were never updated. So starting in 5.1, we
* fix those files by essentially doing what ED_node_set_active_viewer_key is supposed to do at
* load time during versioning. Note that the invalid active viewer will just cause a harmless
* assert, so this does not need to exist in previous releases. */
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 0)) {
for (bScreen &screen : bmain->screens) {
for (ScrArea &area : screen.areabase) {
for (SpaceLink &space : area.spacedata) {
if (space.spacetype == SPACE_NODE) {
SpaceNode *space_node = reinterpret_cast<SpaceNode *>(&space);
bNodeTreePath *path = static_cast<bNodeTreePath *>(space_node->treepath.last);
if (space_node->nodetree && path) {
space_node->nodetree->active_viewer_key = path->parent_key;
}
}
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 0)) {
version_clear_unused_strip_flags(*bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 24)) {
/* Note: For legacy Grease Pencil objects (#OB_GPENCIL_LEGACY) this is handled as part of
* bke::greasepencil::convert::legacy_main. */
bke::greasepencil::convert::material_stroke_fill_toggles_to_attributes(
*bmain, {}, *fd->reports);
/* Set the stroke mode for all brushes. */
for (Brush &brush : bmain->brushes) {
if (BrushGpencilSettings *settings = brush.gpencil_settings) {
if (Material *material = settings->material) {
BLI_assert(material->gp_style != nullptr);
SET_FLAG_FROM_TEST(settings->flag2,
(material->gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0,
GP_BRUSH_USE_STROKE);
SET_FLAG_FROM_TEST(settings->flag2,
(material->gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0,
GP_BRUSH_USE_FILL);
}
else {
settings->flag2 |= GP_BRUSH_USE_STROKE;
settings->flag2 &= ~GP_BRUSH_USE_FILL;
}
}
}
/* Set the color to transparent for when the stroke/fill is disabled. */
for (Material &material : bmain->materials) {
if (material.gp_style == nullptr) {
continue;
}
MaterialGPencilStyle &gp_style = *material.gp_style;
if ((gp_style.flag & GP_MATERIAL_STROKE_SHOW) == 0) {
gp_style.stroke_rgba[3] = 0.0f;
}
if ((gp_style.flag & GP_MATERIAL_FILL_SHOW) == 0) {
gp_style.fill_rgba[3] = 0.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.
*/
}
void blo_do_versions_510(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
{
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 1)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
for (bNode &node : node_tree->nodes) {
if (node.type_legacy == SH_NODE_MIX) {
do_version_mix_node_mix_mode_compositor(*node_tree, node);
}
}
}
else if (node_tree->type == NTREE_GEOMETRY) {
for (bNode &node : node_tree->nodes) {
if (node.type_legacy == SH_NODE_MIX) {
do_version_mix_node_mix_mode_geometry(*node_tree, node);
}
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 5)) {
version_realize_instances_to_curve_domain(*bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 7)) {
version_mesh_uv_map_strings(*bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 8)) {
for (Object &obj : bmain->objects) {
if (!obj.pose) {
continue;
}
for (bPoseChannel &pose_bone : obj.pose->chanbase) {
/* Those flags were previously unused, so to be safe we clear them. */
pose_bone.flag &= ~(POSE_SELECTED_ROOT | POSE_SELECTED_TIP);
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 9)) {
init_node_tool_operator_idnames(*bmain);
for (Scene &scene : bmain->scenes) {
scene.r.ffcodecdata.custom_constant_rate_factor = 23;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 10)) {
for (wmWindowManager &wm : bmain->wm) {
wm.xr.session_settings.view_scale = 1.0f;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 12)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
version_node_input_socket_name(node_tree, CMP_NODE_CRYPTOMATTE_LEGACY, "image", "Image");
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 13)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
for (bNode &node : node_tree->nodes) {
if (node.type_legacy == CMP_NODE_R_LAYERS) {
for (bNodeSocket &socket : node.outputs) {
const char *new_pass_name = legacy_pass_name_to_new_name(socket.name);
STRNCPY(socket.name, new_pass_name);
const char *new_pass_identifier = legacy_pass_name_to_new_name(socket.identifier);
version_node_socket_identifier_set(socket, new_pass_identifier);
}
}
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 14)) {
for (bScreen &screen : bmain->screens) {
for (ScrArea &area : screen.areabase) {
for (SpaceLink &sl : area.spacedata) {
if (sl.spacetype == SPACE_IMAGE) {
SpaceImage *sima = reinterpret_cast<SpaceImage *>(&sl);
sima->uv_edge_opacity = sima->uv_opacity;
}
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 16)) {
for (Scene &scene : bmain->scenes) {
if (scene.toolsettings) {
scene.toolsettings->anim_mirror_object = nullptr;
scene.toolsettings->anim_relative_object = nullptr;
scene.toolsettings->anim_mirror_bone[0] = '\0';
}
}
}
/* This has no version check and always runs for all versions because there is forward
* compatibility code at write time that reallocates the storage, so we need to free it
* regardless of the version. */
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
for (bNode &node : node_tree->nodes) {
if (ELEM(node.type_legacy, CMP_NODE_IMAGE, CMP_NODE_R_LAYERS)) {
for (bNodeSocket &socket : node.outputs) {
if (socket.storage) {
MEM_delete_void(socket.storage);
socket.storage = nullptr;
}
}
}
}
}
}
FOREACH_NODETREE_END;
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 15)) {
for (Light &light : bmain->lights) {
do_version_light_remove_use_nodes(bmain, &light);
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 17)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
for (bNode &node : node_tree->nodes) {
if (node.type_legacy == CMP_NODE_MOVIEDISTORTION) {
if (node.storage) {
BKE_tracking_distortion_free(static_cast<MovieDistortion *>(node.storage));
}
node.storage = nullptr;
}
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 18)) {
FOREACH_NODETREE_BEGIN (bmain, tree, id) {
if (tree->type == NTREE_GEOMETRY) {
for (bNode &node : tree->nodes) {
if (node.type_legacy == GEO_NODE_STRING_TO_CURVES) {
version_string_to_curves_node_inputs(*tree, node);
}
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 19)) {
for (Mesh &mesh : bmain->meshes) {
bke::mesh_convert_customdata_to_storage(mesh);
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 20)) {
for (Scene &scene : bmain->scenes) {
SequencerToolSettings *seq_ts = seq::tool_settings_ensure(&scene);
constexpr eSequencerSnapMode SEQ_SNAP_TO_FRAME_RANGE_OLD = eSequencerSnapMode(1 << 8);
/* Snap to frame range was bit 8, now bit 9, to make room for snap to increment in bit 8.
*/
if (seq_ts->snap_mode & SEQ_SNAP_TO_FRAME_RANGE_OLD) {
seq_ts->snap_mode &= ~SEQ_SNAP_TO_FRAME_RANGE_OLD;
seq_ts->snap_mode |= SEQ_SNAP_TO_FRAME_RANGE;
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 21)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
for (bNode &node : node_tree->nodes) {
if (node.type_legacy == CMP_NODE_R_LAYERS) {
do_version_render_layers_node_albedo_normal_swap(node);
}
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 22)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
for (bNode &node : node_tree->nodes) {
free_compositor_forward_compatibility_storage(node);
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 23)) {
for (Brush &brush : bmain->brushes) {
convert_brush_flags_to_type(brush);
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 24)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
/* The 'Viewer Region' option was removed from the UI. */
node_tree->flag &= ~NTREE_VIEWER_BORDER;
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 25)) {
for (Scene &scene : bmain->scenes) {
scene.eevee.direct_light_intensity = 1.0f;
scene.eevee.indirect_light_intensity = 1.0f;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 501, 27)) {
for (Scene &scene : bmain->scenes) {
if (scene.toolsettings) {
const short snap_geom_old = SCE_SNAP_TO_VERTEX | SCE_SNAP_TO_EDGE | SCE_SNAP_TO_FACE |
SCE_SNAP_TO_EDGE_MIDPOINT | SCE_SNAP_TO_EDGE_PERPENDICULAR;
static_assert(snap_geom_old == 63);
if (scene.toolsettings->snap_mode_tools == snap_geom_old) {
scene.toolsettings->snap_mode_tools = SCE_SNAP_TO_GEOM;
}
}
}
}
/**
* 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.
*/
}
} // namespace blender

View File

@@ -0,0 +1,924 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blenloader
*/
#define DNA_DEPRECATED_ALLOW
#include "NOD_geometry_nodes_srna.hh"
#include "DNA_ID.h"
#include "DNA_brush_types.h"
#include "DNA_camera_types.h"
#include "DNA_curve_types.h"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_node_tree_interface_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_windowmanager_types.h"
#include "DNA_xr_types.h"
#include "BLI_listbase_iterator.hh"
#include "BLI_string.h"
#include "BLI_string_utf8.h"
#include "BLI_string_utils.hh"
#include "BLI_sys_types.h"
#include "BKE_anim_visualization.h"
#include "BKE_animsys.h"
#include "BKE_attribute.hh"
#include "BKE_colortools.hh"
#include "BKE_curves.hh"
#include "BKE_idprop.hh"
#include "BKE_layer.hh"
#include "BKE_lib_id.hh"
#include "BKE_lib_override.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_report.hh"
#include "SEQ_effects.hh"
#include "SEQ_iterator.hh"
#include "SEQ_sequencer.hh"
#include "BLO_read_write.hh"
#include "readfile.hh"
#include "versioning_common.hh"
// #include "CLG_log.h"
namespace blender {
// static CLG_LogRef LOG = {"blend.doversion"};
static void version_geometry_nodes_properties(FileData &fd,
Main &bmain,
Object &object,
NodesModifierData &nmd)
{
const IDProperty *old_props = nmd.settings_legacy.properties;
if (!old_props) {
/* Versioning has already been done, this check makes the function idempotent. */
return;
}
if (!nmd.node_group) {
IDP_FreeProperty(nmd.settings_legacy.properties);
nmd.settings_legacy.properties = nullptr;
BLO_reportf_wrap(fd.reports,
RPT_WARNING,
"Modifier '%s' from Object '%s' is missing its Geometry Node Group, its "
"settings will be lost (reset to default).",
nmd.modifier.name,
BKE_id_name(object.id));
return;
}
if (ID_MISSING(&nmd.node_group->id)) {
/* Keeping the old idproperties is not an option, and not really useful, since if the
* blend-file is saved in this current state, it won't be re-versioned here later anyway.
*
* Furthermore, the whole remaining part of the code expects this to be nullptr, and keeping it
* at runtime actually causes weird issues in depsgraph nodes building phase.
*
* So all in all, it's simpler and safer to also just lose these values here - if file is not
* saved in this state, next loading will do the versioning if the node-group is available
* again, otherwise that data is lost.
*/
IDP_FreeProperty(nmd.settings_legacy.properties);
nmd.settings_legacy.properties = nullptr;
BLO_reportf_wrap(
fd.reports,
RPT_WARNING,
"Modifier '%s' from Object '%s' is using a missing linked Geometry Node Group, its "
"settings will be lost (reset to default) if the file is saved in this state.",
nmd.modifier.name,
BKE_id_name(object.id));
return;
}
const bNodeTree &ntree = *nmd.node_group;
ntree.ensure_interface_cache();
IDProperty *system_props = bke::idprop::create_group("NodesModifierProperties").release();
IDProperty *inputs = bke::idprop::create_group("inputs").release();
IDP_AddToGroup(system_props, inputs);
const std::string inputs_path_prefix = fmt::format("modifiers[\"{}\"]", nmd.modifier.name);
for (const bNodeTreeInterfaceSocket *input : ntree.interface_inputs()) {
const StringRefNull identifier = input->identifier;
IDProperty *old_value_prop = IDP_GetPropertyFromGroup(old_props, identifier);
if (!old_value_prop) {
continue;
}
IDProperty *group = bke::idprop::create_group(identifier).release();
IDP_AddToGroup(inputs, group);
if (input->flag & NODE_INTERFACE_SOCKET_LAYER_SELECTION) {
IDP_AddToGroup(
group, bke::idprop::create("type", int(nodes::GeometryNodesInputType::Layer)).release());
const StringRefNull layer_name = [&]() {
const IDProperty *layer_name = IDP_GetPropertyTypeFromGroup(
old_props, identifier, IDP_STRING);
if (layer_name) {
return StringRefNull(IDP_string_get(layer_name));
}
return StringRefNull();
}();
IDP_AddToGroup(group, bke::idprop::create("layer_name", layer_name).release());
continue;
}
IDProperty *new_value_prop = IDP_CopyProperty(old_value_prop);
STRNCPY(new_value_prop->name, "value");
IDP_AddToGroup(group, new_value_prop);
const std::string old_value_path = fmt::format("[\"{}\"]", identifier);
const std::string new_value_path = fmt::format(".properties.inputs.{}.value", identifier);
BKE_animdata_fix_paths_rename_all_ex(&bmain,
&object.id,
inputs_path_prefix.c_str(),
old_value_path.c_str(),
new_value_path.c_str(),
0,
0,
false,
false);
if (IDOverrideLibrary *override_library = object.id.override_library) {
for (IDOverrideLibraryProperty &prop : override_library->properties) {
const StringRef path = prop.rna_path;
const int64_t i = path.find(inputs_path_prefix);
if (i == StringRef::not_found) {
continue;
}
if (path.drop_known_prefix(inputs_path_prefix) != old_value_path) {
continue;
}
MEM_delete(prop.rna_path);
prop.rna_path = BLI_sprintfN("%s%s", inputs_path_prefix.c_str(), new_value_path.c_str());
}
}
bool use_attribute = false;
if (const IDProperty *use_attribute_prop = IDP_GetPropertyFromGroup(
old_props, identifier + "_use_attribute"))
{
/* This property changed to an enum property and animation is not versioned. */
if (use_attribute_prop->type == IDP_INT) {
use_attribute = bool(IDP_int_get(use_attribute_prop));
}
else if (use_attribute_prop->type == IDP_BOOLEAN) {
use_attribute = bool(IDP_bool_get(use_attribute_prop));
}
}
const auto input_type = use_attribute ? nodes::GeometryNodesInputType::Attribute :
nodes::GeometryNodesInputType::Value;
IDP_AddToGroup(group, bke::idprop::create("type", int(input_type)).release());
const StringRefNull attribute_name = [&]() {
const IDProperty *attribute_name = IDP_GetPropertyTypeFromGroup(
old_props, identifier + "_attribute_name", IDP_STRING);
if (attribute_name) {
return StringRefNull(IDP_string_get(attribute_name));
}
return StringRefNull();
}();
IDP_AddToGroup(group, bke::idprop::create("attribute_name", attribute_name).release());
}
IDProperty *outputs = bke::idprop::create_group("outputs").release();
IDP_AddToGroup(system_props, outputs);
for (const bNodeTreeInterfaceSocket *output : ntree.interface_outputs()) {
const StringRef identifier = output->identifier;
IDProperty *old_name_prop = IDP_GetPropertyTypeFromGroup(
old_props, identifier + "_attribute_name", IDP_STRING);
if (!old_name_prop) {
continue;
}
IDProperty *group = bke::idprop::create_group(identifier).release();
IDP_AddToGroup(outputs, group);
IDProperty *new_value_prop = IDP_CopyProperty(old_name_prop);
STRNCPY(new_value_prop->name, "attribute_name");
IDP_AddToGroup(group, new_value_prop);
}
if (nmd.modifier.system_properties) {
IDP_FreeProperty(nmd.modifier.system_properties);
}
nmd.modifier.system_properties = system_props;
IDP_FreeProperty(nmd.settings_legacy.properties);
nmd.settings_legacy.properties = nullptr;
}
static void sanitize_node_tree_interface_socket_identifiers(bNodeTree &node_tree)
{
node_tree.ensure_interface_cache();
Set<StringRef> all_identifiers;
Map<std::string, StringRefNull> identifier_map;
for (bNodeTreeInterfaceItem *item : node_tree.interface_items()) {
if (item->item_type == NodeTreeInterfaceItemType::Panel) {
continue;
}
auto &socket = *bke::node_interface::get_item_as<bNodeTreeInterfaceSocket>(item);
/* Socket identifiers are required to be valid RNA identifiers and unique. */
if (!RNA_validate_identifier(socket.identifier, true)) {
std::string prev_identifier(socket.identifier);
RNA_identifier_sanitize(socket.identifier, true);
if (all_identifiers.contains(socket.identifier)) {
std::string new_identifier = BLI_uniquename_cb(
[&](StringRef name) { return all_identifiers.contains(name); },
'_',
socket.identifier);
MEM_SAFE_DELETE(socket.identifier);
socket.identifier = BLI_strdup(new_identifier.c_str());
}
identifier_map.add(std::move(prev_identifier), socket.identifier);
}
all_identifiers.add(socket.identifier);
}
/* Rename all the node socket identifiers that got changed in the interface. */
if (!identifier_map.is_empty()) {
for (bNode &node : node_tree.nodes) {
if (!(node.is_group_input() || node.is_group_output())) {
continue;
}
ListBaseT<bNodeSocket> sockets = node.is_group_output() ? node.inputs : node.outputs;
for (bNodeSocket &socket : sockets) {
if (identifier_map.contains(socket.identifier)) {
version_node_socket_identifier_set(socket, identifier_map.lookup(socket.identifier));
}
}
}
}
}
/* Saving file extension is now a property of the File Output node. So inherit this
* setting from the active scene to restore the old behavior.
* Note: One limitation is that node groups containing file outputs that are not part of any
* scene are not affected by versioning. */
static void do_version_file_output_use_file_extension_recursive(bNodeTree &node_tree,
const Scene &scene)
{
for (bNode &node : node_tree.nodes) {
if (node.type_legacy == CMP_NODE_OUTPUT_FILE) {
NodeCompositorFileOutput *data = static_cast<NodeCompositorFileOutput *>(node.storage);
data->use_file_extension = (scene.r.scemode & R_EXTENSION) != 0;
}
else if (node.type_legacy == NODE_GROUP) {
bNodeTree *ngroup = id_cast<bNodeTree *>(node.id);
if (ngroup) {
do_version_file_output_use_file_extension_recursive(*ngroup, scene);
}
}
}
}
static void version_clear_strip_linear_modifier_flag(Main &bmain)
{
for (Scene &scene : bmain.scenes) {
Editing *ed = seq::editing_get(&scene);
if (ed != nullptr) {
seq::foreach_strip(&ed->seqbase, [&](Strip *strip) {
constexpr eStripFlag flag_linear_modifiers = eStripFlag(1 << 23);
strip->flag &= ~flag_linear_modifiers;
return true;
});
}
}
}
static void version_text_strip_space_line(Main &bmain)
{
for (Scene &scene : bmain.scenes) {
Editing *ed = seq::editing_get(&scene);
if (ed == nullptr) {
continue;
}
seq::foreach_strip(&ed->seqbase, [&](Strip *strip) {
if (strip->type == STRIP_TYPE_TEXT && strip->effectdata != nullptr) {
TextVars *data = static_cast<TextVars *>(strip->effectdata);
data->space_line = 1.0f;
}
return true;
});
}
}
static void version_compositor_effect_initialized(Main &bmain)
{
/* A file with compositor effects that was saved, opened in
* previous version and saved there, would have lost the
* compositor effect data since earlier versions would not
* write it. Ensure the effect data is not null. */
for (Scene &scene : bmain.scenes) {
if (scene.ed) {
seq::foreach_strip(&scene.ed->seqbase, [&](Strip *strip) {
if (strip->type == STRIP_TYPE_COMPOSITOR) {
seq::effect_ensure_initialized(strip);
}
return true;
});
}
}
}
static void version_text_strip_abs_space_line(Main &bmain)
{
for (Scene &scene : bmain.scenes) {
Editing *ed = seq::editing_get(&scene);
if (ed == nullptr) {
continue;
}
seq::foreach_strip(&ed->seqbase, [&](Strip *strip) {
if (strip->type == STRIP_TYPE_TEXT && strip->effectdata != nullptr) {
TextVars *data = static_cast<TextVars *>(strip->effectdata);
data->abs_space_line = 60.0f;
data->flag &= ~SEQ_TEXT_USE_ABSOLUTE_LINE_SPACING;
}
return true;
});
}
}
static void fix_single_point_curves_custom_knots(Main *bmain)
{
/* Fix corrupted flagu/flagv values created by older versions of the Curve Pen tool.
* The tool could create loose vertices with invalid flag values (e.g. -2), where
* CU_NURB_CUSTOM was set alongside other flags and knotsu/knotsv was left null,
* causing a crash when opening these files in newer versions. */
for (Curve &cu : bmain->curves) {
for (Nurb *nu = static_cast<Nurb *>(cu.nurb.first); nu != nullptr; nu = nu->next) {
if (nu->knotsu == nullptr && (nu->flagu & CU_NURB_CUSTOM)) {
nu->flagu &= (CU_NURB_CYCLIC | CU_NURB_BEZIER | CU_NURB_ENDPOINT);
}
if (nu->knotsv == nullptr && (nu->flagv & CU_NURB_CUSTOM)) {
nu->flagv &= (CU_NURB_CYCLIC | CU_NURB_BEZIER | CU_NURB_ENDPOINT);
}
}
}
}
static void version_strip_modifier_show_preview_flag(Main &bmain)
{
for (Scene &scene : bmain.scenes) {
Editing *ed = seq::editing_get(&scene);
if (ed == nullptr) {
continue;
}
seq::foreach_strip(&ed->seqbase, [&](Strip *strip) {
for (StripModifierData &smd : strip->modifiers) {
if ((smd.flag & STRIP_MODIFIER_FLAG_MUTE) == 0) {
smd.flag |= STRIP_MODIFIER_FLAG_SHOW_PREVIEW;
}
}
return true;
});
}
}
static void version_scene_strip_view_layer_name(Main &bmain)
{
for (const Scene &scene : bmain.scenes) {
Editing *ed = seq::editing_get(&scene);
if (ed == nullptr) {
continue;
}
seq::foreach_strip(&ed->seqbase, [&](Strip *strip) {
if (strip->type != STRIP_TYPE_SCENE || strip->scene == nullptr) {
return true;
}
strip->scene_view_layer_name = BLI_strdup(BKE_view_layer_default_render(strip->scene)->name);
return true;
});
}
}
/* Compositor node trees with an image input and an image output can likely be used as strip
* modifiers. */
static void enable_compositor_nodes_is_strip_modifier(Main &bmain)
{
for (bNodeTree &group : bmain.nodetrees) {
if (group.type != NTREE_COMPOSIT) {
continue;
}
bool has_image_input = false;
bool has_image_output = false;
group.tree_interface.foreach_item([&](const bNodeTreeInterfaceItem &item) {
if (item.item_type != NodeTreeInterfaceItemType::Socket) {
/* Continue. */
return true;
}
const auto &socket = reinterpret_cast<const bNodeTreeInterfaceSocket &>(item);
if (socket.flag & NODE_INTERFACE_SOCKET_INPUT) {
has_image_input = has_image_input || STREQ(socket.socket_type, "NodeSocketColor");
/* Continue. */
return true;
}
if (socket.flag & NODE_INTERFACE_SOCKET_OUTPUT) {
has_image_output = has_image_output || STREQ(socket.socket_type, "NodeSocketColor");
/* Continue. */
return true;
}
/* Break. */
return false;
});
if (has_image_input && has_image_output) {
if (!group.compositor_node_asset_traits) {
group.compositor_node_asset_traits = MEM_new<CompositorNodeAssetTraits>(__func__);
}
group.compositor_node_asset_traits->flag |= COMPOSIT_NODE_ASSET_STRIP_MODIFIER;
bke::node_update_asset_metadata(group);
}
}
}
static void versioning_replace_legacy_compositor_switch_node(bNodeTree *node_tree)
{
version_node_input_socket_name(node_tree, CMP_NODE_SWITCH, "On", "True");
version_node_input_socket_name(node_tree, CMP_NODE_SWITCH, "Off", "False");
version_node_output_socket_name(node_tree, CMP_NODE_SWITCH, "Image", "Output");
for (bNode &node : node_tree->nodes) {
if (node.type_legacy == CMP_NODE_SWITCH) {
node.type_legacy = GEO_NODE_SWITCH;
NodeSwitch *storage = MEM_new<NodeSwitch>(__func__);
storage->input_type = SOCK_RGBA;
STRNCPY_UTF8(node.idname, "GeometryNodeSwitch");
node.storage = storage;
}
}
}
void do_versions_after_linking_520(FileData *fd, Main *bmain)
{
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 2)) {
for (Scene &scene : bmain->scenes) {
bNodeTree *node_tree = version_get_scene_compositor_node_tree(bmain, &scene);
if (node_tree == nullptr) {
continue;
}
do_version_file_output_use_file_extension_recursive(*node_tree, scene);
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 16)) {
for (Object &object : bmain->objects) {
for (ModifierData &md : object.modifiers) {
if (md.type == eModifierType_Nodes) {
version_geometry_nodes_properties(
*fd, *bmain, object, reinterpret_cast<NodesModifierData &>(md));
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 27)) {
version_scene_strip_view_layer_name(*bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 36)) {
/* Shift animation data to accommodate the new thin wall input. */
version_node_socket_index_animdata(bmain, NTREE_SHADER, SH_NODE_BSDF_PRINCIPLED, 5, 1, 31);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 44)) {
/* We have to remove the invalid motion paths. Re-baking into clip space on file load would be
* very expensive. */
for (Object &object : bmain->objects) {
if (object.mpath && (object.avs.path_bakeflag & MOTIONPATH_BAKE_CAMERA_SPACE)) {
animviz_free_motionpath(object.mpath);
object.mpath = nullptr;
object.avs.path_bakeflag &= ~MOTIONPATH_BAKE_HAS_PATHS;
}
if (object.pose && (object.pose->avs.path_bakeflag & MOTIONPATH_BAKE_CAMERA_SPACE)) {
for (bPoseChannel &pose_bone : object.pose->chanbase) {
if (pose_bone.mpath) {
animviz_free_motionpath(pose_bone.mpath);
pose_bone.mpath = nullptr;
}
}
object.pose->avs.path_bakeflag &= ~MOTIONPATH_BAKE_HAS_PATHS;
}
}
}
/**
* 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 version_solid_color_width_height_defaults(Main &bmain)
{
for (Scene &scene : bmain.scenes) {
Editing *ed = seq::editing_get(&scene);
if (ed == nullptr) {
continue;
}
seq::foreach_strip(&ed->seqbase, [&](Strip *strip) {
if (strip->type == STRIP_TYPE_COLOR && strip->effectdata != nullptr) {
SolidColorVars *data = static_cast<SolidColorVars *>(strip->effectdata);
data->width = scene.r.xsch;
data->height = scene.r.ysch;
}
return true;
});
}
}
void blo_do_versions_520(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
{
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 1)) {
for (Scene &scene : bmain->scenes) {
scene.r.mode |= R_SAVE_OUTPUT;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 4)) {
for (Brush &brush : bmain->brushes) {
if (brush.gpencil_settings != nullptr) {
brush.blend = 0;
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 5)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id_owner) {
for (bNode &node : node_tree->nodes) {
if (node.type_legacy == FN_NODE_INPUT_VECTOR) {
auto &data = *static_cast<NodeInputVector *>(node.storage);
data.vector[3] = 0.0f;
data.dimensions = 3;
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 6)) {
for (Scene &scene : bmain->scenes) {
SequencerToolSettings *sequencer_tool_settings = seq::tool_settings_ensure(&scene);
sequencer_tool_settings->snap_flag |= SEQ_SNAP_TO_ALL_CHANNEL_STRIPS;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 7)) {
for (Scene &scene : bmain->scenes) {
scene.r.anisotropic_filter = 2;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 9)) {
for (Mesh &mesh : bmain->meshes) {
bke::mesh_freestyle_marks_to_generic(mesh);
}
}
/* Convert H.264 codec value for older files (2.79), see #155775. */
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 10)) {
for (Scene &scene : bmain->scenes) {
if (scene.r.ffcodecdata.codec == 28) {
scene.r.ffcodecdata.codec = 27;
}
}
}
/* Disable "unified" flags for Grease Pencil Draw mode. */
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 11)) {
for (Scene &scene : bmain->scenes) {
if (scene.toolsettings->gp_paint) {
UnifiedPaintSettings &settings =
scene.toolsettings->gp_paint->paint.unified_paint_settings;
settings.flag &= ~(UNIFIED_PAINT_SIZE | UNIFIED_PAINT_ALPHA | UNIFIED_PAINT_COLOR);
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 12)) {
for (bScreen &screen : bmain->screens) {
for (ScrArea &area : screen.areabase) {
for (SpaceLink &space : area.spacedata) {
if (space.spacetype == SPACE_NODE) {
SpaceNode *space_node = reinterpret_cast<SpaceNode *>(&space);
space_node->overlay.flag |= SN_OVERLAY_SHOW_RENDER_REGION;
space_node->overlay.passepartout_alpha = 0.5f;
}
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 13)) {
version_clear_strip_linear_modifier_flag(*bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 14)) {
fix_single_point_curves_custom_knots(bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 15)) {
for (Scene &scene : bmain->scenes) {
scene.r.scemode |= R_USE_TEXTURE_CACHE;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 16)) {
for (Brush &brush : bmain->brushes) {
if (brush.gpencil_settings != nullptr) {
brush.gpencil_settings->curve_type = CURVE_TYPE_POLY;
brush.gpencil_settings->conversion_threshold = 0.001f;
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 17)) {
for (Material &materials : bmain->materials) {
if (materials.gp_style != nullptr) {
materials.gp_style->placement_mode = GP_MATERIAL_PLACEMENT_COUNT;
materials.gp_style->placement_count = 1;
materials.gp_style->placement_density = 10.0f;
materials.gp_style->placement_radius_spacing = 100.0f;
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 18)) {
for (Scene &scene : bmain->scenes) {
if (scene.toolsettings->sculpt) {
Sculpt &sculpt = *scene.toolsettings->sculpt;
MeshAutomaskingSettings *settings = MEM_new<MeshAutomaskingSettings>(__func__);
settings->flags = sculpt.automasking_flags;
settings->boundary_edges_propagation_steps =
sculpt.automasking_boundary_edges_propagation_steps;
settings->cavity_blur_steps = sculpt.automasking_cavity_blur_steps;
settings->cavity_factor = sculpt.automasking_cavity_factor;
settings->start_normal_limit = sculpt.automasking_start_normal_limit;
settings->start_normal_falloff = sculpt.automasking_start_normal_falloff;
settings->view_normal_limit = sculpt.automasking_view_normal_limit;
settings->view_normal_falloff = sculpt.automasking_view_normal_falloff;
settings->cavity_curve = BKE_curvemapping_copy(sculpt.automasking_cavity_curve);
settings->cavity_curve_op = BKE_curvemapping_copy(sculpt.automasking_cavity_curve_op);
scene.toolsettings->sculpt->paint.mesh_automasking_settings = settings;
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 19)) {
for (bNodeTree &tree : bmain->nodetrees) {
sanitize_node_tree_interface_socket_identifiers(tree);
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 20)) {
for (Brush &brush : bmain->brushes) {
if (brush.ob_mode != OB_MODE_SCULPT) {
continue;
}
brush.mesh_automasking_settings = MEM_new<MeshAutomaskingSettings>(__func__);
brush.mesh_automasking_settings->flags = brush.automasking_flags;
brush.mesh_automasking_settings->boundary_edges_propagation_steps =
brush.automasking_boundary_edges_propagation_steps;
brush.mesh_automasking_settings->cavity_blur_steps = brush.automasking_cavity_blur_steps;
brush.mesh_automasking_settings->cavity_factor = brush.automasking_cavity_factor;
brush.mesh_automasking_settings->start_normal_falloff =
brush.automasking_start_normal_falloff;
brush.mesh_automasking_settings->start_normal_limit = brush.automasking_start_normal_limit;
brush.mesh_automasking_settings->view_normal_falloff = brush.automasking_view_normal_falloff;
brush.mesh_automasking_settings->view_normal_limit = brush.automasking_view_normal_limit;
brush.mesh_automasking_settings->cavity_curve = BKE_curvemapping_copy(
brush.automasking_cavity_curve);
brush.mesh_automasking_settings->cavity_curve_op = nullptr;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 21)) {
for (Material &materials : bmain->materials) {
if (materials.gp_style != nullptr) {
materials.gp_style->random_size_factor = 0.0f;
materials.gp_style->random_strength_factor = 0.0f;
materials.gp_style->random_rotation_factor = 0.0f;
materials.gp_style->random_hue_factor = 0.0f;
materials.gp_style->random_saturation_factor = 0.0f;
materials.gp_style->random_value_factor = 0.0f;
materials.gp_style->random_noise_scale = 1.0f;
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 22)) {
version_strip_modifier_show_preview_flag(*bmain);
}
/* The ID member of the Viewer node is no longer initialized to the Viewer Image, so clear that
* member. */
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 23)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
for (bNode &node : node_tree->nodes) {
if (node.type_legacy == CMP_NODE_VIEWER) {
node.id = nullptr;
}
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 24)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_SHADER) {
for (bNode &node : node_tree->nodes) {
if (node.type_legacy == SH_NODE_RAYCAST && node.storage == nullptr) {
node.storage = MEM_new<NodeShaderRaycast>(__func__);
}
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 25)) {
for (bScreen &screen : bmain->screens) {
for (ScrArea &area : screen.areabase) {
for (SpaceLink &space : area.spacedata) {
if (space.spacetype == SPACE_OUTLINER) {
SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(&space);
space_outliner->flag |= SO_SCROLL_TO_ACTIVE;
}
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 26)) {
FOREACH_NODETREE_BEGIN (bmain, tree, id) {
if (tree->type != NTREE_GEOMETRY) {
continue;
}
for (bNode &node : tree->nodes) {
switch (node.type_legacy) {
case FN_NODE_COMPARE:
case FN_NODE_RANDOM_VALUE: {
version_socket_identifier_suffixes_for_dynamic_types(node.inputs, "_");
version_socket_identifier_suffixes_for_dynamic_types(node.outputs, "_");
break;
}
}
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 28)) {
version_text_strip_space_line(*bmain);
version_compositor_effect_initialized(*bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 29)) {
for (bScreen &screen : bmain->screens) {
for (ScrArea &area : screen.areabase) {
for (SpaceLink &sl : area.spacedata) {
if (sl.spacetype != SPACE_SEQ) {
continue;
}
ListBaseT<ARegion> *regionbase = (&sl == area.spacedata.first) ? &area.regionbase :
&sl.regionbase;
ARegion *scrubbing_region = do_versions_add_region_if_not_found(
regionbase, RGN_TYPE_SCRUBBING, "Scrubbing Region", RGN_TYPE_FOOTER);
if (scrubbing_region) {
scrubbing_region->alignment = RGN_ALIGN_BOTTOM | RGN_STACK_ON_PREV |
RGN_ALIGN_HIDE_WITH_PREV;
}
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 30)) {
enable_compositor_nodes_is_strip_modifier(*bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 31)) {
for (Mesh &mesh : bmain->meshes) {
if (mesh.attributes().contains(".uv_seam")) {
mesh.attributes_for_write().rename(".uv_seam", "uv_seam");
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 34)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
if (ntree->type == NTREE_COMPOSIT) {
versioning_replace_legacy_compositor_switch_node(ntree);
}
}
FOREACH_NODETREE_END;
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 35)) {
for (Object &object : bmain->objects) {
object.parent_bone_head_tail_factor = 1.0;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 37)) {
version_text_strip_abs_space_line(*bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 38)) {
for (Brush &brush : bmain->brushes) {
if (brush.gpencil_settings != nullptr) {
brush.gpencil_settings->fill_gap_factor = 0.4f;
brush.gpencil_settings->flag |= GP_BRUSH_FILL_INTERNAL_GAPS;
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 39)) {
for (bScreen &screen : bmain->screens) {
for (ScrArea &area : screen.areabase) {
for (SpaceLink &sl : area.spacedata) {
if (sl.spacetype == SPACE_SEQ) {
SpaceSeq *sseq = reinterpret_cast<SpaceSeq *>(&sl);
sseq->preview_overlay.flag |= SEQ_PREVIEW_SHOW_COMPOSITION_GUIDES;
float default_col[4] = {0.5f, 0.5f, 0.5f, 1.0f};
copy_v4_v4(sseq->preview_overlay.composition_guide_color, default_col);
}
}
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 40)) {
for (wmWindowManager &wm : bmain->wm) {
wm.xr.session_settings.viewfinder_enabled = false;
wm.xr.session_settings.viewfinder_crosshair_enabled = true;
wm.xr.session_settings.viewfinder_hand = XR_VIEWFINDER_HAND_RIGHT;
wm.xr.session_settings.viewfinder_scale = 1.0f;
wm.xr.session_settings.viewfinder_passepartout_overscan = 0.5f;
wm.xr.session_settings.viewfinder_passepartout_opacity = 0.5f;
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 41)) {
version_solid_color_width_height_defaults(*bmain);
}
/* Fix the fact that previously, making a linked data local and/or clearing a liboverride would
* not properly flag some sub-data like modifiers or constraints as local. */
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 43)) {
for (ID &id : MainAllIDsIterator{*bmain}) {
if (!ID_IS_LINKED(&id) && !ID_IS_OVERRIDE_LIBRARY(&id)) {
BKE_lib_override_flag_subdata_local(id);
}
}
}
/* The compositor previously did not support default inputs for group nodes, but some built-in
* nodes had the position field default type for some inputs, so node groups would gain it as a
* default type through some operators. Later, the default inputs were supported for group nodes,
* though position field were not supported in the compositor, so it would assert. To fix this,
* we reset any position field default input to the default value. */
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 502, 44)) {
FOREACH_NODETREE_BEGIN (bmain, node_tree, id) {
if (node_tree->type == NTREE_COMPOSIT) {
node_tree->ensure_interface_cache();
for (bNodeTreeInterfaceSocket *input : node_tree->interface_inputs()) {
if (input->default_input == NODE_DEFAULT_INPUT_POSITION_FIELD) {
input->default_input = NODE_DEFAULT_INPUT_VALUE;
}
}
}
}
FOREACH_NODETREE_END;
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
*
* \note Keep this message at the bottom of the function.
*/
}
} // namespace blender

View File

@@ -0,0 +1,884 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blenloader
*/
/* allow readfile to use deprecated functionality */
#define DNA_DEPRECATED_ALLOW
#include <cstring>
#include "DNA_layer_types.h"
#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
#include "DNA_screen_types.h"
#include "DNA_sequence_types.h"
#include "BLI_listbase.h"
#include "BLI_map.hh"
#include "BLI_string.h"
#include "BLI_string_ref.hh"
#include "BLI_string_utf8.h"
#include "BKE_animsys.h"
#include "BKE_grease_pencil_legacy_convert.hh"
#include "BKE_idprop.hh"
#include "BKE_lib_id.hh"
#include "BKE_lib_override.hh"
#include "BKE_library.hh"
#include "BKE_main.hh"
#include "BKE_main_namemap.hh"
#include "BKE_mesh_legacy_convert.hh"
#include "BKE_node.hh"
#include "BKE_node_legacy_types.hh"
#include "BKE_node_runtime.hh"
#include "BKE_node_tree_update.hh"
#include "BKE_report.hh"
#include "BKE_screen.hh"
#include "ANIM_versioning.hh"
#include "NOD_socket.hh"
#include "BLT_translation.hh"
#include "SEQ_sequencer.hh"
#include "MEM_guardedalloc.h"
#include "BLO_readfile.hh"
#include "readfile.hh"
#include "versioning_common.hh"
namespace blender {
short do_versions_new_to_old_idcode_get(const short id_code_new)
{
switch (id_code_new) {
case ID_GP:
/* ID_GD_LEGACY (Grease Pencil v2) is now converted to ID_GP (Grease Pencil v3). */
return ID_GD_LEGACY;
default:
return ID_LINK_PLACEHOLDER;
}
}
ARegion *do_versions_add_region_if_not_found(ListBaseT<ARegion> *regionbase,
int region_type,
const char * /*allocname*/,
int link_after_region_type)
{
ARegion *link_after_region = nullptr;
for (ARegion &region : *regionbase) {
if (region.regiontype == region_type) {
return nullptr;
}
if (region.regiontype == link_after_region_type) {
link_after_region = &region;
}
}
ARegion *new_region = BKE_area_region_new();
new_region->regiontype = eRegion_Type(region_type);
BLI_insertlinkafter(regionbase, link_after_region, new_region);
return new_region;
}
ARegion *do_versions_ensure_region(ListBaseT<ARegion> *regionbase,
int region_type,
const char * /*allocname*/,
int link_after_region_type)
{
ARegion *link_after_region = nullptr;
for (ARegion &region : *regionbase) {
if (region.regiontype == region_type) {
return &region;
}
if (region.regiontype == link_after_region_type) {
link_after_region = &region;
}
}
ARegion *new_region = BKE_area_region_new();
new_region->regiontype = eRegion_Type(region_type);
BLI_insertlinkafter(regionbase, link_after_region, new_region);
return new_region;
}
ID *do_versions_rename_id(Main *bmain,
const short id_type,
const char *name_src,
const char *name_dst)
{
/* We can ignore libraries */
ListBaseT<ID> *lb = which_libbase(bmain, id_type);
ID *id = nullptr;
for (ID &idtest : *lb) {
if (!ID_IS_LINKED(&idtest)) {
if (STREQ(idtest.name + 2, name_src)) {
id = &idtest;
}
if (STREQ(idtest.name + 2, name_dst)) {
return nullptr;
}
}
}
if (id != nullptr) {
BKE_libblock_rename(*bmain, *id, name_dst);
}
return id;
}
static void change_node_socket_name(ListBaseT<bNodeSocket> *sockets,
const char *old_name,
const char *new_name)
{
for (bNodeSocket &socket : *sockets) {
if (STREQ(socket.name, old_name)) {
STRNCPY_UTF8(socket.name, new_name);
}
if (STREQ(socket.identifier, old_name)) {
version_node_socket_identifier_set(socket, new_name);
}
}
}
bool version_node_socket_is_used(bNodeSocket *sock)
{
BLI_assert(sock != nullptr);
return sock->flag & SOCK_IS_LINKED;
}
void version_node_socket_id_delim(bNodeSocket *socket)
{
StringRef name = socket->name;
StringRef id = socket->identifier;
if (!id.startswith(name)) {
/* We only need to affect the case where the identifier starts with the name. */
return;
}
StringRef id_number = id.drop_known_prefix(name);
if (id_number.is_empty()) {
/* The name was already unique, and didn't need numbers at the end for the id. */
return;
}
if (id_number.startswith(".")) {
socket->identifier[name.size()] = '_';
socket->runtime->identifier_ustr = UString(socket->identifier);
}
}
void version_node_socket_identifier_set(bNodeSocket &socket, const StringRefNull identifier)
{
STRNCPY_UTF8(socket.identifier, identifier.c_str());
socket.runtime->identifier_ustr = UString(socket.identifier);
}
void version_node_socket_name(bNodeTree *ntree,
const int node_type,
const char *old_name,
const char *new_name)
{
for (bNode *node : ntree->all_nodes()) {
if (node->type_legacy == node_type) {
change_node_socket_name(&node->inputs, old_name, new_name);
change_node_socket_name(&node->outputs, old_name, new_name);
}
}
}
void version_node_input_socket_name(bNodeTree *ntree,
const int node_type,
const char *old_name,
const char *new_name)
{
for (bNode *node : ntree->all_nodes()) {
if (node->type_legacy == node_type) {
change_node_socket_name(&node->inputs, old_name, new_name);
}
}
}
void version_node_output_socket_name(bNodeTree *ntree,
const int node_type,
const char *old_name,
const char *new_name)
{
for (bNode *node : ntree->all_nodes()) {
if (node->type_legacy == node_type) {
change_node_socket_name(&node->outputs, old_name, new_name);
}
}
}
StringRef legacy_socket_idname_to_socket_type(StringRef idname)
{
using string_pair = std::pair<const char *, const char *>;
static const string_pair subtypes_map[] = {{"NodeSocketFloatUnsigned", "NodeSocketFloat"},
{"NodeSocketFloatPercentage", "NodeSocketFloat"},
{"NodeSocketFloatFactor", "NodeSocketFloat"},
{"NodeSocketFloatAngle", "NodeSocketFloat"},
{"NodeSocketFloatTime", "NodeSocketFloat"},
{"NodeSocketFloatTimeAbsolute", "NodeSocketFloat"},
{"NodeSocketFloatDistance", "NodeSocketFloat"},
{"NodeSocketIntUnsigned", "NodeSocketInt"},
{"NodeSocketIntPercentage", "NodeSocketInt"},
{"NodeSocketIntFactor", "NodeSocketInt"},
{"NodeSocketVectorTranslation", "NodeSocketVector"},
{"NodeSocketVectorDirection", "NodeSocketVector"},
{"NodeSocketVectorVelocity", "NodeSocketVector"},
{"NodeSocketVectorAcceleration", "NodeSocketVector"},
{"NodeSocketVectorEuler", "NodeSocketVector"},
{"NodeSocketVectorXYZ", "NodeSocketVector"}};
for (const string_pair &pair : subtypes_map) {
if (pair.first == idname) {
return pair.second;
}
}
/* Unchanged socket idname. */
return idname;
}
bNode &version_node_add_empty(bNodeTree &ntree, const char *idname)
{
bke::bNodeType *ntype = bke::node_type_find(UString(idname));
bNode *node = MEM_new<bNode>(__func__);
node->runtime = MEM_new<bke::bNodeRuntime>(__func__);
BLI_addtail(&ntree.nodes, node);
bke::node_unique_id(ntree, *node);
STRNCPY(node->idname, idname);
DATA_(ntype->ui_name).copy_utf8_truncated(node->name);
bke::node_unique_name(ntree, *node);
node->flag = NODE_SELECT | NODE_OPTIONS | NODE_INIT;
node->width = ntype->default_width;
node->height = ntype->height;
node->color[0] = node->color[1] = node->color[2] = 0.608;
node->type_legacy = ntype->type_legacy;
BKE_ntree_update_tag_node_new(&ntree, node);
return *node;
}
bNode &version_node_add_unknown(bNodeTree &ntree,
bke::bNodeType &ntype,
const char *idname,
const int16_t legacy_type,
const std::string &ui_name,
const std::string &ui_description,
const std::string &enum_name_legacy,
const short nclass,
const float width,
const float height,
const bool no_muting)
{
using namespace blender::bke;
ntype.idname = UString(idname);
ntype.type_legacy = legacy_type;
ntype.height = height;
ntype.default_width = width;
ntype.minheight = 30.0f;
ntype.maxheight = FLT_MAX;
ntype.ui_name = ui_name;
ntype.ui_description = ui_description;
ntype.enum_name_legacy = enum_name_legacy.c_str();
ntype.nclass = nclass;
ntype.no_muting = no_muting;
ntype.ui_name = ui_name;
bNode *node = MEM_new<bNode>(__func__);
node->runtime = MEM_new<bNodeRuntime>(__func__);
BLI_addtail(&ntree.nodes, node);
node_unique_id(ntree, *node);
node->typeinfo = &ntype;
STRNCPY(node->idname, idname);
DATA_(ntype.ui_name).copy_utf8_truncated(node->name);
node_unique_name(ntree, *node);
node->flag = NODE_SELECT | NODE_OPTIONS | NODE_INIT;
node->width = ntype.default_width;
node->height = ntype.height;
node->color[0] = node->color[1] = node->color[2] = 0.608f;
node->type_legacy = ntype.type_legacy;
BKE_ntree_update_tag_node_new(&ntree, node);
return *node;
}
void version_node_remove(bNodeTree &ntree, bNode &node)
{
bke::node_unlink_node(ntree, node);
bke::node_unlink_attached(&ntree, &node);
bke::node_free_node(&ntree, node);
bke::node_rebuild_id_vector(ntree);
}
bNodeSocket &version_node_add_socket(bNodeTree &ntree,
bNode &node,
const eNodeSocketInOut in_out,
const char *idname,
const char *identifier)
{
bke::bNodeSocketType *stype = bke::node_socket_type_find(idname);
BLI_assert(stype != nullptr);
bNodeSocket *socket = MEM_new<bNodeSocket>(__func__);
socket->runtime = MEM_new<bke::bNodeSocketRuntime>(__func__);
socket->in_out = in_out;
socket->limit = (in_out == SOCK_IN ? 1 : 0xFFF);
socket->type = stype->type;
STRNCPY_UTF8(socket->idname, idname);
STRNCPY_UTF8(socket->identifier, identifier);
socket->runtime->identifier_ustr = UString(socket->identifier);
STRNCPY_UTF8(socket->name, identifier);
if (in_out == SOCK_IN) {
BLI_addtail(&node.inputs, socket);
}
else {
BLI_addtail(&node.outputs, socket);
}
node_socket_init_default_value_data(stype->type, stype->subtype, &socket->default_value);
BKE_ntree_update_tag_socket_new(&ntree, socket);
return *socket;
}
bNodeLink &version_node_add_link(
bNodeTree &ntree, bNode &node_a, bNodeSocket &socket_a, bNode &node_b, bNodeSocket &socket_b)
{
BLI_assert(socket_a.in_out != socket_b.in_out);
if (socket_a.in_out == SOCK_IN) {
return version_node_add_link(ntree, node_b, socket_b, node_a, socket_a);
}
bNode &node_from = node_a;
bNodeSocket &socket_from = socket_a;
bNode &node_to = node_b;
bNodeSocket &socket_to = socket_b;
bNodeLink *link = MEM_new<bNodeLink>(__func__);
link->fromnode = &node_from;
link->fromsock = &socket_from;
link->tonode = &node_to;
link->tosock = &socket_to;
BLI_addtail(&ntree.links, link);
BKE_ntree_update_tag_link_added(&ntree, link);
return *link;
}
bool version_node_ensure_storage_or_invalidate(bNode &node)
{
/* Accept node if storage is valid. */
if (node.storage != nullptr) {
return true;
}
/* Invalidate the type identifiers to prevent invalid access where storage data is expected
* (#154086). */
bke::node_set_undefined_type(node);
return false;
}
bNodeSocket *version_node_add_socket_if_not_exist(bNodeTree *ntree,
bNode *node,
int in_out,
int type,
int subtype,
const char *identifier,
const char *name)
{
bNodeSocket *sock = bke::node_find_socket(*node, eNodeSocketInOut(in_out), UString(identifier));
if (sock != nullptr) {
return sock;
}
return bke::node_add_static_socket(
*ntree, *node, eNodeSocketInOut(in_out), type, subtype, identifier, name);
}
void version_node_tree_clear_interface(bNodeTree &ntree)
{
ntree.tree_interface.clear_items();
}
void version_node_id(bNodeTree *ntree, const int node_type, const char *new_name)
{
for (bNode *node : ntree->all_nodes()) {
if (node->type_legacy == node_type) {
if (!STREQ(node->idname, new_name)) {
STRNCPY(node->idname, new_name);
}
}
}
}
void version_node_socket_index_animdata(Main *bmain,
const int node_tree_type,
const int node_type,
const int socket_index_orig,
const int socket_index_offset,
const int total_number_of_sockets)
{
/* The for loop for the input ids is at the top level otherwise we lose the animation
* keyframe data. Not sure what causes that, so I (Sybren) moved the code here from
* versioning_290.cc as-is (structure-wise). */
for (int input_index = total_number_of_sockets - 1; input_index >= socket_index_orig;
input_index--)
{
FOREACH_NODETREE_BEGIN (bmain, ntree, owner_id) {
if (ntree->type != node_tree_type) {
continue;
}
for (bNode *node : ntree->all_nodes()) {
if (node->type_legacy != node_type) {
continue;
}
char node_name_escaped[sizeof(node->name) * 2];
BLI_str_escape(node_name_escaped, node->name, sizeof(node_name_escaped));
char *rna_path_prefix = BLI_sprintfN("nodes[\"%s\"].inputs", node_name_escaped);
const int new_index = input_index + socket_index_offset;
BKE_animdata_fix_paths_rename_all_ex(bmain,
owner_id,
rna_path_prefix,
nullptr,
nullptr,
input_index,
new_index,
/*verify_paths=*/false,
/*infix_is_name=*/true);
MEM_delete(rna_path_prefix);
}
}
FOREACH_NODETREE_END;
}
}
void version_socket_update_is_used(bNodeTree *ntree)
{
for (bNode *node : ntree->all_nodes()) {
for (bNodeSocket &socket : node->inputs) {
socket.flag &= ~SOCK_IS_LINKED;
}
for (bNodeSocket &socket : node->outputs) {
socket.flag &= ~SOCK_IS_LINKED;
}
}
for (bNodeLink &link : ntree->links) {
link.fromsock->flag |= SOCK_IS_LINKED;
link.tosock->flag |= SOCK_IS_LINKED;
}
}
ARegion *do_versions_add_region(int regiontype, const char * /*name*/)
{
ARegion *region = BKE_area_region_new();
region->regiontype = eRegion_Type(regiontype);
return region;
}
void node_tree_relink_with_socket_id_map(bNodeTree &ntree,
bNode &old_node,
bNode &new_node,
const Map<std::string, std::string> &map)
{
for (bNodeLink &link : ntree.links.items_mutable()) {
if (link.tonode == &old_node) {
bNodeSocket *old_socket = link.tosock;
if (old_socket->is_available()) {
if (const std::string *new_identifier = map.lookup_ptr_as(old_socket->identifier)) {
bNodeSocket *new_socket = bke::node_find_socket(
*&new_node, SOCK_IN, UString(*new_identifier));
link.tonode = &new_node;
link.tosock = new_socket;
old_socket->link = nullptr;
}
}
}
if (link.fromnode == &old_node) {
bNodeSocket *old_socket = link.fromsock;
if (old_socket->is_available()) {
if (const std::string *new_identifier = map.lookup_ptr_as(old_socket->identifier)) {
bNodeSocket *new_socket = bke::node_find_socket(
*&new_node, SOCK_OUT, UString(*new_identifier));
link.fromnode = &new_node;
link.fromsock = new_socket;
old_socket->link = nullptr;
}
}
}
}
}
static Vector<bNodeLink *> find_connected_links(bNodeTree *ntree, bNodeSocket *in_socket)
{
Vector<bNodeLink *> links;
for (bNodeLink &link : ntree->links) {
if (link.tosock == in_socket) {
links.append(&link);
}
}
return links;
}
void add_realize_instances_before_socket(bNodeTree *ntree,
bNode *node,
bNodeSocket *geometry_socket)
{
BLI_assert(geometry_socket->type == SOCK_GEOMETRY);
Vector<bNodeLink *> links = find_connected_links(ntree, geometry_socket);
for (bNodeLink *link : links) {
/* If the realize instances node is already before this socket, no need to continue. */
if (link->fromnode->type_legacy == GEO_NODE_REALIZE_INSTANCES) {
return;
}
bNode *realize_node = bke::node_add_static_node(nullptr, *ntree, GEO_NODE_REALIZE_INSTANCES);
realize_node->parent = node->parent;
realize_node->locx_legacy = node->locx_legacy - 100;
realize_node->locy_legacy = node->locy_legacy;
bke::node_add_link(*ntree,
*link->fromnode,
*link->fromsock,
*realize_node,
*static_cast<bNodeSocket *>(realize_node->inputs.first));
link->fromnode = realize_node;
link->fromsock = static_cast<bNodeSocket *>(realize_node->outputs.first);
}
}
float *version_cycles_node_socket_float_value(bNodeSocket *socket)
{
bNodeSocketValueFloat *socket_data = static_cast<bNodeSocketValueFloat *>(socket->default_value);
return &socket_data->value;
}
float *version_cycles_node_socket_rgba_value(bNodeSocket *socket)
{
bNodeSocketValueRGBA *socket_data = static_cast<bNodeSocketValueRGBA *>(socket->default_value);
return socket_data->value;
}
float *version_cycles_node_socket_vector_value(bNodeSocket *socket)
{
bNodeSocketValueVector *socket_data = static_cast<bNodeSocketValueVector *>(
socket->default_value);
return socket_data->value;
}
IDProperty *version_cycles_properties_from_ID(ID *id)
{
IDProperty *idprop = IDP_ID_system_properties_get(id);
return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles", IDP_GROUP) : nullptr;
}
IDProperty *version_cycles_properties_from_view_layer(ViewLayer *view_layer)
{
IDProperty *idprop = view_layer->system_properties;
return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles", IDP_GROUP) : nullptr;
}
IDProperty *version_cycles_properties_from_render_layer(SceneRenderLayer *render_layer)
{
IDProperty *idprop = render_layer->prop;
return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles", IDP_GROUP) : nullptr;
}
float version_cycles_property_float(IDProperty *idprop, const char *name, float default_value)
{
IDProperty *prop = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_FLOAT);
return (prop) ? IDP_float_get(prop) : default_value;
}
int version_cycles_property_int(IDProperty *idprop, const char *name, int default_value)
{
IDProperty *prop = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_INT);
return (prop) ? IDP_int_get(prop) : default_value;
}
void version_cycles_property_int_set(IDProperty *idprop, const char *name, int value)
{
if (IDProperty *prop = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_INT)) {
IDP_int_set(prop, value);
}
else {
IDP_AddToGroup(idprop, bke::idprop::create(name, value).release());
}
}
bool version_cycles_property_boolean(IDProperty *idprop, const char *name, bool default_value)
{
return version_cycles_property_int(idprop, name, default_value);
}
void version_cycles_property_boolean_set(IDProperty *idprop, const char *name, bool value)
{
version_cycles_property_int_set(idprop, name, value);
}
IDProperty *version_cycles_visibility_properties_from_ID(ID *id)
{
IDProperty *idprop = IDP_ID_system_properties_get(id);
return (idprop) ? IDP_GetPropertyTypeFromGroup(idprop, "cycles_visibility", IDP_GROUP) : nullptr;
}
void version_update_node_input(
bNodeTree *ntree,
FunctionRef<bool(bNode *)> check_node,
const char *socket_identifier,
FunctionRef<void(bNode *, bNodeSocket *)> update_input,
FunctionRef<void(bNode *, bNodeSocket *, bNode *, bNodeSocket *)> update_input_link)
{
bool need_update = false;
/* Iterate backwards from end so we don't encounter newly added links. */
for (bNodeLink &link : ntree->links.items_reversed_mutable()) {
/* Detect link to replace. */
bNode *fromnode = link.fromnode;
bNodeSocket *fromsock = link.fromsock;
bNode *tonode = link.tonode;
bNodeSocket *tosock = link.tosock;
if (!(tonode != nullptr && check_node(tonode) && STREQ(tosock->identifier, socket_identifier)))
{
continue;
}
/* Replace links with updated equivalent */
bke::node_remove_link(ntree, link);
update_input_link(fromnode, fromsock, tonode, tosock);
need_update = true;
}
/* Update sockets and/or their default values.
* Do this after the link update in case it changes the identifier. */
for (bNode &node : ntree->nodes) {
if (check_node(&node)) {
bNodeSocket *input = bke::node_find_socket(node, SOCK_IN, UString(socket_identifier));
if (input != nullptr) {
update_input(&node, input);
}
}
}
if (need_update) {
version_socket_update_is_used(ntree);
}
}
bNode *version_eevee_output_node_get(bNodeTree *ntree, int16_t node_type)
{
bNode *output_node = nullptr;
/* NOTE: duplicated from `ntreeShaderOutputNode` with small adjustments so it can be called
* during versioning. */
for (bNode &node : ntree->nodes) {
if (node.type_legacy != node_type) {
continue;
}
if (node.custom1 == SHD_OUTPUT_ALL) {
if (output_node == nullptr) {
output_node = &node;
}
else if (output_node->custom1 == SHD_OUTPUT_ALL) {
if ((node.flag & NODE_DO_OUTPUT) && !(output_node->flag & NODE_DO_OUTPUT)) {
output_node = &node;
}
}
}
else if (node.custom1 == SHD_OUTPUT_EEVEE) {
if (output_node == nullptr) {
output_node = &node;
}
else if ((node.flag & NODE_DO_OUTPUT) && !(output_node->flag & NODE_DO_OUTPUT)) {
output_node = &node;
}
}
}
return output_node;
}
bool all_scenes_use(Main *bmain, const Span<const char *> engines)
{
if (!bmain->scenes.first) {
return false;
}
for (Scene &scene : bmain->scenes) {
bool match = false;
for (const char *engine : engines) {
if (STREQ(scene.r.engine, engine)) {
match = true;
}
}
if (!match) {
return false;
}
}
return true;
}
bNodeTree *version_get_scene_compositor_node_tree(Main *bmain, Scene *scene)
{
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 500, 4)) {
return scene->nodetree;
}
return scene->compositing_node_group;
}
static bool blendfile_or_libraries_versions_atleast(Main *bmain,
const short versionfile,
const short subversionfile)
{
if (!MAIN_VERSION_FILE_ATLEAST(bmain, versionfile, subversionfile)) {
return false;
}
for (Library &library : bmain->libraries) {
if (!LIBRARY_VERSION_FILE_ATLEAST(&library, versionfile, subversionfile)) {
return false;
}
}
return true;
}
void do_versions_after_setup(Main *new_bmain,
BlendfileLinkAppendContext *lapp_context,
BlendFileReadReport *reports)
{
/* WARNING: The code below may add IDs. These IDs _will_ be (by definition) conforming to current
* code's version already, and _must not_ be *versioned* again.
*
* This means that when adding code here, _extreme_ care must be taken that it will not badly
* affect these 'modern' IDs potentially added by already existing processing.
*
* Adding code here should only be done in exceptional cases.
*
* Some further points to keep in mind:
* - While typically versioning order should be respected in code below (i.e. versioning
* affecting older versions should be done first), _this is not a hard rule_. And it should
* not be assumed older code must not be checked when adding newer code.
* - Do not rely strongly on versioning numbers here. This code may be run on data from
* different Blender versions (through the usage of linked data), and all existing data have
* already been processed through the whole do_version during blendfile reading itself. So
* decision to apply some versioning on some data should mostly rely on the data itself.
* - Unlike the regular do_version code, this one should _not_ be assumed as 'valid forever'.
* It is closer to the Editing or BKE code in that respect, changes to the logic or data
* model of an ID will require a careful update here as well.
*
* Another critical weakness of this code is that it is currently _not_ performed on data linked
* during an editing session, but only on data linked while reading a whole blendfile. This will
* have to be fixed at some point.
*/
/* NOTE: Version number is checked against Main version (i.e. current blend file version), AND
* the versions of all the linked libraries. */
if (!blendfile_or_libraries_versions_atleast(new_bmain, 250, 0)) {
/* This happens here, because at this point in the versioning code there's
* 'reports' available. */
reports->pre_animato_file_loaded = true;
}
if (!blendfile_or_libraries_versions_atleast(new_bmain, 250, 0)) {
for (Scene &scene : new_bmain->scenes) {
if (scene.ed) {
seq::doversion_250_sound_proxy_update(new_bmain, scene.ed);
}
}
}
if (!blendfile_or_libraries_versions_atleast(new_bmain, 302, 1)) {
BKE_lib_override_library_main_proxy_convert(new_bmain, reports);
/* Currently liboverride code can generate invalid namemap. This is a known issue, requires
* #107847 to be properly fixed. */
BKE_main_namemap_validate_and_fix(*new_bmain);
}
if (!blendfile_or_libraries_versions_atleast(new_bmain, 302, 3)) {
/* Does not add any new IDs, but needs the full Main data-base. */
BKE_lib_override_library_main_hierarchy_root_ensure(new_bmain);
}
if (!blendfile_or_libraries_versions_atleast(new_bmain, 402, 22)) {
/* Initial auto smooth versioning started at (401, 2), but a bug caused the legacy flag to not
* be cleared, so it is re-run in a later version when the bug is fixed and the versioning has
* been made idempotent. */
BKE_main_mesh_legacy_convert_auto_smooth(*new_bmain);
}
if (!blendfile_or_libraries_versions_atleast(new_bmain, 404, 2)) {
/* Version all the action assignments of just-versioned datablocks. This MUST happen before the
* GreasePencil conversion, as that assumes the Action Slots have already been assigned. */
animrig::versioning::convert_legacy_action_assignments(*new_bmain, reports->reports);
}
if (!blendfile_or_libraries_versions_atleast(new_bmain, 403, 3)) {
/* Convert all the legacy grease pencil objects. This does not touch annotations. */
bke::greasepencil::convert::legacy_main(*new_bmain, lapp_context, *reports);
}
if (!blendfile_or_libraries_versions_atleast(new_bmain, 500, 4)) {
for (Scene &scene : new_bmain->scenes) {
bNodeTree *ntree = scene.nodetree;
if (!ntree) {
continue;
}
ntree->id.flag &= ~ID_FLAG_EMBEDDED_DATA;
ntree->owner_id = nullptr;
ntree->id.tag |= ID_TAG_NO_MAIN;
scene.compositing_node_group = ntree;
scene.nodetree = nullptr;
BKE_libblock_management_main_add(new_bmain, ntree);
/* NOTE: The user count remains zero at this point. It will get automatically updated after
* blend file reading is done. */
}
}
if (!blendfile_or_libraries_versions_atleast(new_bmain, 501, 29)) {
/* Clear modifier node trees if the tree type is undefined.
* This can happen to generated auto-smooth node groups for unknown reasons (#152810). */
for (Object &object : new_bmain->objects) {
for (ModifierData &md : object.modifiers) {
if (md.type != eModifierType_Nodes) {
continue;
}
NodesModifierData &nmd = *reinterpret_cast<NodesModifierData *>(&md);
if (nmd.node_group && !ID_MISSING(nmd.node_group) &&
!STREQ(nmd.node_group->idname, "GeometryNodeTree"))
{
id_us_min(&nmd.node_group->id);
nmd.node_group = nullptr;
}
}
}
}
}
} // namespace blender

View File

@@ -0,0 +1,906 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blenloader
*
* This file handles updating the `startup.blend`, this is used when reading old files.
*
* Unlike regular versioning this makes changes that ensure the startup file
* has brushes and other presets setup to take advantage of newer features.
*
* To update preference defaults see `userdef_default.c`.
*/
#define DNA_DEPRECATED_ALLOW
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math_rotation.h"
#include "BLI_math_vector.h"
#include "BLI_math_vector_types.hh"
#include "BLI_mempool.h"
#include "BLI_string.h"
#include "BLI_string_utf8.h"
#include "BLI_utildefines.h"
#include "DNA_camera_types.h"
#include "DNA_curveprofile_types.h"
#include "DNA_gpencil_legacy_types.h"
#include "DNA_light_types.h"
#include "DNA_mask_types.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_sequence_types.h"
#include "DNA_space_types.h"
#include "DNA_windowmanager_types.h"
#include "DNA_workspace_types.h"
#include "DNA_world_types.h"
#include "BKE_appdir.hh"
#include "BKE_attribute.hh"
#include "BKE_brush.hh"
#include "BKE_colortools.hh"
#include "BKE_curveprofile.h"
#include "BKE_customdata.hh"
#include "BKE_gpencil_legacy.h"
#include "BKE_idprop.hh"
#include "BKE_layer.hh"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
#include "BKE_main_namemap.hh"
#include "BKE_material.hh"
#include "BKE_mesh.hh"
#include "BKE_node_legacy_types.hh"
#include "BKE_node_runtime.hh"
#include "BKE_node_tree_update.hh"
#include "BKE_paint.hh"
#include "BKE_paint_types.hh"
#include "BKE_screen.hh"
#include "BKE_workspace.hh"
#include "BLO_readfile.hh"
#include "BLT_translation.hh"
#include "versioning_common.hh"
namespace blender {
/* Make preferences read-only, use `versioning_userdef.cc`. */
#define U (*((const UserDef *)&U))
static bool blo_is_builtin_template(const char *app_template)
{
/* For all builtin templates shipped with Blender. */
return (!app_template || STR_ELEM(app_template,
N_("2D_Animation"),
N_("Storyboarding"),
N_("Sculpting"),
N_("VFX"),
N_("Video_Editing")));
}
static void blo_update_defaults_screen(bScreen *screen,
const char *app_template,
const char *workspace_name)
{
/* For all app templates. */
for (ScrArea &area : screen->areabase) {
for (ARegion &region : area.regionbase) {
/* Some toolbars have been saved as initialized,
* we don't want them to have odd zoom-level or scrolling set, see: #47047 */
if (ELEM(region.regiontype, RGN_TYPE_UI, RGN_TYPE_TOOLS, RGN_TYPE_TOOL_PROPS)) {
region.v2d.flag &= ~V2D_IS_INIT;
}
}
/* Set default folder. */
for (SpaceLink &sl : area.spacedata) {
if (sl.spacetype == SPACE_FILE) {
SpaceFile *sfile = reinterpret_cast<SpaceFile *>(&sl);
if (sfile->params) {
const char *dir_default = BKE_appdir_folder_default();
if (dir_default) {
STRNCPY(sfile->params->dir, dir_default);
sfile->params->file[0] = '\0';
}
}
}
}
}
/* For builtin templates only. */
if (!blo_is_builtin_template(app_template)) {
return;
}
for (ScrArea &area : screen->areabase) {
for (ARegion &region : area.regionbase) {
/* Remove all stored panels, we want to use defaults
* (order, open/closed) as defined by UI code here! */
BKE_area_region_panels_free(&region.panels);
region.panels_category_active.free_no_destruct();
/* Reset size so it uses consistent defaults from the region types. */
region.sizex = 0;
region.sizey = 0;
}
if (area.spacetype == SPACE_IMAGE) {
if (STREQ(workspace_name, "UV Editing")) {
SpaceImage *sima = static_cast<SpaceImage *>(area.spacedata.first);
if (sima->mode == SI_MODE_VIEW) {
sima->mode = SI_MODE_UV;
}
sima->uv_face_opacity = 1.0f;
sima->uv_edge_opacity = 1.0f;
}
else if (STR_ELEM(workspace_name, "Texture Paint", "Shading")) {
SpaceImage *sima = static_cast<SpaceImage *>(area.spacedata.first);
/* Face opacity is set to 0 to not interfere with visualization while painting */
sima->uv_face_opacity = 0.0f;
sima->uv_edge_opacity = 1.0f;
}
else if (BLI_str_startswith(workspace_name, "Compositing")) {
SpaceImage *sima = static_cast<SpaceImage *>(area.spacedata.first);
sima->overlay.flag &= ~SI_OVERLAY_DRAW_TEXT_INFO;
}
}
else if (area.spacetype == SPACE_ACTION) {
/* Show markers region, hide channels and collapse summary in timelines. */
SpaceAction *saction = static_cast<SpaceAction *>(area.spacedata.first);
saction->flag |= SACTION_SHOW_MARKERS;
if (saction->mode == SACTCONT_TIMELINE) {
saction->ads.flag |= ADS_FLAG_SUMMARY_COLLAPSED;
for (ARegion &region : area.regionbase) {
if (region.regiontype == RGN_TYPE_CHANNELS) {
region.flag |= RGN_FLAG_HIDDEN;
}
}
}
else {
/* Open properties panel by default. */
for (ARegion &region : area.regionbase) {
if (region.regiontype == RGN_TYPE_UI) {
region.flag &= ~RGN_FLAG_HIDDEN;
}
}
}
}
else if (area.spacetype == SPACE_GRAPH) {
SpaceGraph *sipo = static_cast<SpaceGraph *>(area.spacedata.first);
sipo->flag |= SIPO_SHOW_MARKERS;
}
else if (area.spacetype == SPACE_NLA) {
SpaceNla *snla = static_cast<SpaceNla *>(area.spacedata.first);
snla->flag |= SNLA_SHOW_MARKERS;
}
else if (area.spacetype == SPACE_SEQ) {
SpaceSeq *seq = static_cast<SpaceSeq *>(area.spacedata.first);
seq->flag |= SEQ_SHOW_MARKERS | SEQ_ZOOM_TO_FIT | SEQ_USE_PROXIES | SEQ_SHOW_OVERLAY;
seq->render_size = SEQ_RENDER_SIZE_PROXY_100;
seq->timeline_overlay.flag |= SEQ_TIMELINE_SHOW_STRIP_SOURCE | SEQ_TIMELINE_SHOW_STRIP_NAME |
SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID |
SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG |
SEQ_TIMELINE_SHOW_STRIP_RETIMING |
SEQ_TIMELINE_WAVEFORMS_HALF |
SEQ_TIMELINE_STRIP_END_THUMBNAILS;
seq->preview_overlay.flag |= SEQ_PREVIEW_SHOW_OUTLINE_SELECTED;
seq->cache_overlay.flag = SEQ_CACHE_SHOW | SEQ_CACHE_SHOW_FINAL_OUT;
seq->draw_flag |= SEQ_DRAW_TRANSFORM_PREVIEW;
}
else if (area.spacetype == SPACE_TEXT) {
/* Show syntax and line numbers in Script workspace text editor. */
SpaceText *stext = static_cast<SpaceText *>(area.spacedata.first);
stext->showsyntax = true;
stext->showlinenrs = true;
stext->flags |= ST_FIND_WRAP;
}
else if (area.spacetype == SPACE_VIEW3D) {
View3D *v3d = static_cast<View3D *>(area.spacedata.first);
/* Screen space cavity by default for faster performance. */
v3d->shading.cavity_type = V3D_SHADING_CAVITY_CURVATURE;
v3d->shading.flag |= V3D_SHADING_SPECULAR_HIGHLIGHT;
v3d->overlay.texture_paint_mode_opacity = 1.0f;
v3d->overlay.weight_paint_mode_opacity = 1.0f;
v3d->overlay.vertex_paint_mode_opacity = 1.0f;
/* Update default Z bias for retopology overlay. */
v3d->overlay.retopology_offset = 0.01f;
/* Clear this deprecated bit for later reuse. */
v3d->overlay.edit_flag &= ~V3D_OVERLAY_EDIT_EDGES_DEPRECATED;
/* grease pencil settings */
v3d->vertex_opacity = 1.0f;
v3d->gp_flag |= V3D_GP_SHOW_EDIT_LINES;
/* Remove dither pattern in wireframe mode. */
v3d->shading.xray_alpha_wire = 0.0f;
v3d->clip_start = 0.01f;
/* Skip startups that use the viewport color by default. */
if (v3d->shading.background_type != V3D_SHADING_BACKGROUND_VIEWPORT) {
copy_v3_fl(v3d->shading.background_color, 0.05f);
}
/* Disable Curve Normals. */
v3d->overlay.edit_flag &= ~V3D_OVERLAY_EDIT_CU_NORMALS;
v3d->overlay.normals_constant_screen_size = 7.0f;
/* Always enable Grease Pencil vertex color overlay by default. */
v3d->overlay.gpencil_vertex_paint_opacity = 1.0f;
/* Always use theme color for wireframe by default. */
v3d->shading.wire_color_type = V3D_SHADING_SINGLE_COLOR;
/* Level out the 3D Viewport camera rotation, see: #113751. */
constexpr float viewports_to_level[][4] = {
/* Animation, Modeling, Scripting, Texture Paint, UV Editing. */
{0x1.6e7cb8p-1, -0x1.c1747p-2, -0x1.2997dap-2, -0x1.d5d806p-2},
/* Layout. */
{0x1.6e7cb8p-1, -0x1.c17478p-2, -0x1.2997dcp-2, -0x1.d5d80cp-2},
/* Geometry Nodes. */
{0x1.6e7cb6p-1, -0x1.c17476p-2, -0x1.2997dep-2, -0x1.d5d80cp-2},
};
constexpr float viewports_to_clear_ofs[][4] = {
/* Geometry Nodes. */
{0x1.6e7cb6p-1, -0x1.c17476p-2, -0x1.2997dep-2, -0x1.d5d80cp-2},
/* Sculpting. */
{0x1.885b28p-1, -0x1.2d10cp-1, -0x1.42ae54p-3, -0x1.a486a2p-3},
};
constexpr float unified_viewquat[4] = {
0x1.6cbc88p-1, -0x1.c3a5c8p-2, -0x1.26413ep-2, -0x1.db430ap-2};
for (ARegion &region : area.regionbase) {
if (region.regiontype == RGN_TYPE_WINDOW) {
RegionView3D *rv3d = static_cast<RegionView3D *>(region.regiondata);
for (int i = 0; i < ARRAY_SIZE(viewports_to_clear_ofs); i++) {
if (equals_v4v4(rv3d->viewquat, viewports_to_clear_ofs[i])) {
zero_v3(rv3d->ofs);
}
}
for (int i = 0; i < ARRAY_SIZE(viewports_to_level); i++) {
if (equals_v4v4(rv3d->viewquat, viewports_to_level[i])) {
copy_qt_qt(rv3d->viewquat, unified_viewquat);
}
}
}
}
}
else if (area.spacetype == SPACE_CLIP) {
SpaceClip *sclip = static_cast<SpaceClip *>(area.spacedata.first);
sclip->around = V3D_AROUND_CENTER_MEDIAN;
sclip->mask_info.blend_factor = 0.7f;
sclip->mask_info.draw_flag = MASK_DRAWFLAG_SPLINE;
}
}
/* Show tool-header by default (for most cases at least, hide for others). */
const bool hide_image_tool_header = STR_ELEM(workspace_name, "Rendering", "Compositing");
for (ScrArea &area : screen->areabase) {
for (SpaceLink &sl : area.spacedata) {
ListBaseT<ARegion> *regionbase = (&sl == static_cast<SpaceLink *>(area.spacedata.first)) ?
&area.regionbase :
&sl.regionbase;
for (ARegion &region : *regionbase) {
if (region.regiontype == RGN_TYPE_TOOL_HEADER) {
if (((sl.spacetype == SPACE_IMAGE) && hide_image_tool_header) ||
sl.spacetype == SPACE_SEQ)
{
region.flag |= RGN_FLAG_HIDDEN;
}
else {
region.flag &= ~(RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER);
}
}
}
}
}
/* 2D animation template. */
if (app_template && STREQ(app_template, "2D_Animation")) {
for (ScrArea &area : screen->areabase) {
if (area.spacetype == SPACE_ACTION) {
SpaceAction *saction = static_cast<SpaceAction *>(area.spacedata.first);
/* Enable Sliders. */
saction->flag |= SACTION_SLIDERS;
}
else if (area.spacetype == SPACE_VIEW3D) {
View3D *v3d = static_cast<View3D *>(area.spacedata.first);
/* Set Material Color by default. */
v3d->shading.color_type = V3D_SHADING_MATERIAL_COLOR;
/* Enable Annotations. */
v3d->flag2 |= V3D_SHOW_ANNOTATION;
}
}
}
}
void BLO_update_defaults_workspace(WorkSpace *workspace, const char *app_template)
{
for (WorkSpaceLayout &layout : workspace->layouts) {
if (layout.screen) {
blo_update_defaults_screen(layout.screen, app_template, workspace->id.name + 2);
}
}
if (blo_is_builtin_template(app_template)) {
/* Clear all tools to use default options instead, ignore the tool saved in the file. */
while (!workspace->tools.is_empty()) {
BKE_workspace_tool_remove(workspace, static_cast<bToolRef *>(workspace->tools.first));
}
/* For 2D animation template. */
if (STREQ(workspace->id.name + 2, "Drawing")) {
workspace->object_mode = OB_MODE_PAINT_GREASE_PENCIL;
}
/* For Sculpting template. */
if (STREQ(workspace->id.name + 2, "Sculpting")) {
for (WorkSpaceLayout &layout : workspace->layouts) {
bScreen *screen = layout.screen;
if (screen) {
for (ScrArea &area : screen->areabase) {
if (area.spacetype == SPACE_VIEW3D) {
View3D *v3d = static_cast<View3D *>(area.spacedata.first);
v3d->shading.flag &= ~V3D_SHADING_CAVITY;
copy_v3_fl(v3d->shading.single_color, 1.0f);
STRNCPY(v3d->shading.matcap, "basic_1");
}
}
}
}
}
}
/* For Video Editing template. */
if (STRPREFIX(workspace->id.name + 2, "Video Editing")) {
for (WorkSpaceLayout &layout : workspace->layouts) {
bScreen *screen = layout.screen;
if (screen) {
for (ScrArea &area : screen->areabase) {
for (SpaceLink &sl : area.spacedata) {
if (sl.spacetype == SPACE_SEQ) {
if ((reinterpret_cast<SpaceSeq *>(&sl))->view == SEQ_VIEW_PREVIEW) {
continue;
}
ListBaseT<ARegion> *regionbase = (&sl == area.spacedata.first) ? &area.regionbase :
&sl.regionbase;
ARegion *sidebar = BKE_region_find_in_listbase_by_type(regionbase, RGN_TYPE_UI);
sidebar->flag |= RGN_FLAG_HIDDEN;
}
if (sl.spacetype == SPACE_PROPERTIES) {
SpaceProperties *properties = reinterpret_cast<SpaceProperties *>(&sl);
properties->mainb = properties->mainbo = properties->mainbuser = BCONTEXT_STRIP;
}
}
}
}
}
}
}
static void blo_update_defaults_paint(Paint *paint)
{
if (!paint) {
return;
}
/* Ensure input_samples has a correct default value of 1. */
if (paint->unified_paint_settings.input_samples == 0) {
paint->unified_paint_settings.input_samples = 1;
}
const UnifiedPaintSettings &default_ups = UnifiedPaintSettings();
paint->unified_paint_settings.size = default_ups.size;
paint->unified_paint_settings.input_samples = default_ups.input_samples;
paint->unified_paint_settings.unprojected_size = default_ups.unprojected_size;
paint->unified_paint_settings.alpha = default_ups.alpha;
paint->unified_paint_settings.weight = default_ups.weight;
paint->unified_paint_settings.flag = default_ups.flag;
copy_v3_v3(paint->unified_paint_settings.color, default_ups.color);
copy_v3_v3(paint->unified_paint_settings.secondary_color, default_ups.secondary_color);
if (paint->unified_paint_settings.curve_rand_hue == nullptr) {
paint->unified_paint_settings.curve_rand_hue = BKE_paint_default_curve();
}
if (paint->unified_paint_settings.curve_rand_saturation == nullptr) {
paint->unified_paint_settings.curve_rand_saturation = BKE_paint_default_curve();
}
if (paint->unified_paint_settings.curve_rand_value == nullptr) {
paint->unified_paint_settings.curve_rand_value = BKE_paint_default_curve();
}
}
static void blo_update_defaults_windowmanager(wmWindowManager *wm)
{
wm->xr.session_settings.fly_speed = 3.0f;
wm->xr.session_settings.view_scale = 1.0f;
}
static void blo_update_defaults_scene(Main *bmain, Scene *scene)
{
ToolSettings *ts = scene->toolsettings;
STRNCPY_UTF8(scene->r.engine, RE_engine_id_BLENDER_EEVEE);
scene->r.cfra = 1.0f;
scene->r.im_format.exr_flag |= R_IMF_EXR_FLAG_MULTIPART;
scene->r.bake.im_format.exr_flag |= R_IMF_EXR_FLAG_MULTIPART;
scene->r.compositor_device = SCE_COMPOSITOR_DEVICE_GPU;
/* Don't enable compositing nodes. */
if (scene->nodetree) {
bke::node_tree_free_embedded_tree(scene->nodetree);
MEM_delete(scene->nodetree);
scene->nodetree = nullptr;
scene->use_nodes = false;
}
/* Rename render layers. */
BKE_view_layer_rename(
bmain, scene, static_cast<ViewLayer *>(scene->view_layers.first), "ViewLayer");
/* Disable Z pass by default. */
for (ViewLayer &view_layer : scene->view_layers) {
view_layer.passflag &= ~SCE_PASS_DEPTH;
view_layer.eevee.ambient_occlusion_distance = 10.0f;
}
if (scene->ed) {
/* Display missing media by default. */
scene->ed->show_missing_media_flag |= SEQ_EDIT_SHOW_MISSING_MEDIA;
/* Turn on frame pre-fetching per default. */
scene->ed->cache_flag |= SEQ_CACHE_PREFETCH_ENABLE;
}
/* New EEVEE defaults. */
scene->eevee.motion_blur_shutter_deprecated = 0.5f;
scene->eevee.flag &= ~SCE_EEVEE_VOLUME_CUSTOM_RANGE;
scene->eevee.clamp_volume_indirect = 0.0f; /* Default from versioning is not 0. */
scene->eevee.ray_tracing_options = {};
scene->eevee.fast_gi_thickness_near = 0.1f; /* Default from versioning is not 0.1f. */
copy_v3_v3(scene->display.light_direction, float3(M_SQRT1_3));
copy_v2_fl2(scene->safe_areas.title, 0.1f, 0.05f);
copy_v2_fl2(scene->safe_areas.action, 0.035f, 0.035f);
ts->uv_flag |= UV_FLAG_SELECT_SYNC;
/* Default Rotate Increment. */
const float default_snap_angle_increment = DEG2RADF(5.0f);
ts->snap_angle_increment_2d = default_snap_angle_increment;
ts->snap_angle_increment_3d = default_snap_angle_increment;
const float default_snap_angle_increment_precision = DEG2RADF(1.0f);
ts->snap_angle_increment_2d_precision = default_snap_angle_increment_precision;
ts->snap_angle_increment_3d_precision = default_snap_angle_increment_precision;
/* Be sure `curfalloff` and primitive are initialized. */
if (ts->gp_sculpt.cur_falloff == nullptr) {
ts->gp_sculpt.cur_falloff = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
CurveMapping *gp_falloff_curve = ts->gp_sculpt.cur_falloff;
BKE_curvemapping_init(gp_falloff_curve);
BKE_curvemap_reset(gp_falloff_curve->cm,
&gp_falloff_curve->clipr,
CURVE_PRESET_GAUSS,
CurveMapSlopeType::Positive);
}
if (ts->gp_sculpt.cur_primitive == nullptr) {
ts->gp_sculpt.cur_primitive = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
CurveMapping *gp_primitive_curve = ts->gp_sculpt.cur_primitive;
BKE_curvemapping_init(gp_primitive_curve);
BKE_curvemap_reset(gp_primitive_curve->cm,
&gp_primitive_curve->clipr,
CURVE_PRESET_BELL,
CurveMapSlopeType::Positive);
}
if (ts->sculpt) {
ts->sculpt->flags = Sculpt().flags;
}
/* Correct default startup UVs. */
Mesh *mesh = static_cast<Mesh *>(BLI_findstring(&bmain->meshes, "Cube", offsetof(ID, name) + 2));
if (mesh && (mesh->corners_num == 24) &&
CustomData_has_layer(&mesh->corner_data, CD_PROP_FLOAT2))
{
const float uv_values[24][2] = {
{0.625, 0.50}, {0.875, 0.50}, {0.875, 0.75}, {0.625, 0.75}, {0.375, 0.75}, {0.625, 0.75},
{0.625, 1.00}, {0.375, 1.00}, {0.375, 0.00}, {0.625, 0.00}, {0.625, 0.25}, {0.375, 0.25},
{0.125, 0.50}, {0.375, 0.50}, {0.375, 0.75}, {0.125, 0.75}, {0.375, 0.50}, {0.625, 0.50},
{0.625, 0.75}, {0.375, 0.75}, {0.375, 0.25}, {0.625, 0.25}, {0.625, 0.50}, {0.375, 0.50},
};
float (*uv_map)[2] = static_cast<float (*)[2]>(
CustomData_get_layer_for_write(&mesh->corner_data, CD_PROP_FLOAT2, mesh->corners_num));
memcpy(uv_map, uv_values, sizeof(float[2]) * mesh->corners_num);
}
/* Make sure that the curve profile is initialized */
if (ts->custom_bevel_profile_preset == nullptr) {
ts->custom_bevel_profile_preset = BKE_curveprofile_add(PROF_PRESET_LINE);
}
/* Clear ID properties so Cycles gets defaults. */
IDProperty *idprop = IDP_GetProperties(&scene->id);
if (idprop) {
IDP_ClearProperty(idprop);
}
if (ts->sculpt) {
ts->sculpt->automasking_boundary_edges_propagation_steps = 1;
}
/* Ensure input_samples has a correct default value of 1. */
if (ts->unified_paint_settings.input_samples == 0) {
ts->unified_paint_settings.input_samples = 1;
}
const UnifiedPaintSettings default_ups = {};
ts->unified_paint_settings.flag = default_ups.flag;
copy_v3_v3(ts->unified_paint_settings.color, default_ups.color);
copy_v3_v3(ts->unified_paint_settings.secondary_color, default_ups.secondary_color);
if (ts->unified_paint_settings.curve_rand_hue == nullptr) {
ts->unified_paint_settings.curve_rand_hue = BKE_paint_default_curve();
}
if (ts->unified_paint_settings.curve_rand_saturation == nullptr) {
ts->unified_paint_settings.curve_rand_saturation = BKE_paint_default_curve();
}
if (ts->unified_paint_settings.curve_rand_value == nullptr) {
ts->unified_paint_settings.curve_rand_value = BKE_paint_default_curve();
}
blo_update_defaults_paint(reinterpret_cast<Paint *>(ts->vpaint));
blo_update_defaults_paint(reinterpret_cast<Paint *>(ts->wpaint));
blo_update_defaults_paint(reinterpret_cast<Paint *>(ts->sculpt));
blo_update_defaults_paint(reinterpret_cast<Paint *>(ts->gp_paint));
blo_update_defaults_paint(reinterpret_cast<Paint *>(ts->gp_vertexpaint));
blo_update_defaults_paint(reinterpret_cast<Paint *>(ts->gp_sculptpaint));
blo_update_defaults_paint(reinterpret_cast<Paint *>(ts->curves_sculpt));
blo_update_defaults_paint(reinterpret_cast<Paint *>(&ts->imapaint));
/* Weight Paint settings */
ts->weightuser = OB_DRAW_GROUPUSER_ACTIVE;
/* Cycles settings. */
IDProperty *cscene = version_cycles_properties_from_ID(&scene->id);
if (cscene) {
/* Set the default sampling pattern to AUTOMATIC. */
version_cycles_property_int_set(cscene, "sampling_pattern", 5);
}
}
void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
{
/* For all app templates. */
for (WorkSpace &workspace : bmain->workspaces) {
BLO_update_defaults_workspace(&workspace, app_template);
}
/* Grease pencil materials and paint modes setup. */
{
/* Rename and fix materials and enable default object lights on. */
if (app_template && STREQ(app_template, "2D_Animation")) {
Material *ma = nullptr;
do_versions_rename_id(bmain, ID_MA, "Black", "Solid Stroke");
do_versions_rename_id(bmain, ID_MA, "Red", "Squares Stroke");
do_versions_rename_id(bmain, ID_MA, "Grey", "Solid Fill");
do_versions_rename_id(bmain, ID_MA, "Black Dots", "Dots Stroke");
/* Dots Stroke. */
ma = static_cast<Material *>(
BLI_findstring(&bmain->materials, "Dots Stroke", offsetof(ID, name) + 2));
if (ma == nullptr) {
ma = BKE_gpencil_material_add(bmain, "Dots Stroke");
}
ma->gp_style->mode = GP_MATERIAL_MODE_DOT;
/* Squares Stroke. */
ma = static_cast<Material *>(
BLI_findstring(&bmain->materials, "Squares Stroke", offsetof(ID, name) + 2));
if (ma == nullptr) {
ma = BKE_gpencil_material_add(bmain, "Squares Stroke");
}
ma->gp_style->mode = GP_MATERIAL_MODE_SQUARE;
/* Change Solid Stroke settings. */
ma = static_cast<Material *>(
BLI_findstring(&bmain->materials, "Solid Stroke", offsetof(ID, name) + 2));
if (ma != nullptr) {
ma->gp_style->mix_rgba[3] = 1.0f;
ma->gp_style->texture_offset[0] = -0.5f;
ma->gp_style->mix_factor = 0.5f;
}
/* Change Solid Fill settings. */
ma = static_cast<Material *>(
BLI_findstring(&bmain->materials, "Solid Fill", offsetof(ID, name) + 2));
if (ma != nullptr) {
ma->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW;
ma->gp_style->mix_rgba[3] = 1.0f;
ma->gp_style->texture_offset[0] = -0.5f;
ma->gp_style->mix_factor = 0.5f;
}
Object *ob = static_cast<Object *>(
BLI_findstring(&bmain->objects, "Stroke", offsetof(ID, name) + 2));
if (ob && ob->type == OB_GPENCIL_LEGACY) {
ob->dtx |= OB_USE_GPENCIL_LIGHTS;
}
}
/* Add library weak references to avoid duplicating materials from essentials. */
const std::optional<std::string> assets_path = BKE_appdir_folder_id(BLENDER_SYSTEM_DATAFILES,
"assets/brushes");
if (assets_path.has_value()) {
const std::string assets_blend_path = *assets_path + "/essentials_brushes-gp_draw.blend";
for (Material &material : bmain->materials) {
BKE_main_library_weak_reference_add(
&material.id, assets_blend_path.c_str(), material.id.name);
}
}
/* Reset grease pencil paint modes. */
for (Scene &scene : bmain->scenes) {
ToolSettings *ts = scene.toolsettings;
/* Ensure new Paint modes. */
BKE_paint_ensure_from_paintmode(&scene, PaintMode::VertexGPencil);
BKE_paint_ensure_from_paintmode(&scene, PaintMode::SculptGPencil);
BKE_paint_ensure_from_paintmode(&scene, PaintMode::WeightGPencil);
/* Enable cursor. */
if (ts->gp_paint) {
ts->gp_paint->paint.flags |= PAINT_SHOW_BRUSH;
}
/* Ensure Palette by default. */
if (ts->gp_paint) {
BKE_gpencil_palette_ensure(bmain, &scene);
}
}
if (app_template && (STR_ELEM(app_template, "2D_Animation", "Storyboarding"))) {
/* Since !153036, the base colors for stroke & fill were getting versioned to have 0% opacity
* if the stroke/fill was disabled. This meant that in a new file using the following App
* Templates, the "Solid Stroke" material wouldn't show anything when trying to draw a fill.
* This sets the fill to a mid grey to make sure users don't run into this issue. */
/* Change Solid Stroke settings. */
Material *ma = static_cast<Material *>(
BLI_findstring(&bmain->materials, "Solid Stroke", offsetof(ID, name) + 2));
if (ma != nullptr) {
/* Black Stroke and Grey Fill. */
copy_v4_fl4(ma->gp_style->stroke_rgba, 0.0f, 0.0f, 0.0f, 1.0f);
copy_v4_fl4(ma->gp_style->fill_rgba, 0.5f, 0.5f, 0.5f, 1.0f);
}
}
}
/* For builtin templates only. */
if (!blo_is_builtin_template(app_template)) {
return;
}
/* Work-spaces. */
for (wmWindowManager &wm : bmain->wm) {
blo_update_defaults_windowmanager(&wm);
for (wmWindow &win : wm.windows) {
for (WorkSpace &workspace : bmain->workspaces) {
WorkSpaceLayout *layout = BKE_workspace_active_layout_for_workspace_get(win.workspace_hook,
&workspace);
/* Name all screens by their workspaces (avoids 'Default.###' names). */
/* Default only has one window. */
if (layout->screen) {
bScreen *screen = layout->screen;
if (!STREQ(screen->id.name + 2, workspace.id.name + 2)) {
BKE_libblock_rename(*bmain, screen->id, workspace.id.name + 2);
}
}
/* For some reason we have unused screens, needed until re-saving.
* Clear unused layouts because they're visible in the outliner & Python API. */
for (WorkSpaceLayout &layout_iter : workspace.layouts.items_mutable()) {
if (layout != &layout_iter) {
BKE_workspace_layout_remove(bmain, &workspace, &layout_iter);
}
}
}
}
}
/* Scenes */
for (Scene &scene : bmain->scenes) {
blo_update_defaults_scene(bmain, &scene);
if (app_template && STR_ELEM(app_template, "Video_Editing", "2D_Animation")) {
/* Filmic is too slow, use standard until it is optimized. */
STRNCPY_UTF8(scene.view_settings.view_transform, "Standard");
STRNCPY_UTF8(scene.view_settings.look, "None");
}
else {
/* Default to AgX view transform. */
STRNCPY_UTF8(scene.view_settings.view_transform, "AgX");
}
if (app_template && STREQ(app_template, "Video_Editing")) {
/* Pass: no extra tweaks needed. Keep the view settings configured above, and rely on the
* default state of enabled AV sync. */
}
else {
/* AV Sync break physics sim caching, disable until that is fixed. */
scene.audio.flag &= ~AUDIO_SYNC;
scene.flag &= ~SCE_FRAME_DROP;
}
/* Change default selection mode for Grease Pencil. */
if (app_template && STREQ(app_template, "2D_Animation")) {
ToolSettings *ts = scene.toolsettings;
ts->gpencil_selectmode_edit = GP_SELECTMODE_STROKE;
}
}
/* Objects */
do_versions_rename_id(bmain, ID_OB, "Lamp", "Light");
do_versions_rename_id(bmain, ID_LA, "Lamp", "Light");
if (app_template && STREQ(app_template, "2D_Animation")) {
for (Object &object : bmain->objects) {
if (object.type == OB_GPENCIL_LEGACY) {
/* Set grease pencil object in drawing mode */
bGPdata *gpd = id_cast<bGPdata *>(object.data);
object.mode = OB_MODE_PAINT_GREASE_PENCIL;
gpd->flag |= GP_DATA_STROKE_PAINTMODE;
break;
}
}
}
for (Object &object : bmain->objects) {
const Object dob;
/* Set default for shadow terminator bias. */
object.shadow_terminator_normal_offset = dob.shadow_terminator_normal_offset;
object.shadow_terminator_geometry_offset = dob.shadow_terminator_geometry_offset;
object.shadow_terminator_shading_offset = dob.shadow_terminator_shading_offset;
}
for (Mesh &mesh : bmain->meshes) {
/* Match default for new meshes. */
mesh.smoothresh_legacy = DEG2RADF(30);
/* Match voxel remesher options for all existing meshes in templates. */
mesh.flag |= ME_REMESH_REPROJECT_VOLUME | ME_REMESH_REPROJECT_ATTRIBUTES;
/* For Sculpting template. */
if (app_template && STREQ(app_template, "Sculpting")) {
mesh.remesh_voxel_size = 0.035f;
bke::mesh_smooth_set(mesh, false);
}
else {
/* Remove sculpt-mask data in default mesh objects for all non-sculpt templates. */
CustomData_free_layers(&mesh.vert_data, CD_PAINT_MASK);
CustomData_free_layers(&mesh.corner_data, CD_GRID_PAINT_MASK);
}
mesh.attributes_for_write().remove(".sculpt_face_set");
}
for (Camera &camera : bmain->cameras) {
/* Initialize to a useful value. */
camera.dof.focus_distance = 10.0f;
camera.dof.aperture_fstop = 2.8f;
}
for (Light &light : bmain->lights) {
/* Fix lights defaults. */
light.clipsta = 0.05f;
light.att_dist = 40.0f;
}
/* Materials */
for (Material &ma : bmain->materials) {
/* Update default material to be a bit more rough. */
ma.roughness = 0.5f;
/* Enable transparent shadows. */
ma.blend_flag |= MA_BL_TRANSPARENT_SHADOW;
if (ma.nodetree) {
for (bNode *node : ma.nodetree->all_nodes()) {
if (node->type_legacy == SH_NODE_BSDF_PRINCIPLED) {
bNodeSocket *roughness_socket = bke::node_find_socket(*node, SOCK_IN, "Roughness"_ustr);
*version_cycles_node_socket_float_value(roughness_socket) = 0.5f;
bNodeSocket *emission = bke::node_find_socket(*node, SOCK_IN, "Emission Color"_ustr);
copy_v4_fl(version_cycles_node_socket_rgba_value(emission), 1.0f);
bNodeSocket *emission_strength = bke::node_find_socket(
*node, SOCK_IN, "Emission Strength"_ustr);
*version_cycles_node_socket_float_value(emission_strength) = 0.0f;
bNodeSocket *ior = bke::node_find_socket(*node, SOCK_IN, "IOR"_ustr);
*version_cycles_node_socket_float_value(ior) = 1.5f;
bNodeSocket *subsurface_scale = bke::node_find_socket(
*node, SOCK_IN, "Subsurface Scale"_ustr);
*version_cycles_node_socket_float_value(subsurface_scale) = 0.005f;
node->custom1 = SHD_GLOSSY_MULTI_GGX;
node->custom2 = SHD_SUBSURFACE_RANDOM_WALK;
node->location[0] = -200.0f;
node->location[1] = 100.0f;
BKE_ntree_update_tag_node_property(ma.nodetree, node);
}
else if (node->type_legacy == SH_NODE_SUBSURFACE_SCATTERING) {
node->custom1 = SHD_SUBSURFACE_RANDOM_WALK;
BKE_ntree_update_tag_node_property(ma.nodetree, node);
}
else if (node->type_legacy == SH_NODE_OUTPUT_MATERIAL) {
node->location[0] = 200.0f;
node->location[1] = 100.0f;
}
}
}
}
/* Brushes */
{
/* Remove default brushes replaced by assets. Also remove outliner `treestore` that may point
* to brushes. Normally the treestore is updated properly but it doesn't seem to update during
* versioning code. It's not helpful anyway. */
for (bScreen &screen : bmain->screens) {
for (ScrArea &area : screen.areabase) {
for (SpaceLink &space_link : area.spacedata) {
if (space_link.spacetype == SPACE_OUTLINER) {
SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(&space_link);
if (space_outliner->treestore) {
BLI_mempool_destroy(space_outliner->treestore);
space_outliner->treestore = nullptr;
}
}
}
}
}
for (Brush &brush : bmain->brushes.items_mutable()) {
BKE_id_delete(bmain, &brush);
}
}
{
for (Light &light : bmain->lights) {
light.shadow_maximum_resolution = 0.001f;
light.transmission_fac = 1.0f;
SET_FLAG_FROM_TEST(light.mode, false, LA_SHAD_RES_ABSOLUTE);
}
}
{
for (World &world : bmain->worlds) {
SET_FLAG_FROM_TEST(world.flag, true, WO_USE_SUN_SHADOW);
if (world.nodetree) {
for (bNode *node : world.nodetree->all_nodes()) {
if (node->type_legacy == SH_NODE_OUTPUT_WORLD) {
node->location[0] = 200.0f;
node->location[1] = 100.0f;
}
else if (node->type_legacy == SH_NODE_BACKGROUND) {
node->location[0] = -200.0f;
node->location[1] = 100.0f;
}
}
}
}
}
/* Grease Pencil Anti-Aliasing. */
{
for (Scene &scene : bmain->scenes) {
scene.grease_pencil_settings.smaa_threshold = 1.0f;
scene.grease_pencil_settings.smaa_threshold_render = 0.25f;
scene.grease_pencil_settings.aa_samples = 8;
scene.grease_pencil_settings.motion_blur_steps = 8;
}
}
}
} // namespace blender

View File

@@ -0,0 +1,67 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blenloader
*
* Apply edits to DNA at load time to behave as if old files were written with new names.
*/
#include "DNA_genfile.h"
#include "readfile.hh"
namespace blender {
void blo_do_versions_dna(SDNA *sdna, const int versionfile, const int subversionfile)
{
#define DNA_VERSION_ATLEAST(ver, subver) \
(versionfile > (ver) || (versionfile == (ver) && (subversionfile >= (subver))))
if (!DNA_VERSION_ATLEAST(280, 2)) {
/* Version files created in the 'blender2.8' branch
* between October 2016, and November 2017 (>=280.0 and < 280.2). */
if (versionfile >= 280) {
DNA_sdna_patch_struct_by_name(sdna, "SceneLayer", "ViewLayer");
DNA_sdna_patch_struct_member_by_name(
sdna, "FileGlobal", "cur_render_layer", "cur_view_layer");
DNA_sdna_patch_struct_member_by_name(
sdna, "ParticleEditSettings", "scene_layer", "view_layer");
DNA_sdna_patch_struct_member_by_name(sdna, "Scene", "active_layer", "active_view_layer");
DNA_sdna_patch_struct_member_by_name(sdna, "Scene", "render_layers", "view_layers");
DNA_sdna_patch_struct_member_by_name(sdna, "WorkSpace", "render_layer", "view_layer");
}
}
if (!DNA_VERSION_ATLEAST(500, 51)) {
/* These old struct names were only used by an experimental feature. They were renamed before
* the feature became official. This versioning just allows to read old files but does not
* provide forward compatibility. */
DNA_sdna_patch_struct_by_name(sdna, "NodeGeometryClosureInput", "NodeClosureInput");
DNA_sdna_patch_struct_by_name(sdna, "NodeGeometryClosureInputItem", "NodeClosureInputItem");
DNA_sdna_patch_struct_by_name(sdna, "NodeGeometryClosureOutputItem", "NodeClosureOutputItem");
DNA_sdna_patch_struct_by_name(sdna, "NodeGeometryClosureInputItems", "NodeClosureInputItems");
DNA_sdna_patch_struct_by_name(
sdna, "NodeGeometryClosureOutputItems", "NodeClosureOutputItems");
DNA_sdna_patch_struct_by_name(sdna, "NodeGeometryClosureOutput", "NodeClosureOutput");
DNA_sdna_patch_struct_by_name(
sdna, "NodeGeometryEvaluateClosureInputItem", "NodeEvaluateClosureInputItem");
DNA_sdna_patch_struct_by_name(
sdna, "NodeGeometryEvaluateClosureOutputItem", "NodeEvaluateClosureOutputItem");
DNA_sdna_patch_struct_by_name(
sdna, "NodeGeometryEvaluateClosureInputItems", "NodeEvaluateClosureInputItems");
DNA_sdna_patch_struct_by_name(
sdna, "NodeGeometryEvaluateClosureOutputItems", "NodeEvaluateClosureOutputItems");
DNA_sdna_patch_struct_by_name(sdna, "NodeGeometryEvaluateClosure", "NodeEvaluateClosure");
DNA_sdna_patch_struct_by_name(sdna, "NodeGeometryCombineBundleItem", "NodeCombineBundleItem");
DNA_sdna_patch_struct_by_name(sdna, "NodeGeometryCombineBundle", "NodeCombineBundle");
DNA_sdna_patch_struct_by_name(
sdna, "NodeGeometrySeparateBundleItem", "NodeSeparateBundleItem");
DNA_sdna_patch_struct_by_name(sdna, "NodeGeometrySeparateBundle", "NodeSeparateBundle");
}
#undef DNA_VERSION_ATLEAST
}
} // namespace blender

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,61 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blenloader
*/
/**
* This file is a template to use as base when switching to a new version of Blender.
*
* DO NOT add this file to CMakeList.txt.
*
* When initializing a new version of Blender in main, after branching out the current one into
* its release branch:
* - Copy that file and rename it to the proper new version number (e.g. `versioning_510.cc`).
* - Rename the two functions below by replacing the `xxx` with the matching new version number.
* - Add the new file to CMakeList.txt
* - Add matching calls in #do_versions_after_linking and #do_versions, in `readfile.cc` and update
* declarations in`readfile.hh`.
*/
#define DNA_DEPRECATED_ALLOW
#include "DNA_ID.h"
#include "BLI_sys_types.h"
#include "BKE_main.hh"
#include "readfile.hh"
#include "versioning_common.hh"
// #include "CLG_log.h"
namespace blender {
// static CLG_LogRef LOG = {"blend.doversion"};
void do_versions_after_linking_xxx(FileData * /*fd*/, Main * /*bmain*/)
{
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
*
* \note Keep this message at the bottom of the function.
*/
}
void blo_do_versions_xxx(FileData * /*fd*/, Library * /*lib*/, Main * /*bmain*/)
{
/**
* 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.
*/
}
} // namespace blender

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,150 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup blenloader
* blenloader readfile private function prototypes.
*/
#pragma once
#include <ostream>
#include "DNA_sdna_pointers.hh"
#include "DNA_sdna_types.h"
#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLO_undofile.hh"
namespace blender {
class WriteWrap;
struct WriteDataStableAddressIDs {
/**
* Knows which DNA members are pointers. Those members are overridden when serializing the
* .blend file to get more stable pointer identifiers.
*/
std::shared_ptr<dna::pointers::PointersInDNA> sdna_pointers;
/**
* Maps each runtime-pointer to a unique identifier that's written in the .blend file.
*
* Currently, for regular blendfile writing, no pointers are ever removed from this map during
* writing of a single file.
* Correctness wise, this is fine. However, when some data-blocks write temporary addresses,
* those may be reused across IDs while actually pointing to different data. This can break
* address id stability in some situations. In the future this could be improved by clearing
* such temporary pointers before writing the next data-block.
*
* In undo case, only a very small sub-set of 'generated' addresses (temp data generated for
* writefile only) is added to this mapping, most pointers are written as-is (since their
* addresses are always stable in undo context: if unchanged, address remains the same).
* The mapping is also cleared after every ID writing, to allow temp allocated data to re-use the
* same source address across different IDs. The generated address ids will always be unique
* though (see also #used_ids description below).
*/
Map<const void *, uint64_t> pointer_map;
/**
* Contains all the #pointer_map.values() ever generated during a writefile operation.
*
* This is used to make sure that the same id is never reused for a different pointer. While this
* is technically allowed in .blend files (when the pointers are local data of different
* objects), we currently don't always know what type a pointer points to when writing it. So we
* can't determine if a pointer is local or not.
*
* Note that in undo (memfile) case, this set is _not_ reset after every ID writing, to ensure
* that all generated address ids remain unique across a whole blend-file writing operation.
*/
Set<uint64_t> used_ids;
/**
* The next stable address id is derived from this. This is modified in
* two cases:
* - A new stable address is needed, in which case this is just incremented.
* - A new "section" of the .blend file starts. In this case, this should be reinitialized with
* some hash of an identifier of the next section. This makes sure that if the number of
* pointers in the previous section is modified, the pointers in the new section are not
* affected. A "section" can be anything, but currently a section simply starts when a new
* data-block starts. In the future, an API could be added that allows sections to start
* within a data-block which could isolate stable pointer ids even more.
*
* When creating the new address id, keep in mind that this may be 0 and it may collide with
* previous hints.
*/
uint64_t next_id_hint = 0;
};
struct WriteData {
const SDNA *sdna;
std::ostream *debug_dst = nullptr;
struct {
/** Use for file and memory writing (size stored in max_size). */
uchar *buf;
/** Number of bytes used in #WriteData.buf (flushed when exceeded). */
size_t used_len;
/** Maximum size of the buffer. */
size_t max_size;
/** Threshold above which writes get their own chunk. */
size_t chunk_size;
} buffer;
#ifdef USE_WRITE_DATA_LEN
/** Total number of bytes written. */
size_t write_len;
#endif
/** Whether writefile code is currently writing an ID. */
bool is_writing_id;
/** Some validation and error handling data. */
struct {
/**
* Set on unlikely case of an error (ignores further file writing). Only used for very
* low-level errors (like if the actual write on file fails).
*/
bool critical_error;
/**
* A set of all 'old' addresses used as UID of written blocks for the current ID. Allows
* detecting invalid re-uses of the same address multiple times.
*/
Set<const void *> per_id_addresses_set;
} validation_data;
/**
* Data to generate stable fake pointer values in written blendfile.
*
* \note For undo steps, a partial copy of this data is stored in the written MemFile at the end
* of the writing, and used to initialize this data on the next undo step writing (see
* #BLO_memfile_write_init and #BLO_memfile_write_finalize).
*/
WriteDataStableAddressIDs stable_address_ids;
/**
* Keeps track of which shared data has been written for the current ID. This is necessary to
* avoid writing the same data more than once.
*/
Set<const void *> per_id_written_shared_addresses;
/** #MemFile writing (used for undo). */
MemFileWriteData mem;
/** When true, write to #WriteData.current, could also call 'is_undo'. */
bool use_memfile;
/**
* Wrap writing, so we can use ZSTD or
* other compression types later, see: #G_FILE_COMPRESS.
* Will be nullptr for UNDO.
*/
WriteWrap *ww;
/**
* Timestamp info defined when creating the new WriteData. Used for performance logging.
*/
double timestamp_init;
};
} // namespace blender