diff options
author | Hans Goudey <h.goudey@me.com> | 2021-03-13 17:14:22 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2021-03-13 17:14:22 +0300 |
commit | 9b2b6674ba18cedc836a7b50450485334d6e028d (patch) | |
tree | c72ac9dd193e333f1c5ccfd427cdee3418a310e8 | |
parent | cec588d757c7e442c86aae6d59897ac66a4cc587 (diff) | |
parent | 258b15da74ad6f734d4e9870bbe051066a9a705c (diff) |
Merge branch 'master' into geometry-nodes-mesh-primitives
131 files changed, 2155 insertions, 1317 deletions
diff --git a/.clang-format b/.clang-format index 8a992fea3a9..79aa893cada 100644 --- a/.clang-format +++ b/.clang-format @@ -161,6 +161,7 @@ PenaltyBreakString: 1000000 # "^\s+[A-Z][A-Z0-9_]+\s*\([^\n]*\)\n\s*\{" ForEachMacros: - BEGIN_ANIMFILTER_SUBCHANNELS + - BKE_pbvh_vertex_iter_begin - BLI_FOREACH_SPARSE_RANGE - BLI_SMALLSTACK_ITER_BEGIN - BMO_ITER diff --git a/build_files/utils/make_source_archive.py b/build_files/utils/make_source_archive.py index 24928742a2d..271ca358f7e 100755 --- a/build_files/utils/make_source_archive.py +++ b/build_files/utils/make_source_archive.py @@ -9,6 +9,10 @@ from typing import Iterable, TextIO # This script can run from any location, # output is created in the $CWD +# +# NOTE: while the Python part of this script is portable, +# it relies on external commands typically found on GNU/Linux. +# Support for other platforms could be added by moving GNU `tar` & `md5sum` use to Python. SKIP_NAMES = { ".gitignore", @@ -52,8 +56,9 @@ class BlenderVersion: >>> str(BlenderVersion(327, 0, "release")) '3.27.0' """ - - as_string = f"{self.version/100:.2f}.{self.patch}" + version_major = self.version // 100 + version_minor = self.version % 100 + as_string = f"{version_major}.{version_minor}.{self.patch}" if self.is_release: return as_string return f"{as_string}-{self.cycle}" @@ -101,6 +106,7 @@ def submodules_to_manifest(version: BlenderVersion, outfile: TextIO) -> None: for line in git_command("submodule"): submodule = line.split()[1] + # Don't use native slashes as GIT for MS-Windows outputs forward slashes. if skip_addon_contrib and submodule == "release/scripts/addons_contrib": continue @@ -110,6 +116,7 @@ def submodules_to_manifest(version: BlenderVersion, outfile: TextIO) -> None: def create_tarball(version: BlenderVersion, tarball: Path, manifest: Path) -> None: print(f'Creating archive: "{tarball}" ...', end="", flush=True) + # Requires GNU `tar`, since `--transform` is used. command = [ "tar", "--transform", @@ -139,7 +146,7 @@ def create_checksum_file(tarball: Path) -> None: md5_cmd = subprocess.run( command, stdout=subprocess.PIPE, check=True, text=True, timeout=300 ) - with md5_path.open("w") as outfile: + with md5_path.open("w", encoding="utf-8") as outfile: outfile.write(md5_cmd.stdout) print("OK") diff --git a/extern/mantaflow/CMakeLists.txt b/extern/mantaflow/CMakeLists.txt index 3220a45bef4..9b047eb1a3e 100644 --- a/extern/mantaflow/CMakeLists.txt +++ b/extern/mantaflow/CMakeLists.txt @@ -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") diff --git a/intern/clog/clog.c b/intern/clog/clog.c index a26ac10a61f..391b71d77de 100644 --- a/intern/clog/clog.c +++ b/intern/clog/clog.c @@ -303,19 +303,27 @@ static enum eCLogColor clg_severity_to_color(enum CLG_Severity severity) * - `foo` exact match of `foo`. * - `foo.bar` exact match for `foo.bar` * - `foo.*` match for `foo` & `foo.bar` & `foo.bar.baz` + * - `*bar*` match for `foo.bar` & `baz.bar` & `foo.barbaz` * - `*` matches everything. */ static bool clg_ctx_filter_check(CLogContext *ctx, const char *identifier) { - const int identifier_len = strlen(identifier); + const size_t identifier_len = strlen(identifier); for (uint i = 0; i < 2; i++) { const CLG_IDFilter *flt = ctx->filters[i]; while (flt != NULL) { - const int len = strlen(flt->match); + const size_t len = strlen(flt->match); if (STREQ(flt->match, "*") || ((len == identifier_len) && (STREQ(identifier, flt->match)))) { return (bool)i; } - if ((len >= 2) && (STREQLEN(".*", &flt->match[len - 2], 2))) { + if (flt->match[0] == '*' && flt->match[len - 1] == '*') { + char *match = MEM_callocN(sizeof(char) * len - 1, __func__); + memcpy(match, flt->match + 1, len - 2); + if (strstr(identifier, match) != NULL) { + return (bool)i; + } + } + else if ((len >= 2) && (STREQLEN(".*", &flt->match[len - 2], 2))) { if (((identifier_len == len - 2) && STREQLEN(identifier, flt->match, len - 2)) || ((identifier_len >= len - 1) && STREQLEN(identifier, flt->match, len - 1))) { return (bool)i; diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index dc4437bdc52..91bc1cb84a4 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -226,6 +226,9 @@ def update_render_passes(self, context): view_layer = context.view_layer view_layer.update_render_passes() +def poll_object_is_camera(self, obj): + return obj.type == 'CAMERA' + class CyclesRenderSettings(bpy.types.PropertyGroup): @@ -538,7 +541,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): description="Camera to use as reference point when subdividing geometry, useful to avoid crawling " "artifacts in animations when the scene camera is moving", type=bpy.types.Object, - poll=lambda self, obj: obj.type == 'CAMERA', + poll=poll_object_is_camera, ) offscreen_dicing_scale: FloatProperty( name="Offscreen Dicing Scale", diff --git a/intern/cycles/bvh/bvh_optix.cpp b/intern/cycles/bvh/bvh_optix.cpp index e094f339ede..d630e8965dc 100644 --- a/intern/cycles/bvh/bvh_optix.cpp +++ b/intern/cycles/bvh/bvh_optix.cpp @@ -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) { } diff --git a/intern/cycles/device/cuda/device_cuda_impl.cpp b/intern/cycles/device/cuda/device_cuda_impl.cpp index 44a51835f4c..5b62292ca55 100644 --- a/intern/cycles/device/cuda/device_cuda_impl.cpp +++ b/intern/cycles/device/cuda/device_cuda_impl.cpp @@ -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) { diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp index fdfd3f83be6..e2f9c7391da 100644 --- a/intern/cycles/device/device_cpu.cpp +++ b/intern/cycles/device/device_cpu.cpp @@ -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; diff --git a/intern/cycles/device/device_denoising.h b/intern/cycles/device/device_denoising.h index 2c0dc23b44a..bb8bdfdd225 100644 --- a/intern/cycles/device/device_denoising.h +++ b/intern/cycles/device/device_denoising.h @@ -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; diff --git a/intern/cycles/device/device_memory.h b/intern/cycles/device/device_memory.h index 1f63a152458..97459b9ae6a 100644 --- a/intern/cycles/device/device_memory.h +++ b/intern/cycles/device/device_memory.h @@ -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); diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp index 07ce63f5394..51e1a0033ba 100644 --- a/intern/cycles/device/device_optix.cpp +++ b/intern/cycles/device/device_optix.cpp @@ -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 } } diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/render/alembic.cpp index 468ebf7140f..0841a3c09cd 100644 --- a/intern/cycles/render/alembic.cpp +++ b/intern/cycles/render/alembic.cpp @@ -402,16 +402,16 @@ static void add_uvs(AlembicProcedural *proc, } const ISampleSelector iss = ISampleSelector(time); - const IV2fGeomParam::Sample sample = uvs.getExpandedValue(iss); - const IV2fGeomParam::Sample uvsample = uvs.getIndexedValue(iss); if (!uvsample.valid()) { continue; } - const array<int3> *triangles = cached_data.triangles.data_for_time_no_check(time); - const array<int3> *triangles_loops = cached_data.triangles_loops.data_for_time_no_check(time); + const array<int3> *triangles = + cached_data.triangles.data_for_time_no_check(time).get_data_or_null(); + const array<int3> *triangles_loops = + cached_data.triangles_loops.data_for_time_no_check(time).get_data_or_null(); if (!triangles || !triangles_loops) { continue; @@ -458,7 +458,8 @@ static void add_normals(const Int32ArraySamplePtr face_indices, *normals.getTimeSampling()); attr.std = ATTR_STD_VERTEX_NORMAL; - const array<float3> *vertices = cached_data.vertices.data_for_time_no_check(time); + const array<float3> *vertices = + cached_data.vertices.data_for_time_no_check(time).get_data_or_null(); if (!vertices) { return; @@ -493,7 +494,8 @@ static void add_normals(const Int32ArraySamplePtr face_indices, *normals.getTimeSampling()); attr.std = ATTR_STD_VERTEX_NORMAL; - const array<float3> *vertices = cached_data.vertices.data_for_time_no_check(time); + const array<float3> *vertices = + cached_data.vertices.data_for_time_no_check(time).get_data_or_null(); if (!vertices) { return; @@ -717,11 +719,15 @@ void AlembicObject::read_face_sets(SchemaType &schema, void AlembicObject::load_all_data(AlembicProcedural *proc, IPolyMeshSchema &schema, - float scale, Progress &progress) { cached_data.clear(); + /* Only load data for the original Geometry. */ + if (instance_of) { + return; + } + const TimeSamplingPtr time_sampling = schema.getTimeSampling(); cached_data.set_time_sampling(*time_sampling); @@ -780,22 +786,18 @@ void AlembicObject::load_all_data(AlembicProcedural *proc, add_uvs(proc, uvs, cached_data, progress); } - if (progress.get_cancel()) { - return; - } - - setup_transform_cache(scale); - data_loaded = true; } -void AlembicObject::load_all_data(AlembicProcedural *proc, - ISubDSchema &schema, - float scale, - Progress &progress) +void AlembicObject::load_all_data(AlembicProcedural *proc, ISubDSchema &schema, Progress &progress) { cached_data.clear(); + /* Only load data for the original Geometry. */ + if (instance_of) { + return; + } + AttributeRequestSet requested_attributes = get_requested_attributes(); const TimeSamplingPtr time_sampling = schema.getTimeSampling(); @@ -920,19 +922,21 @@ void AlembicObject::load_all_data(AlembicProcedural *proc, return; } - setup_transform_cache(scale); - data_loaded = true; } void AlembicObject::load_all_data(AlembicProcedural *proc, const ICurvesSchema &schema, - float scale, Progress &progress, float default_radius) { cached_data.clear(); + /* Only load data for the original Geometry. */ + if (instance_of) { + return; + } + const TimeSamplingPtr time_sampling = schema.getTimeSampling(); cached_data.set_time_sampling(*time_sampling); @@ -1007,8 +1011,6 @@ void AlembicObject::load_all_data(AlembicProcedural *proc, // TODO(@kevindietrich): attributes, need example files - setup_transform_cache(scale); - data_loaded = true; } @@ -1017,6 +1019,10 @@ void AlembicObject::setup_transform_cache(float scale) cached_data.transforms.clear(); cached_data.transforms.invalidate_last_loaded_time(); + if (scale == 0.0f) { + scale = 1.0f; + } + if (xform_time_sampling) { cached_data.transforms.set_time_sampling(*xform_time_sampling); } @@ -1107,9 +1113,10 @@ void AlembicObject::read_attribute(const ICompoundProperty &arb_geom_params, attribute.element = ATTR_ELEMENT_CORNER; attribute.type_desc = TypeFloat2; - const array<int3> *triangles = cached_data.triangles.data_for_time_no_check(time); - const array<int3> *triangles_loops = cached_data.triangles_loops.data_for_time_no_check( - time); + const array<int3> *triangles = + cached_data.triangles.data_for_time_no_check(time).get_data_or_null(); + const array<int3> *triangles_loops = + cached_data.triangles_loops.data_for_time_no_check(time).get_data_or_null(); if (!triangles || !triangles_loops) { return; @@ -1162,7 +1169,8 @@ void AlembicObject::read_attribute(const ICompoundProperty &arb_geom_params, attribute.element = ATTR_ELEMENT_CORNER_BYTE; attribute.type_desc = TypeRGBA; - const array<int3> *triangles = cached_data.triangles.data_for_time_no_check(time); + const array<int3> *triangles = + cached_data.triangles.data_for_time_no_check(time).get_data_or_null(); if (!triangles) { return; @@ -1218,7 +1226,8 @@ void AlembicObject::read_attribute(const ICompoundProperty &arb_geom_params, attribute.element = ATTR_ELEMENT_CORNER_BYTE; attribute.type_desc = TypeRGBA; - const array<int3> *triangles = cached_data.triangles.data_for_time_no_check(time); + const array<int3> *triangles = + cached_data.triangles.data_for_time_no_check(time).get_data_or_null(); if (!triangles) { return; @@ -1257,7 +1266,7 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data, set<Attribute *> cached_attributes; for (CachedData::CachedAttribute &attribute : cached_data.attributes) { - const array<char> *attr_data = attribute.data.data_for_time(frame_time); + const array<char> *attr_data = attribute.data.data_for_time(frame_time).get_data_or_null(); Attribute *attr = nullptr; if (attribute.std != ATTR_STD_NONE) { @@ -1282,6 +1291,7 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data, } memcpy(attr->data(), attr_data->data(), attr_data->size()); + attr->modified = true; } /* remove any attributes not in cached_attributes */ @@ -1289,6 +1299,7 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data, for (it = attributes.attributes.begin(); it != attributes.attributes.end();) { if (cached_attributes.find(&(*it)) == cached_attributes.end()) { attributes.attributes.erase(it++); + attributes.modified = true; continue; } @@ -1406,6 +1417,8 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress) const chrono_t frame_time = (chrono_t)((frame - frame_offset) / frame_rate); + build_caches(progress); + foreach (Node *node, objects) { AlembicObject *object = static_cast<AlembicObject *>(node); @@ -1414,19 +1427,19 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress) } /* skip constant objects */ - if (object->has_data_loaded() && object->is_constant() && !object->is_modified() && - !object->need_shader_update && !scale_is_modified()) { + if (object->is_constant() && !object->is_modified() && !object->need_shader_update && + !scale_is_modified()) { continue; } if (object->schema_type == AlembicObject::POLY_MESH) { - read_mesh(object, frame_time, progress); + read_mesh(object, frame_time); } else if (object->schema_type == AlembicObject::CURVES) { - read_curves(object, frame_time, progress); + read_curves(object, frame_time); } else if (object->schema_type == AlembicObject::SUBD) { - read_subd(object, frame_time, progress); + read_subd(object, frame_time); } object->clear_modified(); @@ -1489,22 +1502,24 @@ void AlembicProcedural::load_objects(Progress &progress) Geometry *geometry = nullptr; - if (abc_object->schema_type == AlembicObject::CURVES) { - geometry = scene_->create_node<Hair>(); - } - else if (abc_object->schema_type == AlembicObject::POLY_MESH || - abc_object->schema_type == AlembicObject::SUBD) { - geometry = scene_->create_node<Mesh>(); - } - else { - continue; - } + if (!abc_object->instance_of) { + if (abc_object->schema_type == AlembicObject::CURVES) { + geometry = scene_->create_node<Hair>(); + } + else if (abc_object->schema_type == AlembicObject::POLY_MESH || + abc_object->schema_type == AlembicObject::SUBD) { + geometry = scene_->create_node<Mesh>(); + } + else { + continue; + } - geometry->set_owner(this); - geometry->name = abc_object->iobject.getName(); + geometry->set_owner(this); + geometry->name = abc_object->iobject.getName(); - array<Node *> used_shaders = abc_object->get_used_shaders(); - geometry->set_used_shaders(used_shaders); + array<Node *> used_shaders = abc_object->get_used_shaders(); + geometry->set_used_shaders(used_shaders); + } Object *object = scene_->create_node<Object>(); object->set_owner(this); @@ -1513,31 +1528,22 @@ void AlembicProcedural::load_objects(Progress &progress) abc_object->set_object(object); } -} -void AlembicProcedural::read_mesh(AlembicObject *abc_object, - Abc::chrono_t frame_time, - Progress &progress) -{ - IPolyMesh polymesh(abc_object->iobject, Alembic::Abc::kWrapExisting); - - Mesh *mesh = static_cast<Mesh *>(abc_object->get_object()->get_geometry()); - - CachedData &cached_data = abc_object->get_cached_data(); - IPolyMeshSchema schema = polymesh.getSchema(); - - if (!abc_object->has_data_loaded()) { - abc_object->load_all_data(this, schema, scale, progress); - } - else { - if (abc_object->need_shader_update) { - abc_object->update_shader_attributes(schema.getArbGeomParams(), progress); - } + /* Share geometries between instances. */ + foreach (Node *node, objects) { + AlembicObject *abc_object = static_cast<AlembicObject *>(node); - if (scale_is_modified()) { - abc_object->setup_transform_cache(scale); + if (abc_object->instance_of) { + abc_object->get_object()->set_geometry( + abc_object->instance_of->get_object()->get_geometry()); + abc_object->schema_type = abc_object->instance_of->schema_type; } } +} + +void AlembicProcedural::read_mesh(AlembicObject *abc_object, Abc::chrono_t frame_time) +{ + CachedData &cached_data = abc_object->get_cached_data(); /* update sockets */ @@ -1548,11 +1554,18 @@ void AlembicProcedural::read_mesh(AlembicObject *abc_object, object->tag_update(scene_); } + /* Only update sockets for the original Geometry. */ + if (abc_object->instance_of) { + return; + } + + Mesh *mesh = static_cast<Mesh *>(object->get_geometry()); + cached_data.vertices.copy_to_socket(frame_time, mesh, mesh->get_verts_socket()); cached_data.shader.copy_to_socket(frame_time, mesh, mesh->get_shader_socket()); - array<int3> *triangle_data = cached_data.triangles.data_for_time(frame_time); + array<int3> *triangle_data = cached_data.triangles.data_for_time(frame_time).get_data_or_null(); if (triangle_data) { array<int> triangles; array<bool> smooth; @@ -1590,41 +1603,31 @@ void AlembicProcedural::read_mesh(AlembicObject *abc_object, } } -void AlembicProcedural::read_subd(AlembicObject *abc_object, - Abc::chrono_t frame_time, - Progress &progress) +void AlembicProcedural::read_subd(AlembicObject *abc_object, Abc::chrono_t frame_time) { - ISubD subd_mesh(abc_object->iobject, Alembic::Abc::kWrapExisting); - ISubDSchema schema = subd_mesh.getSchema(); - - Mesh *mesh = static_cast<Mesh *>(abc_object->get_object()->get_geometry()); - - /* Alembic is OpenSubDiv compliant, there is no option to set another subdivision type. */ - mesh->set_subdivision_type(Mesh::SubdivisionType::SUBDIVISION_CATMULL_CLARK); + CachedData &cached_data = abc_object->get_cached_data(); - if (!abc_object->has_data_loaded()) { - abc_object->load_all_data(this, schema, scale, progress); + if (abc_object->subd_max_level_is_modified() || abc_object->subd_dicing_rate_is_modified()) { + /* need to reset the current data is something changed */ + cached_data.invalidate_last_loaded_time(); } - else { - if (abc_object->need_shader_update) { - abc_object->update_shader_attributes(schema.getArbGeomParams(), progress); - } - if (scale_is_modified()) { - abc_object->setup_transform_cache(scale); - } - } + /* Update sockets. */ - mesh->set_subd_max_level(abc_object->get_subd_max_level()); - mesh->set_subd_dicing_rate(abc_object->get_subd_dicing_rate()); + Object *object = abc_object->get_object(); + cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket()); - CachedData &cached_data = abc_object->get_cached_data(); + if (object->is_modified()) { + object->tag_update(scene_); + } - if (abc_object->subd_max_level_is_modified() || abc_object->subd_dicing_rate_is_modified()) { - /* need to reset the current data is something changed */ - cached_data.invalidate_last_loaded_time(); + /* Only update sockets for the original Geometry. */ + if (abc_object->instance_of) { + return; } + Mesh *mesh = static_cast<Mesh *>(object->get_geometry()); + /* Cycles overwrites the original triangles when computing displacement, so we always have to * repass the data if something is animated (vertices most likely) to avoid buffer overflows. */ if (!cached_data.is_constant()) { @@ -1637,14 +1640,10 @@ void AlembicProcedural::read_subd(AlembicObject *abc_object, mesh->clear_non_sockets(); - /* Update sockets. */ - - Object *object = abc_object->get_object(); - cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket()); - - if (object->is_modified()) { - object->tag_update(scene_); - } + /* Alembic is OpenSubDiv compliant, there is no option to set another subdivision type. */ + mesh->set_subdivision_type(Mesh::SubdivisionType::SUBDIVISION_CATMULL_CLARK); + mesh->set_subd_max_level(abc_object->get_subd_max_level()); + mesh->set_subd_dicing_rate(abc_object->get_subd_dicing_rate()); cached_data.vertices.copy_to_socket(frame_time, mesh, mesh->get_verts_socket()); @@ -1699,25 +1698,8 @@ void AlembicProcedural::read_subd(AlembicObject *abc_object, } } -void AlembicProcedural::read_curves(AlembicObject *abc_object, - Abc::chrono_t frame_time, - Progress &progress) +void AlembicProcedural::read_curves(AlembicObject *abc_object, Abc::chrono_t frame_time) { - ICurves curves(abc_object->iobject, Alembic::Abc::kWrapExisting); - Hair *hair = static_cast<Hair *>(abc_object->get_object()->get_geometry()); - - ICurvesSchema schema = curves.getSchema(); - - if (!abc_object->has_data_loaded() || default_radius_is_modified() || - abc_object->radius_scale_is_modified()) { - abc_object->load_all_data(this, schema, scale, progress, default_radius); - } - else { - if (scale_is_modified()) { - abc_object->setup_transform_cache(scale); - } - } - CachedData &cached_data = abc_object->get_cached_data(); /* update sockets */ @@ -1729,6 +1711,13 @@ void AlembicProcedural::read_curves(AlembicObject *abc_object, object->tag_update(scene_); } + /* Only update sockets for the original Geometry. */ + if (abc_object->instance_of) { + return; + } + + Hair *hair = static_cast<Hair *>(object->get_geometry()); + cached_data.curve_keys.copy_to_socket(frame_time, hair, hair->get_curve_keys_socket()); cached_data.curve_radius.copy_to_socket(frame_time, hair, hair->get_curve_radius_socket()); @@ -1867,9 +1856,38 @@ void AlembicProcedural::walk_hierarchy( else if (IFaceSet::matches(header)) { // ignore the face set, it will be read along with the data } + else if (IPoints::matches(header)) { + // unsupported for now + } + else if (INuPatch::matches(header)) { + // unsupported for now + } else { - // unsupported type for now (Points, NuPatch) next_object = parent.getChild(header.getName()); + + if (next_object.isInstanceRoot()) { + unordered_map<std::string, AlembicObject *>::const_iterator iter; + + /* Was this object asked to be rendered? */ + iter = object_map.find(next_object.getFullName()); + + if (iter != object_map.end()) { + AlembicObject *abc_object = iter->second; + + /* Only try to render an instance if the original object is also rendered. */ + iter = object_map.find(next_object.instanceSourcePath()); + + if (iter != object_map.end()) { + abc_object->iobject = next_object; + abc_object->instance_of = iter->second; + + if (matrix_samples_data.samples) { + abc_object->xform_samples = *matrix_samples_data.samples; + abc_object->xform_time_sampling = matrix_samples_data.time_sampling; + } + } + } + } } if (next_object.valid()) { @@ -1880,6 +1898,54 @@ void AlembicProcedural::walk_hierarchy( } } +void AlembicProcedural::build_caches(Progress &progress) +{ + for (Node *node : objects) { + AlembicObject *object = static_cast<AlembicObject *>(node); + + if (progress.get_cancel()) { + return; + } + + if (object->schema_type == AlembicObject::POLY_MESH) { + if (!object->has_data_loaded()) { + IPolyMesh polymesh(object->iobject, Alembic::Abc::kWrapExisting); + IPolyMeshSchema schema = polymesh.getSchema(); + object->load_all_data(this, schema, progress); + } + else if (object->need_shader_update) { + IPolyMesh polymesh(object->iobject, Alembic::Abc::kWrapExisting); + IPolyMeshSchema schema = polymesh.getSchema(); + object->update_shader_attributes(schema.getArbGeomParams(), progress); + } + } + else if (object->schema_type == AlembicObject::CURVES) { + if (!object->has_data_loaded() || default_radius_is_modified() || + object->radius_scale_is_modified()) { + ICurves curves(object->iobject, Alembic::Abc::kWrapExisting); + ICurvesSchema schema = curves.getSchema(); + object->load_all_data(this, schema, progress, default_radius); + } + } + else if (object->schema_type == AlembicObject::SUBD) { + if (!object->has_data_loaded()) { + ISubD subd_mesh(object->iobject, Alembic::Abc::kWrapExisting); + ISubDSchema schema = subd_mesh.getSchema(); + object->load_all_data(this, schema, progress); + } + else if (object->need_shader_update) { + ISubD subd_mesh(object->iobject, Alembic::Abc::kWrapExisting); + ISubDSchema schema = subd_mesh.getSchema(); + object->update_shader_attributes(schema.getArbGeomParams(), progress); + } + } + + if (scale_is_modified() || object->get_cached_data().transforms.size() == 0) { + object->setup_transform_cache(scale); + } + } +} + CCL_NAMESPACE_END #endif diff --git a/intern/cycles/render/alembic.h b/intern/cycles/render/alembic.h index 587843201ce..0fc02f1da23 100644 --- a/intern/cycles/render/alembic.h +++ b/intern/cycles/render/alembic.h @@ -50,6 +50,78 @@ template<typename> struct is_array : public std::false_type { template<typename T> struct is_array<array<T>> : public std::true_type { }; +/* Holds the data for a cache lookup at a given time, as well as informations to + * help disambiguate successes or failures to get data from the cache. */ +template<typename T> class CacheLookupResult { + enum class State { + NEW_DATA, + ALREADY_LOADED, + NO_DATA_FOR_TIME, + }; + + T *data; + State state; + + protected: + /* Prevent default construction outside of the class: for a valid result, we + * should use the static functions below. */ + CacheLookupResult() = default; + + public: + static CacheLookupResult new_data(T *data_) + { + CacheLookupResult result; + result.data = data_; + result.state = State::NEW_DATA; + return result; + } + + static CacheLookupResult no_data_found_for_time() + { + CacheLookupResult result; + result.data = nullptr; + result.state = State::NO_DATA_FOR_TIME; + return result; + } + + static CacheLookupResult already_loaded() + { + CacheLookupResult result; + result.data = nullptr; + result.state = State::ALREADY_LOADED; + return result; + } + + /* This should only be call if new data is available. */ + const T &get_data() const + { + assert(state == State::NEW_DATA); + assert(data != nullptr); + return *data; + } + + T *get_data_or_null() const + { + // data_ should already be null if there is no new data so no need to check + return data; + } + + bool has_new_data() const + { + return state == State::NEW_DATA; + } + + bool has_already_loaded() const + { + return state == State::ALREADY_LOADED; + } + + bool has_no_data_for_time() const + { + return state == State::NO_DATA_FOR_TIME; + } +}; + /* Store the data set for an animation at every time points, or at the beginning of the animation * for constant data. * @@ -79,10 +151,10 @@ template<typename T> class DataStore { /* Get the data for the specified time. * Return nullptr if there is no data or if the data for this time was already loaded. */ - T *data_for_time(double time) + CacheLookupResult<T> data_for_time(double time) { if (size() == 0) { - return nullptr; + return CacheLookupResult<T>::no_data_found_for_time(); } std::pair<size_t, Alembic::Abc::chrono_t> index_pair; @@ -90,26 +162,26 @@ template<typename T> class DataStore { DataTimePair &data_pair = data[index_pair.first]; if (last_loaded_time == data_pair.time) { - return nullptr; + return CacheLookupResult<T>::already_loaded(); } last_loaded_time = data_pair.time; - return &data_pair.data; + return CacheLookupResult<T>::new_data(&data_pair.data); } /* get the data for the specified time, but do not check if the data was already loaded for this * time return nullptr if there is no data */ - T *data_for_time_no_check(double time) + CacheLookupResult<T> data_for_time_no_check(double time) { if (size() == 0) { - return nullptr; + return CacheLookupResult<T>::no_data_found_for_time(); } std::pair<size_t, Alembic::Abc::chrono_t> index_pair; index_pair = time_sampling.getNearIndex(time, data.size()); DataTimePair &data_pair = data[index_pair.first]; - return &data_pair.data; + return CacheLookupResult<T>::new_data(&data_pair.data); } void add_data(T &data_, double time) @@ -149,15 +221,15 @@ template<typename T> class DataStore { * data for this time or it was already loaded, do nothing. */ void copy_to_socket(double time, Node *node, const SocketType *socket) { - T *data_ = data_for_time(time); + CacheLookupResult<T> result = data_for_time(time); - if (data_ == nullptr) { + if (!result.has_new_data()) { return; } /* TODO(kevindietrich): arrays are emptied when passed to the sockets, so for now we copy the * arrays to avoid reloading the data */ - T value = *data_; + T value = result.get_data(); node->set(*socket, value); } }; @@ -254,15 +326,12 @@ class AlembicObject : public Node { void load_all_data(AlembicProcedural *proc, Alembic::AbcGeom::IPolyMeshSchema &schema, - float scale, Progress &progress); void load_all_data(AlembicProcedural *proc, Alembic::AbcGeom::ISubDSchema &schema, - float scale, Progress &progress); void load_all_data(AlembicProcedural *proc, const Alembic::AbcGeom::ICurvesSchema &schema, - float scale, Progress &progress, float default_radius); @@ -279,6 +348,8 @@ class AlembicObject : public Node { bool need_shader_update = true; + AlembicObject *instance_of = nullptr; + Alembic::AbcCoreAbstract::TimeSamplingPtr xform_time_sampling; MatrixSampleMap xform_samples; Alembic::AbcGeom::IObject iobject; @@ -396,21 +467,17 @@ class AlembicProcedural : public Procedural { /* Read the data for an IPolyMesh at the specified frame_time. Creates corresponding Geometry and * Object Nodes in the Cycles scene if none exist yet. */ - void read_mesh(AlembicObject *abc_object, - Alembic::AbcGeom::Abc::chrono_t frame_time, - Progress &progress); + void read_mesh(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time); /* Read the data for an ICurves at the specified frame_time. Creates corresponding Geometry and * Object Nodes in the Cycles scene if none exist yet. */ - void read_curves(AlembicObject *abc_object, - Alembic::AbcGeom::Abc::chrono_t frame_time, - Progress &progress); + void read_curves(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time); /* Read the data for an ISubD at the specified frame_time. Creates corresponding Geometry and * Object Nodes in the Cycles scene if none exist yet. */ - void read_subd(AlembicObject *abc_object, - Alembic::AbcGeom::Abc::chrono_t frame_time, - Progress &progress); + void read_subd(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time); + + void build_caches(Progress &progress); }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/render/geometry.cpp index 9f8bf68dadf..de0e06828bc 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/render/geometry.cpp @@ -1367,7 +1367,7 @@ void GeometryManager::device_update_bvh(Device *device, dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0); dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions(); /* The scene handle is set in 'CPUDevice::const_copy_to' and 'OptiXDevice::const_copy_to' */ - dscene->data.bvh.scene = NULL; + dscene->data.bvh.scene = 0; } /* Set of flags used to help determining what data has been modified or needs reallocation, so we diff --git a/intern/utfconv/utfconv.h b/intern/utfconv/utfconv.h index 36dff288966..b4addd72c75 100644 --- a/intern/utfconv/utfconv.h +++ b/intern/utfconv/utfconv.h @@ -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); diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py index 5132b358f5e..cedbe542287 100644 --- a/release/scripts/startup/bl_operators/presets.py +++ b/release/scripts/startup/bl_operators/presets.py @@ -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" diff --git a/release/scripts/startup/bl_operators/spreadsheet.py b/release/scripts/startup/bl_operators/spreadsheet.py index a2f9b2ad412..fa6568f6f11 100644 --- a/release/scripts/startup/bl_operators/spreadsheet.py +++ b/release/scripts/startup/bl_operators/spreadsheet.py @@ -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): diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py index 60caa39b723..a9f040db9b5 100644 --- a/release/scripts/startup/bl_ui/properties_particle.py +++ b/release/scripts/startup/bl_ui/properties_particle.py @@ -1263,6 +1263,8 @@ class PARTICLE_PT_render(ParticleButtonsPanel, Panel): if ( part.type == 'EMITTER' or + part.type in {'FLIP', 'SPRAY', 'BUBBLE', 'FOAM', 'TRACER', + 'SPRAYFOAM', 'SPRAYBUBBLE', 'FOAMBUBBLE', 'SPRAYFOAMBUBBLE'} or (part.render_type in {'OBJECT', 'COLLECTION'} and part.type == 'HAIR') ): if part.render_type != 'NONE': diff --git a/release/scripts/startup/bl_ui/space_spreadsheet.py b/release/scripts/startup/bl_ui/space_spreadsheet.py index 1d124019ce8..1ba650fa096 100644 --- a/release/scripts/startup/bl_ui/space_spreadsheet.py +++ b/release/scripts/startup/bl_ui/space_spreadsheet.py @@ -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") diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index b214dc0c245..5fcd84d119f 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2269,6 +2269,7 @@ class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel): self._draw_items( context, ( ({"property": "use_undo_legacy"}, "T60695"), + ({"property": "override_auto_resync"}, "T83811"), ({"property": "use_cycles_debug"}, None), ), ) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 4b687945974..e393ddf16d6 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -3121,6 +3121,11 @@ class VIEW3D_MT_mask(Menu): props = layout.operator("sculpt.dirty_mask", text='Dirty Mask') + layout.separator() + + layout.menu("VIEW3D_MT_random_mask", text="Random Mask") + + class VIEW3D_MT_face_sets(Menu): bl_label = "Face Sets" @@ -3222,6 +3227,21 @@ class VIEW3D_MT_face_sets_init(Menu): op.mode = 'FACE_MAPS' +class VIEW3D_MT_random_mask(Menu): + bl_label = "Random Mask" + + def draw(self, _context): + layout = self.layout + + op = layout.operator("sculpt.mask_init", text='Per Vertex') + op.mode = 'RANDOM_PER_VERTEX' + + op = layout.operator("sculpt.mask_init", text='Per Face Set') + op.mode = 'RANDOM_PER_FACE_SET' + + op = layout.operator("sculpt.mask_init", text='Per Loose Part') + op.mode = 'RANDOM_PER_LOOSE_PART' + class VIEW3D_MT_particle(Menu): bl_label = "Particle" @@ -5013,7 +5033,7 @@ class VIEW3D_MT_edit_gpencil_stroke(Menu): layout.menu("GPENCIL_MT_move_to_layer") layout.menu("VIEW3D_MT_assign_material") layout.operator("gpencil.set_active_material", text="Set as Active Material") - layout.operator_menu_enum("gpencil.stroke_arrange", "direction", text="Arrange Strokes") + layout.operator_menu_enum("gpencil.stroke_arrange", "direction", text="Arrange") layout.separator() @@ -5024,7 +5044,7 @@ class VIEW3D_MT_edit_gpencil_stroke(Menu): layout.operator("gpencil.stroke_cyclical_set", text="Toggle Cyclic").type = 'TOGGLE' layout.operator_menu_enum("gpencil.stroke_caps_set", text="Toggle Caps", property="type") layout.operator("gpencil.stroke_flip", text="Switch Direction") - layout.prop(settings, "use_scale_thickness") + layout.prop(settings, "use_scale_thickness", text="Scale Thickness") layout.separator() layout.operator("gpencil.reset_transform_fill", text="Reset Fill Transform") @@ -5036,15 +5056,15 @@ class VIEW3D_MT_edit_gpencil_point(Menu): def draw(self, _context): layout = self.layout - layout.operator("gpencil.extrude_move", text="Extrude Points") + layout.operator("gpencil.extrude_move", text="Extrude") layout.separator() - layout.operator("gpencil.stroke_smooth", text="Smooth Points").only_selected = True + layout.operator("gpencil.stroke_smooth", text="Smooth").only_selected = True layout.separator() - layout.operator("gpencil.stroke_merge", text="Merge Points") + layout.operator("gpencil.stroke_merge", text="Merge") # TODO: add new RIP operator @@ -7048,12 +7068,12 @@ class VIEW3D_MT_gpencil_edit_context_menu(Menu): col.separator() - col.operator("gpencil.extrude_move", text="Extrude Points") + col.operator("gpencil.extrude_move", text="Extrude") col.separator() # Deform Operators - col.operator("gpencil.stroke_smooth", text="Smooth Points").only_selected = True + col.operator("gpencil.stroke_smooth", text="Smooth").only_selected = True col.operator("transform.bend", text="Bend") col.operator("transform.shear", text="Shear") col.operator("transform.tosphere", text="To Sphere") @@ -7061,8 +7081,8 @@ class VIEW3D_MT_gpencil_edit_context_menu(Menu): col.separator() - col.menu("VIEW3D_MT_mirror", text="Mirror Points") - col.menu("GPENCIL_MT_snap", text="Snap Points") + col.menu("VIEW3D_MT_mirror", text="Mirror") + col.menu("GPENCIL_MT_snap", text="Snap") col.separator() @@ -7075,15 +7095,15 @@ class VIEW3D_MT_gpencil_edit_context_menu(Menu): col.separator() # Removal Operators - col.operator("gpencil.stroke_merge", text="Merge Points") + col.operator("gpencil.stroke_merge", text="Merge") col.operator("gpencil.stroke_merge_by_distance").use_unselected = False col.operator("gpencil.stroke_split", text="Split") col.operator("gpencil.stroke_separate", text="Separate").mode = 'POINT' col.separator() - col.operator("gpencil.delete", text="Delete Points").type = 'POINTS' - col.operator("gpencil.dissolve", text="Dissolve Points").type = 'POINTS' + col.operator("gpencil.delete", text="Delete").type = 'POINTS' + col.operator("gpencil.dissolve", text="Dissolve").type = 'POINTS' col.operator("gpencil.dissolve", text="Dissolve Between").type = 'BETWEEN' col.operator("gpencil.dissolve", text="Dissolve Unselected").type = 'UNSELECT' @@ -7100,7 +7120,7 @@ class VIEW3D_MT_gpencil_edit_context_menu(Menu): col.separator() - col.operator("gpencil.stroke_smooth", text="Smooth Stroke").only_selected = False + col.operator("gpencil.stroke_smooth", text="Smooth").only_selected = False col.operator("transform.transform", text="Shrink/Fatten").mode = 'GPENCIL_SHRINKFATTEN' col.separator() @@ -7109,12 +7129,12 @@ class VIEW3D_MT_gpencil_edit_context_menu(Menu): col.menu("GPENCIL_MT_move_to_layer") col.menu("VIEW3D_MT_assign_material") col.operator("gpencil.set_active_material", text="Set as Active Material") - col.operator_menu_enum("gpencil.stroke_arrange", "direction", text="Arrange Strokes") + col.operator_menu_enum("gpencil.stroke_arrange", "direction", text="Arrange") col.separator() - col.menu("VIEW3D_MT_mirror", text="Mirror Stroke") - col.menu("VIEW3D_MT_snap", text="Snap Stroke") + col.menu("VIEW3D_MT_mirror", text="Mirror") + col.menu("VIEW3D_MT_snap", text="Snap") col.separator() @@ -7134,11 +7154,11 @@ class VIEW3D_MT_gpencil_edit_context_menu(Menu): col.separator() - col.operator("gpencil.delete", text="Delete Strokes").type = 'STROKES' + col.operator("gpencil.delete", text="Delete").type = 'STROKES' col.separator() - col.operator("gpencil.reproject", text="Reproject Strokes") + col.operator("gpencil.reproject", text="Reproject") def draw_gpencil_layer_active(context, layout): @@ -7563,6 +7583,7 @@ classes = ( VIEW3D_MT_mask, VIEW3D_MT_face_sets, VIEW3D_MT_face_sets_init, + VIEW3D_MT_random_mask, VIEW3D_MT_particle, VIEW3D_MT_particle_context_menu, VIEW3D_MT_particle_showhide, diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index a2122fb01b7..cac5a8f835c 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -493,6 +493,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeAttributeSampleTexture"), NodeItem("GeometryNodeAttributeCombineXYZ"), NodeItem("GeometryNodeAttributeSeparateXYZ"), + NodeItem("GeometryNodeAttributeRemove"), ]), GeometryNodeCategory("GEO_COLOR", "Color", items=[ NodeItem("ShaderNodeValToRGB"), diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h index 8666291cec8..d43332ae1ac 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -266,7 +266,7 @@ void BKE_animsys_evaluate_all_animation(struct Main *main, void animsys_evaluate_action(struct PointerRNA *ptr, struct bAction *act, const struct AnimationEvalContext *anim_eval_context, - const bool flush_to_original); + bool flush_to_original); /* Evaluate Action Group */ void animsys_evaluate_action_group(struct PointerRNA *ptr, diff --git a/source/blender/blenkernel/BKE_blendfile.h b/source/blender/blenkernel/BKE_blendfile.h index f73c4a70809..429e294a337 100644 --- a/source/blender/blenkernel/BKE_blendfile.h +++ b/source/blender/blenkernel/BKE_blendfile.h @@ -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); diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 5fd451dc986..ccb313f0e2a 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -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); @@ -77,7 +79,12 @@ bool BKE_lib_override_library_proxy_convert(struct Main *bmain, bool BKE_lib_override_library_resync(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, - struct ID *id_root); + struct ID *id_root, + const bool do_hierarchy_enforce); +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( diff --git a/source/blender/blenkernel/BKE_lib_query.h b/source/blender/blenkernel/BKE_lib_query.h index f064d03261d..4e781aea9d3 100644 --- a/source/blender/blenkernel/BKE_lib_query.h +++ b/source/blender/blenkernel/BKE_lib_query.h @@ -71,6 +71,13 @@ enum { IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE = (1 << 5), /** + * Indicates that this is an internal runtime ID pointer, like e.g. `ID.newid` or `ID.original`. + * \note Those should be ignored in most cases, and won't be processed/generated anyway unless + * `IDWALK_DO_INTERNAL_RUNTIME_POINTERS` option is enabled. + */ + IDWALK_CB_INTERNAL = (1 << 6), + + /** * This ID usage is fully refcounted. * Callback is responsible to deal accordingly with #ID.us if needed. */ @@ -126,6 +133,9 @@ enum { IDWALK_IGNORE_EMBEDDED_ID = (1 << 3), IDWALK_NO_INDIRECT_PROXY_DATA_USAGE = (1 << 8), /* Ugly special case :(((( */ + /** Also process internal ID pointers like `ID.newid` or `ID.orig_id`. + * WARNING: Dangerous, use with caution. */ + IDWALK_DO_INTERNAL_RUNTIME_POINTERS = (1 << 9), }; typedef struct LibraryForeachIDData LibraryForeachIDData; diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index a8d75213d39..705d2b030e5 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -76,6 +76,15 @@ 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, + /** + * Force internal ID runtime pointers (like `ID.newid`, `ID.orig_id` etc.) to also be processed. + * This should only be needed in some very specific cases, typically only BKE ID management code + * should need it (e.g. required from `id_delete` to ensure no runtime pointer remains using + * freed ones). + */ + ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS = 1 << 7, }; /* Note: Requiring new_id to be non-null, this *may* not be the case ultimately, diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index ba55b2bc02a..7da4c8c1f4e 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1372,12 +1372,13 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_ATTRIBUTE_COMBINE_XYZ 1027 #define GEO_NODE_ATTRIBUTE_SEPARATE_XYZ 1028 #define GEO_NODE_SUBDIVIDE 1029 -#define GEO_NODE_MESH_PRIMITIVE_CUBE 1030 -#define GEO_NODE_MESH_PRIMITIVE_CIRCLE 1031 -#define GEO_NODE_MESH_PRIMITIVE_UV_SPHERE 1032 -#define GEO_NODE_MESH_PRIMITIVE_CYLINDER 1033 -#define GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE 1034 -#define GEO_NODE_MESH_PRIMITIVE_CONE 1035 +#define GEO_NODE_ATTRIBUTE_REMOVE 1030 +#define GEO_NODE_MESH_PRIMITIVE_CUBE 1031 +#define GEO_NODE_MESH_PRIMITIVE_CIRCLE 1032 +#define GEO_NODE_MESH_PRIMITIVE_UV_SPHERE 1033 +#define GEO_NODE_MESH_PRIMITIVE_CYLINDER 1034 +#define GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE 1035 +#define GEO_NODE_MESH_PRIMITIVE_CONE 1036 /** \} */ diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c index d826aaf24e3..9f8a30722c2 100644 --- a/source/blender/blenkernel/intern/blender_undo.c +++ b/source/blender/blenkernel/intern/blender_undo.c @@ -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. */ diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 101f4b7caf6..967eb81026c 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -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 && !USER_EXPERIMENTAL_TEST(&U, no_override_auto_resync)) { + 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); -} - -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); + return bfd; } -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); -} - -bool BKE_blendfile_read_from_memory(bContext *C, - const void *filebuf, - int filelength, - const struct BlendFileReadParams *params, - ReportList *reports) -{ - return BKE_blendfile_read_from_memory_ex(C, filebuf, filelength, params, reports, false, NULL); + return bfd; } -/* memfile is the undo buffer */ -bool BKE_blendfile_read_from_memfile(bContext *C, - struct MemFile *memfile, - 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) { - 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; } /** diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c index 1d7f89e1e8d..67b2e4429d6 100644 --- a/source/blender/blenkernel/intern/lib_id_delete.c +++ b/source/blender/blenkernel/intern/lib_id_delete.c @@ -300,11 +300,15 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) * links, this can lead to nasty crashing here in second, actual deleting loop. * Also, this will also flag users of deleted data that cannot be unlinked * (object using deleted obdata, etc.), so that they also get deleted. */ - BKE_libblock_remap_locked( - bmain, id, NULL, ID_REMAP_FLAG_NEVER_NULL_USAGE | ID_REMAP_FORCE_NEVER_NULL_USAGE); + BKE_libblock_remap_locked(bmain, + id, + NULL, + (ID_REMAP_FLAG_NEVER_NULL_USAGE | + ID_REMAP_FORCE_NEVER_NULL_USAGE | + ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS)); /* Since we removed ID from Main, * we also need to unlink its own other IDs usages ourself. */ - BKE_libblock_relink_ex(bmain, id, NULL, NULL, 0); + BKE_libblock_relink_ex(bmain, id, NULL, NULL, ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS); } } @@ -337,8 +341,12 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion) * actual deleting loop. * Also, this will also flag users of deleted data that cannot be unlinked * (object using deleted obdata, etc.), so that they also get deleted. */ - BKE_libblock_remap_locked( - bmain, id, NULL, ID_REMAP_FLAG_NEVER_NULL_USAGE | ID_REMAP_FORCE_NEVER_NULL_USAGE); + BKE_libblock_remap_locked(bmain, + id, + NULL, + (ID_REMAP_FLAG_NEVER_NULL_USAGE | + ID_REMAP_FORCE_NEVER_NULL_USAGE | + ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS)); } } } diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 702d718f2b9..7a6e7a35724 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -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) { @@ -624,53 +650,59 @@ static void lib_override_library_create_post_process( { BKE_main_collection_sync(bmain); - switch (GS(id_root->name)) { - case ID_GR: { - Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ? - (Object *)id_reference : - NULL; - Collection *collection_new = ((Collection *)id_root->newid); - if (ob_reference != NULL) { - BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new); - } - else if (id_reference != NULL) { - BKE_collection_add_from_collection( - bmain, scene, ((Collection *)id_reference), collection_new); - } - else { - BKE_collection_add_from_collection(bmain, scene, ((Collection *)id_root), collection_new); - } + if (id_root->newid != NULL) { + switch (GS(id_root->name)) { + case ID_GR: { + Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ? + (Object *)id_reference : + NULL; + Collection *collection_new = ((Collection *)id_root->newid); + if (ob_reference != NULL) { + BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new); + } + else if (id_reference != NULL) { + BLI_assert(GS(id_reference->name) == ID_GR); + BKE_collection_add_from_collection( + bmain, scene, ((Collection *)id_reference), collection_new); + } + else { + BKE_collection_add_from_collection( + bmain, scene, ((Collection *)id_root), collection_new); + } - FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection_new, ob_new) { - if (ob_new != NULL && ob_new->id.override_library != NULL) { - if (ob_reference != NULL) { - Base *base; - if ((base = BKE_view_layer_base_find(view_layer, ob_new)) == NULL) { - BKE_collection_object_add_from(bmain, scene, ob_reference, ob_new); - base = BKE_view_layer_base_find(view_layer, ob_new); - DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); - } + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection_new, ob_new) { + if (ob_new != NULL && ob_new->id.override_library != NULL) { + if (ob_reference != NULL) { + Base *base; + if ((base = BKE_view_layer_base_find(view_layer, ob_new)) == NULL) { + BKE_collection_object_add_from(bmain, scene, ob_reference, ob_new); + base = BKE_view_layer_base_find(view_layer, ob_new); + DEG_id_tag_update_ex( + bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); + } - if (ob_new == (Object *)ob_reference->id.newid) { - /* TODO: is setting active needed? */ - BKE_view_layer_base_select_and_set_active(view_layer, base); + if (ob_new == (Object *)ob_reference->id.newid) { + /* TODO: is setting active needed? */ + BKE_view_layer_base_select_and_set_active(view_layer, base); + } + } + else if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) { + BKE_collection_object_add(bmain, collection_new, ob_new); + DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); } - } - else if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) { - BKE_collection_object_add(bmain, collection_new, ob_new); - DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); } } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + break; } - FOREACH_COLLECTION_OBJECT_RECURSIVE_END; - break; - } - case ID_OB: { - BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ((Object *)id_root->newid)); - break; + case ID_OB: { + BKE_collection_object_add_from( + bmain, scene, (Object *)id_root, ((Object *)id_root->newid)); + break; + } + default: + break; } - default: - break; } /* We need to ensure all new overrides of objects are properly instantiated. */ @@ -803,7 +835,8 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain, * \param id_root: The root liboverride ID to resync from. * \return true if override was successfully resynced. */ -bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root) +bool BKE_lib_override_library_resync( + Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, const bool do_hierarchy_enforce) { BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); @@ -883,6 +916,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]; @@ -950,8 +985,14 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_ } } - RNA_struct_override_apply( - bmain, &rnaptr_dst, &rnaptr_src, NULL, id_override_new->override_library); + RNA_struct_override_apply(bmain, + &rnaptr_dst, + &rnaptr_src, + NULL, + id_override_new->override_library, + do_hierarchy_enforce ? + RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS : + RNA_OVERRIDE_APPLY_FLAG_NOP); } } } @@ -975,11 +1016,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; @@ -1007,6 +1058,119 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_ } /** + * 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, false); + 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 +2046,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) { @@ -1945,16 +2117,20 @@ void BKE_lib_override_library_update(Main *bmain, ID *local) RNA_id_pointer_create(local->override_library->storage, rnaptr_storage); } - RNA_struct_override_apply( - bmain, &rnaptr_dst, &rnaptr_src, rnaptr_storage, local->override_library); + RNA_struct_override_apply(bmain, + &rnaptr_dst, + &rnaptr_src, + rnaptr_storage, + local->override_library, + RNA_OVERRIDE_APPLY_FLAG_NOP); /* 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. */ diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index acd0c10040c..e33743eb36b 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -183,8 +183,9 @@ static void library_foreach_ID_link(Main *bmain, BLI_assert(inherit_data == NULL || data.bmain == inherit_data->bmain); if (flag & IDWALK_RECURSE) { - /* For now, recursion implies read-only. */ + /* For now, recursion implies read-only, and no internal pointers. */ flag |= IDWALK_READONLY; + flag &= ~IDWALK_DO_INTERNAL_RUNTIME_POINTERS; data.ids_handled = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); BLI_LINKSTACK_INIT(data.ids_todo); @@ -230,6 +231,7 @@ static void library_foreach_ID_link(Main *bmain, } if (bmain != NULL && bmain->relations != NULL && (flag & IDWALK_READONLY) && + (flag & IDWALK_DO_INTERNAL_RUNTIME_POINTERS) == 0 && (((bmain->relations->flag & MAINIDRELATIONS_INCLUDE_UI) == 0) == ((data.flag & IDWALK_INCLUDE_UI) == 0))) { /* Note that this is minor optimization, even in worst cases (like id being an object with @@ -250,6 +252,11 @@ static void library_foreach_ID_link(Main *bmain, /* Note: ID.lib pointer is purposefully fully ignored here... * We may want to add it at some point? */ + if (flag & IDWALK_DO_INTERNAL_RUNTIME_POINTERS) { + CALLBACK_INVOKE_ID(id->newid, IDWALK_CB_INTERNAL); + CALLBACK_INVOKE_ID(id->orig_id, IDWALK_CB_INTERNAL); + } + if (id->override_library != NULL) { CALLBACK_INVOKE_ID(id->override_library->reference, IDWALK_CB_USER | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE); diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 56f7bb0be6f..1f597bbb9a6 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -373,9 +373,12 @@ static void libblock_remap_data( Main *bmain, ID *id, ID *old_id, ID *new_id, const short remap_flags, IDRemap *r_id_remap_data) { IDRemap id_remap_data; - const int foreach_id_flags = (remap_flags & ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE) != 0 ? - IDWALK_NO_INDIRECT_PROXY_DATA_USAGE : - IDWALK_NOP; + const int foreach_id_flags = ((remap_flags & ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE) != 0 ? + IDWALK_NO_INDIRECT_PROXY_DATA_USAGE : + IDWALK_NOP) | + ((remap_flags & ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS) != 0 ? + IDWALK_DO_INTERNAL_RUNTIME_POINTERS : + IDWALK_NOP); if (r_id_remap_data == NULL) { r_id_remap_data = &id_remap_data; @@ -422,15 +425,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 +484,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) { diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 2de5543b276..17bdc08c995 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -4799,6 +4799,7 @@ static void registerGeometryNodes() register_node_type_geo_attribute_randomize(); register_node_type_geo_attribute_separate_xyz(); register_node_type_geo_attribute_vector_math(); + register_node_type_geo_attribute_remove(); register_node_type_geo_boolean(); register_node_type_geo_collection_info(); register_node_type_geo_edge_split(); diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 8a98780d918..77dde3a921a 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -137,8 +137,7 @@ static void update_node_vb(PBVH *pbvh, PBVHNode *node) if (node->flag & PBVH_Leaf) { PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(pbvh, node, vd, PBVH_ITER_ALL) - { + BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_ALL) { BB_expand(&vb, vd.co); } BKE_pbvh_vertex_iter_end; @@ -1143,8 +1142,7 @@ static void pbvh_update_mask_redraw_task_cb(void *__restrict userdata, if (node->flag & PBVH_Leaf) { PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(pbvh, node, vd, PBVH_ITER_ALL) - { + BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_ALL) { if (vd.mask && *vd.mask < 1.0f) { has_unmasked = true; } @@ -1191,8 +1189,7 @@ static void pbvh_update_visibility_redraw_task_cb(void *__restrict userdata, BKE_pbvh_node_fully_hidden_set(node, true); if (node->flag & PBVH_Leaf) { PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(pbvh, node, vd, PBVH_ITER_ALL) - { + BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_ALL) { if (vd.visible) { BKE_pbvh_node_fully_hidden_set(node, false); return; diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index de7353d827a..347ee1378a4 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -555,6 +555,23 @@ static void read_file_version(FileData *fd, Main *main) } } +static bool blo_bhead_is_id(const BHead *bhead) +{ + /* BHead codes are four bytes (like 'ENDB', 'TEST', etc.), but if the two most-significant bytes + * are zero, the values actually indicate an ID type. */ + return bhead->code <= 0xFFFF; +} + +static bool blo_bhead_is_id_valid_type(const BHead *bhead) +{ + if (!blo_bhead_is_id(bhead)) { + return false; + } + + const short id_type_code = bhead->code & 0xFFFF; + return BKE_idtype_idcode_is_valid(id_type_code); +} + #ifdef USE_GHASH_BHEAD static void read_file_bhead_idname_map_create(FileData *fd) { @@ -568,8 +585,9 @@ static void read_file_bhead_idname_map_create(FileData *fd) for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { if (code_prev != bhead->code) { code_prev = bhead->code; - is_link = BKE_idtype_idcode_is_valid(code_prev) ? BKE_idtype_idcode_is_linkable(code_prev) : - false; + is_link = blo_bhead_is_id_valid_type(bhead) ? + BKE_idtype_idcode_is_linkable((short)code_prev) : + false; } if (is_link) { @@ -584,8 +602,9 @@ static void read_file_bhead_idname_map_create(FileData *fd) for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { if (code_prev != bhead->code) { code_prev = bhead->code; - is_link = BKE_idtype_idcode_is_valid(code_prev) ? BKE_idtype_idcode_is_linkable(code_prev) : - false; + is_link = blo_bhead_is_id_valid_type(bhead) ? + BKE_idtype_idcode_is_linkable((short)code_prev) : + false; } if (is_link) { @@ -973,7 +992,7 @@ const char *blo_bhead_id_name(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) { - BLI_assert(BKE_idtype_idcode_is_valid(bhead->code)); + BLI_assert(blo_bhead_is_id_valid_type(bhead)); return (fd->id_asset_data_offset >= 0) ? *(AssetMetaData **)POINTER_OFFSET(bhead, sizeof(*bhead) + fd->id_asset_data_offset) : NULL; @@ -2985,6 +3004,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); + } } } } @@ -3705,7 +3730,7 @@ static BHead *read_libblock(FileData *fd, BHead *blo_read_asset_data_block(FileData *fd, BHead *bhead, AssetMetaData **r_asset_data) { - BLI_assert(BKE_idtype_idcode_is_valid(bhead->code)); + BLI_assert(blo_bhead_is_id_valid_type(bhead)); bhead = read_data_into_datamap(fd, bhead, "asset-data read"); @@ -4917,7 +4942,7 @@ int BLO_library_link_copypaste(Main *mainl, BlendHandle *bh, const uint64_t id_t break; } - if (BKE_idtype_idcode_is_valid(bhead->code) && BKE_idtype_idcode_is_linkable(bhead->code) && + if (blo_bhead_is_id_valid_type(bhead) && BKE_idtype_idcode_is_linkable((short)bhead->code) && (id_types_mask == 0 || (BKE_idtype_idcode_to_idfilter((short)bhead->code) & id_types_mask) != 0)) { read_libblock(fd, mainl, bhead, LIB_TAG_NEED_EXPAND | LIB_TAG_INDIRECT, false, &id); diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index b49d575cade..bee3026816e 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -56,9 +56,7 @@ std::string DebugInfo::node_name(const Node *node) if (it != m_node_names.end()) { return it->second; } - else { - return ""; - } + return ""; } std::string DebugInfo::operation_name(const NodeOperation *op) @@ -67,9 +65,7 @@ std::string DebugInfo::operation_name(const NodeOperation *op) if (it != m_op_names.end()) { return it->second; } - else { - return ""; - } + return ""; } void DebugInfo::convert_started() @@ -81,10 +77,8 @@ void DebugInfo::execute_started(const ExecutionSystem *system) { m_file_index = 1; m_group_states.clear(); - for (ExecutionSystem::Groups::const_iterator it = system->m_groups.begin(); - it != system->m_groups.end(); - ++it) { - m_group_states[*it] = EG_WAIT; + for (ExecutionGroup *execution_group : system->m_groups) { + m_group_states[execution_group] = EG_WAIT; } } @@ -355,10 +349,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "fillcolor=chartreuse4\r\n"); } - for (ExecutionGroup::Operations::const_iterator it = group->m_operations.begin(); - it != group->m_operations.end(); - ++it) { - NodeOperation *operation = *it; + for (NodeOperation *operation : group->m_operations) { sprintf(strbuf, "_%p", group); op_groups[operation].push_back(std::string(strbuf)); @@ -385,7 +376,8 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma op_groups[operation].push_back(std::string("")); - len += graphviz_operation(system, operation, 0, str + len, maxlen > len ? maxlen - len : 0); + len += graphviz_operation( + system, operation, nullptr, str + len, maxlen > len ? maxlen - len : 0); } for (int i = 0; i < totops; i++) { @@ -452,7 +444,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma for (int l = 0; l < to_groups.size(); l++) { len += snprintf(str + len, maxlen > len ? maxlen - len : 0, - "\"O_%p%s\":\"OUT_%p\":e -> \"O_%p%s\":\"IN_%p\":w", + R"("O_%p%s":"OUT_%p":e -> "O_%p%s":"IN_%p":w)", from_op, from_groups[k].c_str(), from, diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc index a70b6ba4abe..f19c54991cd 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.cc +++ b/source/blender/compositor/intern/COM_WorkScheduler.cc @@ -190,10 +190,10 @@ bool WorkScheduler::has_gpu_devices() # ifdef COM_OPENCL_ENABLED return !g_work_scheduler.gpu_devices.empty(); # else - return 0; + return false; # endif #else - return 0; + return false; #endif } diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c index d1d7c67d16c..f4f7acb8862 100644 --- a/source/blender/draw/engines/eevee/eevee_effects.c +++ b/source/blender/draw/engines/eevee/eevee_effects.c @@ -149,8 +149,6 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata, */ common_data->hiz_uv_scale[0] = viewport_size[0] / effects->hiz_size[0]; common_data->hiz_uv_scale[1] = viewport_size[1] / effects->hiz_size[1]; - common_data->hiz_uv_scale[2] = 1.0f / effects->hiz_size[0]; - common_data->hiz_uv_scale[3] = 1.0f / effects->hiz_size[1]; /* Compute pixel size. Size is multiplied by 2 because it is applied in NDC [-1..1] range. */ sldata->common_data.ssr_pixelsize[0] = 2.0f / size_fs[0]; diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c index 0ce94b8f1b1..3e1d4a8aaa6 100644 --- a/source/blender/draw/engines/eevee/eevee_lightprobes.c +++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c @@ -245,7 +245,7 @@ void EEVEE_lightbake_cache_init(EEVEE_ViewLayerData *sldata, DRW_shgroup_uniform_float(grp, "intensityFac", &pinfo->intensity_fac, 1); DRW_shgroup_uniform_float(grp, "sampleCount", &pinfo->samples_len, 1); DRW_shgroup_uniform_float(grp, "invSampleCount", &pinfo->samples_len_inv, 1); - DRW_shgroup_uniform_float(grp, "roughnessSquared", &pinfo->roughness, 1); + DRW_shgroup_uniform_float(grp, "roughness", &pinfo->roughness, 1); DRW_shgroup_uniform_float(grp, "lodFactor", &pinfo->lodfactor, 1); DRW_shgroup_uniform_float(grp, "lodMax", &pinfo->lod_rt_max, 1); DRW_shgroup_uniform_float(grp, "texelSize", &pinfo->texel_size, 1); @@ -1045,8 +1045,8 @@ void EEVEE_lightbake_filter_glossy(EEVEE_ViewLayerData *sldata, /* Disney Roughness */ pinfo->roughness = square_f(pinfo->roughness); /* Distribute Roughness across lod more evenly */ - pinfo->roughness = square_f(square_f(pinfo->roughness)); - CLAMP(pinfo->roughness, 1e-8f, 0.99999f); /* Avoid artifacts */ + pinfo->roughness = square_f(pinfo->roughness); + CLAMP(pinfo->roughness, 1e-4f, 0.9999f); /* Avoid artifacts */ #if 1 /* Variable Sample count and bias (fast) */ switch (i) { diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index bc7db4b5df6..042fa621117 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -128,7 +128,8 @@ void EEVEE_material_bind_resources(DRWShadingGroup *shgrp, DRW_shgroup_uniform_float_copy( shgrp, "refractionDepth", (refract_depth) ? *refract_depth : 0.0); if (use_ssrefraction) { - DRW_shgroup_uniform_texture_ref(shgrp, "colorBuffer", &vedata->txl->filtered_radiance); + DRW_shgroup_uniform_texture_ref( + shgrp, "refractColorBuffer", &vedata->txl->filtered_radiance); } } if (use_alpha_blend) { diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 45afee31591..1ffd4a071f1 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -737,7 +737,7 @@ typedef struct EEVEE_EffectsInfo { struct GPUTexture *ssr_normal_input; /* Textures from pool */ struct GPUTexture *ssr_specrough_input; struct GPUTexture *ssr_hit_output; - struct GPUTexture *ssr_pdf_output; + struct GPUTexture *ssr_hit_depth; /* Temporal Anti Aliasing */ int taa_reproject_sample; int taa_current_sample; @@ -854,8 +854,8 @@ typedef struct EEVEE_EffectsInfo { * - Arrays of vec2/vec3 are padded as arrays of vec4. * - sizeof(bool) == sizeof(int) in GLSL so use int in C */ typedef struct EEVEE_CommonUniformBuffer { - float prev_persmat[4][4]; /* mat4 */ - float hiz_uv_scale[4]; /* vec4 */ + float prev_persmat[4][4]; /* mat4 */ + float hiz_uv_scale[2], ssr_uv_scale[2]; /* vec4 */ /* Ambient Occlusion */ /* -- 16 byte aligned -- */ float ao_dist, pad1, ao_factor, pad2; /* vec4 */ diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c index 0e16037f42d..5739024993e 100644 --- a/source/blender/draw/engines/eevee/eevee_renderpasses.c +++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c @@ -490,7 +490,7 @@ void EEVEE_renderpasses_draw_debug(EEVEE_Data *vedata) tx = txl->maxzbuffer; break; case 2: - tx = effects->ssr_pdf_output; + /* UNUSED */ break; case 3: tx = effects->ssr_normal_input; diff --git a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c index 80a1c9fcbe5..f5b4e4d43a5 100644 --- a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c +++ b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c @@ -77,6 +77,7 @@ int EEVEE_screen_raytrace_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) common_data->ssr_firefly_fac = FLT_MAX; } + void *owner = (void *)EEVEE_screen_raytrace_init; const int divisor = (effects->reflection_trace_full) ? 1 : 2; int tracing_res[2] = {(int)viewport_size[0] / divisor, (int)viewport_size[1] / divisor}; const int size_fs[2] = {(int)viewport_size[0], (int)viewport_size[1]}; @@ -86,22 +87,24 @@ int EEVEE_screen_raytrace_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) tracing_res[0] = max_ii(1, tracing_res[0]); tracing_res[1] = max_ii(1, tracing_res[1]); + common_data->ssr_uv_scale[0] = size_fs[0] / ((float)tracing_res[0] * divisor); + common_data->ssr_uv_scale[1] = size_fs[1] / ((float)tracing_res[1] * divisor); + /* MRT for the shading pass in order to output needed data for the SSR pass. */ - effects->ssr_specrough_input = DRW_texture_pool_query_2d( - size_fs[0], size_fs[1], format, &draw_engine_eevee_type); + effects->ssr_specrough_input = DRW_texture_pool_query_2d(UNPACK2(size_fs), format, owner); GPU_framebuffer_texture_attach(fbl->main_fb, effects->ssr_specrough_input, 2, 0); /* Ray-tracing output. */ - effects->ssr_hit_output = DRW_texture_pool_query_2d( - tracing_res[0], tracing_res[1], GPU_RG16I, &draw_engine_eevee_type); - effects->ssr_pdf_output = DRW_texture_pool_query_2d( - tracing_res[0], tracing_res[1], GPU_R16F, &draw_engine_eevee_type); + effects->ssr_hit_output = DRW_texture_pool_query_2d(UNPACK2(tracing_res), GPU_RGBA16F, owner); + effects->ssr_hit_depth = DRW_texture_pool_query_2d(UNPACK2(tracing_res), GPU_R16F, owner); GPU_framebuffer_ensure_config(&fbl->screen_tracing_fb, - {GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(effects->ssr_hit_output), - GPU_ATTACHMENT_TEXTURE(effects->ssr_pdf_output)}); + { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(effects->ssr_hit_output), + GPU_ATTACHMENT_TEXTURE(effects->ssr_hit_depth), + }); return EFFECT_SSR | EFFECT_NORMAL_BUFFER | EFFECT_RADIANCE_BUFFER | EFFECT_DOUBLE_BUFFER | ((use_refraction) ? EFFECT_REFRACT : 0); @@ -111,7 +114,6 @@ int EEVEE_screen_raytrace_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) GPU_FRAMEBUFFER_FREE_SAFE(fbl->screen_tracing_fb); effects->ssr_specrough_input = NULL; effects->ssr_hit_output = NULL; - effects->ssr_pdf_output = NULL; return 0; } @@ -137,18 +139,17 @@ 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); DRWShadingGroup *grp = DRW_shgroup_create(trace_shader, psl->ssr_raytrace); - DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src); DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input); DRW_shgroup_uniform_texture_ref(grp, "specroughBuffer", &effects->ssr_specrough_input); DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer); @@ -164,17 +165,18 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v } DRW_shgroup_call(grp, quad, NULL); + eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT; + DRW_PASS_CREATE(psl->ssr_resolve, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD); grp = DRW_shgroup_create(resolve_shader, psl->ssr_resolve); - DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src); DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input); DRW_shgroup_uniform_texture_ref(grp, "specroughBuffer", &effects->ssr_specrough_input); DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &lcache->cube_tx.tex); DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &vedata->txl->planar_pool); DRW_shgroup_uniform_texture_ref(grp, "planarDepth", &vedata->txl->planar_depth); - DRW_shgroup_uniform_texture_ref(grp, "hitBuffer", &effects->ssr_hit_output); - DRW_shgroup_uniform_texture_ref(grp, "pdfBuffer", &effects->ssr_pdf_output); - DRW_shgroup_uniform_texture_ref(grp, "prevColorBuffer", &txl->filtered_radiance); + DRW_shgroup_uniform_texture_ref_ex(grp, "hitBuffer", &effects->ssr_hit_output, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "hitDepth", &effects->ssr_hit_depth, no_filter); + DRW_shgroup_uniform_texture_ref(grp, "colorBuffer", &txl->filtered_radiance); DRW_shgroup_uniform_texture_ref(grp, "maxzBuffer", &txl->maxzbuffer); DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool); DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool); diff --git a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl index a0bfd440dd9..4398247472d 100644 --- a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl @@ -50,14 +50,6 @@ OcclusionData unpack_occlusion_data(vec4 v) return OcclusionData((1.0 - v) * vec4(1, -1, 1, -1) * M_PI, 0.0); } -/* Returns maximum screen distance an AO ray can travel for a given view depth, in NDC space. */ -vec2 get_ao_area(float view_depth, float radius) -{ - float homcco = ProjectionMatrix[2][3] * view_depth + ProjectionMatrix[3][3]; - float max_dist = radius / homcco; - return vec2(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) * max_dist; -} - vec2 get_ao_noise(void) { vec2 noise = texelfetch_noise_tex(gl_FragCoord.xy).xy; @@ -108,7 +100,7 @@ float search_horizon(vec3 vI, float depth = textureLod(depth_tx, uv * hizUvScale.xy, floor(lod)).r; if (depth == 1.0 && inverted == 0.0) { - /* Skip background. This avoids issues with the thickness heuristic. */ + /* Skip background. Avoids making shadow on the geometry near the far plane. */ continue; } @@ -146,7 +138,6 @@ OcclusionData occlusion_search( } vec2 noise = get_ao_noise(); - vec2 area = get_ao_area(vP.z, radius); vec2 dir = get_ao_dir(noise.x); vec2 uv = get_uvs_from_view(vP); vec3 vI = ((ProjectionMatrix[3][3] == 0.0) ? normalize(-vP) : vec3(0.0, 0.0, 1.0)); diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl index 5f0fde134d1..c8eaa06094e 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -102,7 +102,7 @@ float D_ggx_opti(float NH, float a2) return M_PI * tmp * tmp; /* Doing RCP and mul a2 at the end */ } -float G1_Smith_GGX(float NX, float a2) +float G1_Smith_GGX_opti(float NX, float a2) { /* Using Brian Karis approach and refactoring by NX/NX * this way the (2*NL)*(2*NV) in G = G1(V) * G1(L) gets canceled by the brdf denominator 4*NL*NV @@ -122,7 +122,7 @@ float bsdf_ggx(vec3 N, vec3 L, vec3 V, float roughness) float NL = max(dot(N, L), 1e-8); float NV = max(dot(N, V), 1e-8); - float G = G1_Smith_GGX(NV, a2) * G1_Smith_GGX(NL, a2); /* Doing RCP at the end */ + float G = G1_Smith_GGX_opti(NV, a2) * G1_Smith_GGX_opti(NL, a2); /* Doing RCP at the end */ float D = D_ggx_opti(NH, a2); /* Denominator is canceled by G1_Smith */ diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl index 46ea8b747c8..4c1544654c1 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl @@ -25,7 +25,8 @@ void main() vec3 Xi = (vec3(i, j, 0.0) + 0.5) / sampleCount; Xi.yz = vec2(cos(Xi.y * M_2PI), sin(Xi.y * M_2PI)); - vec3 H = sample_ggx(Xi, a2); /* Microfacet normal */ + /* Microfacet normal */ + vec3 H = sample_ggx(Xi, a, V); vec3 L = -reflect(V, H); float NL = L.z; @@ -33,9 +34,10 @@ void main() float NH = max(H.z, 0.0); float VH = max(dot(V, H), 0.0); - float G1_v = G1_Smith_GGX(NV, a2); - float G1_l = G1_Smith_GGX(NL, a2); - float G_smith = 4.0 * NV * NL / (G1_v * G1_l); /* See G1_Smith_GGX for explanations. */ + float G1_v = G1_Smith_GGX_opti(NV, a2); + float G1_l = G1_Smith_GGX_opti(NL, a2); + /* See G1_Smith_GGX_opti for explanations. */ + float G_smith = 4.0 * NV * NL / (G1_v * G1_l); float brdf = (G_smith * VH) / (NH * NV); diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl index bbc79a2d05b..004d884dc75 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl @@ -1,4 +1,5 @@ +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) #pragma BLENDER_REQUIRE(bsdf_common_lib.glsl) uniform sampler1D texHammersley; @@ -8,6 +9,11 @@ vec3 tangent_to_world(vec3 vector, vec3 N, vec3 T, vec3 B) return T * vector.x + B * vector.y + N * vector.z; } +vec3 world_to_tangent(vec3 vector, vec3 N, vec3 T, vec3 B) +{ + return vec3(dot(T, vector), dot(B, vector), dot(N, vector)); +} + #ifdef HAMMERSLEY_SIZE vec3 hammersley_3d(float i, float invsamplenbr) { @@ -22,9 +28,18 @@ vec3 hammersley_3d(float i, float invsamplenbr) /* -------------- BSDFS -------------- */ -float pdf_ggx_reflect(float NH, float a2) +#define USE_VISIBLE_NORMAL 1 + +float pdf_ggx_reflect(float NH, float NV, float VH, float alpha) { + float a2 = sqr(alpha); +#if USE_VISIBLE_NORMAL + float D = a2 / D_ggx_opti(NH, a2); + float G1 = NV * 2.0 / G1_Smith_GGX_opti(NV, a2); + return G1 * VH * D / NV; +#else return NH * a2 / D_ggx_opti(NH, a2); +#endif } float pdf_hemisphere() @@ -32,22 +47,50 @@ float pdf_hemisphere() return 0.5 * M_1_PI; } -vec3 sample_ggx(vec3 rand, float a2) +vec3 sample_ggx(vec3 rand, float alpha, vec3 Vt) { +#if USE_VISIBLE_NORMAL + /* From: + * "A Simpler and Exact Sampling Routine for the GGXDistribution of Visible Normals" + * by Eric Heitz. + * http://jcgt.org/published/0007/04/01/slides.pdf + * View vector is expected to be in tangent space. */ + + /* Stretch view. */ + vec3 Th, Bh, Vh = normalize(vec3(alpha * Vt.xy, Vt.z)); + make_orthonormal_basis(Vh, Th, Bh); + /* Sample point with polar coordinates (r, phi). */ + float r = sqrt(rand.x); + float x = r * rand.y; + float y = r * rand.z; + float s = 0.5 * (1.0 + Vh.z); + y = (1.0 - s) * sqrt(1.0 - x * x) + s * y; + float z = sqrt(saturate(1.0 - x * x - y * y)); + /* Compute normal. */ + vec3 Hh = x * Th + y * Bh + z * Vh; + /* Unstretch. */ + vec3 Ht = normalize(vec3(alpha * Hh.xy, saturate(Hh.z))); + /* Microfacet Normal. */ + return Ht; +#else /* Theta is the cone angle. */ - float z = sqrt((1.0 - rand.x) / (1.0 + a2 * rand.x - rand.x)); /* cos theta */ - float r = sqrt(max(0.0, 1.0 - z * z)); /* sin theta */ + float z = sqrt((1.0 - rand.x) / (1.0 + sqr(alpha) * rand.x - rand.x)); /* cos theta */ + float r = sqrt(max(0.0, 1.0 - z * z)); /* sin theta */ float x = r * rand.y; float y = r * rand.z; - /* Microfacet Normal */ return vec3(x, y, z); +#endif } -vec3 sample_ggx(vec3 rand, float a2, vec3 N, vec3 T, vec3 B, out float NH) +vec3 sample_ggx(vec3 rand, float alpha, vec3 V, vec3 N, vec3 T, vec3 B, out float pdf) { - vec3 Ht = sample_ggx(rand, a2); - NH = Ht.z; + vec3 Vt = world_to_tangent(V, N, T, B); + vec3 Ht = sample_ggx(rand, alpha, Vt); + float NH = saturate(Ht.z); + float NV = saturate(Vt.z); + float VH = saturate(dot(Vt, Ht)); + pdf = pdf_ggx_reflect(NH, NV, VH, alpha); return tangent_to_world(Ht, N, T, B); } @@ -69,18 +112,23 @@ vec3 sample_hemisphere(vec3 rand, vec3 N, vec3 T, vec3 B) } #ifdef HAMMERSLEY_SIZE -vec3 sample_ggx(float nsample, float inv_sample_count, float a2, vec3 N, vec3 T, vec3 B) +vec3 sample_ggx(float nsample, + float inv_sample_count, + float alpha, + vec3 V, + vec3 N, + vec3 T, + vec3 B, + out float pdf) { vec3 Xi = hammersley_3d(nsample, inv_sample_count); - vec3 Ht = sample_ggx(Xi, a2); - return tangent_to_world(Ht, N, T, B); + return sample_ggx(Xi, alpha, V, N, T, B, pdf); } vec3 sample_hemisphere(float nsample, float inv_sample_count, vec3 N, vec3 T, vec3 B) { vec3 Xi = hammersley_3d(nsample, inv_sample_count); - vec3 Ht = sample_hemisphere(Xi); - return tangent_to_world(Ht, N, T, B); + return sample_hemisphere(Xi, N, T, B); } vec3 sample_cone(float nsample, float inv_sample_count, float angle, vec3 N, vec3 T, vec3 B) diff --git a/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl b/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl index 2ffe23a9197..2f1298e2707 100644 --- a/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl @@ -37,7 +37,7 @@ void main() Xi.yz = vec2(cos(Xi.y * M_2PI), sin(Xi.y * M_2PI)); /* Microfacet normal. */ - vec3 H = sample_ggx(Xi, a2); + vec3 H = sample_ggx(Xi, a2, V); float VH = dot(V, H); @@ -59,7 +59,7 @@ void main() float LH = dot(L, H); /* Balancing the adjustments made in G1_Smith. */ - float G1_l = NL * 2.0 / G1_Smith_GGX(NL, a2); + float G1_l = NL * 2.0 / G1_Smith_GGX_opti(NL, a2); /* btdf = abs(VH*LH) * (ior*ior) * D * G(V) * G(L) / (Ht2 * NV) * pdf = (VH * abs(LH)) * (ior*ior) * D * G(V) / (Ht2 * NV) */ diff --git a/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl b/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl index 24de4520207..b3174afc799 100644 --- a/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl @@ -69,3 +69,5 @@ layout(std140) uniform common_block #define ssrQuality ssrParameters.x #define ssrThickness ssrParameters.y #define ssrPixelSize ssrParameters.zw + +#define ssrUvScale hizUvScale.zw diff --git a/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl index 66183e1bc02..11048a46f8e 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl @@ -6,32 +6,54 @@ #pragma BLENDER_REQUIRE(closure_eval_lib.glsl) #pragma BLENDER_REQUIRE(raytrace_lib.glsl) #pragma BLENDER_REQUIRE(lightprobe_lib.glsl) -#pragma BLENDER_REQUIRE(ssr_lib.glsl) - -/* Based on Stochastic Screen Space Reflections - * https://www.ea.com/frostbite/news/stochastic-screen-space-reflections */ - -#define MAX_MIP 9.0 +#pragma BLENDER_REQUIRE(bsdf_common_lib.glsl) +#pragma BLENDER_REQUIRE(bsdf_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(surface_lib.glsl) + +/* Based on: + * "Stochastic Screen Space Reflections" + * by Tomasz Stachowiak. + * https://www.ea.com/frostbite/news/stochastic-screen-space-reflections + * and + * "Stochastic all the things: raytracing in hybrid real-time rendering" + * by Tomasz Stachowiak. + * https://media.contentapi.ea.com/content/dam/ea/seed/presentations/dd18-seed-raytracing-in-hybrid-real-time-rendering.pdf + */ uniform ivec2 halfresOffset; -ivec2 encode_hit_data(vec2 hit_pos, bool has_hit, bool is_planar) +struct HitData { + /** Hit direction scaled by intersection time. */ + vec3 hit_dir; + /** Screen space [0..1] depth of the reflection hit position, or -1.0 for planar reflections. */ + float hit_depth; + /** Inverse probability of ray spawning in this direction. */ + float ray_pdf_inv; + /** True if ray has hit valid geometry. */ + bool is_hit; + /** True if ray was generated from a planar reflection probe. */ + bool is_planar; +}; + +void encode_hit_data(HitData data, vec3 hit_sP, vec3 vP, out vec4 hit_data, out float hit_depth) { - ivec2 hit_data = ivec2(saturate(hit_pos) * 32767.0); /* 16bit signed int limit */ - hit_data.x *= (is_planar) ? -1 : 1; - hit_data.y *= (has_hit) ? 1 : -1; - return hit_data; + vec3 hit_vP = get_view_space_from_depth(hit_sP.xy, hit_sP.z); + hit_data.xyz = hit_vP - vP; + hit_depth = data.is_planar ? -1.0 : hit_sP.z; + /* Record 1.0 / pdf to reduce the computation in the resolve phase. */ + /* Encode hit validity in sign. */ + hit_data.w = data.ray_pdf_inv * ((data.is_hit) ? 1.0 : -1.0); } -vec2 decode_hit_data(vec2 hit_data, out bool has_hit, out bool is_planar) +HitData decode_hit_data(vec4 hit_data, float hit_depth) { - is_planar = (hit_data.x < 0); - has_hit = (hit_data.y > 0); - vec2 hit_co = vec2(abs(hit_data)) / 32767.0; /* 16bit signed int limit */ - if (is_planar) { - hit_co.x = 1.0 - hit_co.x; - } - return hit_co; + HitData data; + data.hit_dir.xyz = hit_data.xyz; + data.hit_depth = hit_depth; + data.is_planar = (hit_depth == -1.0); + data.ray_pdf_inv = abs(hit_data.w); + data.is_hit = (hit_data.w > 0.0); + return data; } #ifdef STEP_RAYTRACE @@ -39,92 +61,67 @@ vec2 decode_hit_data(vec2 hit_data, out bool has_hit, out bool is_planar) uniform sampler2D normalBuffer; uniform sampler2D specroughBuffer; -layout(location = 0) out ivec2 hitData; -layout(location = 1) out float pdfData; - -void do_planar_ssr( - int index, vec3 V, vec3 N, vec3 T, vec3 B, vec3 planeNormal, vec3 vP, float a2, vec4 rand) +layout(location = 0) out vec4 hitData; +layout(location = 1) out float hitDepth; + +void do_planar_ssr(int index, + vec3 vV, + vec3 vN, + vec3 vT, + vec3 vB, + vec3 viewPlaneNormal, + vec3 vP, + float alpha, + vec4 rand) { - float NH; - vec3 H = sample_ggx(rand.xzw, a2, N, T, B, NH); /* Microfacet normal */ - float pdf = pdf_ggx_reflect(NH, a2); - - vec3 R = reflect(-V, H); - R = reflect(R, planeNormal); - - /* If ray is bad (i.e. going below the plane) regenerate. */ - if (dot(R, planeNormal) > 0.0) { - vec3 H = sample_ggx(rand.xzw * vec3(1.0, -1.0, -1.0), a2, N, T, B, NH); /* Microfacet normal */ - pdf = pdf_ggx_reflect(NH, a2); - - R = reflect(-V, H); - R = reflect(R, planeNormal); - } - - pdfData = min(1024e32, pdf); /* Theoretical limit of 16bit float */ + float pdf; + /* Microfacet normal */ + vec3 vH = sample_ggx(rand.xzw, alpha, vV, vN, vT, vB, pdf); + vec3 vR = reflect(-vV, vH); + vR = reflect(vR, viewPlaneNormal); Ray ray; ray.origin = vP; - ray.direction = R * 1e16; + ray.direction = vR * 1e16; RayTraceParameters params; params.jitter = rand.y; params.trace_quality = ssrQuality; - params.roughness = a2; + params.roughness = alpha * alpha; - vec3 hit_pos; - bool hit = raytrace_planar(ray, params, index, hit_pos); + vec3 hit_sP; + HitData data; + data.is_planar = true; + data.ray_pdf_inv = safe_rcp(pdf); + data.is_hit = raytrace_planar(ray, params, index, hit_sP); - hitData = encode_hit_data(hit_pos.xy, hit, true); + encode_hit_data(data, hit_sP, ray.origin, hitData, hitDepth); } -void do_ssr(vec3 V, vec3 N, vec3 T, vec3 B, vec3 vP, float a2, vec4 rand) +void do_ssr(vec3 vV, vec3 vN, vec3 vT, vec3 vB, vec3 vP, float alpha, vec4 rand) { - float NH; + float pdf; /* Microfacet normal */ - vec3 H = sample_ggx(rand.xzw, a2, N, T, B, NH); - vec3 R = reflect(-V, H); - - const float bad_ray_threshold = 0.01; - - vec3 vNg = safe_normalize(cross(dFdx(vP), dFdy(vP))); - - /* If ray is bad (i.e. going below the surface) regenerate. */ - if (dot(R, vNg) < bad_ray_threshold) { - H = sample_ggx(rand.xzw * vec3(1.0, -1.0, -1.0), a2, N, T, B, NH); - R = reflect(-V, H); - } - if (dot(R, vNg) < bad_ray_threshold) { - H = sample_ggx(rand.xzw * vec3(1.0, 1.0, -1.0), a2, N, T, B, NH); - R = reflect(-V, H); - } - if (dot(R, vNg) < bad_ray_threshold) { - H = sample_ggx(rand.xzw * vec3(1.0, -1.0, 1.0), a2, N, T, B, NH); - R = reflect(-V, H); - } - if (dot(R, vNg) < bad_ray_threshold) { - /* Not worth tracing. */ - return; - } - - pdfData = min(1024e32, pdf_ggx_reflect(NH, a2)); /* Theoretical limit of 16bit float */ - - vP = raytrace_offset(vP, vNg); + vec3 vH = sample_ggx(rand.xzw, alpha, vV, vN, vT, vB, pdf); + vec3 vR = reflect(-vV, vH); Ray ray; - ray.origin = vP; - ray.direction = R * 1e16; + ray.origin = vP + vN * 1e-4; + ray.direction = vR * 1e16; RayTraceParameters params; params.thickness = ssrThickness; params.jitter = rand.y; params.trace_quality = ssrQuality; - params.roughness = a2; + params.roughness = alpha * alpha; - vec3 hit_pos; - bool hit = raytrace(ray, params, true, hit_pos); + vec3 hit_sP; + HitData data; + data.is_planar = false; + data.ray_pdf_inv = safe_rcp(pdf); + data.is_hit = raytrace(ray, params, true, hit_sP); - hitData = encode_hit_data(hit_pos.xy, hit, false); + encode_hit_data(data, hit_sP, ray.origin, hitData, hitDepth); } in vec4 uvcoordsvar; @@ -132,11 +129,16 @@ in vec4 uvcoordsvar; void main() { vec2 uvs = uvcoordsvar.xy; - float depth = textureLod(depthBuffer, uvs, 0.0).r; + float depth = textureLod(maxzBuffer, uvs * hizUvScale.xy, 0.0).r; + + HitData data; + data.is_planar = false; + data.ray_pdf_inv = 0.0; + data.is_hit = false; + data.hit_dir = vec3(0.0, 0.0, 0.0); /* Default: not hits. */ - hitData = encode_hit_data(vec2(0.5), false, false); - pdfData = 0.0; + encode_hit_data(data, data.hit_dir, data.hit_dir, hitData, hitDepth); /* Early out */ /* We can't do discard because we don't clear the render target. */ @@ -161,8 +163,7 @@ void main() } float roughness = speccol_roughness.a; - float roughnessSquared = max(1e-3, roughness * roughness); - float a2 = roughnessSquared * roughnessSquared; + float alpha = max(1e-3, roughness * roughness); /* Early out */ if (roughness > ssrMaxRoughness + 0.2) { @@ -193,282 +194,107 @@ void main() /* TODO optimize, use view space for all. */ vec3 tracePosition = line_plane_intersect(P, V, pd.pl_plane_eq); tracePosition = transform_point(ViewMatrix, tracePosition); - vec3 planeNormal = transform_direction(ViewMatrix, pd.pl_normal); + vec3 viewPlaneNormal = transform_direction(ViewMatrix, pd.pl_normal); - do_planar_ssr(i, vV, vN, vT, vB, planeNormal, tracePosition, a2, rand); + do_planar_ssr(i, vV, vN, vT, vB, viewPlaneNormal, tracePosition, alpha, rand); return; } } - do_ssr(vV, vN, vT, vB, vP, a2, rand); + do_ssr(vV, vN, vT, vB, vP, alpha, rand); } #else /* STEP_RESOLVE */ -uniform sampler2D prevColorBuffer; /* previous frame */ +uniform sampler2D colorBuffer; /* previous frame */ uniform sampler2D normalBuffer; uniform sampler2D specroughBuffer; - -uniform isampler2D hitBuffer; -uniform sampler2D pdfBuffer; - -uniform int neighborOffset; +uniform sampler2D hitBuffer; +uniform sampler2D hitDepth; in vec4 uvcoordsvar; -const ivec2 neighbors[32] = ivec2[32](ivec2(0, 0), - ivec2(1, 1), - ivec2(-2, 0), - ivec2(0, -2), - ivec2(0, 0), - ivec2(1, -1), - ivec2(-2, 0), - ivec2(0, 2), - ivec2(0, 0), - ivec2(-1, -1), - ivec2(2, 0), - ivec2(0, 2), - ivec2(0, 0), - ivec2(-1, 1), - ivec2(2, 0), - ivec2(0, -2), - - ivec2(0, 0), - ivec2(2, 2), - ivec2(-2, 2), - ivec2(0, -1), - ivec2(0, 0), - ivec2(2, -2), - ivec2(-2, -2), - ivec2(0, 1), - ivec2(0, 0), - ivec2(-2, -2), - ivec2(-2, 2), - ivec2(1, 0), - ivec2(0, 0), - ivec2(2, 2), - ivec2(2, -2), - ivec2(-1, 0)); - out vec4 fragColor; -# if 0 /* Finish reprojection with motion vectors */ -vec3 get_motion_vector(vec3 pos) -{ -} - -/* http://bitsquid.blogspot.fr/2017/06/reprojecting-reflections_22.html */ -vec3 find_reflection_incident_point(vec3 cam, vec3 hit, vec3 pos, vec3 N) -{ - float d_cam = point_plane_projection_dist(cam, pos, N); - float d_hit = point_plane_projection_dist(hit, pos, N); - - if (d_hit < d_cam) { - /* Swap */ - float tmp = d_cam; - d_cam = d_hit; - d_hit = tmp; - } - - vec3 proj_cam = cam - (N * d_cam); - vec3 proj_hit = hit - (N * d_hit); - - return (proj_hit - proj_cam) * d_cam / (d_cam + d_hit) + proj_cam; -} -# endif - float brightness(vec3 c) { return max(max(c.r, c.g), c.b); } -vec2 get_reprojected_reflection(vec3 hit, vec3 pos, vec3 N) -{ - /* TODO real reprojection with motion vectors, etc... */ - return project_point(pastViewProjectionMatrix, hit).xy * 0.5 + 0.5; -} - -float get_sample_depth(vec2 hit_co, bool is_planar, float planar_index) +vec4 ssr_get_scene_color_and_mask(vec3 hit_vP, int planar_index, float mip) { - if (is_planar) { - hit_co.x = 1.0 - hit_co.x; - return textureLod(planarDepth, vec3(hit_co, planar_index), 0.0).r; + vec2 uv; + if (planar_index != -1) { + uv = get_uvs_from_view(hit_vP); + /* Planar X axis is flipped. */ + uv.x = 1.0 - uv.x; } else { - return textureLod(depthBuffer, hit_co, 0.0).r; + /* Find hit position in previous frame. */ + /* TODO Combine matrices. */ + vec3 hit_P = transform_point(ViewMatrixInverse, hit_vP); + /* TODO real reprojection with motion vectors, etc... */ + uv = project_point(pastViewProjectionMatrix, hit_P).xy * 0.5 + 0.5; } -} -vec3 get_hit_vector(vec3 hit_pos, - PlanarData pd, - vec3 P, - vec3 N, - vec3 V, - bool is_planar, - inout vec2 hit_co, - inout float mask) -{ - vec3 hit_vec; - - if (is_planar) { - /* Reflect back the hit position to have it in non-reflected world space */ - vec3 trace_pos = line_plane_intersect(P, V, pd.pl_plane_eq); - hit_vec = hit_pos - trace_pos; - hit_vec = reflect(hit_vec, pd.pl_normal); - /* Modify here so mip texel alignment is correct. */ - hit_co.x = 1.0 - hit_co.x; + vec3 color; + if (planar_index != -1) { + color = textureLod(probePlanars, vec3(uv, planar_index), mip).rgb; } else { - /* Find hit position in previous frame. */ - hit_co = get_reprojected_reflection(hit_pos, P, N); - hit_vec = hit_pos - P; + color = textureLod(colorBuffer, uv * hizUvScale.xy, mip).rgb; } - mask = screen_border_mask(hit_co); - return hit_vec; -} + /* Clamped brightness. */ + float luma = brightness(color); + color *= 1.0 - max(0.0, luma - ssrFireflyFac) * safe_rcp(luma); -vec3 get_scene_color(vec2 ref_uvs, float mip, float planar_index, bool is_planar) -{ - if (is_planar) { - return textureLod(probePlanars, vec3(ref_uvs, planar_index), mip).rgb; - } - else { - return textureLod(prevColorBuffer, ref_uvs * hizUvScale.xy, mip).rgb; - } + float mask = screen_border_mask(uv); + return vec4(color, mask); } -vec4 get_ssr_samples(vec4 hit_pdf, - ivec4 hit_data[2], - PlanarData pd, - float planar_index, - vec3 P, - vec3 N, - vec3 V, - float roughnessSquared, - float cone_tan, - vec2 source_uvs, - inout float weight_acc) +void resolve_reflection_sample(int planar_index, + vec2 sample_uv, + vec3 vP, + vec3 vN, + vec3 vV, + float roughness_squared, + float cone_tan, + inout float weight_accum, + inout vec4 ssr_accum) { - bvec4 is_planar, has_hit; - vec4 hit_co[2]; - hit_co[0].xy = decode_hit_data(hit_data[0].xy, has_hit.x, is_planar.x); - hit_co[0].zw = decode_hit_data(hit_data[0].zw, has_hit.y, is_planar.y); - hit_co[1].xy = decode_hit_data(hit_data[1].xy, has_hit.z, is_planar.z); - hit_co[1].zw = decode_hit_data(hit_data[1].zw, has_hit.w, is_planar.w); - - /* TODO/FIXME(fclem) This is giving precision issues due to refined intersection. This is most - * noticeable on rough surfaces. */ - vec4 hit_depth; - hit_depth.x = get_sample_depth(hit_co[0].xy, is_planar.x, planar_index); - hit_depth.y = get_sample_depth(hit_co[0].zw, is_planar.y, planar_index); - hit_depth.z = get_sample_depth(hit_co[1].xy, is_planar.z, planar_index); - hit_depth.w = get_sample_depth(hit_co[1].zw, is_planar.w, planar_index); - - /* Hit position in view space. */ - vec3 hit_view[4]; - hit_view[0] = get_view_space_from_depth(hit_co[0].xy, hit_depth.x); - hit_view[1] = get_view_space_from_depth(hit_co[0].zw, hit_depth.y); - hit_view[2] = get_view_space_from_depth(hit_co[1].xy, hit_depth.z); - hit_view[3] = get_view_space_from_depth(hit_co[1].zw, hit_depth.w); - - vec4 homcoord = vec4(hit_view[0].z, hit_view[1].z, hit_view[2].z, hit_view[3].z); - homcoord = ProjectionMatrix[2][3] * homcoord + ProjectionMatrix[3][3]; - - /* Hit position in world space. */ - vec3 hit_pos[4]; - hit_pos[0] = transform_point(ViewMatrixInverse, hit_view[0]); - hit_pos[1] = transform_point(ViewMatrixInverse, hit_view[1]); - hit_pos[2] = transform_point(ViewMatrixInverse, hit_view[2]); - hit_pos[3] = transform_point(ViewMatrixInverse, hit_view[3]); - - /* Get actual hit vector and hit coordinate (from last frame). */ - vec4 mask = vec4(1.0); - hit_pos[0] = get_hit_vector(hit_pos[0], pd, P, N, V, is_planar.x, hit_co[0].xy, mask.x); - hit_pos[1] = get_hit_vector(hit_pos[1], pd, P, N, V, is_planar.y, hit_co[0].zw, mask.y); - hit_pos[2] = get_hit_vector(hit_pos[2], pd, P, N, V, is_planar.z, hit_co[1].xy, mask.z); - hit_pos[3] = get_hit_vector(hit_pos[3], pd, P, N, V, is_planar.w, hit_co[1].zw, mask.w); - - vec4 hit_dist; - hit_dist.x = length(hit_pos[0]); - hit_dist.y = length(hit_pos[1]); - hit_dist.z = length(hit_pos[2]); - hit_dist.w = length(hit_pos[3]); - hit_dist = max(vec4(1e-8), hit_dist); - - /* Normalize */ - hit_pos[0] /= hit_dist.x; - hit_pos[1] /= hit_dist.y; - hit_pos[2] /= hit_dist.z; - hit_pos[3] /= hit_dist.w; + vec4 hit_data = texture(hitBuffer, sample_uv * ssrUvScale); + float hit_depth = texture(hitDepth, sample_uv * ssrUvScale).r; + HitData data = decode_hit_data(hit_data, hit_depth); - /* Compute cone footprint in screen space. */ - vec4 cone_footprint = hit_dist * cone_tan; - cone_footprint = ssrBrdfBias * 0.5 * cone_footprint * - max(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) / homcoord; + float hit_dist = length(data.hit_dir); - /* Estimate a cone footprint to sample a corresponding mipmap level. */ - vec4 mip = log2(cone_footprint * max_v2(vec2(textureSize(depthBuffer, 0)))); - mip = clamp(mip, 0.0, MAX_MIP); + /* Slide 54. */ + float bsdf = bsdf_ggx(vN, data.hit_dir / hit_dist, vV, roughness_squared); - /* Slide 54 */ - vec4 bsdf; - bsdf.x = bsdf_ggx(N, hit_pos[0], V, roughnessSquared); - bsdf.y = bsdf_ggx(N, hit_pos[1], V, roughnessSquared); - bsdf.z = bsdf_ggx(N, hit_pos[2], V, roughnessSquared); - bsdf.w = bsdf_ggx(N, hit_pos[3], V, roughnessSquared); + float weight = bsdf * data.ray_pdf_inv; - vec4 weight = step(1e-8, hit_pdf) * bsdf / max(vec4(1e-8), hit_pdf); + /* Do not add light if ray has failed but still weight it. */ + if (!data.is_hit || (planar_index == -1 && data.is_planar) || + (planar_index != -1 && !data.is_planar)) { + weight_accum += weight; + return; + } - vec3 sample[4]; - sample[0] = get_scene_color(hit_co[0].xy, mip.x, planar_index, is_planar.x); - sample[1] = get_scene_color(hit_co[0].zw, mip.y, planar_index, is_planar.y); - sample[2] = get_scene_color(hit_co[1].xy, mip.z, planar_index, is_planar.z); - sample[3] = get_scene_color(hit_co[1].zw, mip.w, planar_index, is_planar.w); + vec3 hit_vP = vP + data.hit_dir; - /* Clamped brightness. */ - vec4 luma; - luma.x = brightness(sample[0]); - luma.y = brightness(sample[1]); - luma.z = brightness(sample[2]); - luma.w = brightness(sample[3]); - luma = max(vec4(1e-8), luma); - luma = 1.0 - max(vec4(0.0), luma - ssrFireflyFac) / luma; - - sample[0] *= luma.x; - sample[1] *= luma.y; - sample[2] *= luma.z; - sample[3] *= luma.w; - - /* Protection against NaNs in the history buffer. - * This could be removed if some previous pass has already - * sanitized the input. */ - if (any(isnan(sample[0]))) { - sample[0] = vec3(0.0); - weight.x = 0.0; - } - if (any(isnan(sample[1]))) { - sample[1] = vec3(0.0); - weight.y = 0.0; - } - if (any(isnan(sample[2]))) { - sample[2] = vec3(0.0); - weight.z = 0.0; - } - if (any(isnan(sample[3]))) { - sample[3] = vec3(0.0); - weight.w = 0.0; - } + /* Compute cone footprint in screen space. */ + float cone_footprint = hit_dist * cone_tan; + float homcoord = ProjectionMatrix[2][3] * hit_vP.z + ProjectionMatrix[3][3]; + cone_footprint *= max(ProjectionMatrix[0][0], ProjectionMatrix[1][1]) / homcoord; + cone_footprint *= ssrBrdfBias * 0.5; + /* Estimate a cone footprint to sample a corresponding mipmap level. */ + float mip = log2(cone_footprint * max_v2(vec2(textureSize(specroughBuffer, 0)))); - weight_acc += sum(weight); + vec4 radiance_mask = ssr_get_scene_color_and_mask(hit_vP, planar_index, mip); - /* Do not add light if ray has failed. */ - vec4 accum; - accum = vec4(sample[0], mask.x) * weight.x * float(has_hit.x); - accum += vec4(sample[1], mask.y) * weight.y * float(has_hit.y); - accum += vec4(sample[2], mask.z) * weight.z * float(has_hit.z); - accum += vec4(sample[3], mask.w) * weight.w * float(has_hit.w); - return accum; + ssr_accum += radiance_mask * weight; + weight_accum += weight; } void raytrace_resolve(ClosureInputGlossy cl_in, @@ -476,74 +302,55 @@ void raytrace_resolve(ClosureInputGlossy cl_in, inout ClosureEvalCommon cl_common, inout ClosureOutputGlossy cl_out) { -# ifdef FULLRES - ivec2 texel = ivec2(gl_FragCoord.xy); -# else - ivec2 texel = ivec2(gl_FragCoord.xy / 2.0); -# endif - /* Using world space */ - vec3 V = cl_common.V; - vec3 N = cl_in.N; - vec3 P = cl_common.P; - float roughness = cl_in.roughness; - float roughnessSquared = max(1e-3, sqr(roughness)); - - /* Resolve SSR */ - float cone_cos = cone_cosine(roughnessSquared); - float cone_tan = sqrt(1 - cone_cos * cone_cos) / cone_cos; - cone_tan *= mix(saturate(dot(N, -V) * 2.0), 1.0, roughness); /* Elongation fit */ - - vec2 source_uvs = project_point(pastViewProjectionMatrix, P).xy * 0.5 + 0.5; vec4 ssr_accum = vec4(0.0); float weight_acc = 0.0; if (roughness < ssrMaxRoughness + 0.2) { - /* TODO optimize with textureGather */ - /* Doing these fetches early to hide latency. */ - vec4 hit_pdf; - hit_pdf.x = texelFetch(pdfBuffer, texel + neighbors[0 + neighborOffset], 0).r; - hit_pdf.y = texelFetch(pdfBuffer, texel + neighbors[1 + neighborOffset], 0).r; - hit_pdf.z = texelFetch(pdfBuffer, texel + neighbors[2 + neighborOffset], 0).r; - hit_pdf.w = texelFetch(pdfBuffer, texel + neighbors[3 + neighborOffset], 0).r; - - ivec4 hit_data[2]; - hit_data[0].xy = texelFetch(hitBuffer, texel + neighbors[0 + neighborOffset], 0).rg; - hit_data[0].zw = texelFetch(hitBuffer, texel + neighbors[1 + neighborOffset], 0).rg; - hit_data[1].xy = texelFetch(hitBuffer, texel + neighbors[2 + neighborOffset], 0).rg; - hit_data[1].zw = texelFetch(hitBuffer, texel + neighbors[3 + neighborOffset], 0).rg; - /* Find Planar Reflections affecting this pixel */ - PlanarData pd; - float planar_index; + int planar_index = -1; for (int i = 0; i < MAX_PLANAR && i < prbNumPlanar; i++) { - pd = planars_data[i]; - - float fade = probe_attenuation_planar(pd, P); - fade *= probe_attenuation_planar_normal_roughness(pd, N, 0.0); - + float fade = probe_attenuation_planar(planars_data[i], cl_common.P); + fade *= probe_attenuation_planar_normal_roughness(planars_data[i], cl_in.N, 0.0); if (fade > 0.5) { - planar_index = float(i); + planar_index = i; break; } } - ssr_accum += get_ssr_samples(hit_pdf, - hit_data, - pd, - planar_index, - P, - N, - V, - roughnessSquared, - cone_tan, - source_uvs, - weight_acc); + vec3 V, P, N; + if (planar_index != -1) { + PlanarData pd = planars_data[planar_index]; + /* Evaluate everything in refected space. */ + P = line_plane_intersect(cl_common.P, cl_common.V, pd.pl_plane_eq); + V = reflect(cl_common.V, pd.pl_normal); + N = reflect(cl_in.N, pd.pl_normal); + } + else { + V = cl_common.V; + P = cl_common.P; + N = cl_in.N; + } + + /* Using view space */ + vec3 vV = transform_direction(ViewMatrix, cl_common.V); + vec3 vP = transform_point(ViewMatrix, cl_common.P); + vec3 vN = transform_direction(ViewMatrix, cl_in.N); + + float roughness_squared = max(1e-3, sqr(roughness)); + float cone_cos = cone_cosine(roughness_squared); + float cone_tan = sqrt(1.0 - cone_cos * cone_cos) / cone_cos; + cone_tan *= mix(saturate(dot(vN, -vV) * 2.0), 1.0, roughness); /* Elongation fit */ + + vec2 sample_uv = uvcoordsvar.xy; + + resolve_reflection_sample( + planar_index, sample_uv, vP, vN, vV, roughness_squared, cone_tan, weight_acc, ssr_accum); } /* Compute SSR contribution */ - ssr_accum *= (weight_acc == 0.0) ? 0.0 : (1.0 / weight_acc); + ssr_accum *= safe_rcp(weight_acc); /* fade between 0.5 and 1.0 roughness */ ssr_accum.a *= smoothstep(ssrMaxRoughness + 0.2, ssrMaxRoughness, roughness); @@ -555,13 +362,13 @@ CLOSURE_EVAL_FUNCTION_DECLARE_1(ssr_resolve, Glossy) void main() { - ivec2 texel = ivec2(gl_FragCoord.xy); - float depth = texelFetch(depthBuffer, texel, 0).r; + float depth = textureLod(maxzBuffer, uvcoordsvar.xy * hizUvScale.xy, 0.0).r; if (depth == 1.0) { discard; } + ivec2 texel = ivec2(gl_FragCoord.xy); vec4 speccol_roughness = texelFetch(specroughBuffer, texel, 0).rgba; vec3 brdf = speccol_roughness.rgb; float roughness = speccol_roughness.a; diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl index 35fdbcb715f..99cbf2839ad 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl @@ -3,7 +3,7 @@ #pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) uniform samplerCube probeHdr; -uniform float roughnessSquared; +uniform float roughness; uniform float texelSize; uniform float lodFactor; uniform float lodMax; @@ -52,7 +52,9 @@ void main() float weight = 0.0; vec3 out_radiance = vec3(0.0); for (float i = 0; i < sampleCount; i++) { - vec3 H = sample_ggx(i, invSampleCount, roughnessSquared, N, T, B); /* Microfacet normal */ + float pdf; + /* Microfacet normal */ + vec3 H = sample_ggx(i, invSampleCount, roughness, V, N, T, B, pdf); vec3 L = -reflect(V, H); float NL = dot(N, L); @@ -62,7 +64,6 @@ void main() /* Coarse Approximation of the mapping distortion * Unit Sphere -> Cubemap Face */ const float dist = 4.0 * M_PI / 6.0; - float pdf = pdf_ggx_reflect(NH, roughnessSquared); /* http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html : Equation 13 */ float lod = clamp(lodFactor - 0.5 * log2(pdf * dist), 0.0, lodMax); diff --git a/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl b/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl index 7c375aabb62..8975397b62a 100644 --- a/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl @@ -15,13 +15,6 @@ struct Ray { vec3 direction; }; -vec3 raytrace_offset(vec3 P, vec3 Ng) -{ - /* TODO(fclem) better offset */ - const float epsilon_f = 1e-4; - return P + epsilon_f * Ng; -} - /* Inputs expected to be in viewspace. */ void raytrace_clip_ray_to_near_plane(inout Ray ray) { @@ -50,12 +43,17 @@ void raytrace_screenspace_ray_finalize(inout ScreenSpaceRay ray) ray.direction.zw += bias; ray.direction -= ray.origin; - float ray_len_sqr = len_squared(ray.direction.xyz); /* If the line is degenerate, make it cover at least one pixel * to not have to handle zero-pixel extent as a special case later */ - if (ray_len_sqr < 0.00001) { - ray.direction.xy = vec2(0.0, 0.0001); + if (len_squared(ray.direction.xy) < 0.00001) { + ray.direction.xy = vec2(0.0, 0.01); + } + /* Avoid divide by 0 error in line_unit_box_intersect_dist, leading to undefined behavior + * (see T86429). */ + if (ray.direction.z == 0.0) { + ray.direction.z = 0.0001; } + float ray_len_sqr = len_squared(ray.direction.xyz); /* Make ray.direction cover one pixel. */ bool is_more_vertical = abs(ray.direction.x) < abs(ray.direction.y); ray.direction /= (is_more_vertical) ? abs(ray.direction.y) : abs(ray.direction.x); @@ -150,7 +148,7 @@ bool raytrace(Ray ray, /* Check if the ray is below the surface ... */ hit = (delta < 0.0); /* ... and above it with the added thickness. */ - hit = hit && (delta > ss_p.z - ss_p.w); + hit = hit && (delta > ss_p.z - ss_p.w || abs(delta) < abs(ssray.direction.z * stride)); } /* Discard backface hits. */ hit = hit && !(discard_backface && prev_delta < 0.0); @@ -173,8 +171,6 @@ bool raytrace_planar(Ray ray, RayTraceParameters params, int planar_ref_id, out } ScreenSpaceRay ssray = raytrace_screenspace_ray_create(ray); - /* Avoid no iteration. */ - ssray.max_time = max(ssray.max_time, 1.1); /* Planar Reflections have X mirrored. */ ssray.origin.x = 1.0 - ssray.origin.x; @@ -184,9 +180,10 @@ bool raytrace_planar(Ray ray, RayTraceParameters params, int planar_ref_id, out float depth_sample = get_depth_from_view_z(ray.origin.z); float delta = depth_sample - ssray.origin.z; - /* Cross at least one pixel. */ - float t = 1.001, time = 1.001; - bool hit = false; + float t = 0.0, time = 0.0; + /* On very sharp reflections, the ray can be perfectly aligned with the view direction + * making the tracing useless. Bypass tracing in this case. */ + bool hit = (ssray.max_time < 1.0); const float max_steps = 255.0; for (float iter = 1.0; !hit && (time < ssray.max_time) && (iter < max_steps); iter++) { float stride = 1.0 + iter * params.trace_quality; @@ -212,6 +209,8 @@ bool raytrace_planar(Ray ray, RayTraceParameters params, int planar_ref_id, out time = mix(prev_time, time, saturate(prev_delta / (prev_delta - delta))); hit_position = ssray.origin.xyz + ssray.direction.xyz * time; + /* Planar Reflections have X mirrored. */ + hit_position.x = 1.0 - hit_position.x; return hit; } diff --git a/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl b/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl index 5a09120916a..612e95832e4 100644 --- a/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/ssr_lib.glsl @@ -9,23 +9,26 @@ #define BTDF_BIAS 0.85 +uniform sampler2D refractColorBuffer; + +uniform float refractionDepth; + vec4 screen_space_refraction(vec3 vP, vec3 N, vec3 V, float ior, float roughnessSquared, vec4 rand) { - float a2 = max(5e-6, roughnessSquared * roughnessSquared); + float alpha = max(0.002, roughnessSquared); /* Importance sampling bias */ rand.x = mix(rand.x, 0.0, BTDF_BIAS); vec3 T, B; - float NH; make_orthonormal_basis(N, T, B); - vec3 H = sample_ggx(rand.xzw, a2, N, T, B, NH); /* Microfacet normal */ - float pdf = pdf_ggx_reflect(NH, a2); + float pdf; + /* Microfacet normal */ + vec3 H = sample_ggx(rand.xzw, alpha, V, N, T, B, pdf); /* If ray is bad (i.e. going below the plane) regenerate. */ if (F_eta(ior, dot(H, V)) < 1.0) { - H = sample_ggx(rand.xzw * vec3(1.0, -1.0, -1.0), a2, N, T, B, NH); /* Microfacet normal */ - pdf = pdf_ggx_reflect(NH, a2); + H = sample_ggx(rand.xzw * vec3(1.0, -1.0, -1.0), alpha, V, N, T, B, pdf); } vec3 vV = viewCameraVec(vP); @@ -75,10 +78,10 @@ vec4 screen_space_refraction(vec3 vP, vec3 N, vec3 V, float ior, float roughness vec2 hit_uvs = project_point(ProjectionMatrix, hit_pos).xy * 0.5 + 0.5; /* Texel footprint */ - vec2 texture_size = vec2(textureSize(colorBuffer, 0).xy); + vec2 texture_size = vec2(textureSize(refractColorBuffer, 0).xy) / hizUvScale.xy; float mip = clamp(log2(cone_footprint * max(texture_size.x, texture_size.y)), 0.0, 9.0); - vec3 spec = textureLod(colorBuffer, hit_uvs * hizUvScale.xy, mip).xyz; + vec3 spec = textureLod(refractColorBuffer, hit_uvs * hizUvScale.xy, mip).xyz; float mask = screen_border_mask(hit_uvs); return vec4(spec, mask); diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl index e80dc1761f0..0acb35b2399 100644 --- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl @@ -1,12 +1,5 @@ /** This describe the entire interface of the shader. */ -/* Samplers */ -uniform sampler2D colorBuffer; -uniform sampler2D depthBuffer; - -/* Uniforms */ -uniform float refractionDepth; - #define SURFACE_INTERFACE \ vec3 worldPosition; \ vec3 viewPosition; \ diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index b03d2c6e795..4bbd475dd2c 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -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; diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 85130e89ad1..4749f40fac5 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1710,6 +1710,15 @@ 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) { + 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); diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 3b7c80cee07..1281f1392d8 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -435,16 +435,16 @@ static void gpencil_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgp WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); } -/* Helper: Get previous keyframe. */ +/* Helper: Get previous keyframe (exclude breakdown type). */ static bGPDframe *gpencil_get_previous_keyframe(bGPDlayer *gpl, int cfra) { if (gpl->actframe != NULL && gpl->actframe->framenum < cfra && - gpl->actframe->key_type == BEZT_KEYTYPE_KEYFRAME) { + gpl->actframe->key_type != BEZT_KEYTYPE_BREAKDOWN) { return gpl->actframe; } LISTBASE_FOREACH_BACKWARD (bGPDframe *, gpf, &gpl->frames) { - if (gpf->key_type != BEZT_KEYTYPE_KEYFRAME) { + if (gpf->key_type == BEZT_KEYTYPE_BREAKDOWN) { continue; } if (gpf->framenum >= cfra) { @@ -456,11 +456,11 @@ static bGPDframe *gpencil_get_previous_keyframe(bGPDlayer *gpl, int cfra) return NULL; } -/* Helper: Get next keyframe. */ +/* Helper: Get next keyframe (exclude breakdown type). */ static bGPDframe *gpencil_get_next_keyframe(bGPDlayer *gpl, int cfra) { LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { - if (gpf->key_type != BEZT_KEYTYPE_KEYFRAME) { + if (gpf->key_type == BEZT_KEYTYPE_BREAKDOWN) { continue; } if (gpf->framenum <= cfra) { @@ -760,7 +760,7 @@ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent Scene *scene = CTX_data_scene(C); tGPDinterpolate *tgpi = NULL; - /* cannot interpolate if not between 2 frames */ + /* Cannot interpolate if not between 2 frames. */ int cfra = CFRA; bGPDframe *gpf_prv = gpencil_get_previous_keyframe(gpl, cfra); bGPDframe *gpf_next = gpencil_get_next_keyframe(gpl, cfra); @@ -768,7 +768,7 @@ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent BKE_report( op->reports, RPT_ERROR, - "Cannot find a pair of grease pencil frames to interpolate between in active layer"); + "Cannot find valid keyframes to interpolate (Breakdowns keyframes are not allowed)"); return OPERATOR_CANCELLED; } @@ -1260,7 +1260,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) BKE_report( op->reports, RPT_ERROR, - "Cannot find a pair of grease pencil frames to interpolate between in active layer"); + "Cannot find valid keyframes to interpolate (Breakdowns keyframes are not allowed)"); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index edfd1ecd539..91c19ff2850 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -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)) { diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 26b2c7a9091..0c6be7b1196 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -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; diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 610551e8539..d64769567f7 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -450,8 +450,9 @@ static bool bake_object_check(ViewLayer *view_layer, if (target == R_BAKE_TARGET_VERTEX_COLORS) { MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); + const bool mcol_valid = (mcol != NULL && U.experimental.use_sculpt_vertex_colors); MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_MLOOPCOL); - if (mcol == NULL && mloopcol == NULL) { + if (mloopcol == NULL && !mcol_valid) { BKE_reportf(reports, RPT_ERROR, "No vertex colors layer found in the object \"%s\"", @@ -933,8 +934,9 @@ static bool bake_targets_init_vertex_colors(BakeTargets *targets, Object *ob, Re Mesh *me = ob->data; MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); + const bool mcol_valid = (mcol != NULL && U.experimental.use_sculpt_vertex_colors); MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_MLOOPCOL); - if (mcol == NULL && mloopcol == NULL) { + if (mloopcol == NULL && !mcol_valid) { BKE_report(reports, RPT_ERROR, "No vertex colors layer found to bake to"); return false; } @@ -1043,6 +1045,7 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob, { Mesh *me = ob->data; MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); + const bool mcol_valid = (mcol != NULL && U.experimental.use_sculpt_vertex_colors); MLoopCol *mloopcol = CustomData_get_layer(&me->ldata, CD_MLOOPCOL); const int num_channels = targets->num_channels; const float *result = targets->result; @@ -1052,7 +1055,7 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob, BLI_assert(me->totloop == me_split->totloop); UNUSED_VARS_NDEBUG(me_split); - if (mcol) { + if (mcol_valid) { const int totvert = me->totvert; const int totloop = me->totloop; diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index e74d04ab17f..0aa739c2fc8 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -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; diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index fff172c0707..3b668a1bd4c 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -68,6 +68,7 @@ set(SRC sculpt_filter_mesh.c sculpt_geodesic.c sculpt_mask_expand.c + sculpt_mask_init.c sculpt_multiplane_scrape.c sculpt_paint_color.c sculpt_pose.c diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 966f2ace931..7671f69ee05 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -131,8 +131,7 @@ static void mask_flood_fill_task_cb(void *__restrict userdata, SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_MASK); - BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (data->pbvh, node, vi, PBVH_ITER_UNIQUE) { float prevmask = *vi.mask; mask_flood_fill_set_elem(vi.mask, mode, value); if (prevmask != *vi.mask) { @@ -757,8 +756,7 @@ static void face_set_gesture_apply_task_cb(void *__restrict userdata, PBVHVertexIter vd; bool any_updated = false; - BKE_pbvh_vertex_iter_begin(sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) { SCULPT_vertex_face_set_set(sgcontext->ss, vd.index, face_set_operation->new_face_set_id); any_updated = true; @@ -832,8 +830,7 @@ static void mask_gesture_apply_task_cb(void *__restrict userdata, bool any_masked = false; bool redraw = false; - BKE_pbvh_vertex_iter_begin(sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) { float prevmask = *vd.mask; if (!any_masked) { @@ -1415,8 +1412,7 @@ static void project_line_gesture_apply_task_cb(void *__restrict userdata, SCULPT_undo_push_node(sgcontext->vc.obact, node, SCULPT_UNDO_COORDS); - BKE_pbvh_vertex_iter_begin(sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { if (!sculpt_gesture_is_vertex_effected(sgcontext, &vd)) { continue; } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index f45c244f675..fc52f6fea7c 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -1841,8 +1841,7 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata, /* For each vertex */ PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { /* Test to see if the vertex coordinates are within the spherical brush region. */ if (sculpt_brush_test_sq_fn(&test, vd.co)) { /* For grid based pbvh, take the vert whose loop corresponds to the current grid. @@ -1938,8 +1937,7 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata, /* For each vertex */ PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { /* Test to see if the vertex coordinates are within the spherical brush region. */ if (sculpt_brush_test_sq_fn(&test, vd.co)) { /* For grid based pbvh, take the vert whose loop corresponds to the current grid. @@ -2045,8 +2043,7 @@ static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata, /* For each vertex */ PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { /* Test to see if the vertex coordinates are within the spherical brush region. */ if (sculpt_brush_test_sq_fn(&test, vd.co)) { /* Note: grids are 1:1 with corners (aka loops). @@ -2114,8 +2111,7 @@ static void do_wpaint_brush_calc_average_weight_cb_ex( /* For each vertex */ PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { /* Test to see if the vertex coordinates are within the spherical brush region. */ if (sculpt_brush_test_sq_fn(&test, vd.co)) { const float angle_cos = (use_normal && vd.no) ? dot_vf3vs3(sculpt_normal_frontface, vd.no) : @@ -2810,8 +2806,7 @@ static void do_vpaint_brush_calc_average_color_cb_ex(void *__restrict userdata, /* For each vertex */ PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { /* Test to see if the vertex coordinates are within the spherical brush region. */ if (sculpt_brush_test_sq_fn(&test, vd.co)) { const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v : @@ -2880,8 +2875,7 @@ static void do_vpaint_brush_draw_task_cb_ex(void *__restrict userdata, /* For each vertex */ PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { /* Test to see if the vertex coordinates are within the spherical brush region. */ if (sculpt_brush_test_sq_fn(&test, vd.co)) { /* Note: Grids are 1:1 with corners (aka loops). @@ -2979,8 +2973,7 @@ static void do_vpaint_brush_blur_task_cb_ex(void *__restrict userdata, /* For each vertex */ PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { /* Test to see if the vertex coordinates are within the spherical brush region. */ if (sculpt_brush_test_sq_fn(&test, vd.co)) { /* For grid based pbvh, take the vert whose loop corresponds to the current grid. @@ -3103,8 +3096,7 @@ static void do_vpaint_brush_smear_task_cb_ex(void *__restrict userdata, /* For each vertex */ PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { /* Test to see if the vertex coordinates are within the spherical brush region. */ if (sculpt_brush_test_sq_fn(&test, vd.co)) { /* For grid based pbvh, take the vert whose loop corresponds to the current grid. diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 0b30303de91..964e5bdaa90 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -970,8 +970,7 @@ static void do_nearest_vertex_get_task_cb(void *__restrict userdata, NearestVertexTLSData *nvtd = tls->userdata_chunk; PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co); if (distance_squared < nvtd->nearest_vertex_distance_squared && distance_squared < data->max_distance_squared) { @@ -1488,8 +1487,7 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, SCULPT_orig_vert_data_unode_init(&orig_data, data->ob, unode); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (orig_data.unode->type == SCULPT_UNDO_COORDS) { @@ -2050,8 +2048,7 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, } } else { - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { float co[3]; /* For bm_vert only. */ @@ -2922,8 +2919,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, const int thread_id = BLI_task_parallel_thread_id(tls); PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -2995,8 +2991,7 @@ static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -3072,8 +3067,7 @@ static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata, const int thread_id = BLI_task_parallel_thread_id(tls); PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -3138,8 +3132,7 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, const int thread_id = BLI_task_parallel_thread_id(tls); PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -3219,8 +3212,7 @@ static void do_displacement_smear_store_prev_disp_task_cb_ex( SculptSession *ss = data->ob->sculpt; PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { sub_v3_v3v3(ss->cache->prev_displacement[vd.index], SCULPT_vertex_co_get(ss, vd.index), ss->cache->limit_surface_co[vd.index]); @@ -3283,8 +3275,7 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -3362,8 +3353,7 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; @@ -3445,8 +3435,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; @@ -3603,8 +3592,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; @@ -3770,8 +3758,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -3888,8 +3875,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata, copy_v3_v3(x_object_space, stroke_xz[0]); copy_v3_v3(z_object_space, stroke_xz[1]); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -4003,8 +3989,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, const bool grab_silhouette = brush->flag2 & BRUSH_GRAB_SILHOUETTE; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { @@ -4105,8 +4090,7 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, BKE_kelvinlet_init_params( ¶ms, ss->cache->radius, force, 1.0f, brush->elastic_deform_volume_preservation); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); float final_disp[3]; switch (brush->elastic_deform_type) { @@ -4336,8 +4320,7 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -4416,8 +4399,7 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, KelvinletParams params; BKE_kelvinlet_init_params(¶ms, ss->cache->radius, bstrength, 1.0f, 0.4f); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!do_elastic && !sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -4554,8 +4536,7 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { @@ -4628,8 +4609,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { @@ -4701,8 +4681,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { @@ -4816,8 +4795,7 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -4922,8 +4900,7 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata, plane_from_point_normal_v3(test.plane_tool, area_co, area_no); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -5029,8 +5006,7 @@ static void calc_clay_surface_task_cb(void *__restrict userdata, return; } - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -5080,8 +5056,7 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata, plane_from_point_normal_v3(test.plane_tool, area_co, area_no); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -5200,8 +5175,7 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!SCULPT_brush_test_cube(&test, vd.co, mat, brush->tip_roundness)) { continue; } @@ -5354,8 +5328,7 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata, plane_from_point_normal_v3(test.plane_tool, area_co, area_no); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -5453,8 +5426,7 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata, const int thread_id = BLI_task_parallel_thread_id(tls); plane_from_point_normal_v3(test.plane_tool, area_co, area_no); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -5568,8 +5540,7 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata, /* Tilted plane (front part of the brush). */ plane_from_point_normal_v3(plane_tilt, area_co, normal_tilt); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -5724,8 +5695,7 @@ static void do_gravity_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -6236,8 +6206,7 @@ static void sculpt_combine_proxies_task_cb(void *__restrict userdata, BKE_pbvh_node_get_proxies(data->nodes[n], &proxies, &proxy_count); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { float val[3]; if (use_orco) { @@ -6332,8 +6301,7 @@ static void SCULPT_flush_stroke_deform_task_cb(void *__restrict userdata, PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { sculpt_flush_pbvhvert_deform(ob, &vd); if (!vertCos) { @@ -8917,8 +8885,7 @@ static void do_fake_neighbor_search_task_cb(void *__restrict userdata, NearestVertexFakeNeighborTLSData *nvtd = tls->userdata_chunk; PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { int vd_topology_id = SCULPT_vertex_get_connected_component(ss, vd.index); if (vd_topology_id != nvtd->current_topology_id && ss->fake_neighbors.fake_neighbor_index[vd.index] == FAKE_NEIGHBOR_NONE) { @@ -9188,8 +9155,7 @@ static void do_mask_by_color_contiguous_update_nodes_cb( const bool preserve_mask = data->mask_by_color_preserve_mask; PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { const float current_mask = *vd.mask; const float new_mask = data->mask_by_color_floodfill[vd.index]; *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask); @@ -9295,8 +9261,7 @@ static void do_mask_by_color_task_cb(void *__restrict userdata, const float *active_color = SCULPT_vertex_color_get(ss, data->mask_by_color_vertex); PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { const float current_mask = *vd.mask; const float new_mask = sculpt_mask_by_color_delta_get(active_color, vd.col, threshold, invert); *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask); @@ -9461,6 +9426,7 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_color_filter); WM_operatortype_append(SCULPT_OT_mask_by_color); WM_operatortype_append(SCULPT_OT_dyntopo_detail_size_edit); + WM_operatortype_append(SCULPT_OT_mask_init); WM_operatortype_append(SCULPT_OT_expand); } diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.c b/source/blender/editors/sculpt_paint/sculpt_boundary.c index f79621ccffd..37678ec276a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.c +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.c @@ -667,8 +667,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, } const float angle = angle_factor * M_PI; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (boundary->edit_info[vd.index].num_propagation_steps == -1) { continue; } @@ -716,8 +715,7 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (boundary->edit_info[vd.index].num_propagation_steps == -1) { continue; } @@ -763,8 +761,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (boundary->edit_info[vd.index].num_propagation_steps == -1) { continue; } @@ -810,8 +807,7 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (boundary->edit_info[vd.index].num_propagation_steps == -1) { continue; } @@ -862,8 +858,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, } const float angle = angle_factor * M_PI; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (boundary->edit_info[vd.index].num_propagation_steps == -1) { continue; } @@ -909,8 +904,7 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (boundary->edit_info[vd.index].num_propagation_steps == -1) { continue; } diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index 16d10f6d6bb..a53a2126af4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -377,8 +377,7 @@ static void do_cloth_brush_build_constraints_task_cb_ex( data->cloth_sim_radius * data->cloth_sim_radius : FLT_MAX; - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { const float len_squared = len_squared_v3v3(vd.co, data->cloth_sim_initial_location); if (len_squared < cloth_sim_radius_squared) { @@ -518,8 +517,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, madd_v3_v3fl(gravity, ss->cache->gravity_direction, -data->sd->gravity_factor); } - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { float force[3]; float sim_location[3]; cloth_brush_simulation_location_get(ss, brush, sim_location); @@ -783,8 +781,7 @@ static void do_cloth_brush_solve_simulation_task_cb_ex( AutomaskingCache *automasking = SCULPT_automasking_active_cache_get(ss); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { float sim_location[3]; cloth_brush_simulation_location_get(ss, brush, sim_location); const float sim_factor = @@ -1449,8 +1446,7 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, mul_v3_fl(sculpt_gravity, sd->gravity_factor * data->filter_strength); PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { float fade = vd.mask ? *vd.mask : 0.0f; fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index); fade = 1.0f - fade; diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index db6d33c2700..8b8ed42a694 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -1143,8 +1143,7 @@ static void sculpt_expand_restore_color_data(SculptSession *ss, ExpandCache *exp for (int n = 0; n < totnode; n++) { PBVHNode *node = nodes[n]; PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { copy_v4_v4(vd.col, expand_cache->original_colors[vd.index]); } BKE_pbvh_vertex_iter_end; @@ -1161,8 +1160,7 @@ static void sculpt_expand_restore_mask_data(SculptSession *ss, ExpandCache *expa for (int n = 0; n < totnode; n++) { PBVHNode *node = nodes[n]; PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { *vd.mask = expand_cache->original_mask[vd.index]; } BKE_pbvh_vertex_iter_end; @@ -1231,8 +1229,7 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata, bool any_changed = false; PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { const float initial_mask = *vd.mask; const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index); @@ -1304,8 +1301,7 @@ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata, bool any_changed = false; PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { float initial_color[4]; copy_v4_v4(initial_color, vd.col); diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index 332e551c577..17c4beab086 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -138,8 +138,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { MeshElemMap *vert_map = &ss->pmap[vd.index]; for (int j = 0; j < ss->pmap[vd.index].count; j++) { @@ -214,8 +213,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index b5fade32a25..de9511bab6f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -110,8 +110,7 @@ static void color_filter_task_cb(void *__restrict userdata, SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); float orig_color[3], final_color[4], hsv_color[3]; int hue; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c index 0297ed73dd4..10f141e2311 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c @@ -112,8 +112,7 @@ static void mask_filter_task_cb(void *__restrict userdata, contrast = -0.1f; } - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { float delta, gain, offset, max, min; float prev_val = *vd.mask; SculptVertexNeighborIter ni; @@ -363,8 +362,7 @@ static void dirty_mask_compute_range_task_cb(void *__restrict userdata, DirtyMaskRangeData *range = tls->userdata_chunk; PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { float dirty_mask = neighbor_dirty_mask(ss, &vd); range->min = min_ff(dirty_mask, range->min); range->max = max_ff(dirty_mask, range->max); @@ -403,8 +401,7 @@ static void dirty_mask_apply_task_cb(void *__restrict userdata, range = 1.0f / range; } - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { float dirty_mask = neighbor_dirty_mask(ss, &vd); float mask = *vd.mask + (1.0f - ((dirty_mask - min) * range)); if (dirty_only) { diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index 3cf6a8cc561..3fc1a7674f7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -302,8 +302,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, const bool relax_face_sets = !(ss->filter_cache->iteration_count % 3 == 0); PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); float orig_co[3], val[3], avg[3], normal[3], disp[3], disp2[3], transform[3][3], final_pos[3]; float fade = vd.mask ? *vd.mask : 0.0f; @@ -586,8 +585,7 @@ static void mesh_filter_surface_smooth_displace_task_cb( PBVHNode *node = data->nodes[i]; PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { float fade = vd.mask ? *vd.mask : 0.0f; fade = 1.0f - fade; fade *= data->filter_strength; diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 16c2996e392..087cb6dd94a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -792,6 +792,9 @@ typedef struct SculptThreadedTaskData { int face_set; int filter_undo_type; + int mask_init_mode; + int mask_init_seed; + ThreadMutex mutex; } SculptThreadedTaskData; @@ -1254,7 +1257,7 @@ typedef struct FilterCache { float *sharpen_factor; float (*detail_directions)[3]; - /* Filter orientaiton. */ + /* Filter orientation. */ SculptFilterOrientation orientation; float obmat[4][4]; float obmat_inv[4][4]; @@ -1356,6 +1359,9 @@ void SCULPT_OT_dirty_mask(struct wmOperatorType *ot); /* Mask and Face Sets Expand. */ void SCULPT_OT_mask_expand(struct wmOperatorType *ot); +/* Mask Init. */ +void SCULPT_OT_mask_init(struct wmOperatorType *ot); + /* Detail size. */ void SCULPT_OT_detail_flood_fill(struct wmOperatorType *ot); void SCULPT_OT_sample_detail_size(struct wmOperatorType *ot); diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 5e229e020ad..9b06b2ee5d5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -85,8 +85,7 @@ static void sculpt_mask_expand_cancel(bContext *C, wmOperator *op) } else { PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { *vd.mask = ss->filter_cache->prev_mask[vd.index]; } BKE_pbvh_vertex_iter_end; @@ -114,8 +113,7 @@ static void sculpt_expand_task_cb(void *__restrict userdata, PBVHVertexIter vd; int update_it = data->mask_expand_update_it; - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { int vi = vd.index; float final_mask = *vd.mask; if (data->mask_expand_use_normals) { @@ -227,8 +225,7 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * for (int n = 0; n < ss->filter_cache->totnode; n++) { PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, ss->filter_cache->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, ss->filter_cache->nodes[n], vd, PBVH_ITER_UNIQUE) { const float mask = (vd.mask) ? *vd.mask : 0.0f; if (mask < (0.5f + threshold) && mask > (0.5f - threshold)) { if (SCULPT_check_vertex_pivot_symmetry( diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_init.c b/source/blender/editors/sculpt_paint/sculpt_mask_init.c new file mode 100644 index 00000000000..0c383cdf035 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_mask_init.c @@ -0,0 +1,195 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_hash.h" +#include "BLI_math.h" +#include "BLI_task.h" + +#include "BLT_translation.h" + +#include "PIL_time.h" + +#include "DNA_brush_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" + +#include "BKE_brush.h" +#include "BKE_ccg.h" +#include "BKE_context.h" +#include "BKE_mesh.h" +#include "BKE_multires.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" + +#include "DEG_depsgraph.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_sculpt.h" +#include "paint_intern.h" +#include "sculpt_intern.h" + +#include "bmesh.h" + +#include <math.h> +#include <stdlib.h> + +/* Mask Init operator. */ +/* Initializes mask values for the entire mesh depending on the mode. */ + +typedef enum eSculptMaskInitMode { + SCULPT_MASK_INIT_RANDOM_PER_VERTEX, + SCULPT_MASK_INIT_RANDOM_PER_FACE_SET, + SCULPT_MASK_INIT_RANDOM_PER_LOOSE_PART, +} eSculptMaskInitMode; + +static EnumPropertyItem prop_sculpt_mask_init_mode_types[] = { + { + SCULPT_MASK_INIT_RANDOM_PER_VERTEX, + "RANDOM_PER_VERTEX", + 0, + "Random per Vertex", + "", + }, + { + SCULPT_MASK_INIT_RANDOM_PER_FACE_SET, + "RANDOM_PER_FACE_SET", + 0, + "Random per Face Set", + "", + }, + { + SCULPT_MASK_INIT_RANDOM_PER_LOOSE_PART, + "RANDOM_PER_LOOSE_PART", + 0, + "Random per Loose Part", + "", + }, + {0, NULL, 0, NULL, NULL}, +}; + +static void mask_init_task_cb(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + PBVHNode *node = data->nodes[i]; + PBVHVertexIter vd; + const int mode = data->mask_init_mode; + const int seed = data->mask_init_seed; + SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_MASK); + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + switch (mode) { + case SCULPT_MASK_INIT_RANDOM_PER_VERTEX: + *vd.mask = BLI_hash_int_01(vd.index + seed); + break; + case SCULPT_MASK_INIT_RANDOM_PER_FACE_SET: { + const int face_set = SCULPT_vertex_face_set_get(ss, vd.index); + *vd.mask = BLI_hash_int_01(face_set + seed); + break; + } + case SCULPT_MASK_INIT_RANDOM_PER_LOOSE_PART: + *vd.mask = BLI_hash_int_01(ss->vertex_info.connected_component[vd.index] + seed); + break; + } + } + BKE_pbvh_vertex_iter_end; + BKE_pbvh_node_mark_update_mask(data->nodes[i]); +} + +static int sculpt_mask_init_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + + const int mode = RNA_enum_get(op->ptr, "mode"); + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + + PBVH *pbvh = ob->sculpt->pbvh; + PBVHNode **nodes; + int totnode; + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + + if (totnode == 0) { + return OPERATOR_CANCELLED; + } + + SCULPT_undo_push_begin(ob, "init mask"); + + if (mode == SCULPT_MASK_INIT_RANDOM_PER_LOOSE_PART) { + SCULPT_connected_components_ensure(ob); + } + + SculptThreadedTaskData data = { + .ob = ob, + .nodes = nodes, + .mask_init_mode = mode, + .mask_init_seed = PIL_check_seconds_timer(), + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, mask_init_task_cb, &settings); + + multires_stitch_grids(ob); + + SCULPT_undo_push_end(); + + BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask); + MEM_SAFE_FREE(nodes); + SCULPT_tag_update_overlays(C); + return OPERATOR_FINISHED; +} + +void SCULPT_OT_mask_init(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Init Mask"; + ot->description = "Creates a new mask for the entire mesh"; + ot->idname = "SCULPT_OT_mask_init"; + + /* api callbacks */ + ot->exec = sculpt_mask_init_exec; + ot->poll = SCULPT_mode_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + RNA_def_enum(ot->srna, + "mode", + prop_sculpt_mask_init_mode_types, + SCULPT_MASK_INIT_RANDOM_PER_VERTEX, + "Mode", + ""); +} diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c index cfc31e1dcdd..f78f30a2cfd 100644 --- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c +++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c @@ -85,8 +85,7 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata, test_radius *= brush->normal_radius_factor; test.radius_squared = test_radius * test_radius; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -166,8 +165,7 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index 5fdf8415f28..c3977b28178 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -85,8 +85,7 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -139,8 +138,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, BKE_brush_color_get(ss->scene, brush)); IMB_colormanagement_srgb_to_scene_linear_v3(brush_color); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); bool affect_vertex = false; @@ -227,8 +225,7 @@ static void do_sample_wet_paint_task_cb(void *__restrict userdata, test.radius *= data->brush->wet_paint_radius_factor; test.radius_squared = test.radius * test.radius; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -384,8 +381,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -454,8 +450,7 @@ static void do_smear_store_prev_colors_task_cb_exec(void *__restrict userdata, SculptSession *ss = data->ob->sculpt; PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { copy_v4_v4(ss->cache->prev_colors[vd.index], SCULPT_vertex_color_get(ss, vd.index)); } BKE_pbvh_vertex_iter_end; diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c index a85f805894b..4d2a1bf13dc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.c +++ b/source/blender/editors/sculpt_paint/sculpt_pose.c @@ -174,8 +174,7 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata, SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); float total_disp[3]; @@ -232,8 +231,7 @@ static void pose_brush_grow_factor_task_cb_ex(void *__restrict userdata, SculptSession *ss = data->ob->sculpt; const char symm = SCULPT_mesh_symmetry_xyz_get(data->ob); PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SculptVertexNeighborIter ni; float max = 0.0f; @@ -605,8 +603,7 @@ static void pose_brush_init_task_cb_ex(void *__restrict userdata, SculptThreadedTaskData *data = userdata; SculptSession *ss = data->ob->sculpt; PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SculptVertexNeighborIter ni; float avg = 0.0f; int total = 0; diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index 4c0795eb0f7..61984610a5a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -229,8 +229,7 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata, ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -312,8 +311,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -473,8 +471,7 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -516,8 +513,7 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex( ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index 4554ea178ab..3c0a591e8a7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -164,8 +164,7 @@ static void sculpt_transform_task_cb(void *__restrict userdata, PBVHVertexIter vd; SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS); - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); float transformed_co[3], orig_co[3], disp[3]; float *start_co; @@ -335,8 +334,7 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op) if (mode == SCULPT_PIVOT_POSITION_UNMASKED) { for (int n = 0; n < totnode; n++) { PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { const float mask = (vd.mask) ? *vd.mask : 0.0f; if (mask < 1.0f) { if (SCULPT_check_vertex_pivot_symmetry(vd.co, ss->pivot_pos, symm)) { @@ -354,8 +352,7 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op) for (int n = 0; n < totnode; n++) { PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { const float mask = (vd.mask) ? *vd.mask : 0.0f; if (mask < (0.5f + threshold) && mask > (0.5f - threshold)) { if (SCULPT_check_vertex_pivot_symmetry(vd.co, ss->pivot_pos, symm)) { diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index ec103bd2b98..4d063099216 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -1104,8 +1104,7 @@ static void sculpt_undo_store_coords(Object *ob, SculptUndoNode *unode) SculptSession *ss = ob->sculpt; PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, unode->node, vd, PBVH_ITER_ALL) { copy_v3_v3(unode->co[vd.i], vd.co); if (vd.no) { copy_v3_v3_short(unode->no[vd.i], vd.no); @@ -1147,8 +1146,7 @@ static void sculpt_undo_store_mask(Object *ob, SculptUndoNode *unode) SculptSession *ss = ob->sculpt; PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, unode->node, vd, PBVH_ITER_ALL) { unode->mask[vd.i] = *vd.mask; } BKE_pbvh_vertex_iter_end; @@ -1159,8 +1157,7 @@ static void sculpt_undo_store_color(Object *ob, SculptUndoNode *unode) SculptSession *ss = ob->sculpt; PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, unode->node, vd, PBVH_ITER_ALL) { copy_v4_v4(unode->col[vd.i], vd.col); } BKE_pbvh_vertex_iter_end; @@ -1258,8 +1255,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt case SCULPT_UNDO_MASK: /* Before any vertex values get modified, ensure their * original positions are logged. */ - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset); } BKE_pbvh_vertex_iter_end; @@ -1268,8 +1264,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt case SCULPT_UNDO_HIDDEN: { GSetIterator gs_iter; GSet *faces = BKE_pbvh_bmesh_node_faces(node); - BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) - { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset); } BKE_pbvh_vertex_iter_end; @@ -1492,7 +1487,8 @@ static void sculpt_undosys_step_decode_redo_impl(struct bContext *C, static void sculpt_undosys_step_decode_undo(struct bContext *C, Depsgraph *depsgraph, - SculptUndoStep *us) + SculptUndoStep *us, + const bool is_final) { SculptUndoStep *us_iter = us; while (us_iter->step.next && (us_iter->step.next->type == us_iter->step.type)) { @@ -1501,8 +1497,12 @@ static void sculpt_undosys_step_decode_undo(struct bContext *C, } us_iter = (SculptUndoStep *)us_iter->step.next; } - while (us_iter != us) { + + while ((us_iter != us) || (!is_final && us_iter == us)) { sculpt_undosys_step_decode_undo_impl(C, depsgraph, us_iter); + if (us_iter == us) { + break; + } us_iter = (SculptUndoStep *)us_iter->step.prev; } } @@ -1527,11 +1527,8 @@ static void sculpt_undosys_step_decode_redo(struct bContext *C, } } -static void sculpt_undosys_step_decode(struct bContext *C, - struct Main *bmain, - UndoStep *us_p, - const eUndoStepDir dir, - bool UNUSED(is_final)) +static void sculpt_undosys_step_decode( + struct bContext *C, struct Main *bmain, UndoStep *us_p, const eUndoStepDir dir, bool is_final) { BLI_assert(dir != STEP_INVALID); @@ -1574,7 +1571,7 @@ static void sculpt_undosys_step_decode(struct bContext *C, SculptUndoStep *us = (SculptUndoStep *)us_p; if (dir == STEP_UNDO) { - sculpt_undosys_step_decode_undo(C, depsgraph, us); + sculpt_undosys_step_decode_undo(C, depsgraph, us, is_final); } else if (dir == STEP_REDO) { sculpt_undosys_step_decode_redo(C, depsgraph, us); diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index b82290205c7..bd6b15bdf74 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -962,7 +962,7 @@ void FILE_OT_select_all(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Select All Operator +/** \name View Selected Operator * \{ */ static int file_view_selected_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 83bb8abf5d8..993b1d9b69c 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -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 */ diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index bb0cd754c7b..f64ce771b25 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -37,6 +37,7 @@ #include "BLI_blenlib.h" #include "BLI_map.hh" #include "BLI_math.h" +#include "BLI_set.hh" #include "BLI_span.hh" #include "BLI_string_ref.hh" #include "BLI_vector.hh" @@ -81,6 +82,7 @@ # include "COM_compositor.h" #endif +using blender::Set; using blender::Span; using blender::Vector; @@ -1746,10 +1748,11 @@ static void count_mutli_input_socket_links(bNodeTree *ntree, SpaceNode *snode) LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { LISTBASE_FOREACH (struct bNodeSocket *, socket, &node->inputs) { if (socket->flag & SOCK_MULTI_INPUT) { + Set<bNodeSocket *> visited_from_sockets; socket->total_inputs = 0; LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { if (link->tosock == socket) { - socket->total_inputs++; + visited_from_sockets.add(link->fromsock); } } /* Count temporary links going into this socket. */ @@ -1757,10 +1760,11 @@ static void count_mutli_input_socket_links(bNodeTree *ntree, SpaceNode *snode) LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { bNodeLink *link = (bNodeLink *)linkdata->data; if (link->tosock == socket) { - socket->total_inputs++; + visited_from_sockets.add(link->fromsock); } } } + socket->total_inputs = visited_from_sockets.size(); } } } diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index 963349f876b..9293494a16a 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -890,10 +890,24 @@ static void node_link_find_socket(bContext *C, wmOperator *op, float cursor[2]) continue; } + /* Skip if tsock is already linked with this output. */ + bNodeLink *existing_link_connected_to_fromsock = NULL; + LISTBASE_FOREACH (bNodeLink *, existing_link, &snode->edittree->links) { + if (existing_link->fromsock == link->fromsock && existing_link->tosock == tsock) { + existing_link_connected_to_fromsock = existing_link; + break; + } + } + /* attach links to the socket */ link->tonode = tnode; link->tosock = tsock; snode->runtime->last_node_hovered_while_dragging_a_link = tnode; + if (existing_link_connected_to_fromsock) { + link->multi_input_socket_index = + existing_link_connected_to_fromsock->multi_input_socket_index; + continue; + } sort_multi_input_socket_links(snode, tnode, link, cursor); } } diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c index d54e35f659c..062d2e2b5d1 100644 --- a/source/blender/editors/space_outliner/outliner_collections.c +++ b/source/blender/editors/space_outliner/outliner_collections.c @@ -49,8 +49,6 @@ #include "RNA_define.h" #include "RNA_enum_types.h" -#include "UI_resources.h" - #include "outliner_intern.h" /* own include */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index 01fb0fc6f78..b3b36811411 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -26,28 +26,21 @@ #include "MEM_guardedalloc.h" #include "DNA_collection_types.h" -#include "DNA_constraint_types.h" #include "DNA_material_types.h" -#include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "DNA_space_types.h" #include "BLI_listbase.h" -#include "BLI_string.h" #include "BLT_translation.h" #include "BKE_collection.h" -#include "BKE_constraint.h" #include "BKE_context.h" #include "BKE_layer.h" -#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_object.h" #include "BKE_report.h" -#include "BKE_scene.h" -#include "BKE_shader_fx.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" @@ -57,12 +50,9 @@ #include "ED_screen.h" #include "UI_interface.h" -#include "UI_resources.h" #include "UI_view2d.h" #include "RNA_access.h" -#include "RNA_define.h" -#include "RNA_enum_types.h" #include "WM_api.h" #include "WM_types.h" @@ -647,7 +637,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; diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 690adb09570..5b4a45f6115 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -21,7 +21,6 @@ * \ingroup spoutliner */ -#include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_collection_types.h" #include "DNA_constraint_types.h" @@ -45,7 +44,6 @@ #include "BKE_armature.h" #include "BKE_context.h" #include "BKE_deform.h" -#include "BKE_fcurve.h" #include "BKE_gpencil.h" #include "BKE_idtype.h" #include "BKE_layer.h" @@ -56,14 +54,11 @@ #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_report.h" -#include "BKE_scene.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" #include "ED_armature.h" -#include "ED_keyframing.h" -#include "ED_object.h" #include "ED_outliner.h" #include "ED_screen.h" diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 18abe17d515..5501e52d69b 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -28,7 +28,6 @@ #include "DNA_ID.h" #include "DNA_anim_types.h" #include "DNA_collection_types.h" -#include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -43,27 +42,19 @@ #include "BKE_appdir.h" #include "BKE_armature.h" #include "BKE_blender_copybuffer.h" -#include "BKE_collection.h" #include "BKE_context.h" #include "BKE_idtype.h" -#include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_lib_remap.h" #include "BKE_main.h" -#include "BKE_material.h" -#include "BKE_outliner_treehash.h" #include "BKE_report.h" -#include "BKE_scene.h" #include "BKE_workspace.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" -#include "../blenloader/BLO_readfile.h" - #include "ED_keyframing.h" -#include "ED_object.h" #include "ED_outliner.h" #include "ED_screen.h" #include "ED_select_utils.h" @@ -72,7 +63,6 @@ #include "WM_types.h" #include "UI_interface.h" -#include "UI_resources.h" #include "UI_view2d.h" #include "RNA_access.h" diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index d53a37fa60e..50f089f894a 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -27,17 +27,13 @@ #include "DNA_armature_types.h" #include "DNA_collection_types.h" -#include "DNA_constraint_types.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" -#include "DNA_light_types.h" -#include "DNA_material_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_sequence_types.h" #include "DNA_shader_fx_types.h" -#include "DNA_world_types.h" #include "BLI_listbase.h" #include "BLI_utildefines.h" @@ -52,19 +48,15 @@ #include "BKE_main.h" #include "BKE_modifier.h" #include "BKE_object.h" -#include "BKE_paint.h" #include "BKE_particle.h" #include "BKE_report.h" -#include "BKE_scene.h" #include "BKE_shader_fx.h" -#include "BKE_workspace.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" #include "ED_armature.h" #include "ED_buttons.h" -#include "ED_gpencil.h" #include "ED_object.h" #include "ED_outliner.h" #include "ED_screen.h" @@ -76,7 +68,6 @@ #include "SEQ_sequencer.h" #include "WM_api.h" -#include "WM_toolsystem.h" #include "WM_types.h" #include "UI_interface.h" diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 9af2ba6a82b..d9641930134 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -776,6 +776,11 @@ static void object_proxy_to_override_convert_fn(bContext *C, typedef struct OutlinerLibOverrideData { bool do_hierarchy; + /** + * For resync operation, force keeping newly created override IDs (or original linked IDs) + * instead of re-applying relevant existing ID pointer property override operations. Helps + * solving broken overrides while not losing *all* of your overrides. */ + bool do_resync_hierarchy_enforce; } OutlinerLibOverrideData; static void id_override_library_create_fn(bContext *C, @@ -872,10 +877,12 @@ static void id_override_library_resync_fn(bContext *C, TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, - void *UNUSED(user_data)) + void *user_data) { BLI_assert(TSE_IS_REAL_ID(tselem)); ID *id_root = tselem->id; + OutlinerLibOverrideData *data = user_data; + const bool do_hierarchy_enforce = data->do_resync_hierarchy_enforce; if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) { Main *bmain = CTX_data_main(C); @@ -893,7 +900,8 @@ static void id_override_library_resync_fn(bContext *C, te->store_elem->id->tag |= LIB_TAG_DOIT; } - BKE_lib_override_library_resync(bmain, scene, CTX_data_view_layer(C), id_root); + BKE_lib_override_library_resync( + bmain, scene, CTX_data_view_layer(C), id_root, do_hierarchy_enforce); WM_event_add_notifier(C, NC_WINDOW, NULL); } @@ -1710,6 +1718,7 @@ typedef enum eOutlinerIdOpTypes { OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY, + OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE, OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY, OUTLINER_IDOP_SINGLE, OUTLINER_IDOP_DELETE, @@ -1770,6 +1779,13 @@ static const EnumPropertyItem prop_id_op_types[] = { "Resync Library Override Hierarchy", "Rebuild this local override from its linked reference, as well as its hierarchy of " "dependencies"}, + {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE, + "OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE", + 0, + "Resync Library Override Hierarchy Enforce", + "Rebuild this local override from its linked reference, as well as its hierarchy of " + "dependencies, enforcing that hierarchy to match the linked data (i.e. ignoring exiting " + "overrides on data-blocks pointer properties)"}, {OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY, "OVERRIDE_LIBRARY_DELETE_HIERARCHY", 0, @@ -1831,6 +1847,7 @@ static bool outliner_id_operation_item_poll(bContext *C, case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: + case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE: case OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY: if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id)) { return true; @@ -2041,6 +2058,18 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) ED_undo_push(C, "Resync Overridden Data Hierarchy"); break; } + case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE: { + outliner_do_libdata_operation( + C, + op->reports, + scene, + space_outliner, + &space_outliner->tree, + id_override_library_resync_fn, + &(OutlinerLibOverrideData){.do_hierarchy = true, .do_resync_hierarchy_enforce = true}); + ED_undo_push(C, "Resync Overridden Data Hierarchy"); + break; + } case OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY: { outliner_do_libdata_operation(C, op->reports, diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 6319b890b3b..573fb492613 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -62,23 +62,14 @@ #include "BLT_translation.h" #include "BKE_armature.h" -#include "BKE_fcurve_driver.h" -#include "BKE_idtype.h" #include "BKE_layer.h" #include "BKE_lib_id.h" -#include "BKE_lib_override.h" #include "BKE_main.h" #include "BKE_modifier.h" #include "BKE_outliner_treehash.h" -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_build.h" - #include "ED_screen.h" -#include "WM_api.h" -#include "WM_types.h" - #include "RNA_access.h" #include "UI_interface.h" @@ -889,6 +880,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 */ diff --git a/source/blender/editors/space_outliner/tree/tree_element.cc b/source/blender/editors/space_outliner/tree/tree_element.cc index 25bd9a53cf8..b0508e10671 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.cc +++ b/source/blender/editors/space_outliner/tree/tree_element.cc @@ -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: diff --git a/source/blender/editors/space_outliner/tree/tree_element_anim_data.cc b/source/blender/editors/space_outliner/tree/tree_element_anim_data.cc index 5a9568ea906..c0fef7c98e2 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_anim_data.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_anim_data.cc @@ -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 */ diff --git a/source/blender/editors/space_outliner/tree/tree_element_anim_data.hh b/source/blender/editors/space_outliner/tree/tree_element_anim_data.hh index 8114277b6d6..95d08cd20b7 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_anim_data.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_anim_data.hh @@ -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; diff --git a/source/blender/editors/space_outliner/tree/tree_element_nla.cc b/source/blender/editors/space_outliner/tree/tree_element_nla.cc index 5d4ec53e60c..65832e8f981 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_nla.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_nla.cc @@ -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 diff --git a/source/blender/editors/space_outliner/tree/tree_element_nla.hh b/source/blender/editors/space_outliner/tree/tree_element_nla.hh index c94287ce576..7cbc8689483 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_nla.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_nla.hh @@ -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 diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index 53424c60d59..8ab1baaaea3 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -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); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.cc index 8459973ed4a..35dc9d62aa3 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.cc @@ -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>( diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index 36da0791c4f..17f60dfc210 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -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)) diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c index cce9287679c..72c62321e88 100644 --- a/source/blender/editors/space_view3d/view3d_snap.c +++ b/source/blender/editors/space_view3d/view3d_snap.c @@ -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; @@ -910,10 +910,9 @@ static bool snap_calc_active_center(bContext *C, const bool select_only, float r static int snap_curs_to_active_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); - 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; diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 24335b6b6b7..4b43592165b 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -429,6 +429,20 @@ typedef struct TransCustomDataContainer { } TransCustomDataContainer; #define TRANS_CUSTOM_DATA_ELEM_MAX (sizeof(TransCustomDataContainer) / sizeof(TransCustomData)) +/** + * Container for Transform Data + * + * Used to implement multi-object modes, so each object can have it's + * own data array as well as object matrix, local center etc. + * + * Anything that can't be shared between all objects + * and doesn't make sense to store for every vertex (in the #TransDataContainer.data). + * + * \note at some point this could be used to store non object containers + * although this only makes sense if each container has it's own matrices, + * otherwise all elements may as well be stored in one array (#TransDataContainer.data), + * as is already done for curve-objects, f-curves. etc. + */ typedef struct TransDataContainer { /** Transformed data (array). */ TransData *data; diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 01c00247a7a..9b5f15a2574 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -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; } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c index b2a83e83c9e..ff493258b9b 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c @@ -265,7 +265,7 @@ GpencilModifierTypeInfo modifierType_Gpencil_Lattice = { /* structName */ "LatticeGpencilModifierData", /* structSize */ sizeof(LatticeGpencilModifierData), /* type */ eGpencilModifierTypeType_Gpencil, - /* flags */ eGpencilModifierTypeFlag_Single | eGpencilModifierTypeFlag_SupportsEditmode, + /* flags */ eGpencilModifierTypeFlag_SupportsEditmode, /* copyData */ copyData, diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index c708219cfe8..077f9bf8bdc 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -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. */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index b18add0a826..0febbe26596 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -694,7 +694,7 @@ typedef enum eSpaceSeq_OverlayType { * custom library. Otherwise idname is not used. */ typedef struct FileSelectAssetLibraryUID { - short type; + short type; /* eFileAssetLibrary_Type */ char _pad[2]; /** * If showing a custom asset library (#FILE_ASSET_LIBRARY_CUSTOM), this is the index of the @@ -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; /** \} */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index bd8f3cd95a7..233c476cbf2 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -635,6 +635,7 @@ typedef struct UserDef_FileSpaceData { typedef struct UserDef_Experimental { /* Debug options, always available. */ char use_undo_legacy; + char no_override_auto_resync; char use_cycles_debug; char SANITIZE_AFTER_HERE; /* The following options are automatically sanitized (set to 0) @@ -645,7 +646,7 @@ typedef struct UserDef_Experimental { char use_switch_object_operator; char use_sculpt_tools_tilt; char use_asset_browser; - char _pad[7]; + char _pad[6]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 74b8517f538..4c60ffe4f16 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -1512,11 +1512,21 @@ bool RNA_struct_override_store(struct Main *bmain, PointerRNA *ptr_storage, struct IDOverrideLibrary *override); +typedef enum eRNAOverrideApplyFlag { + RNA_OVERRIDE_APPLY_FLAG_NOP = 0, + /** + * Hack to work around/fix older broken overrides: Do not apply override operations affecting ID + * pointers properties, unless the destination original value (the one being overridden) is NULL. + */ + RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS = 1 << 0, +} eRNAOverrideApplyFlag; + void RNA_struct_override_apply(struct Main *bmain, struct PointerRNA *ptr_dst, struct PointerRNA *ptr_src, struct PointerRNA *ptr_storage, - struct IDOverrideLibrary *override); + struct IDOverrideLibrary *override, + const eRNAOverrideApplyFlag flag); struct IDOverrideLibraryProperty *RNA_property_override_property_find(struct Main *bmain, PointerRNA *ptr, diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index 0f3b0a895db..28d4cc4d075 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -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; } @@ -1127,7 +1123,8 @@ void RNA_struct_override_apply(Main *bmain, PointerRNA *ptr_dst, PointerRNA *ptr_src, PointerRNA *ptr_storage, - IDOverrideLibrary *override) + IDOverrideLibrary *override, + const eRNAOverrideApplyFlag flag) { #ifdef DEBUG_OVERRIDE_TIMEIT TIMEIT_START_AVERAGED(RNA_struct_override_apply); @@ -1158,6 +1155,72 @@ 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); + } + } + + /* Workaround for older broken overrides, we then assume that non-matching ID pointers + * override operations that replace a non-NULL value are 'mistakes', and ignore (do not + * apply) them. */ + if ((flag & RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS) != 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_dst = RNA_property_pointer_get(&data_dst, prop_dst); + if (prop_ptr_dst.type != NULL && RNA_struct_is_ID(prop_ptr_dst.type)) { +#ifndef NDEBUG + PointerRNA prop_ptr_src = RNA_property_pointer_get(&data_src, prop_src); + BLI_assert(prop_ptr_src.type == NULL || RNA_struct_is_ID(prop_ptr_src.type)); +#endif + ID *id_dst = rna_property_override_property_real_id_owner( + bmain, &prop_ptr_dst, NULL, NULL); + + if (id_dst != NULL) { + CLOG_INFO(&LOG, + 3, + "%s: Ignoring local override on ID pointer property '%s', as requested by " + "RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS flag", + ptr_dst->owner_id->name, + op->rna_path); + continue; + } + } + } + rna_property_override_apply_ex(bmain, &data_dst, &data_src, diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c index d553ead1e45..d8336e79064 100644 --- a/source/blender/makesrna/intern/rna_rna.c +++ b/source/blender/makesrna/intern/rna_rna.c @@ -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, diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 574c4e98819..9e8e9030925 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -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) diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 4097e2dddea..e07d46dbe3e 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6245,6 +6245,14 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) "Undo Legacy", "Use legacy undo (slower than the new default one, but may be more stable in some cases)"); + prop = RNA_def_property(srna, "override_auto_resync", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "no_override_auto_resync", 1); + RNA_def_property_ui_text( + prop, + "Override Auto Resync", + "Enable library overrides automatic resync detection and process on file load. Disable when " + "dealing with older .blend files that need manual Resync (Enforce) handling"); + prop = RNA_def_property(srna, "use_new_point_cloud_type", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_new_point_cloud_type", 1); RNA_def_property_ui_text( diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 877b15f5010..f8242c87bd3 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -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}; diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c index 487250eb4e3..3162a33edc2 100644 --- a/source/blender/modifiers/intern/MOD_uvproject.c +++ b/source/blender/modifiers/intern/MOD_uvproject.c @@ -337,6 +337,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA ob_projector = RNA_pointer_get(&projector_ptr, "object"); if (!RNA_pointer_is_null(&ob_projector) && RNA_enum_get(&ob_projector, "type") == OB_CAMERA) { has_camera = true; + break; } } RNA_END; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 743aaad84a2..cdf18f21f8a 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -154,6 +154,7 @@ set(SRC geometry/nodes/node_geo_attribute_sample_texture.cc geometry/nodes/node_geo_attribute_separate_xyz.cc geometry/nodes/node_geo_attribute_vector_math.cc + geometry/nodes/node_geo_attribute_remove.cc geometry/nodes/node_geo_boolean.cc geometry/nodes/node_geo_collection_info.cc geometry/nodes/node_geo_common.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 29d70dc06a9..2f3bd66859d 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -37,6 +37,7 @@ void register_node_type_geo_attribute_proximity(void); void register_node_type_geo_attribute_randomize(void); void register_node_type_geo_attribute_separate_xyz(void); void register_node_type_geo_attribute_vector_math(void); +void register_node_type_geo_attribute_remove(void); void register_node_type_geo_boolean(void); void register_node_type_geo_collection_info(void); void register_node_type_geo_edge_split(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index c5a1ec1a786..cc05c221820 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -299,6 +299,7 @@ DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_T DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "ATTRIBUTE_COMBINE_XYZ", AttributeCombineXYZ, "Attribute Combine XYZ", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "ATTRIBUTE_SEPARATE_XYZ", AttributeSeparateXYZ, "Attribute Separate XYZ", "") DefNode(GeometryNode, GEO_NODE_SUBDIVIDE, 0, "SUBDIVIDE", Subdivide, "Subdivide", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Circle", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc new file mode 100644 index 00000000000..bf9bda67045 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc @@ -0,0 +1,71 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_attribute_remove_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Attribute")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_attribute_remove_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void remove_attribute(GeometryComponent &component, const GeoNodeExecParams ¶ms) +{ + const std::string attribute_name = params.get_input<std::string>("Attribute"); + if (attribute_name.empty()) { + return; + } + + if (!component.attribute_try_delete(attribute_name)) { + params.error_message_add(NodeWarningType::Error, + TIP_("Cannot delete attribute with name \"") + attribute_name + "\""); + } +} + +static void geo_node_attribute_remove_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + geometry_set = geometry_set_realize_instances(geometry_set); + + if (geometry_set.has<MeshComponent>()) { + remove_attribute(geometry_set.get_component_for_write<MeshComponent>(), params); + } + if (geometry_set.has<PointCloudComponent>()) { + remove_attribute(geometry_set.get_component_for_write<PointCloudComponent>(), params); + } + + params.set_output("Geometry", geometry_set); +} +} // namespace blender::nodes + +void register_node_type_geo_attribute_remove() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_ATTRIBUTE_REMOVE, "Attribute Remove", NODE_CLASS_ATTRIBUTE, 0); + node_type_socket_templates(&ntype, geo_node_attribute_remove_in, geo_node_attribute_remove_out); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_remove_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc index b5731851229..06c5586a3ff 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivide.cc @@ -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); diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 775e16d0a29..a66b7b1d2fe 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -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); } } } diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc index a1ef65a5655..495c8d12824 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.cc +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -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; diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c index 1ee14df24cf..96ff6a111d9 100644 --- a/source/blender/python/intern/bpy_library_load.c +++ b/source/blender/python/intern/bpy_library_load.c @@ -118,8 +118,8 @@ static PyTypeObject bpy_lib_Type = { NULL, /* reprfunc tp_str; */ /* will only use these if this is a subtype of a py class */ - NULL /*PyObject_GenericGetAttr is assigned later */, /* getattrofunc tp_getattro; */ - NULL, /* setattrofunc tp_setattro; */ + PyObject_GenericGetAttr, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ /* Functions to access object as input/output buffer */ NULL, /* PyBufferProcs *tp_as_buffer; */ @@ -498,10 +498,6 @@ PyMethodDef BPY_library_load_method_def = { int BPY_library_load_type_ready(void) { - - /* some compilers don't like accessing this directly, delay assignment */ - bpy_lib_Type.tp_getattro = PyObject_GenericGetAttr; - if (PyType_Ready(&bpy_lib_Type) < 0) { return -1; } diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 7a43c9cb997..fab73d0f3dc 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -7078,13 +7078,9 @@ static PyTypeObject pyrna_prop_collection_iter_Type = { NULL, /* ternaryfunc tp_call; */ NULL, /* reprfunc tp_str; */ -/* will only use these if this is a subtype of a py class */ -# if defined(_MSC_VER) - NULL, /* defer assignment */ -# else + /* will only use these if this is a subtype of a py class */ PyObject_GenericGetAttr, /* getattrofunc tp_getattro; */ -# endif - NULL, /* setattrofunc tp_setattro; */ + NULL, /* setattrofunc tp_setattro; */ /* Functions to access object as input/output buffer */ NULL, /* PyBufferProcs *tp_as_buffer; */ @@ -7110,13 +7106,9 @@ static PyTypeObject pyrna_prop_collection_iter_Type = { # else 0, # endif -/*** Added in release 2.2 ***/ -/* Iterators */ -# if defined(_MSC_VER) - NULL, /* defer assignment */ -# else - PyObject_SelfIter, /* getiterfunc tp_iter; */ -# endif + /*** Added in release 2.2 ***/ + /* Iterators */ + PyObject_SelfIter, /* getiterfunc tp_iter; */ (iternextfunc)pyrna_prop_collection_iter_next, /* iternextfunc tp_iternext; */ /*** Attribute descriptor and subclassing stuff ***/ @@ -7640,9 +7632,6 @@ void BPY_rna_init(void) /* For some reason MSVC complains of these. */ #if defined(_MSC_VER) pyrna_struct_meta_idprop_Type.tp_base = &PyType_Type; - - pyrna_prop_collection_iter_Type.tp_iter = PyObject_SelfIter; - pyrna_prop_collection_iter_Type.tp_getattro = PyObject_GenericGetAttr; #endif /* metaclass */ diff --git a/source/blender/python/intern/bpy_rna_data.c b/source/blender/python/intern/bpy_rna_data.c index 3771cc05490..daab1631e8e 100644 --- a/source/blender/python/intern/bpy_rna_data.c +++ b/source/blender/python/intern/bpy_rna_data.c @@ -100,8 +100,8 @@ static PyTypeObject bpy_rna_data_context_Type = { NULL, /* reprfunc tp_str; */ /* will only use these if this is a subtype of a py class */ - NULL /*PyObject_GenericGetAttr is assigned later */, /* getattrofunc tp_getattro; */ - NULL, /* setattrofunc tp_setattro; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ /* Functions to access object as input/output buffer */ NULL, /* PyBufferProcs *tp_as_buffer; */ diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index b46a354c7ae..385a572ab85 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -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) diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index c5a429d7839..b66544831f1 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -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); } } diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 470952956c8..646aa71025c 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -577,6 +577,13 @@ void wm_event_do_notifiers(bContext *C) } ED_screen_areas_iter (win, screen, area) { + if ((note->category == NC_SPACE) && note->reference) { + /* Filter out notifiers sent to other spaces. RNA sets the reference to the owning ID + * though, the screen, so let notifiers through that reference the entire screen. */ + if ((note->reference != area->spacedata.first) && (note->reference != screen)) { + continue; + } + } wmSpaceTypeListenerParams area_params = { .window = win, .area = area, @@ -1319,7 +1326,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 +3197,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 +3208,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) */ diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 13f3afdfc4c..227520ed3f9 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.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,36 @@ 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); + + /* Don't leave the old pointer in the context. */ + CTX_wm_manager_set(C, wm); + } + bool has_match = false; /* this code could move to setup_appdata */ @@ -713,8 +744,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 +751,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; - - /* put aside screens to match with persistent windows later */ - /* also exit screens and editors */ - wm_window_match_init(C, &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, + }; + + struct BlendFileData *bfd = BKE_blendfile_read(filepath, ¶ms, reports); + if (bfd != NULL) { + wm_file_read_pre(C, use_data, use_userdef); + + /* Put aside screens to match with persistent windows later, + * also exit screens and editors. */ + ListBase wmbase; + wm_window_match_init(C, &wmbase); + + /* 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; + + BKE_blendfile_read_setup(C, bfd, ¶ms, reports); + + 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); + } - /* 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); - - /* BKE_file_read sets new Main into context. */ - Main *bmain = CTX_data_main(C); - - /* 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_result_setup 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. */ - 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); - } + /* 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 */ + /* 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 +981,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 +1075,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 +1106,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); diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 0e0d66d40a9..7316c1729f5 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -869,6 +869,8 @@ static const char arg_handle_log_set_doc[] = "\tEnable logging categories, taking a single comma separated argument.\n" "\tMultiple categories can be matched using a '.*' suffix,\n" "\tso '--log \"wm.*\"' logs every kind of window-manager message.\n" + "\tSub-string can be matched using a '*' prefix and suffix,\n" + "\tso '--log \"*undo*\"' logs every kind of undo-related message.\n" "\tUse \"^\" prefix to ignore, so '--log \"*,^wm.operator.*\"' logs all except for " "'wm.operators.*'\n" "\tUse \"*\" to log everything."; |