Add Chromium-only Blender WebEngine parity work
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
/** \file
|
||||
* \ingroup blenloader
|
||||
* \brief Utilities ensuring `.blend` file (i.e. Main)
|
||||
* is in valid state during write and/or read process.
|
||||
*/
|
||||
|
||||
struct Main;
|
||||
struct ReportList;
|
||||
|
||||
/**
|
||||
* Check (but do *not* fix) that all linked data-blocks are still valid
|
||||
* (i.e. pointing to the right library).
|
||||
*/
|
||||
bool BLO_main_validate_libraries(Main *bmain, ReportList *reports);
|
||||
/**
|
||||
* * Check (and fix if needed) that shape key's 'from' pointer is valid.
|
||||
*/
|
||||
bool BLO_main_validate_shapekeys(Main *bmain, ReportList *reports);
|
||||
|
||||
/**
|
||||
* Check that the `ID_FLAG_EMBEDDED_DATA_LIB_OVERRIDE` flag for embedded IDs actually matches
|
||||
* reality of embedded IDs being used by a liboverride ID.
|
||||
*
|
||||
* This is needed because embedded IDs did not get their flag properly cleared when runtime data
|
||||
* was split in `ID.tag`, which can create crashing situations in some rare cases, see #117795.
|
||||
*/
|
||||
void BLO_main_validate_embedded_liboverrides(Main *bmain, ReportList *reports);
|
||||
|
||||
/**
|
||||
* Check that the `ID_FLAG_EMBEDDED_DATA` flag is correctly set for embedded IDs, and not for any
|
||||
* Main ID.
|
||||
*
|
||||
* NOTE: It is unknown why/how this can happen, but there are some files out there that have e.g.
|
||||
* Objects flagged as embedded data... See e.g. the `(Anim) Hero p23 for 2.blend` file from our
|
||||
* cloud gallery (https://cloud.blender.org/p/gallery/5b642e25bf419c1042056fc6).
|
||||
*/
|
||||
void BLO_main_validate_embedded_flag(Main *bmain, ReportList *reports);
|
||||
|
||||
} // namespace blender
|
||||
543
blender-5.2.0/source/blender/blenloader/BLO_read_write.hh
Normal file
543
blender-5.2.0/source/blender/blenloader/BLO_read_write.hh
Normal file
@@ -0,0 +1,543 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup blenloader
|
||||
*
|
||||
* This file contains an API that allows different parts of Blender to define what data is stored
|
||||
* in .blend files.
|
||||
*
|
||||
* Four callbacks have to be provided to fully implement .blend I/O for a piece of data. One of
|
||||
* those is related to file writing and three for file reading. Reading requires multiple
|
||||
* callbacks, due to the way linking between files works.
|
||||
*
|
||||
* Brief description of the individual callbacks:
|
||||
* - Blend Write: Define which structs and memory buffers are saved.
|
||||
* - Blend Read Data: Loads structs and memory buffers from file and updates pointers them.
|
||||
* - Blend Read Lib: Updates pointers to ID data blocks.
|
||||
* - Blend Expand: Defines which other data blocks should be loaded (possibly from other files).
|
||||
* Note, this is now handled as part of the foreach-id iteration. This needs to be implemented
|
||||
* for DNA data that has references to data-blocks.
|
||||
*
|
||||
* Each of these callbacks uses a different API functions.
|
||||
*
|
||||
* Some parts of Blender, e.g. modifiers, don't require you to implement all four callbacks.
|
||||
* Instead only the first two are necessary. The other two are handled by general ID management. In
|
||||
* the future, we might want to get rid of those two callbacks entirely, but for now they are
|
||||
* necessary.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_sdna_type_ids.hh"
|
||||
|
||||
#include "BLI_dynamic_stack_buffer.hh"
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_implicit_sharing.hh"
|
||||
#include "BLI_map.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
class ImplicitSharingInfo;
|
||||
struct BlendFileReadReport;
|
||||
struct BlendLibReader;
|
||||
struct ID;
|
||||
struct ListBase;
|
||||
struct Main;
|
||||
struct WriteData;
|
||||
struct FileData;
|
||||
enum eReportType : uint16_t;
|
||||
|
||||
struct BlendWriter {
|
||||
WriteData *wd = nullptr;
|
||||
|
||||
void write_struct_by_name(const char *struct_name, const void *data);
|
||||
void write_struct_by_id(int struct_id, const void *data);
|
||||
void write_struct_at_address_by_id(int struct_id, const void *address, const void *data);
|
||||
void write_struct_at_address_by_id_with_filecode(int filecode,
|
||||
int struct_id,
|
||||
const void *address,
|
||||
const void *data);
|
||||
void write_struct_array_by_name(const char *struct_name, int64_t array_size, const void *data);
|
||||
void write_struct_array_by_id(int struct_id, int64_t array_size, const void *data);
|
||||
void write_struct_array_at_address_by_id(int struct_id,
|
||||
int64_t array_size,
|
||||
const void *address,
|
||||
const void *data);
|
||||
void write_struct_list_by_name(const char *struct_name, ListBase *list);
|
||||
void write_struct_list_by_id(int struct_id, const ListBase *list);
|
||||
|
||||
/**
|
||||
* Write raw data.
|
||||
*
|
||||
* \warning Avoid using this method if possible. There are only a very few cases in current
|
||||
* code where it is actually needed (e.g. the ShapeKey's data, since its items size varies
|
||||
* depending on the type of geometry owning it, see #shapekey_blend_write).
|
||||
*
|
||||
* \warning Data written with this call have no type information attached to them
|
||||
* in the blend-file. The main consequence is that there will be no handling of endianness
|
||||
* conversion for them in readfile code.
|
||||
* Basic typed array methods (like #write_int8_array etc.) also use this
|
||||
* internally, but if their matching read function is used to load the data (like
|
||||
* #BLO_read_array), the read function will take care of endianness conversion.
|
||||
*/
|
||||
void write_raw(size_t size_in_bytes, const void *data);
|
||||
|
||||
/** Write typed arrays. */
|
||||
void write_char_array(int64_t num, const char *data);
|
||||
void write_int8_array(int64_t num, const int8_t *data);
|
||||
void write_int16_array(int64_t num, const int16_t *data);
|
||||
void write_uint8_array(int64_t num, const uint8_t *data);
|
||||
void write_int32_array(int64_t num, const int32_t *data);
|
||||
void write_uint32_array(int64_t num, const uint32_t *data);
|
||||
void write_float_array(int64_t num, const float *data);
|
||||
void write_double_array(int64_t num, const double *data);
|
||||
void write_float3_array(int64_t num, const float *data);
|
||||
void write_pointer_array(int64_t num, const void *data);
|
||||
|
||||
/** Write a null terminated string. */
|
||||
void write_string(const char *data);
|
||||
|
||||
int struct_id_by_name(const char *struct_name) const;
|
||||
|
||||
template<typename T> void write_struct(const T *data)
|
||||
{
|
||||
this->write_struct_by_id(dna::sdna_struct_id_get<T>(), data);
|
||||
}
|
||||
|
||||
template<typename T> void write_struct_cast(const void *data)
|
||||
{
|
||||
this->write_struct_by_id(dna::sdna_struct_id_get<T>(), data);
|
||||
}
|
||||
|
||||
template<typename T> void write_struct_at_address(const void *address, const T *data)
|
||||
{
|
||||
this->write_struct_at_address_by_id(dna::sdna_struct_id_get<T>(), address, data);
|
||||
}
|
||||
|
||||
template<typename T> void write_struct_at_address_cast(const void *address, const void *data)
|
||||
{
|
||||
this->write_struct_at_address_by_id(dna::sdna_struct_id_get<T>(), address, data);
|
||||
}
|
||||
|
||||
template<typename T> void write_struct_array(const int64_t array_size, const T *data)
|
||||
{
|
||||
this->write_struct_array_by_id(dna::sdna_struct_id_get<T>(), array_size, data);
|
||||
}
|
||||
|
||||
template<typename T> void write_struct_array_cast(const int64_t array_size, const void *data)
|
||||
{
|
||||
this->write_struct_array_by_id(dna::sdna_struct_id_get<T>(), array_size, data);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void write_struct_array_at_address(const int64_t array_size, const void *address, const T *data)
|
||||
{
|
||||
this->write_struct_array_at_address_by_id(
|
||||
dna::sdna_struct_id_get<T>(), array_size, address, data);
|
||||
}
|
||||
|
||||
template<typename T> void write_struct_list(const ListBaseT<T> *list)
|
||||
{
|
||||
this->write_struct_list_by_id(dna::sdna_struct_id_get<T>(), list);
|
||||
}
|
||||
|
||||
template<typename T> void write_id_struct(const void *id_address, const T *id)
|
||||
{
|
||||
this->write_struct_at_address_by_id_with_filecode(
|
||||
GS(id_cast<const ID *>(id)->name), dna::sdna_struct_id_get<T>(), id_address, id);
|
||||
}
|
||||
};
|
||||
|
||||
struct BlendDataReader {
|
||||
/** Pointer to private #FileData in readfile.cc. */
|
||||
FileData *fd = nullptr;
|
||||
|
||||
/**
|
||||
* The key is the old address id referencing shared data that's written to a file, typically an
|
||||
* array. The corresponding value is the shared data at run-time.
|
||||
*/
|
||||
Map<uint64_t, ImplicitSharingInfoAndData> shared_data_by_stored_address;
|
||||
};
|
||||
|
||||
struct BlendLibReader {
|
||||
FileData *fd;
|
||||
Main *main;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Blend Write API
|
||||
*
|
||||
* Most functions fall into one of two categories. Either they write a DNA struct or a raw memory
|
||||
* buffer to the .blend file.
|
||||
*
|
||||
* It is safe to pass NULL as data_ptr. In this case nothing will be stored.
|
||||
*
|
||||
* DNA Struct Writing
|
||||
* ------------------
|
||||
*
|
||||
* Functions dealing with DNA structs begin with `BLO_write_struct_*`.
|
||||
*
|
||||
* DNA struct types can be identified in different ways:
|
||||
* - Run-time Name: The name is provided as `const char *`.
|
||||
* - Compile-time Name: The name is provided at compile time. This is more efficient.
|
||||
* - Struct ID: Every DNA struct type has an integer ID that can be queried with
|
||||
* #BlendWriter::struct_id_by_name. Providing this ID can be a useful optimization when many
|
||||
* structs of the same type are stored AND if those structs are not in a continuous array.
|
||||
*
|
||||
* Often only a single instance of a struct is written at once. However, sometimes it is necessary
|
||||
* to write arrays or linked lists. Separate functions for that are provided as well.
|
||||
*
|
||||
* There is a special macro for writing id structs: #BLO_write_id_struct.
|
||||
* Those are handled differently from other structs.
|
||||
*
|
||||
* Raw Data Writing
|
||||
* ----------------
|
||||
*
|
||||
* At the core there is #BlendWriter::write_raw, which can write arbitrary memory buffers to the
|
||||
* file. The code that reads this data might have to correct its byte-order. For the common cases
|
||||
* there are convenience functions that write and read arrays of simple types such as `int32`.
|
||||
* Those will correct endianness automatically.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Specific code to prepare IDs to be written.
|
||||
*
|
||||
* Required for writing properly embedded IDs currently.
|
||||
*
|
||||
* \note Once there is a better generic handling of embedded IDs,
|
||||
* this may go back to private code in `writefile.cc`.
|
||||
*/
|
||||
struct BLO_Write_IDBuffer {
|
||||
private:
|
||||
static constexpr int static_size = 8192;
|
||||
DynamicStackBuffer<static_size> buffer_;
|
||||
|
||||
public:
|
||||
BLO_Write_IDBuffer(ID &id, bool is_undo, bool is_placeholder);
|
||||
BLO_Write_IDBuffer(ID &id, BlendWriter *writer);
|
||||
|
||||
ID *get()
|
||||
{
|
||||
return static_cast<ID *>(buffer_.buffer());
|
||||
};
|
||||
};
|
||||
|
||||
/* Misc. */
|
||||
|
||||
/**
|
||||
* Check if the data can be written more efficiently by making use of implicit-sharing. If yes, the
|
||||
* user count of the sharing-info is increased making the data immutable. The provided callback
|
||||
* should serialize the potentially shared data. It is only called when necessary.
|
||||
*
|
||||
* \param approximate_size_in_bytes: Used to be able to approximate how large the undo step is in
|
||||
* total.
|
||||
* \param write_fn: Use the #BlendWrite to serialize the potentially shared data.
|
||||
*/
|
||||
void BLO_write_shared(BlendWriter *writer,
|
||||
const void *data,
|
||||
size_t approximate_size_in_bytes,
|
||||
const ImplicitSharingInfo *sharing_info,
|
||||
FunctionRef<void()> write_fn);
|
||||
|
||||
/**
|
||||
* Needs to be called for all pointers that _need_ to be 'stabilized' when writing undo steps,
|
||||
* _before_ any of these pointers are actually written (so typically at the very start of a write
|
||||
* function)..
|
||||
*
|
||||
* Typically required for data dynamically generated as part of the write process, see e.g.
|
||||
* AttributeStorage::dna_attributes.
|
||||
*/
|
||||
void BLO_write_generated_pointer_tag(BlendWriter *writer, const void *data);
|
||||
|
||||
/**
|
||||
* Sometimes different data is written depending on whether the file is saved to disk or used for
|
||||
* undo. This function returns true when the current file-writing is done for undo.
|
||||
*/
|
||||
bool BLO_write_is_undo(BlendWriter *writer);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Blend Read Data API
|
||||
*
|
||||
* Generally, for every BLO_write_* call there should be a corresponding BLO_read_* call.
|
||||
*
|
||||
* Most BLO_read_* functions get a pointer to a pointer as argument. That allows the function to
|
||||
* update the pointer to its new value.
|
||||
*
|
||||
* When the given pointer points to a memory buffer that was not stored in the file, the pointer is
|
||||
* updated to be NULL. When it was pointing to NULL before, it will stay that way.
|
||||
*
|
||||
* Examples of matching calls:
|
||||
*
|
||||
* \code{.c}
|
||||
* writer->write_struct(clmd->sim_parms);
|
||||
* BLO_read_struct(reader, ClothSimSettings, &clmd->sim_parms);
|
||||
*
|
||||
* writer->write_struct_list(&action->markers);
|
||||
* BLO_read_struct_list(reader, TimeMarker, &action->markers);
|
||||
*
|
||||
* writer->write_int32_array(hmd->totindex, hmd->indexar);
|
||||
* if (!BLO_read_array(reader, &hmd->indexar, hmd->totindex)) {
|
||||
* hmd->totindex = 0;
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* Avoid using the generic #BLO_read_raw_address when possible, use the typed functions instead.
|
||||
* Only data written with #BlendWriter::write_raw should typically be read with
|
||||
* #BLO_read_raw_address.
|
||||
* \{ */
|
||||
|
||||
void *blo_read_raw_address_impl(BlendDataReader *reader, const void *old_address);
|
||||
#define BLO_read_raw_address(reader, ptr_p) \
|
||||
*((void **)ptr_p) = blo_read_raw_address_impl((reader), *(ptr_p))
|
||||
|
||||
/**
|
||||
* Read function for pointers to structs.
|
||||
*
|
||||
* NOTE: Currently the usage of the type info is very minimal/basic, it does a loose check on
|
||||
* the data size and marks the blend file as invalid when it's mismatched.
|
||||
*/
|
||||
void *blo_read_struct_impl(BlendDataReader *reader, const void *old_address, size_t expected_size);
|
||||
#define BLO_read_struct(reader, struct_name, ptr_p) \
|
||||
(*((void **)ptr_p) = blo_read_struct_impl(reader, *((void **)ptr_p), sizeof(struct_name)))
|
||||
|
||||
/**
|
||||
* Like #BLO_read_struct, but mark the blend file as invalid (with an error report) when the
|
||||
* pointer was non-null but failed to resolve.
|
||||
*/
|
||||
void *blo_read_struct_nonnull_impl(BlendDataReader *reader,
|
||||
const void *old_address,
|
||||
size_t expected_size);
|
||||
#define BLO_read_struct_nonnull(reader, struct_name, ptr_p) \
|
||||
(*((void **)ptr_p) = blo_read_struct_nonnull_impl( \
|
||||
reader, *((void **)ptr_p), sizeof(struct_name)))
|
||||
|
||||
/**
|
||||
* Like #BLO_read_struct, but does not consider the read data as 'used'. It will still be freed
|
||||
* by readfile code at the end of the reading process, if no other 'real' usage was detected.
|
||||
*
|
||||
* Typical valid usages include:
|
||||
* - Restoring pointers to a specific item in an array or list (usually 'active' item e.g.). The
|
||||
* found item is expected to also be read as part of its array/list storage reading.
|
||||
* - Doing temporary access to deprecated data as part of some versioning code.
|
||||
*/
|
||||
void *blo_read_struct_no_us_impl(BlendDataReader *reader,
|
||||
const void *old_address,
|
||||
size_t expected_size);
|
||||
|
||||
#define BLO_read_struct_no_us(reader, struct_name, ptr_p) \
|
||||
(*((void **)ptr_p) = blo_read_struct_no_us_impl(reader, *((void **)ptr_p), sizeof(struct_name)))
|
||||
|
||||
#define BLO_read_struct_array_no_us(reader, struct_name, ptr_p, array_size) \
|
||||
(*((void **)ptr_p) = blo_read_struct_no_us_impl( \
|
||||
reader, *((void **)ptr_p), sizeof(struct_name) * size_t(array_size)))
|
||||
|
||||
/**
|
||||
* Like #BLO_read_struct_no_us, but with the same nonnull semantics as #BLO_read_struct_nonnull.
|
||||
*/
|
||||
void *blo_read_struct_no_us_nonnull_impl(BlendDataReader *reader,
|
||||
const void *old_address,
|
||||
size_t expected_size);
|
||||
|
||||
#define BLO_read_struct_no_us_nonnull(reader, struct_name, ptr_p) \
|
||||
(*((void **)ptr_p) = blo_read_struct_no_us_nonnull_impl( \
|
||||
reader, *((void **)ptr_p), sizeof(struct_name)))
|
||||
|
||||
/**
|
||||
* Similar to #BLO_read_struct, but can use a (DNA) type name instead of the type
|
||||
* itself to find the expected data size.
|
||||
*
|
||||
* Somewhat mirrors #BlendWriter::write_struct_array_by_name.
|
||||
*/
|
||||
void *BLO_read_struct_by_name_array(BlendDataReader *reader,
|
||||
const char *struct_name,
|
||||
int64_t items_num,
|
||||
const void *old_address);
|
||||
|
||||
/* Read all elements in list
|
||||
*
|
||||
* Updates all `->prev` and `->next` pointers of the list elements.
|
||||
* Updates the `list->first` and `list->last` pointers.
|
||||
*/
|
||||
void BLO_read_struct_list_with_size(BlendDataReader *reader,
|
||||
size_t expected_elem_size,
|
||||
ListBase *list);
|
||||
|
||||
#define BLO_read_struct_list(reader, struct_name, list) \
|
||||
BLO_read_struct_list_with_size(reader, sizeof(struct_name), list)
|
||||
|
||||
/**
|
||||
* Read an array of typed elements (struct or primitive type) from the file.
|
||||
*
|
||||
* With corrupt blend files the size may not match the array memory allocation.
|
||||
* This must be handled either by using #BLO_read_array_and_validate_size to
|
||||
* automatically set the size to zero, or checking the return value of
|
||||
* #BLO_read_array to manually handle invalid data.
|
||||
*
|
||||
* Typically #BLO_read_array_and_validate_size should be used for cases where
|
||||
* a size member is only for the array pointer, while a size member shared
|
||||
* between multiple array needs particular handling.
|
||||
*/
|
||||
[[nodiscard]] bool blo_read_array_impl(
|
||||
BlendDataReader *reader, int64_t array_size, int elems, size_t elem_size, void **ptr_p);
|
||||
|
||||
template<typename T, typename SizeT>
|
||||
requires(!std::is_void_v<T> && !std::is_pointer_v<T>)
|
||||
[[nodiscard]] bool BLO_read_array(BlendDataReader *reader,
|
||||
T **ptr_p,
|
||||
const SizeT array_size,
|
||||
const int elems = 1)
|
||||
{
|
||||
return blo_read_array_impl(
|
||||
reader, int64_t(array_size), elems, sizeof(T), reinterpret_cast<void **>(ptr_p));
|
||||
}
|
||||
|
||||
template<typename T, typename SizeT>
|
||||
requires(!std::is_void_v<T> && !std::is_pointer_v<T>)
|
||||
void BLO_read_array_and_validate_size(BlendDataReader *reader,
|
||||
T **ptr_p,
|
||||
SizeT *array_size,
|
||||
const int elems = 1)
|
||||
{
|
||||
if (!blo_read_array_impl(
|
||||
reader, int64_t(*array_size), elems, sizeof(T), reinterpret_cast<void **>(ptr_p)))
|
||||
{
|
||||
*array_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an array of pointers, converting between 32/64-bit pointer sizes if needed.
|
||||
* Same size mismatch handling as #BLO_read_array.
|
||||
*/
|
||||
[[nodiscard]] bool blo_read_pointer_array_impl(BlendDataReader *reader,
|
||||
int64_t array_size,
|
||||
void **ptr_p);
|
||||
|
||||
template<typename T, typename SizeT>
|
||||
requires(std::is_pointer_v<T> || std::is_void_v<T>)
|
||||
[[nodiscard]] bool BLO_read_pointer_array(BlendDataReader *reader,
|
||||
T **ptr_p,
|
||||
const SizeT array_size)
|
||||
{
|
||||
return blo_read_pointer_array_impl(reader, array_size, reinterpret_cast<void **>(ptr_p));
|
||||
}
|
||||
|
||||
template<typename T, typename SizeT>
|
||||
requires(std::is_pointer_v<T> || std::is_void_v<T>)
|
||||
void BLO_read_pointer_array_and_validate_size(BlendDataReader *reader,
|
||||
T **ptr_p,
|
||||
SizeT *array_size)
|
||||
{
|
||||
if (!blo_read_pointer_array_impl(reader, *array_size, reinterpret_cast<void **>(ptr_p))) {
|
||||
*array_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read null terminated string. */
|
||||
|
||||
void BLO_read_string(BlendDataReader *reader, char **ptr_p);
|
||||
void BLO_read_string(BlendDataReader *reader, char *const *ptr_p);
|
||||
void BLO_read_string(BlendDataReader *reader, const char **ptr_p);
|
||||
|
||||
/* Misc. */
|
||||
|
||||
ImplicitSharingInfoAndData blo_read_shared_impl(
|
||||
BlendDataReader *reader,
|
||||
const void **ptr_p,
|
||||
FunctionRef<const ImplicitSharingInfo *()> read_fn);
|
||||
|
||||
/**
|
||||
* Check if there is any shared data for the given data pointer. If yes, return the existing
|
||||
* sharing-info. If not, call the provided function to actually read the data now.
|
||||
*/
|
||||
template<typename T>
|
||||
const ImplicitSharingInfo *BLO_read_shared(BlendDataReader *reader,
|
||||
T **data_ptr,
|
||||
FunctionRef<const ImplicitSharingInfo *()> read_fn)
|
||||
{
|
||||
ImplicitSharingInfoAndData shared_data = blo_read_shared_impl(
|
||||
reader, (const void **)data_ptr, read_fn);
|
||||
/* Need const-cast here, because not all DNA members that reference potentially shared data are
|
||||
* const yet. */
|
||||
*data_ptr = const_cast<T *>(static_cast<const T *>(shared_data.data));
|
||||
return shared_data.sharing_info;
|
||||
}
|
||||
|
||||
int BLO_read_fileversion_get(BlendDataReader *reader);
|
||||
bool BLO_read_data_is_undo(BlendDataReader *reader);
|
||||
void BLO_read_data_globmap_add(BlendDataReader *reader, void *oldaddr, void *newaddr);
|
||||
void BLO_read_glob_list(BlendDataReader *reader, ListBase *list);
|
||||
BlendFileReadReport *BLO_read_data_reports(BlendDataReader *reader);
|
||||
struct Library *BLO_read_data_current_library(BlendDataReader *reader);
|
||||
void BLO_read_data_set_need_preview_render_restart(BlendDataReader *reader);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Blend Read Lib API
|
||||
*
|
||||
* This API does almost the same as the Blend Read Data API.
|
||||
* However, now only pointers to ID data blocks are updated.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Search for the new address of given `id`,
|
||||
* during library linking part of blend-file reading process.
|
||||
*
|
||||
* \param self_id: the ID owner of the given `id` pointer. Note that it may be an embedded ID.
|
||||
* \param is_linked_only: If `true`, only return found pointer if it is a linked ID. Used to
|
||||
* prevent linked data to point to local IDs.
|
||||
* \return the new address of the given ID pointer, or null if not found.
|
||||
*/
|
||||
ID *BLO_read_get_new_id_address(BlendLibReader *reader,
|
||||
ID *self_id,
|
||||
const bool is_linked_only,
|
||||
ID *id) ATTR_NONNULL(2);
|
||||
|
||||
/**
|
||||
* Search for the new address of the ID for the given `session_uid`.
|
||||
*
|
||||
* Only IDs existing in the newly read Main will be returned. If no matching `session_uid` in new
|
||||
* main can be found, `nullptr` is returned.
|
||||
*
|
||||
* This expected to be used during library-linking and/or 'undo_preserve' processes in undo case
|
||||
* (i.e. memfile reading), typically to find a valid value (or nullptr) for ID pointers values
|
||||
* coming from the previous, existing Main data, when it is preserved in newly read Main.
|
||||
* See e.g. the #scene_undo_preserve code-path.
|
||||
*/
|
||||
ID *BLO_read_get_new_id_address_from_session_uid(BlendLibReader *reader, uint session_uid)
|
||||
ATTR_NONNULL(1);
|
||||
|
||||
/* Misc. */
|
||||
|
||||
bool BLO_read_lib_is_undo(BlendLibReader *reader);
|
||||
Main *BLO_read_lib_get_main(BlendLibReader *reader);
|
||||
BlendFileReadReport *BLO_read_lib_reports(BlendLibReader *reader);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Report API
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* This function ensures that reports are printed,
|
||||
* in the case of library linking errors this is important!
|
||||
*
|
||||
* NOTE(@ideasman42) a kludge but better than doubling up on prints,
|
||||
* we could alternatively have a versions of a report function which forces printing.
|
||||
*/
|
||||
void BLO_reportf_wrap(BlendFileReadReport *reports, eReportType type, const char *format, ...)
|
||||
ATTR_PRINTF_FORMAT(3, 4);
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
635
blender-5.2.0/source/blender/blenloader/BLO_readfile.hh
Normal file
635
blender-5.2.0/source/blender/blenloader/BLO_readfile.hh
Normal file
@@ -0,0 +1,635 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "DNA_ID_enums.h"
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_sys_types.h"
|
||||
#include "BLI_utility_mixins.hh"
|
||||
struct BlendHandle;
|
||||
namespace blender {
|
||||
|
||||
/** \file
|
||||
* \ingroup blenloader
|
||||
* \brief external readfile function prototypes.
|
||||
*/
|
||||
|
||||
struct AssetMetaData;
|
||||
struct BHead;
|
||||
struct BlendfileLinkAppendContext;
|
||||
struct BlendThumbnail;
|
||||
struct FileData;
|
||||
struct FileReader;
|
||||
struct ID;
|
||||
struct Library;
|
||||
struct LinkNode;
|
||||
struct Main;
|
||||
struct MemFile;
|
||||
struct PreviewImage;
|
||||
struct ReportList;
|
||||
struct Scene;
|
||||
struct UserDef;
|
||||
struct View3D;
|
||||
struct ViewLayer;
|
||||
struct WorkSpace;
|
||||
struct bScreen;
|
||||
struct wmWindowManager;
|
||||
|
||||
struct WorkspaceConfigFileData {
|
||||
Main *main; /* has to be freed when done reading file data */
|
||||
|
||||
ListBaseT<WorkSpace> workspaces;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name BLO Read File API
|
||||
*
|
||||
* \see #BLO_write_file for file writing.
|
||||
* \{ */
|
||||
|
||||
enum eBlenFileType {
|
||||
BLENFILETYPE_BLEND = 1,
|
||||
// BLENFILETYPE_PUB = 2, /* UNUSED */
|
||||
// BLENFILETYPE_RUNTIME = 3, /* UNUSED */
|
||||
};
|
||||
|
||||
struct BlendFileData : NonCopyable, NonMovable {
|
||||
Main *main = nullptr;
|
||||
UserDef *user = nullptr;
|
||||
|
||||
int fileflags = 0;
|
||||
int globalf = 0;
|
||||
/** 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 filepath[/*FILE_MAX*/ 1024] = {};
|
||||
|
||||
/** TODO: think this isn't needed anymore? */
|
||||
bScreen *curscreen = nullptr;
|
||||
Scene *curscene = nullptr;
|
||||
/** Layer to activate in workspaces when reading without UI. */
|
||||
ViewLayer *cur_view_layer = nullptr;
|
||||
|
||||
eBlenFileType type = eBlenFileType(0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Data used by WM readfile code and BKE's setup_app_data to handle the complex preservation logic
|
||||
* of WindowManager and other UI data-blocks across blend-file reading process.
|
||||
*/
|
||||
struct BlendFileReadWMSetupData {
|
||||
/** The existing WM when file-reading process is started. */
|
||||
wmWindowManager *old_wm;
|
||||
|
||||
/** The startup file is being read. */
|
||||
bool is_read_homefile;
|
||||
/** The factory startup file is being read. */
|
||||
bool is_factory_startup;
|
||||
};
|
||||
|
||||
struct BlendFileReadParams {
|
||||
uint skip_flags : 3; /* #eBLOReadSkip */
|
||||
uint is_startup : 1;
|
||||
uint is_factory_settings : 1;
|
||||
|
||||
/** Whether we are reading the memfile for an undo or a redo. */
|
||||
int undo_direction; /* #eUndoStepDir */
|
||||
};
|
||||
|
||||
struct BlendFileReadReport {
|
||||
/** General reports handling. */
|
||||
ReportList *reports;
|
||||
|
||||
/** Timing information. */
|
||||
struct {
|
||||
double whole;
|
||||
double libraries;
|
||||
double lib_overrides;
|
||||
double lib_overrides_resync;
|
||||
double lib_overrides_recursive_resync;
|
||||
} duration;
|
||||
|
||||
/** Count information. */
|
||||
struct {
|
||||
/**
|
||||
* Some numbers of IDs that ended up in a specific state, or required some specific process
|
||||
* during this file read.
|
||||
*/
|
||||
int missing_libraries;
|
||||
int missing_linked_id;
|
||||
/** Some sub-categories of the above `missing_linked_id` counter. */
|
||||
int missing_obdata;
|
||||
int missing_obproxies;
|
||||
|
||||
/** Number of root override IDs that were resynced. */
|
||||
int resynced_lib_overrides;
|
||||
|
||||
/** Number of proxies converted to library overrides. */
|
||||
int proxies_to_lib_overrides_success;
|
||||
/** Number of proxies that failed to convert to library overrides. */
|
||||
int proxies_to_lib_overrides_failures;
|
||||
/** Number of sequencer strips that were not read because were in non-supported channels. */
|
||||
int sequence_strips_skipped;
|
||||
} count;
|
||||
|
||||
/**
|
||||
* Number of libraries which had overrides that needed to be resynced,
|
||||
* and a single linked list of those.
|
||||
*/
|
||||
int resynced_lib_overrides_libraries_count;
|
||||
bool do_resynced_lib_overrides_libraries_list;
|
||||
LinkNode *resynced_lib_overrides_libraries;
|
||||
|
||||
/** Whether a pre-2.50 blend file was loaded, in which case any animation is lost. */
|
||||
bool pre_animato_file_loaded;
|
||||
};
|
||||
|
||||
/** Skip reading some data-block types (may want to skip screen data too). */
|
||||
enum eBLOReadSkip {
|
||||
BLO_READ_SKIP_NONE = 0,
|
||||
/** Skip #BLO_CODE_USER blocks. */
|
||||
BLO_READ_SKIP_USERDEF = (1 << 0),
|
||||
/** Only read #BLO_CODE_USER (and associated data). */
|
||||
BLO_READ_SKIP_DATA = (1 << 1),
|
||||
/** Do not attempt to re-use IDs from old bmain for unchanged ones in case of undo. */
|
||||
BLO_READ_SKIP_UNDO_OLD_MAIN = (1 << 2),
|
||||
};
|
||||
ENUM_OPERATORS(eBLOReadSkip)
|
||||
#define BLO_READ_SKIP_ALL (BLO_READ_SKIP_USERDEF | BLO_READ_SKIP_DATA)
|
||||
|
||||
/**
|
||||
* Open a blender file from a `filepath`. The function returns NULL
|
||||
* and sets a report in the list if it cannot open the file.
|
||||
*
|
||||
* \param filepath: The path of the file to open.
|
||||
* \param reports: If the return value is NULL, errors indicating the cause of the failure.
|
||||
* \return The data of the file.
|
||||
*/
|
||||
BlendFileData *BLO_read_from_file(const char *filepath,
|
||||
eBLOReadSkip skip_flags,
|
||||
BlendFileReadReport *reports);
|
||||
/**
|
||||
* Open a blender file from memory. The function returns NULL
|
||||
* and sets a report in the list if it cannot open the file.
|
||||
*
|
||||
* \param mem: The file data.
|
||||
* \param memsize: The length of \a mem.
|
||||
* \param reports: If the return value is NULL, errors indicating the cause of the failure.
|
||||
* \return The data of the file.
|
||||
*/
|
||||
BlendFileData *BLO_read_from_memory(const void *mem,
|
||||
int memsize,
|
||||
eBLOReadSkip skip_flags,
|
||||
ReportList *reports);
|
||||
/**
|
||||
* Used for undo/redo, skips part of libraries reading
|
||||
* (assuming their data are already loaded & valid).
|
||||
*
|
||||
* \param oldmain: old main,
|
||||
* from which we will keep libraries and other data-blocks that should not have changed.
|
||||
* \param filepath: current file, only for retrieving library data.
|
||||
* Typically `BKE_main_blendfile_path(oldmain)`.
|
||||
*/
|
||||
BlendFileData *BLO_read_from_memfile(Main *oldmain,
|
||||
const char *filepath,
|
||||
MemFile *memfile,
|
||||
const BlendFileReadParams *params,
|
||||
ReportList *reports);
|
||||
|
||||
/**
|
||||
* Frees a BlendFileData structure and *all* the data associated with it
|
||||
* (the userdef data, and the main libblock data).
|
||||
*
|
||||
* \param bfd: The structure to free.
|
||||
*/
|
||||
void BLO_blendfiledata_free(BlendFileData *bfd);
|
||||
|
||||
/**
|
||||
* Does versioning code that requires the Main data-base to be fully loaded and valid.
|
||||
*
|
||||
* readfile's `do_versions` does not allow to create (or delete) IDs, and only operates on a single
|
||||
* library at a time.
|
||||
*
|
||||
* Called at the end of #setup_add_data from BKE's `blendfile.cc`.
|
||||
*
|
||||
* \param new_bmain: the newly read Main data-base.
|
||||
*/
|
||||
void BLO_read_do_version_after_setup(Main *new_bmain,
|
||||
BlendfileLinkAppendContext *lapp_context,
|
||||
BlendFileReadReport *reports);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name BLO Blend File Handle API
|
||||
* \{ */
|
||||
|
||||
struct BLODataBlockInfo {
|
||||
struct Library {
|
||||
const char *filepath = nullptr;
|
||||
LibraryFlag flag = LibraryFlag(0);
|
||||
};
|
||||
|
||||
char name[/*MAX_ID_NAME-2*/ 256] = "";
|
||||
AssetMetaData *asset_data = nullptr;
|
||||
/** For Library IDs only: specific info, like the stored blendfile path, flags. */
|
||||
BLODataBlockInfo::Library library_data = {};
|
||||
/** Ownership over #asset_data above can be "stolen out" of this struct, for more permanent
|
||||
* storage. In that case, set this to false to avoid double freeing of the stolen data. */
|
||||
bool free_asset_data = false;
|
||||
/**
|
||||
* Optimization: Tag data-blocks for which we know there is no preview.
|
||||
* Knowing this can be used to skip the (potentially expensive) preview loading process. If this
|
||||
* is set to true it means we looked for a preview and couldn't find one. False may mean that
|
||||
* either no preview was found, or that it wasn't looked for in the first place.
|
||||
*/
|
||||
bool no_preview_found = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Frees contained data, not \a datablock_info itself.
|
||||
*/
|
||||
void BLO_datablock_info_free(BLODataBlockInfo *datablock_info);
|
||||
/**
|
||||
* Can be used to free the list returned by #BLO_blendhandle_get_datablock_info().
|
||||
*/
|
||||
void BLO_datablock_info_linklist_free(LinkNode * /*BLODataBlockInfo*/ datablock_infos);
|
||||
|
||||
/**
|
||||
* Open a blendhandle from a file path.
|
||||
*
|
||||
* \param filepath: The file path to open.
|
||||
* \param reports: Report errors in opening the file (can be NULL).
|
||||
* \return A handle on success, or NULL on failure.
|
||||
*/
|
||||
BlendHandle *BLO_blendhandle_from_file(const char *filepath, BlendFileReadReport *reports);
|
||||
/**
|
||||
* Open a blendhandle from memory.
|
||||
*
|
||||
* \param mem: The data to load from.
|
||||
* \param memsize: The size of the data.
|
||||
* \return A handle on success, or NULL on failure.
|
||||
*/
|
||||
BlendHandle *BLO_blendhandle_from_memory(const void *mem,
|
||||
int memsize,
|
||||
BlendFileReadReport *reports);
|
||||
|
||||
/** Returns the major and minor version number of Blender used to create the file. */
|
||||
int3 BLO_blendhandle_get_version(const BlendHandle *bh);
|
||||
|
||||
/**
|
||||
* Gets the names of all the data-blocks in a file of a certain type
|
||||
* (e.g. all the scene names in a file).
|
||||
*
|
||||
* \param bh: The blendhandle to access.
|
||||
* \param ofblocktype: The type of names to get.
|
||||
* \param use_assets_only: Only list IDs marked as assets.
|
||||
* \param r_tot_names: The length of the returned list.
|
||||
* \return A BLI_linklist of strings. The string links should be freed with #MEM_delete().
|
||||
*/
|
||||
LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh,
|
||||
int ofblocktype,
|
||||
|
||||
bool use_assets_only,
|
||||
int *r_tot_names);
|
||||
/**
|
||||
* Gets the names and asset-data (if ID is an asset) of data-blocks in a file of a certain type.
|
||||
* The data-blocks can be limited to assets.
|
||||
*
|
||||
* \param bh: The blendhandle to access.
|
||||
* \param ofblocktype: The type of names to get.
|
||||
* \param use_assets_only: Limit the result to assets only.
|
||||
* \param r_tot_info_items: The length of the returned list.
|
||||
*
|
||||
* \return A BLI_linklist of `BLODataBlockInfo *`.
|
||||
*
|
||||
* \note The links should be freed using #BLO_datablock_info_free() or the entire list using
|
||||
* #BLO_datablock_info_linklist_free().
|
||||
*/
|
||||
LinkNode * /*BLODataBlockInfo*/ BLO_blendhandle_get_datablock_info(BlendHandle *bh,
|
||||
int ofblocktype,
|
||||
bool use_assets_only,
|
||||
int *r_tot_info_items);
|
||||
/**
|
||||
* Get the PreviewImage of a single data block in a file.
|
||||
* (e.g. all the scene previews in a file).
|
||||
*
|
||||
* \param bh: The blendhandle to access.
|
||||
* \param ofblocktype: The type of names to get.
|
||||
* \param name: Name of the block without the ID_ prefix, to read the preview image from.
|
||||
* \return PreviewImage or NULL when no preview Images have been found. Caller owns the returned
|
||||
*/
|
||||
PreviewImage *BLO_blendhandle_get_preview_for_id(BlendHandle *bh,
|
||||
int ofblocktype,
|
||||
const char *name);
|
||||
/**
|
||||
* Gets the names of all the linkable data-block types available in a file.
|
||||
* (e.g. "Scene", "Mesh", "Light", etc.).
|
||||
*
|
||||
* \param bh: The blendhandle to access.
|
||||
* \return A BLI_linklist of strings. The string links should be freed with #MEM_delete().
|
||||
*/
|
||||
LinkNode *BLO_blendhandle_get_linkable_groups(BlendHandle *bh);
|
||||
|
||||
/**
|
||||
* Close and free a blendhandle. The handle becomes invalid after this call.
|
||||
*
|
||||
* \param bh: The handle to close.
|
||||
*/
|
||||
void BLO_blendhandle_close(BlendHandle *bh) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* Mark the given Main (and the 'root' local one in case of lib-split Mains) as invalid, and
|
||||
* generate an error report containing given `message`.
|
||||
*/
|
||||
void BLO_read_invalidate_message(BlendHandle *bh, Main *bmain, const char *message);
|
||||
|
||||
/**
|
||||
* BLI_assert-like macro to check a condition, and if `false`, fail the whole .blend reading
|
||||
* process by marking the Main data-base as invalid, and returning provided `_ret_value`.
|
||||
*
|
||||
* NOTE: About usages:
|
||||
* - #BLI_assert should be used when the error is considered as a bug, but there is some code to
|
||||
* recover from it and produce a valid Main data-base.
|
||||
* - #BLO_read_assert_message should be used when the error is not considered as recoverable.
|
||||
*/
|
||||
#define BLO_read_assert_message(_check_expr, _ret_value, _bh, _bmain, _message) \
|
||||
if (_check_expr) { \
|
||||
BLO_read_invalidate_message((_bh), (_bmain), (_message)); \
|
||||
return _ret_value; \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
/** \} */
|
||||
|
||||
#define BLO_GROUP_MAX 32
|
||||
#define BLO_EMBEDDED_STARTUP_BLEND "<startup.blend>"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name BLO Blend File Linking API
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Options controlling behavior of append/link code.
|
||||
* \note merged with 'user-level' options from operators etc. in 16 lower bits
|
||||
* (see #eFileSel_Params_Flag in DNA_space_types.h).
|
||||
*/
|
||||
enum eBLOLibLinkFlags {
|
||||
/** Generate a placeholder (empty ID) if not found in current lib file. */
|
||||
BLO_LIBLINK_USE_PLACEHOLDERS = 1 << 16,
|
||||
/** Force loaded ID to be tagged as #ID_TAG_INDIRECT (used in reload context only). */
|
||||
BLO_LIBLINK_FORCE_INDIRECT = 1 << 17,
|
||||
/**
|
||||
* Set the object active when #OB_FLAG_ACTIVE_CLIPBOARD is set.
|
||||
* Used for copy & paste so the active object is preserved.
|
||||
*/
|
||||
BLO_LIBLINK_APPEND_SET_OB_ACTIVE_CLIPBOARD = 1 << 18,
|
||||
/** Set fake user on appended IDs. */
|
||||
BLO_LIBLINK_APPEND_SET_FAKEUSER = 1 << 19,
|
||||
/**
|
||||
* Append (make local) also indirect dependencies of appended IDs coming from other libraries.
|
||||
* NOTE: All IDs (including indirectly linked ones) coming from the same initial library are
|
||||
* always made local.
|
||||
*/
|
||||
BLO_LIBLINK_APPEND_RECURSIVE = 1 << 20,
|
||||
/** Try to re-use previously appended matching ID on new append. */
|
||||
BLO_LIBLINK_APPEND_LOCAL_ID_REUSE = 1 << 21,
|
||||
/** Clear the asset data. */
|
||||
BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR = 1 << 22,
|
||||
/** Instantiate object data IDs (i.e. create objects for them if needed). */
|
||||
BLO_LIBLINK_OBDATA_INSTANCE = 1 << 24,
|
||||
/** Instantiate collections as empties, instead of linking them into current view layer. */
|
||||
BLO_LIBLINK_COLLECTION_INSTANCE = 1 << 25,
|
||||
/**
|
||||
* Do not rebuild collections hierarchy runtime data (mainly the parents info)
|
||||
* as part of #BLO_library_link_end.
|
||||
* Needed when some IDs have been temporarily removed from Main,
|
||||
* see e.g. #BKE_blendfile_library_relocate.
|
||||
*/
|
||||
BLO_LIBLINK_COLLECTION_NO_HIERARCHY_REBUILD = 1 << 26,
|
||||
/**
|
||||
* Pack the linked data-blocks to keep them working even if the source file is not available.
|
||||
*/
|
||||
BLO_LIBLINK_PACK = 1 << 27,
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct for passing arguments to
|
||||
* #BLO_library_link_begin, #BLO_library_link_named_part & #BLO_library_link_end.
|
||||
* Wrap these in parameters since it's important both functions receive matching values.
|
||||
*/
|
||||
struct LibraryLink_Params {
|
||||
/** The current main database, e.g. #G_MAIN or `CTX_data_main(C)`. */
|
||||
Main *bmain;
|
||||
/** Options for linking, used for instantiating. */
|
||||
int flag;
|
||||
/** Additional tag for #ID.tag. */
|
||||
int id_tag_extra;
|
||||
/** Context for instancing objects (optional, no instantiation will be performed when NULL). */
|
||||
struct {
|
||||
/** The scene in which to instantiate objects/collections. */
|
||||
Scene *scene;
|
||||
/** The scene layer in which to instantiate objects/collections. */
|
||||
ViewLayer *view_layer;
|
||||
/** The active 3D viewport (only used to define local-view). */
|
||||
const View3D *v3d;
|
||||
} context;
|
||||
};
|
||||
|
||||
void BLO_library_link_params_init(LibraryLink_Params *params,
|
||||
Main *bmain,
|
||||
int flag,
|
||||
int id_tag_extra);
|
||||
void BLO_library_link_params_init_with_context(LibraryLink_Params *params,
|
||||
Main *bmain,
|
||||
int flag,
|
||||
int id_tag_extra,
|
||||
Scene *scene,
|
||||
ViewLayer *view_layer,
|
||||
const View3D *v3d);
|
||||
|
||||
/**
|
||||
* Initialize the #BlendHandle for linking library data.
|
||||
*
|
||||
* \param bh: A blender file handle as returned by
|
||||
* #BLO_blendhandle_from_file or #BLO_blendhandle_from_memory.
|
||||
* \param filepath: Used for relative linking, copied to the `lib->filepath`.
|
||||
* \param params: Settings for linking that don't change from beginning to end of linking.
|
||||
* \return the library #Main, to be passed to #BLO_library_link_named_part as \a mainl.
|
||||
*/
|
||||
Main *BLO_library_link_begin(BlendHandle **bh,
|
||||
const char *filepath,
|
||||
const LibraryLink_Params *params);
|
||||
/**
|
||||
* Link a named data-block from an external blend file.
|
||||
*
|
||||
* \param mainl: The main database to link from (not the active one).
|
||||
* \param bh: The blender file handle.
|
||||
* \param idcode: The kind of data-block to link.
|
||||
* \param name: The name of the data-block (without the 2 char ID prefix).
|
||||
* \return the linked ID when found.
|
||||
*/
|
||||
ID *BLO_library_link_named_part(Main *mainl,
|
||||
BlendHandle **bh,
|
||||
short idcode,
|
||||
const char *name,
|
||||
const LibraryLink_Params *params);
|
||||
/**
|
||||
* Finalize linking from a given .blend file (library).
|
||||
* Optionally instance the indirect object/collection in the scene when the flags are set.
|
||||
* \note Do not use \a bh after calling this function, it may frees it.
|
||||
*
|
||||
* \param mainl: The main database to link from (not the active one).
|
||||
* \param bh: The blender file handle (WARNING! may be freed by this function!).
|
||||
* \param params: Settings for linking that don't change from beginning to end of linking.
|
||||
*/
|
||||
void BLO_library_link_end(Main *mainl,
|
||||
BlendHandle **bh,
|
||||
const LibraryLink_Params *params,
|
||||
ReportList *reports);
|
||||
|
||||
/**
|
||||
* Struct for temporarily loading datablocks from a blend file.
|
||||
*/
|
||||
struct TempLibraryContext {
|
||||
/** Temporary main used to load data into (currently initialized from `real_main`). */
|
||||
Main *bmain_base;
|
||||
BlendFileReadReport bf_reports;
|
||||
|
||||
/** The ID datablock that was loaded. Is NULL if loading failed. */
|
||||
ID *temp_id;
|
||||
};
|
||||
|
||||
TempLibraryContext *BLO_library_temp_load_id(Main *real_main,
|
||||
const char *blend_file_path,
|
||||
short idcode,
|
||||
const char *idname,
|
||||
ReportList *reports);
|
||||
void BLO_library_temp_free(TempLibraryContext *temp_lib_ctx);
|
||||
|
||||
/** \} */
|
||||
|
||||
void *BLO_library_read_struct(FileData *fd, BHead *bh, const char *blockname);
|
||||
|
||||
/**
|
||||
* Update defaults in startup.blend, without having to save and embed it.
|
||||
* \note defaults for preferences are stored in `userdef_default.c` and can be updated there.
|
||||
*/
|
||||
/**
|
||||
* Update defaults in startup.blend, without having to save and embed the file.
|
||||
* This function can be emptied each time the startup.blend is updated.
|
||||
*
|
||||
* \note Screen data may be cleared at this point, this will happen in the case
|
||||
* an app-template's data needs to be versioned when read-file is called with "Load UI" disabled.
|
||||
* Versioning the screen data can be safely skipped without "Load UI" since the screen data
|
||||
* will have been versioned when it was first loaded.
|
||||
*/
|
||||
void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template);
|
||||
void BLO_update_defaults_workspace(WorkSpace *workspace, const char *app_template);
|
||||
|
||||
/** Disable unwanted experimental feature settings on startup. */
|
||||
void BLO_sanitize_experimental_features_userpref_blend(UserDef *userdef);
|
||||
|
||||
/**
|
||||
* Does a very light reading of given .blend file to extract its stored thumbnail.
|
||||
*
|
||||
* \param filepath: The path of the file to extract thumbnail from.
|
||||
* \return The raw thumbnail
|
||||
* (MEM-allocated, as stored in file, use #BKE_main_thumbnail_to_imbuf()
|
||||
* to convert it to ImBuf image).
|
||||
*/
|
||||
BlendThumbnail *BLO_thumbnail_from_file(const char *filepath);
|
||||
|
||||
/**
|
||||
* Does a very light reading of given .blend file to extract its version.
|
||||
*
|
||||
* \param filepath: The path of the blend file to extract version from.
|
||||
* \return The file version
|
||||
*/
|
||||
short BLO_version_from_file(const char *filepath);
|
||||
|
||||
/**
|
||||
* Runtime structure on `ID.runtime.readfile_data` that is available during the readfile process.
|
||||
*
|
||||
* This is intended for short-lived data, for example for things that are detected in an early
|
||||
* phase of versioning that should be used in a later stage of versioning.
|
||||
*
|
||||
* \note This is NOT allocated when 'reading' an undo step, as that doesn't have to deal with
|
||||
* versioning, linking, and the other stuff that this struct was meant for.
|
||||
*/
|
||||
struct ID_Readfile_Data {
|
||||
struct Tags {
|
||||
/* General ID reading related tags. */
|
||||
|
||||
/**
|
||||
* Mark ID placeholders for linked data-blocks needing to be read from their library
|
||||
* blend-files.
|
||||
*/
|
||||
bool is_link_placeholder : 1;
|
||||
/**
|
||||
* Mark IDs needing to be expanded (only done once). See #expand_main.
|
||||
*/
|
||||
bool needs_expanding : 1;
|
||||
/**
|
||||
* Mark IDs needing to be 'lib-linked', i.e. to get their pointers to other data-blocks
|
||||
* updated from the 'UID' values stored in `.blend` files to the new, actual pointers.
|
||||
*/
|
||||
bool needs_linking : 1;
|
||||
|
||||
/**
|
||||
* Memfile undo only: mark IDs used by 'no undo' IDs (e.g. brush dependencies).
|
||||
*
|
||||
* This is currently used to ensure that all linked 'no undo' IDs are preserved and remain
|
||||
* fully valid across undo steps (also used to tag libraries containing such no-undo linked
|
||||
* IDs).
|
||||
*/
|
||||
bool used_by_no_undo_id : 1;
|
||||
|
||||
/* Specific ID-type reading/versioning related tags. */
|
||||
|
||||
/**
|
||||
* Set when this ID used a legacy Action, in which case it also should pick
|
||||
* an appropriate slot.
|
||||
*
|
||||
* \see ANIM_versioning.hh
|
||||
*/
|
||||
bool action_assignment_needs_slot : 1;
|
||||
} tags;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return `id->runtime->readfile_data->tags` if the `readfile_data` is allocated,
|
||||
* otherwise return an all-zero set of tags.
|
||||
*/
|
||||
ID_Readfile_Data::Tags BLO_readfile_id_runtime_tags(const ID &id);
|
||||
|
||||
/**
|
||||
* Create the `readfile_data` if needed, and return `id->runtime->readfile_data->tags`.
|
||||
*
|
||||
* Use it instead of #BLO_readfile_id_runtime_tags when tags need to be set.
|
||||
*/
|
||||
ID_Readfile_Data::Tags &BLO_readfile_id_runtime_tags_for_write(ID &id);
|
||||
|
||||
/**
|
||||
* Free the #ID_Readfile_Data of all IDs in this bmain and all their embedded IDs.
|
||||
*
|
||||
* This is typically called at the end of the versioning process, as after that
|
||||
* `ID.runtime.readfile_data` should no longer be needed.
|
||||
*/
|
||||
void BLO_readfile_id_runtime_data_free_all(Main &bmain);
|
||||
|
||||
/**
|
||||
* Free the #ID_Readfile_Data of this ID. Does _not_ deal with embedded IDs.
|
||||
*/
|
||||
void BLO_readfile_id_runtime_data_free(ID &id);
|
||||
|
||||
#define BLEN_THUMB_MEMSIZE_FILE(_x, _y) (sizeof(int) * (2 + size_t(_x) * size_t(_y)))
|
||||
|
||||
} // namespace blender
|
||||
121
blender-5.2.0/source/blender/blenloader/BLO_undofile.hh
Normal file
121
blender-5.2.0/source/blender/blenloader/BLO_undofile.hh
Normal file
@@ -0,0 +1,121 @@
|
||||
/* SPDX-FileCopyrightText: 2004 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup blenloader
|
||||
* External write-file function prototypes.
|
||||
*/
|
||||
|
||||
#include "BLI_filereader.h"
|
||||
#include "BLI_implicit_sharing.hh"
|
||||
#include "BLI_map.hh"
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Main;
|
||||
struct Scene;
|
||||
struct WriteData;
|
||||
struct WriteDataStableAddressIDs;
|
||||
|
||||
struct MemFileSharedStorage {
|
||||
/**
|
||||
* Maps the address id to the shared data and corresponding sharing info..
|
||||
*/
|
||||
Map<uint64_t, ImplicitSharingInfoAndData> sharing_info_by_address_id;
|
||||
|
||||
~MemFileSharedStorage();
|
||||
};
|
||||
|
||||
struct MemFileChunk {
|
||||
void *next, *prev;
|
||||
const char *buf;
|
||||
/** Size in bytes. */
|
||||
size_t size;
|
||||
/** When true, this chunk doesn't own the memory, it's shared with a previous #MemFileChunk */
|
||||
bool is_identical;
|
||||
/** When true, this chunk is also identical to the one in the next step (used by undo code to
|
||||
* detect unchanged IDs).
|
||||
* Defined when writing the next step (i.e. last undo step has those always false). */
|
||||
bool is_identical_future;
|
||||
/** Session UID of the ID being currently written (MAIN_ID_SESSION_UID_UNSET when not writing
|
||||
* ID-related data). Used to find matching chunks in previous memundo step. */
|
||||
uint id_session_uid;
|
||||
};
|
||||
|
||||
struct MemFile {
|
||||
ListBaseT<MemFileChunk> chunks;
|
||||
size_t size;
|
||||
/**
|
||||
* Some data is not serialized into a new buffer because the undo-step can take ownership of it
|
||||
* without making a copy. This is faster and requires less memory.
|
||||
*/
|
||||
MemFileSharedStorage *shared_storage;
|
||||
};
|
||||
|
||||
struct MemFileWriteData {
|
||||
MemFile *written_memfile;
|
||||
MemFile *reference_memfile;
|
||||
|
||||
uint current_id_session_uid;
|
||||
MemFileChunk *reference_current_chunk;
|
||||
|
||||
/** Maps an ID session uid to its first reference MemFileChunk, if existing. */
|
||||
Map<uint, MemFileChunk *> id_session_uid_mapping;
|
||||
};
|
||||
|
||||
struct MemFileUndoData {
|
||||
char filepath[/*FILE_MAX*/ 1024];
|
||||
MemFile memfile;
|
||||
size_t undo_size;
|
||||
};
|
||||
|
||||
/* FileReader-compatible wrapper for reading MemFiles */
|
||||
struct UndoReader {
|
||||
FileReader reader;
|
||||
|
||||
MemFile *memfile;
|
||||
int undo_direction;
|
||||
|
||||
bool memchunk_identical;
|
||||
};
|
||||
|
||||
/* Actually only used `writefile.cc`. */
|
||||
|
||||
void BLO_memfile_write_init(WriteData *wd,
|
||||
MemFileWriteData *mem_data,
|
||||
MemFile *written_memfile,
|
||||
MemFile *reference_memfile);
|
||||
void BLO_memfile_write_finalize(WriteData *wd, MemFileWriteData *mem_data);
|
||||
|
||||
void BLO_memfile_chunk_add(MemFileWriteData *mem_data, const char *buf, size_t size);
|
||||
|
||||
/* exports */
|
||||
|
||||
/**
|
||||
* Not memfile itself.
|
||||
*/
|
||||
/* **************** support for memory-write, for undo buffers *************** */
|
||||
|
||||
void BLO_memfile_free(MemFile *memfile);
|
||||
/**
|
||||
* Result is that 'first' is being freed.
|
||||
* To keep the #MemFile linked list of consistent, `first` is always first in list.
|
||||
*/
|
||||
void BLO_memfile_merge(MemFile *first, MemFile *second);
|
||||
/**
|
||||
* Clear is_identical_future before adding next memfile.
|
||||
*/
|
||||
void BLO_memfile_clear_future(MemFile *memfile);
|
||||
|
||||
/* Utilities. */
|
||||
|
||||
Main *BLO_memfile_main_get(MemFile *memfile, Main *bmain, Scene **r_scene);
|
||||
|
||||
FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction);
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,22 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Default theme, see: `release/datafiles/userdef/userdef_default_theme.c`. */
|
||||
extern const bTheme U_theme_default;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace blender
|
||||
85
blender-5.2.0/source/blender/blenloader/BLO_writefile.hh
Normal file
85
blender-5.2.0/source/blender/blenloader/BLO_writefile.hh
Normal file
@@ -0,0 +1,85 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup blenloader
|
||||
* \brief external `writefile.cc` function prototypes.
|
||||
*/
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BlendThumbnail;
|
||||
struct Main;
|
||||
struct MemFile;
|
||||
struct ReportList;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name BLO Write File API
|
||||
*
|
||||
* \see #BLO_read_from_file for file reading.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Adjust paths when saving (kept unless #BlendFileWriteParams.use_save_as_copy is set).
|
||||
*/
|
||||
enum eBLO_WritePathRemap {
|
||||
/** No path manipulation. */
|
||||
BLO_WRITE_PATH_REMAP_NONE = 0,
|
||||
/** Remap existing relative paths (default). */
|
||||
BLO_WRITE_PATH_REMAP_RELATIVE = 1,
|
||||
/** Remap paths making all paths relative to the new location. */
|
||||
BLO_WRITE_PATH_REMAP_RELATIVE_ALL = 2,
|
||||
/** Make all paths absolute. */
|
||||
BLO_WRITE_PATH_REMAP_ABSOLUTE = 3,
|
||||
};
|
||||
|
||||
/** Similar to #BlendFileReadParams. */
|
||||
struct BlendFileWriteParams {
|
||||
eBLO_WritePathRemap remap_mode = {};
|
||||
/** Save `.blend1`, `.blend2`... etc. */
|
||||
uint use_save_versions : 1 = false;
|
||||
/** On write, restore paths after editing them (see #BLO_WRITE_PATH_REMAP_RELATIVE). */
|
||||
uint use_save_as_copy : 1 = false;
|
||||
uint use_userdef : 1 = false;
|
||||
/** This is writing a copy/paste buffer, not a regular blendfile. */
|
||||
uint is_copypaste_buffer : 1 = false;
|
||||
const BlendThumbnail *thumb = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* \return Success.
|
||||
*/
|
||||
extern bool BLO_write_file(Main *mainvar,
|
||||
const char *filepath,
|
||||
int write_flags,
|
||||
const BlendFileWriteParams *params,
|
||||
ReportList *reports);
|
||||
|
||||
/**
|
||||
* \return Success.
|
||||
*/
|
||||
extern bool BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int write_flags);
|
||||
|
||||
using BLO_WriteFileCallback = bool (*)(const void *data, size_t size, void *user_data);
|
||||
|
||||
/**
|
||||
* Write a regular, non-undo blend file through a caller-provided byte sink.
|
||||
*
|
||||
* This keeps the same ID filtering and serialization semantics as #BLO_write_file without
|
||||
* requiring a temporary filesystem path.
|
||||
*
|
||||
* \return Success.
|
||||
*/
|
||||
extern bool BLO_write_file_to_callback(Main *mainvar,
|
||||
int write_flags,
|
||||
BLO_WriteFileCallback callback,
|
||||
void *user_data);
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
150
blender-5.2.0/source/blender/blenloader/CMakeLists.txt
Normal file
150
blender-5.2.0/source/blender/blenloader/CMakeLists.txt
Normal file
@@ -0,0 +1,150 @@
|
||||
# SPDX-FileCopyrightText: 2006 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
PUBLIC .
|
||||
../editors/include
|
||||
../makesrna
|
||||
|
||||
# RNA_prototypes.hh
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
${CMAKE_SOURCE_DIR}/release/datafiles/userdef/userdef_default_theme.c
|
||||
intern/blend_validate.cc
|
||||
intern/readblenentry.cc
|
||||
intern/readfile.cc
|
||||
intern/readfile_tempload.cc
|
||||
intern/undofile.cc
|
||||
intern/versioning_250.cc
|
||||
intern/versioning_260.cc
|
||||
intern/versioning_270.cc
|
||||
intern/versioning_280.cc
|
||||
intern/versioning_290.cc
|
||||
intern/versioning_300.cc
|
||||
intern/versioning_400.cc
|
||||
intern/versioning_410.cc
|
||||
intern/versioning_420.cc
|
||||
intern/versioning_430.cc
|
||||
intern/versioning_440.cc
|
||||
intern/versioning_450.cc
|
||||
intern/versioning_500.cc
|
||||
intern/versioning_510.cc
|
||||
intern/versioning_520.cc
|
||||
intern/versioning_common.cc
|
||||
intern/versioning_defaults.cc
|
||||
intern/versioning_dna.cc
|
||||
intern/versioning_legacy.cc
|
||||
intern/versioning_userdef.cc
|
||||
intern/writefile.cc
|
||||
|
||||
BLO_blend_validate.hh
|
||||
BLO_read_write.hh
|
||||
BLO_readfile.hh
|
||||
BLO_undofile.hh
|
||||
BLO_userdef_default.h
|
||||
BLO_writefile.hh
|
||||
versioning_common.hh
|
||||
intern/readfile.hh
|
||||
intern/writefile.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::animrig
|
||||
PRIVATE bf::asset_system
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PUBLIC bf::blenloader_core
|
||||
PRIVATE bf::blentranslation
|
||||
PRIVATE bf::bmesh
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::draw
|
||||
PRIVATE bf::gpu
|
||||
PRIVATE bf::imbuf
|
||||
PRIVATE bf::imbuf::movie
|
||||
PRIVATE bf::intern::clog
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::intern::memutil
|
||||
PRIVATE bf::nodes
|
||||
PRIVATE bf::render
|
||||
PRIVATE bf::sequencer
|
||||
PRIVATE bf::windowmanager
|
||||
PRIVATE bf::extern::xxhash
|
||||
PRIVATE bf::dependencies::zstd
|
||||
)
|
||||
|
||||
if(WITH_BUILDINFO)
|
||||
add_definitions(-DWITH_BUILDINFO)
|
||||
endif()
|
||||
|
||||
if(WITH_CODEC_FFMPEG)
|
||||
add_definitions(-DWITH_FFMPEG)
|
||||
endif()
|
||||
|
||||
if(WITH_ALEMBIC)
|
||||
list(APPEND INC
|
||||
../io/alembic
|
||||
)
|
||||
add_definitions(-DWITH_ALEMBIC)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_blenloader "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
add_library(bf::blenloader ALIAS bf_blenloader)
|
||||
|
||||
# RNA_prototypes.hh
|
||||
add_dependencies(bf_blenloader bf_rna)
|
||||
|
||||
if(WITH_GTESTS)
|
||||
# Utility functions for test also used by other tests.
|
||||
set(TEST_UTIL_SRC
|
||||
tests/blendfile_loading_base_test.cc
|
||||
tests/blendfile_loading_base_test.h
|
||||
)
|
||||
set(TEST_UTIL_INC
|
||||
${INC}
|
||||
../../../tests/gtests
|
||||
../../../intern/ghost
|
||||
)
|
||||
set(TEST_UTIL_INC_SYS
|
||||
${INC_SYS}
|
||||
${CMAKE_SOURCE_DIR}/extern/gtest/include
|
||||
)
|
||||
set(TEST_UTIL_LIB
|
||||
${LIB}
|
||||
PRIVATE bf::blenfont
|
||||
bf_blenloader
|
||||
PRIVATE bf::dependencies::gflags
|
||||
PRIVATE bf::dependencies::glog
|
||||
)
|
||||
blender_add_lib(bf_blenloader_test_util "${TEST_UTIL_SRC}" "${TEST_UTIL_INC}" "${TEST_UTIL_INC_SYS}" "${TEST_UTIL_LIB}")
|
||||
|
||||
# Actual `blenloader` tests.
|
||||
set(TEST_SRC
|
||||
tests/blendfile_load_test.cc
|
||||
)
|
||||
set(TEST_LIB
|
||||
${LIB}
|
||||
bf_blenloader
|
||||
bf_blenloader_test_util
|
||||
)
|
||||
blender_add_test_suite_lib(blenloader "${TEST_SRC}" "${INC}" "${INC_SYS}" "${TEST_LIB}")
|
||||
endif()
|
||||
|
||||
if(WITH_EXPERIMENTAL_FEATURES)
|
||||
add_definitions(-DWITH_EXPERIMENTAL_FEATURES)
|
||||
endif()
|
||||
|
||||
if(WITH_WEB)
|
||||
# Web files are produced by the current Blender schema. Historical
|
||||
# migration units are editor/sequencer-heavy and are not part of the
|
||||
# headless evaluator; unsupported old file versions are rejected by the
|
||||
# Web API before entering this reader.
|
||||
list(FILTER SRC EXCLUDE REGEX "intern/versioning_.*\\.cc$")
|
||||
list(APPEND SRC intern/versioning_dna.cc)
|
||||
endif()
|
||||
278
blender-5.2.0/source/blender/blenloader/intern/blend_validate.cc
Normal file
278
blender-5.2.0/source/blender/blenloader/intern/blend_validate.cc
Normal 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
|
||||
470
blender-5.2.0/source/blender/blenloader/intern/readblenentry.cc
Normal file
470
blender-5.2.0/source/blender/blenloader/intern/readblenentry.cc
Normal 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
|
||||
6246
blender-5.2.0/source/blender/blenloader/intern/readfile.cc
Normal file
6246
blender-5.2.0/source/blender/blenloader/intern/readfile.cc
Normal file
File diff suppressed because it is too large
Load Diff
381
blender-5.2.0/source/blender/blenloader/intern/readfile.hh
Normal file
381
blender-5.2.0/source/blender/blenloader/intern/readfile.hh
Normal 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
|
||||
@@ -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
|
||||
286
blender-5.2.0/source/blender/blenloader/intern/undofile.cc
Normal file
286
blender-5.2.0/source/blender/blenloader/intern/undofile.cc
Normal 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
|
||||
2144
blender-5.2.0/source/blender/blenloader/intern/versioning_250.cc
Normal file
2144
blender-5.2.0/source/blender/blenloader/intern/versioning_250.cc
Normal file
File diff suppressed because it is too large
Load Diff
3068
blender-5.2.0/source/blender/blenloader/intern/versioning_260.cc
Normal file
3068
blender-5.2.0/source/blender/blenloader/intern/versioning_260.cc
Normal file
File diff suppressed because it is too large
Load Diff
1675
blender-5.2.0/source/blender/blenloader/intern/versioning_270.cc
Normal file
1675
blender-5.2.0/source/blender/blenloader/intern/versioning_270.cc
Normal file
File diff suppressed because it is too large
Load Diff
6334
blender-5.2.0/source/blender/blenloader/intern/versioning_280.cc
Normal file
6334
blender-5.2.0/source/blender/blenloader/intern/versioning_280.cc
Normal file
File diff suppressed because it is too large
Load Diff
1965
blender-5.2.0/source/blender/blenloader/intern/versioning_290.cc
Normal file
1965
blender-5.2.0/source/blender/blenloader/intern/versioning_290.cc
Normal file
File diff suppressed because it is too large
Load Diff
4301
blender-5.2.0/source/blender/blenloader/intern/versioning_300.cc
Normal file
4301
blender-5.2.0/source/blender/blenloader/intern/versioning_300.cc
Normal file
File diff suppressed because it is too large
Load Diff
1781
blender-5.2.0/source/blender/blenloader/intern/versioning_400.cc
Normal file
1781
blender-5.2.0/source/blender/blenloader/intern/versioning_400.cc
Normal file
File diff suppressed because it is too large
Load Diff
1182
blender-5.2.0/source/blender/blenloader/intern/versioning_410.cc
Normal file
1182
blender-5.2.0/source/blender/blenloader/intern/versioning_410.cc
Normal file
File diff suppressed because it is too large
Load Diff
1387
blender-5.2.0/source/blender/blenloader/intern/versioning_420.cc
Normal file
1387
blender-5.2.0/source/blender/blenloader/intern/versioning_420.cc
Normal file
File diff suppressed because it is too large
Load Diff
521
blender-5.2.0/source/blender/blenloader/intern/versioning_430.cc
Normal file
521
blender-5.2.0/source/blender/blenloader/intern/versioning_430.cc
Normal 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 = ®ion->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
|
||||
1006
blender-5.2.0/source/blender/blenloader/intern/versioning_440.cc
Normal file
1006
blender-5.2.0/source/blender/blenloader/intern/versioning_440.cc
Normal file
File diff suppressed because it is too large
Load Diff
5974
blender-5.2.0/source/blender/blenloader/intern/versioning_450.cc
Normal file
5974
blender-5.2.0/source/blender/blenloader/intern/versioning_450.cc
Normal file
File diff suppressed because it is too large
Load Diff
4532
blender-5.2.0/source/blender/blenloader/intern/versioning_500.cc
Normal file
4532
blender-5.2.0/source/blender/blenloader/intern/versioning_500.cc
Normal file
File diff suppressed because it is too large
Load Diff
934
blender-5.2.0/source/blender/blenloader/intern/versioning_510.cc
Normal file
934
blender-5.2.0/source/blender/blenloader/intern/versioning_510.cc
Normal 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
|
||||
924
blender-5.2.0/source/blender/blenloader/intern/versioning_520.cc
Normal file
924
blender-5.2.0/source/blender/blenloader/intern/versioning_520.cc
Normal 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
|
||||
@@ -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 ®ion : *regionbase) {
|
||||
if (region.regiontype == region_type) {
|
||||
return nullptr;
|
||||
}
|
||||
if (region.regiontype == link_after_region_type) {
|
||||
link_after_region = ®ion;
|
||||
}
|
||||
}
|
||||
|
||||
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 ®ion : *regionbase) {
|
||||
if (region.regiontype == region_type) {
|
||||
return ®ion;
|
||||
}
|
||||
if (region.regiontype == link_after_region_type) {
|
||||
link_after_region = ®ion;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -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 ®ion : 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 ®ion : 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(®ion.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 ®ion : area.regionbase) {
|
||||
if (region.regiontype == RGN_TYPE_CHANNELS) {
|
||||
region.flag |= RGN_FLAG_HIDDEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Open properties panel by default. */
|
||||
for (ARegion ®ion : 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 ®ion : 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 ®ion : *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
|
||||
@@ -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
|
||||
2537
blender-5.2.0/source/blender/blenloader/intern/versioning_legacy.cc
Normal file
2537
blender-5.2.0/source/blender/blenloader/intern/versioning_legacy.cc
Normal file
File diff suppressed because it is too large
Load Diff
1822
blender-5.2.0/source/blender/blenloader/intern/versioning_userdef.cc
Normal file
1822
blender-5.2.0/source/blender/blenloader/intern/versioning_userdef.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
2342
blender-5.2.0/source/blender/blenloader/intern/writefile.cc
Normal file
2342
blender-5.2.0/source/blender/blenloader/intern/writefile.cc
Normal file
File diff suppressed because it is too large
Load Diff
150
blender-5.2.0/source/blender/blenloader/intern/writefile.hh
Normal file
150
blender-5.2.0/source/blender/blenloader/intern/writefile.hh
Normal 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
|
||||
@@ -0,0 +1,22 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#include "blendfile_loading_base_test.h"
|
||||
|
||||
#include "BLI_path_utils.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
class BlendfileLoadingTest : public BlendfileLoadingBaseTest {};
|
||||
|
||||
TEST_F(BlendfileLoadingTest, CanaryTest)
|
||||
{
|
||||
/* Load the smallest blend file we have in the tests/files directory. */
|
||||
if (!blendfile_load("modifier_stack" SEP_STR "array_test.blend")) {
|
||||
return;
|
||||
}
|
||||
depsgraph_create(DAG_EVAL_RENDER);
|
||||
EXPECT_NE(nullptr, this->depsgraph);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,175 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#include "blendfile_loading_base_test.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BKE_appdir.hh"
|
||||
#include "BKE_blender.hh"
|
||||
#include "BKE_callbacks.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_cpp_types.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_idtype.hh"
|
||||
#include "BKE_image.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_mball_tessellate.hh"
|
||||
#include "BKE_modifier.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_scene.hh"
|
||||
#include "BKE_vfont.hh"
|
||||
|
||||
#include "BLF_api.hh"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_path_utils.hh"
|
||||
#include "BLI_threads.h"
|
||||
|
||||
#include "BLO_readfile.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
|
||||
#include "DNA_genfile.h" /* for DNA_sdna_current_init() */
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "IMB_imbuf.hh"
|
||||
|
||||
#include "ED_datafiles.h"
|
||||
|
||||
#include "RNA_define.hh"
|
||||
|
||||
#include "SEQ_modifier.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "wm.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void BlendfileLoadingBaseTest::SetUpTestCase()
|
||||
{
|
||||
testing::Test::SetUpTestCase();
|
||||
|
||||
/* Minimal code to make loading a blendfile and constructing a depsgraph not crash, copied from
|
||||
* main() in creator.c. */
|
||||
CLG_init();
|
||||
BLI_threadapi_init();
|
||||
|
||||
DNA_sdna_current_init();
|
||||
BKE_blender_globals_init();
|
||||
|
||||
BKE_idtype_init();
|
||||
BKE_cpp_types_init();
|
||||
BKE_appdir_init();
|
||||
IMB_init();
|
||||
BKE_modifier_init();
|
||||
seq::modifiers_init();
|
||||
DEG_register_node_types();
|
||||
RNA_init();
|
||||
bke::node_system_init();
|
||||
BKE_callback_global_init();
|
||||
BKE_vfont_builtin_register(datatoc_bfont_pfb, datatoc_bfont_pfb_size);
|
||||
BLF_init();
|
||||
|
||||
BKE_blender_globals_main_replace(BKE_main_new());
|
||||
|
||||
G.background = true;
|
||||
G.factory_startup = true;
|
||||
|
||||
/* Allocate a dummy window manager. The real window manager will try and load Python scripts from
|
||||
* the release directory, which it won't be able to find. */
|
||||
ASSERT_EQ(G.main->wm.first, nullptr);
|
||||
wmWindowManager *wm = BKE_id_new<wmWindowManager>(G.main, "WMdummy");
|
||||
wm->runtime = MEM_new<bke::WindowManagerRuntime>(__func__);
|
||||
}
|
||||
|
||||
void BlendfileLoadingBaseTest::TearDownTestCase()
|
||||
{
|
||||
/* Copied from WM_exit_ex() in wm_init_exit.cc, and cherry-picked those lines that match the
|
||||
* allocation/initialization done in SetUpTestCase(). */
|
||||
BKE_blender_free();
|
||||
RNA_exit();
|
||||
|
||||
BLF_exit();
|
||||
DEG_free_node_types();
|
||||
DNA_sdna_current_free();
|
||||
BLI_threadapi_exit();
|
||||
|
||||
BKE_blender_atexit();
|
||||
|
||||
BKE_tempdir_session_purge();
|
||||
BKE_appdir_exit();
|
||||
CLG_exit();
|
||||
|
||||
testing::Test::TearDownTestCase();
|
||||
}
|
||||
|
||||
void BlendfileLoadingBaseTest::TearDown()
|
||||
{
|
||||
BKE_mball_cubeTable_free();
|
||||
blendfile_free();
|
||||
depsgraph_free();
|
||||
|
||||
testing::Test::TearDown();
|
||||
}
|
||||
|
||||
bool BlendfileLoadingBaseTest::blendfile_load(const char *filepath)
|
||||
{
|
||||
const std::string &test_assets_dir = tests::flags_test_asset_dir();
|
||||
if (test_assets_dir.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char abspath[FILE_MAX];
|
||||
BLI_path_join(abspath, sizeof(abspath), test_assets_dir.c_str(), filepath);
|
||||
|
||||
BlendFileReadReport bf_reports = {};
|
||||
bfile = BLO_read_from_file(abspath, BLO_READ_SKIP_NONE, &bf_reports);
|
||||
if (bfile == nullptr) {
|
||||
ADD_FAILURE() << "Unable to load file '" << filepath << "' from test assets dir '"
|
||||
<< test_assets_dir << "'";
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Make sure that all view_layers in the file are synced. Depsgraph can make a copy of the whole
|
||||
* scene, which will fail when one view layer isn't synced. */
|
||||
for (ViewLayer &view_layer : bfile->curscene->view_layers) {
|
||||
BKE_view_layer_synced_ensure(*bfile->main, bfile->curscene, &view_layer);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BlendfileLoadingBaseTest::blendfile_free()
|
||||
{
|
||||
if (bfile == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
BLO_blendfiledata_free(bfile);
|
||||
bfile = nullptr;
|
||||
}
|
||||
|
||||
void BlendfileLoadingBaseTest::depsgraph_create(eEvaluationMode depsgraph_evaluation_mode)
|
||||
{
|
||||
depsgraph = DEG_graph_new(
|
||||
bfile->main, bfile->curscene, bfile->cur_view_layer, depsgraph_evaluation_mode);
|
||||
DEG_graph_build_from_view_layer(depsgraph);
|
||||
BKE_scene_graph_update_tagged(depsgraph, bfile->main);
|
||||
}
|
||||
|
||||
void BlendfileLoadingBaseTest::depsgraph_free()
|
||||
{
|
||||
if (depsgraph == nullptr) {
|
||||
return;
|
||||
}
|
||||
DEG_graph_free(depsgraph);
|
||||
depsgraph = nullptr;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,50 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Depsgraph;
|
||||
struct BlendFileData;
|
||||
|
||||
class BlendfileLoadingBaseTest : public testing::Test {
|
||||
protected:
|
||||
struct BlendFileData *bfile = nullptr;
|
||||
struct Depsgraph *depsgraph = nullptr;
|
||||
|
||||
public:
|
||||
/* Sets up Blender just enough to not crash on loading
|
||||
* a blendfile and constructing a depsgraph. */
|
||||
static void SetUpTestCase();
|
||||
static void TearDownTestCase();
|
||||
|
||||
protected:
|
||||
/* Frees the depsgraph & blendfile. */
|
||||
void TearDown() override;
|
||||
|
||||
/* Loads a blend file from the tests/files directory from SVN.
|
||||
* Returns 'ok' flag (true=good, false=bad) and sets `this->bfile`.
|
||||
* Fails the test if the file cannot be loaded (still returns though).
|
||||
* Requires the CLI argument `--test-asset-dir` to point to `../tests/files`.
|
||||
*
|
||||
* WARNING: only files saved with Blender 2.80+ can be loaded. Since Blender
|
||||
* is only partially initialized (most importantly, without window manager),
|
||||
* the space types are not registered, so any versioning code that handles
|
||||
* those will SEGFAULT.
|
||||
*/
|
||||
bool blendfile_load(const char *filepath);
|
||||
/* Free bfile if it is not nullptr. */
|
||||
void blendfile_free();
|
||||
|
||||
/* Create a depsgraph. Assumes a blend file has been loaded to this->bfile. */
|
||||
virtual void depsgraph_create(eEvaluationMode depsgraph_evaluation_mode);
|
||||
/* Free the depsgraph if it's not nullptr. */
|
||||
virtual void depsgraph_free();
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
319
blender-5.2.0/source/blender/blenloader/versioning_common.hh
Normal file
319
blender-5.2.0/source/blender/blenloader/versioning_common.hh
Normal file
@@ -0,0 +1,319 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup blenloader
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "BKE_fcurve.hh"
|
||||
|
||||
#include "RNA_define.hh"
|
||||
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ARegion;
|
||||
struct bNode;
|
||||
struct bNodeSocket;
|
||||
struct bNodeTree;
|
||||
struct ID;
|
||||
struct IDProperty;
|
||||
struct Main;
|
||||
struct ViewLayer;
|
||||
struct SceneRenderLayer;
|
||||
|
||||
/**
|
||||
* Mapping from new ID types to old converted ID types.
|
||||
*
|
||||
* This is used by linking code, when a directly linked data comes from a library where it exists
|
||||
* as an older, different type of ID.
|
||||
*
|
||||
* Since the current blend-file will convert that older ID type to the new one as part of its
|
||||
* versioning process, when saved, it will store a reference to that linked ID using the _new_ ID
|
||||
* type, which will not be found on next blend-file opening/reloading when searching for that ID in
|
||||
* the library blend-file.
|
||||
*
|
||||
* \returns The old, deprecated ID type code if any matches the given `id_code_new` one, otherwise
|
||||
* `ID_LINK_PLACEHOLDER`.
|
||||
*/
|
||||
short do_versions_new_to_old_idcode_get(short id_code_new);
|
||||
|
||||
/**
|
||||
* Check if a region of type \a region_type exists in \a regionbase. Otherwise add it after the
|
||||
* first region of type \a link_after_region_type.
|
||||
* \returns null if a region of the given type already existed, otherwise the newly added region.
|
||||
*/
|
||||
ARegion *do_versions_add_region_if_not_found(ListBaseT<ARegion> *regionbase,
|
||||
int region_type,
|
||||
const char *allocname,
|
||||
int link_after_region_type);
|
||||
/**
|
||||
* Check if a region of type \a region_type exists in \a regionbase. Otherwise add it after the
|
||||
* first region of type \a link_after_region_type.
|
||||
* \returns either a new, or already existing region.
|
||||
*/
|
||||
ARegion *do_versions_ensure_region(ListBaseT<ARegion> *regionbase,
|
||||
int region_type,
|
||||
const char *allocname,
|
||||
int link_after_region_type);
|
||||
|
||||
/**
|
||||
* Rename if the ID doesn't exist.
|
||||
*
|
||||
* \return the ID (if found).
|
||||
*/
|
||||
ID *do_versions_rename_id(Main *bmain, short id_type, const char *name_src, const char *name_dst);
|
||||
|
||||
bool version_node_socket_is_used(bNodeSocket *sock);
|
||||
|
||||
void version_node_socket_name(bNodeTree *ntree,
|
||||
int node_type,
|
||||
const char *old_name,
|
||||
const char *new_name);
|
||||
void version_node_input_socket_name(bNodeTree *ntree,
|
||||
int node_type,
|
||||
const char *old_name,
|
||||
const char *new_name);
|
||||
void version_node_output_socket_name(bNodeTree *ntree,
|
||||
int node_type,
|
||||
const char *old_name,
|
||||
const char *new_name);
|
||||
|
||||
/**
|
||||
* Find the base socket name for an idname that may include a subtype.
|
||||
*/
|
||||
StringRef legacy_socket_idname_to_socket_type(StringRef idname);
|
||||
|
||||
/**
|
||||
* Adds a new node for versioning purposes. This is intended to be used to create raw DNA that
|
||||
* might have been read from a file. The created node does not have storage or sockets. Both have
|
||||
* to be added manually afterwards.
|
||||
*
|
||||
* This may seem redundant because the set of sockets is already part of the node declaration.
|
||||
* However, the declaration should not be used here, because it changes over time. The versioning
|
||||
* code generally expects to get the sockets that the node had at the time of writing the
|
||||
* versioning code. Changing the declaration later can break the versioning code in ways that are
|
||||
* hard to detect.
|
||||
*
|
||||
* When adding new nodes in versioning code that replace or belong to existing nodes, they should
|
||||
* be positioned so that it overlaps the existing node with just a slight offset. This is better
|
||||
* than putting them next to each other they way one would do it manually, because it messes up
|
||||
* more complex node trees significantly. In simple tests, putting the nodes next to each other
|
||||
* looks better, but in actual user-files it looks way worse and makes it less obvious what was
|
||||
* changed by versioning code.
|
||||
*/
|
||||
bNode &version_node_add_empty(bNodeTree &ntree, const char *idname);
|
||||
|
||||
/**
|
||||
* Similar to #version_node_add_empty but doesn't require a valid #idname. This is typically
|
||||
* needed to write a node in a blend file for forward compatibility reasons, where the node was
|
||||
* removed and has no RNA definition anymore.
|
||||
*
|
||||
* The same rules defined in #version_node_add_empty apply here as well (node placement, separate
|
||||
* socket and storage definition etc..).
|
||||
*
|
||||
* The parameters are needed to create a valid #bNodeType to set it as node->typeinfo.
|
||||
* See also #bNodeType for more details.
|
||||
*/
|
||||
bNode &version_node_add_unknown(bNodeTree &ntree,
|
||||
bke::bNodeType &node_type,
|
||||
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 = 140.0f,
|
||||
const float height = 100.0f,
|
||||
const bool no_muting = false);
|
||||
|
||||
/**
|
||||
* Removes a node for versioning purposes:
|
||||
* - Animation data (#AnimData) are not removed, because they might be using #bAction.id which
|
||||
* is not be available before linking.
|
||||
* - User count is not updated. This is ensured after blend file reading is done.
|
||||
*/
|
||||
void version_node_remove(bNodeTree &ntree, bNode &node);
|
||||
bNodeSocket &version_node_add_socket(bNodeTree &ntree,
|
||||
bNode &node,
|
||||
eNodeSocketInOut in_out,
|
||||
const char *idname,
|
||||
const char *identifier);
|
||||
bNodeLink &version_node_add_link(
|
||||
bNodeTree &ntree, bNode &node_a, bNodeSocket &socket_a, bNode &node_b, bNodeSocket &socket_b);
|
||||
|
||||
/**
|
||||
* Returns true if the node has valid storage data.
|
||||
* If the node does not have storage data then the node type is set to "Undefined" to prevent
|
||||
* further access and the function returns false.
|
||||
*
|
||||
* Storage can get lost when saving nodes in older versions and then loading such files may contain
|
||||
* nodes where storage is expected but does not exist (#154086).
|
||||
*/
|
||||
bool version_node_ensure_storage_or_invalidate(bNode &node);
|
||||
|
||||
/**
|
||||
* Adjust animation data for newly added node sockets.
|
||||
*
|
||||
* Node sockets are addressed by their index (in their RNA path, and thus FCurves/drivers), and
|
||||
* thus when a new node is added in the middle of the list, existing animation data needs to be
|
||||
* adjusted.
|
||||
*
|
||||
* Since this is about animation data, it only concerns input sockets.
|
||||
*
|
||||
* \param node_tree_type: Node tree type that has these nodes, for example #NTREE_SHADER.
|
||||
* \param node_type: Node type to adjust, for example #SH_NODE_BSDF_PRINCIPLED.
|
||||
* \param socket_index_orig: The original index of the moved socket; when socket 4 moved to 6,
|
||||
* pass 4 here.
|
||||
* \param socket_index_offset: The offset of the nodes, so when socket 4 moved to 6,
|
||||
* pass 2 here.
|
||||
* \param total_number_of_sockets: The total number of sockets in the node.
|
||||
*/
|
||||
void version_node_socket_index_animdata(
|
||||
Main *bmain,
|
||||
int node_tree_type, /* NTREE_....., e.g. NTREE_SHADER */
|
||||
int node_type, /* SH_NODE_..., e.g. SH_NODE_BSDF_PRINCIPLED */
|
||||
int socket_index_orig,
|
||||
int socket_index_offset,
|
||||
int total_number_of_sockets);
|
||||
|
||||
/**
|
||||
* Replace the ID name of all nodes in the tree with the given type with the new name.
|
||||
*/
|
||||
void version_node_id(bNodeTree *ntree, int node_type, const char *new_name);
|
||||
|
||||
/**
|
||||
* Convert `SocketName.001` unique name format to `SocketName_001`. Previously both were used.
|
||||
*/
|
||||
void version_node_socket_id_delim(bNodeSocket *socket);
|
||||
|
||||
void version_node_socket_identifier_set(bNodeSocket &socket, StringRefNull identifier);
|
||||
|
||||
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);
|
||||
|
||||
void version_node_tree_clear_interface(bNodeTree &ntree);
|
||||
|
||||
/**
|
||||
* Change socket identifiers so that everything after the separator is removed for available
|
||||
* sockets.
|
||||
*/
|
||||
void version_socket_identifier_suffixes_for_dynamic_types(
|
||||
const ListBaseT<bNodeSocket> &sockets,
|
||||
const char *separator,
|
||||
const std::optional<int> total = std::nullopt);
|
||||
|
||||
/**
|
||||
* The versioning code generally expects `SOCK_IS_LINKED` to be set correctly. This function
|
||||
* updates the flag on all sockets after changes to the node tree.
|
||||
*/
|
||||
void version_socket_update_is_used(bNodeTree *ntree);
|
||||
ARegion *do_versions_add_region(int regiontype, const char *name);
|
||||
|
||||
void sequencer_init_preview_region(ARegion *region);
|
||||
|
||||
void add_realize_instances_before_socket(bNodeTree *ntree,
|
||||
bNode *node,
|
||||
bNodeSocket *geometry_socket);
|
||||
|
||||
float *version_cycles_node_socket_float_value(bNodeSocket *socket);
|
||||
float *version_cycles_node_socket_rgba_value(bNodeSocket *socket);
|
||||
float *version_cycles_node_socket_vector_value(bNodeSocket *socket);
|
||||
|
||||
IDProperty *version_cycles_properties_from_ID(ID *id);
|
||||
IDProperty *version_cycles_properties_from_view_layer(ViewLayer *view_layer);
|
||||
IDProperty *version_cycles_properties_from_render_layer(SceneRenderLayer *render_layer);
|
||||
IDProperty *version_cycles_visibility_properties_from_ID(ID *id);
|
||||
|
||||
float version_cycles_property_float(IDProperty *idprop, const char *name, float default_value);
|
||||
int version_cycles_property_int(IDProperty *idprop, const char *name, int default_value);
|
||||
void version_cycles_property_int_set(IDProperty *idprop, const char *name, int value);
|
||||
bool version_cycles_property_boolean(IDProperty *idprop, const char *name, bool default_value);
|
||||
void version_cycles_property_boolean_set(IDProperty *idprop, const char *name, bool value);
|
||||
|
||||
void node_tree_relink_with_socket_id_map(bNodeTree &ntree,
|
||||
bNode &old_node,
|
||||
bNode &new_node,
|
||||
const Map<std::string, std::string> &map);
|
||||
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);
|
||||
|
||||
bNode *version_eevee_output_node_get(bNodeTree *ntree, int16_t node_type);
|
||||
|
||||
/**
|
||||
* Allow 5.0+ to 'convert' older blend-files' system properties storage.
|
||||
*/
|
||||
void version_system_idprops_generate(Main *bmain);
|
||||
void version_system_idprops_nodes_generate(Main *bmain);
|
||||
void version_system_idprops_children_bones_generate(Main *bmain);
|
||||
|
||||
bool all_scenes_use(Main *bmain, const Span<const char *> engines);
|
||||
|
||||
/**
|
||||
* Adjust the values of the given FCurve key frames by applying the given function. The function is
|
||||
* expected to get and return a float representing the value of the key frame. The FCurve is
|
||||
* potentially changed to have the given property type, if not already the case.
|
||||
*/
|
||||
template<typename Function>
|
||||
static void adjust_fcurve_key_frame_values(FCurve *fcurve,
|
||||
const PropertyType property_type,
|
||||
const Function &function)
|
||||
{
|
||||
/* Adjust key frames. */
|
||||
if (fcurve->bezt) {
|
||||
for (int i = 0; i < fcurve->totvert; i++) {
|
||||
fcurve->bezt[i].vec[0][1] = function(fcurve->bezt[i].vec[0][1]);
|
||||
fcurve->bezt[i].vec[1][1] = function(fcurve->bezt[i].vec[1][1]);
|
||||
fcurve->bezt[i].vec[2][1] = function(fcurve->bezt[i].vec[2][1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Adjust baked key frames. */
|
||||
if (fcurve->fpt) {
|
||||
for (int i = 0; i < fcurve->totvert; i++) {
|
||||
fcurve->fpt[i].vec[1] = function(fcurve->fpt[i].vec[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup the flags based on the property type. */
|
||||
fcurve->flag &= ~(FCURVE_INT_VALUES | FCURVE_DISCRETE_VALUES);
|
||||
switch (property_type) {
|
||||
case PROP_FLOAT:
|
||||
break;
|
||||
case PROP_INT:
|
||||
fcurve->flag |= FCURVE_INT_VALUES;
|
||||
break;
|
||||
default:
|
||||
fcurve->flag |= (FCURVE_DISCRETE_VALUES | FCURVE_INT_VALUES);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Recalculate the automatic handles of the FCurve after adjustments. */
|
||||
BKE_fcurve_handles_recalc(*fcurve);
|
||||
}
|
||||
|
||||
/* Gets the compositing node tree of the given scene. The deprecated node-tree member is returned
|
||||
* for older versions before reusable node trees were introduced in bd61e69be5, while the new
|
||||
* compositing_node_group is returned otherwise. */
|
||||
bNodeTree *version_get_scene_compositor_node_tree(Main *bmain, Scene *scene);
|
||||
|
||||
} // namespace blender
|
||||
Reference in New Issue
Block a user