diff options
author | YimingWu <xp8110@outlook.com> | 2019-11-02 08:53:39 +0300 |
---|---|---|
committer | YimingWu <xp8110@outlook.com> | 2019-11-02 08:53:39 +0300 |
commit | c30aaff302e0d64ca11939a18d00460c3e656293 (patch) | |
tree | 4d638ef7d6de3dfb90b0086d658c079a9e851ca9 | |
parent | b475daeb04098ecdad22ec91cae9d5fefdffca38 (diff) | |
parent | 9da7a0f8bf1403b96452d58f232f47adbbe0876b (diff) |
Merge remote-tracking branch 'origin/master' into temp-npr-gpencil-modifiers
77 files changed, 1318 insertions, 557 deletions
diff --git a/GNUmakefile b/GNUmakefile index e52fd38a7e3..2c7856db7b4 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -118,7 +118,7 @@ Utilities Example make icons_geom BLENDER_BIN=/path/to/blender - * tgz: + * source_archive: Create a compressed archive of the source code. * update: @@ -525,8 +525,8 @@ check_descriptions: .FORCE # Utilities # -tgz: .FORCE - ./build_files/utils/build_tgz.sh +source_archive: .FORCE + ./build_files/utils/make_source_archive.sh INKSCAPE_BIN?="inkscape" icons: .FORCE diff --git a/build_files/utils/build_tgz.sh b/build_files/utils/make_source_archive.sh index 63c3264b18e..cafd1c31486 100755 --- a/build_files/utils/build_tgz.sh +++ b/build_files/utils/make_source_archive.sh @@ -20,7 +20,7 @@ else fi MANIFEST="blender-$VERSION-manifest.txt" -TARBALL="blender-$VERSION.tar.gz" +TARBALL="blender-$VERSION.tar.xz" cd "$blender_srcdir" @@ -51,19 +51,26 @@ echo "OK" # Create the tarball +# +# Without owner/group args, extracting the files as root will +# use ownership from the tar archive. cd "$blender_srcdir" echo -n "Creating archive: \"$BASE_DIR/$TARBALL\" ..." -tar --transform "s,^,blender-$VERSION/,g" \ - --use-compress-program="gzip --best" \ +tar \ + --transform "s,^,blender-$VERSION/,g" \ + --use-compress-program="xz -9" \ --create \ --file="$BASE_DIR/$TARBALL" \ - --files-from="$BASE_DIR/$MANIFEST" + --files-from="$BASE_DIR/$MANIFEST" \ + --owner=0 \ + --group=0 + echo "OK" # Create checksum file cd "$BASE_DIR" -echo -n "Creating checksum: \"$BASE_DIR/$TARBALL.md5sum\" ..." +echo -n "Creating checksum: \"$BASE_DIR/$TARBALL.md5sum\" ..." md5sum "$TARBALL" > "$TARBALL.md5sum" echo "OK" diff --git a/build_files/windows/check_libraries.cmd b/build_files/windows/check_libraries.cmd index b838c7d7d19..24b30c6a9b0 100644 --- a/build_files/windows/check_libraries.cmd +++ b/build_files/windows/check_libraries.cmd @@ -10,7 +10,6 @@ if NOT "%verbose%" == "" ( ) if NOT EXIST %BUILD_VS_LIBDIR% ( rem libs not found, but svn is on the system - echo if not "%SVN%"=="" ( echo. echo The required external libraries in %BUILD_VS_LIBDIR% are missing @@ -55,5 +54,8 @@ if NOT EXIST %BUILD_VS_LIBDIR% ( echo Error: Required libraries not found at "%BUILD_VS_LIBDIR%" echo This is needed for building, aborting! echo. + if "%SVN%"=="" ( + echo This is most likely caused by svn.exe not being available. + ) exit /b 1 )
\ No newline at end of file diff --git a/doc/manpage/blender.1.py b/doc/manpage/blender.1.py index fc2200ab859..da83abe8442 100755 --- a/doc/manpage/blender.1.py +++ b/doc/manpage/blender.1.py @@ -62,7 +62,7 @@ if blender_date is None: # Happens when built without WITH_BUILD_INFO e.g. date_string = time.strftime("%B %d, %Y", time.gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))) else: - blender_date = blender_date.strip().partition(" ")[2] # remove 'date:' prefix + blender_date = blender_date.strip().partition(" ")[2] # remove 'date:' prefix date_string = time.strftime("%B %d, %Y", time.strptime(blender_date, "%Y-%m-%d")) outfile = open(outfilename, "w") diff --git a/doc/python_api/examples/bpy.app.timers.1.py b/doc/python_api/examples/bpy.app.timers.1.py index bae3b94b24a..a8ce7fde552 100644 --- a/doc/python_api/examples/bpy.app.timers.1.py +++ b/doc/python_api/examples/bpy.app.timers.1.py @@ -4,7 +4,9 @@ Run a Function in x Seconds """ import bpy + def in_5_seconds(): print("Hello World") + bpy.app.timers.register(in_5_seconds, first_interval=5) diff --git a/doc/python_api/examples/bpy.app.timers.2.py b/doc/python_api/examples/bpy.app.timers.2.py index c663959c209..e17d43cccd8 100644 --- a/doc/python_api/examples/bpy.app.timers.2.py +++ b/doc/python_api/examples/bpy.app.timers.2.py @@ -4,8 +4,10 @@ Run a Function every x Seconds """ import bpy + def every_2_seconds(): print("Hello World") return 2.0 + bpy.app.timers.register(every_2_seconds) diff --git a/doc/python_api/examples/bpy.app.timers.3.py b/doc/python_api/examples/bpy.app.timers.3.py index 79daf6a7740..a738f9ca01c 100644 --- a/doc/python_api/examples/bpy.app.timers.3.py +++ b/doc/python_api/examples/bpy.app.timers.3.py @@ -6,6 +6,7 @@ import bpy counter = 0 + def run_10_times(): global counter counter += 1 @@ -14,4 +15,5 @@ def run_10_times(): return None return 0.1 + bpy.app.timers.register(run_10_times) diff --git a/doc/python_api/examples/bpy.app.timers.4.py b/doc/python_api/examples/bpy.app.timers.4.py index 6cdee564bb5..c14bc15166c 100644 --- a/doc/python_api/examples/bpy.app.timers.4.py +++ b/doc/python_api/examples/bpy.app.timers.4.py @@ -5,8 +5,10 @@ Assign parameters to functions import bpy import functools + def print_message(message): print("Message:", message) + bpy.app.timers.register(functools.partial(print_message, "Hello"), first_interval=2.0) bpy.app.timers.register(functools.partial(print_message, "World"), first_interval=3.0) diff --git a/doc/python_api/examples/bpy.types.Depsgraph.6.py b/doc/python_api/examples/bpy.types.Depsgraph.6.py index 56e028e8813..1f809356b13 100644 --- a/doc/python_api/examples/bpy.types.Depsgraph.6.py +++ b/doc/python_api/examples/bpy.types.Depsgraph.6.py @@ -29,7 +29,7 @@ class OBJECT_OT_simple_exporter(bpy.types.Operator): # Happens for non-geometry objects. continue print(f"Exporting mesh with {len(mesh.vertices)} vertices " - f"at {object_instance.matrix_world}") + f"at {object_instance.matrix_world}") object_instace.to_mesh_clear() return {'FINISHED'} diff --git a/doc/python_api/examples/bpy.types.RenderEngine.py b/doc/python_api/examples/bpy.types.RenderEngine.py index 86ab4b3097d..45910194244 100644 --- a/doc/python_api/examples/bpy.types.RenderEngine.py +++ b/doc/python_api/examples/bpy.types.RenderEngine.py @@ -101,7 +101,7 @@ class CustomRenderEngine(bpy.types.RenderEngine): # Bind shader that converts from scene linear to display space, bgl.glEnable(bgl.GL_BLEND) - bgl.glBlendFunc(bgl.GL_ONE, bgl.GL_ONE_MINUS_SRC_ALPHA); + bgl.glBlendFunc(bgl.GL_ONE, bgl.GL_ONE_MINUS_SRC_ALPHA) self.bind_display_space_shader(scene) if not self.draw_data or self.draw_data.dimensions != dimensions: @@ -135,18 +135,18 @@ class CustomDrawData: # Bind shader that converts from scene linear to display space, # use the scene's color management settings. shader_program = bgl.Buffer(bgl.GL_INT, 1) - bgl.glGetIntegerv(bgl.GL_CURRENT_PROGRAM, shader_program); + bgl.glGetIntegerv(bgl.GL_CURRENT_PROGRAM, shader_program) # Generate vertex array self.vertex_array = bgl.Buffer(bgl.GL_INT, 1) bgl.glGenVertexArrays(1, self.vertex_array) bgl.glBindVertexArray(self.vertex_array[0]) - texturecoord_location = bgl.glGetAttribLocation(shader_program[0], "texCoord"); - position_location = bgl.glGetAttribLocation(shader_program[0], "pos"); + texturecoord_location = bgl.glGetAttribLocation(shader_program[0], "texCoord") + position_location = bgl.glGetAttribLocation(shader_program[0], "pos") - bgl.glEnableVertexAttribArray(texturecoord_location); - bgl.glEnableVertexAttribArray(position_location); + bgl.glEnableVertexAttribArray(texturecoord_location) + bgl.glEnableVertexAttribArray(position_location) # Generate geometry buffers for drawing textured quad position = [0.0, 0.0, width, 0.0, width, height, 0.0, height] @@ -178,7 +178,7 @@ class CustomDrawData: bgl.glActiveTexture(bgl.GL_TEXTURE0) bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.texture[0]) bgl.glBindVertexArray(self.vertex_array[0]) - bgl.glDrawArrays(bgl.GL_TRIANGLE_FAN, 0, 4); + bgl.glDrawArrays(bgl.GL_TRIANGLE_FAN, 0, 4) bgl.glBindVertexArray(0) bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0) @@ -201,6 +201,7 @@ def get_panels(): return panels + def register(): # Register the RenderEngine bpy.utils.register_class(CustomRenderEngine) @@ -208,6 +209,7 @@ def register(): for panel in get_panels(): panel.COMPAT_ENGINES.add('CUSTOM') + def unregister(): bpy.utils.unregister_class(CustomRenderEngine) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 840ab557132..26e1a6a223a 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -1467,9 +1467,11 @@ class CyclesPreferences(bpy.types.AddonPreferences): devices.extend(cpu_devices) return devices - # For backwards compatibility, only has CUDA and OpenCL. + # For backwards compatibility, only returns CUDA and OpenCL but still + # refreshes all devices. def get_devices(self, compute_device_type=''): cuda_devices = self.get_devices_for_type('CUDA') + self.get_devices_for_type('OPTIX') opencl_devices = self.get_devices_for_type('OPENCL') return cuda_devices, opencl_devices diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index b2624b2aa81..4ff154d4bcd 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -2166,7 +2166,7 @@ def draw_pause(self, context): if view.shading.type == 'RENDERED': cscene = scene.cycles - layout.prop(cscene, "preview_pause", icon='PAUSE', text="") + layout.prop(cscene, "preview_pause", icon='PLAY' if cscene.preview_pause else 'PAUSE', text="") def get_panels(): diff --git a/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp b/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp index 3b28f055191..aabaffc7732 100644 --- a/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp +++ b/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp @@ -24,8 +24,6 @@ #include "GHOST_DisplayManagerWin32.h" #include "GHOST_Debug.h" -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x501 // require Windows XP or newer #define WIN32_LEAN_AND_MEAN #include <windows.h> diff --git a/intern/ghost/intern/GHOST_SystemPathsWin32.cpp b/intern/ghost/intern/GHOST_SystemPathsWin32.cpp index bdc403b947e..63a6b7224b5 100644 --- a/intern/ghost/intern/GHOST_SystemPathsWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemPathsWin32.cpp @@ -39,15 +39,16 @@ GHOST_SystemPathsWin32::~GHOST_SystemPathsWin32() const GHOST_TUns8 *GHOST_SystemPathsWin32::getSystemDir(int, const char *versionstr) const { - static char knownpath[MAX_PATH * 3 + 128] = { - 0}; /* 1 utf-16 might translante into 3 utf-8. 2 utf-16 translates into 4 utf-8*/ - wchar_t knownpath_16[MAX_PATH]; + /* 1 utf-16 might translante into 3 utf-8. 2 utf-16 translates into 4 utf-8*/ + static char knownpath[MAX_PATH * 3 + 128] = {0}; + PWSTR knownpath_16 = NULL; - HRESULT hResult = SHGetFolderPathW( - NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, knownpath_16); + HRESULT hResult = SHGetKnownFolderPath( + FOLDERID_ProgramData, KF_FLAG_DEFAULT, NULL, &knownpath_16); if (hResult == S_OK) { conv_utf_16_to_8(knownpath_16, knownpath, MAX_PATH * 3); + CoTaskMemFree(knownpath_16); strcat(knownpath, "\\Blender Foundation\\Blender\\"); strcat(knownpath, versionstr); return (GHOST_TUns8 *)knownpath; @@ -59,12 +60,14 @@ const GHOST_TUns8 *GHOST_SystemPathsWin32::getSystemDir(int, const char *version const GHOST_TUns8 *GHOST_SystemPathsWin32::getUserDir(int, const char *versionstr) const { static char knownpath[MAX_PATH * 3 + 128] = {0}; - wchar_t knownpath_16[MAX_PATH]; + PWSTR knownpath_16 = NULL; - HRESULT hResult = SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, knownpath_16); + HRESULT hResult = SHGetKnownFolderPath( + FOLDERID_RoamingAppData, KF_FLAG_DEFAULT, NULL, &knownpath_16); if (hResult == S_OK) { conv_utf_16_to_8(knownpath_16, knownpath, MAX_PATH * 3); + CoTaskMemFree(knownpath_16); strcat(knownpath, "\\Blender Foundation\\Blender\\"); strcat(knownpath, versionstr); return (GHOST_TUns8 *)knownpath; diff --git a/intern/ghost/intern/GHOST_SystemPathsWin32.h b/intern/ghost/intern/GHOST_SystemPathsWin32.h index 49d241df633..f1924ea51bc 100644 --- a/intern/ghost/intern/GHOST_SystemPathsWin32.h +++ b/intern/ghost/intern/GHOST_SystemPathsWin32.h @@ -28,8 +28,6 @@ # error WIN32 only! #endif // WIN32 -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x501 // require Windows XP or newer #define WIN32_LEAN_AND_MEAN #include <windows.h> diff --git a/intern/ghost/intern/GHOST_TaskbarWin32.h b/intern/ghost/intern/GHOST_TaskbarWin32.h index b1b81337494..abf1172cea8 100644 --- a/intern/ghost/intern/GHOST_TaskbarWin32.h +++ b/intern/ghost/intern/GHOST_TaskbarWin32.h @@ -24,10 +24,6 @@ # error WIN32 only! #endif // WIN32 -/* require Windows XP or newer */ -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x501 - #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <shlobj.h> diff --git a/intern/opensubdiv/internal/opensubdiv_evaluator.cc b/intern/opensubdiv/internal/opensubdiv_evaluator.cc index 2500691885c..4f5a1db82ca 100644 --- a/intern/opensubdiv/internal/opensubdiv_evaluator.cc +++ b/intern/opensubdiv/internal/opensubdiv_evaluator.cc @@ -102,6 +102,17 @@ void evaluateLimit(OpenSubdiv_Evaluator *evaluator, evaluator->internal->eval_output->evaluateLimit(ptex_face_index, face_u, face_v, P, dPdu, dPdv); } +void evaluatePatchesLimit(OpenSubdiv_Evaluator *evaluator, + const OpenSubdiv_PatchCoord *patch_coords, + const int num_patch_coords, + float *P, + float *dPdu, + float *dPdv) +{ + evaluator->internal->eval_output->evaluatePatchesLimit( + patch_coords, num_patch_coords, P, dPdu, dPdv); +} + void evaluateVarying(OpenSubdiv_Evaluator *evaluator, const int ptex_face_index, float face_u, @@ -137,6 +148,8 @@ void assignFunctionPointers(OpenSubdiv_Evaluator *evaluator) evaluator->evaluateLimit = evaluateLimit; evaluator->evaluateVarying = evaluateVarying; evaluator->evaluateFaceVarying = evaluateFaceVarying; + + evaluator->evaluatePatchesLimit = evaluatePatchesLimit; } } // namespace diff --git a/intern/opensubdiv/internal/opensubdiv_evaluator_internal.cc b/intern/opensubdiv/internal/opensubdiv_evaluator_internal.cc index fa45c0119ec..c5dd4509976 100644 --- a/intern/opensubdiv/internal/opensubdiv_evaluator_internal.cc +++ b/intern/opensubdiv/internal/opensubdiv_evaluator_internal.cc @@ -54,109 +54,133 @@ using OpenSubdiv::Osd::CpuPatchTable; using OpenSubdiv::Osd::CpuVertexBuffer; using OpenSubdiv::Osd::PatchCoord; -// TODO(sergey): Remove after official requirement bump for OSD version. -#if OPENSUBDIV_VERSION_NUMBER >= 30200 -# define OPENSUBDIV_HAS_FVAR_EVALUATION -#else -# undef OPENSUBDIV_HAS_FVAR_EVALUATION -#endif - namespace opensubdiv_capi { namespace { -// Helper class to wrap numerous of patch coordinates into a buffer. -// Used to pass coordinates to the CPU evaluator. Other evaluators are not -// supported. -class PatchCoordBuffer : public vector<PatchCoord> { +// Array implementation which stores small data on stack (or, rather, in the class itself). +template<typename T, int kNumMaxElementsOnStack> class StackOrHeapArray { public: - static PatchCoordBuffer *Create(int size) + StackOrHeapArray() + : num_elements_(0), heap_elements_(NULL), num_heap_elements_(0), effective_elements_(NULL) { - PatchCoordBuffer *buffer = new PatchCoordBuffer(); - buffer->resize(size); - return buffer; } - PatchCoord *BindCpuBuffer() + explicit StackOrHeapArray(int size) : StackOrHeapArray() { - return reinterpret_cast<PatchCoord *>(&(*this)[0]); + resize(size); } - int GetNumVertices() + ~StackOrHeapArray() { - return size(); + delete[] heap_elements_; } - void UpdateData(const PatchCoord *patch_coords, int num_patch_coords) + int size() const { - memcpy(&(*this)[0], - reinterpret_cast<const void *>(patch_coords), - sizeof(PatchCoord) * num_patch_coords); - } -}; + return num_elements_; + }; -// Helper class to wrap single of patch coord into a buffer. Used to pass -// coordinates to the CPU evaluator. Other evaluators are not supported. -class SinglePatchCoordBuffer { - public: - static SinglePatchCoordBuffer *Create() + T *data() { - return new SinglePatchCoordBuffer(); + return effective_elements_; } - SinglePatchCoordBuffer() + void resize(int num_elements) { + const int old_num_elements = num_elements_; + num_elements_ = num_elements; + // Early output if allcoation size did not change, or allocation size is smaller. + // We never re-allocate, sacrificing some memory over performance. + if (old_num_elements >= num_elements) { + return; + } + // Simple case: no previously allocated buffer, can simply do one allocation. + if (effective_elements_ == NULL) { + effective_elements_ = allocate(num_elements); + return; + } + // Make new allocation, and copy elements if needed. + T *old_buffer = effective_elements_; + effective_elements_ = allocate(num_elements); + if (old_buffer != effective_elements_) { + memcpy(effective_elements_, old_buffer, sizeof(T) * min(old_num_elements, num_elements)); + } + if (old_buffer != stack_elements_) { + delete[] old_buffer; + } } - explicit SinglePatchCoordBuffer(const PatchCoord &patch_coord) : patch_coord_(patch_coord) + protected: + T *allocate(int num_elements) { + if (num_elements < kNumMaxElementsOnStack) { + return stack_elements_; + } + heap_elements_ = new T[num_elements]; + return heap_elements_; } - PatchCoord *BindCpuBuffer() - { - return &patch_coord_; - } + // Number of elements in the buffer. + int num_elements_; - int GetNumVertices() + // Elements which are allocated on a stack (or, rather, in the same allocation as the buffer + // itself). + // Is used as long as buffer is smaller than kNumMaxElementsOnStack. + T stack_elements_[kNumMaxElementsOnStack]; + + // Heap storage for buffer larger than kNumMaxElementsOnStack. + T *heap_elements_; + int num_heap_elements_; + + // Depending on the current buffer size points to rither stack_elements_ or heap_elements_. + T *effective_elements_; +}; + +// 32 is a number of inner vertices along the patch size at subdivision level 6. +typedef StackOrHeapArray<PatchCoord, 32 * 32> StackOrHeapPatchCoordArray; + +// Buffer which implements API required by OpenSubdiv and uses an existing memory as an underlying +// storage. +template<typename T> class RawDataWrapperBuffer { + public: + RawDataWrapperBuffer(T *data) : data_(data) { - return 1; } - void UpdateData(const PatchCoord &patch_coord) + T *BindCpuBuffer() { - patch_coord_ = patch_coord; + return data_; } + // TODO(sergey): Support UpdateData(). + protected: - PatchCoord patch_coord_; + T *data_; }; -// Helper class which is aimed to be used in cases when buffer is small enough -// and better to be allocated in stack rather than in heap. -// -// TODO(sergey): Check if bare arrays could be used by CPU evaluator. -template<int element_size, int num_vertices> class StackAllocatedBuffer { +template<typename T> class RawDataWrapperVertexBuffer : public RawDataWrapperBuffer<T> { public: - static PatchCoordBuffer *Create(int /*size*/) + RawDataWrapperVertexBuffer(T *data, int num_vertices) + : RawDataWrapperBuffer<T>(data), num_vertices_(num_vertices) { - // TODO(sergey): Validate that requested size is smaller than static - // stack memory size. - return new StackAllocatedBuffer<element_size, num_vertices>(); - } - - float *BindCpuBuffer() - { - return &data_[0]; } int GetNumVertices() { - return num_vertices; + return num_vertices_; } - // TODO(sergey): Support UpdateData(). protected: - float data_[element_size * num_vertices]; + int num_vertices_; +}; + +class ConstPatchCoordWrapperBuffer : public RawDataWrapperVertexBuffer<const PatchCoord> { + public: + ConstPatchCoordWrapperBuffer(const PatchCoord *data, int num_vertices) + : RawDataWrapperVertexBuffer(data, num_vertices) + { + } }; template<typename EVAL_VERTEX_BUFFER, @@ -217,11 +241,12 @@ class FaceVaryingVolatileEval { device_context_); } - void evalPatch(const PatchCoord &patch_coord, float face_varying[2]) + // NOTE: face_varying must point to a memory of at least float[2]*num_patch_coords. + void evalPatches(const PatchCoord *patch_coord, const int num_patch_coords, float *face_varying) { - StackAllocatedBuffer<2, 1> face_varying_data; + RawDataWrapperBuffer<float> face_varying_data(face_varying); BufferDescriptor face_varying_desc(0, 2, 2); - SinglePatchCoordBuffer patch_coord_buffer(patch_coord); + ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords); const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( evaluator_cache_, src_face_varying_desc_, face_varying_desc, device_context_); EVALUATOR::EvalPatchesFaceVarying(src_face_varying_data_, @@ -234,8 +259,6 @@ class FaceVaryingVolatileEval { face_varying_channel_, eval_instance, device_context_); - const float *refined_face_varying = face_varying_data.BindCpuBuffer(); - memcpy(face_varying, refined_face_varying, sizeof(float) * 2); } protected: @@ -297,7 +320,6 @@ class VolatileEvalOutput { src_data_ = SRC_VERTEX_BUFFER::Create(3, num_total_vertices, device_context_); src_varying_data_ = SRC_VERTEX_BUFFER::Create(3, num_total_vertices, device_context_); patch_table_ = PATCH_TABLE::Create(patch_table, device_context_); - patch_coords_ = NULL; vertex_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(vertex_stencils, device_context_); varying_stencils_ = convertToCompatibleStencilTable<STENCIL_TABLE>(varying_stencils, @@ -398,74 +420,66 @@ class VolatileEvalOutput { } } - void evalPatchCoord(const PatchCoord &patch_coord, float P[3]) + // NOTE: P must point to a memory of at least float[3]*num_patch_coords. + void evalPatches(const PatchCoord *patch_coord, const int num_patch_coords, float *P) { - StackAllocatedBuffer<6, 1> vertex_data; - // TODO(sergey): Varying data is interleaved in vertex array, so need to - // adjust stride if there is a varying data. - // BufferDescriptor vertex_desc(0, 3, 6); - BufferDescriptor vertex_desc(0, 3, 3); - SinglePatchCoordBuffer patch_coord_buffer(patch_coord); + RawDataWrapperBuffer<float> P_data(P); + // TODO(sergey): Support interleaved vertex-varying data. + BufferDescriptor P_desc(0, 3, 3); + ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords); const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( - evaluator_cache_, src_desc_, vertex_desc, device_context_); + evaluator_cache_, src_desc_, P_desc, device_context_); EVALUATOR::EvalPatches(src_data_, src_desc_, - &vertex_data, - vertex_desc, + &P_data, + P_desc, patch_coord_buffer.GetNumVertices(), &patch_coord_buffer, patch_table_, eval_instance, device_context_); - const float *refined_vertices = vertex_data.BindCpuBuffer(); - memcpy(P, refined_vertices, sizeof(float) * 3); } - void evalPatchesWithDerivatives(const PatchCoord &patch_coord, - float P[3], - float dPdu[3], - float dPdv[3]) + // NOTE: P, dPdu, dPdv must point to a memory of at least float[3]*num_patch_coords. + void evalPatchesWithDerivatives(const PatchCoord *patch_coord, + const int num_patch_coords, + float *P, + float *dPdu, + float *dPdv) { - StackAllocatedBuffer<6, 1> vertex_data, derivatives; - // TODO(sergey): Varying data is interleaved in vertex array, so need to - // adjust stride if there is a varying data. - // BufferDescriptor vertex_desc(0, 3, 6); - BufferDescriptor vertex_desc(0, 3, 3); - BufferDescriptor du_desc(0, 3, 6), dv_desc(3, 3, 6); - SinglePatchCoordBuffer patch_coord_buffer(patch_coord); + assert(dPdu); + assert(dPdv); + RawDataWrapperBuffer<float> P_data(P); + RawDataWrapperBuffer<float> dPdu_data(dPdu), dPdv_data(dPdv); + // TODO(sergey): Support interleaved vertex-varying data. + BufferDescriptor P_desc(0, 3, 3); + BufferDescriptor dpDu_desc(0, 3, 3), pPdv_desc(0, 3, 3); + ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords); const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( - evaluator_cache_, src_desc_, vertex_desc, du_desc, dv_desc, device_context_); + evaluator_cache_, src_desc_, P_desc, dpDu_desc, pPdv_desc, device_context_); EVALUATOR::EvalPatches(src_data_, src_desc_, - &vertex_data, - vertex_desc, - &derivatives, - du_desc, - &derivatives, - dv_desc, + &P_data, + P_desc, + &dPdu_data, + dpDu_desc, + &dPdv_data, + pPdv_desc, patch_coord_buffer.GetNumVertices(), &patch_coord_buffer, patch_table_, eval_instance, device_context_); - const float *refined_vertices = vertex_data.BindCpuBuffer(); - memcpy(P, refined_vertices, sizeof(float) * 3); - if (dPdu != NULL || dPdv != NULL) { - const float *refined_derivatives = derivatives.BindCpuBuffer(); - if (dPdu != NULL) { - memcpy(dPdu, refined_derivatives, sizeof(float) * 3); - } - if (dPdv != NULL) { - memcpy(dPdv, refined_derivatives + 3, sizeof(float) * 3); - } - } } - void evalPatchVarying(const PatchCoord &patch_coord, float varying[3]) + // NOTE: varying must point to a memory of at least float[3]*num_patch_coords. + void evalPatchesVarying(const PatchCoord *patch_coord, + const int num_patch_coords, + float *varying) { - StackAllocatedBuffer<6, 1> varying_data; + RawDataWrapperBuffer<float> varying_data(varying); BufferDescriptor varying_desc(3, 3, 6); - SinglePatchCoordBuffer patch_coord_buffer(patch_coord); + ConstPatchCoordWrapperBuffer patch_coord_buffer(patch_coord, num_patch_coords); const EVALUATOR *eval_instance = OpenSubdiv::Osd::GetEvaluator<EVALUATOR>( evaluator_cache_, src_varying_desc_, varying_desc, device_context_); EVALUATOR::EvalPatchesVarying(src_varying_data_, @@ -477,23 +491,22 @@ class VolatileEvalOutput { patch_table_, eval_instance, device_context_); - const float *refined_varying = varying_data.BindCpuBuffer(); - memcpy(varying, refined_varying, sizeof(float) * 3); } - void evalPatchFaceVarying(const int face_varying_channel, - const PatchCoord &patch_coord, - float face_varying[2]) + void evalPatchesFaceVarying(const int face_varying_channel, + const PatchCoord *patch_coord, + const int num_patch_coords, + float face_varying[2]) { assert(face_varying_channel >= 0); assert(face_varying_channel < face_varying_evaluators.size()); - face_varying_evaluators[face_varying_channel]->evalPatch(patch_coord, face_varying); + face_varying_evaluators[face_varying_channel]->evalPatches( + patch_coord, num_patch_coords, face_varying); } private: SRC_VERTEX_BUFFER *src_data_; SRC_VERTEX_BUFFER *src_varying_data_; - PatchCoordBuffer *patch_coords_; PATCH_TABLE *patch_table_; BufferDescriptor src_desc_; BufferDescriptor src_varying_desc_; @@ -510,6 +523,19 @@ class VolatileEvalOutput { DEVICE_CONTEXT *device_context_; }; +void convertPatchCoordsToArray(const OpenSubdiv_PatchCoord *patch_coords, + const int num_patch_coords, + const OpenSubdiv::Far::PatchMap *patch_map, + StackOrHeapPatchCoordArray *array) +{ + array->resize(num_patch_coords); + for (int i = 0; i < num_patch_coords; ++i) { + const PatchTable::PatchHandle *handle = patch_map->FindPatch( + patch_coords[i].ptex_face, patch_coords[i].u, patch_coords[i].v); + (array->data())[i] = PatchCoord(*handle, patch_coords[i].u, patch_coords[i].v); + } +} + } // namespace // Note: Define as a class instead of typedcef to make it possible @@ -653,10 +679,10 @@ void CpuEvalOutputAPI::evaluateLimit(const int ptex_face_index, const PatchTable::PatchHandle *handle = patch_map_->FindPatch(ptex_face_index, face_u, face_v); PatchCoord patch_coord(*handle, face_u, face_v); if (dPdu != NULL || dPdv != NULL) { - implementation_->evalPatchesWithDerivatives(patch_coord, P, dPdu, dPdv); + implementation_->evalPatchesWithDerivatives(&patch_coord, 1, P, dPdu, dPdv); } else { - implementation_->evalPatchCoord(patch_coord, P); + implementation_->evalPatches(&patch_coord, 1, P); } } @@ -671,7 +697,7 @@ void CpuEvalOutputAPI::evaluateVarying(const int ptex_face_index, assert(face_v <= 1.0f); const PatchTable::PatchHandle *handle = patch_map_->FindPatch(ptex_face_index, face_u, face_v); PatchCoord patch_coord(*handle, face_u, face_v); - implementation_->evalPatchVarying(patch_coord, varying); + implementation_->evalPatchesVarying(&patch_coord, 1, varying); } void CpuEvalOutputAPI::evaluateFaceVarying(const int face_varying_channel, @@ -686,7 +712,24 @@ void CpuEvalOutputAPI::evaluateFaceVarying(const int face_varying_channel, assert(face_v <= 1.0f); const PatchTable::PatchHandle *handle = patch_map_->FindPatch(ptex_face_index, face_u, face_v); PatchCoord patch_coord(*handle, face_u, face_v); - implementation_->evalPatchFaceVarying(face_varying_channel, patch_coord, face_varying); + implementation_->evalPatchesFaceVarying(face_varying_channel, &patch_coord, 1, face_varying); +} + +void CpuEvalOutputAPI::evaluatePatchesLimit(const OpenSubdiv_PatchCoord *patch_coords, + const int num_patch_coords, + float *P, + float *dPdu, + float *dPdv) +{ + StackOrHeapPatchCoordArray patch_coords_array; + convertPatchCoordsToArray(patch_coords, num_patch_coords, patch_map_, &patch_coords_array); + if (dPdu != NULL || dPdv != NULL) { + implementation_->evalPatchesWithDerivatives( + patch_coords_array.data(), num_patch_coords, P, dPdu, dPdv); + } + else { + implementation_->evalPatches(patch_coords_array.data(), num_patch_coords, P); + } } } // namespace opensubdiv_capi @@ -757,7 +800,6 @@ OpenSubdiv_EvaluatorInternal *openSubdiv_createEvaluatorInternal( } // Face warying stencil. vector<const StencilTable *> all_face_varying_stencils; -#ifdef OPENSUBDIV_HAS_FVAR_EVALUATION all_face_varying_stencils.reserve(num_face_varying_channels); for (int face_varying_channel = 0; face_varying_channel < num_face_varying_channels; ++face_varying_channel) { @@ -769,7 +811,6 @@ OpenSubdiv_EvaluatorInternal *openSubdiv_createEvaluatorInternal( all_face_varying_stencils.push_back( StencilTableFactory::Create(*refiner, face_varying_stencil_options)); } -#endif // Generate bi-cubic patch table for the limit surface. // TODO(sergey): Ideally we would want to expose end-cap settings via // C-API to make it more generic. Currently it matches old Blender's @@ -800,7 +841,6 @@ OpenSubdiv_EvaluatorInternal *openSubdiv_createEvaluatorInternal( varying_stencils = table; } } -#ifdef OPENSUBDIV_HAS_FVAR_EVALUATION for (int face_varying_channel = 0; face_varying_channel < num_face_varying_channels; ++face_varying_channel) { const StencilTable *table = StencilTableFactory::AppendLocalPointStencilTableFaceVarying( @@ -813,7 +853,6 @@ OpenSubdiv_EvaluatorInternal *openSubdiv_createEvaluatorInternal( all_face_varying_stencils[face_varying_channel] = table; } } -#endif // Create OpenSubdiv's CPU side evaluator. // TODO(sergey): Make it possible to use different evaluators. opensubdiv_capi::CpuEvalOutput *eval_output = new opensubdiv_capi::CpuEvalOutput( diff --git a/intern/opensubdiv/internal/opensubdiv_evaluator_internal.h b/intern/opensubdiv/internal/opensubdiv_evaluator_internal.h index 7c963227d17..392633944c6 100644 --- a/intern/opensubdiv/internal/opensubdiv_evaluator_internal.h +++ b/intern/opensubdiv/internal/opensubdiv_evaluator_internal.h @@ -26,6 +26,7 @@ #include <opensubdiv/far/patchMap.h> #include <opensubdiv/far/patchTable.h> +struct OpenSubdiv_PatchCoord; struct OpenSubdiv_TopologyRefiner; namespace opensubdiv_capi { @@ -114,6 +115,18 @@ class CpuEvalOutputAPI { float face_v, float face_varying[2]); + // Batched evaluation of multiple input coordinates. + + // Evaluate given ptex face at given bilinear coordinate. + // If derivatives are NULL, they will not be evaluated. + // + // NOTE: Output arrays must point to a memory of size float[3]*num_patch_coords. + void evaluatePatchesLimit(const OpenSubdiv_PatchCoord *patch_coords, + const int num_patch_coords, + float *P, + float *dPdu, + float *dPdv); + protected: CpuEvalOutput *implementation_; OpenSubdiv::Far::PatchMap *patch_map_; diff --git a/intern/opensubdiv/opensubdiv_capi_type.h b/intern/opensubdiv/opensubdiv_capi_type.h index 35eeb71dede..e759c5f43b0 100644 --- a/intern/opensubdiv/opensubdiv_capi_type.h +++ b/intern/opensubdiv/opensubdiv_capi_type.h @@ -58,6 +58,13 @@ typedef enum OpenSubdiv_FVarLinearInterpolation { OSD_FVAR_LINEAR_INTERPOLATION_ALL, } OpenSubdiv_FVarLinearInterpolation; +typedef struct OpenSubdiv_PatchCoord { + int ptex_face; + + // Parametric location on patch. + float u, v; +} OpenSubdiv_PatchCoord; + #ifdef __cplusplus } #endif diff --git a/intern/opensubdiv/opensubdiv_evaluator_capi.h b/intern/opensubdiv/opensubdiv_evaluator_capi.h index ceb0c58feba..1572d01b851 100644 --- a/intern/opensubdiv/opensubdiv_evaluator_capi.h +++ b/intern/opensubdiv/opensubdiv_evaluator_capi.h @@ -24,6 +24,7 @@ extern "C" { #endif struct OpenSubdiv_EvaluatorInternal; +struct OpenSubdiv_PatchCoord; struct OpenSubdiv_TopologyRefiner; typedef struct OpenSubdiv_Evaluator { @@ -108,6 +109,19 @@ typedef struct OpenSubdiv_Evaluator { float face_v, float face_varying[2]); + // Batched evaluation of multiple input coordinates. + + // Evaluate limit surface. + // If derivatives are NULL, they will not be evaluated. + // + // NOTE: Output arrays must point to a memory of size float[3]*num_patch_coords. + void (*evaluatePatchesLimit)(struct OpenSubdiv_Evaluator *evaluator, + const struct OpenSubdiv_PatchCoord *patch_coords, + const int num_patch_coords, + float *P, + float *dPdu, + float *dPdv); + // Internal storage for the use in this module only. // // This is where actual OpenSubdiv's evaluator is living. diff --git a/release/datafiles/icons/ops.gpencil.primitive_polyline.dat b/release/datafiles/icons/ops.gpencil.primitive_polyline.dat Binary files differnew file mode 100644 index 00000000000..35a869661b2 --- /dev/null +++ b/release/datafiles/icons/ops.gpencil.primitive_polyline.dat diff --git a/release/datafiles/icons/ops.paint.eyedropper_add.dat b/release/datafiles/icons/ops.paint.eyedropper_add.dat Binary files differnew file mode 100644 index 00000000000..135cbbb4a9d --- /dev/null +++ b/release/datafiles/icons/ops.paint.eyedropper_add.dat diff --git a/release/scripts/modules/bpy/utils/__init__.py b/release/scripts/modules/bpy/utils/__init__.py index 04aaa7bd69d..abe33b0e8ea 100644 --- a/release/scripts/modules/bpy/utils/__init__.py +++ b/release/scripts/modules/bpy/utils/__init__.py @@ -457,6 +457,41 @@ def preset_paths(subdir): return dirs +def is_path_builtin(path): + """ + Returns True if the path is one of the built-in paths used by Blender. + + :arg path: Path you want to check if it is in the built-in settings directory + :type path: str + :rtype: bool + """ + # Note that this function is is not optimized for speed, + # it's intended to be used to check if it's OK to remove presets. + # + # If this is used in a draw-loop for example, we could cache some of the values. + user_path = resource_path('USER') + + for res in ('SYSTEM', 'LOCAL'): + parent_path = resource_path(res) + if not parent_path or parent_path == user_path: + # Make sure that the current path is not empty string and that it is + # not the same as the user config path. IE "~/.config/blender" on Linux + # This can happen on portable installs. + continue + + try: + if _os.path.samefile( + _os.path.commonpath([parent_path]), + _os.path.commonpath([parent_path, path]) + ): + return True + except FileNotFoundError: + #The path we tried to look up doesn't exist + pass + + return False + + def smpte_from_seconds(time, fps=None, fps_base=None): """ Returns an SMPTE formatted string from the *time*: diff --git a/release/scripts/startup/bl_operators/add_mesh_torus.py b/release/scripts/startup/bl_operators/add_mesh_torus.py index 0f95371d4c5..3ce06647cdc 100644 --- a/release/scripts/startup/bl_operators/add_mesh_torus.py +++ b/release/scripts/startup/bl_operators/add_mesh_torus.py @@ -22,6 +22,7 @@ from bpy.types import Operator from bpy.props import ( BoolProperty, + EnumProperty, FloatProperty, IntProperty, ) @@ -147,7 +148,7 @@ class AddTorus(Operator, object_utils.AddObjectHelper): min=3, max=256, default=12, ) - mode: bpy.props.EnumProperty( + mode: EnumProperty( name="Torus Dimensions", items=( ('MAJOR_MINOR', "Major/Minor", diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py index 661a7bd4aea..deb33f77050 100644 --- a/release/scripts/startup/bl_operators/presets.py +++ b/release/scripts/startup/bl_operators/presets.py @@ -84,6 +84,7 @@ class AddPresetBase: def execute(self, context): import os + from bpy.utils import is_path_builtin if hasattr(self, "pre_cb"): self.pre_cb(context) @@ -190,6 +191,11 @@ class AddPresetBase: if not filepath: return {'CANCELLED'} + # Do not remove bundled presets + if is_path_builtin(filepath): + self.report({'WARNING'}, "You can't remove the default presets") + return {'CANCELLED'} + try: if hasattr(self, "remove"): self.remove(context, filepath) diff --git a/release/scripts/startup/bl_operators/sequencer.py b/release/scripts/startup/bl_operators/sequencer.py index ab87dc47075..9a0028d1f14 100644 --- a/release/scripts/startup/bl_operators/sequencer.py +++ b/release/scripts/startup/bl_operators/sequencer.py @@ -21,7 +21,11 @@ import bpy from bpy.types import Operator -from bpy.props import IntProperty +from bpy.props import ( + EnumProperty, + FloatProperty, + IntProperty, +) class SequencerCrossfadeSounds(Operator): @@ -170,12 +174,12 @@ class SequencerFadesAdd(Operator): bl_label = "Add Fades" bl_options = {'REGISTER', 'UNDO'} - duration_seconds: bpy.props.FloatProperty( + duration_seconds: FloatProperty( name="Fade Duration", description="Duration of the fade in seconds", default=1.0, min=0.01) - type: bpy.props.EnumProperty( + type: EnumProperty( items=( ('IN_OUT', 'Fade In And Out', 'Fade selected strips in and out'), ('IN', 'Fade In', 'Fade in selected strips'), diff --git a/release/scripts/startup/bl_operators/userpref.py b/release/scripts/startup/bl_operators/userpref.py index 0cd90610cdc..4c5c269955a 100644 --- a/release/scripts/startup/bl_operators/userpref.py +++ b/release/scripts/startup/bl_operators/userpref.py @@ -1032,7 +1032,7 @@ class PREFERENCES_OT_studiolight_uninstall(Operator): """Delete Studio Light""" bl_idname = "preferences.studiolight_uninstall" bl_label = "Uninstall Studio Light" - index: bpy.props.IntProperty() + index: IntProperty() def execute(self, context): import os @@ -1055,7 +1055,7 @@ class PREFERENCES_OT_studiolight_copy_settings(Operator): """Copy Studio Light settings to the Studio light editor""" bl_idname = "preferences.studiolight_copy_settings" bl_label = "Copy Studio Light settings" - index: bpy.props.IntProperty() + index: IntProperty() def execute(self, context): prefs = context.preferences diff --git a/release/scripts/startup/bl_operators/uvcalc_follow_active.py b/release/scripts/startup/bl_operators/uvcalc_follow_active.py index 7b25491764b..83d451fbc89 100644 --- a/release/scripts/startup/bl_operators/uvcalc_follow_active.py +++ b/release/scripts/startup/bl_operators/uvcalc_follow_active.py @@ -24,6 +24,9 @@ import bpy from bpy.types import Operator +from bpy.props import ( + EnumProperty, +) STATUS_OK = (1 << 0) STATUS_ERR_ACTIVE_FACE = (1 << 1) @@ -253,7 +256,7 @@ class FollowActiveQuads(Operator): bl_label = "Follow Active Quads" bl_options = {'REGISTER', 'UNDO'} - mode: bpy.props.EnumProperty( + mode: EnumProperty( name="Edge Length Mode", description="Method to space UV edge loops", items=( diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 037303a08e1..bdac6332bd8 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -1781,6 +1781,44 @@ class WM_OT_toolbar_prompt(Operator): bl_idname = "wm.toolbar_prompt" bl_label = "Toolbar Prompt" + @staticmethod + def _status_items_generate(cls, keymap, context): + from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + + # The keymap doesn't have the same order the tools are declared in, + # while we could support this, it's simpler to apply order here. + tool_map_id_to_order = {} + # Map the + tool_map_id_to_label = {} + for item in ToolSelectPanelHelper._tools_flatten(cls.tools_from_context(context)): + if item is not None: + tool_map_id_to_label[item.idname] = item.label + tool_map_id_to_order[item.idname] = len(tool_map_id_to_order) + + status_items = [] + + for item in keymap.keymap_items: + name = item.name + key_str = item.to_string() + # These are duplicated from regular numbers. + if key_str.startswith("Numpad "): + continue + properties = item.properties + idname = item.idname + if idname == "wm.tool_set_by_id": + tool_idname = properties["name"] + name = tool_map_id_to_label[tool_idname] + name = name.replace("Annotate ", "") + else: + continue + + status_items.append((tool_idname, name, item)) + + status_items.sort( + key=lambda a: tool_map_id_to_order[a[0]] + ) + return status_items + def modal(self, context, event): event_type = event.type event_value = event.value @@ -1805,14 +1843,18 @@ class WM_OT_toolbar_prompt(Operator): return {'RUNNING_MODAL'} def invoke(self, context, event): - space_type = context.space_data.type + space_data = context.space_data + if space_data is None: + return {'CANCELLED'} + + space_type = space_data.type cls, keymap = WM_OT_toolbar.keymap_from_toolbar( context, space_type, use_fallback_keys=False, use_reset=False, ) - if keymap is None: + if (keymap is None) or (not keymap.keymap_items): return {'CANCELLED'} self._init_event_type = event.type @@ -1823,18 +1865,9 @@ class WM_OT_toolbar_prompt(Operator): del init_event_type_as_text[0] init_event_type_as_text = " ".join(init_event_type_as_text) + status_items = self._status_items_generate(cls, keymap, context) + def status_text_fn(self, context): - from bl_ui.space_toolsystem_common import ToolSelectPanelHelper - - # The keymap doesn't have the same order the tools are declared in, - # while we could support this, it's simpler to apply order here. - tool_map_id_to_order = {} - # Map the - tool_map_id_to_label = {} - for item in ToolSelectPanelHelper._tools_flatten(cls.tools_from_context(context)): - if item is not None: - tool_map_id_to_label[item.idname] = item.label - tool_map_id_to_order[item.idname] = len(tool_map_id_to_order) layout = self.layout if True: @@ -1842,31 +1875,7 @@ class WM_OT_toolbar_prompt(Operator): box.scale_x = 0.8 box.label(text=init_event_type_as_text) - status_items = [] - - for item in keymap.keymap_items: - name = item.name - key_str = item.to_string() - # These are duplicated from regular numbers. - if key_str.startswith("Numpad "): - continue - properties = item.properties - idname = item.idname - if idname == "wm.tool_set_by_id": - tool_idname = properties["name"] - name = tool_map_id_to_label[tool_idname] - name = name.replace("Annotate ", "") - else: - continue - - status_items.append((tool_idname, name, item)) - - status_items.sort( - key=lambda a: tool_map_id_to_order[a[0]] - ) - flow = layout.grid_flow(columns=len(status_items), align=True, row_major=True) - for _, name, item in status_items: row = flow.row(align=True) row.template_event_from_keymap_item(item, text=name) diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index e545ee971d8..b6db3cf0b97 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -892,7 +892,7 @@ class GreasePencilMaterialsPanel: if is_view3d and brush is not None: gp_settings = brush.gpencil_settings if gp_settings.use_material_pin is False: - if ob.active_material_index > 0: + if ob.active_material_index >= 0: ma = ob.material_slots[ob.active_material_index].material else: ma = gp_settings.material diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 636c7f42f65..98e812cf02d 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -108,7 +108,7 @@ class VIEW3D_MT_tools_projectpaint_clone(Menu): props.value = i -def brush_texpaint_common(panel, context, layout, brush, _settings, projpaint=False): +def brush_texpaint_common(panel, context, layout, brush, _settings, *, projpaint=False): col = layout.column() if brush.image_tool == 'FILL' and not projpaint: @@ -132,7 +132,7 @@ def brush_texpaint_common(panel, context, layout, brush, _settings, projpaint=Fa brush_basic_texpaint_settings(col, context, brush) -def brush_texpaint_common_clone(_panel, context, layout, _brush, settings, projpaint=False): +def brush_texpaint_common_clone(_panel, context, layout, _brush, settings, *, projpaint=False): ob = context.active_object col = layout.column() @@ -160,7 +160,7 @@ def brush_texpaint_common_clone(_panel, context, layout, _brush, settings, projp col.menu("VIEW3D_MT_tools_projectpaint_clone", text=clone_text, translate=False) -def brush_texpaint_common_color(_panel, context, layout, brush, _settings, projpaint=False): +def brush_texpaint_common_color(_panel, context, layout, brush, _settings, *, projpaint=False): UnifiedPaintPanel.prop_unified_color_picker(layout, context, brush, "color", value_slider=True) row = layout.row(align=True) @@ -170,7 +170,7 @@ def brush_texpaint_common_color(_panel, context, layout, brush, _settings, projp row.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="", emboss=False) -def brush_texpaint_common_gradient(_panel, context, layout, brush, _settings, projpaint=False): +def brush_texpaint_common_gradient(_panel, context, layout, brush, _settings, *, projpaint=False): layout.template_color_ramp(brush, "gradient", expand=True) layout.use_property_split = True @@ -186,7 +186,7 @@ def brush_texpaint_common_gradient(_panel, context, layout, brush, _settings, pr col.prop(brush, "gradient_fill_mode") -def brush_texpaint_common_options(_panel, _context, layout, brush, _settings, projpaint=False): +def brush_texpaint_common_options(_panel, _context, layout, brush, _settings, *, projpaint=False): capabilities = brush.image_paint_capabilities col = layout.column() @@ -199,6 +199,8 @@ def brush_texpaint_common_options(_panel, _context, layout, brush, _settings, pr if projpaint: col.prop(brush, "use_alpha") + else: + col.prop(brush, "use_paint_antialiasing") # Used in both the View3D toolbar and texture properties diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 37271f2e242..958052c8f25 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -432,7 +432,7 @@ class FILEBROWSER_MT_view(Menu): class FILEBROWSER_MT_select(Menu): bl_label = "Select" - def draw(self, context): + def draw(self, _context): layout = self.layout layout.operator("file.select_all", text="All").action = 'SELECT' diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index cf5283d65cb..8de12f759fc 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -1106,9 +1106,9 @@ class IMAGE_PT_paint_color(Panel, ImagePaintPanel): layout.prop(brush, "color_type", expand=True) if brush.color_type == 'COLOR': - brush_texpaint_common_color(self, context, layout, brush, settings, True) + brush_texpaint_common_color(self, context, layout, brush, settings) elif brush.color_type == 'GRADIENT': - brush_texpaint_common_gradient(self, context, layout, brush, settings, True) + brush_texpaint_common_gradient(self, context, layout, brush, settings) class IMAGE_PT_paint_swatches(Panel, ImagePaintPanel): @@ -1160,7 +1160,7 @@ class IMAGE_PT_paint_clone(Panel, ImagePaintPanel): layout.active = settings.use_clone_layer - brush_texpaint_common_clone(self, context, layout, brush, settings, True) + brush_texpaint_common_clone(self, context, layout, brush, settings) class IMAGE_PT_paint_options(Panel, ImagePaintPanel): @@ -1186,7 +1186,7 @@ class IMAGE_PT_paint_options(Panel, ImagePaintPanel): layout.use_property_split = True layout.use_property_decorate = False # No animation. - brush_texpaint_common_options(self, context, layout, brush, settings, True) + brush_texpaint_common_options(self, context, layout, brush, settings) class IMAGE_PT_tools_brush_display(BrushButtonsPanel, Panel): diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 5624236b6fc..e145b53cbd1 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -316,8 +316,10 @@ class SEQUENCER_MT_select_channel(Menu): def draw(self, _context): layout = self.layout - layout.operator("sequencer.select_active_side", text="Left").side = 'LEFT' - layout.operator("sequencer.select_active_side", text="Right").side = 'RIGHT' + layout.operator("sequencer.select_side", text="Left").side = 'LEFT' + layout.operator("sequencer.select_side", text="Right").side = 'RIGHT' + layout.separator() + layout.operator("sequencer.select_side", text="Both Sides").side = 'BOTH' class SEQUENCER_MT_select_linked(Menu): diff --git a/release/scripts/startup/bl_ui/space_text.py b/release/scripts/startup/bl_ui/space_text.py index 9e9ddcf3505..81ccc9216a1 100644 --- a/release/scripts/startup/bl_ui/space_text.py +++ b/release/scripts/startup/bl_ui/space_text.py @@ -57,7 +57,8 @@ class TEXT_HT_header(Header): syntax.prop(st, "show_syntax_highlight", text="") if text: - is_osl = text.name.endswith((".osl", ".osl")) + text_name = text.name + is_osl = text_name.endswith((".osl", ".oso")) row = layout.row() if is_osl: @@ -65,7 +66,7 @@ class TEXT_HT_header(Header): row.operator("node.shader_script_update") else: row = layout.row() - row.active = text.name.endswith(".py") + row.active = text_name.endswith(".py") row.prop(text, "use_module") row = layout.row() diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 00beb523d21..7d6ca5c9557 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1405,7 +1405,7 @@ class _defs_gpencil_paint: return dict( idname="builtin.eyedropper", label="Eyedropper", - icon="ops.paint.weight_sample", + icon="ops.paint.eyedropper_add", cursor='EYEDROPPER', widget=None, keymap=(), diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index b22afe338ed..8ff0ba7bce4 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2522,11 +2522,18 @@ class VIEW3D_MT_object_parent(Menu): def draw(self, _context): layout = self.layout + operator_context_default = layout.operator_context layout.operator_enum("object.parent_set", "type") layout.separator() + layout.operator_context = 'EXEC_DEFAULT' + layout.operator("object.parent_no_inverse_set") + layout.operator_context = operator_context_default + + layout.separator() + layout.operator_enum("object.parent_clear", "type") @@ -4498,11 +4505,13 @@ class VIEW3D_MT_assign_material(Menu): def draw(self, context): layout = self.layout ob = context.active_object + mat_active = ob.active_material for slot in ob.material_slots: mat = slot.material if mat: - layout.operator("gpencil.stroke_change_color", text=mat.name).material = mat.name + layout.operator("gpencil.stroke_change_color", text=mat.name, + icon='LAYER_ACTIVE' if mat == mat_active else 'BLANK1').material = mat.name class VIEW3D_MT_gpencil_copy_layer(Menu): @@ -6073,6 +6082,8 @@ class VIEW3D_PT_snapping(Panel): row = col.row(align=True) row.prop(tool_settings, "snap_target", expand=True) + col.prop(tool_settings, "use_snap_backface_culling") + if obj: if object_mode == 'EDIT': col.prop(tool_settings, "use_snap_self") diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index afaf06d2320..d99c7ae65b0 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -503,7 +503,7 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel): # Texture Paint Mode # elif context.image_paint_object and brush: - brush_texpaint_common(self, context, layout, brush, settings, True) + brush_texpaint_common(self, context, layout, brush, settings, projpaint=True) # Weight Paint Mode # elif context.weight_paint_object and brush: @@ -551,15 +551,15 @@ class VIEW3D_PT_tools_brush_color(Panel, View3DPaintPanel): brush = settings.brush if context.vertex_paint_object: - brush_texpaint_common_color(self, context, layout, brush, settings, True) + brush_texpaint_common_color(self, context, layout, brush, settings, projpaint=True) else: layout.prop(brush, "color_type", expand=True) if brush.color_type == 'COLOR': - brush_texpaint_common_color(self, context, layout, brush, settings, True) + brush_texpaint_common_color(self, context, layout, brush, settings, projpaint=True) elif brush.color_type == 'GRADIENT': - brush_texpaint_common_gradient(self, context, layout, brush, settings, True) + brush_texpaint_common_gradient(self, context, layout, brush, settings, projpaint=True) class VIEW3D_PT_tools_brush_swatches(Panel, View3DPaintPanel): @@ -613,7 +613,7 @@ class VIEW3D_PT_tools_brush_clone(Panel, View3DPaintPanel): layout.active = settings.use_clone_layer - brush_texpaint_common_clone(self, context, layout, brush, settings, True) + brush_texpaint_common_clone(self, context, layout, brush, settings, projpaint=True) class VIEW3D_PT_tools_brush_options(Panel, View3DPaintPanel): @@ -635,7 +635,7 @@ class VIEW3D_PT_tools_brush_options(Panel, View3DPaintPanel): col = layout.column() if context.image_paint_object and brush: - brush_texpaint_common_options(self, context, layout, brush, settings, True) + brush_texpaint_common_options(self, context, layout, brush, settings, projpaint=True) elif context.sculpt_object and brush: col.prop(brush, "use_automasking_topology") diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index fe740f4898e..73b7b1e2858 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -84,6 +84,7 @@ static void brush_defaults(Brush *brush) FROM_DEFAULT(normal_weight); FROM_DEFAULT(fill_threshold); FROM_DEFAULT(flag); + FROM_DEFAULT(sampling_flag); FROM_DEFAULT_PTR(rgb); FROM_DEFAULT_PTR(secondary_rgb); FROM_DEFAULT(spacing); diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h index 568d6c9a84a..7ef5e518cc8 100644 --- a/source/blender/blenlib/BLI_task.h +++ b/source/blender/blenlib/BLI_task.h @@ -198,11 +198,45 @@ void BLI_task_parallel_range(const int start, TaskParallelRangeFunc func, const TaskParallelSettings *settings); -typedef void (*TaskParallelListbaseFunc)(void *userdata, struct Link *iter, int index); +/* This data is shared between all tasks, its access needs thread lock or similar protection. */ +typedef struct TaskParallelIteratorStateShared { + /* Maximum amount of items to acquire at once. */ + int chunk_size; + /* Next item to be acquired. */ + void *next_item; + /* Index of the next item to be acquired. */ + int next_index; + /* Indicates that end of iteration has been reached. */ + bool is_finished; + /* Helper lock to protect access to this data in iterator getter callback, + * can be ignored (if the callback implements its own protection system, using atomics e.g.). + * Will be NULL when iterator is actually processed in a single thread. */ + SpinLock *spin_lock; +} TaskParallelIteratorStateShared; + +typedef void (*TaskParallelIteratorIterFunc)(void *__restrict userdata, + const TaskParallelTLS *__restrict tls, + void **r_next_item, + int *r_next_index, + bool *r_do_abort); + +typedef void (*TaskParallelIteratorFunc)(void *__restrict userdata, + void *item, + int index, + const TaskParallelTLS *__restrict tls); + +void BLI_task_parallel_iterator(void *userdata, + TaskParallelIteratorIterFunc iter_func, + void *init_item, + const int init_index, + const int tot_items, + TaskParallelIteratorFunc func, + const TaskParallelSettings *settings); + void BLI_task_parallel_listbase(struct ListBase *listbase, void *userdata, - TaskParallelListbaseFunc func, - const bool use_threading); + TaskParallelIteratorFunc func, + const TaskParallelSettings *settings); typedef struct MempoolIterData MempoolIterData; typedef void (*TaskParallelMempoolFunc)(void *userdata, MempoolIterData *iter); diff --git a/source/blender/blenlib/intern/task.c b/source/blender/blenlib/intern/task.c index 6cdaec97d9a..bb69dc6452f 100644 --- a/source/blender/blenlib/intern/task.c +++ b/source/blender/blenlib/intern/task.c @@ -149,7 +149,7 @@ typedef struct TaskThreadLocalStorage { * without "interrupting" for task execution. * * We try to accumulate as much tasks as possible in a local queue without - * any locks first, and then we push all of them into a schedulers queue + * any locks first, and then we push all of them into a scheduler's queue * from within a single mutex lock. */ bool do_delayed_push; @@ -1052,14 +1052,20 @@ typedef struct ParallelRangeState { int chunk_size; } ParallelRangeState; -BLI_INLINE void task_parallel_range_calc_chunk_size(const TaskParallelSettings *settings, - const int num_tasks, - ParallelRangeState *state) +BLI_INLINE void task_parallel_calc_chunk_size(const TaskParallelSettings *settings, + const int tot_items, + int num_tasks, + int *r_chunk_size) { - const int tot_items = state->stop - state->start; int chunk_size = 0; - if (settings->min_iter_per_thread > 0) { + if (!settings->use_threading) { + /* Some users of this helper will still need a valid chunk size in case processing is not + * threaded. We can use a bigger one than in default threaded case then. */ + chunk_size = 1024; + num_tasks = 1; + } + else if (settings->min_iter_per_thread > 0) { /* Already set by user, no need to do anything here. */ chunk_size = settings->min_iter_per_thread; } @@ -1091,16 +1097,30 @@ BLI_INLINE void task_parallel_range_calc_chunk_size(const TaskParallelSettings * BLI_assert(chunk_size > 0); - switch (settings->scheduling_mode) { - case TASK_SCHEDULING_STATIC: - state->chunk_size = max_ii(chunk_size, tot_items / (num_tasks)); - break; - case TASK_SCHEDULING_DYNAMIC: - state->chunk_size = chunk_size; - break; + if (tot_items > 0) { + switch (settings->scheduling_mode) { + case TASK_SCHEDULING_STATIC: + *r_chunk_size = max_ii(chunk_size, tot_items / num_tasks); + break; + case TASK_SCHEDULING_DYNAMIC: + *r_chunk_size = chunk_size; + break; + } + } + else { + /* If total amount of items is unknown, we can only use dynamic scheduling. */ + *r_chunk_size = chunk_size; } } +BLI_INLINE void task_parallel_range_calc_chunk_size(const TaskParallelSettings *settings, + const int num_tasks, + ParallelRangeState *state) +{ + task_parallel_calc_chunk_size( + settings, state->stop - state->start, num_tasks, &state->chunk_size); +} + BLI_INLINE bool parallel_range_next_iter_get(ParallelRangeState *__restrict state, int *__restrict iter, int *__restrict count) @@ -1256,77 +1276,239 @@ void BLI_task_parallel_range(const int start, } } -#undef MALLOCA -#undef MALLOCA_FREE - -typedef struct ParallelListbaseState { +typedef struct TaskParallelIteratorState { void *userdata; - TaskParallelListbaseFunc func; + TaskParallelIteratorIterFunc iter_func; + TaskParallelIteratorFunc func; + + /* *** Data used to 'acquire' chunks of items from the iterator. *** */ + /* Common data also passed to the generator callback. */ + TaskParallelIteratorStateShared iter_shared; + /* Total number of items. If unknown, set it to a negative number. */ + int tot_items; +} TaskParallelIteratorState; + +BLI_INLINE void task_parallel_iterator_calc_chunk_size(const TaskParallelSettings *settings, + const int num_tasks, + TaskParallelIteratorState *state) +{ + task_parallel_calc_chunk_size( + settings, state->tot_items, num_tasks, &state->iter_shared.chunk_size); +} - int chunk_size; - int index; - Link *link; - SpinLock lock; -} ParallelListState; - -BLI_INLINE Link *parallel_listbase_next_iter_get(ParallelListState *__restrict state, - int *__restrict index, - int *__restrict count) +static void parallel_iterator_func_do(TaskParallelIteratorState *__restrict state, + void *userdata_chunk, + int threadid) { - int task_count = 0; - BLI_spin_lock(&state->lock); - Link *result = state->link; - if (LIKELY(result != NULL)) { - *index = state->index; - while (state->link != NULL && task_count < state->chunk_size) { - task_count++; - state->link = state->link->next; + TaskParallelTLS tls = { + .thread_id = threadid, + .userdata_chunk = userdata_chunk, + }; + + void **current_chunk_items; + int *current_chunk_indices; + int current_chunk_size; + + const size_t items_size = sizeof(*current_chunk_items) * (size_t)state->iter_shared.chunk_size; + const size_t indices_size = sizeof(*current_chunk_indices) * + (size_t)state->iter_shared.chunk_size; + + current_chunk_items = MALLOCA(items_size); + current_chunk_indices = MALLOCA(indices_size); + current_chunk_size = 0; + + for (bool do_abort = false; !do_abort;) { + if (state->iter_shared.spin_lock != NULL) { + BLI_spin_lock(state->iter_shared.spin_lock); + } + + /* Get current status. */ + int index = state->iter_shared.next_index; + void *item = state->iter_shared.next_item; + int i; + + /* 'Acquire' a chunk of items from the iterator function. */ + for (i = 0; i < state->iter_shared.chunk_size && !state->iter_shared.is_finished; i++) { + current_chunk_indices[i] = index; + current_chunk_items[i] = item; + state->iter_func(state->userdata, &tls, &item, &index, &state->iter_shared.is_finished); + } + + /* Update current status. */ + state->iter_shared.next_index = index; + state->iter_shared.next_item = item; + current_chunk_size = i; + + do_abort = state->iter_shared.is_finished; + + if (state->iter_shared.spin_lock != NULL) { + BLI_spin_unlock(state->iter_shared.spin_lock); + } + + for (i = 0; i < current_chunk_size; ++i) { + state->func(state->userdata, current_chunk_items[i], current_chunk_indices[i], &tls); } - state->index += task_count; } - BLI_spin_unlock(&state->lock); - *count = task_count; - return result; + + MALLOCA_FREE(current_chunk_items, items_size); + MALLOCA_FREE(current_chunk_indices, indices_size); } -static void parallel_listbase_func(TaskPool *__restrict pool, - void *UNUSED(taskdata), - int UNUSED(threadid)) +static void parallel_iterator_func(TaskPool *__restrict pool, void *userdata_chunk, int threadid) { - ParallelListState *__restrict state = BLI_task_pool_userdata(pool); - Link *link; - int index, count; + TaskParallelIteratorState *__restrict state = BLI_task_pool_userdata(pool); - while ((link = parallel_listbase_next_iter_get(state, &index, &count)) != NULL) { - for (int i = 0; i < count; i++) { - state->func(state->userdata, link, index + i); - link = link->next; + parallel_iterator_func_do(state, userdata_chunk, threadid); +} + +static void task_parallel_iterator_no_threads(const TaskParallelSettings *settings, + TaskParallelIteratorState *state) +{ + /* Prepare user's TLS data. */ + void *userdata_chunk = settings->userdata_chunk; + const size_t userdata_chunk_size = settings->userdata_chunk_size; + void *userdata_chunk_local = NULL; + const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL); + if (use_userdata_chunk) { + userdata_chunk_local = MALLOCA(userdata_chunk_size); + memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size); + } + + /* Also marking it as non-threaded for the iterator callback. */ + state->iter_shared.spin_lock = NULL; + + parallel_iterator_func_do(state, userdata_chunk, 0); + + if (use_userdata_chunk) { + if (settings->func_finalize != NULL) { + settings->func_finalize(state->userdata, userdata_chunk_local); } + MALLOCA_FREE(userdata_chunk_local, userdata_chunk_size); } } -static void task_parallel_listbase_no_threads(struct ListBase *listbase, - void *userdata, - TaskParallelListbaseFunc func) +static void task_parallel_iterator_do(const TaskParallelSettings *settings, + TaskParallelIteratorState *state) { - int i = 0; - for (Link *link = listbase->first; link != NULL; link = link->next, i++) { - func(userdata, link, i); + TaskScheduler *task_scheduler = BLI_task_scheduler_get(); + const int num_threads = BLI_task_scheduler_num_threads(task_scheduler); + + task_parallel_iterator_calc_chunk_size(settings, num_threads, state); + + if (!settings->use_threading) { + task_parallel_iterator_no_threads(settings, state); + return; } + + const int chunk_size = state->iter_shared.chunk_size; + const int tot_items = state->tot_items; + const size_t num_tasks = tot_items >= 0 ? + (size_t)min_ii(num_threads, state->tot_items / chunk_size) : + (size_t)num_threads; + + BLI_assert(num_tasks > 0); + if (num_tasks == 1) { + task_parallel_iterator_no_threads(settings, state); + return; + } + + SpinLock spin_lock; + BLI_spin_init(&spin_lock); + state->iter_shared.spin_lock = &spin_lock; + + void *userdata_chunk = settings->userdata_chunk; + const size_t userdata_chunk_size = settings->userdata_chunk_size; + void *userdata_chunk_local = NULL; + void *userdata_chunk_array = NULL; + const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL); + + TaskPool *task_pool = BLI_task_pool_create_suspended(task_scheduler, state); + + if (use_userdata_chunk) { + userdata_chunk_array = MALLOCA(userdata_chunk_size * num_tasks); + } + + for (size_t i = 0; i < num_tasks; i++) { + if (use_userdata_chunk) { + userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i); + memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size); + } + /* Use this pool's pre-allocated tasks. */ + BLI_task_pool_push_from_thread(task_pool, + parallel_iterator_func, + userdata_chunk_local, + false, + TASK_PRIORITY_HIGH, + task_pool->thread_id); + } + + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); + + if (use_userdata_chunk) { + if (settings->func_finalize != NULL) { + for (size_t i = 0; i < num_tasks; i++) { + userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i); + settings->func_finalize(state->userdata, userdata_chunk_local); + } + } + MALLOCA_FREE(userdata_chunk_array, userdata_chunk_size * num_tasks); + } + + BLI_spin_end(&spin_lock); + state->iter_shared.spin_lock = NULL; } -/* NOTE: The idea here is to compensate for rather measurable threading - * overhead caused by fetching tasks. With too many CPU threads we are starting - * to spend too much time in those overheads. */ -BLI_INLINE int task_parallel_listbasecalc_chunk_size(const int num_threads) +/** + * This function allows to parallelize for loops using a generic iterator. + * + * \param userdata: Common userdata passed to all instances of \a func. + * \param iter_func: Callback function used to generate chunks of items. + * \param init_item: The initial item, if necessary (may be NULL if unused). + * \param init_index: The initial index. + * \param tot_items: The total amount of items to iterate over + * (if unkown, set it to a negative number). + * \param func: Callback function. + * \param settings: See public API doc of TaskParallelSettings for description of all settings. + * + * \note Static scheduling is only available when \a tot_items is >= 0. + */ + +void BLI_task_parallel_iterator(void *userdata, + TaskParallelIteratorIterFunc iter_func, + void *init_item, + const int init_index, + const int tot_items, + TaskParallelIteratorFunc func, + const TaskParallelSettings *settings) { - if (num_threads > 32) { - return 128; - } - else if (num_threads > 16) { - return 64; + TaskParallelIteratorState state = {0}; + + state.tot_items = tot_items; + state.iter_shared.next_index = init_index; + state.iter_shared.next_item = init_item; + state.iter_shared.is_finished = false; + state.userdata = userdata; + state.iter_func = iter_func; + state.func = func; + + task_parallel_iterator_do(settings, &state); +} + +static void task_parallel_listbase_get(void *__restrict UNUSED(userdata), + const TaskParallelTLS *__restrict UNUSED(tls), + void **r_next_item, + int *r_next_index, + bool *r_do_abort) +{ + /* Get current status. */ + Link *link = *r_next_item; + + if (link->next == NULL) { + *r_do_abort = true; } - return 32; + *r_next_item = link->next; + (*r_next_index)++; } /** @@ -1335,58 +1517,36 @@ BLI_INLINE int task_parallel_listbasecalc_chunk_size(const int num_threads) * \param listbase: The double linked list to loop over. * \param userdata: Common userdata passed to all instances of \a func. * \param func: Callback function. - * \param use_threading: If \a true, actually split-execute loop in threads, - * else just do a sequential forloop - * (allows caller to use any kind of test to switch on parallelization or not). + * \param settings: See public API doc of ParallelRangeSettings for description of all settings. * * \note There is no static scheduling here, * since it would need another full loop over items to count them. */ -void BLI_task_parallel_listbase(struct ListBase *listbase, +void BLI_task_parallel_listbase(ListBase *listbase, void *userdata, - TaskParallelListbaseFunc func, - const bool use_threading) + TaskParallelIteratorFunc func, + const TaskParallelSettings *settings) { if (BLI_listbase_is_empty(listbase)) { return; } - if (!use_threading) { - task_parallel_listbase_no_threads(listbase, userdata, func); - return; - } - TaskScheduler *task_scheduler = BLI_task_scheduler_get(); - const int num_threads = BLI_task_scheduler_num_threads(task_scheduler); - /* TODO(sergey): Consider making chunk size configurable. */ - const int chunk_size = task_parallel_listbasecalc_chunk_size(num_threads); - const int num_tasks = min_ii(num_threads, BLI_listbase_count(listbase) / chunk_size); - if (num_tasks <= 1) { - task_parallel_listbase_no_threads(listbase, userdata, func); - return; - } - ParallelListState state; - TaskPool *task_pool = BLI_task_pool_create_suspended(task_scheduler, &state); + TaskParallelIteratorState state = {0}; - state.index = 0; - state.link = listbase->first; + state.tot_items = BLI_listbase_count(listbase); + state.iter_shared.next_index = 0; + state.iter_shared.next_item = listbase->first; + state.iter_shared.is_finished = false; state.userdata = userdata; + state.iter_func = task_parallel_listbase_get; state.func = func; - state.chunk_size = chunk_size; - BLI_spin_init(&state.lock); - BLI_assert(num_tasks > 0); - for (int i = 0; i < num_tasks; i++) { - /* Use this pool's pre-allocated tasks. */ - BLI_task_pool_push_from_thread( - task_pool, parallel_listbase_func, NULL, false, TASK_PRIORITY_HIGH, task_pool->thread_id); - } - - BLI_task_pool_work_and_wait(task_pool); - BLI_task_pool_free(task_pool); - - BLI_spin_end(&state.lock); + task_parallel_iterator_do(settings, &state); } +#undef MALLOCA +#undef MALLOCA_FREE + typedef struct ParallelMempoolState { void *userdata; TaskParallelMempoolFunc func; diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index c9fb8b6990b..9fce89558b6 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -435,6 +435,9 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) * Note that sculpt is an exception, * it's values are overwritten by #BKE_brush_sculpt_reset below. */ brush->alpha = 1.0; + + /* Enable antialiasing by default */ + brush->sampling_flag |= BRUSH_PAINT_ANTIALIASING; } { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index f1e7278ffdb..f68b03034a9 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -120,31 +120,72 @@ namespace DEG { /* ***************** */ /* Relations Builder */ +namespace { + /* TODO(sergey): This is somewhat weak, but we don't want neither false-positive - * time dependencies nor special exceptions in the depsgraph evaluation. - */ -static bool python_driver_depends_on_time(ChannelDriver *driver) + * time dependencies nor special exceptions in the depsgraph evaluation. */ + +bool python_driver_exression_depends_on_time(const char *expression) { - if (driver->expression[0] == '\0') { + if (expression[0] == '\0') { /* Empty expression depends on nothing. */ return false; } - if (strchr(driver->expression, '(') != NULL) { + if (strchr(expression, '(') != NULL) { /* Function calls are considered dependent on a time. */ return true; } - if (strstr(driver->expression, "frame") != NULL) { + if (strstr(expression, "frame") != NULL) { /* Variable `frame` depends on time. */ - /* TODO(sergey): This is a bit weak, but not sure about better way of - * handling this. */ + /* TODO(sergey): This is a bit weak, but not sure about better way of handling this. */ + return true; + } + /* Possible indirect time relation s should be handled via variable targets. */ + return false; +} + +bool driver_target_depends_on_time(const DriverTarget *target) +{ + if (target->idtype == ID_SCE && STREQ(target->rna_path, "frame_current")) { return true; } - /* Possible indirect time relation s should be handled via variable - * targets. */ return false; } -static bool particle_system_depends_on_time(ParticleSystem *psys) +bool driver_variable_depends_on_time(const DriverVar *variable) +{ + for (int i = 0; i < variable->num_targets; ++i) { + if (driver_target_depends_on_time(&variable->targets[i])) { + return true; + } + } + return false; +} + +bool driver_variables_depends_on_time(const ListBase *variables) +{ + LISTBASE_FOREACH (const DriverVar *, variable, variables) { + if (driver_variable_depends_on_time(variable)) { + return true; + } + } + return false; +} + +bool driver_depends_on_time(ChannelDriver *driver) +{ + if (driver->type == DRIVER_TYPE_PYTHON) { + if (python_driver_exression_depends_on_time(driver->expression)) { + return true; + } + } + if (driver_variables_depends_on_time(&driver->variables)) { + return true; + } + return false; +} + +bool particle_system_depends_on_time(ParticleSystem *psys) { ParticleSettings *part = psys->part; /* Non-hair particles we always consider dependent on time. */ @@ -159,7 +200,7 @@ static bool particle_system_depends_on_time(ParticleSystem *psys) return false; } -static bool object_particles_depends_on_time(Object *object) +bool object_particles_depends_on_time(Object *object) { if (object->type != OB_MESH) { return false; @@ -172,7 +213,7 @@ static bool object_particles_depends_on_time(Object *object) return false; } -static bool check_id_has_anim_component(ID *id) +bool check_id_has_anim_component(ID *id) { AnimData *adt = BKE_animdata_from_id(id); if (adt == NULL) { @@ -181,11 +222,11 @@ static bool check_id_has_anim_component(ID *id) return (adt->action != NULL) || (!BLI_listbase_is_empty(&adt->nla_tracks)); } -static OperationCode bone_target_opcode(ID *target, - const char *subtarget, - ID *id, - const char *component_subdata, - RootPChanMap *root_map) +OperationCode bone_target_opcode(ID *target, + const char *subtarget, + ID *id, + const char *component_subdata, + RootPChanMap *root_map) { /* Same armature. */ if (target == id) { @@ -200,11 +241,13 @@ static OperationCode bone_target_opcode(ID *target, return OperationCode::BONE_DONE; } -static bool object_have_geometry_component(const Object *object) +bool object_have_geometry_component(const Object *object) { return ELEM(object->type, OB_MESH, OB_CURVE, OB_FONT, OB_SURF, OB_MBALL, OB_LATTICE, OB_GPENCIL); } +} // namespace + /* **** General purpose functions **** */ DepsgraphRelationBuilder::DepsgraphRelationBuilder(Main *bmain, @@ -1380,7 +1423,7 @@ void DepsgraphRelationBuilder::build_driver(ID *id, FCurve *fcu) /* It's quite tricky to detect if the driver actually depends on time or * not, so for now we'll be quite conservative here about optimization and * consider all python drivers to be depending on time. */ - if ((driver->type == DRIVER_TYPE_PYTHON) && python_driver_depends_on_time(driver)) { + if (driver_depends_on_time(driver)) { TimeSourceKey time_src_key; add_relation(time_src_key, driver_key, "TimeSrc -> Driver"); } diff --git a/source/blender/draw/modes/object_mode.c b/source/blender/draw/modes/object_mode.c index 70bccb4849c..fe17019a5b5 100644 --- a/source/blender/draw/modes/object_mode.c +++ b/source/blender/draw/modes/object_mode.c @@ -1235,22 +1235,42 @@ static void DRW_shgroup_camera_background_images(OBJECT_Shaders *sh_data, uv2img_space[1][1] = image_height; const float fit_scale = image_aspect / camera_aspect; - img2cam_space[0][0] = 1.0 / image_width; - img2cam_space[1][1] = 1.0 / fit_scale / image_height; + if (camera_aspect < image_aspect) { + img2cam_space[0][0] = 1.0 / (1.0 / fit_scale) / image_width; + img2cam_space[1][1] = 1.0 / image_height; + } + else { + img2cam_space[0][0] = 1.0 / image_width; + img2cam_space[1][1] = 1.0 / fit_scale / image_height; + } /* Update scaling based on image and camera framing */ float scale_x = bgpic->scale; float scale_y = bgpic->scale; + float scale_x_offset = image_width; + float scale_y_offset = image_height; + if (image_aspect > 1.0f) { + scale_x_offset /= image_aspect; + if (camera_aspect > 1.0f) { + scale_x_offset *= min_ff(image_aspect, camera_aspect); + scale_y_offset *= min_ff(image_aspect, camera_aspect); + } + } + else { + scale_y_offset *= image_aspect; + if (camera_aspect < 1.0f) { + scale_x_offset /= max_ff(image_aspect, camera_aspect); + scale_y_offset /= max_ff(image_aspect, camera_aspect); + } + } + if (bgpic->flag & CAM_BGIMG_FLAG_CAMERA_ASPECT) { if (bgpic->flag & CAM_BGIMG_FLAG_CAMERA_CROP) { - if (image_aspect > camera_aspect) { - scale_x *= fit_scale; - scale_y *= fit_scale; - } + /* pass */ } else { - if (image_aspect > camera_aspect) { + if (camera_aspect < image_aspect) { scale_x /= fit_scale; scale_y /= fit_scale; } @@ -1262,7 +1282,12 @@ static void DRW_shgroup_camera_background_images(OBJECT_Shaders *sh_data, } else { /* Stretch image to camera aspect */ - scale_y /= 1.0 / fit_scale; + if (camera_aspect < image_aspect) { + scale_x /= fit_scale; + } + else { + scale_y *= fit_scale; + } } // scale image to match the desired aspect ratio @@ -1270,8 +1295,8 @@ static void DRW_shgroup_camera_background_images(OBJECT_Shaders *sh_data, scale_m4[1][1] = scale_y; /* Translate */ - translate_m4[3][0] = image_width * bgpic->offset[0] * 2.0f; - translate_m4[3][1] = image_height * bgpic->offset[1] * 2.0f; + translate_m4[3][0] = bgpic->offset[0] * 2.0f * scale_x_offset; + translate_m4[3][1] = bgpic->offset[1] * 2.0f * scale_y_offset; mul_m4_series(bg_data->transform_mat, win_m4_translate, diff --git a/source/blender/draw/modes/sculpt_mode.c b/source/blender/draw/modes/sculpt_mode.c index 9749619cffe..f32eb3b73ba 100644 --- a/source/blender/draw/modes/sculpt_mode.c +++ b/source/blender/draw/modes/sculpt_mode.c @@ -157,7 +157,7 @@ static void SCULPT_cache_populate(void *vedata, Object *ob) if ((ob == draw_ctx->obact) && (BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) || - ob->sculpt->deform_modifiers_active || ob->sculpt->shapekey_active)) { + !ob->sculpt->deform_modifiers_active || ob->sculpt->shapekey_active)) { PBVH *pbvh = ob->sculpt->pbvh; if (pbvh && pbvh_has_mask(pbvh)) { DRW_shgroup_call_sculpt(stl->g_data->mask_overlay_grp, ob, false, true, false); diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 1c7edb6c46a..680fe3a00e5 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -693,6 +693,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.gpencil.primitive_circle ops.gpencil.primitive_curve ops.gpencil.primitive_line + ops.gpencil.primitive_polyline ops.gpencil.radius ops.gpencil.sculpt_clone ops.gpencil.sculpt_grab @@ -728,6 +729,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.mesh.spin.duplicate ops.mesh.vertices_smooth ops.node.links_cut + ops.paint.eyedropper_add ops.paint.vertex_color_fill ops.paint.weight_fill ops.paint.weight_gradient diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index c4f18c60f4d..3ab11f8f3f7 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -1538,6 +1538,7 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot) /* GPencil layer to use. */ ot->prop = RNA_def_int(ot->srna, "layer", 0, 0, INT_MAX, "Grease Pencil Layer", "", 0, INT_MAX); + RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /* ********************* Add Blank Frame *************************** */ diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index 40e0005b487..0e20abe4221 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -68,6 +68,8 @@ struct SnapObjectParams { unsigned int use_object_edit_cage : 1; /* snap to the closest element, use when using more than one snap type */ unsigned int use_occlusion_test : 1; + /* exclude back facing geometry from snapping */ + unsigned int use_backface_culling : 1; }; typedef struct SnapObjectContext SnapObjectContext; diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index d6a1d7f193c..c15f299bca8 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -857,7 +857,7 @@ DEF_ICON(SCRIPTPLUGINS) DEF_ICON_BLANK(855) DEF_ICON_BLANK(856) DEF_ICON_BLANK(857) -DEF_ICON(DISK) +DEF_ICON(DISC) DEF_ICON(DESKTOP) DEF_ICON(EXTERNAL_DRIVE) DEF_ICON(NETWORK_DRIVE) diff --git a/source/blender/editors/mesh/editmesh_extrude_spin.c b/source/blender/editors/mesh/editmesh_extrude_spin.c index bf4403e49ee..7cad7e1e062 100644 --- a/source/blender/editors/mesh/editmesh_extrude_spin.c +++ b/source/blender/editors/mesh/editmesh_extrude_spin.c @@ -200,7 +200,10 @@ void MESH_OT_spin(wmOperatorType *ot) /* props */ RNA_def_int(ot->srna, "steps", 9, 0, 1000000, "Steps", "Steps", 0, 1000); - RNA_def_boolean(ot->srna, "dupli", 0, "Duplicate", "Make Duplicates"); + + prop = RNA_def_boolean(ot->srna, "dupli", 0, "Use Duplicates", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_float(ot->srna, "angle", DEG2RADF(90.0f), diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c index 8d98a3bf231..25d3118b3a9 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.c +++ b/source/blender/editors/mesh/editmesh_mask_extract.c @@ -30,6 +30,7 @@ #include "BKE_context.h" #include "BKE_editmesh.h" #include "BKE_layer.h" +#include "BKE_library.h" #include "BKE_mesh.h" #include "BKE_modifier.h" #include "BKE_paint.h" @@ -179,7 +180,7 @@ static int paint_mask_extract_exec(bContext *C, wmOperator *op) BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); - BKE_mesh_free(new_mesh); + BKE_id_free(bmain, new_mesh); new_mesh = BKE_mesh_from_bmesh_nomain(bm, (&(struct BMeshToMeshParams){ .calc_object_remap = false, @@ -190,7 +191,7 @@ static int paint_mask_extract_exec(bContext *C, wmOperator *op) MEM_freeN(em); if (new_mesh->totvert == 0) { - BKE_mesh_free(new_mesh); + BKE_id_free(bmain, new_mesh); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/mesh/editmesh_polybuild.c b/source/blender/editors/mesh/editmesh_polybuild.c index 21c850160dd..a91f0f9274e 100644 --- a/source/blender/editors/mesh/editmesh_polybuild.c +++ b/source/blender/editors/mesh/editmesh_polybuild.c @@ -216,7 +216,7 @@ static int edbm_polybuild_delete_at_cursor_invoke(bContext *C, } if (ele_act->head.htype == BM_VERT) { BMVert *v_act = (BMVert *)ele_act; - if (BM_vert_is_edge_pair(v_act)) { + if (BM_vert_is_edge_pair(v_act) && !BM_vert_is_wire(v_act)) { BM_edge_collapse(bm, v_act->e, v_act, true, true); changed = true; } diff --git a/source/blender/editors/render/render_view.c b/source/blender/editors/render/render_view.c index a54701f8725..8bc84388a1b 100644 --- a/source/blender/editors/render/render_view.c +++ b/source/blender/editors/render/render_view.c @@ -118,7 +118,7 @@ static ScrArea *find_area_image_empty(bContext *C) for (sa = sc->areabase.first; sa; sa = sa->next) { if (sa->spacetype == SPACE_IMAGE) { sima = sa->spacedata.first; - if (!sima->image) { + if ((sima->mode == SI_MODE_VIEW) && !sima->image) { break; } } diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 9c95a3cee4d..a1e67e78a10 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -387,7 +387,12 @@ static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, m = mask; int aa_samples = 1.0f / (radius * 0.20f); - aa_samples = clamp_i(aa_samples, 3, 16); + if (brush->sampling_flag & BRUSH_PAINT_ANTIALIASING) { + aa_samples = clamp_i(aa_samples, 3, 16); + } + else { + aa_samples = 1; + } /* Temporal until we have the brush properties */ const float hardness = 1.0f; diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 77c95c6acb3..ac7cf310099 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -2341,7 +2341,7 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P /* calculate pivot for rotation around seletion if needed */ /* also needed for "View Selected" on last stroke */ - paint_last_stroke_update(scene, ss->cache->location); + paint_last_stroke_update(scene, ss->cache->true_location); BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); @@ -3316,7 +3316,7 @@ static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P /* calculate pivot for rotation around seletion if needed */ /* also needed for "View Selected" on last stroke */ - paint_last_stroke_update(scene, ss->cache->location); + paint_last_stroke_update(scene, ss->cache->true_location); ED_region_tag_redraw(vc->ar); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 5d95cc80280..fc990c01bfb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -77,6 +77,7 @@ static UndoSculpt *sculpt_undo_get_nodes(void); static void update_cb(PBVHNode *node, void *rebuild) { BKE_pbvh_node_mark_update(node); + BKE_pbvh_node_mark_update_mask(node); if (*((bool *)rebuild)) { BKE_pbvh_node_mark_rebuild_draw(node); } @@ -497,7 +498,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase SculptSession *ss = ob->sculpt; SubdivCCG *subdiv_ccg = ss->subdiv_ccg; SculptUndoNode *unode; - bool update = false, rebuild = false; + bool update = false, rebuild = false, update_mask = false; bool need_mask = false; for (unode = lb->first; unode; unode = unode->next) { @@ -579,6 +580,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase case SCULPT_UNDO_MASK: if (sculpt_undo_restore_mask(C, unode)) { update = true; + update_mask = true; } break; @@ -616,6 +618,9 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase }; BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb_partial, &data); BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw); + if (update_mask) { + BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask); + } if (BKE_sculpt_multires_active(scene, ob)) { if (rebuild) { diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index e61375b2d4c..1ea7d81f9da 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -338,7 +338,7 @@ enum { enum { SPECIAL_IMG_DOCUMENT = 0, - SPECIAL_IMG_DRIVE_DISK = 1, + SPECIAL_IMG_DRIVE_DISC = 1, SPECIAL_IMG_FOLDER = 2, SPECIAL_IMG_PARENT = 3, SPECIAL_IMG_DRIVE_FIXED = 4, diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c index 577c4e24b11..b6b32293cee 100644 --- a/source/blender/editors/space_image/image_undo.c +++ b/source/blender/editors/space_image/image_undo.c @@ -115,7 +115,7 @@ typedef struct PaintTile { ushort *mask; bool valid; bool use_float; - int x, y; + int x_tile, y_tile; } PaintTile; static void ptile_free(PaintTile *ptile) @@ -154,7 +154,7 @@ void *ED_image_paint_tile_find(ListBase *paint_tiles, bool validate) { for (PaintTile *ptile = paint_tiles->first; ptile; ptile = ptile->next) { - if (ptile->x == x_tile && ptile->y == y_tile) { + if (ptile->x_tile == x_tile && ptile->y_tile == y_tile) { if (ptile->image == image && ptile->ibuf == ibuf) { if (r_mask) { /* allocate mask if requested. */ @@ -206,8 +206,8 @@ void *ED_image_paint_tile_push(ListBase *paint_tiles, ptile->image = image; ptile->ibuf = ibuf; - ptile->x = x_tile; - ptile->y = y_tile; + ptile->x_tile = x_tile; + ptile->y_tile = y_tile; /* add mask explicitly here */ if (r_mask) { @@ -269,8 +269,14 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles) SWAP(uint *, ptile->rect.uint, tmpibuf->rect); } - IMB_rectcpy( - ibuf, tmpibuf, ptile->x, ptile->y, 0, 0, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE); + IMB_rectcpy(ibuf, + tmpibuf, + ptile->x_tile * ED_IMAGE_UNDO_TILE_SIZE, + ptile->y_tile * ED_IMAGE_UNDO_TILE_SIZE, + 0, + 0, + ED_IMAGE_UNDO_TILE_SIZE, + ED_IMAGE_UNDO_TILE_SIZE); if (has_float) { SWAP(float *, ptile->rect.fp, tmpibuf->rect_float); @@ -764,7 +770,7 @@ static bool image_undosys_step_encode(struct bContext *C, utile->users = 1; utile->rect.pt = ptile->rect.pt; ptile->rect.pt = NULL; - const uint tile_index = index_from_xy(ptile->x, ptile->y, ubuf_pre->tiles_dims); + const uint tile_index = index_from_xy(ptile->x_tile, ptile->y_tile, ubuf_pre->tiles_dims); BLI_assert(ubuf_pre->tiles[tile_index] == NULL); ubuf_pre->tiles[tile_index] = utile; diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 3b86e04308e..d7a7d57b100 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -756,14 +756,15 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname) } case TSE_BONE: { - ViewLayer *view_layer = CTX_data_view_layer(C); - Scene *scene = CTX_data_scene(C); + TreeViewContext tvc; + outliner_viewcontext_init(C, &tvc); + bArmature *arm = (bArmature *)tselem->id; Bone *bone = te->directdata; char newname[sizeof(bone->name)]; /* always make current object active */ - tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, true); + tree_element_active(C, &tvc, soops, te, OL_SETSEL_NORMAL, true); /* restore bone name */ BLI_strncpy(newname, bone->name, sizeof(bone->name)); @@ -773,14 +774,15 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname) break; } case TSE_POSE_CHANNEL: { - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); + TreeViewContext tvc; + outliner_viewcontext_init(C, &tvc); + Object *ob = (Object *)tselem->id; bPoseChannel *pchan = te->directdata; char newname[sizeof(pchan->name)]; /* always make current pose-bone active */ - tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, true); + tree_element_active(C, &tvc, soops, te, OL_SETSEL_NORMAL, true); BLI_assert(ob->type == OB_ARMATURE); @@ -2815,8 +2817,7 @@ typedef struct MergedIconRow { static void outliner_draw_iconrow(bContext *C, uiBlock *block, const uiFontStyle *fstyle, - Scene *scene, - ViewLayer *view_layer, + const TreeViewContext *tvc, SpaceOutliner *soops, ListBase *lb, int level, @@ -2827,7 +2828,6 @@ static void outliner_draw_iconrow(bContext *C, MergedIconRow *merged) { eOLDrawState active = OL_DRAWSEL_NONE; - const Object *obact = OBACT(view_layer); for (TreeElement *te = lb->first; te; te = te->next) { TreeStoreElem *tselem = TREESTORE(te); @@ -2837,14 +2837,13 @@ static void outliner_draw_iconrow(bContext *C, /* active blocks get white circle */ if (tselem->type == 0) { if (te->idcode == ID_OB) { - active = (OBACT(view_layer) == (Object *)tselem->id) ? OL_DRAWSEL_NORMAL : - OL_DRAWSEL_NONE; + active = (tvc->obact == (Object *)tselem->id) ? OL_DRAWSEL_NORMAL : OL_DRAWSEL_NONE; } - else if (is_object_data_in_editmode(tselem->id, obact)) { + else if (is_object_data_in_editmode(tselem->id, tvc->obact)) { active = OL_DRAWSEL_ACTIVE; } else { - active = tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NONE, false); + active = tree_element_active(C, tvc, soops, te, OL_SETSEL_NONE, false); } } else if (tselem->type == TSE_GP_LAYER) { @@ -2852,8 +2851,7 @@ static void outliner_draw_iconrow(bContext *C, active = (gpl->flag & GP_LAYER_ACTIVE) ? OL_DRAWSEL_ACTIVE : OL_DRAWSEL_NONE; } else { - active = tree_element_type_active( - C, scene, view_layer, soops, te, tselem, OL_SETSEL_NONE, false); + active = tree_element_type_active(C, tvc, soops, te, tselem, OL_SETSEL_NONE, false); } if (!ELEM(tselem->type, 0, TSE_LAYER_COLLECTION, TSE_R_LAYER, TSE_GP_LAYER)) { @@ -2874,8 +2872,7 @@ static void outliner_draw_iconrow(bContext *C, outliner_draw_iconrow(C, block, fstyle, - scene, - view_layer, + tvc, soops, &te->subtree, level + 1, @@ -2933,8 +2930,7 @@ static void outliner_set_coord_tree_element(TreeElement *te, int startx, int sta static void outliner_draw_tree_element(bContext *C, uiBlock *block, const uiFontStyle *fstyle, - Scene *scene, - ViewLayer *view_layer, + const TreeViewContext *tvc, ARegion *ar, SpaceOutliner *soops, TreeElement *te, @@ -2973,9 +2969,8 @@ static void outliner_draw_tree_element(bContext *C, /* colors for active/selected data */ if (tselem->type == 0) { - const Object *obact = OBACT(view_layer); if (te->idcode == ID_SCE) { - if (tselem->id == (ID *)scene) { + if (tselem->id == (ID *)tvc->scene) { /* active scene */ icon_bgcolor[3] = 0.2f; active = OL_DRAWSEL_ACTIVE; @@ -2984,15 +2979,15 @@ static void outliner_draw_tree_element(bContext *C, else if (te->idcode == ID_OB) { Object *ob = (Object *)tselem->id; Base *base = (te->directdata) ? (Base *)te->directdata : - BKE_view_layer_base_find(view_layer, ob); + BKE_view_layer_base_find(tvc->view_layer, ob); const bool is_selected = (base != NULL) && ((base->flag & BASE_SELECTED) != 0); - if (ob == obact) { + if (ob == tvc->obact) { active = OL_DRAWSEL_ACTIVE; } if (is_selected) { - if (ob == obact) { + if (ob == tvc->obact) { /* active selected object */ UI_GetThemeColor3ubv(TH_ACTIVE_OBJECT, text_color); text_color[3] = 255; @@ -3004,14 +2999,14 @@ static void outliner_draw_tree_element(bContext *C, } } } - else if (is_object_data_in_editmode(tselem->id, obact)) { + else if (is_object_data_in_editmode(tselem->id, tvc->obact)) { /* objects being edited */ UI_GetThemeColor4fv(TH_EDITED_OBJECT, icon_bgcolor); icon_border[3] = 0.3f; active = OL_DRAWSEL_ACTIVE; } else { - if (tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NONE, false)) { + if (tree_element_active(C, tvc, soops, te, OL_SETSEL_NONE, false)) { /* active items like camera or material */ icon_bgcolor[3] = 0.2f; active = OL_DRAWSEL_ACTIVE; @@ -3026,8 +3021,7 @@ static void outliner_draw_tree_element(bContext *C, } } else { - active = tree_element_type_active( - C, scene, view_layer, soops, te, tselem, OL_SETSEL_NONE, false); + active = tree_element_type_active(C, tvc, soops, te, tselem, OL_SETSEL_NONE, false); /* active collection*/ icon_bgcolor[3] = 0.2f; } @@ -3036,7 +3030,7 @@ static void outliner_draw_tree_element(bContext *C, if ((tselem->type == TSE_LAYER_COLLECTION) && (soops->show_restrict_flags & SO_RESTRICT_ENABLE)) { tselem_draw_layer_collection_enable_icon( - scene, block, xmax, (float)startx + offsx + UI_UNIT_X, (float)*starty, te, 0.8f); + tvc->scene, block, xmax, (float)startx + offsx + UI_UNIT_X, (float)*starty, te, 0.8f); offsx += UI_UNIT_X; } @@ -3155,8 +3149,7 @@ static void outliner_draw_tree_element(bContext *C, outliner_draw_iconrow(C, block, fstyle, - scene, - view_layer, + tvc, soops, &te->subtree, 0, @@ -3186,8 +3179,7 @@ static void outliner_draw_tree_element(bContext *C, outliner_draw_tree_element(C, block, fstyle, - scene, - view_layer, + tvc, ar, soops, ten, @@ -3486,8 +3478,7 @@ static void outliner_draw_highlights(ARegion *ar, SpaceOutliner *soops, int star static void outliner_draw_tree(bContext *C, uiBlock *block, - Scene *scene, - ViewLayer *view_layer, + const TreeViewContext *tvc, ARegion *ar, SpaceOutliner *soops, const float restrict_column_width, @@ -3533,8 +3524,7 @@ static void outliner_draw_tree(bContext *C, outliner_draw_tree_element(C, block, fstyle, - scene, - view_layer, + tvc, ar, soops, te, @@ -3625,15 +3615,16 @@ static void outliner_update_viewable_area(ARegion *ar, void draw_outliner(const bContext *C) { Main *mainvar = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); ARegion *ar = CTX_wm_region(C); View2D *v2d = &ar->v2d; SpaceOutliner *soops = CTX_wm_space_outliner(C); uiBlock *block; TreeElement *te_edit = NULL; - outliner_build_tree(mainvar, scene, view_layer, soops, ar); // always + TreeViewContext tvc; + outliner_viewcontext_init(C, &tvc); + + outliner_build_tree(mainvar, tvc.scene, tvc.view_layer, soops, ar); // always /* If global sync select is dirty, flag other outliners */ if (ED_outliner_select_sync_is_dirty(C)) { @@ -3655,8 +3646,7 @@ void draw_outliner(const bContext *C) const float restrict_column_width = outliner_restrict_columns_width(soops); outliner_back(ar); block = UI_block_begin(C, ar, __func__, UI_EMBOSS); - outliner_draw_tree( - (bContext *)C, block, scene, view_layer, ar, soops, restrict_column_width, &te_edit); + outliner_draw_tree((bContext *)C, block, &tvc, ar, soops, restrict_column_width, &te_edit); /* Compute outliner dimensions after it has been drawn. */ int tree_width, tree_height; @@ -3682,7 +3672,8 @@ void draw_outliner(const bContext *C) /* draw restriction columns */ RestrictPropertiesActive props_active; memset(&props_active, 1, sizeof(RestrictPropertiesActive)); - outliner_draw_restrictbuts(block, scene, view_layer, ar, soops, &soops->tree, props_active); + outliner_draw_restrictbuts( + block, tvc.scene, tvc.view_layer, ar, soops, &soops->tree, props_active); } UI_block_emboss_set(block, UI_EMBOSS); diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index c55140db46f..34bbf3a2a30 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -1600,7 +1600,11 @@ static void tree_element_show_hierarchy(Scene *scene, SpaceOutliner *soops, List for (te = lb->first; te; te = te->next) { tselem = TREESTORE(te); - if (tselem->type == 0) { + if (ELEM(tselem->type, + 0, + TSE_SCENE_OBJECTS_BASE, + TSE_VIEW_COLLECTION_BASE, + TSE_LAYER_COLLECTION)) { if (te->idcode == ID_SCE) { if (tselem->id != (ID *)scene) { tselem->flag |= TSE_CLOSED; diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 95e37dea249..23c883c0db3 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -200,6 +200,25 @@ typedef enum { #define TSELEM_OPEN(telm, sv) \ ((telm->flag & TSE_CLOSED) == 0 || (SEARCHING_OUTLINER(sv) && (telm->flag & TSE_CHILDSEARCH))) +/** + * Container to avoid passing around these variables to many functions. + * Also so we can have one place to assing these variables. + */ +typedef struct TreeViewContext { + /* Scene level. */ + struct Scene *scene; + struct ViewLayer *view_layer; + + /* Object level. */ + /** Avoid OBACT macro everywhere. */ + Object *obact; + Object *ob_edit; + /** + * The pose object may not be the active object (when in weight paint mode). + * Checking this in draw loops isn't efficient, so set only once. */ + Object *ob_pose; +} TreeViewContext; + /* outliner_tree.c ----------------------------------------------- */ void outliner_free_tree(ListBase *tree); @@ -237,16 +256,14 @@ int tree_element_id_type_to_index(TreeElement *te); /* outliner_select.c -------------------------------------------- */ eOLDrawState tree_element_type_active(struct bContext *C, - struct Scene *scene, - struct ViewLayer *view_layer, + const TreeViewContext *tvc, struct SpaceOutliner *soops, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, bool recursive); eOLDrawState tree_element_active(struct bContext *C, - struct Scene *scene, - struct ViewLayer *view_layer, + const TreeViewContext *tvc, SpaceOutliner *soops, TreeElement *te, const eOLSetState set, @@ -267,7 +284,8 @@ void outliner_object_mode_toggle(struct bContext *C, void outliner_element_activate(struct SpaceOutliner *soops, struct TreeStoreElem *tselem); -bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x); +bool outliner_item_is_co_over_name_icons(const TreeElement *te, float view_co_x); +bool outliner_item_is_co_within_close_toggle(const TreeElement *te, float view_co_x); /* outliner_edit.c ---------------------------------------------- */ typedef void (*outliner_operation_cb)(struct bContext *C, @@ -457,6 +475,8 @@ void OUTLINER_OT_unhide_all(struct wmOperatorType *ot); /* outliner_utils.c ---------------------------------------------- */ +void outliner_viewcontext_init(const struct bContext *C, TreeViewContext *tvc); + TreeElement *outliner_find_item_at_y(const SpaceOutliner *soops, const ListBase *tree, float view_co_y); diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 19bbb115788..c96f2f9956f 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -595,6 +595,7 @@ static eOLDrawState tree_element_active_posegroup(bContext *C, static eOLDrawState tree_element_active_posechannel(bContext *C, Scene *UNUSED(scene), ViewLayer *view_layer, + Object *ob_pose, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set, @@ -651,7 +652,7 @@ static eOLDrawState tree_element_active_posechannel(bContext *C, } } else { - if (ob == OBACT(view_layer) && ob->pose) { + if (ob == ob_pose && ob->pose) { if (pchan->bone->flag & BONE_SELECTED) { return OL_DRAWSEL_NORMAL; } @@ -1007,8 +1008,7 @@ static eOLDrawState tree_element_active_layer_collection(bContext *C, /* generic call for ID data check or make/check active in UI */ eOLDrawState tree_element_active(bContext *C, - Scene *scene, - ViewLayer *view_layer, + const TreeViewContext *tvc, SpaceOutliner *soops, TreeElement *te, const eOLSetState set, @@ -1020,17 +1020,18 @@ eOLDrawState tree_element_active(bContext *C, * See #do_outliner_item_activate. */ case ID_OB: if (handle_all_types) { - return tree_element_set_active_object(C, scene, view_layer, soops, te, set, false); + return tree_element_set_active_object( + C, tvc->scene, tvc->view_layer, soops, te, set, false); } break; case ID_MA: - return tree_element_active_material(C, scene, view_layer, soops, te, set); + return tree_element_active_material(C, tvc->scene, tvc->view_layer, soops, te, set); case ID_WO: - return tree_element_active_world(C, scene, view_layer, soops, te, set); + return tree_element_active_world(C, tvc->scene, tvc->view_layer, soops, te, set); case ID_TXT: - return tree_element_active_text(C, scene, view_layer, soops, te, set); + return tree_element_active_text(C, tvc->scene, tvc->view_layer, soops, te, set); case ID_CA: - return tree_element_active_camera(C, scene, view_layer, soops, te, set); + return tree_element_active_camera(C, tvc->scene, tvc->view_layer, soops, te, set); } return OL_DRAWSEL_NONE; } @@ -1039,8 +1040,7 @@ eOLDrawState tree_element_active(bContext *C, * Generic call for non-id data to make/check active in UI */ eOLDrawState tree_element_type_active(bContext *C, - Scene *scene, - ViewLayer *view_layer, + const TreeViewContext *tvc, SpaceOutliner *soops, TreeElement *te, TreeStoreElem *tselem, @@ -1049,41 +1049,42 @@ eOLDrawState tree_element_type_active(bContext *C, { switch (tselem->type) { case TSE_DEFGROUP: - return tree_element_active_defgroup(C, view_layer, te, tselem, set); + return tree_element_active_defgroup(C, tvc->view_layer, te, tselem, set); case TSE_BONE: - return tree_element_active_bone(C, view_layer, te, tselem, set, recursive); + return tree_element_active_bone(C, tvc->view_layer, te, tselem, set, recursive); case TSE_EBONE: - return tree_element_active_ebone(C, view_layer, te, tselem, set, recursive); + return tree_element_active_ebone(C, tvc->view_layer, te, tselem, set, recursive); case TSE_MODIFIER: - return tree_element_active_modifier(C, scene, view_layer, te, tselem, set); + return tree_element_active_modifier(C, tvc->scene, tvc->view_layer, te, tselem, set); case TSE_LINKED_OB: if (set != OL_SETSEL_NONE) { - tree_element_set_active_object(C, scene, view_layer, soops, te, set, false); + tree_element_set_active_object(C, tvc->scene, tvc->view_layer, soops, te, set, false); } - else if (tselem->id == (ID *)OBACT(view_layer)) { + else if (tselem->id == (ID *)tvc->obact) { return OL_DRAWSEL_NORMAL; } break; case TSE_LINKED_PSYS: - return tree_element_active_psys(C, scene, te, tselem, set); + return tree_element_active_psys(C, tvc->scene, te, tselem, set); case TSE_POSE_BASE: - return tree_element_active_pose(C, scene, view_layer, te, tselem, set); + return tree_element_active_pose(C, tvc->scene, tvc->view_layer, te, tselem, set); case TSE_POSE_CHANNEL: - return tree_element_active_posechannel(C, scene, view_layer, te, tselem, set, recursive); + return tree_element_active_posechannel( + C, tvc->scene, tvc->view_layer, tvc->ob_pose, te, tselem, set, recursive); case TSE_CONSTRAINT: - return tree_element_active_constraint(C, scene, view_layer, te, tselem, set); + return tree_element_active_constraint(C, tvc->scene, tvc->view_layer, te, tselem, set); case TSE_R_LAYER: - return active_viewlayer(C, scene, view_layer, te, set); + return active_viewlayer(C, tvc->scene, tvc->view_layer, te, set); case TSE_POSEGRP: - return tree_element_active_posegroup(C, scene, view_layer, te, tselem, set); + return tree_element_active_posegroup(C, tvc->scene, tvc->view_layer, te, tselem, set); case TSE_SEQUENCE: - return tree_element_active_sequence(C, scene, te, tselem, set); + return tree_element_active_sequence(C, tvc->scene, te, tselem, set); case TSE_SEQUENCE_DUP: - return tree_element_active_sequence_dup(scene, te, tselem, set); + return tree_element_active_sequence_dup(tvc->scene, te, tselem, set); case TSE_KEYMAP_ITEM: - return tree_element_active_keymap_item(C, scene, view_layer, te, tselem, set); + return tree_element_active_keymap_item(C, tvc->scene, tvc->view_layer, te, tselem, set); case TSE_GP_LAYER: - return tree_element_active_gplayer(C, scene, te, tselem, set); + return tree_element_active_gplayer(C, tvc->scene, te, tselem, set); break; case TSE_VIEW_COLLECTION_BASE: return tree_element_active_master_collection(C, te, set); @@ -1109,14 +1110,15 @@ void outliner_element_activate(SpaceOutliner *soops, TreeStoreElem *tselem) * Needed to run from operators accessed from a menu. */ static void do_outliner_item_activate_tree_element(bContext *C, - Scene *scene, - ViewLayer *view_layer, + const TreeViewContext *tvc, SpaceOutliner *soops, TreeElement *te, TreeStoreElem *tselem, const bool extend, - const bool recursive) + const bool recursive, + const bool is_over_name_icons) { + bool do_activate_data = soops->flag & SO_SYNC_SELECT || is_over_name_icons; /* Always makes active object, except for some specific types. */ if (ELEM(tselem->type, TSE_SEQUENCE, @@ -1133,11 +1135,11 @@ static void do_outliner_item_activate_tree_element(bContext *C, else if (tselem->type == TSE_POSE_BASE) { /* Support pose mode toggle, keeping the active object as is. */ } - else if (soops->flag & SO_SYNC_SELECT) { + else if (do_activate_data) { /* Only activate when synced selection is enabled */ tree_element_set_active_object(C, - scene, - view_layer, + tvc->scene, + tvc->view_layer, soops, te, (extend && tselem->type == 0) ? OL_SETSEL_EXTEND : @@ -1149,9 +1151,11 @@ static void do_outliner_item_activate_tree_element(bContext *C, outliner_element_activate(soops, tselem); if (tselem->type == 0) { // the lib blocks - /* editmode? */ - if (te->idcode == ID_SCE) { - if (scene != (Scene *)tselem->id) { + if (do_activate_data == false) { + /* Only select in outliner. */ + } + else if (te->idcode == ID_SCE) { + if (tvc->scene != (Scene *)tselem->id) { WM_window_set_active_scene(CTX_data_main(C), C, CTX_wm_window(C), (Scene *)tselem->id); } } @@ -1161,7 +1165,7 @@ static void do_outliner_item_activate_tree_element(bContext *C, if (extend) { int sel = BA_SELECT; FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (gr, object) { - Base *base = BKE_view_layer_base_find(view_layer, object); + Base *base = BKE_view_layer_base_find(tvc->view_layer, object); if (base && (base->flag & BASE_SELECTED)) { sel = BA_DESELECT; break; @@ -1170,7 +1174,7 @@ static void do_outliner_item_activate_tree_element(bContext *C, FOREACH_COLLECTION_OBJECT_RECURSIVE_END; FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (gr, object) { - Base *base = BKE_view_layer_base_find(view_layer, object); + Base *base = BKE_view_layer_base_find(tvc->view_layer, object); if (base) { ED_object_base_select(base, sel); } @@ -1178,10 +1182,10 @@ static void do_outliner_item_activate_tree_element(bContext *C, FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } else { - BKE_view_layer_base_deselect_all(view_layer); + BKE_view_layer_base_deselect_all(tvc->view_layer); FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (gr, object) { - Base *base = BKE_view_layer_base_find(view_layer, object); + Base *base = BKE_view_layer_base_find(tvc->view_layer, object); /* Object may not be in this scene */ if (base != NULL) { if ((base->flag & BASE_SELECTED) == 0) { @@ -1192,15 +1196,15 @@ static void do_outliner_item_activate_tree_element(bContext *C, FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } - DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + DEG_id_tag_update(&tvc->scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, tvc->scene); } else if (OB_DATA_SUPPORT_EDITMODE(te->idcode)) { Object *ob = (Object *)outliner_search_back(soops, te, ID_OB); if ((ob != NULL) && (ob->data == tselem->id)) { - Base *base = BKE_view_layer_base_find(view_layer, ob); + Base *base = BKE_view_layer_base_find(tvc->view_layer, ob); if ((base != NULL) && (base->flag & BASE_VISIBLE_DEPSGRAPH)) { - do_outliner_activate_obdata(C, scene, view_layer, base, extend); + do_outliner_activate_obdata(C, tvc->scene, tvc->view_layer, base, extend); } } } @@ -1209,18 +1213,12 @@ static void do_outliner_item_activate_tree_element(bContext *C, WM_operator_name_call(C, "GPENCIL_OT_editmode_toggle", WM_OP_INVOKE_REGION_WIN, NULL); } else { // rest of types - tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, false); + tree_element_active(C, tvc, soops, te, OL_SETSEL_NORMAL, false); } } - else if (soops->flag & SO_SYNC_SELECT) { - tree_element_type_active(C, - scene, - view_layer, - soops, - te, - tselem, - extend ? OL_SETSEL_EXTEND : OL_SETSEL_NORMAL, - recursive); + else if (do_activate_data) { + tree_element_type_active( + C, tvc, soops, te, tselem, extend ? OL_SETSEL_EXTEND : OL_SETSEL_NORMAL, recursive); } } @@ -1327,12 +1325,12 @@ static bool outliner_is_co_within_restrict_columns(const SpaceOutliner *soops, void outliner_item_do_activate_from_tree_element( bContext *C, TreeElement *te, TreeStoreElem *tselem, bool extend, bool recursive) { - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); SpaceOutliner *soops = CTX_wm_space_outliner(C); - do_outliner_item_activate_tree_element( - C, scene, view_layer, soops, te, tselem, extend, recursive); + TreeViewContext tvc; + outliner_viewcontext_init(C, &tvc); + + do_outliner_item_activate_tree_element(C, &tvc, soops, te, tselem, extend, recursive, false); } /** @@ -1347,7 +1345,6 @@ static int outliner_item_do_activate_from_cursor(bContext *C, const bool deselect_all) { ARegion *ar = CTX_wm_region(C); - Scene *scene = CTX_data_scene(C); SpaceOutliner *soops = CTX_wm_space_outliner(C); TreeElement *te; float view_mval[2]; @@ -1371,8 +1368,6 @@ static int outliner_item_do_activate_from_cursor(bContext *C, return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } else { - ViewLayer *view_layer = CTX_data_view_layer(C); - /* The row may also contain children, if one is hovered we want this instead of current te */ bool merged_elements = false; TreeElement *activate_te = outliner_find_item_at_x_in_row( @@ -1390,9 +1385,14 @@ static int outliner_item_do_activate_from_cursor(bContext *C, do_outliner_range_select(C, soops, activate_te, extend); } else { + TreeViewContext tvc; + outliner_viewcontext_init(C, &tvc); + + const bool is_over_name_icons = outliner_item_is_co_over_name_icons(activate_te, + view_mval[0]); outliner_item_select(soops, activate_te, extend, extend); do_outliner_item_activate_tree_element( - C, scene, view_layer, soops, activate_te, activate_tselem, extend, false); + C, &tvc, soops, activate_te, activate_tselem, extend, false, is_over_name_icons); } changed = true; @@ -1505,17 +1505,6 @@ static int outliner_box_select_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -/* Find if x coordinate is over an icon or name */ -static bool outliner_item_is_co_over_name_icons(TreeElement *te, float view_co_x) -{ - /* Special case: count area left of Scene Collection as empty space */ - bool outside_left = (TREESTORE(te)->type == TSE_VIEW_COLLECTION_BASE) ? - (view_co_x > te->xs + UI_UNIT_X) : - (view_co_x > te->xs); - - return outside_left && (view_co_x < te->xend); -} - static int outliner_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) { SpaceOutliner *soops = CTX_wm_space_outliner(C); diff --git a/source/blender/editors/space_outliner/outliner_sync.c b/source/blender/editors/space_outliner/outliner_sync.c index 29c820bce92..745a527cc15 100644 --- a/source/blender/editors/space_outliner/outliner_sync.c +++ b/source/blender/editors/space_outliner/outliner_sync.c @@ -37,6 +37,7 @@ #include "BKE_context.h" #include "BKE_layer.h" #include "BKE_main.h" +#include "BKE_object.h" #include "BKE_sequencer.h" #include "DEG_depsgraph.h" @@ -129,14 +130,14 @@ static void outliner_sync_select_from_outliner_set_types(bContext *C, SpaceOutliner *soops, SyncSelectTypes *sync_types) { - Object *obact = CTX_data_active_object(C); - Object *obedit = CTX_data_edit_object(C); + TreeViewContext tvc; + outliner_viewcontext_init(C, &tvc); const bool sequence_view = soops->outlinevis == SO_SEQUENCE; sync_types->object = !sequence_view; - sync_types->edit_bone = !sequence_view && (obedit && obedit->type == OB_ARMATURE); - sync_types->pose_bone = !sequence_view && (obact && obact->mode == OB_MODE_POSE); + sync_types->edit_bone = !sequence_view && (tvc.ob_edit && tvc.ob_edit->type == OB_ARMATURE); + sync_types->pose_bone = !sequence_view && (tvc.ob_pose && tvc.ob_pose->mode == OB_MODE_POSE); sync_types->sequence = sequence_view; } @@ -149,16 +150,16 @@ static bool outliner_sync_select_to_outliner_set_types(const bContext *C, SpaceOutliner *soops, SyncSelectTypes *sync_types) { - Object *obact = CTX_data_active_object(C); - Object *obedit = CTX_data_edit_object(C); + TreeViewContext tvc; + outliner_viewcontext_init(C, &tvc); const bool sequence_view = soops->outlinevis == SO_SEQUENCE; sync_types->object = !sequence_view && (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_OBJECT); - sync_types->edit_bone = !sequence_view && (obedit && obedit->type == OB_ARMATURE) && + sync_types->edit_bone = !sequence_view && (tvc.ob_edit && tvc.ob_edit->type == OB_ARMATURE) && (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE); - sync_types->pose_bone = !sequence_view && (obact && obact->mode == OB_MODE_POSE) && + sync_types->pose_bone = !sequence_view && (tvc.ob_pose && tvc.ob_pose->mode == OB_MODE_POSE) && (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE); sync_types->sequence = sequence_view && (soops->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE); diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c index 5dfdf6f129b..c3984ab16fa 100644 --- a/source/blender/editors/space_outliner/outliner_utils.c +++ b/source/blender/editors/space_outliner/outliner_utils.c @@ -21,6 +21,8 @@ * \ingroup spoutliner */ +#include <string.h> + #include "BLI_utildefines.h" #include "DNA_action_types.h" @@ -30,6 +32,7 @@ #include "BKE_context.h" #include "BKE_outliner_treehash.h" #include "BKE_layer.h" +#include "BKE_object.h" #include "ED_armature.h" #include "ED_outliner.h" @@ -39,6 +42,33 @@ #include "outliner_intern.h" +/* -------------------------------------------------------------------- */ +/** \name Tree View Context + * \{ */ + +void outliner_viewcontext_init(const bContext *C, TreeViewContext *tvc) +{ + memset(tvc, 0, sizeof(*tvc)); + + /* Scene level. */ + tvc->scene = CTX_data_scene(C); + tvc->view_layer = CTX_data_view_layer(C); + + /* Objects. */ + tvc->obact = OBACT(tvc->view_layer); + if (tvc->obact != NULL) { + tvc->ob_edit = OBEDIT_FROM_OBACT(tvc->obact); + + if ((tvc->obact->type == OB_ARMATURE) || + /* This could be made into it's own function. */ + ((tvc->obact->type == OB_MESH) && tvc->obact->mode & OB_MODE_WEIGHT_PAINT)) { + tvc->ob_pose = BKE_object_pose_armature_get(tvc->obact); + } + } +} + +/** \} */ + /** * Try to find an item under y-coordinate \a view_co_y (view-space). * \note Recursive @@ -361,8 +391,19 @@ bool outliner_is_element_visible(const TreeElement *te) return true; } +/* Find if x coordinate is over an icon or name */ +bool outliner_item_is_co_over_name_icons(const TreeElement *te, float view_co_x) +{ + /* Special case: count area left of Scene Collection as empty space */ + bool outside_left = (TREESTORE(te)->type == TSE_VIEW_COLLECTION_BASE) ? + (view_co_x > te->xs + UI_UNIT_X) : + (view_co_x > te->xs); + + return outside_left && (view_co_x < te->xend); +} + /* Find if x coordinate is over element disclosure toggle */ -bool outliner_item_is_co_within_close_toggle(TreeElement *te, float view_co_x) +bool outliner_item_is_co_within_close_toggle(const TreeElement *te, float view_co_x) { return (view_co_x > te->xs) && (view_co_x < te->xs + UI_UNIT_X); } diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 851d3b5f3aa..093d333c007 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -160,7 +160,7 @@ void SEQUENCER_OT_select_less(struct wmOperatorType *ot); void SEQUENCER_OT_select_linked(struct wmOperatorType *ot); void SEQUENCER_OT_select_linked_pick(struct wmOperatorType *ot); void SEQUENCER_OT_select_handles(struct wmOperatorType *ot); -void SEQUENCER_OT_select_active_side(struct wmOperatorType *ot); +void SEQUENCER_OT_select_side(struct wmOperatorType *ot); void SEQUENCER_OT_select_box(struct wmOperatorType *ot); void SEQUENCER_OT_select_inverse(struct wmOperatorType *ot); void SEQUENCER_OT_select_grouped(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index b0bb775de83..e91d6cfa3e6 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -96,7 +96,7 @@ void sequencer_operatortypes(void) WM_operatortype_append(SEQUENCER_OT_select_linked_pick); WM_operatortype_append(SEQUENCER_OT_select_linked); WM_operatortype_append(SEQUENCER_OT_select_handles); - WM_operatortype_append(SEQUENCER_OT_select_active_side); + WM_operatortype_append(SEQUENCER_OT_select_side); WM_operatortype_append(SEQUENCER_OT_select_box); WM_operatortype_append(SEQUENCER_OT_select_grouped); diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 4c20fc1707a..a51b08f7525 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -27,6 +27,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_math.h" #include "DNA_scene_types.h" @@ -81,7 +82,7 @@ static void select_surrounding_handles(Scene *scene, Sequence *test) /* XXX BRIN } } -/* used for mouse selection and for SEQUENCER_OT_select_active_side() */ +/* Used for mouse selection in SEQUENCER_OT_select. */ static void select_active_side(ListBase *seqbase, int sel_side, int channel, int frame) { Sequence *seq; @@ -110,7 +111,43 @@ static void select_active_side(ListBase *seqbase, int sel_side, int channel, int } } -/* used for mouse selection and for SEQUENCER_OT_select_active_side() */ +/* Used for mouse selection in SEQUENCER_OT_select_side. */ +static void select_active_side_range(ListBase *seqbase, + const int sel_side, + const int frame_ranges[MAXSEQ], + const int frame_ignore) +{ + Sequence *seq; + + for (seq = seqbase->first; seq; seq = seq->next) { + if (seq->machine < MAXSEQ) { + const int frame = frame_ranges[seq->machine]; + if (frame == frame_ignore) { + continue; + } + switch (sel_side) { + case SEQ_SIDE_LEFT: + if (frame > (seq->startdisp)) { + seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL); + seq->flag |= SELECT; + } + break; + case SEQ_SIDE_RIGHT: + if (frame < (seq->startdisp)) { + seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL); + seq->flag |= SELECT; + } + break; + case SEQ_SIDE_BOTH: + seq->flag &= ~(SEQ_RIGHTSEL | SEQ_LEFTSEL); + seq->flag |= SELECT; + break; + } + } + } +} + +/* used for mouse selection in SEQUENCER_OT_select */ static void select_linked_time(ListBase *seqbase, Sequence *seq_link) { Sequence *seq; @@ -913,20 +950,39 @@ void SEQUENCER_OT_select_handles(wmOperatorType *ot) } /* select side operator */ -static int sequencer_select_active_side_exec(bContext *C, wmOperator *op) +static int sequencer_select_side_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); Editing *ed = BKE_sequencer_editing_get(scene, false); - Sequence *seq_act = BKE_sequencer_active_get(scene); - if (ed == NULL || seq_act == NULL) { - return OPERATOR_CANCELLED; + const int sel_side = RNA_enum_get(op->ptr, "side"); + const int frame_init = sel_side == SEQ_SIDE_LEFT ? INT_MIN : INT_MAX; + int frame_ranges[MAXSEQ]; + bool selected = false; + + copy_vn_i(frame_ranges, ARRAY_SIZE(frame_ranges), frame_init); + + for (Sequence *seq = ed->seqbasep->first; seq; seq = seq->next) { + if (UNLIKELY(seq->machine >= MAXSEQ)) { + continue; + } + int *frame_limit_p = &frame_ranges[seq->machine]; + if (seq->flag & SELECT) { + selected = true; + if (sel_side == SEQ_SIDE_LEFT) { + *frame_limit_p = max_ii(*frame_limit_p, seq->startdisp); + } + else { + *frame_limit_p = min_ii(*frame_limit_p, seq->startdisp); + } + } } - seq_act->flag |= SELECT; + if (selected == false) { + return OPERATOR_CANCELLED; + } - select_active_side( - ed->seqbasep, RNA_enum_get(op->ptr, "side"), seq_act->machine, seq_act->startdisp); + select_active_side_range(ed->seqbasep, sel_side, frame_ranges, frame_init); ED_outliner_select_sync_from_sequence_tag(C); @@ -935,15 +991,15 @@ static int sequencer_select_active_side_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void SEQUENCER_OT_select_active_side(wmOperatorType *ot) +void SEQUENCER_OT_select_side(wmOperatorType *ot) { /* identifiers */ - ot->name = "Select Active Side"; - ot->idname = "SEQUENCER_OT_select_active_side"; - ot->description = "Select strips on the nominated side of the active strip"; + ot->name = "Select Side"; + ot->idname = "SEQUENCER_OT_select_side"; + ot->description = "Select strips on the nominated side of the selected strips"; /* api callbacks */ - ot->exec = sequencer_select_active_side_exec; + ot->exec = sequencer_select_side_exec; ot->poll = sequencer_edit_poll; /* flags */ @@ -955,7 +1011,7 @@ void SEQUENCER_OT_select_active_side(wmOperatorType *ot) prop_side_types, SEQ_SIDE_BOTH, "Side", - "The side of the handle that is selected"); + "The side to which the selection is applied"); } /* box_select operator */ diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index a66e76abc58..15208c1a7d2 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -379,6 +379,8 @@ void applyProject(TransInfo *t) .snap_select = t->tsnap.modeSelect, .use_object_edit_cage = (t->flag & T_EDIT) != 0, .use_occlusion_test = false, + .use_backface_culling = (t->scene->toolsettings->snap_flag & + SCE_SNAP_BACKFACE_CULLING) != 0, }, mval_fl, NULL, @@ -1364,6 +1366,8 @@ short snapObjectsTransform( .snap_select = t->tsnap.modeSelect, .use_object_edit_cage = (t->flag & T_EDIT) != 0, .use_occlusion_test = t->scene->toolsettings->snap_mode != SCE_SNAP_MODE_FACE, + .use_backface_culling = (t->scene->toolsettings->snap_flag & + SCE_SNAP_BACKFACE_CULLING) != 0, }, mval, t->tsnap.snapTarget, diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index f35a2808f22..14e8b8f97e0 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -201,8 +201,12 @@ static SnapObjectData_EditMesh *snap_object_data_editmesh_get(SnapObjectContext return *sod_p; } -typedef void (*IterSnapObjsCallback)( - SnapObjectContext *sctx, bool is_obedit, Object *ob, float obmat[4][4], void *data); +typedef void (*IterSnapObjsCallback)(SnapObjectContext *sctx, + bool is_obedit, + bool use_backface_culling, + Object *ob, + float obmat[4][4], + void *data); /** * Walks through all objects in the scene to create the list of objects to snap. @@ -219,6 +223,7 @@ static void iter_snap_objects(SnapObjectContext *sctx, const View3D *v3d = sctx->v3d_data.v3d; const eSnapSelect snap_select = params->snap_select; const bool use_object_edit_cage = params->use_object_edit_cage; + const bool use_backface_culling = params->use_backface_culling; Base *base_act = view_layer->basact; for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) { @@ -250,12 +255,14 @@ static void iter_snap_objects(SnapObjectContext *sctx, DupliObject *dupli_ob; ListBase *lb = object_duplilist(sctx->depsgraph, sctx->scene, obj_eval); for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) { - sob_callback(sctx, use_object_edit_cage, dupli_ob->ob, dupli_ob->mat, data); + sob_callback( + sctx, use_object_edit_cage, use_backface_culling, dupli_ob->ob, dupli_ob->mat, data); } free_object_duplilist(lb); } - sob_callback(sctx, use_object_edit_cage, obj_eval, obj_eval->obmat, data); + sob_callback( + sctx, use_object_edit_cage, use_backface_culling, obj_eval, obj_eval->obmat, data); } } @@ -350,6 +357,70 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH } } +static bool raycast_tri_backface_culling_test( + const float dir[3], const float v0[3], const float v1[3], const float v2[3], float no[3]) +{ + cross_tri_v3(no, v0, v1, v2); + return dot_v3v3(no, dir) < 0.0f; +} + +/* Callback to raycast with backface culling (Mesh). */ +static void mesh_looptri_raycast_backface_culling_cb(void *userdata, + int index, + const BVHTreeRay *ray, + BVHTreeRayHit *hit) +{ + const BVHTreeFromMesh *data = (BVHTreeFromMesh *)userdata; + const MVert *vert = data->vert; + const MLoopTri *lt = &data->looptri[index]; + const float *vtri_co[3] = { + vert[data->loop[lt->tri[0]].v].co, + vert[data->loop[lt->tri[1]].v].co, + vert[data->loop[lt->tri[2]].v].co, + }; + float dist = bvhtree_ray_tri_intersection(ray, hit->dist, UNPACK3(vtri_co)); + + if (dist >= 0 && dist < hit->dist) { + float no[3]; + if (raycast_tri_backface_culling_test(ray->direction, UNPACK3(vtri_co), no)) { + hit->index = index; + hit->dist = dist; + madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); + normalize_v3_v3(hit->no, no); + } + } +} + +/* Callback to raycast with backface culling (EditMesh). */ +static void editmesh_looptri_raycast_backface_culling_cb(void *userdata, + int index, + const BVHTreeRay *ray, + BVHTreeRayHit *hit) +{ + const BVHTreeFromEditMesh *data = (BVHTreeFromEditMesh *)userdata; + BMEditMesh *em = data->em; + const BMLoop **ltri = (const BMLoop **)em->looptris[index]; + + const float *t0, *t1, *t2; + t0 = ltri[0]->v->co; + t1 = ltri[1]->v->co; + t2 = ltri[2]->v->co; + + { + float dist = bvhtree_ray_tri_intersection(ray, hit->dist, t0, t1, t2); + + if (dist >= 0 && dist < hit->dist) { + float no[3]; + if (raycast_tri_backface_culling_test(ray->direction, t0, t1, t2, no)) { + hit->index = index; + hit->dist = dist; + madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); + normalize_v3_v3(hit->no, no); + } + } + } +} + static bool raycastMesh(SnapObjectContext *sctx, const float ray_start[3], const float ray_dir[3], @@ -358,6 +429,7 @@ static bool raycastMesh(SnapObjectContext *sctx, const float obmat[4][4], const unsigned int ob_index, bool use_hide, + bool use_backface_culling, /* read/write args */ float *ray_depth, /* return args */ @@ -494,7 +566,8 @@ static bool raycastMesh(SnapObjectContext *sctx, ray_normal_local, 0.0f, &hit, - treedata->raycast_callback, + use_backface_culling ? mesh_looptri_raycast_backface_culling_cb : + treedata->raycast_callback, treedata) != -1) { hit.dist += len_diff; hit.dist /= local_scale; @@ -530,6 +603,7 @@ static bool raycastEditMesh(SnapObjectContext *sctx, BMEditMesh *em, const float obmat[4][4], const unsigned int ob_index, + bool use_backface_culling, /* read/write args */ float *ray_depth, /* return args */ @@ -670,7 +744,8 @@ static bool raycastEditMesh(SnapObjectContext *sctx, ray_normal_local, 0.0f, &hit, - treedata->raycast_callback, + use_backface_culling ? editmesh_looptri_raycast_backface_culling_cb : + treedata->raycast_callback, treedata) != -1) { hit.dist += len_diff; hit.dist /= local_scale; @@ -715,6 +790,7 @@ static bool raycastObj(SnapObjectContext *sctx, const unsigned int ob_index, bool use_obedit, bool use_occlusion_test, + bool use_backface_culling, /* read/write args */ float *ray_depth, /* return args */ @@ -753,6 +829,7 @@ static bool raycastObj(SnapObjectContext *sctx, em, obmat, ob_index, + use_backface_culling, ray_depth, r_loc, r_no, @@ -773,6 +850,7 @@ static bool raycastObj(SnapObjectContext *sctx, obmat, ob_index, use_hide, + use_backface_culling, ray_depth, r_loc, r_no, @@ -792,6 +870,7 @@ static bool raycastObj(SnapObjectContext *sctx, obmat, ob_index, false, + use_backface_culling, ray_depth, r_loc, r_no, @@ -832,8 +911,12 @@ struct RaycastObjUserData { bool ret; }; -static void raycast_obj_cb( - SnapObjectContext *sctx, bool use_obedit, Object *ob, float obmat[4][4], void *data) +static void raycast_obj_cb(SnapObjectContext *sctx, + bool use_obedit, + bool use_backface_culling, + Object *ob, + float obmat[4][4], + void *data) { struct RaycastObjUserData *dt = data; @@ -845,6 +928,7 @@ static void raycast_obj_cb( dt->ob_index++, use_obedit, dt->use_occlusion_test, + use_backface_culling, dt->ray_depth, dt->r_loc, dt->r_no, @@ -1088,8 +1172,6 @@ typedef void (*Nearest2DGetTriEdgesCallback)(const int index, int e_index[3], vo typedef void (*Nearest2DCopyVertNoCallback)(const int index, float r_no[3], void *data); typedef struct Nearest2dUserData { - bool is_persp; - void *userdata; Nearest2DGetVertCoCallback get_vert_co; Nearest2DGetEdgeVertsCallback get_edge_verts_index; @@ -1097,6 +1179,8 @@ typedef struct Nearest2dUserData { Nearest2DGetTriEdgesCallback get_tri_edges_index; Nearest2DCopyVertNoCallback copy_vert_no; + bool is_persp; + bool use_backface_culling; } Nearest2dUserData; static void cb_snap_vert(void *userdata, @@ -1181,6 +1265,20 @@ static void cb_snap_tri_edges(void *userdata, { struct Nearest2dUserData *data = userdata; + if (data->use_backface_culling) { + int vindex[3]; + data->get_tri_verts_index(index, vindex, data->userdata); + + const float *t0, *t1, *t2; + data->get_vert_co(vindex[0], &t0, data->userdata); + data->get_vert_co(vindex[1], &t1, data->userdata); + data->get_vert_co(vindex[2], &t2, data->userdata); + float dummy[3]; + if (raycast_tri_backface_culling_test(precalc->ray_direction, t0, t1, t2, dummy)) { + return; + } + } + int eindex[3]; data->get_tri_edges_index(index, eindex, data->userdata); for (int i = 3; i--;) { @@ -1204,6 +1302,18 @@ static void cb_snap_tri_verts(void *userdata, int vindex[3]; data->get_tri_verts_index(index, vindex, data->userdata); + + if (data->use_backface_culling) { + const float *t0, *t1, *t2; + data->get_vert_co(vindex[0], &t0, data->userdata); + data->get_vert_co(vindex[1], &t1, data->userdata); + data->get_vert_co(vindex[2], &t2, data->userdata); + float dummy[3]; + if (raycast_tri_backface_culling_test(precalc->ray_direction, t0, t1, t2, dummy)) { + return; + } + } + for (int i = 3; i--;) { if (vindex[i] == nearest->index) { continue; @@ -1222,6 +1332,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, SnapData *snapdata, Object *ob, const float obmat[4][4], + bool use_backface_culling, /* read/write args */ float *dist_px, /* return args */ @@ -1246,6 +1357,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, Nearest2dUserData nearest2d = { .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, + .use_backface_culling = use_backface_culling, }; BVHTreeNearest nearest = { @@ -1366,6 +1478,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, const float obmat[4][4], float original_dist_px, const float prev_co[3], + bool use_backface_culling, /* read/write args */ float *dist_px, /* return args */ @@ -1392,6 +1505,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, Nearest2dUserData nearest2d; { nearest2d.is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; + nearest2d.use_backface_culling = use_backface_culling; if (sod->type == SNAP_MESH) { nearest2d.userdata = &((SnapObjectData_Mesh *)sod)->treedata; nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get; @@ -1995,6 +2109,7 @@ static short snapMesh(SnapObjectContext *sctx, Object *ob, Mesh *me, const float obmat[4][4], + bool use_backface_culling, /* read/write args */ float *dist_px, /* return args */ @@ -2107,13 +2222,14 @@ static short snapMesh(SnapObjectContext *sctx, } Nearest2dUserData nearest2d = { - .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, .userdata = treedata, .get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get, .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get, .get_tri_verts_index = (Nearest2DGetTriVertsCallback)cb_mlooptri_verts_get, .get_tri_edges_index = (Nearest2DGetTriEdgesCallback)cb_mlooptri_edges_get, .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy, + .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, + .use_backface_culling = use_backface_culling, }; BVHTreeNearest nearest = { @@ -2233,6 +2349,7 @@ static short snapEditMesh(SnapObjectContext *sctx, Object *ob, BMEditMesh *em, const float obmat[4][4], + bool use_backface_culling, /* read/write args */ float *dist_px, /* return args */ @@ -2346,11 +2463,12 @@ static short snapEditMesh(SnapObjectContext *sctx, } Nearest2dUserData nearest2d = { - .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, .userdata = em, .get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get, .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get, .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy, + .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, + .use_backface_culling = use_backface_culling, }; BVHTreeNearest nearest = { @@ -2436,6 +2554,7 @@ static short snapObject(SnapObjectContext *sctx, Object *ob, float obmat[4][4], bool use_obedit, + bool use_backface_culling, /* read/write args */ float *dist_px, /* return args */ @@ -2453,7 +2572,8 @@ static short snapObject(SnapObjectContext *sctx, if (BKE_object_is_in_editmode(ob)) { BMEditMesh *em = BKE_editmesh_from_object(ob); if (use_obedit) { - retval = snapEditMesh(sctx, snapdata, ob, em, obmat, dist_px, r_loc, r_no, r_index); + retval = snapEditMesh( + sctx, snapdata, ob, em, obmat, use_backface_culling, dist_px, r_loc, r_no, r_index); break; } else if (em->mesh_eval_final) { @@ -2465,7 +2585,8 @@ static short snapObject(SnapObjectContext *sctx, return 0; } - retval = snapMesh(sctx, snapdata, ob, me, obmat, dist_px, r_loc, r_no, r_index); + retval = snapMesh( + sctx, snapdata, ob, me, obmat, use_backface_culling, dist_px, r_loc, r_no, r_index); break; } case OB_ARMATURE: @@ -2477,8 +2598,16 @@ static short snapObject(SnapObjectContext *sctx, case OB_SURF: case OB_FONT: { if (ob->runtime.mesh_eval) { - retval |= snapMesh( - sctx, snapdata, ob, ob->runtime.mesh_eval, obmat, dist_px, r_loc, r_no, r_index); + retval |= snapMesh(sctx, + snapdata, + ob, + ob->runtime.mesh_eval, + obmat, + use_backface_culling, + dist_px, + r_loc, + r_no, + r_index); } break; } @@ -2519,8 +2648,12 @@ struct SnapObjUserData { short ret; }; -static void sanp_obj_cb( - SnapObjectContext *sctx, bool is_obedit, Object *ob, float obmat[4][4], void *data) +static void sanp_obj_cb(SnapObjectContext *sctx, + bool is_obedit, + bool use_backface_culling, + Object *ob, + float obmat[4][4], + void *data) { struct SnapObjUserData *dt = data; @@ -2529,6 +2662,7 @@ static void sanp_obj_cb( ob, obmat, is_obedit, + use_backface_culling, /* read/write args */ dt->dist_px, /* return args */ @@ -2881,7 +3015,8 @@ static short transform_snap_context_project_view3d_mixed_impl( new_clipplane[3] += 0.01f; /* Try to snap only to the polygon. */ - elem_test = snap_mesh_polygon(sctx, &snapdata, ob, obmat, &dist_px_tmp, loc, no, &index); + elem_test = snap_mesh_polygon( + sctx, &snapdata, ob, obmat, params->use_backface_culling, &dist_px_tmp, loc, no, &index); if (elem_test) { elem = elem_test; } @@ -2904,8 +3039,17 @@ static short transform_snap_context_project_view3d_mixed_impl( (snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR))) { snapdata.snap_to_flag = snap_to_flag; - elem = snap_mesh_edge_verts_mixed( - sctx, &snapdata, ob, obmat, *dist_px, prev_co, &dist_px_tmp, loc, no, &index); + elem = snap_mesh_edge_verts_mixed(sctx, + &snapdata, + ob, + obmat, + *dist_px, + prev_co, + params->use_backface_culling, + &dist_px_tmp, + loc, + no, + &index); } if (elem & snap_to_flag) { diff --git a/source/blender/makesdna/DNA_brush_defaults.h b/source/blender/makesdna/DNA_brush_defaults.h index 714c205cda2..b2d4124a348 100644 --- a/source/blender/makesdna/DNA_brush_defaults.h +++ b/source/blender/makesdna/DNA_brush_defaults.h @@ -34,6 +34,7 @@ { \ .blend = 0, \ .flag = (BRUSH_ALPHA_PRESSURE | BRUSH_SPACE | BRUSH_SPACE_ATTEN), \ + .sampling_flag = (BRUSH_PAINT_ANTIALIASING), \ \ .ob_mode = OB_MODE_ALL_PAINT, \ \ diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index fc8763f1519..63fbf576bba 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -245,8 +245,9 @@ typedef struct Brush { float weight; /** Brush diameter. */ int size; - /** General purpose flag. */ + /** General purpose flags. */ int flag; + int sampling_flag; /** Pressure influence for mask. */ int mask_pressure; /** Jitter the position of the brush. */ @@ -283,7 +284,7 @@ typedef struct Brush { /** Source for fill tool color gradient application. */ char gradient_fill_mode; - char _pad; + char _pad[5]; /** Projection shape (sphere, circle). */ char falloff_shape; float falloff_angle; @@ -435,6 +436,11 @@ typedef enum eBrushFlags { BRUSH_CURVE = (1u << 31), } eBrushFlags; +/* Brush.sampling_flag */ +typedef enum eBrushSamplingFlags { + BRUSH_PAINT_ANTIALIASING = (1 << 0), +} eBrushSamplingFlags; + typedef enum { BRUSH_MASK_PRESSURE_RAMP = (1 << 1), BRUSH_MASK_PRESSURE_CUTOFF = (1 << 2), diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 3794508d5a2..2ca9e3b2ef7 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -2035,6 +2035,7 @@ enum { #define SCE_SNAP_PROJECT (1 << 3) #define SCE_SNAP_NO_SELF (1 << 4) #define SCE_SNAP_ABS_GRID (1 << 5) +#define SCE_SNAP_BACKFACE_CULLING (1 << 6) /* ToolSettings.snap_target */ #define SCE_SNAP_TARGET_CLOSEST 0 diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 99c1bdfdbee..a392e4c080f 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -2007,6 +2007,11 @@ static void rna_def_brush(BlenderRNA *brna) "Apply the maximum grab strength to the active vertex instead of the cursor location"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_paint_antialiasing", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "sampling_flag", BRUSH_PAINT_ANTIALIASING); + RNA_def_property_ui_text(prop, "Antialasing", "Smooths the edges of the strokes"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_pressure_strength", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", BRUSH_ALPHA_PRESSURE); RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index f1e20b32ddc..b9bc7b2bbf5 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3035,6 +3035,11 @@ static void rna_def_tool_settings(BlenderRNA *brna) "Project individual elements on the surface of other objects"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + prop = RNA_def_property(srna, "use_snap_backface_culling", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_BACKFACE_CULLING); + RNA_def_property_ui_text(prop, "Backface Culling", "Exclude back facing geometry from snapping"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + prop = RNA_def_property(srna, "use_snap_self", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "snap_flag", SCE_SNAP_NO_SELF); RNA_def_property_ui_text(prop, "Project onto Self", "Snap onto itself (Edit Mode Only)"); diff --git a/tests/gtests/blenlib/BLI_task_performance_test.cc b/tests/gtests/blenlib/BLI_task_performance_test.cc index ecc012aa47a..dc8981f8064 100644 --- a/tests/gtests/blenlib/BLI_task_performance_test.cc +++ b/tests/gtests/blenlib/BLI_task_performance_test.cc @@ -38,14 +38,22 @@ static uint gen_pseudo_random_number(uint num) return ((num & 255) << 6) + 1; } -static void task_listbase_light_iter_func(void *UNUSED(userdata), Link *item, int index) +static void task_listbase_light_iter_func(void *UNUSED(userdata), + void *item, + int index, + const TaskParallelTLS *__restrict UNUSED(tls)) + { LinkData *data = (LinkData *)item; data->data = POINTER_FROM_INT(POINTER_AS_INT(data->data) + index); } -static void task_listbase_light_membarrier_iter_func(void *userdata, Link *item, int index) +static void task_listbase_light_membarrier_iter_func(void *userdata, + void *item, + int index, + const TaskParallelTLS *__restrict UNUSED(tls)) + { LinkData *data = (LinkData *)item; int *count = (int *)userdata; @@ -54,7 +62,11 @@ static void task_listbase_light_membarrier_iter_func(void *userdata, Link *item, atomic_sub_and_fetch_uint32((uint32_t *)count, 1); } -static void task_listbase_heavy_iter_func(void *UNUSED(userdata), Link *item, int index) +static void task_listbase_heavy_iter_func(void *UNUSED(userdata), + void *item, + int index, + const TaskParallelTLS *__restrict UNUSED(tls)) + { LinkData *data = (LinkData *)item; @@ -66,7 +78,11 @@ static void task_listbase_heavy_iter_func(void *UNUSED(userdata), Link *item, in } } -static void task_listbase_heavy_membarrier_iter_func(void *userdata, Link *item, int index) +static void task_listbase_heavy_membarrier_iter_func(void *userdata, + void *item, + int index, + const TaskParallelTLS *__restrict UNUSED(tls)) + { LinkData *data = (LinkData *)item; int *count = (int *)userdata; @@ -84,14 +100,18 @@ static void task_listbase_test_do(ListBase *list, const int num_items, int *num_items_tmp, const char *id, - TaskParallelListbaseFunc func, + TaskParallelIteratorFunc func, const bool use_threads, const bool check_num_items_tmp) { + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = use_threads; + double averaged_timing = 0.0; for (int i = 0; i < NUM_RUN_AVERAGED; i++) { const double init_time = PIL_check_seconds_timer(); - BLI_task_parallel_listbase(list, num_items_tmp, func, use_threads); + BLI_task_parallel_listbase(list, num_items_tmp, func, &settings); averaged_timing += PIL_check_seconds_timer() - init_time; /* Those checks should ensure us all items of the listbase were processed once, and only once - diff --git a/tests/gtests/blenlib/BLI_task_test.cc b/tests/gtests/blenlib/BLI_task_test.cc index 0c1868380da..62ae0baaec9 100644 --- a/tests/gtests/blenlib/BLI_task_test.cc +++ b/tests/gtests/blenlib/BLI_task_test.cc @@ -88,7 +88,10 @@ TEST(task, MempoolIter) /* *** Parallel iterations over double-linked list items. *** */ -static void task_listbase_iter_func(void *userdata, Link *item, int index) +static void task_listbase_iter_func(void *userdata, + void *item, + int index, + const TaskParallelTLS *__restrict UNUSED(tls)) { LinkData *data = (LinkData *)item; int *count = (int *)userdata; @@ -112,7 +115,10 @@ TEST(task, ListBaseIter) num_items++; } - BLI_task_parallel_listbase(&list, &num_items, task_listbase_iter_func, true); + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + + BLI_task_parallel_listbase(&list, &num_items, task_listbase_iter_func, &settings); /* Those checks should ensure us all items of the listbase were processed once, and only once - * as expected. */ |