Merge branch 'master' into sculpt-dev
This commit is contained in:
commit
e1f338b9f4
|
@ -32,9 +32,7 @@ if(MSVC_CLANG AND WITH_OPENMP AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0.1
|
|||
endif()
|
||||
|
||||
# Exporting functions from the blender binary gives linker warnings on Apple arm64 systems.
|
||||
# For now and until Apple arm64 is officially supported, these will just be silenced here.
|
||||
# TODO (sebbas): Check if official arm64 devices give linker warnings without this block.
|
||||
|
||||
# Silence them here.
|
||||
if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
string(APPEND CMAKE_C_FLAGS " -fvisibility=hidden")
|
||||
|
|
|
@ -27,8 +27,8 @@ BVHOptiX::BVHOptiX(const BVHParams ¶ms_,
|
|||
Device *device)
|
||||
: BVH(params_, geometry_, objects_),
|
||||
traversable_handle(0),
|
||||
as_data(device, params_.top_level ? "optix tlas" : "optix blas"),
|
||||
motion_transform_data(device, "optix motion transform")
|
||||
as_data(device, params_.top_level ? "optix tlas" : "optix blas", false),
|
||||
motion_transform_data(device, "optix motion transform", false)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -854,7 +854,7 @@ CUDADevice::CUDAMem *CUDADevice::generic_alloc(device_memory &mem, size_t pitch_
|
|||
|
||||
void *shared_pointer = 0;
|
||||
|
||||
if (mem_alloc_result != CUDA_SUCCESS && can_map_host) {
|
||||
if (mem_alloc_result != CUDA_SUCCESS && can_map_host && mem.type != MEM_DEVICE_ONLY) {
|
||||
if (mem.shared_pointer) {
|
||||
/* Another device already allocated host memory. */
|
||||
mem_alloc_result = CUDA_SUCCESS;
|
||||
|
@ -877,8 +877,14 @@ CUDADevice::CUDAMem *CUDADevice::generic_alloc(device_memory &mem, size_t pitch_
|
|||
}
|
||||
|
||||
if (mem_alloc_result != CUDA_SUCCESS) {
|
||||
status = " failed, out of device and host memory";
|
||||
set_error("System is out of GPU and shared host memory");
|
||||
if (mem.type == MEM_DEVICE_ONLY) {
|
||||
status = " failed, out of device memory";
|
||||
set_error("System is out of GPU memory");
|
||||
}
|
||||
else {
|
||||
status = " failed, out of device and host memory";
|
||||
set_error("System is out of GPU and shared host memory");
|
||||
}
|
||||
}
|
||||
|
||||
if (mem.name) {
|
||||
|
|
|
@ -396,8 +396,7 @@ class CPUDevice : public Device {
|
|||
<< string_human_readable_size(mem.memory_size()) << ")";
|
||||
}
|
||||
|
||||
if (mem.type == MEM_DEVICE_ONLY) {
|
||||
assert(!mem.host_pointer);
|
||||
if (mem.type == MEM_DEVICE_ONLY || !mem.host_pointer) {
|
||||
size_t alignment = MIN_ALIGNMENT_CPU_DATA_TYPES;
|
||||
void *data = util_aligned_malloc(mem.memory_size(), alignment);
|
||||
mem.device_pointer = (device_ptr)data;
|
||||
|
@ -459,7 +458,7 @@ class CPUDevice : public Device {
|
|||
tex_free((device_texture &)mem);
|
||||
}
|
||||
else if (mem.device_pointer) {
|
||||
if (mem.type == MEM_DEVICE_ONLY) {
|
||||
if (mem.type == MEM_DEVICE_ONLY || !mem.host_pointer) {
|
||||
util_aligned_free((void *)mem.device_pointer);
|
||||
}
|
||||
mem.device_pointer = 0;
|
||||
|
|
|
@ -171,7 +171,8 @@ class DenoisingTask {
|
|||
bool gpu_temporary_mem;
|
||||
|
||||
DenoiseBuffers(Device *device)
|
||||
: mem(device, "denoising pixel buffer"), temporary_mem(device, "denoising temporary mem")
|
||||
: mem(device, "denoising pixel buffer"),
|
||||
temporary_mem(device, "denoising temporary mem", true)
|
||||
{
|
||||
}
|
||||
} buffer;
|
||||
|
|
|
@ -270,8 +270,8 @@ class device_memory {
|
|||
|
||||
template<typename T> class device_only_memory : public device_memory {
|
||||
public:
|
||||
device_only_memory(Device *device, const char *name)
|
||||
: device_memory(device, name, MEM_DEVICE_ONLY)
|
||||
device_only_memory(Device *device, const char *name, bool allow_host_memory_fallback = false)
|
||||
: device_memory(device, name, allow_host_memory_fallback ? MEM_READ_WRITE : MEM_DEVICE_ONLY)
|
||||
{
|
||||
data_type = device_type_traits<T>::data_type;
|
||||
data_elements = max(device_type_traits<T>::num_elements, 1);
|
||||
|
|
|
@ -197,8 +197,8 @@ class OptiXDevice : public CUDADevice {
|
|||
OptiXDevice(DeviceInfo &info_, Stats &stats_, Profiler &profiler_, bool background_)
|
||||
: CUDADevice(info_, stats_, profiler_, background_),
|
||||
sbt_data(this, "__sbt", MEM_READ_ONLY),
|
||||
launch_params(this, "__params"),
|
||||
denoiser_state(this, "__denoiser_state")
|
||||
launch_params(this, "__params", false),
|
||||
denoiser_state(this, "__denoiser_state", true)
|
||||
{
|
||||
// Store number of CUDA streams in device info
|
||||
info.cpu_threads = DebugFlags().optix.cuda_streams;
|
||||
|
@ -878,8 +878,8 @@ class OptiXDevice : public CUDADevice {
|
|||
device_ptr input_ptr = rtile.buffer + pixel_offset;
|
||||
|
||||
// Copy tile data into a common buffer if necessary
|
||||
device_only_memory<float> input(this, "denoiser input");
|
||||
device_vector<TileInfo> tile_info_mem(this, "denoiser tile info", MEM_READ_WRITE);
|
||||
device_only_memory<float> input(this, "denoiser input", true);
|
||||
device_vector<TileInfo> tile_info_mem(this, "denoiser tile info", MEM_READ_ONLY);
|
||||
|
||||
bool contiguous_memory = true;
|
||||
for (int i = 0; i < RenderTileNeighbors::SIZE; i++) {
|
||||
|
@ -924,7 +924,7 @@ class OptiXDevice : public CUDADevice {
|
|||
}
|
||||
|
||||
# if OPTIX_DENOISER_NO_PIXEL_STRIDE
|
||||
device_only_memory<float> input_rgb(this, "denoiser input rgb");
|
||||
device_only_memory<float> input_rgb(this, "denoiser input rgb", true);
|
||||
input_rgb.alloc_to_device(rect_size.x * rect_size.y * 3 * task.denoising.input_passes);
|
||||
|
||||
void *input_args[] = {&input_rgb.device_pointer,
|
||||
|
@ -1146,6 +1146,13 @@ class OptiXDevice : public CUDADevice {
|
|||
const OptixBuildInput &build_input,
|
||||
uint16_t num_motion_steps)
|
||||
{
|
||||
/* Allocate and build acceleration structures only one at a time, to prevent parallel builds
|
||||
* from running out of memory (since both original and compacted acceleration structure memory
|
||||
* may be allocated at the same time for the duration of this function). The builds would
|
||||
* otherwise happen on the same CUDA stream anyway. */
|
||||
static thread_mutex mutex;
|
||||
thread_scoped_lock lock(mutex);
|
||||
|
||||
const CUDAContextScope scope(cuContext);
|
||||
|
||||
// Compute memory usage
|
||||
|
@ -1170,11 +1177,12 @@ class OptiXDevice : public CUDADevice {
|
|||
optixAccelComputeMemoryUsage(context, &options, &build_input, 1, &sizes));
|
||||
|
||||
// Allocate required output buffers
|
||||
device_only_memory<char> temp_mem(this, "optix temp as build mem");
|
||||
device_only_memory<char> temp_mem(this, "optix temp as build mem", true);
|
||||
temp_mem.alloc_to_device(align_up(sizes.tempSizeInBytes, 8) + 8);
|
||||
if (!temp_mem.device_pointer)
|
||||
return false; // Make sure temporary memory allocation succeeded
|
||||
|
||||
// Acceleration structure memory has to be allocated on the device (not allowed to be on host)
|
||||
device_only_memory<char> &out_data = bvh->as_data;
|
||||
if (operation == OPTIX_BUILD_OPERATION_BUILD) {
|
||||
assert(out_data.device == this);
|
||||
|
@ -1222,7 +1230,7 @@ class OptiXDevice : public CUDADevice {
|
|||
|
||||
// There is no point compacting if the size does not change
|
||||
if (compacted_size < sizes.outputSizeInBytes) {
|
||||
device_only_memory<char> compacted_data(this, "optix compacted as");
|
||||
device_only_memory<char> compacted_data(this, "optix compacted as", false);
|
||||
compacted_data.alloc_to_device(compacted_size);
|
||||
if (!compacted_data.device_pointer)
|
||||
// Do not compact if memory allocation for compacted acceleration structure fails
|
||||
|
@ -1242,6 +1250,7 @@ class OptiXDevice : public CUDADevice {
|
|||
|
||||
std::swap(out_data.device_size, compacted_data.device_size);
|
||||
std::swap(out_data.device_pointer, compacted_data.device_pointer);
|
||||
// Original acceleration structure memory is freed when 'compacted_data' goes out of scope
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,32 +32,36 @@ extern "C" {
|
|||
/**
|
||||
* Counts how many bytes is required for future utf-8 string using utf-16
|
||||
* \param string16: pointer to working utf-16 string
|
||||
* \return How many bytes must be allocated includeng NULL.
|
||||
* \return How many bytes must be allocated including NULL.
|
||||
*/
|
||||
size_t count_utf_8_from_16(const wchar_t *string16);
|
||||
|
||||
/**
|
||||
* Counts how many wchar_t (two byte) is required for future utf-16 string using utf-8
|
||||
* \param string8: pointer to working utf-8 string
|
||||
* \return How many bytes must be allocated includeng NULL.
|
||||
* \return How many bytes must be allocated including NULL.
|
||||
*/
|
||||
size_t count_utf_16_from_8(const char *string8);
|
||||
|
||||
/**
|
||||
/*
|
||||
* conv_utf_*** errors
|
||||
*/
|
||||
#define UTF_ERROR_NULL_IN (1 << 0) /* Error occures when requered parameter is missing*/
|
||||
#define UTF_ERROR_ILLCHAR (1 << 1) /* Error if character is in illigal UTF rage*/
|
||||
#define UTF_ERROR_SMALL \
|
||||
(1 << 2) /* Passed size is to small. It gives legal string with character missing at the end */
|
||||
#define UTF_ERROR_ILLSEQ (1 << 3) /* Error if sequence is broken and doesn't finish*/
|
||||
|
||||
/** Error occurs when required parameter is missing. */
|
||||
#define UTF_ERROR_NULL_IN (1 << 0)
|
||||
/** Error if character is in illegal UTF range. */
|
||||
#define UTF_ERROR_ILLCHAR (1 << 1)
|
||||
/** Passed size is to small. It gives legal string with character missing at the end. */
|
||||
#define UTF_ERROR_SMALL (1 << 2)
|
||||
/** Error if sequence is broken and doesn't finish. */
|
||||
#define UTF_ERROR_ILLSEQ (1 << 3)
|
||||
|
||||
/**
|
||||
* Converts utf-16 string to allocated utf-8 string
|
||||
* \param in16: utf-16 string to convert
|
||||
* \param out8: utf-8 string to string the conversion
|
||||
* \param size8: the allocated size in bytes of out8
|
||||
* \return Returns any errors occured during conversion. See the block above,
|
||||
* \return Returns any errors occurred during conversion. See the block above,
|
||||
*/
|
||||
int conv_utf_16_to_8(const wchar_t *in16, char *out8, size_t size8);
|
||||
|
||||
|
@ -66,7 +70,7 @@ int conv_utf_16_to_8(const wchar_t *in16, char *out8, size_t size8);
|
|||
* \param in8: utf-8 string to convert
|
||||
* \param out16: utf-16 string to string the conversion
|
||||
* \param size16: the allocated size in wchar_t (two byte) of out16
|
||||
* \return Returns any errors occured during conversion. See the block above,
|
||||
* \return Returns any errors occurred during conversion. See the block above,
|
||||
*/
|
||||
int conv_utf_8_to_16(const char *in8, wchar_t *out16, size_t size16);
|
||||
|
||||
|
|
|
@ -384,7 +384,7 @@ class AddPresetFluid(AddPresetBase, Operator):
|
|||
"""Add or remove a Fluid Preset"""
|
||||
bl_idname = "fluid.preset_add"
|
||||
bl_label = "Add Fluid Preset"
|
||||
preset_menu = "FLUID_MT_presets"
|
||||
preset_menu = "FLUID_PT_presets"
|
||||
|
||||
preset_defines = [
|
||||
"fluid = bpy.context.fluid"
|
||||
|
|
|
@ -24,7 +24,7 @@ class SPREADSHEET_OT_toggle_pin(bpy.types.Operator):
|
|||
'''Turn on or off pinning'''
|
||||
bl_idname = "spreadsheet.toggle_pin"
|
||||
bl_label = "Toggle Pin"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
bl_options = {'REGISTER'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
|
|
@ -31,6 +31,9 @@ class SPREADSHEET_HT_header(bpy.types.Header):
|
|||
pinned_id = space.pinned_id
|
||||
used_id = pinned_id if pinned_id else context.active_object
|
||||
|
||||
layout.prop(space, "geometry_component_type", text="")
|
||||
layout.prop(space, "attribute_domain", text="")
|
||||
|
||||
if used_id:
|
||||
layout.label(text=used_id.name, icon="OBJECT_DATA")
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct BlendFileData;
|
||||
struct BlendFileReadParams;
|
||||
struct ID;
|
||||
struct Main;
|
||||
|
@ -31,36 +32,32 @@ struct ReportList;
|
|||
struct UserDef;
|
||||
struct bContext;
|
||||
|
||||
bool BKE_blendfile_read_ex(struct bContext *C,
|
||||
const char *filepath,
|
||||
const struct BlendFileReadParams *params,
|
||||
struct ReportList *reports,
|
||||
/* Extra args. */
|
||||
const bool startup_update_defaults,
|
||||
const char *startup_app_template);
|
||||
bool BKE_blendfile_read(struct bContext *C,
|
||||
const char *filepath,
|
||||
const struct BlendFileReadParams *params,
|
||||
struct ReportList *reports);
|
||||
void BKE_blendfile_read_setup_ex(struct bContext *C,
|
||||
struct BlendFileData *bfd,
|
||||
const struct BlendFileReadParams *params,
|
||||
struct ReportList *reports,
|
||||
/* Extra args. */
|
||||
const bool startup_update_defaults,
|
||||
const char *startup_app_template);
|
||||
|
||||
bool BKE_blendfile_read_from_memory_ex(struct bContext *C,
|
||||
const void *filebuf,
|
||||
int filelength,
|
||||
const struct BlendFileReadParams *params,
|
||||
struct ReportList *reports,
|
||||
/* Extra args. */
|
||||
const bool startup_update_defaults,
|
||||
const char *startup_app_template);
|
||||
bool BKE_blendfile_read_from_memory(struct bContext *C,
|
||||
const void *filebuf,
|
||||
int filelength,
|
||||
const struct BlendFileReadParams *params,
|
||||
struct ReportList *reports);
|
||||
void BKE_blendfile_read_setup(struct bContext *C,
|
||||
struct BlendFileData *bfd,
|
||||
const struct BlendFileReadParams *params,
|
||||
struct ReportList *reports);
|
||||
|
||||
bool BKE_blendfile_read_from_memfile(struct bContext *C,
|
||||
struct MemFile *memfile,
|
||||
const struct BlendFileReadParams *params,
|
||||
struct ReportList *reports);
|
||||
struct BlendFileData *BKE_blendfile_read(const char *filepath,
|
||||
const struct BlendFileReadParams *params,
|
||||
struct ReportList *reports);
|
||||
|
||||
struct BlendFileData *BKE_blendfile_read_from_memory(const void *filebuf,
|
||||
int filelength,
|
||||
const struct BlendFileReadParams *params,
|
||||
struct ReportList *reports);
|
||||
|
||||
struct BlendFileData *BKE_blendfile_read_from_memfile(struct Main *bmain,
|
||||
struct MemFile *memfile,
|
||||
const struct BlendFileReadParams *params,
|
||||
struct ReportList *reports);
|
||||
void BKE_blendfile_read_make_empty(struct bContext *C);
|
||||
|
||||
struct UserDef *BKE_blendfile_userdef_read(const char *filepath, struct ReportList *reports);
|
||||
|
|
|
@ -61,6 +61,8 @@ void BKE_lib_override_library_copy(struct ID *dst_id,
|
|||
void BKE_lib_override_library_clear(struct IDOverrideLibrary *override, const bool do_id_user);
|
||||
void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bool do_id_user);
|
||||
|
||||
bool BKE_lib_override_library_is_user_edited(struct ID *id);
|
||||
|
||||
struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain,
|
||||
struct ID *reference_id,
|
||||
const bool do_tagged_remap);
|
||||
|
@ -78,6 +80,10 @@ bool BKE_lib_override_library_resync(struct Main *bmain,
|
|||
struct Scene *scene,
|
||||
struct ViewLayer *view_layer,
|
||||
struct ID *id_root);
|
||||
void BKE_lib_override_library_main_resync(struct Main *bmain,
|
||||
struct Scene *scene,
|
||||
struct ViewLayer *view_layer);
|
||||
|
||||
void BKE_lib_override_library_delete(struct Main *bmain, struct ID *id_root);
|
||||
|
||||
struct IDOverrideLibraryProperty *BKE_lib_override_library_property_find(
|
||||
|
|
|
@ -76,6 +76,8 @@ enum {
|
|||
ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE = 1 << 4,
|
||||
/** Do not remap library override pointers. */
|
||||
ID_REMAP_SKIP_OVERRIDE_LIBRARY = 1 << 5,
|
||||
/** Don't touch the user count (use for low level actions such as swapping pointers). */
|
||||
ID_REMAP_SKIP_USER_CLEAR = 1 << 6,
|
||||
};
|
||||
|
||||
/* Note: Requiring new_id to be non-null, this *may* not be the case ultimately,
|
||||
|
|
|
@ -77,7 +77,12 @@ bool BKE_memfile_undo_decode(MemFileUndoData *mfu,
|
|||
G.fileflags |= G_FILE_NO_UI;
|
||||
|
||||
if (UNDO_DISK) {
|
||||
success = BKE_blendfile_read(C, mfu->filename, &(const struct BlendFileReadParams){0}, NULL);
|
||||
const struct BlendFileReadParams params = {0};
|
||||
struct BlendFileData *bfd = BKE_blendfile_read(mfu->filename, ¶ms, NULL);
|
||||
if (bfd != NULL) {
|
||||
BKE_blendfile_read_setup(C, bfd, ¶ms, NULL);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
struct BlendFileReadParams params = {0};
|
||||
|
@ -85,7 +90,12 @@ bool BKE_memfile_undo_decode(MemFileUndoData *mfu,
|
|||
if (!use_old_bmain_data) {
|
||||
params.skip_flags |= BLO_READ_SKIP_UNDO_OLD_MAIN;
|
||||
}
|
||||
success = BKE_blendfile_read_from_memfile(C, &mfu->memfile, ¶ms, NULL);
|
||||
struct BlendFileData *bfd = BKE_blendfile_read_from_memfile(
|
||||
bmain, &mfu->memfile, ¶ms, NULL);
|
||||
if (bfd != NULL) {
|
||||
BKE_blendfile_read_setup(C, bfd, ¶ms, NULL);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore, bmain has been re-allocated. */
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "BKE_keyconfig.h"
|
||||
#include "BKE_layer.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_lib_override.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_preferences.h"
|
||||
#include "BKE_report.h"
|
||||
|
@ -131,11 +132,9 @@ static void setup_app_userdef(BlendFileData *bfd)
|
|||
* should be avoided or check (mode != LOAD_UNDO).
|
||||
*
|
||||
* \param bfd: Blend file data, freed by this function on exit.
|
||||
* \param filepath: File path or identifier.
|
||||
*/
|
||||
static void setup_app_data(bContext *C,
|
||||
BlendFileData *bfd,
|
||||
const char *filepath,
|
||||
const struct BlendFileReadParams *params,
|
||||
ReportList *reports)
|
||||
{
|
||||
|
@ -349,16 +348,10 @@ static void setup_app_data(bContext *C,
|
|||
if (is_startup) {
|
||||
bmain->name[0] = '\0';
|
||||
}
|
||||
else if (recover && G.relbase_valid) {
|
||||
/* in case of autosave or quit.blend, use original filename instead
|
||||
* use relbase_valid to make sure the file is saved, else we get <memory2> in the filename */
|
||||
filepath = bfd->filename;
|
||||
else if (recover) {
|
||||
/* In case of autosave or quit.blend, use original filename instead. */
|
||||
bmain->recovered = 1;
|
||||
|
||||
/* these are the same at times, should never copy to the same location */
|
||||
if (bmain->name != filepath) {
|
||||
BLI_strncpy(bmain->name, filepath, FILE_MAX);
|
||||
}
|
||||
BLI_strncpy(bmain->name, bfd->filename, FILE_MAX);
|
||||
}
|
||||
|
||||
/* baseflags, groups, make depsgraph, etc */
|
||||
|
@ -401,11 +394,17 @@ static void setup_app_data(bContext *C,
|
|||
* to recompute refcount for all local IDs too. */
|
||||
BKE_main_id_refcount_recompute(bmain, false);
|
||||
}
|
||||
|
||||
if (mode != LOAD_UNDO) {
|
||||
BKE_lib_override_library_main_resync(
|
||||
bmain,
|
||||
curscene,
|
||||
bfd->cur_view_layer ? bfd->cur_view_layer : BKE_view_layer_default_view(curscene));
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_app_blend_file_data(bContext *C,
|
||||
BlendFileData *bfd,
|
||||
const char *filepath,
|
||||
const struct BlendFileReadParams *params,
|
||||
ReportList *reports)
|
||||
{
|
||||
|
@ -413,7 +412,7 @@ static void setup_app_blend_file_data(bContext *C,
|
|||
setup_app_userdef(bfd);
|
||||
}
|
||||
if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) {
|
||||
setup_app_data(C, bfd, filepath, params, reports);
|
||||
setup_app_data(C, bfd, params, reports);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -430,15 +429,46 @@ static void handle_subversion_warning(Main *main, ReportList *reports)
|
|||
}
|
||||
}
|
||||
|
||||
bool BKE_blendfile_read_ex(bContext *C,
|
||||
const char *filepath,
|
||||
const struct BlendFileReadParams *params,
|
||||
ReportList *reports,
|
||||
/* Extra args. */
|
||||
const bool startup_update_defaults,
|
||||
const char *startup_app_template)
|
||||
/**
|
||||
* Shared setup function that makes the data from `bfd` into the current blend file,
|
||||
* replacing the contents of #G.main.
|
||||
* This uses the bfd #BKE_blendfile_read and similarly named functions.
|
||||
*
|
||||
* This is done in a separate step so the caller may perform actions after it is known the file
|
||||
* loaded correctly but before the file replaces the existing blend file contents.
|
||||
*/
|
||||
void BKE_blendfile_read_setup_ex(bContext *C,
|
||||
BlendFileData *bfd,
|
||||
const struct BlendFileReadParams *params,
|
||||
ReportList *reports,
|
||||
/* Extra args. */
|
||||
const bool startup_update_defaults,
|
||||
const char *startup_app_template)
|
||||
{
|
||||
if (startup_update_defaults) {
|
||||
if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) {
|
||||
BLO_update_defaults_startup_blend(bfd->main, startup_app_template);
|
||||
}
|
||||
}
|
||||
setup_app_blend_file_data(C, bfd, params, reports);
|
||||
BLO_blendfiledata_free(bfd);
|
||||
}
|
||||
|
||||
void BKE_blendfile_read_setup(bContext *C,
|
||||
BlendFileData *bfd,
|
||||
const struct BlendFileReadParams *params,
|
||||
ReportList *reports)
|
||||
{
|
||||
BKE_blendfile_read_setup_ex(C, bfd, params, reports, false, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* \return Blend file data, this must be passed to #BKE_blendfile_read_setup when non-NULL.
|
||||
*/
|
||||
struct BlendFileData *BKE_blendfile_read(const char *filepath,
|
||||
const struct BlendFileReadParams *params,
|
||||
ReportList *reports)
|
||||
{
|
||||
/* Don't print startup file loading. */
|
||||
if (params->is_startup == false) {
|
||||
printf("Read blend: %s\n", filepath);
|
||||
|
@ -447,69 +477,40 @@ bool BKE_blendfile_read_ex(bContext *C,
|
|||
BlendFileData *bfd = BLO_read_from_file(filepath, params->skip_flags, reports);
|
||||
if (bfd) {
|
||||
handle_subversion_warning(bfd->main, reports);
|
||||
if (startup_update_defaults) {
|
||||
if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) {
|
||||
BLO_update_defaults_startup_blend(bfd->main, startup_app_template);
|
||||
}
|
||||
}
|
||||
setup_app_blend_file_data(C, bfd, filepath, params, reports);
|
||||
BLO_blendfiledata_free(bfd);
|
||||
}
|
||||
else {
|
||||
BKE_reports_prependf(reports, "Loading '%s' failed: ", filepath);
|
||||
}
|
||||
return (bfd != NULL);
|
||||
return bfd;
|
||||
}
|
||||
|
||||
bool BKE_blendfile_read(bContext *C,
|
||||
const char *filepath,
|
||||
const struct BlendFileReadParams *params,
|
||||
ReportList *reports)
|
||||
{
|
||||
return BKE_blendfile_read_ex(C, filepath, params, reports, false, NULL);
|
||||
}
|
||||
|
||||
bool BKE_blendfile_read_from_memory_ex(bContext *C,
|
||||
const void *filebuf,
|
||||
int filelength,
|
||||
const struct BlendFileReadParams *params,
|
||||
ReportList *reports,
|
||||
/* Extra args. */
|
||||
const bool startup_update_defaults,
|
||||
const char *startup_app_template)
|
||||
/**
|
||||
* \return Blend file data, this must be passed to #BKE_blendfile_read_setup when non-NULL.
|
||||
*/
|
||||
struct BlendFileData *BKE_blendfile_read_from_memory(const void *filebuf,
|
||||
int filelength,
|
||||
const struct BlendFileReadParams *params,
|
||||
ReportList *reports)
|
||||
{
|
||||
BlendFileData *bfd = BLO_read_from_memory(filebuf, filelength, params->skip_flags, reports);
|
||||
if (bfd) {
|
||||
if (startup_update_defaults) {
|
||||
if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) {
|
||||
BLO_update_defaults_startup_blend(bfd->main, startup_app_template);
|
||||
}
|
||||
}
|
||||
setup_app_blend_file_data(C, bfd, "<memory2>", params, reports);
|
||||
BLO_blendfiledata_free(bfd);
|
||||
/* Pass. */
|
||||
}
|
||||
else {
|
||||
BKE_reports_prepend(reports, "Loading failed: ");
|
||||
}
|
||||
return (bfd != NULL);
|
||||
return bfd;
|
||||
}
|
||||
|
||||
bool BKE_blendfile_read_from_memory(bContext *C,
|
||||
const void *filebuf,
|
||||
int filelength,
|
||||
const struct BlendFileReadParams *params,
|
||||
ReportList *reports)
|
||||
/**
|
||||
* \return Blend file data, this must be passed to #BKE_blendfile_read_setup when non-NULL.
|
||||
* \note `memfile` is the undo buffer.
|
||||
*/
|
||||
struct BlendFileData *BKE_blendfile_read_from_memfile(Main *bmain,
|
||||
struct MemFile *memfile,
|
||||
const struct BlendFileReadParams *params,
|
||||
ReportList *reports)
|
||||
{
|
||||
return BKE_blendfile_read_from_memory_ex(C, filebuf, filelength, params, reports, false, NULL);
|
||||
}
|
||||
|
||||
/* memfile is the undo buffer */
|
||||
bool BKE_blendfile_read_from_memfile(bContext *C,
|
||||
struct MemFile *memfile,
|
||||
const struct BlendFileReadParams *params,
|
||||
ReportList *reports)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
BlendFileData *bfd = BLO_read_from_memfile(
|
||||
bmain, BKE_main_blendfile_path(bmain), memfile, params, reports);
|
||||
if (bfd) {
|
||||
|
@ -520,14 +521,11 @@ bool BKE_blendfile_read_from_memfile(bContext *C,
|
|||
BLI_assert(BLI_listbase_is_empty(&bfd->main->wm));
|
||||
BLI_assert(BLI_listbase_is_empty(&bfd->main->workspaces));
|
||||
BLI_assert(BLI_listbase_is_empty(&bfd->main->screens));
|
||||
|
||||
setup_app_blend_file_data(C, bfd, "<memory1>", params, reports);
|
||||
BLO_blendfiledata_free(bfd);
|
||||
}
|
||||
else {
|
||||
BKE_reports_prepend(reports, "Loading failed: ");
|
||||
}
|
||||
return (bfd != NULL);
|
||||
return bfd;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -217,6 +217,32 @@ static ID *lib_override_library_create_from(Main *bmain, ID *reference_id)
|
|||
return local_id;
|
||||
}
|
||||
|
||||
/** Check if given ID has some override rules that actually indicate the user edited it.
|
||||
*
|
||||
* TODO: This could be simplified by storing a flag in IDOverrideLibrary during the diffing
|
||||
* process? */
|
||||
bool BKE_lib_override_library_is_user_edited(struct ID *id)
|
||||
{
|
||||
if (!ID_IS_OVERRIDE_LIBRARY(id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) {
|
||||
LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
|
||||
if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) != 0) {
|
||||
continue;
|
||||
}
|
||||
if (opop->operation == IDOVERRIDE_LIBRARY_OP_NOOP) {
|
||||
continue;
|
||||
}
|
||||
/* If an operation does not match the filters above, it is considered as a user-editing one,
|
||||
* therefore this override is user-edited. */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Create an overridden local copy of linked reference. */
|
||||
ID *BKE_lib_override_library_create_from_id(Main *bmain,
|
||||
ID *reference_id,
|
||||
|
@ -383,7 +409,7 @@ typedef struct LibOverrideGroupTagData {
|
|||
*
|
||||
* Requires existing `Main.relations`.
|
||||
*
|
||||
* Note: this is typically called to complete `lib_override_linked_group_tag()`.
|
||||
* NOTE: This is typically called to complete `lib_override_linked_group_tag()`.
|
||||
*/
|
||||
static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTagData *data)
|
||||
{
|
||||
|
@ -883,6 +909,8 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_
|
|||
ID *id_override_new = id->newid;
|
||||
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
|
||||
|
||||
BLI_assert((id_override_new->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0);
|
||||
|
||||
if (id_override_old != NULL) {
|
||||
/* Swap the names between old override ID and new one. */
|
||||
char id_name_buf[MAX_ID_NAME];
|
||||
|
@ -975,11 +1003,21 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_
|
|||
}
|
||||
id->tag &= ~LIB_TAG_DOIT;
|
||||
}
|
||||
/* Also cleanup old overrides that went missing in new linked data. */
|
||||
/* Also deal with old overrides that went missing in new linked data. */
|
||||
else if (id->tag & LIB_TAG_MISSING && !ID_IS_LINKED(id)) {
|
||||
BLI_assert(ID_IS_OVERRIDE_LIBRARY(id));
|
||||
id->tag |= LIB_TAG_DOIT;
|
||||
id->tag &= ~LIB_TAG_MISSING;
|
||||
if (!BKE_lib_override_library_is_user_edited(id)) {
|
||||
/* If user never edited them, we can delete them. */
|
||||
id->tag |= LIB_TAG_DOIT;
|
||||
id->tag &= ~LIB_TAG_MISSING;
|
||||
CLOG_INFO(&LOG, 3, "Old override %s is being deleted", id->name);
|
||||
}
|
||||
else {
|
||||
/* Otherwise, keep them, user needs to decide whether what to do with them. */
|
||||
BLI_assert((id->tag & LIB_TAG_DOIT) == 0);
|
||||
id_fake_user_set(id);
|
||||
CLOG_INFO(&LOG, 3, "Old override %s is being kept around as it was user-edited", id->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
@ -1006,6 +1044,119 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_
|
|||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect and handle required resync of overrides data, when relations between reference linked IDs
|
||||
* have changed.
|
||||
*
|
||||
* This is a fairly complex and costly operation, typically it should be called after
|
||||
* #BKE_lib_override_library_main_update, which would already detect and tag a lot of cases.
|
||||
*
|
||||
* This function will first detect the remaining cases requiring a resync (namely, either when an
|
||||
* existing linked ID that did not require to be overridden before now would be, or when new IDs
|
||||
* are added to the hierarchy).
|
||||
*
|
||||
* Then it will handle the resync of necessary IDs (through calls to
|
||||
* #BKE_lib_override_library_resync).
|
||||
*/
|
||||
void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *view_layer)
|
||||
{
|
||||
BKE_main_relations_create(bmain, 0);
|
||||
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
|
||||
|
||||
/* NOTE: in code below, the order in which `FOREACH_MAIN_ID_BEGIN` processes ID types ensures
|
||||
* that we always process 'higher-level' overrides first (i.e. scenes, then collections, then
|
||||
* objects, then other types). */
|
||||
|
||||
/* Detect all linked data that would need to be overridden if we had to create an override from
|
||||
* those used by current existing overrides. */
|
||||
ID *id;
|
||||
FOREACH_MAIN_ID_BEGIN (bmain, id) {
|
||||
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
|
||||
continue;
|
||||
}
|
||||
if (id->tag & (LIB_TAG_DOIT | LIB_TAG_MISSING)) {
|
||||
/* We already processed that ID as part of another ID's hierarchy. */
|
||||
continue;
|
||||
}
|
||||
|
||||
LibOverrideGroupTagData data = {.bmain = bmain,
|
||||
.id_root = id->override_library->reference,
|
||||
.tag = LIB_TAG_DOIT,
|
||||
.missing_tag = LIB_TAG_MISSING};
|
||||
lib_override_linked_group_tag(&data);
|
||||
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
|
||||
lib_override_hierarchy_dependencies_recursive_tag(&data);
|
||||
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
/* Now check existing overrides, those needing resync will be the one either already tagged as
|
||||
* such, or the one using linked data that is now tagged as needing override. */
|
||||
FOREACH_MAIN_ID_BEGIN (bmain, id) {
|
||||
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) {
|
||||
CLOG_INFO(&LOG, 4, "ID %s was already tagged as needing resync", id->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id);
|
||||
BLI_assert(entry != NULL);
|
||||
|
||||
for (MainIDRelationsEntryItem *entry_item = entry->to_ids; entry_item != NULL;
|
||||
entry_item = entry_item->next) {
|
||||
if (entry_item->usage_flag &
|
||||
(IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) {
|
||||
continue;
|
||||
}
|
||||
ID *id_to = *entry_item->id_pointer.to;
|
||||
|
||||
/* Case where this ID pointer was to a linked ID, that now needs to be overridden. */
|
||||
if (ID_IS_LINKED(id_to) && (id_to->tag & LIB_TAG_DOIT) != 0) {
|
||||
id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
|
||||
CLOG_INFO(&LOG,
|
||||
3,
|
||||
"ID %s now tagged as needing resync because they use linked %s that now needs "
|
||||
"to be overridden",
|
||||
id->name,
|
||||
id_to->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
BKE_main_relations_free(bmain);
|
||||
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
|
||||
|
||||
/* And do the actual resync for all IDs detected as needing it.
|
||||
* NOTE: Since this changes `bmain` (adding **and** removing IDs), we cannot use
|
||||
* `FOREACH_MAIN_ID_BEGIN/END` here, and need special multi-loop processing. */
|
||||
bool do_continue = true;
|
||||
while (do_continue) {
|
||||
ListBase *lb;
|
||||
do_continue = false;
|
||||
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
|
||||
FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
|
||||
if ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0) {
|
||||
continue;
|
||||
}
|
||||
do_continue = true;
|
||||
const bool success = BKE_lib_override_library_resync(bmain, scene, view_layer, id);
|
||||
CLOG_INFO(&LOG, 2, "Resynced %s, success: %d", id->name, success);
|
||||
break;
|
||||
}
|
||||
FOREACH_MAIN_LISTBASE_ID_END;
|
||||
if (do_continue) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_LISTBASE_END;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Advanced 'smart' function to delete library overrides (including their existing override
|
||||
* hierarchy) and remap their usages to their linked reference IDs.
|
||||
|
@ -1882,6 +2033,14 @@ void BKE_lib_override_library_main_unused_cleanup(struct Main *bmain)
|
|||
FOREACH_MAIN_ID_END;
|
||||
}
|
||||
|
||||
static void lib_override_id_swap(Main *bmain, ID *id_local, ID *id_temp)
|
||||
{
|
||||
BKE_lib_id_swap(bmain, id_local, id_temp);
|
||||
/* We need to keep these tags from temp ID into orig one.
|
||||
* ID swap does not swap most of ID data itself. */
|
||||
id_local->tag |= (id_temp->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC);
|
||||
}
|
||||
|
||||
/** Update given override from its reference (re-applying overridden properties). */
|
||||
void BKE_lib_override_library_update(Main *bmain, ID *local)
|
||||
{
|
||||
|
@ -1950,11 +2109,11 @@ void BKE_lib_override_library_update(Main *bmain, ID *local)
|
|||
|
||||
/* This also transfers all pointers (memory) owned by local to tmp_id, and vice-versa.
|
||||
* So when we'll free tmp_id, we'll actually free old, outdated data from local. */
|
||||
BKE_lib_id_swap(bmain, local, tmp_id);
|
||||
lib_override_id_swap(bmain, local, tmp_id);
|
||||
|
||||
if (local_key != NULL && tmp_key != NULL) {
|
||||
/* This is some kind of hard-coded 'always enforced override'. */
|
||||
BKE_lib_id_swap(bmain, &local_key->id, &tmp_key->id);
|
||||
lib_override_id_swap(bmain, &local_key->id, &tmp_key->id);
|
||||
tmp_key->id.flag |= (local_key->id.flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE);
|
||||
/* The swap of local and tmp_id inverted those pointers, we need to redefine proper
|
||||
* relationships. */
|
||||
|
|
|
@ -422,15 +422,17 @@ static void libblock_remap_data(
|
|||
FOREACH_MAIN_ID_END;
|
||||
}
|
||||
|
||||
/* XXX We may not want to always 'transfer' fake-user from old to new id...
|
||||
* Think for now it's desired behavior though,
|
||||
* we can always add an option (flag) to control this later if needed. */
|
||||
if (old_id && (old_id->flag & LIB_FAKEUSER)) {
|
||||
id_fake_user_clear(old_id);
|
||||
id_fake_user_set(new_id);
|
||||
}
|
||||
if ((remap_flags & ID_REMAP_SKIP_USER_CLEAR) == 0) {
|
||||
/* XXX We may not want to always 'transfer' fake-user from old to new id...
|
||||
* Think for now it's desired behavior though,
|
||||
* we can always add an option (flag) to control this later if needed. */
|
||||
if (old_id && (old_id->flag & LIB_FAKEUSER)) {
|
||||
id_fake_user_clear(old_id);
|
||||
id_fake_user_set(new_id);
|
||||
}
|
||||
|
||||
id_us_clear_real(old_id);
|
||||
id_us_clear_real(old_id);
|
||||
}
|
||||
|
||||
if (new_id && (new_id->tag & LIB_TAG_INDIRECT) &&
|
||||
(r_id_remap_data->status & ID_REMAP_IS_LINKED_DIRECT)) {
|
||||
|
@ -479,12 +481,14 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const
|
|||
skipped_direct = id_remap_data.skipped_direct;
|
||||
skipped_refcounted = id_remap_data.skipped_refcounted;
|
||||
|
||||
/* If old_id was used by some ugly 'user_one' stuff (like Image or Clip editors...), and user
|
||||
* count has actually been incremented for that, we have to decrease once more its user count...
|
||||
* unless we had to skip some 'user_one' cases. */
|
||||
if ((old_id->tag & LIB_TAG_EXTRAUSER_SET) &&
|
||||
!(id_remap_data.status & ID_REMAP_IS_USER_ONE_SKIPPED)) {
|
||||
id_us_clear_real(old_id);
|
||||
if ((remap_flags & ID_REMAP_SKIP_USER_CLEAR) == 0) {
|
||||
/* If old_id was used by some ugly 'user_one' stuff (like Image or Clip editors...), and user
|
||||
* count has actually been incremented for that, we have to decrease once more its user
|
||||
* count... unless we had to skip some 'user_one' cases. */
|
||||
if ((old_id->tag & LIB_TAG_EXTRAUSER_SET) &&
|
||||
!(id_remap_data.status & ID_REMAP_IS_USER_ONE_SKIPPED)) {
|
||||
id_us_clear_real(old_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (old_id->us - skipped_refcounted < 0) {
|
||||
|
|
|
@ -2985,6 +2985,12 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
|
|||
|
||||
sclip->scopes.ok = 0;
|
||||
}
|
||||
else if (sl->spacetype == SPACE_SPREADSHEET) {
|
||||
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
|
||||
|
||||
sspreadsheet->pinned_id = restore_pointer_by_name(
|
||||
id_map, sspreadsheet->pinned_id, USER_IGNORE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,13 +139,13 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
|
|||
*
|
||||
* Following Frostbite stochastic SSR.
|
||||
*
|
||||
* - First pass Trace rays across the depth buffer. The hit position and pdf are
|
||||
* - First pass Trace rays across the depth buffer. The hit position and PDF are
|
||||
* recorded in a RGBA16F render target for each ray (sample).
|
||||
*
|
||||
* - We down-sample the previous frame color buffer.
|
||||
*
|
||||
* - For each final pixel, we gather neighbors rays and choose a color buffer
|
||||
* mipmap for each ray using its pdf. (filtered importance sampling)
|
||||
* mipmap for each ray using its PDF. (filtered importance sampling)
|
||||
* We then evaluate the lighting from the probes and mix the results together.
|
||||
*/
|
||||
DRW_PASS_CREATE(psl->ssr_raytrace, DRW_STATE_WRITE_COLOR);
|
||||
|
|
|
@ -3095,7 +3095,6 @@ static int gpencil_snap_cursor_to_sel(bContext *C, wmOperator *op)
|
|||
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
|
||||
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
View3D *v3d = CTX_wm_view3d(C);
|
||||
|
||||
float *cursor = scene->cursor.location;
|
||||
float centroid[3] = {0.0f};
|
||||
|
@ -3125,7 +3124,7 @@ static int gpencil_snap_cursor_to_sel(bContext *C, wmOperator *op)
|
|||
}
|
||||
|
||||
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
|
|
|
@ -1710,6 +1710,17 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *op)
|
|||
|
||||
tgpf->mat = ma;
|
||||
|
||||
/* Untag strokes to be sure nothing is pending due any canceled process. */
|
||||
LISTBASE_FOREACH (bGPDlayer *, gpl, &tgpf->gpd->layers) {
|
||||
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
|
||||
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
|
||||
if (gps->flag & GP_STROKE_TAG) {
|
||||
gps->flag &= ~GP_STROKE_TAG;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* check whether the material was newly added */
|
||||
if (totcol != tgpf->ob->totcol) {
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_PROPERTIES, NULL);
|
||||
|
|
|
@ -936,6 +936,12 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
|
|||
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Data Path"),
|
||||
ICON_NONE,
|
||||
"UI_OT_copy_data_path_button");
|
||||
uiItemBooleanO(layout,
|
||||
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Full Data Path"),
|
||||
ICON_NONE,
|
||||
"UI_OT_copy_data_path_button",
|
||||
"full_path",
|
||||
true);
|
||||
|
||||
if (ptr->owner_id && !is_whole_array &&
|
||||
ELEM(type, PROP_BOOLEAN, PROP_INT, PROP_FLOAT, PROP_ENUM)) {
|
||||
|
|
|
@ -1819,7 +1819,7 @@ static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, cons
|
|||
but->strwidth = BLF_width(fstyle->uifont_id, but->drawstr, sizeof(but->drawstr));
|
||||
but->ofs = 0;
|
||||
|
||||
/* First shorten num-buttons eg,
|
||||
/* First shorten number-buttons eg,
|
||||
* Translucency: 0.000
|
||||
* becomes
|
||||
* Trans: 0.000
|
||||
|
@ -3801,7 +3801,7 @@ static void widget_numslider(
|
|||
wtb.draw_inner = false;
|
||||
widgetbase_draw(&wtb, wcol);
|
||||
|
||||
/* Add space at either side of the button so text aligns with numbuttons
|
||||
/* Add space at either side of the button so text aligns with number-buttons
|
||||
* (which have arrow icons). */
|
||||
if (!(state & UI_STATE_TEXT_INPUT)) {
|
||||
rect->xmax -= toffs;
|
||||
|
|
|
@ -1756,7 +1756,7 @@ static int make_links_data_exec(bContext *C, wmOperator *op)
|
|||
}
|
||||
|
||||
DEG_relations_tag_update(bmain);
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, CTX_wm_view3d(C));
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
|
||||
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, CTX_wm_view3d(C));
|
||||
WM_event_add_notifier(C, NC_OBJECT, NULL);
|
||||
|
||||
|
@ -2712,7 +2712,7 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent
|
|||
DEG_id_tag_update(&base->object->id, ID_RECALC_TRANSFORM);
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, base->object);
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, CTX_wm_view3d(C));
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
|
||||
WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, ma);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
|
|
|
@ -193,6 +193,8 @@ static void file_init(wmWindowManager *UNUSED(wm), ScrArea *area)
|
|||
if (sfile->runtime == NULL) {
|
||||
sfile->runtime = MEM_callocN(sizeof(*sfile->runtime), __func__);
|
||||
}
|
||||
/* Validate the params right after file read. */
|
||||
fileselect_refresh_params(sfile);
|
||||
}
|
||||
|
||||
static void file_exit(wmWindowManager *wm, ScrArea *area)
|
||||
|
@ -418,6 +420,15 @@ static void file_on_reload_callback_call(SpaceFile *sfile)
|
|||
sfile->runtime->on_reload_custom_data = NULL;
|
||||
}
|
||||
|
||||
static void file_reset_filelist_showing_main_data(ScrArea *area, SpaceFile *sfile)
|
||||
{
|
||||
if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) {
|
||||
/* Full refresh of the file list if local asset data was changed. Refreshing this view
|
||||
* is cheap and users expect this to be updated immediately. */
|
||||
file_tag_reset_list(area, sfile);
|
||||
}
|
||||
}
|
||||
|
||||
static void file_listener(const wmSpaceTypeListenerParams *params)
|
||||
{
|
||||
ScrArea *area = params->area;
|
||||
|
@ -444,6 +455,11 @@ static void file_listener(const wmSpaceTypeListenerParams *params)
|
|||
ED_area_tag_refresh(area);
|
||||
}
|
||||
break;
|
||||
case ND_SPACE_CHANGED:
|
||||
/* If the space was just turned into a file/asset browser, the file-list may need to be
|
||||
* updated to reflect latest changes in main data. */
|
||||
file_reset_filelist_showing_main_data(area, sfile);
|
||||
break;
|
||||
}
|
||||
switch (wmn->action) {
|
||||
case NA_JOB_FINISHED:
|
||||
|
@ -460,11 +476,7 @@ static void file_listener(const wmSpaceTypeListenerParams *params)
|
|||
case NA_ADDED:
|
||||
case NA_REMOVED:
|
||||
case NA_EDITED:
|
||||
if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) {
|
||||
/* Full refresh of the file list if local asset data was changed. Refreshing this view
|
||||
* is cheap and users expect this to be updated immediately. */
|
||||
file_tag_reset_list(area, sfile);
|
||||
}
|
||||
file_reset_filelist_showing_main_data(area, sfile);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -888,13 +900,11 @@ static void file_id_remap(ScrArea *area, SpaceLink *sl, ID *UNUSED(old_id), ID *
|
|||
{
|
||||
SpaceFile *sfile = (SpaceFile *)sl;
|
||||
|
||||
/* If the file shows main data (IDs), tag it for reset. */
|
||||
if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) {
|
||||
/* Full refresh of the file list if main data was changed, don't even attempt remap pointers.
|
||||
* We could give file list types a id-remap callback, but it's probably not worth it.
|
||||
* Refreshing local file lists is relatively cheap. */
|
||||
file_tag_reset_list(area, sfile);
|
||||
}
|
||||
/* If the file shows main data (IDs), tag it for reset.
|
||||
* Full reset of the file list if main data was changed, don't even attempt remap pointers.
|
||||
* We could give file list types a id-remap callback, but it's probably not worth it.
|
||||
* Refreshing local file lists is relatively cheap. */
|
||||
file_reset_filelist_showing_main_data(area, sfile);
|
||||
}
|
||||
|
||||
/* only called once, from space/spacetypes.c */
|
||||
|
|
|
@ -647,7 +647,7 @@ static int material_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEve
|
|||
BKE_object_material_assign(bmain, ob, ma, ob->totcol + 1, BKE_MAT_ASSIGN_USERPREF);
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, ob);
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, CTX_wm_view3d(C));
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
|
||||
WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, ma);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
|
|
|
@ -889,6 +889,10 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
|
|||
/* New C++ based type handle (`TreeElementType` in C, `AbstractTreeElement` in C++). Only some
|
||||
* support this, eventually this should replace `TreeElement` entirely. */
|
||||
te->type = outliner_tree_element_type_create(type, te, idv);
|
||||
if (te->type) {
|
||||
/* Element types ported to the new design are expected to have their name set at this point! */
|
||||
BLI_assert(te->name != NULL);
|
||||
}
|
||||
|
||||
if (ELEM(type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) {
|
||||
/* pass */
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
* \ingroup spoutliner
|
||||
*/
|
||||
|
||||
#include "DNA_anim_types.h"
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#include "tree_element_anim_data.hh"
|
||||
|
@ -36,15 +37,25 @@ namespace blender::ed::outliner {
|
|||
|
||||
static AbstractTreeElement *tree_element_create(int type, TreeElement &legacy_te, void *idv)
|
||||
{
|
||||
/* Would be nice to get rid of void * here, can we somehow expect the right type right away?
|
||||
* Perfect forwarding maybe, once the API is C++ only? */
|
||||
ID &id = *static_cast<ID *>(idv);
|
||||
|
||||
/*
|
||||
* The following calls make an implicit assumption about what data was passed to the `idv`
|
||||
* argument of #outliner_add_element(). The old code does this already, here we just centralize
|
||||
* it as much as possible for now. Would be nice to entirely get rid of that, no more `void *`.
|
||||
*
|
||||
* Once #outliner_add_element() is sufficiently simplified, it should be replaced by a C++ call.
|
||||
* It could take the derived type as template parameter (e.g. #TreeElementAnimData) and use C++
|
||||
* perfect forwarding to pass any data to the type's constructor.
|
||||
* If general Outliner code wants to access the data, they can query that through the derived
|
||||
* element type then. There's no need for `void *` anymore then.
|
||||
*/
|
||||
|
||||
switch (type) {
|
||||
case TSE_SOME_ID:
|
||||
return TreeElementID::createFromID(legacy_te, id);
|
||||
case TSE_ANIM_DATA:
|
||||
return new TreeElementAnimData(legacy_te, id);
|
||||
return new TreeElementAnimData(legacy_te, *reinterpret_cast<IdAdtTemplate &>(id).adt);
|
||||
case TSE_DRIVER_BASE:
|
||||
return new TreeElementDriverBase(legacy_te, *static_cast<AnimData *>(idv));
|
||||
case TSE_NLA:
|
||||
|
@ -52,7 +63,7 @@ static AbstractTreeElement *tree_element_create(int type, TreeElement &legacy_te
|
|||
case TSE_NLA_TRACK:
|
||||
return new TreeElementNLATrack(legacy_te, *static_cast<NlaTrack *>(idv));
|
||||
case TSE_NLA_ACTION:
|
||||
return new TreeElementNLAAction(legacy_te);
|
||||
return new TreeElementNLAAction(legacy_te, *static_cast<bAction *>(idv));
|
||||
case TSE_GP_LAYER:
|
||||
return new TreeElementGPencilLayer(legacy_te, *static_cast<bGPDlayer *>(idv));
|
||||
case TSE_R_LAYER_BASE:
|
||||
|
|
|
@ -32,8 +32,8 @@
|
|||
|
||||
namespace blender::ed::outliner {
|
||||
|
||||
TreeElementAnimData::TreeElementAnimData(TreeElement &legacy_te, ID &id)
|
||||
: AbstractTreeElement(legacy_te), anim_data_(*reinterpret_cast<IdAdtTemplate &>(id).adt)
|
||||
TreeElementAnimData::TreeElementAnimData(TreeElement &legacy_te, AnimData &anim_data)
|
||||
: AbstractTreeElement(legacy_te), anim_data_(anim_data)
|
||||
{
|
||||
BLI_assert(legacy_te.store_elem->type == TSE_ANIM_DATA);
|
||||
/* this element's info */
|
||||
|
|
|
@ -30,7 +30,7 @@ class TreeElementAnimData final : public AbstractTreeElement {
|
|||
AnimData &anim_data_;
|
||||
|
||||
public:
|
||||
TreeElementAnimData(TreeElement &legacy_te, ID &id);
|
||||
TreeElementAnimData(TreeElement &legacy_te, AnimData &anim_data);
|
||||
|
||||
void expand(SpaceOutliner &space_outliner) const override;
|
||||
|
||||
|
|
|
@ -70,9 +70,11 @@ void TreeElementNLATrack::expand(SpaceOutliner &space_outliner) const
|
|||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
TreeElementNLAAction::TreeElementNLAAction(TreeElement &legacy_te) : AbstractTreeElement(legacy_te)
|
||||
TreeElementNLAAction::TreeElementNLAAction(TreeElement &legacy_te, const bAction &action)
|
||||
: AbstractTreeElement(legacy_te)
|
||||
{
|
||||
BLI_assert(legacy_te.store_elem->type == TSE_NLA_ACTION);
|
||||
legacy_te.name = action.id.name + 2;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::outliner
|
||||
|
|
|
@ -46,7 +46,7 @@ class TreeElementNLATrack final : public AbstractTreeElement {
|
|||
|
||||
class TreeElementNLAAction final : public AbstractTreeElement {
|
||||
public:
|
||||
TreeElementNLAAction(TreeElement &legacy_te);
|
||||
TreeElementNLAAction(TreeElement &legacy_te, const bAction &action);
|
||||
};
|
||||
|
||||
} // namespace blender::ed::outliner
|
||||
|
|
|
@ -126,7 +126,7 @@ static std::unique_ptr<SpreadsheetDrawer> generate_spreadsheet_drawer(const bCon
|
|||
return {};
|
||||
}
|
||||
Object *object_orig = (Object *)used_id;
|
||||
if (object_orig->type != OB_MESH) {
|
||||
if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD)) {
|
||||
return {};
|
||||
}
|
||||
Object *object_eval = DEG_get_evaluated_object(depsgraph, object_orig);
|
||||
|
|
|
@ -332,10 +332,11 @@ static void add_columns_for_attribute(const ReadAttribute *attribute,
|
|||
}
|
||||
}
|
||||
|
||||
static GeometrySet get_display_geometry_set(Object *object_eval)
|
||||
static GeometrySet get_display_geometry_set(Object *object_eval,
|
||||
const GeometryComponentType used_component_type)
|
||||
{
|
||||
GeometrySet geometry_set;
|
||||
if (object_eval->mode == OB_MODE_EDIT) {
|
||||
if (used_component_type == GEO_COMPONENT_TYPE_MESH && object_eval->mode == OB_MODE_EDIT) {
|
||||
Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_eval, false);
|
||||
if (mesh == nullptr) {
|
||||
return geometry_set;
|
||||
|
@ -354,10 +355,87 @@ static GeometrySet get_display_geometry_set(Object *object_eval)
|
|||
return geometry_set;
|
||||
}
|
||||
|
||||
static Span<int64_t> filter_visible_mesh_vertex_rows(const bContext *C,
|
||||
Object *object_eval,
|
||||
const MeshComponent *component,
|
||||
ResourceCollector &resources)
|
||||
using IsVertexSelectedFn = FunctionRef<bool(int vertex_index)>;
|
||||
|
||||
static void get_selected_vertex_indices(const Mesh &mesh,
|
||||
const IsVertexSelectedFn is_vertex_selected_fn,
|
||||
Vector<int64_t> &r_vertex_indices)
|
||||
{
|
||||
for (const int i : IndexRange(mesh.totvert)) {
|
||||
if (is_vertex_selected_fn(i)) {
|
||||
r_vertex_indices.append(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_selected_corner_indices(const Mesh &mesh,
|
||||
const IsVertexSelectedFn is_vertex_selected_fn,
|
||||
Vector<int64_t> &r_corner_indices)
|
||||
{
|
||||
for (const int i : IndexRange(mesh.totloop)) {
|
||||
const MLoop &loop = mesh.mloop[i];
|
||||
if (is_vertex_selected_fn(loop.v)) {
|
||||
r_corner_indices.append(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_selected_polygon_indices(const Mesh &mesh,
|
||||
const IsVertexSelectedFn is_vertex_selected_fn,
|
||||
Vector<int64_t> &r_polygon_indices)
|
||||
{
|
||||
for (const int poly_index : IndexRange(mesh.totpoly)) {
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
bool is_selected = true;
|
||||
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
if (!is_vertex_selected_fn(loop.v)) {
|
||||
is_selected = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_selected) {
|
||||
r_polygon_indices.append(poly_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_selected_edge_indices(const Mesh &mesh,
|
||||
const IsVertexSelectedFn is_vertex_selected_fn,
|
||||
Vector<int64_t> &r_edge_indices)
|
||||
{
|
||||
for (const int i : IndexRange(mesh.totedge)) {
|
||||
const MEdge &edge = mesh.medge[i];
|
||||
if (is_vertex_selected_fn(edge.v1) && is_vertex_selected_fn(edge.v2)) {
|
||||
r_edge_indices.append(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_selected_indices_on_domain(const Mesh &mesh,
|
||||
const AttributeDomain domain,
|
||||
const IsVertexSelectedFn is_vertex_selected_fn,
|
||||
Vector<int64_t> &r_indices)
|
||||
{
|
||||
switch (domain) {
|
||||
case ATTR_DOMAIN_POINT:
|
||||
return get_selected_vertex_indices(mesh, is_vertex_selected_fn, r_indices);
|
||||
case ATTR_DOMAIN_POLYGON:
|
||||
return get_selected_polygon_indices(mesh, is_vertex_selected_fn, r_indices);
|
||||
case ATTR_DOMAIN_CORNER:
|
||||
return get_selected_corner_indices(mesh, is_vertex_selected_fn, r_indices);
|
||||
case ATTR_DOMAIN_EDGE:
|
||||
return get_selected_edge_indices(mesh, is_vertex_selected_fn, r_indices);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static Span<int64_t> filter_mesh_elements_by_selection(const bContext *C,
|
||||
Object *object_eval,
|
||||
const MeshComponent *component,
|
||||
const AttributeDomain domain,
|
||||
ResourceCollector &resources)
|
||||
{
|
||||
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
|
||||
const bool show_only_selected = sspreadsheet->filter_flag & SPREADSHEET_FILTER_SELECTED_ONLY;
|
||||
|
@ -372,47 +450,55 @@ static Span<int64_t> filter_visible_mesh_vertex_rows(const bContext *C,
|
|||
int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
|
||||
if (orig_indices != nullptr) {
|
||||
/* Use CD_ORIGINDEX layer if it exists. */
|
||||
for (const int i_eval : IndexRange(mesh_eval->totvert)) {
|
||||
const int i_orig = orig_indices[i_eval];
|
||||
if (i_orig >= 0 && i_orig < bm->totvert) {
|
||||
BMVert *vert = bm->vtable[i_orig];
|
||||
if (BM_elem_flag_test(vert, BM_ELEM_SELECT)) {
|
||||
visible_rows.append(i_eval);
|
||||
}
|
||||
auto is_vertex_selected = [&](int vertex_index) -> bool {
|
||||
const int i_orig = orig_indices[vertex_index];
|
||||
if (i_orig < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (i_orig >= bm->totvert) {
|
||||
return false;
|
||||
}
|
||||
BMVert *vert = bm->vtable[i_orig];
|
||||
return BM_elem_flag_test(vert, BM_ELEM_SELECT);
|
||||
};
|
||||
get_selected_indices_on_domain(*mesh_eval, domain, is_vertex_selected, visible_rows);
|
||||
}
|
||||
else if (mesh_eval->totvert == bm->totvert) {
|
||||
/* Use a simple heuristic to match original vertices to evaluated ones. */
|
||||
for (const int i : IndexRange(mesh_eval->totvert)) {
|
||||
BMVert *vert = bm->vtable[i];
|
||||
if (BM_elem_flag_test(vert, BM_ELEM_SELECT)) {
|
||||
visible_rows.append(i);
|
||||
}
|
||||
}
|
||||
auto is_vertex_selected = [&](int vertex_index) -> bool {
|
||||
BMVert *vert = bm->vtable[vertex_index];
|
||||
return BM_elem_flag_test(vert, BM_ELEM_SELECT);
|
||||
};
|
||||
get_selected_indices_on_domain(*mesh_eval, domain, is_vertex_selected, visible_rows);
|
||||
}
|
||||
/* This is safe, because the vector lives in the resource collector. */
|
||||
return visible_rows.as_span();
|
||||
}
|
||||
/* No filter is used. */
|
||||
const int domain_size = component->attribute_domain_size(ATTR_DOMAIN_POINT);
|
||||
const int domain_size = component->attribute_domain_size(domain);
|
||||
return IndexRange(domain_size).as_span();
|
||||
}
|
||||
|
||||
std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_geometry_attributes(const bContext *C,
|
||||
Object *object_eval)
|
||||
{
|
||||
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
|
||||
const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain;
|
||||
const GeometryComponentType component_type = (GeometryComponentType)
|
||||
sspreadsheet->geometry_component_type;
|
||||
|
||||
/* Create a resource collector that owns stuff that needs to live until drawing is done. */
|
||||
std::unique_ptr<ResourceCollector> resources = std::make_unique<ResourceCollector>();
|
||||
GeometrySet &geometry_set = resources->add_value(get_display_geometry_set(object_eval),
|
||||
"geometry set");
|
||||
GeometrySet &geometry_set = resources->add_value(
|
||||
get_display_geometry_set(object_eval, component_type), "geometry set");
|
||||
|
||||
const AttributeDomain domain = ATTR_DOMAIN_POINT;
|
||||
const GeometryComponentType component_type = GEO_COMPONENT_TYPE_MESH;
|
||||
const GeometryComponent *component = geometry_set.get_component_for_read(component_type);
|
||||
if (component == nullptr) {
|
||||
return {};
|
||||
}
|
||||
if (!component->attribute_domain_supported(domain)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
Vector<std::string> attribute_names = get_sorted_attribute_names_to_display(*component, domain);
|
||||
|
||||
|
@ -425,9 +511,14 @@ std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_geometry_attributes(c
|
|||
}
|
||||
|
||||
/* The filter below only works for mesh vertices currently. */
|
||||
BLI_assert(domain == ATTR_DOMAIN_POINT && component_type == GEO_COMPONENT_TYPE_MESH);
|
||||
Span<int64_t> visible_rows = filter_visible_mesh_vertex_rows(
|
||||
C, object_eval, static_cast<const MeshComponent *>(component), *resources);
|
||||
Span<int64_t> visible_rows;
|
||||
if (component_type == GEO_COMPONENT_TYPE_MESH) {
|
||||
visible_rows = filter_mesh_elements_by_selection(
|
||||
C, object_eval, static_cast<const MeshComponent *>(component), domain, *resources);
|
||||
}
|
||||
else {
|
||||
visible_rows = IndexRange(component->attribute_domain_size(domain)).as_span();
|
||||
}
|
||||
|
||||
const int domain_size = component->attribute_domain_size(domain);
|
||||
return std::make_unique<GeometryAttributeSpreadsheetDrawer>(
|
||||
|
|
|
@ -1575,7 +1575,7 @@ static void do_view3d_region_buttons(bContext *C, void *UNUSED(index), int event
|
|||
}
|
||||
|
||||
/* default for now */
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
|
||||
}
|
||||
|
||||
static bool view3d_panel_transform_poll(const bContext *C, PanelType *UNUSED(pt))
|
||||
|
|
|
@ -667,7 +667,7 @@ static int snap_curs_to_grid_exec(bContext *C, wmOperator *UNUSED(op))
|
|||
curs[1] = gridf * floorf(0.5f + curs[1] / gridf);
|
||||
curs[2] = gridf * floorf(0.5f + curs[2] / gridf);
|
||||
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); /* hrm */
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL); /* hrm */
|
||||
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
|
@ -913,7 +913,7 @@ static int snap_curs_to_active_exec(bContext *C, wmOperator *UNUSED(op))
|
|||
View3D *v3d = CTX_wm_view3d(C);
|
||||
|
||||
if (snap_calc_active_center(C, false, scene->cursor.location)) {
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
|
||||
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
|
|
|
@ -163,14 +163,13 @@ const EnumPropertyItem rna_enum_transform_mode_types[] = {
|
|||
static int select_orientation_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
View3D *v3d = CTX_wm_view3d(C);
|
||||
|
||||
int orientation = RNA_enum_get(op->ptr, "orientation");
|
||||
|
||||
BKE_scene_orientation_slot_set_index(&scene->orientation_slots[SCE_ORIENT_DEFAULT], orientation);
|
||||
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL);
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
|
||||
|
||||
struct wmMsgBus *mbus = CTX_wm_message_bus(C);
|
||||
WM_msg_publish_rna_prop(mbus, &scene->id, scene, TransformOrientationSlot, type);
|
||||
|
@ -286,7 +285,7 @@ static int create_orientation_exec(bContext *C, wmOperator *op)
|
|||
WM_event_add_notifier(C, NC_SCENE | NA_EDITED, scene);
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
|
||||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
|
|
@ -572,6 +572,10 @@ enum {
|
|||
* When set #ID.session_uuid isn't initialized, since the data isn't part of the session. */
|
||||
LIB_TAG_TEMP_MAIN = 1 << 20,
|
||||
|
||||
/**
|
||||
* The data-block is a library override that needs re-sync to its linked reference.
|
||||
*/
|
||||
LIB_TAG_LIB_OVERRIDE_NEED_RESYNC = 1 << 21,
|
||||
};
|
||||
|
||||
/* Tag given ID for an update in all the dependency graphs. */
|
||||
|
|
|
@ -1858,7 +1858,12 @@ typedef struct SpaceSpreadsheet {
|
|||
/* eSpaceSpreadsheet_FilterFlag. */
|
||||
uint8_t filter_flag;
|
||||
|
||||
char _pad1[7];
|
||||
/* #GeometryComponentType. */
|
||||
uint8_t geometry_component_type;
|
||||
/* #AttributeDomain. */
|
||||
uint8_t attribute_domain;
|
||||
|
||||
char _pad1[5];
|
||||
} SpaceSpreadsheet;
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -61,7 +61,7 @@ static CLG_LogRef LOG = {"rna.access_compare_override"};
|
|||
* Find the actual ID owner of the given \a ptr #PointerRNA, in override sense, and generate the
|
||||
* full rna path from it to given \a prop #PropertyRNA if \a rna_path is given.
|
||||
*
|
||||
* \note this is slightly different than 'generic' RNA 'id owner' as returned by
|
||||
* \note This is slightly different than 'generic' RNA 'id owner' as returned by
|
||||
* #RNA_find_real_ID_and_path, since in overrides we also consider shape keys as embedded data, not
|
||||
* only root node trees and master collections.
|
||||
*/
|
||||
|
@ -104,10 +104,6 @@ static ID *rna_property_override_property_real_id_owner(Main *bmain,
|
|||
}
|
||||
}
|
||||
|
||||
if (!ID_IS_OVERRIDE_LIBRARY_REAL(owner_id)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (r_rna_path == NULL) {
|
||||
return owner_id;
|
||||
}
|
||||
|
@ -1158,6 +1154,39 @@ void RNA_struct_override_apply(Main *bmain,
|
|||
ptr_storage, op->rna_path, &data_storage, &prop_storage, &data_item_storage);
|
||||
}
|
||||
|
||||
/* Check if an overridden ID pointer supposed to be in sync with linked data gets out of
|
||||
* sync. */
|
||||
if ((ptr_dst->owner_id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0 &&
|
||||
op->rna_prop_type == PROP_POINTER &&
|
||||
(((IDOverrideLibraryPropertyOperation *)op->operations.first)->flag &
|
||||
IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) != 0) {
|
||||
BLI_assert(ptr_src->owner_id ==
|
||||
rna_property_override_property_real_id_owner(bmain, &data_src, NULL, NULL));
|
||||
BLI_assert(ptr_dst->owner_id ==
|
||||
rna_property_override_property_real_id_owner(bmain, &data_dst, NULL, NULL));
|
||||
|
||||
PointerRNA prop_ptr_src = RNA_property_pointer_get(&data_src, prop_src);
|
||||
PointerRNA prop_ptr_dst = RNA_property_pointer_get(&data_dst, prop_dst);
|
||||
ID *id_src = rna_property_override_property_real_id_owner(
|
||||
bmain, &prop_ptr_src, NULL, NULL);
|
||||
ID *id_dst = rna_property_override_property_real_id_owner(
|
||||
bmain, &prop_ptr_dst, NULL, NULL);
|
||||
|
||||
BLI_assert(id_src == NULL || ID_IS_OVERRIDE_LIBRARY_REAL(id_src));
|
||||
|
||||
if (/* We might be in a case where id_dst has already been processed and its usages
|
||||
* remapped to its new local override. In that case overrides and linked data are
|
||||
* always properly matching. */
|
||||
id_src != id_dst &&
|
||||
/* If one of the pointers is NULL and not the other, or if linked reference ID of
|
||||
* `id_src` is not `id_dst`, we are in a non-matching case. */
|
||||
(ELEM(NULL, id_src, id_dst) || id_src->override_library->reference != id_dst)) {
|
||||
ptr_dst->owner_id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
|
||||
CLOG_INFO(
|
||||
&LOG, 3, "Local override %s detected as needing resync", ptr_dst->owner_id->name);
|
||||
}
|
||||
}
|
||||
|
||||
rna_property_override_apply_ex(bmain,
|
||||
&data_dst,
|
||||
&data_src,
|
||||
|
|
|
@ -1238,6 +1238,8 @@ static bool rna_property_override_diff_propptr_validate_diffing(PointerRNA *prop
|
|||
|
||||
/* Used for both Pointer and Collection properties. */
|
||||
static int rna_property_override_diff_propptr(Main *bmain,
|
||||
ID *owner_id_a,
|
||||
ID *owner_id_b,
|
||||
PointerRNA *propptr_a,
|
||||
PointerRNA *propptr_b,
|
||||
eRNACompareMode mode,
|
||||
|
@ -1359,6 +1361,17 @@ static int rna_property_override_diff_propptr(Main *bmain,
|
|||
* override is not matching its reference anymore. */
|
||||
opop->flag &= ~IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE;
|
||||
}
|
||||
else if ((owner_id_a->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) != 0 ||
|
||||
(owner_id_b->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) != 0) {
|
||||
/* In case one of the owner of the checked property is tagged as needing resync, do
|
||||
* not change the 'match reference' status of its ID pointer properties overrides,
|
||||
* since many non-matching ones are likely due to missing resync. */
|
||||
printf(
|
||||
"%s: Not checking matching ID pointer properties, since owner %s is tagged as "
|
||||
"needing resync.\n",
|
||||
__func__,
|
||||
id_a->name);
|
||||
}
|
||||
else if (id_a->override_library != NULL && id_a->override_library->reference == id_b) {
|
||||
opop->flag |= IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE;
|
||||
}
|
||||
|
@ -1778,6 +1791,8 @@ int rna_property_override_diff_default(Main *bmain,
|
|||
PointerRNA propptr_a = RNA_property_pointer_get(ptr_a, rawprop_a);
|
||||
PointerRNA propptr_b = RNA_property_pointer_get(ptr_b, rawprop_b);
|
||||
return rna_property_override_diff_propptr(bmain,
|
||||
ptr_a->owner_id,
|
||||
ptr_b->owner_id,
|
||||
&propptr_a,
|
||||
&propptr_b,
|
||||
mode,
|
||||
|
@ -1934,6 +1949,8 @@ int rna_property_override_diff_default(Main *bmain,
|
|||
else if (is_id || is_valid_for_diffing) {
|
||||
if (equals || do_create) {
|
||||
const int eq = rna_property_override_diff_propptr(bmain,
|
||||
ptr_a->owner_id,
|
||||
ptr_b->owner_id,
|
||||
&iter_a.ptr,
|
||||
&iter_b.ptr,
|
||||
mode,
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BKE_attribute.h"
|
||||
#include "BKE_geometry_set.h"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_key.h"
|
||||
#include "BKE_movieclip.h"
|
||||
|
@ -2991,6 +2993,48 @@ static void rna_SpaceSpreadsheet_pinned_id_set(PointerRNA *ptr,
|
|||
sspreadsheet->pinned_id = value.data;
|
||||
}
|
||||
|
||||
static void rna_SpaceSpreadsheet_geometry_component_type_update(Main *UNUSED(bmain),
|
||||
Scene *UNUSED(scene),
|
||||
PointerRNA *ptr)
|
||||
{
|
||||
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data;
|
||||
if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) {
|
||||
sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT;
|
||||
}
|
||||
}
|
||||
|
||||
const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *UNUSED(C),
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *UNUSED(prop),
|
||||
bool *r_free)
|
||||
{
|
||||
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data;
|
||||
EnumPropertyItem *item_array = NULL;
|
||||
int items_len = 0;
|
||||
for (const EnumPropertyItem *item = rna_enum_attribute_domain_items; item->identifier != NULL;
|
||||
item++) {
|
||||
if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_MESH) {
|
||||
if (!ELEM(item->value,
|
||||
ATTR_DOMAIN_CORNER,
|
||||
ATTR_DOMAIN_EDGE,
|
||||
ATTR_DOMAIN_POINT,
|
||||
ATTR_DOMAIN_POLYGON)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) {
|
||||
if (item->value != ATTR_DOMAIN_POINT) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
RNA_enum_item_add(&item_array, &items_len, item);
|
||||
}
|
||||
RNA_enum_item_end(&item_array, &items_len);
|
||||
|
||||
*r_free = true;
|
||||
return item_array;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static const EnumPropertyItem dt_uv_items[] = {
|
||||
|
@ -7196,6 +7240,20 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
|
|||
PropertyRNA *prop;
|
||||
StructRNA *srna;
|
||||
|
||||
static const EnumPropertyItem geometry_component_type_items[] = {
|
||||
{GEO_COMPONENT_TYPE_MESH,
|
||||
"MESH",
|
||||
ICON_MESH_DATA,
|
||||
"Mesh",
|
||||
"Mesh component containing point, corner, edge and polygon data"},
|
||||
{GEO_COMPONENT_TYPE_POINT_CLOUD,
|
||||
"POINTCLOUD",
|
||||
ICON_POINTCLOUD_DATA,
|
||||
"Point Cloud",
|
||||
"Point cloud component containing only point data"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
srna = RNA_def_struct(brna, "SpaceSpreadsheet", "Space");
|
||||
RNA_def_struct_ui_text(srna, "Space Spreadsheet", "Spreadsheet space data");
|
||||
|
||||
|
@ -7210,6 +7268,20 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(
|
||||
prop, "Show Only Selected", "Only include rows that correspond to selected elements");
|
||||
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "geometry_component_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, geometry_component_type_items);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Geometry Component", "Part of the geometry to display data from");
|
||||
RNA_def_property_update(prop,
|
||||
NC_SPACE | ND_SPACE_SPREADSHEET,
|
||||
"rna_SpaceSpreadsheet_geometry_component_type_update");
|
||||
|
||||
prop = RNA_def_property(srna, "attribute_domain", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
|
||||
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_SpaceSpreadsheet_attribute_domain_itemf");
|
||||
RNA_def_property_ui_text(prop, "Attribute Domain", "Attribute domain to display");
|
||||
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
|
||||
}
|
||||
|
||||
void RNA_def_space(BlenderRNA *brna)
|
||||
|
|
|
@ -331,7 +331,7 @@ class GeometryNodesEvaluator {
|
|||
values.append(this->get_input_from_incoming_link(socket_to_compute, from_socket));
|
||||
}
|
||||
else {
|
||||
/* If the same from-socket occures more than once, we make a copy of the first value. This
|
||||
/* If the same from-socket occurs more than once, we make a copy of the first value. This
|
||||
* can happen when a node linked to a multi-input-socket is muted. */
|
||||
GMutablePointer value = values[first_occurence];
|
||||
const CPPType *type = value.type();
|
||||
|
@ -647,7 +647,7 @@ static IDProperty *socket_add_property(IDProperty *settings_prop_group,
|
|||
|
||||
prop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY;
|
||||
|
||||
/* Make the group in the ui container group to hold the property's UI settings. */
|
||||
/* Make the group in the UI container group to hold the property's UI settings. */
|
||||
IDProperty *prop_ui_group;
|
||||
{
|
||||
IDPropertyTemplate idprop = {0};
|
||||
|
|
|
@ -91,7 +91,8 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params)
|
|||
Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in);
|
||||
BKE_mesh_calc_normals(mesh_out);
|
||||
|
||||
geometry_set.replace_mesh(mesh_out);
|
||||
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
|
||||
mesh_component.replace_mesh_but_keep_vertex_group_names(mesh_out);
|
||||
|
||||
BKE_subdiv_free(subdiv);
|
||||
|
||||
|
|
|
@ -181,7 +181,7 @@ void NodeTreeRef::find_origins_skipping_reroutes(InputSocketRef &socket,
|
|||
this->find_origins_skipping_reroutes(*direct_origin->node_->inputs_[0], r_origins);
|
||||
}
|
||||
else {
|
||||
r_origins.append_non_duplicates(direct_origin);
|
||||
r_origins.append(direct_origin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ void register_node_type_sh_value(void)
|
|||
{
|
||||
static bNodeType ntype;
|
||||
|
||||
sh_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0);
|
||||
sh_fn_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0);
|
||||
node_type_socket_templates(&ntype, nullptr, sh_node_value_out);
|
||||
node_type_gpu(&ntype, gpu_shader_value);
|
||||
ntype.expand_in_mf_network = sh_node_value_expand_in_mf_network;
|
||||
|
|
|
@ -287,6 +287,9 @@ typedef struct wmNotifier {
|
|||
#define NC_TEXT (12 << 24)
|
||||
#define NC_WORLD (13 << 24)
|
||||
#define NC_ANIMATION (14 << 24)
|
||||
/* When passing a space as reference data with this (e.g. `WM_event_add_notifier(..., space)`),
|
||||
* the notifier will only be sent to this space. That avoids unnecessary updates for unrelated
|
||||
* spaces. */
|
||||
#define NC_SPACE (15 << 24)
|
||||
#define NC_GEOM (16 << 24)
|
||||
#define NC_NODE (17 << 24)
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
/** \file
|
||||
* \ingroup wm
|
||||
*
|
||||
* Internal functions for managing UI registrable types (operator, UI and menu types).
|
||||
* Internal functions for managing UI registerable types (operator, UI and menu types).
|
||||
*
|
||||
* Also Blender's main event loop (WM_main).
|
||||
*/
|
||||
|
@ -642,10 +642,10 @@ void WM_main(bContext *C)
|
|||
/* Per window, all events to the window, screen, area and region handlers. */
|
||||
wm_event_do_handlers(C);
|
||||
|
||||
/* Wvents have left notes about changes, we handle and cache it. */
|
||||
/* Events have left notes about changes, we handle and cache it. */
|
||||
wm_event_do_notifiers(C);
|
||||
|
||||
/* Wxecute cached changes draw. */
|
||||
/* Execute cached changes draw. */
|
||||
wm_draw_update(C);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -577,6 +577,10 @@ void wm_event_do_notifiers(bContext *C)
|
|||
}
|
||||
|
||||
ED_screen_areas_iter (win, screen, area) {
|
||||
if ((note->category == NC_SPACE) && note->reference &&
|
||||
(note->reference != area->spacedata.first)) {
|
||||
continue;
|
||||
}
|
||||
wmSpaceTypeListenerParams area_params = {
|
||||
.window = win,
|
||||
.area = area,
|
||||
|
@ -1319,7 +1323,7 @@ static int wm_operator_invoke(bContext *C,
|
|||
op->flag |= OP_IS_INVOKE;
|
||||
}
|
||||
|
||||
/* /initialize setting from previous run. */
|
||||
/* Initialize setting from previous run. */
|
||||
if (!is_nested_call && use_last_properties) { /* Not called by py script. */
|
||||
WM_operator_last_properties_init(op);
|
||||
}
|
||||
|
@ -3190,7 +3194,7 @@ static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEv
|
|||
else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
|
||||
event->type = EVT_DROP;
|
||||
|
||||
/* Vreate customdata, first free existing. */
|
||||
/* Create customdata, first free existing. */
|
||||
if (event->customdata) {
|
||||
if (event->customdatafree) {
|
||||
MEM_freeN(event->customdata);
|
||||
|
@ -3201,7 +3205,7 @@ static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEv
|
|||
event->customdata = &wm->drags;
|
||||
event->customdatafree = 1;
|
||||
|
||||
/* Vlear drop icon. */
|
||||
/* Clear drop icon. */
|
||||
screen->do_draw_drag = true;
|
||||
|
||||
/* restore cursor (disabled, see wm_dragdrop.c) */
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
#include "BKE_idprop.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_lib_override.h"
|
||||
#include "BKE_lib_remap.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_packedFile.h"
|
||||
#include "BKE_report.h"
|
||||
|
@ -296,6 +297,33 @@ static void wm_window_match_replace_by_file_wm(bContext *C,
|
|||
{
|
||||
wmWindowManager *oldwm = current_wm_list->first;
|
||||
wmWindowManager *wm = readfile_wm_list->first; /* will become our new WM */
|
||||
|
||||
/* Support window-manager ID references being held between file load operations by keeping
|
||||
* #Main.wm.first memory address in-place, while swapping all of it's contents.
|
||||
*
|
||||
* This is needed so items such as key-maps can be held by an add-on,
|
||||
* without it pointing to invalid memory, see: T86431 */
|
||||
{
|
||||
/* Referencing the window-manager pointer from elsewhere in the file is highly unlikely
|
||||
* however it's possible with ID-properties & animation-drivers.
|
||||
* At some point we could check on disallowing this since it doesn't seem practical. */
|
||||
Main *bmain = G_MAIN;
|
||||
BLI_assert(bmain->relations == NULL);
|
||||
BKE_libblock_remap(bmain, wm, oldwm, ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_USER_CLEAR);
|
||||
|
||||
/* Maintain the undo-depth between file loads. Useful so Python can perform
|
||||
* nested operator calls that exit with the proper undo-depth. */
|
||||
wm->op_undo_depth = oldwm->op_undo_depth;
|
||||
|
||||
/* Simple pointer swapping step. */
|
||||
BLI_remlink(current_wm_list, oldwm);
|
||||
BLI_remlink(readfile_wm_list, wm);
|
||||
SWAP(wmWindowManager, *oldwm, *wm);
|
||||
SWAP(wmWindowManager *, oldwm, wm);
|
||||
BLI_addhead(current_wm_list, oldwm);
|
||||
BLI_addhead(readfile_wm_list, wm);
|
||||
}
|
||||
|
||||
bool has_match = false;
|
||||
|
||||
/* this code could move to setup_appdata */
|
||||
|
@ -713,8 +741,6 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
|
|||
|
||||
WM_cursor_wait(true);
|
||||
|
||||
wm_file_read_pre(C, use_data, use_userdef);
|
||||
|
||||
/* first try to append data from exotic file formats... */
|
||||
/* it throws error box when file doesn't exist and returns -1 */
|
||||
/* note; it should set some error message somewhere... (ton) */
|
||||
|
@ -722,58 +748,60 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
|
|||
|
||||
/* we didn't succeed, now try to read Blender file */
|
||||
if (retval == BKE_READ_EXOTIC_OK_BLEND) {
|
||||
const int G_f_orig = G.f;
|
||||
ListBase wmbase;
|
||||
const struct BlendFileReadParams params = {
|
||||
.is_startup = false,
|
||||
/* Loading preferences when the user intended to load a regular file is a security
|
||||
* risk, because the excluded path list is also loaded. Further it's just confusing
|
||||
* if a user loads a file and various preferences change. */
|
||||
.skip_flags = BLO_READ_SKIP_USERDEF,
|
||||
};
|
||||
|
||||
/* put aside screens to match with persistent windows later */
|
||||
/* also exit screens and editors */
|
||||
wm_window_match_init(C, &wmbase);
|
||||
struct BlendFileData *bfd = BKE_blendfile_read(filepath, ¶ms, reports);
|
||||
if (bfd != NULL) {
|
||||
wm_file_read_pre(C, use_data, use_userdef);
|
||||
|
||||
/* confusing this global... */
|
||||
G.relbase_valid = 1;
|
||||
success = BKE_blendfile_read(
|
||||
C,
|
||||
filepath,
|
||||
/* Loading preferences when the user intended to load a regular file is a security risk,
|
||||
* because the excluded path list is also loaded.
|
||||
* Further it's just confusing if a user loads a file and various preferences change. */
|
||||
&(const struct BlendFileReadParams){
|
||||
.is_startup = false,
|
||||
.skip_flags = BLO_READ_SKIP_USERDEF,
|
||||
},
|
||||
reports);
|
||||
/* Put aside screens to match with persistent windows later,
|
||||
* also exit screens and editors. */
|
||||
ListBase wmbase;
|
||||
wm_window_match_init(C, &wmbase);
|
||||
|
||||
/* BKE_file_read sets new Main into context. */
|
||||
Main *bmain = CTX_data_main(C);
|
||||
/* This flag is initialized by the operator but overwritten on read.
|
||||
* need to re-enable it here else drivers + registered scripts wont work. */
|
||||
const int G_f_orig = G.f;
|
||||
|
||||
/* when loading startup.blend's, we can be left with a blank path */
|
||||
if (BKE_main_blendfile_path(bmain)[0] != '\0') {
|
||||
G.save_over = 1;
|
||||
}
|
||||
else {
|
||||
G.save_over = 0;
|
||||
G.relbase_valid = 0;
|
||||
}
|
||||
BKE_blendfile_read_setup(C, bfd, ¶ms, reports);
|
||||
|
||||
/* this flag is initialized by the operator but overwritten on read.
|
||||
* need to re-enable it here else drivers + registered scripts wont work. */
|
||||
if (G.f != G_f_orig) {
|
||||
const int flags_keep = G_FLAG_ALL_RUNTIME;
|
||||
G.f &= G_FLAG_ALL_READFILE;
|
||||
G.f = (G.f & ~flags_keep) | (G_f_orig & flags_keep);
|
||||
}
|
||||
if (G.f != G_f_orig) {
|
||||
const int flags_keep = G_FLAG_ALL_RUNTIME;
|
||||
G.f &= G_FLAG_ALL_READFILE;
|
||||
G.f = (G.f & ~flags_keep) | (G_f_orig & flags_keep);
|
||||
}
|
||||
|
||||
/* match the read WM with current WM */
|
||||
wm_window_match_do(C, &wmbase, &bmain->wm, &bmain->wm);
|
||||
WM_check(C); /* opens window(s), checks keymaps */
|
||||
/* #BKE_blendfile_read_result_setup sets new Main into context. */
|
||||
Main *bmain = CTX_data_main(C);
|
||||
|
||||
/* When recovering a session from an unsaved file, this can have a blank path. */
|
||||
if (BKE_main_blendfile_path(bmain)[0] != '\0') {
|
||||
G.save_over = 1;
|
||||
G.relbase_valid = 1;
|
||||
}
|
||||
else {
|
||||
G.save_over = 0;
|
||||
G.relbase_valid = 0;
|
||||
}
|
||||
|
||||
/* match the read WM with current WM */
|
||||
wm_window_match_do(C, &wmbase, &bmain->wm, &bmain->wm);
|
||||
WM_check(C); /* opens window(s), checks keymaps */
|
||||
|
||||
if (success) {
|
||||
if (do_history_file_update) {
|
||||
wm_history_file_update();
|
||||
}
|
||||
}
|
||||
|
||||
wm_file_read_post(C, false, false, use_data, use_userdef, false);
|
||||
wm_file_read_post(C, false, false, use_data, use_userdef, false);
|
||||
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
else if (retval == BKE_READ_EXOTIC_OK_OTHER) {
|
||||
|
@ -950,6 +978,9 @@ void wm_homefile_read(bContext *C,
|
|||
#endif /* WITH_PYTHON */
|
||||
}
|
||||
|
||||
/* For regular file loading this only runs after the file is successfully read.
|
||||
* In the case of the startup file, the in-memory startup file is used as a fallback
|
||||
* so we know this will work if all else fails. */
|
||||
wm_file_read_pre(C, use_data, use_userdef);
|
||||
|
||||
if (use_data) {
|
||||
|
@ -1041,15 +1072,17 @@ void wm_homefile_read(bContext *C,
|
|||
|
||||
if (!use_factory_settings || (filepath_startup[0] != '\0')) {
|
||||
if (BLI_access(filepath_startup, R_OK) == 0) {
|
||||
success = BKE_blendfile_read_ex(C,
|
||||
filepath_startup,
|
||||
&(const struct BlendFileReadParams){
|
||||
.is_startup = true,
|
||||
.skip_flags = skip_flags | BLO_READ_SKIP_USERDEF,
|
||||
},
|
||||
NULL,
|
||||
update_defaults && use_data,
|
||||
app_template);
|
||||
const struct BlendFileReadParams params = {
|
||||
.is_startup = true,
|
||||
.skip_flags = skip_flags | BLO_READ_SKIP_USERDEF,
|
||||
};
|
||||
|
||||
struct BlendFileData *bfd = BKE_blendfile_read(filepath_startup, ¶ms, NULL);
|
||||
if (bfd != NULL) {
|
||||
BKE_blendfile_read_setup_ex(
|
||||
C, bfd, ¶ms, NULL, update_defaults && use_data, app_template);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
is_factory_startup = filepath_startup_is_factory;
|
||||
|
@ -1070,16 +1103,16 @@ void wm_homefile_read(bContext *C,
|
|||
}
|
||||
|
||||
if (success == false) {
|
||||
success = BKE_blendfile_read_from_memory_ex(C,
|
||||
datatoc_startup_blend,
|
||||
datatoc_startup_blend_size,
|
||||
&(const struct BlendFileReadParams){
|
||||
.is_startup = true,
|
||||
.skip_flags = skip_flags,
|
||||
},
|
||||
NULL,
|
||||
true,
|
||||
NULL);
|
||||
const struct BlendFileReadParams params = {
|
||||
.is_startup = true,
|
||||
.skip_flags = skip_flags,
|
||||
};
|
||||
struct BlendFileData *bfd = BKE_blendfile_read_from_memory(
|
||||
datatoc_startup_blend, datatoc_startup_blend_size, ¶ms, NULL);
|
||||
if (bfd != NULL) {
|
||||
BKE_blendfile_read_setup_ex(C, bfd, ¶ms, NULL, true, NULL);
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (use_data && BLI_listbase_is_empty(&wmbase)) {
|
||||
wm_clear_default_size(C);
|
||||
|
|
Loading…
Reference in New Issue