Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2021-03-13 17:14:22 +0300
committerHans Goudey <h.goudey@me.com>2021-03-13 17:14:22 +0300
commit9b2b6674ba18cedc836a7b50450485334d6e028d (patch)
treec72ac9dd193e333f1c5ccfd427cdee3418a310e8
parentcec588d757c7e442c86aae6d59897ac66a4cc587 (diff)
parent258b15da74ad6f734d4e9870bbe051066a9a705c (diff)
Merge branch 'master' into geometry-nodes-mesh-primitives
-rw-r--r--.clang-format1
-rwxr-xr-xbuild_files/utils/make_source_archive.py13
-rw-r--r--extern/mantaflow/CMakeLists.txt4
-rw-r--r--intern/clog/clog.c14
-rw-r--r--intern/cycles/blender/addon/properties.py5
-rw-r--r--intern/cycles/bvh/bvh_optix.cpp4
-rw-r--r--intern/cycles/device/cuda/device_cuda_impl.cpp12
-rw-r--r--intern/cycles/device/device_cpu.cpp5
-rw-r--r--intern/cycles/device/device_denoising.h3
-rw-r--r--intern/cycles/device/device_memory.h4
-rw-r--r--intern/cycles/device/device_optix.cpp23
-rw-r--r--intern/cycles/render/alembic.cpp310
-rw-r--r--intern/cycles/render/alembic.h111
-rw-r--r--intern/cycles/render/geometry.cpp2
-rw-r--r--intern/utfconv/utfconv.h24
-rw-r--r--release/scripts/startup/bl_operators/presets.py2
-rw-r--r--release/scripts/startup/bl_operators/spreadsheet.py2
-rw-r--r--release/scripts/startup/bl_ui/properties_particle.py2
-rw-r--r--release/scripts/startup/bl_ui/space_spreadsheet.py3
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py1
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py57
-rw-r--r--release/scripts/startup/nodeitems_builtins.py1
-rw-r--r--source/blender/blenkernel/BKE_animsys.h2
-rw-r--r--source/blender/blenkernel/BKE_blendfile.h53
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h9
-rw-r--r--source/blender/blenkernel/BKE_lib_query.h10
-rw-r--r--source/blender/blenkernel/BKE_lib_remap.h9
-rw-r--r--source/blender/blenkernel/BKE_node.h13
-rw-r--r--source/blender/blenkernel/intern/blender_undo.c14
-rw-r--r--source/blender/blenkernel/intern/blendfile.c140
-rw-r--r--source/blender/blenkernel/intern/lib_id_delete.c18
-rw-r--r--source/blender/blenkernel/intern/lib_override.c278
-rw-r--r--source/blender/blenkernel/intern/lib_query.c9
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c41
-rw-r--r--source/blender/blenkernel/intern/node.cc1
-rw-r--r--source/blender/blenkernel/intern/pbvh.c9
-rw-r--r--source/blender/blenloader/intern/readfile.c39
-rw-r--r--source/blender/compositor/intern/COM_Debug.cc24
-rw-r--r--source/blender/compositor/intern/COM_WorkScheduler.cc4
-rw-r--r--source/blender/draw/engines/eevee/eevee_effects.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightprobes.c6
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c3
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h6
-rw-r--r--source/blender/draw/engines/eevee/eevee_renderpasses.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_screen_raytrace.c36
-rw-r--r--source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl11
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl4
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_lut_frag.glsl10
-rw-r--r--source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl74
-rw-r--r--source/blender/draw/engines/eevee/shaders/btdf_lut_frag.glsl4
-rw-r--r--source/blender/draw/engines/eevee/shaders/common_uniforms_lib.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_ssr_frag.glsl557
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl7
-rw-r--r--source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl31
-rw-r--r--source/blender/draw/engines/eevee/shaders/ssr_lib.glsl19
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_lib.glsl7
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c3
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c9
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c16
-rw-r--r--source/blender/editors/interface/interface_context_menu.c6
-rw-r--r--source/blender/editors/interface/interface_widgets.c4
-rw-r--r--source/blender/editors/object/object_bake_api.c9
-rw-r--r--source/blender/editors/object/object_relations.c4
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt1
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c12
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c24
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c106
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_boundary.c18
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c12
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_expand.c12
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.c6
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c3
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mask.c9
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c6
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h8
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_mask_expand.c9
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_mask_init.c195
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c6
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_paint_color.c15
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_pose.c9
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_smooth.c12
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_transform.c9
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c33
-rw-r--r--source/blender/editors/space_file/file_ops.c2
-rw-r--r--source/blender/editors/space_file/space_file.c34
-rw-r--r--source/blender/editors/space_node/node_draw.cc8
-rw-r--r--source/blender/editors/space_node/node_relationships.c14
-rw-r--r--source/blender/editors/space_outliner/outliner_collections.c2
-rw-r--r--source/blender/editors/space_outliner/outliner_dragdrop.c12
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.c5
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.c10
-rw-r--r--source/blender/editors/space_outliner/outliner_select.c9
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c33
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c13
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element.cc19
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_anim_data.cc4
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_anim_data.hh2
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_nla.cc4
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_nla.hh2
-rw-r--r--source/blender/editors/space_spreadsheet/space_spreadsheet.cc2
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_from_geometry.cc147
-rw-r--r--source/blender/editors/space_view3d/view3d_buttons.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_snap.c5
-rw-r--r--source/blender/editors/transform/transform.h14
-rw-r--r--source/blender/editors/transform/transform_ops.c5
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c2
-rw-r--r--source/blender/makesdna/DNA_ID.h4
-rw-r--r--source/blender/makesdna/DNA_space_types.h9
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h3
-rw-r--r--source/blender/makesrna/RNA_access.h12
-rw-r--r--source/blender/makesrna/intern/rna_access_compare_override.c75
-rw-r--r--source/blender/makesrna/intern/rna_rna.c17
-rw-r--r--source/blender/makesrna/intern/rna_space.c72
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c8
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc4
-rw-r--r--source/blender/modifiers/intern/MOD_uvproject.c1
-rw-r--r--source/blender/nodes/CMakeLists.txt1
-rw-r--r--source/blender/nodes/NOD_geometry.h1
-rw-r--r--source/blender/nodes/NOD_static_types.h1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc71
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivide.cc3
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_value.cc2
-rw-r--r--source/blender/python/intern/bpy_library_load.c8
-rw-r--r--source/blender/python/intern/bpy_rna.c21
-rw-r--r--source/blender/python/intern/bpy_rna_data.c4
-rw-r--r--source/blender/windowmanager/WM_types.h3
-rw-r--r--source/blender/windowmanager/intern/wm.c6
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c13
-rw-r--r--source/blender/windowmanager/intern/wm_files.c166
-rw-r--r--source/creator/creator_args.c2
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 &params_,
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, &params, NULL);
+ if (bfd != NULL) {
+ BKE_blendfile_read_setup(C, bfd, &params, 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, &params, NULL);
+ struct BlendFileData *bfd = BKE_blendfile_read_from_memfile(
+ bmain, &mfu->memfile, &params, NULL);
+ if (bfd != NULL) {
+ BKE_blendfile_read_setup(C, bfd, &params, 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(
&params, 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(&params, 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 &params)
+{
+ 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, &params, 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, &params, 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, &params, NULL);
+ if (bfd != NULL) {
+ BKE_blendfile_read_setup_ex(
+ C, bfd, &params, 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, &params, NULL);
+ if (bfd != NULL) {
+ BKE_blendfile_read_setup_ex(C, bfd, &params, 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.";