diff options
589 files changed, 10461 insertions, 3918 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index f60d3bcfb1a..26ef42fa233 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1712,22 +1712,18 @@ if(WITH_PYTHON) endif() endif() -if(MSVC) - string(APPEND CMAKE_CXX_FLAGS " /std:c++17") - # Make MSVC properly report the value of the __cplusplus preprocessor macro - # Available MSVC 15.7 (1914) and up, without this it reports 199711L regardless - # of the C++ standard chosen above - if(MSVC_VERSION GREATER 1913) - string(APPEND CMAKE_CXX_FLAGS " /Zc:__cplusplus") - endif() -elseif( - CMAKE_COMPILER_IS_GNUCC OR - CMAKE_C_COMPILER_ID MATCHES "Clang" OR - CMAKE_C_COMPILER_ID MATCHES "Intel" -) - string(APPEND CMAKE_CXX_FLAGS " -std=c++17") -else() - message(FATAL_ERROR "Unknown compiler ${CMAKE_C_COMPILER_ID}, can't enable C++17 build") +# Select C++17 as the standard for C++ projects. +set(CMAKE_CXX_STANDARD 17) +# If C++17 is not available, downgrading to an earlier standard is NOT OK. +set(CMAKE_CXX_STANDARD_REQUIRED ON) +# Do not enable compiler specific language extentions. +set(CMAKE_CXX_EXTENSIONS OFF) + +# Make MSVC properly report the value of the __cplusplus preprocessor macro +# Available MSVC 15.7 (1914) and up, without this it reports 199711L regardless +# of the C++ standard chosen above. +if(MSVC AND MSVC_VERSION GREATER 1913) + string(APPEND CMAKE_CXX_FLAGS " /Zc:__cplusplus") endif() # Visual Studio has all standards it supports available by default diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt index 62b4602a998..8c739dcd68e 100644 --- a/build_files/build_environment/CMakeLists.txt +++ b/build_files/build_environment/CMakeLists.txt @@ -56,6 +56,7 @@ else() endif() include(cmake/zlib.cmake) +include(cmake/zstd.cmake) include(cmake/openal.cmake) include(cmake/png.cmake) include(cmake/jpeg.cmake) diff --git a/build_files/build_environment/cmake/download.cmake b/build_files/build_environment/cmake/download.cmake index db90e9b40e9..b4d96ea856f 100644 --- a/build_files/build_environment/cmake/download.cmake +++ b/build_files/build_environment/cmake/download.cmake @@ -92,3 +92,4 @@ download_source(ISPC) download_source(GMP) download_source(POTRACE) download_source(HARU) +download_source(ZSTD) diff --git a/build_files/build_environment/cmake/harvest.cmake b/build_files/build_environment/cmake/harvest.cmake index b94790507b2..a19c332457d 100644 --- a/build_files/build_environment/cmake/harvest.cmake +++ b/build_files/build_environment/cmake/harvest.cmake @@ -192,6 +192,8 @@ harvest(potrace/include potrace/include "*.h") harvest(potrace/lib potrace/lib "*.a") harvest(haru/include haru/include "*.h") harvest(haru/lib haru/lib "*.a") +harvest(zstd/include zstd/include "*.h") +harvest(zstd/lib zstd/lib "*.a") if(UNIX AND NOT APPLE) harvest(libglu/lib mesa/lib "*.so*") diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index bb0ee961f05..a71fc4b85e4 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -500,5 +500,11 @@ set(HARU_HASH 4f916aa49c3069b3a10850013c507460) set(HARU_HASH_TYPE MD5) set(HARU_FILE libharu-${HARU_VERSION}.tar.gz) +set(ZSTD_VERSION 1.5.0) +set(ZSTD_URI https://github.com/facebook/zstd/releases/download/v${ZSTD_VERSION}/zstd-${ZSTD_VERSION}.tar.gz) +set(ZSTD_HASH 5194fbfa781fcf45b98c5e849651aa7b3b0a008c6b72d4a0db760f3002291e94) +set(ZSTD_HASH_TYPE SHA256) +set(ZSTD_FILE zstd-${ZSTD_VERSION}.tar.gz) + set(SSE2NEON_GIT https://github.com/DLTcollab/sse2neon.git) set(SSE2NEON_GIT_HASH fe5ff00bb8d19b327714a3c290f3e2ce81ba3525) diff --git a/build_files/build_environment/cmake/zstd.cmake b/build_files/build_environment/cmake/zstd.cmake new file mode 100644 index 00000000000..86cb2d016c6 --- /dev/null +++ b/build_files/build_environment/cmake/zstd.cmake @@ -0,0 +1,51 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ***** END GPL LICENSE BLOCK ***** + +set(ZSTD_EXTRA_ARGS + -DZSTD_BUILD_PROGRAMS=OFF + -DZSTD_BUILD_SHARED=OFF + -DZSTD_BUILD_STATIC=ON + -DZSTD_BUILD_TESTS=OFF + -DZSTD_LEGACY_SUPPORT=OFF + -DZSTD_LZ4_SUPPORT=OFF + -DZSTD_LZMA_SUPPORT=OFF + -DZSTD_MULTITHREAD_SUPPORT=ON + -DZSTD_PROGRAMS_LINK_SHARED=OFF + -DZSTD_USE_STATIC_RUNTIME=OFF + -DZSTD_ZLIB_SUPPORT=OFF +) + +ExternalProject_Add(external_zstd + URL file://${PACKAGE_DIR}/${ZSTD_FILE} + DOWNLOAD_DIR ${DOWNLOAD_DIR} + URL_HASH ${ZSTD_HASH_TYPE}=${ZSTD_HASH} + PREFIX ${BUILD_DIR}/zstd + SOURCE_SUBDIR build/cmake + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/zstd ${DEFAULT_CMAKE_FLAGS} ${ZSTD_EXTRA_ARGS} + INSTALL_DIR ${LIBDIR}/zstd +) + +if(WIN32) + if(BUILD_MODE STREQUAL Release) + ExternalProject_Add_Step(external_zstd after_install + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/zstd/lib/zstd_static${LIBEXT} ${HARVEST_TARGET}/zstd/lib/zstd_static${LIBEXT} + COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/zstd/include/ ${HARVEST_TARGET}/zstd/include/ + DEPENDEES install + ) + endif() +endif() diff --git a/build_files/cmake/cmake_consistency_check_config.py b/build_files/cmake/cmake_consistency_check_config.py index 8e626f8d056..bf842466add 100644 --- a/build_files/cmake/cmake_consistency_check_config.py +++ b/build_files/cmake/cmake_consistency_check_config.py @@ -8,6 +8,9 @@ IGNORE_SOURCE = ( # specific source files "extern/audaspace/", + # Use for `WIN32` only. + "source/creator/blender_launcher_win32.c", + # specific source files "extern/bullet2/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp", "extern/bullet2/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp", diff --git a/extern/glog/README.blender b/extern/glog/README.blender index 5b4dab199bf..bc2ca9f958e 100644 --- a/extern/glog/README.blender +++ b/extern/glog/README.blender @@ -7,3 +7,4 @@ Local modifications: checks for functions and so are needed. * Added special definitions of HAVE_SNPRINTF and HAVE_LIB_GFLAGS in Windows' specific config.h. +* Silenced syscall deprecation warnings on macOS >= 10.12. diff --git a/extern/glog/src/raw_logging.cc b/extern/glog/src/raw_logging.cc index 3bbfda31868..15d14f6e4f5 100644 --- a/extern/glog/src/raw_logging.cc +++ b/extern/glog/src/raw_logging.cc @@ -59,7 +59,7 @@ # include <unistd.h> #endif -#if defined(HAVE_SYSCALL_H) || defined(HAVE_SYS_SYSCALL_H) +#if (defined(HAVE_SYSCALL_H) || defined(HAVE_SYS_SYSCALL_H)) && (!(defined OS_MACOSX)) # define safe_write(fd, s, len) syscall(SYS_write, fd, s, len) #else // Not so safe, but what can you do? diff --git a/extern/glog/src/utilities.cc b/extern/glog/src/utilities.cc index 25c4b760f1c..6387e14e123 100644 --- a/extern/glog/src/utilities.cc +++ b/extern/glog/src/utilities.cc @@ -259,7 +259,13 @@ pid_t GetTID() { #endif static bool lacks_gettid = false; if (!lacks_gettid) { +#ifdef OS_MACOSX + uint64_t tid64; + const int error = pthread_threadid_np(NULL, &tid64); + pid_t tid = error ? -1 : (pid_t)tid64; +#else pid_t tid = syscall(__NR_gettid); +#endif if (tid != -1) { return tid; } diff --git a/intern/atomic/intern/atomic_ops_unix.h b/intern/atomic/intern/atomic_ops_unix.h index dc1e71cda76..b08a0e9bc28 100644 --- a/intern/atomic/intern/atomic_ops_unix.h +++ b/intern/atomic/intern/atomic_ops_unix.h @@ -49,9 +49,9 @@ #include "atomic_ops_utils.h" -#if defined(__arm__) -/* Attempt to fix compilation error on Debian armel kernel. - * arm7 architecture does have both 32 and 64bit atomics, however +#if defined(__arm__) || defined(__riscv) +/* Attempt to fix compilation error on Debian armel and RISC-V kernels. + * Both architectures do have both 32 and 64bit atomics, however * its gcc doesn't have __GCC_HAVE_SYNC_COMPARE_AND_SWAP_n defined. */ # define JE_FORCE_SYNC_COMPARE_AND_SWAP_1 diff --git a/intern/cycles/blender/blender_image.cpp b/intern/cycles/blender/blender_image.cpp index 3a9d159e461..f27275bd457 100644 --- a/intern/cycles/blender/blender_image.cpp +++ b/intern/cycles/blender/blender_image.cpp @@ -137,9 +137,9 @@ bool BlenderImageLoader::load_pixels(const ImageMetaData &metadata, /* Premultiply, byte images are always straight for Blender. */ unsigned char *cp = (unsigned char *)pixels; for (size_t i = 0; i < num_pixels; i++, cp += channels) { - cp[0] = (cp[0] * cp[3]) >> 8; - cp[1] = (cp[1] * cp[3]) >> 8; - cp[2] = (cp[2] * cp[3]) >> 8; + cp[0] = (cp[0] * cp[3]) / 255; + cp[1] = (cp[1] * cp[3]) / 255; + cp[2] = (cp[2] * cp[3]) / 255; } } } diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 635392fb3d4..f5e8db2aee1 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -109,23 +109,23 @@ void BlenderSync::sync_object_motion_init(BL::Object &b_parent, BL::Object &b_ob } Geometry *geom = object->get_geometry(); - geom->set_use_motion_blur(false); - geom->set_motion_steps(0); - uint motion_steps; + int motion_steps = 0; + bool use_motion_blur = false; if (need_motion == Scene::MOTION_BLUR) { motion_steps = object_motion_steps(b_parent, b_ob, Object::MAX_MOTION_STEPS); - geom->set_motion_steps(motion_steps); if (motion_steps && object_use_deform_motion(b_parent, b_ob)) { - geom->set_use_motion_blur(true); + use_motion_blur = true; } } else { motion_steps = 3; - geom->set_motion_steps(motion_steps); } + geom->set_use_motion_blur(use_motion_blur); + geom->set_motion_steps(motion_steps); + motion.resize(motion_steps, transform_empty()); if (motion_steps) { diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index 3b3a193b3e8..ce399579e25 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -404,8 +404,6 @@ void BlenderSync::sync_film(BL::SpaceView3D &b_v3d) Film *film = scene->film; - vector<Pass> prevpasses = scene->passes; - if (b_v3d) { film->set_display_pass(update_viewport_display_passes(b_v3d, scene->passes)); } @@ -435,11 +433,6 @@ void BlenderSync::sync_film(BL::SpaceView3D &b_v3d) break; } } - - if (!Pass::equals(prevpasses, scene->passes)) { - film->tag_passes_update(scene, prevpasses, false); - film->tag_modified(); - } } /* Render Layer */ @@ -749,10 +742,13 @@ vector<Pass> BlenderSync::sync_render_passes(BL::Scene &b_scene, DENOISING_CLEAN_ALL_PASSES); scene->film->set_denoising_prefiltered_pass(denoising.store_passes && denoising.type == DENOISER_NLM); - scene->film->set_pass_alpha_threshold(b_view_layer.pass_alpha_threshold()); - scene->film->tag_passes_update(scene, passes); - scene->integrator->tag_update(scene, Integrator::UPDATE_ALL); + + if (!Pass::equals(passes, scene->passes)) { + scene->film->tag_passes_update(scene, passes); + scene->film->tag_modified(); + scene->integrator->tag_update(scene, Integrator::UPDATE_ALL); + } return passes; } diff --git a/intern/cycles/kernel/svm/svm_vector_rotate.h b/intern/cycles/kernel/svm/svm_vector_rotate.h index 79a4ec2c40e..50045752484 100644 --- a/intern/cycles/kernel/svm/svm_vector_rotate.h +++ b/intern/cycles/kernel/svm/svm_vector_rotate.h @@ -50,24 +50,29 @@ ccl_device void svm_node_vector_rotate(ShaderData *sd, } else { float3 axis; + float axis_length; switch (type) { case NODE_VECTOR_ROTATE_TYPE_AXIS_X: axis = make_float3(1.0f, 0.0f, 0.0f); + axis_length = 1.0f; break; case NODE_VECTOR_ROTATE_TYPE_AXIS_Y: axis = make_float3(0.0f, 1.0f, 0.0f); + axis_length = 1.0f; break; case NODE_VECTOR_ROTATE_TYPE_AXIS_Z: axis = make_float3(0.0f, 0.0f, 1.0f); + axis_length = 1.0f; break; default: - axis = normalize(stack_load_float3(stack, axis_stack_offset)); + axis = stack_load_float3(stack, axis_stack_offset); + axis_length = len(axis); break; } float angle = stack_load_float(stack, angle_stack_offset); angle = invert ? -angle : angle; - result = (len_squared(axis) != 0.0f) ? - rotate_around_axis(vector - center, axis, angle) + center : + result = (axis_length != 0.0f) ? + rotate_around_axis(vector - center, axis / axis_length, angle) + center : vector; } diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index a35c9fffcab..76cac1049fb 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -288,6 +288,13 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) ${dbus_INCLUDE_DIRS} ) + include(CheckSymbolExists) + set(CMAKE_REQUIRED_DEFINITIONS "-D_GNU_SOURCE") + check_symbol_exists(memfd_create "sys/mman.h" HAVE_MEMFD_CREATE) + if (HAVE_MEMFD_CREATE) + add_definitions(-DHAVE_MEMFD_CREATE) + endif() + list(APPEND SRC intern/GHOST_SystemWayland.cpp intern/GHOST_WindowWayland.cpp diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index abc451d2e5d..38700845405 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -52,8 +52,6 @@ #include <cstring> -#include <linux/input-event-codes.h> - /* selected input event code defines from 'linux/input-event-codes.h' * We include some of the button input event codes here, since the header is * only available in more recent kernel versions. The event codes are used to @@ -1806,13 +1804,43 @@ GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(uint8_t *bitmap, static const int32_t stride = sizex * 4; /* ARGB */ cursor->file_buffer->size = size_t(stride * sizey); +#ifdef HAVE_MEMFD_CREATE const int fd = memfd_create("blender-cursor-custom", MFD_CLOEXEC | MFD_ALLOW_SEALING); - fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK); - posix_fallocate(fd, 0, int32_t(cursor->file_buffer->size)); + if (fd >= 0) { + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); + } +#else + char *path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + errno = ENOENT; + return GHOST_kFailure; + } + + char *tmpname; + asprintf(&tmpname, "%s/%s", path, "blender-XXXXXX"); + const int fd = mkostemp(tmpname, O_CLOEXEC); + if (fd >= 0) { + unlink(tmpname); + } + free(tmpname); +#endif + + if (fd < 0) { + return GHOST_kFailure; + } + + if (posix_fallocate(fd, 0, int32_t(cursor->file_buffer->size)) != 0) { + return GHOST_kFailure; + } cursor->file_buffer->data = mmap( nullptr, cursor->file_buffer->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (cursor->file_buffer->data == MAP_FAILED) { + close(fd); + return GHOST_kFailure; + } + struct wl_shm_pool *pool = wl_shm_create_pool(d->shm, fd, int32_t(cursor->file_buffer->size)); wl_buffer *buffer = wl_shm_pool_create_buffer( diff --git a/intern/ghost/intern/GHOST_WindowWayland.h b/intern/ghost/intern/GHOST_WindowWayland.h index 6ffcf99b48c..afbdf1268ee 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.h +++ b/intern/ghost/intern/GHOST_WindowWayland.h @@ -29,9 +29,9 @@ class GHOST_SystemWayland; +struct output_t; struct window_t; struct wl_surface; -struct output_t; class GHOST_WindowWayland : public GHOST_Window { public: diff --git a/intern/guardedalloc/intern/mallocn_intern.h b/intern/guardedalloc/intern/mallocn_intern.h index aa956150484..e4bd3d533a3 100644 --- a/intern/guardedalloc/intern/mallocn_intern.h +++ b/intern/guardedalloc/intern/mallocn_intern.h @@ -53,14 +53,8 @@ size_t malloc_usable_size(void *ptr); # undef USE_MALLOC_USABLE_SIZE #endif -/* Blame Microsoft for LLP64 and no inttypes.h, quick workaround needed: */ -#if defined(WIN64) -# define SIZET_FORMAT "%I64u" -# define SIZET_ARG(a) ((unsigned long long)(a)) -#else -# define SIZET_FORMAT "%lu" -# define SIZET_ARG(a) ((unsigned long)(a)) -#endif +#define SIZET_FORMAT "%zu" +#define SIZET_ARG(a) ((size_t)(a)) #define SIZET_ALIGN_4(len) ((len + 3) & ~(size_t)3) diff --git a/intern/libmv/libmv/numeric/numeric.h b/intern/libmv/libmv/numeric/numeric.h index e3d44226338..e3ebdb5a4bb 100644 --- a/intern/libmv/libmv/numeric/numeric.h +++ b/intern/libmv/libmv/numeric/numeric.h @@ -43,7 +43,7 @@ inline void sincos(double x, double* sinx, double* cosx) { # endif #endif // !__MINGW64__ -#if (defined(WIN32) || defined(WIN64)) && !defined(__MINGW32__) +#if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__) inline long lround(double d) { return (long)(d > 0 ? d + 0.5 : ceil(d - 0.5)); } diff --git a/intern/libmv/libmv/tracking/retrack_region_tracker.cc b/intern/libmv/libmv/tracking/retrack_region_tracker.cc index 9152078053c..124a032cdff 100644 --- a/intern/libmv/libmv/tracking/retrack_region_tracker.cc +++ b/intern/libmv/libmv/tracking/retrack_region_tracker.cc @@ -37,7 +37,7 @@ bool RetrackRegionTracker::Track(const FloatImage& image1, } // Now track x2 and y2 backward, to get xx1 and yy1 which, if the track is // good, should match x1 and y1 (but may not if the track is bad). - double xx1 = *x2, yy1 = *x2; + double xx1 = *x2, yy1 = *y2; if (!tracker_->Track(image2, image1, *x2, *y2, &xx1, &yy1)) { return false; } diff --git a/intern/numaapi/source/build_config.h b/intern/numaapi/source/build_config.h index fdd6ff704c3..49d82aa3e87 100644 --- a/intern/numaapi/source/build_config.h +++ b/intern/numaapi/source/build_config.h @@ -324,6 +324,16 @@ # define ARCH_CPU_ARM64 1 # define ARCH_CPU_64_BITS 1 # define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__riscv) && __riscv_xlen == 32 +# define ARCH_CPU_RISCV_FAMILY 1 +# define ARCH_CPU_RISCV32 1 +# define ARCH_CPU_64_BITS 0 +# define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__riscv) && __riscv_xlen == 64 +# define ARCH_CPU_RISCV_FAMILY 1 +# define ARCH_CPU_RISCV64 1 +# define ARCH_CPU_64_BITS 1 +# define ARCH_CPU_LITTLE_ENDIAN 1 #elif defined(__pnacl__) || defined(__asmjs__) || defined(__wasm__) # define ARCH_CPU_32_BITS 1 # define ARCH_CPU_LITTLE_ENDIAN 1 @@ -381,6 +391,9 @@ #if !defined(ARCH_CPU_PPC64_FAMILY) # define ARCH_CPU_PPC64_FAMILY 0 #endif +#if !defined(ARCH_CPU_RISCV_FAMILY) +# define ARCH_CPU_RISCV_FAMILY 0 +#endif #if !defined(ARCH_CPU_S390_FAMILY) # define ARCH_CPU_S390_FAMILY 0 #endif diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject 1ab25ca4f208edc8fb6c3551b3050ce3ad50ad7 +Subproject f3791fbfdb839860035241ba477bf8872966af9 diff --git a/release/scripts/freestyle/modules/freestyle/shaders.py b/release/scripts/freestyle/modules/freestyle/shaders.py index 28b8aa9b23e..95e1c873657 100644 --- a/release/scripts/freestyle/modules/freestyle/shaders.py +++ b/release/scripts/freestyle/modules/freestyle/shaders.py @@ -1153,11 +1153,9 @@ class RoundCapShader(StrokeShader): return # calculate the number of additional vertices to form caps thickness_beg = sum(stroke[0].attribute.thickness) - caplen_beg = thickness_beg / 2.0 nverts_beg = max(5, int(thickness_beg)) thickness_end = sum(stroke[-1].attribute.thickness) - caplen_end = (thickness_end) / 2.0 nverts_end = max(5, int(thickness_end)) # adjust the total number of stroke vertices @@ -1169,7 +1167,7 @@ class RoundCapShader(StrokeShader): # reshape the cap at the beginning of the stroke q, attr = buffer[1] p, attr = buffer[0] - direction = (p - q).normalized() * caplen_beg + direction = (p - q).normalized() * thickness_beg n = 1.0 / nverts_beg R, L = attr.thickness for t, svert in zip(range(nverts_beg, 0, -1), stroke): @@ -1180,7 +1178,7 @@ class RoundCapShader(StrokeShader): # reshape the cap at the end of the stroke q, attr = buffer[-2] p, attr = buffer[-1] - direction = (p - q).normalized() * caplen_beg + direction = (p - q).normalized() * thickness_end n = 1.0 / nverts_end R, L = attr.thickness for t, svert in zip(range(nverts_end, 0, -1), reversed(stroke)): diff --git a/release/scripts/modules/bpy_extras/asset_utils.py b/release/scripts/modules/bpy_extras/asset_utils.py index e41eeeed4fa..2cd5dddefbc 100644 --- a/release/scripts/modules/bpy_extras/asset_utils.py +++ b/release/scripts/modules/bpy_extras/asset_utils.py @@ -56,6 +56,17 @@ class AssetBrowserPanel: return SpaceAssetInfo.is_asset_browser_poll(context) +class AssetBrowserSpecificCategoryPanel(AssetBrowserPanel): + asset_categories = set() # Set of strings like 'ANIMATIONS', see `asset_category_items` in rna_space.c + + @classmethod + def poll(cls, context): + return ( + SpaceAssetInfo.is_asset_browser_poll(context) + and context.space_data.params.asset_category in cls.asset_categories + ) + + class AssetMetaDataPanel: bl_space_type = 'FILE_BROWSER' bl_region_type = 'TOOL_PROPS' diff --git a/release/scripts/presets/camera/1_inch.py b/release/scripts/presets/camera/1_inch.py index 72b039fb978..97e87b8c5a7 100644 --- a/release/scripts/presets/camera/1_inch.py +++ b/release/scripts/presets/camera/1_inch.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 13.2 bpy.context.camera.sensor_height = 8.80 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/1_slash_1.8_inch.py b/release/scripts/presets/camera/1_slash_1.8_inch.py index 38e09182de6..8b0dc3cea1d 100644 --- a/release/scripts/presets/camera/1_slash_1.8_inch.py +++ b/release/scripts/presets/camera/1_slash_1.8_inch.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 7.18 bpy.context.camera.sensor_height = 5.32 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/1_slash_2.3_inch.py b/release/scripts/presets/camera/1_slash_2.3_inch.py index 4d55738f4ed..bd6808da082 100644 --- a/release/scripts/presets/camera/1_slash_2.3_inch.py +++ b/release/scripts/presets/camera/1_slash_2.3_inch.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 6.17 bpy.context.camera.sensor_height = 4.55 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/1_slash_2.5_inch.py b/release/scripts/presets/camera/1_slash_2.5_inch.py index cbdb6f3cbe0..90f60e7d7f0 100644 --- a/release/scripts/presets/camera/1_slash_2.5_inch.py +++ b/release/scripts/presets/camera/1_slash_2.5_inch.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 5.76 bpy.context.camera.sensor_height = 4.29 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/1_slash_2.7_inch.py b/release/scripts/presets/camera/1_slash_2.7_inch.py index 5ccfa4ab555..4a9591803d0 100644 --- a/release/scripts/presets/camera/1_slash_2.7_inch.py +++ b/release/scripts/presets/camera/1_slash_2.7_inch.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 5.37 bpy.context.camera.sensor_height = 4.04 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/1_slash_3.2_inch.py b/release/scripts/presets/camera/1_slash_3.2_inch.py index 1963f7ec048..5f31b9ec49c 100644 --- a/release/scripts/presets/camera/1_slash_3.2_inch.py +++ b/release/scripts/presets/camera/1_slash_3.2_inch.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 4.54 bpy.context.camera.sensor_height = 3.42 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/2_slash_3_inch.py b/release/scripts/presets/camera/2_slash_3_inch.py index 25b46016800..eb463a31af7 100644 --- a/release/scripts/presets/camera/2_slash_3_inch.py +++ b/release/scripts/presets/camera/2_slash_3_inch.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 8.8 bpy.context.camera.sensor_height = 6.6 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/APS-C.py b/release/scripts/presets/camera/APS-C.py index 84e40825248..4031e8bae71 100644 --- a/release/scripts/presets/camera/APS-C.py +++ b/release/scripts/presets/camera/APS-C.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 23.6 bpy.context.camera.sensor_height = 15.6 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/APS-C_(Canon).py b/release/scripts/presets/camera/APS-C_(Canon).py index 55f20ce0eac..484929a54e7 100644 --- a/release/scripts/presets/camera/APS-C_(Canon).py +++ b/release/scripts/presets/camera/APS-C_(Canon).py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 22.30 bpy.context.camera.sensor_height = 14.90 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/APS-H_(Canon).py b/release/scripts/presets/camera/APS-H_(Canon).py index d63f733280b..d3b61d1aa46 100644 --- a/release/scripts/presets/camera/APS-H_(Canon).py +++ b/release/scripts/presets/camera/APS-H_(Canon).py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 27.90 bpy.context.camera.sensor_height = 18.60 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Analog_16mm.py b/release/scripts/presets/camera/Analog_16mm.py index aa98eaf2408..a290839c8e0 100644 --- a/release/scripts/presets/camera/Analog_16mm.py +++ b/release/scripts/presets/camera/Analog_16mm.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 10.26 bpy.context.camera.sensor_height = 7.49 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Analog_35mm.py b/release/scripts/presets/camera/Analog_35mm.py index a0dee1f0166..fe3338dd292 100644 --- a/release/scripts/presets/camera/Analog_35mm.py +++ b/release/scripts/presets/camera/Analog_35mm.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 22 bpy.context.camera.sensor_height = 16 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Analog_65mm.py b/release/scripts/presets/camera/Analog_65mm.py index 8de91ac0ee3..d6eb9c32283 100644 --- a/release/scripts/presets/camera/Analog_65mm.py +++ b/release/scripts/presets/camera/Analog_65mm.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 52.45 bpy.context.camera.sensor_height = 23.01 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Analog_IMAX.py b/release/scripts/presets/camera/Analog_IMAX.py index 5a445f3de8c..b71b910dee0 100644 --- a/release/scripts/presets/camera/Analog_IMAX.py +++ b/release/scripts/presets/camera/Analog_IMAX.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 71.41 bpy.context.camera.sensor_height = 52.63 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Analog_Super_16.py b/release/scripts/presets/camera/Analog_Super_16.py index a340a31dc25..f76238c69d3 100644 --- a/release/scripts/presets/camera/Analog_Super_16.py +++ b/release/scripts/presets/camera/Analog_Super_16.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 12.35 bpy.context.camera.sensor_height = 7.42 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Analog_Super_35.py b/release/scripts/presets/camera/Analog_Super_35.py index 3c8f1837253..b22ff545c68 100644 --- a/release/scripts/presets/camera/Analog_Super_35.py +++ b/release/scripts/presets/camera/Analog_Super_35.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 24.89 bpy.context.camera.sensor_height = 18.66 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Arri_Alexa_65.py b/release/scripts/presets/camera/Arri_Alexa_65.py index b1467709949..24d03e022ae 100644 --- a/release/scripts/presets/camera/Arri_Alexa_65.py +++ b/release/scripts/presets/camera/Arri_Alexa_65.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 54.12 bpy.context.camera.sensor_height = 25.58 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Arri_Alexa_LF.py b/release/scripts/presets/camera/Arri_Alexa_LF.py index 1cde94fce8d..430fdc996a6 100644 --- a/release/scripts/presets/camera/Arri_Alexa_LF.py +++ b/release/scripts/presets/camera/Arri_Alexa_LF.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 36.70 bpy.context.camera.sensor_height = 25.54 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Arri_Alexa_Mini_&_SXT.py b/release/scripts/presets/camera/Arri_Alexa_Mini_&_SXT.py index 0f61d35a0f9..90998bc0da0 100644 --- a/release/scripts/presets/camera/Arri_Alexa_Mini_&_SXT.py +++ b/release/scripts/presets/camera/Arri_Alexa_Mini_&_SXT.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 29.90 bpy.context.camera.sensor_height = 15.77 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Blackmagic_Pocket_&_Studio.py b/release/scripts/presets/camera/Blackmagic_Pocket_&_Studio.py index 260bfbaf94f..bb2b172919e 100644 --- a/release/scripts/presets/camera/Blackmagic_Pocket_&_Studio.py +++ b/release/scripts/presets/camera/Blackmagic_Pocket_&_Studio.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 12.48 bpy.context.camera.sensor_height = 7.02 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Blackmagic_Pocket_4K.py b/release/scripts/presets/camera/Blackmagic_Pocket_4K.py index dc057397828..4b735283c8b 100644 --- a/release/scripts/presets/camera/Blackmagic_Pocket_4K.py +++ b/release/scripts/presets/camera/Blackmagic_Pocket_4K.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 18.96 bpy.context.camera.sensor_height = 10.00 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Blackmagic_Pocket_6k.py b/release/scripts/presets/camera/Blackmagic_Pocket_6k.py index a483f3d5f98..1a882f05786 100644 --- a/release/scripts/presets/camera/Blackmagic_Pocket_6k.py +++ b/release/scripts/presets/camera/Blackmagic_Pocket_6k.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 23.10 bpy.context.camera.sensor_height = 12.99 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Blackmagic_URSA_4.6K.py b/release/scripts/presets/camera/Blackmagic_URSA_4.6K.py index c71e42d72d3..767d16984d8 100644 --- a/release/scripts/presets/camera/Blackmagic_URSA_4.6K.py +++ b/release/scripts/presets/camera/Blackmagic_URSA_4.6K.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 25.34 bpy.context.camera.sensor_height = 14.25 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Foveon_(Sigma).py b/release/scripts/presets/camera/Foveon_(Sigma).py index e6a1a0ed344..6b35f29acaf 100644 --- a/release/scripts/presets/camera/Foveon_(Sigma).py +++ b/release/scripts/presets/camera/Foveon_(Sigma).py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 20.70 bpy.context.camera.sensor_height = 13.80 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Fullframe.py b/release/scripts/presets/camera/Fullframe.py index 95fb4afc10b..c8017331b28 100644 --- a/release/scripts/presets/camera/Fullframe.py +++ b/release/scripts/presets/camera/Fullframe.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 36 bpy.context.camera.sensor_height = 24 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/MFT.py b/release/scripts/presets/camera/MFT.py index bc0dd49baa8..7441f1aea76 100644 --- a/release/scripts/presets/camera/MFT.py +++ b/release/scripts/presets/camera/MFT.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 17.3 bpy.context.camera.sensor_height = 13.0 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/Medium-format_(Hasselblad).py b/release/scripts/presets/camera/Medium-format_(Hasselblad).py index e9b16024b79..d03a4f22db7 100644 --- a/release/scripts/presets/camera/Medium-format_(Hasselblad).py +++ b/release/scripts/presets/camera/Medium-format_(Hasselblad).py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 44 bpy.context.camera.sensor_height = 33 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/RED_Dragon_5K.py b/release/scripts/presets/camera/RED_Dragon_5K.py index fa95a98f8c4..e8b990d4d00 100644 --- a/release/scripts/presets/camera/RED_Dragon_5K.py +++ b/release/scripts/presets/camera/RED_Dragon_5K.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 25.60 bpy.context.camera.sensor_height = 13.5 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/RED_Dragon_6K.py b/release/scripts/presets/camera/RED_Dragon_6K.py index 80f7ad1bbb8..982e2ab8e00 100644 --- a/release/scripts/presets/camera/RED_Dragon_6K.py +++ b/release/scripts/presets/camera/RED_Dragon_6K.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 30.70 bpy.context.camera.sensor_height = 15.80 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/RED_Helium_8K.py b/release/scripts/presets/camera/RED_Helium_8K.py index 0f61d35a0f9..90998bc0da0 100644 --- a/release/scripts/presets/camera/RED_Helium_8K.py +++ b/release/scripts/presets/camera/RED_Helium_8K.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 29.90 bpy.context.camera.sensor_height = 15.77 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/camera/RED_Monstro_8K.py b/release/scripts/presets/camera/RED_Monstro_8K.py index 86c382624ab..1c8bc11dfaa 100644 --- a/release/scripts/presets/camera/RED_Monstro_8K.py +++ b/release/scripts/presets/camera/RED_Monstro_8K.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 40.96 bpy.context.camera.sensor_height = 21.60 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/keyconfig/Blender.py b/release/scripts/presets/keyconfig/Blender.py index 222ee43432f..eb66c961472 100644 --- a/release/scripts/presets/keyconfig/Blender.py +++ b/release/scripts/presets/keyconfig/Blender.py @@ -103,8 +103,8 @@ class Prefs(bpy.types.KeyConfigPreferences): v3d_tilde_action: EnumProperty( name="Tilde Action", items=( - ('OBJECT_SWITCH', "Object Switch", - "Switch the active object under the cursor (when not in object mode)", + ('VIEW', "Navigate", + "View operations (useful for keyboards without a numpad)", 0), ('GIZMO', "Gizmos", "Control transform gizmos", @@ -113,7 +113,7 @@ class Prefs(bpy.types.KeyConfigPreferences): description=( "Action when 'Tilde' is pressed" ), - default='OBJECT_SWITCH', + default='VIEW', update=update_fn, ) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 0af7493ed47..3527e993173 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -730,6 +730,8 @@ def km_user_interface(_params): ("anim.keyingset_button_add", {"type": 'K', "value": 'PRESS'}, None), ("anim.keyingset_button_remove", {"type": 'K', "value": 'PRESS', "alt": True}, None), ("ui.reset_default_button", {"type": 'BACK_SPACE', "value": 'PRESS'}, {"properties": [("all", True)]}), + # UI lists (polls check if there's a UI list under the cursor). + ("ui.list_start_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), ]) return keymap @@ -1084,7 +1086,13 @@ def km_view3d(params): {"properties": [("use_all_regions", True), ("center", False)]}), ("view3d.view_all", {"type": 'C', "value": 'PRESS', "shift": True}, {"properties": [("center", True)]}), - op_menu_pie("VIEW3D_MT_view_pie", {"type": 'D', "value": 'CLICK_DRAG'}), + op_menu_pie( + "VIEW3D_MT_view_pie" if params.v3d_tilde_action == 'VIEW' else "VIEW3D_MT_transform_gizmo_pie", + {"type": 'ACCENT_GRAVE', "value": params.pie_value}, + ), + *(() if not params.use_pie_click_drag else + (("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'CLICK'}, None),)), + ("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "shift": True}, None), ("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "shift": True}, None), # Numpad views. ("view3d.view_camera", {"type": 'NUMPAD_0', "value": 'PRESS'}, None), @@ -1328,32 +1336,6 @@ def km_view3d(params): op_tool_cycle("builtin.select_box", {"type": 'W', "value": 'PRESS'}), ]) - # Tilda key. - if params.use_pie_click_drag: - items.extend([ - ("object.transfer_mode", - {"type": 'ACCENT_GRAVE', "value": 'CLICK' if params.use_pie_click_drag else 'PRESS'}, - None), - op_menu_pie( - "VIEW3D_MT_transform_gizmo_pie", - {"type": 'ACCENT_GRAVE', "value": 'CLICK_DRAG'}, - ) - ]) - else: - if params.v3d_tilde_action == 'OBJECT_SWITCH': - items.append( - ("object.transfer_mode", - {"type": 'ACCENT_GRAVE', "value": 'PRESS'}, - {"properties": [("use_eyedropper", False)]}) - ) - else: - items.append( - op_menu_pie( - "VIEW3D_MT_transform_gizmo_pie", - {"type": 'ACCENT_GRAVE', "value": 'PRESS'}, - ) - ) - return keymap @@ -2679,7 +2661,8 @@ def km_sequencer(params): {"properties": [("side", 'LEFT')]}), ("sequencer.select_side_of_frame", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, {"properties": [("side", 'RIGHT')]}), - + ("wm.context_toggle", {"type": 'TAB', "value": 'PRESS', "shift": True}, + {"properties": [("data_path", 'tool_settings.use_snap_sequencer')]}), *_template_items_context_menu("SEQUENCER_MT_context_menu", params.context_menu_event), ]) @@ -5071,6 +5054,11 @@ def km_object_non_modal(params): ("object.origin_set", {"type": 'C', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), ]) else: + items.extend([ + # NOTE: this shortcut (while not temporary) is not ideal, see: T89757. + ("object.transfer_mode", {"type": 'Q', "value": 'PRESS', "alt": True}, None), + ]) + if params.use_pie_click_drag: items.extend([ ("object.mode_set", {"type": 'TAB', "value": 'CLICK'}, diff --git a/release/scripts/presets/tracking_camera/1_inch.py b/release/scripts/presets/tracking_camera/1_inch.py index 72b039fb978..97e87b8c5a7 100644 --- a/release/scripts/presets/tracking_camera/1_inch.py +++ b/release/scripts/presets/tracking_camera/1_inch.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 13.2 bpy.context.camera.sensor_height = 8.80 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/1_slash_1.8_inch.py b/release/scripts/presets/tracking_camera/1_slash_1.8_inch.py index 38e09182de6..8b0dc3cea1d 100644 --- a/release/scripts/presets/tracking_camera/1_slash_1.8_inch.py +++ b/release/scripts/presets/tracking_camera/1_slash_1.8_inch.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 7.18 bpy.context.camera.sensor_height = 5.32 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/1_slash_2.3_inch.py b/release/scripts/presets/tracking_camera/1_slash_2.3_inch.py index 4d55738f4ed..bd6808da082 100644 --- a/release/scripts/presets/tracking_camera/1_slash_2.3_inch.py +++ b/release/scripts/presets/tracking_camera/1_slash_2.3_inch.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 6.17 bpy.context.camera.sensor_height = 4.55 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/1_slash_2.5_inch.py b/release/scripts/presets/tracking_camera/1_slash_2.5_inch.py index cbdb6f3cbe0..90f60e7d7f0 100644 --- a/release/scripts/presets/tracking_camera/1_slash_2.5_inch.py +++ b/release/scripts/presets/tracking_camera/1_slash_2.5_inch.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 5.76 bpy.context.camera.sensor_height = 4.29 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/1_slash_2.7_inch.py b/release/scripts/presets/tracking_camera/1_slash_2.7_inch.py index 5ccfa4ab555..4a9591803d0 100644 --- a/release/scripts/presets/tracking_camera/1_slash_2.7_inch.py +++ b/release/scripts/presets/tracking_camera/1_slash_2.7_inch.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 5.37 bpy.context.camera.sensor_height = 4.04 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/1_slash_3.2_inch.py b/release/scripts/presets/tracking_camera/1_slash_3.2_inch.py index 1963f7ec048..5f31b9ec49c 100644 --- a/release/scripts/presets/tracking_camera/1_slash_3.2_inch.py +++ b/release/scripts/presets/tracking_camera/1_slash_3.2_inch.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 4.54 bpy.context.camera.sensor_height = 3.42 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/2_slash_3_inch.py b/release/scripts/presets/tracking_camera/2_slash_3_inch.py index 25b46016800..eb463a31af7 100644 --- a/release/scripts/presets/tracking_camera/2_slash_3_inch.py +++ b/release/scripts/presets/tracking_camera/2_slash_3_inch.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 8.8 bpy.context.camera.sensor_height = 6.6 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/APS-C.py b/release/scripts/presets/tracking_camera/APS-C.py index 84e40825248..4031e8bae71 100644 --- a/release/scripts/presets/tracking_camera/APS-C.py +++ b/release/scripts/presets/tracking_camera/APS-C.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 23.6 bpy.context.camera.sensor_height = 15.6 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/APS-C_(Canon).py b/release/scripts/presets/tracking_camera/APS-C_(Canon).py index 55f20ce0eac..484929a54e7 100644 --- a/release/scripts/presets/tracking_camera/APS-C_(Canon).py +++ b/release/scripts/presets/tracking_camera/APS-C_(Canon).py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 22.30 bpy.context.camera.sensor_height = 14.90 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/APS-H_(Canon).py b/release/scripts/presets/tracking_camera/APS-H_(Canon).py index d63f733280b..d3b61d1aa46 100644 --- a/release/scripts/presets/tracking_camera/APS-H_(Canon).py +++ b/release/scripts/presets/tracking_camera/APS-H_(Canon).py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 27.90 bpy.context.camera.sensor_height = 18.60 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/Analog_16mm.py b/release/scripts/presets/tracking_camera/Analog_16mm.py index aa98eaf2408..a290839c8e0 100644 --- a/release/scripts/presets/tracking_camera/Analog_16mm.py +++ b/release/scripts/presets/tracking_camera/Analog_16mm.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 10.26 bpy.context.camera.sensor_height = 7.49 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/Analog_35mm.py b/release/scripts/presets/tracking_camera/Analog_35mm.py index a0dee1f0166..fe3338dd292 100644 --- a/release/scripts/presets/tracking_camera/Analog_35mm.py +++ b/release/scripts/presets/tracking_camera/Analog_35mm.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 22 bpy.context.camera.sensor_height = 16 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/Analog_65mm.py b/release/scripts/presets/tracking_camera/Analog_65mm.py index 8de91ac0ee3..d6eb9c32283 100644 --- a/release/scripts/presets/tracking_camera/Analog_65mm.py +++ b/release/scripts/presets/tracking_camera/Analog_65mm.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 52.45 bpy.context.camera.sensor_height = 23.01 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/Analog_IMAX.py b/release/scripts/presets/tracking_camera/Analog_IMAX.py index 5a445f3de8c..b71b910dee0 100644 --- a/release/scripts/presets/tracking_camera/Analog_IMAX.py +++ b/release/scripts/presets/tracking_camera/Analog_IMAX.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 71.41 bpy.context.camera.sensor_height = 52.63 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/Analog_Super_16.py b/release/scripts/presets/tracking_camera/Analog_Super_16.py index a340a31dc25..f76238c69d3 100644 --- a/release/scripts/presets/tracking_camera/Analog_Super_16.py +++ b/release/scripts/presets/tracking_camera/Analog_Super_16.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 12.35 bpy.context.camera.sensor_height = 7.42 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/Analog_Super_35.py b/release/scripts/presets/tracking_camera/Analog_Super_35.py index 3c8f1837253..b22ff545c68 100644 --- a/release/scripts/presets/tracking_camera/Analog_Super_35.py +++ b/release/scripts/presets/tracking_camera/Analog_Super_35.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 24.89 bpy.context.camera.sensor_height = 18.66 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/Arri_Alexa_65.py b/release/scripts/presets/tracking_camera/Arri_Alexa_65.py index b1467709949..24d03e022ae 100644 --- a/release/scripts/presets/tracking_camera/Arri_Alexa_65.py +++ b/release/scripts/presets/tracking_camera/Arri_Alexa_65.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 54.12 bpy.context.camera.sensor_height = 25.58 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/Arri_Alexa_LF.py b/release/scripts/presets/tracking_camera/Arri_Alexa_LF.py index 1cde94fce8d..430fdc996a6 100644 --- a/release/scripts/presets/tracking_camera/Arri_Alexa_LF.py +++ b/release/scripts/presets/tracking_camera/Arri_Alexa_LF.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 36.70 bpy.context.camera.sensor_height = 25.54 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/Arri_Alexa_Mini_&_SXT.py b/release/scripts/presets/tracking_camera/Arri_Alexa_Mini_&_SXT.py index 0f61d35a0f9..90998bc0da0 100644 --- a/release/scripts/presets/tracking_camera/Arri_Alexa_Mini_&_SXT.py +++ b/release/scripts/presets/tracking_camera/Arri_Alexa_Mini_&_SXT.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 29.90 bpy.context.camera.sensor_height = 15.77 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_&_Studio.py b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_&_Studio.py index 260bfbaf94f..bb2b172919e 100644 --- a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_&_Studio.py +++ b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_&_Studio.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 12.48 bpy.context.camera.sensor_height = 7.02 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_4K.py b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_4K.py index dc057397828..4b735283c8b 100644 --- a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_4K.py +++ b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_4K.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 18.96 bpy.context.camera.sensor_height = 10.00 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_6k.py b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_6k.py index a483f3d5f98..1a882f05786 100644 --- a/release/scripts/presets/tracking_camera/Blackmagic_Pocket_6k.py +++ b/release/scripts/presets/tracking_camera/Blackmagic_Pocket_6k.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 23.10 bpy.context.camera.sensor_height = 12.99 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/Blackmagic_URSA_4.6K.py b/release/scripts/presets/tracking_camera/Blackmagic_URSA_4.6K.py index c71e42d72d3..767d16984d8 100644 --- a/release/scripts/presets/tracking_camera/Blackmagic_URSA_4.6K.py +++ b/release/scripts/presets/tracking_camera/Blackmagic_URSA_4.6K.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 25.34 bpy.context.camera.sensor_height = 14.25 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/Foveon_(Sigma).py b/release/scripts/presets/tracking_camera/Foveon_(Sigma).py index e6a1a0ed344..6b35f29acaf 100644 --- a/release/scripts/presets/tracking_camera/Foveon_(Sigma).py +++ b/release/scripts/presets/tracking_camera/Foveon_(Sigma).py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 20.70 bpy.context.camera.sensor_height = 13.80 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/Fullframe.py b/release/scripts/presets/tracking_camera/Fullframe.py index 95fb4afc10b..c8017331b28 100644 --- a/release/scripts/presets/tracking_camera/Fullframe.py +++ b/release/scripts/presets/tracking_camera/Fullframe.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 36 bpy.context.camera.sensor_height = 24 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/MFT.py b/release/scripts/presets/tracking_camera/MFT.py index bc0dd49baa8..7441f1aea76 100644 --- a/release/scripts/presets/tracking_camera/MFT.py +++ b/release/scripts/presets/tracking_camera/MFT.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 17.3 bpy.context.camera.sensor_height = 13.0 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/Medium-format_(Hasselblad).py b/release/scripts/presets/tracking_camera/Medium-format_(Hasselblad).py index e9b16024b79..d03a4f22db7 100644 --- a/release/scripts/presets/tracking_camera/Medium-format_(Hasselblad).py +++ b/release/scripts/presets/tracking_camera/Medium-format_(Hasselblad).py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 44 bpy.context.camera.sensor_height = 33 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/RED_Dragon_5K.py b/release/scripts/presets/tracking_camera/RED_Dragon_5K.py index fa95a98f8c4..e8b990d4d00 100644 --- a/release/scripts/presets/tracking_camera/RED_Dragon_5K.py +++ b/release/scripts/presets/tracking_camera/RED_Dragon_5K.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 25.60 bpy.context.camera.sensor_height = 13.5 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/RED_Dragon_6K.py b/release/scripts/presets/tracking_camera/RED_Dragon_6K.py index 80f7ad1bbb8..982e2ab8e00 100644 --- a/release/scripts/presets/tracking_camera/RED_Dragon_6K.py +++ b/release/scripts/presets/tracking_camera/RED_Dragon_6K.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 30.70 bpy.context.camera.sensor_height = 15.80 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/RED_Helium_8K.py b/release/scripts/presets/tracking_camera/RED_Helium_8K.py index 0f61d35a0f9..90998bc0da0 100644 --- a/release/scripts/presets/tracking_camera/RED_Helium_8K.py +++ b/release/scripts/presets/tracking_camera/RED_Helium_8K.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 29.90 bpy.context.camera.sensor_height = 15.77 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/presets/tracking_camera/RED_Monstro_8K.py b/release/scripts/presets/tracking_camera/RED_Monstro_8K.py index 86c382624ab..1c8bc11dfaa 100644 --- a/release/scripts/presets/tracking_camera/RED_Monstro_8K.py +++ b/release/scripts/presets/tracking_camera/RED_Monstro_8K.py @@ -1,4 +1,4 @@ import bpy bpy.context.camera.sensor_width = 40.96 bpy.context.camera.sensor_height = 21.60 -bpy.context.camera.sensor_fit = 'HORIZONTAL'
\ No newline at end of file +bpy.context.camera.sensor_fit = 'HORIZONTAL' diff --git a/release/scripts/startup/bl_operators/assets.py b/release/scripts/startup/bl_operators/assets.py index 48f07a03773..d2655784afd 100644 --- a/release/scripts/startup/bl_operators/assets.py +++ b/release/scripts/startup/bl_operators/assets.py @@ -18,7 +18,9 @@ # <pep8 compliant> from __future__ import annotations +from pathlib import Path +import bpy from bpy.types import Operator from bpy_extras.asset_utils import ( @@ -72,7 +74,88 @@ class ASSET_OT_tag_remove(Operator): return {'FINISHED'} +class ASSET_OT_open_containing_blend_file(Operator): + """Open the blend file that contains the active asset""" + + bl_idname = "asset.open_containing_blend_file" + bl_label = "Open Blend File" + bl_options = {'REGISTER'} + + _process = None # Optional[subprocess.Popen] + + @classmethod + def poll(cls, context): + asset_file_handle = getattr(context, 'asset_file_handle', None) + asset_library = getattr(context, 'asset_library', None) + + if not asset_library: + cls.poll_message_set("No asset library selected") + return False + if not asset_file_handle: + cls.poll_message_set("No asset selected") + return False + if asset_file_handle.local_id: + cls.poll_message_set("Selected asset is contained in the current file") + return False + return True + + def execute(self, context): + asset_file_handle = context.asset_file_handle + asset_library = context.asset_library + + if asset_file_handle.local_id: + self.report({'WARNING'}, "This asset is stored in the current blend file") + return {'CANCELLED'} + + asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library) + self.open_in_new_blender(asset_lib_path) + + wm = context.window_manager + self._timer = wm.event_timer_add(0.1, window=context.window) + wm.modal_handler_add(self) + + return {'RUNNING_MODAL'} + + def modal(self, context, event): + if event.type != 'TIMER': + return {'PASS_THROUGH'} + + if self._process is None: + self.report({'ERROR'}, "Unable to find any running process") + self.cancel(context) + return {'CANCELLED'} + + returncode = self._process.poll() + if returncode is None: + # Process is still running. + return {'RUNNING_MODAL'} + + if returncode: + self.report({'WARNING'}, "Blender subprocess exited with error code %d" % returncode) + + # TODO(Sybren): Replace this with a generic "reload assets" operator + # that can run outside of the Asset Browser context. + if bpy.ops.file.refresh.poll(): + bpy.ops.file.refresh() + if bpy.ops.asset.list_refresh.poll(): + bpy.ops.asset.list_refresh() + + self.cancel(context) + return {'FINISHED'} + + def cancel(self, context): + wm = context.window_manager + wm.event_timer_remove(self._timer) + + def open_in_new_blender(self, filepath): + import subprocess + + cli_args = [bpy.app.binary_path, str(filepath)] + self._process = subprocess.Popen(cli_args) + + classes = ( ASSET_OT_tag_add, ASSET_OT_tag_remove, + ASSET_OT_open_containing_blend_file, ) diff --git a/release/scripts/startup/bl_operators/sequencer.py b/release/scripts/startup/bl_operators/sequencer.py index 48a02a4c5c6..8f678896e61 100644 --- a/release/scripts/startup/bl_operators/sequencer.py +++ b/release/scripts/startup/bl_operators/sequencer.py @@ -108,14 +108,13 @@ class SequencerSplitMulticam(Operator): if s.multicam_source == camera or camera >= s.channel: return {'FINISHED'} - if not s.select: - s.select = True - cfra = context.scene.frame_current - bpy.ops.sequencer.split(frame=cfra, type='SOFT', side='RIGHT') - for s in context.scene.sequence_editor.sequences_all: - if s.select and s.type == 'MULTICAM' and s.frame_final_start <= cfra and cfra < s.frame_final_end: - context.scene.sequence_editor.active_strip = s + right_strip = s.split(frame=cfra, split_method='SOFT') + + if right_strip: + s.select = False + right_strip.select = True + context.scene.sequence_editor.active_strip = right_strip context.scene.sequence_editor.active_strip.multicam_source = camera return {'FINISHED'} diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index ef705f8fe37..25484e905c3 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -117,13 +117,15 @@ def register(): for cls in mod.classes: register_class(cls) - # space_userprefs.py from bpy.props import ( EnumProperty, StringProperty, ) - from bpy.types import WindowManager + from bpy.types import ( + WindowManager, + ) + # space_userprefs.py def addon_filter_items(_self, _context): import addon_utils @@ -234,3 +236,21 @@ class UI_UL_list(bpy.types.UIList): bpy.utils.register_class(UI_UL_list) + + +class UI_MT_list_item_context_menu(bpy.types.Menu): + """ + UI List item context menu definition. Scripts can append/prepend this to + add own operators to the context menu. They must check context though, so + their items only draw in a valid context and for the correct UI list. + """ + + bl_label = "List Item" + bl_idname = "UI_MT_list_item_context_menu" + + def draw(self, context): + # Dummy function. This type is just for scripts to append their own + # context menu items. + pass + +bpy.utils.register_class(UI_MT_list_item_context_menu) diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index fb8aedd1f49..d9ad094ac4f 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -41,7 +41,6 @@ class MESH_MT_vertex_group_context_menu(Menu): ).sort_type = 'BONE_HIERARCHY' layout.separator() layout.operator("object.vertex_group_copy", icon='DUPLICATE') - layout.operator("object.vertex_group_copy_to_linked") layout.operator("object.vertex_group_copy_to_selected") layout.separator() layout.operator("object.vertex_group_mirror", icon='ARROW_LEFTRIGHT').use_topology = False @@ -647,7 +646,7 @@ class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel): if len(colliding_names) == 0: return - layout.label(text="Name Collisions: {}".format(", ".join(colliding_names)), icon='INFO') + layout.label(text="Name collisions: {}".format(", ".join(colliding_names)), icon='ERROR') classes = ( diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 1ad88744b16..8ca93d2406c 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -552,6 +552,10 @@ class FILEBROWSER_MT_context_menu(Menu): sub.operator_context = 'EXEC_DEFAULT' sub.operator("file.delete", text="Delete") + active_asset = asset_utils.SpaceAssetInfo.get_active_asset(context) + if active_asset: + layout.operator("asset.open_containing_blend_file") + layout.separator() sub = layout.row() @@ -592,15 +596,28 @@ class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel): def draw(self, context): layout = self.layout - active_file = context.active_file - active_asset = asset_utils.SpaceAssetInfo.get_active_asset(context) + asset_file_handle = context.asset_file_handle - if not active_file or not active_asset: + if asset_file_handle is None: layout.label(text="No asset selected", icon='INFO') return - # If the active file is an ID, use its name directly so renaming is possible from right here. - layout.prop(context.id if context.id is not None else active_file, "name", text="") + asset_library = context.asset_library + asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library) + + if asset_file_handle.local_id: + # If the active file is an ID, use its name directly so renaming is possible from right here. + layout.prop(asset_file_handle.local_id, "name", text="") + row = layout.row() + row.label(text="Source: Current File") + else: + layout.prop(asset_file_handle, "name", text="") + col = layout.column(align=True) # Just to reduce margin. + col.label(text="Source:") + row = col.row() + row.label(text=asset_lib_path) + + row.operator("asset.open_containing_blend_file", text="", icon='TOOL_SETTINGS') class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel): diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index b1ed1441245..1d616947db6 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -516,6 +516,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeCurveStar"), NodeItem("GeometryNodeCurveSpiral"), NodeItem("GeometryNodeCurveQuadraticBezier"), + NodeItem("GeometryNodeCurvePrimitiveQuadrilateral"), NodeItem("GeometryNodeCurvePrimitiveBezierSegment"), ]), GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[ @@ -559,6 +560,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeMeshLine"), NodeItem("GeometryNodeMeshUVSphere"), ]), + GeometryNodeCategory("GEO_POINT", "Point", items=[ NodeItem("GeometryNodePointDistribute"), NodeItem("GeometryNodePointInstance"), diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h index 030560015a9..07da9d75e59 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -268,6 +268,12 @@ void animsys_evaluate_action(struct PointerRNA *ptr, const struct AnimationEvalContext *anim_eval_context, bool flush_to_original); +/* Evaluate action, and blend the result into the current values (instead of overwriting fully). */ +void animsys_blend_in_action(struct PointerRNA *ptr, + struct bAction *act, + const AnimationEvalContext *anim_eval_context, + float blend_factor); + /* Evaluate Action Group */ void animsys_evaluate_action_group(struct PointerRNA *ptr, struct bAction *act, diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h index 86aa18e5739..e13475fd78c 100644 --- a/source/blender/blenkernel/BKE_armature.h +++ b/source/blender/blenkernel/BKE_armature.h @@ -28,7 +28,6 @@ extern "C" { #endif struct AnimationEvalContext; -struct bAction; struct BMEditMesh; struct Bone; struct Depsgraph; @@ -39,6 +38,7 @@ struct Mesh; struct Object; struct PoseTree; struct Scene; +struct bAction; struct bArmature; struct bConstraint; struct bGPDstroke; @@ -207,9 +207,18 @@ void BKE_pose_where_is_bone_tail(struct bPoseChannel *pchan); /* Evaluate the action and apply it to the pose. If any pose bones are selected, only FCurves that * relate to those bones are evaluated. */ -void BKE_pose_apply_action(struct Object *ob, - struct bAction *action, - struct AnimationEvalContext *anim_eval_context); +void BKE_pose_apply_action_selected_bones(struct Object *ob, + struct bAction *action, + struct AnimationEvalContext *anim_eval_context); +/* Evaluate the action and apply it to the pose. Ignore selection state of the bones. */ +void BKE_pose_apply_action_all_bones(struct Object *ob, + struct bAction *action, + struct AnimationEvalContext *anim_eval_context); + +void BKE_pose_apply_action_blend(struct Object *ob, + struct bAction *action, + struct AnimationEvalContext *anim_eval_context, + float blend_factor); void vec_roll_to_mat3(const float vec[3], const float roll, float r_mat[3][3]); void vec_roll_to_mat3_normalized(const float nor[3], const float roll, float r_mat[3][3]); diff --git a/source/blender/blenkernel/BKE_asset.h b/source/blender/blenkernel/BKE_asset.h index d1f543b1f38..50eb2859279 100644 --- a/source/blender/blenkernel/BKE_asset.h +++ b/source/blender/blenkernel/BKE_asset.h @@ -26,6 +26,7 @@ extern "C" { #endif +struct AssetLibraryReference; struct BlendDataReader; struct BlendWriter; struct ID; @@ -45,6 +46,8 @@ struct AssetTagEnsureResult BKE_asset_metadata_tag_ensure(struct AssetMetaData * const char *name); void BKE_asset_metadata_tag_remove(struct AssetMetaData *asset_data, struct AssetTag *tag); +void BKE_asset_library_reference_init_default(struct AssetLibraryReference *library_ref); + struct PreviewImage *BKE_asset_metadata_preview_get_from_id(const struct AssetMetaData *asset_data, const struct ID *owner_id); diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 30f6ad7475d..28903c4787c 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,13 +39,13 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 9 +#define BLENDER_FILE_SUBVERSION 12 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file * was written with too new a version. */ -#define BLENDER_FILE_MIN_VERSION 290 -#define BLENDER_FILE_MIN_SUBVERSION 0 +#define BLENDER_FILE_MIN_VERSION 300 +#define BLENDER_FILE_MIN_SUBVERSION 11 /** User readable version string. */ const char *BKE_blender_version_string(void); diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index 50aa6027840..8917580689d 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -23,6 +23,9 @@ * \ingroup bke */ +/* XXX temporary, until AssetHandle is designed properly and queries can return a pointer to it. */ +#include "DNA_asset_types.h" + #include "DNA_listBase.h" #include "DNA_object_enums.h" #include "RNA_types.h" @@ -357,6 +360,9 @@ int CTX_data_visible_gpencil_layers(const bContext *C, ListBase *list); int CTX_data_editable_gpencil_layers(const bContext *C, ListBase *list); int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list); +const struct AssetLibraryReference *CTX_wm_asset_library(const bContext *C); +struct AssetHandle CTX_wm_asset_handle(const bContext *C, bool *r_is_valid); + bool CTX_wm_interface_locked(const bContext *C); /* Gets pointer to the dependency graph. diff --git a/source/blender/blenkernel/BKE_deform.h b/source/blender/blenkernel/BKE_deform.h index 8b5fdf69bb0..f4221d57428 100644 --- a/source/blender/blenkernel/BKE_deform.h +++ b/source/blender/blenkernel/BKE_deform.h @@ -30,6 +30,7 @@ extern "C" { struct BlendDataReader; struct BlendWriter; +struct ID; struct ListBase; struct MDeformVert; struct MEdge; @@ -38,6 +39,18 @@ struct MPoly; struct Object; struct bDeformGroup; +bool BKE_object_supports_vertex_groups(const struct Object *ob); +const struct ListBase *BKE_object_defgroup_list(const struct Object *ob); +struct ListBase *BKE_object_defgroup_list_mutable(struct Object *ob); + +int BKE_object_defgroup_count(const struct Object *ob); +int BKE_object_defgroup_active_index_get(const struct Object *ob); +void BKE_object_defgroup_active_index_set(struct Object *ob, const int new_index); + +const struct ListBase *BKE_id_defgroup_list_get(const struct ID *id); +struct ListBase *BKE_id_defgroup_list_get_mutable(struct ID *id); +int BKE_id_defgroup_name_index(const struct ID *id, const char *name); + struct bDeformGroup *BKE_object_defgroup_new(struct Object *ob, const char *name); void BKE_defgroup_copy_list(struct ListBase *outbase, const struct ListBase *inbase); struct bDeformGroup *BKE_defgroup_duplicate(const struct bDeformGroup *ingroup); @@ -171,6 +184,7 @@ void BKE_defvert_blend_write(struct BlendWriter *writer, int count, struct MDefo void BKE_defvert_blend_read(struct BlendDataReader *reader, int count, struct MDeformVert *mdverts); +void BKE_defbase_blend_write(struct BlendWriter *writer, const ListBase *defbase); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_editmesh.h b/source/blender/blenkernel/BKE_editmesh.h index 9112877b5a3..ffd8ac42c63 100644 --- a/source/blender/blenkernel/BKE_editmesh.h +++ b/source/blender/blenkernel/BKE_editmesh.h @@ -24,7 +24,7 @@ * only concerned with low level operations on the #BMEditMesh structure. */ -#include "BKE_customdata.h" +#include "DNA_customdata_types.h" #include "bmesh.h" #ifdef __cplusplus @@ -32,8 +32,8 @@ extern "C" { #endif struct BMLoop; -struct BMesh; struct BMPartialUpdate; +struct BMesh; struct BMeshCalcTessellation_Params; struct BoundBox; struct Depsgraph; @@ -44,34 +44,39 @@ struct Scene; /** * This structure is used for mesh edit-mode. * - * through this, you get access to both the edit #BMesh, - * its tessellation, and various stuff that doesn't belong in the BMesh - * struct itself. + * Through this, you get access to both the edit #BMesh, its tessellation, + * and various data that doesn't belong in the #BMesh struct itself + * (mostly related to mesh evaluation). * - * the entire derivedmesh and modifier system works with this structure, - * and not BMesh. Mesh->edit_bmesh stores a pointer to this structure. */ + * The entire modifier system works with this structure, and not #BMesh. + * #Mesh.edit_bmesh stores a pointer to this structure. */ typedef struct BMEditMesh { struct BMesh *bm; - /* we store tessellations as triplets of three loops, - * which each define a triangle. */ + /** + * Face triangulation (tessellation) is stored as triplets of three loops, + * which each define a triangle. + * + * \see #MLoopTri as the documentation gives useful hints that apply to this data too. + */ struct BMLoop *(*looptris)[3]; int tottri; struct Mesh *mesh_eval_final, *mesh_eval_cage; - /** Cached cage bounding box for selection. */ + /** Cached cage bounding box of `mesh_eval_cage` for selection. */ struct BoundBox *bb_cage; /** Evaluated mesh data-mask. */ CustomData_MeshMasks lastDataMask; - /* Selection mode. */ + /** Selection mode (#SCE_SELECT_VERTEX, #SCE_SELECT_EDGE & #SCE_SELECT_FACE). */ short selectmode; + /** The active material (assigned to newly created faces). */ short mat_nr; - /* Temp variables for x-mirror editing. */ - int mirror_cdlayer; /* -1 is invalid */ + /** Temp variables for x-mirror editing (-1 when the layer does not exist). */ + int mirror_cdlayer; /** * ID data is older than edit-mode data. @@ -94,11 +99,11 @@ void BKE_editmesh_looptri_and_normals_calc_with_partial(BMEditMesh *em, void BKE_editmesh_looptri_and_normals_calc(BMEditMesh *em); -BMEditMesh *BKE_editmesh_create(BMesh *bm, const bool do_tessellate); +BMEditMesh *BKE_editmesh_create(BMesh *bm); BMEditMesh *BKE_editmesh_copy(BMEditMesh *em); BMEditMesh *BKE_editmesh_from_object(struct Object *ob); -void BKE_editmesh_free_derivedmesh(BMEditMesh *em); -void BKE_editmesh_free(BMEditMesh *em); +void BKE_editmesh_free_derived_caches(BMEditMesh *em); +void BKE_editmesh_free_data(BMEditMesh *em); float (*BKE_editmesh_vert_coords_alloc(struct Depsgraph *depsgraph, struct BMEditMesh *em, diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 82c9a31dfce..42e9ce82278 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -35,12 +35,12 @@ #include "BKE_geometry_set.h" struct Collection; +struct Curve; +struct CurveEval; struct Mesh; struct Object; struct PointCloud; struct Volume; -struct Curve; -struct CurveEval; enum class GeometryOwnershipType { /* The geometry is owned. This implies that it can be changed. */ @@ -325,10 +325,6 @@ class MeshComponent : public GeometryComponent { private: Mesh *mesh_ = nullptr; GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned; - /* Due to historical design choices, vertex group data is stored in the mesh, but the vertex - * group names are stored on an object. Since we don't have an object here, we copy over the - * names into this map. */ - blender::Map<std::string, int> vertex_group_names_; public: MeshComponent(); @@ -338,14 +334,8 @@ class MeshComponent : public GeometryComponent { void clear(); bool has_mesh() const; void replace(Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); - void replace_mesh_but_keep_vertex_group_names( - Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); Mesh *release(); - void copy_vertex_group_names_from_object(const struct Object &object); - const blender::Map<std::string, int> &vertex_group_names() const; - blender::Map<std::string, int> &vertex_group_names(); - const Mesh *get_for_read() const; Mesh *get_for_write(); diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index c11c34cb312..92e70b41e7b 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -49,22 +49,22 @@ struct bGPDlayer_Mask; struct bGPDstroke; struct bGPdata; -#define GPENCIL_SIMPLIFY(scene) ((scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ENABLE)) +#define GPENCIL_SIMPLIFY(scene) (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ENABLE) #define GPENCIL_SIMPLIFY_ONPLAY(playing) \ (((playing == true) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ON_PLAY)) || \ ((scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_ON_PLAY) == 0)) #define GPENCIL_SIMPLIFY_FILL(scene, playing) \ - ((GPENCIL_SIMPLIFY_ONPLAY(playing) && (GPENCIL_SIMPLIFY(scene)) && \ + ((GPENCIL_SIMPLIFY_ONPLAY(playing) && GPENCIL_SIMPLIFY(scene) && \ (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_FILL))) #define GPENCIL_SIMPLIFY_MODIF(scene) \ ((GPENCIL_SIMPLIFY(scene) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_MODIFIER))) #define GPENCIL_SIMPLIFY_FX(scene, playing) \ - ((GPENCIL_SIMPLIFY_ONPLAY(playing) && (GPENCIL_SIMPLIFY(scene)) && \ + ((GPENCIL_SIMPLIFY_ONPLAY(playing) && GPENCIL_SIMPLIFY(scene) && \ (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_FX))) #define GPENCIL_SIMPLIFY_TINT(scene) \ - ((GPENCIL_SIMPLIFY(scene)) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_TINT)) + (GPENCIL_SIMPLIFY(scene) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_TINT)) #define GPENCIL_SIMPLIFY_AA(scene) \ - ((GPENCIL_SIMPLIFY(scene)) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_AA)) + (GPENCIL_SIMPLIFY(scene) && (scene->r.simplify_gpencil & SIMPLIFY_GPENCIL_AA)) /* Vertex Color macros. */ #define GPENCIL_USE_VERTEX_COLOR(toolsettings) \ @@ -154,17 +154,6 @@ bool BKE_gpencil_merge_materials(struct Object *ob, /* statistics functions */ void BKE_gpencil_stats_update(struct bGPdata *gpd); -/* Utilities for creating and populating GP strokes */ -/* - Number of values defining each point in the built-in data - * buffers for primitives (e.g. 2D Monkey) - */ -#define GP_PRIM_DATABUF_SIZE 5 - -void BKE_gpencil_stroke_add_points(struct bGPDstroke *gps, - const float *array, - const int totpoints, - const float mat[4][4]); - struct bGPDstroke *BKE_gpencil_stroke_new(int mat_idx, int totpoints, short thickness); struct bGPDstroke *BKE_gpencil_stroke_add( struct bGPDframe *gpf, int mat_idx, int totpoints, short thickness, const bool insert_at_head); diff --git a/source/blender/blenkernel/BKE_gpencil_modifier.h b/source/blender/blenkernel/BKE_gpencil_modifier.h index 8fbc2112c77..33524e47473 100644 --- a/source/blender/blenkernel/BKE_gpencil_modifier.h +++ b/source/blender/blenkernel/BKE_gpencil_modifier.h @@ -325,6 +325,12 @@ struct bGPDframe *BKE_gpencil_frame_retime_get(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob, struct bGPDlayer *gpl); +int BKE_gpencil_time_modifier_cfra(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob, + struct bGPDlayer *gpl, + const int cfra, + const bool is_render); void BKE_gpencil_modifier_blend_write(struct BlendWriter *writer, struct ListBase *modbase); void BKE_gpencil_modifier_blend_read_data(struct BlendDataReader *reader, struct ListBase *lb); diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 27076d908e7..c6658ff424a 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -42,8 +42,8 @@ extern "C" { #endif -struct Collection; struct BlendFileReadReport; +struct Collection; struct ID; struct IDOverrideLibrary; struct IDOverrideLibraryProperty; diff --git a/source/blender/blenkernel/BKE_mesh_types.h b/source/blender/blenkernel/BKE_mesh_types.h index aed8c44a031..b223d3872ff 100644 --- a/source/blender/blenkernel/BKE_mesh_types.h +++ b/source/blender/blenkernel/BKE_mesh_types.h @@ -27,6 +27,7 @@ typedef enum eMeshBatchDirtyMode { BKE_MESH_BATCH_DIRTY_SELECT, BKE_MESH_BATCH_DIRTY_SELECT_PAINT, BKE_MESH_BATCH_DIRTY_SHADING, + BKE_MESH_BATCH_DIRTY_DEFORM, BKE_MESH_BATCH_DIRTY_UVEDIT_ALL, BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT, } eMeshBatchDirtyMode; diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index c9f724036e0..63b11a194ff 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1274,6 +1274,11 @@ void ntreeGPUMaterialNodes(struct bNodeTree *localtree, #define CMP_CRYPTOMATTE_SRC_RENDER 0 #define CMP_CRYPTOMATTE_SRC_IMAGE 1 +/* Default SMAA configuration values. */ +#define CMP_DEFAULT_SMAA_THRESHOLD 1.0f +#define CMP_DEFAULT_SMAA_CONTRAST_LIMIT 0.2f +#define CMP_DEFAULT_SMAA_CORNER_ROUNDING 0.25f + /* API */ void ntreeCompositExecTree(struct Scene *scene, struct bNodeTree *ntree, @@ -1458,6 +1463,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_VIEWER 1067 #define GEO_NODE_CURVE_PRIMITIVE_LINE 1068 #define GEO_NODE_CURVE_ENDPOINTS 1069 +#define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070 /** \} */ diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 5958d1cd73c..3485bd8ce38 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -376,6 +376,10 @@ void BKE_object_runtime_free_data(struct Object *object); void BKE_object_batch_cache_dirty_tag(struct Object *ob); void BKE_object_data_batch_cache_dirty_tag(struct ID *object_data); +void BKE_object_data_eval_batch_cache_dirty_tag(struct Depsgraph *depsgraph, + struct ID *object_data); +void BKE_object_data_eval_batch_cache_deform_tag(struct Depsgraph *depsgraph, + struct ID *object_data); /* this function returns a superset of the scenes selection based on relationships */ diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index 9792f819bf9..6d58e165ea3 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -124,14 +124,16 @@ bool BKE_scene_camera_switch_update(struct Scene *scene); const char *BKE_scene_find_marker_name(const struct Scene *scene, int frame); const char *BKE_scene_find_last_marker_name(const struct Scene *scene, int frame); -int BKE_scene_frame_snap_by_seconds(struct Scene *scene, double interval_in_seconds, int cfra); +int BKE_scene_frame_snap_by_seconds(struct Scene *scene, double interval_in_seconds, int frame); /* checks for cycle, returns 1 if it's all OK */ bool BKE_scene_validate_setscene(struct Main *bmain, struct Scene *sce); +float BKE_scene_ctime_get(const struct Scene *scene); +float BKE_scene_frame_to_ctime(const struct Scene *scene, const int frame); + float BKE_scene_frame_get(const struct Scene *scene); -float BKE_scene_frame_to_ctime(const struct Scene *scene, const float frame); -void BKE_scene_frame_set(struct Scene *scene, double cfra); +void BKE_scene_frame_set(struct Scene *scene, float frame); struct TransformOrientationSlot *BKE_scene_orientation_slot_get_from_flag(struct Scene *scene, int flag); diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index fed155626ed..0b08bbfeff5 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -332,6 +332,9 @@ typedef void (*uiListFilterItemsFunc)(struct uiList *ui_list, struct PointerRNA *, const char *propname); +/* Listen to notifiers. Only for lists defined in C. */ +typedef void (*uiListListener)(struct uiList *ui_list, wmRegionListenerParams *params); + typedef struct uiListType { struct uiListType *next, *prev; @@ -341,6 +344,9 @@ typedef struct uiListType { uiListDrawFilterFunc draw_filter; uiListFilterItemsFunc filter_items; + /* For lists defined in C only. */ + uiListListener listener; + /* RNA integration */ ExtensionRNA rna_ext; } uiListType; diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 1aac2e311e3..f85e62768f7 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -337,6 +337,18 @@ class BezierSpline final : public Spline { blender::MutableSpan<blender::float3> positions) const; bool segment_is_vector(const int start_index) const; + /** See comment and diagram for #calculate_segment_insertion. */ + struct InsertResult { + blender::float3 handle_prev; + blender::float3 left_handle; + blender::float3 position; + blender::float3 right_handle; + blender::float3 handle_next; + }; + InsertResult calculate_segment_insertion(const int index, + const int next_index, + const float parameter); + private: void correct_end_tangents() const final; void copy_settings(Spline &dst) const final; diff --git a/source/blender/blenkernel/BKE_studiolight.h b/source/blender/blenkernel/BKE_studiolight.h index 70b8743bcd2..59b1c2b28d9 100644 --- a/source/blender/blenkernel/BKE_studiolight.h +++ b/source/blender/blenkernel/BKE_studiolight.h @@ -145,7 +145,7 @@ typedef struct StudioLight { void BKE_studiolight_init(void); void BKE_studiolight_free(void); -void BKE_studiolight_default(SolidLight lights[4], float light_ambient[4]); +void BKE_studiolight_default(SolidLight lights[4], float light_ambient[3]); struct StudioLight *BKE_studiolight_find(const char *name, int flag); struct StudioLight *BKE_studiolight_findindex(int index, int flag); struct StudioLight *BKE_studiolight_find_default(int flag); diff --git a/source/blender/blenkernel/BKE_undo_system.h b/source/blender/blenkernel/BKE_undo_system.h index efac5d9097f..2973a432723 100644 --- a/source/blender/blenkernel/BKE_undo_system.h +++ b/source/blender/blenkernel/BKE_undo_system.h @@ -162,6 +162,13 @@ typedef enum UndoTypeFlags { * \note Callback is still supposed to properly deal with a NULL context pointer. */ UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE = 1 << 0, + + /** + * When the active undo step is of this type, it must be read before loading other undo steps. + * + * This is typically used for undo systems that store both before/after states. + */ + UNDOTYPE_FLAG_DECODE_ACTIVE_STEP = 1 << 1, } UndoTypeFlags; /* Expose since we need to perform operations on specific undo types (rarely). */ diff --git a/source/blender/blenkernel/BKE_volume.h b/source/blender/blenkernel/BKE_volume.h index cf755827a6c..d9333996632 100644 --- a/source/blender/blenkernel/BKE_volume.h +++ b/source/blender/blenkernel/BKE_volume.h @@ -214,7 +214,7 @@ auto BKE_volume_grid_type_operation(const VolumeGridType grid_type, OpType &&op) } /* Should never be called. */ - BLI_assert(!"should never be reached"); + BLI_assert_msg(0, "should never be reached"); return op.template operator()<openvdb::FloatGrid>(); } diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 7da9bd11b12..1b6de62fecb 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -143,7 +143,7 @@ set(SRC intern/geometry_set_instances.cc intern/gpencil.c intern/gpencil_curve.c - intern/gpencil_geom.c + intern/gpencil_geom.cc intern/gpencil_modifier.c intern/hair.c intern/icons.cc diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 2d183fdc39e..5cfd6956272 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -900,7 +900,7 @@ static Mesh *prepare_geometry_set_for_mesh_modifier(Mesh *mesh, GeometrySet &r_g { /* Add the mesh to the geometry set. */ MeshComponent &mesh_component = r_geometry_set.get_component_for_write<MeshComponent>(); - mesh_component.replace_mesh_but_keep_vertex_group_names(mesh, GeometryOwnershipType::Editable); + mesh_component.replace(mesh, GeometryOwnershipType::Editable); } { /* Combine mesh and all instances into a single mesh that can be passed to the modifier. */ @@ -948,8 +948,7 @@ static Mesh *modifier_modify_mesh_and_geometry_set(ModifierData *md, /* Replace only the mesh rather than the whole component, because the entire #MeshComponent * might have been replaced by data from a different object in the node tree, which means the * component contains vertex group name data for that object that should not be removed. */ - mesh_component.replace_mesh_but_keep_vertex_group_names(input_mesh, - GeometryOwnershipType::Editable); + mesh_component.replace(input_mesh, GeometryOwnershipType::Editable); /* Let the modifier change the geometry set. */ mti->modifyGeometrySet(md, &mectx, &geometry_set); @@ -993,12 +992,6 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* This geometry set contains the non-mesh data that might be generated by modifiers. */ GeometrySet geometry_set_final; - /* Add the initial mesh component, with a copy of the vertex group names from the object, - * since they need to be stored in the geometry set for evaluation. */ - MeshComponent &initial_mesh_component = - geometry_set_final.get_component_for_write<MeshComponent>(); - initial_mesh_component.copy_vertex_group_names_from_object(*ob); - BLI_assert((mesh_input->id.tag & LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT) == 0); /* Deformed vertex locations array. Deform only modifier need this type of @@ -1602,12 +1595,6 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, /* This geometry set contains the non-mesh data that might be generated by modifiers. */ GeometrySet geometry_set_final; - /* Add the initial mesh component, with a copy of the vertex group names from the object, - * since they need to be stored in the geometry set for evaluation. */ - MeshComponent &initial_mesh_component = - geometry_set_final.get_component_for_write<MeshComponent>(); - initial_mesh_component.copy_vertex_group_names_from_object(*ob); - /* Deformed vertex locations array. Deform only modifier need this type of * float array rather than MVert*. Tracked along with mesh_final as an * optimization to avoid copying coordinates back and forth if there are @@ -1951,8 +1938,7 @@ static void mesh_build_data(struct Depsgraph *depsgraph, /* Add the final mesh as read-only non-owning component to the geometry set. */ MeshComponent &mesh_component = geometry_set_eval->get_component_for_write<MeshComponent>(); - mesh_component.replace_mesh_but_keep_vertex_group_names(mesh_eval, - GeometryOwnershipType::ReadOnly); + mesh_component.replace(mesh_eval, GeometryOwnershipType::ReadOnly); ob->runtime.geometry_set_eval = geometry_set_eval; ob->runtime.mesh_deform_eval = mesh_deform_eval; @@ -1990,7 +1976,7 @@ static void editbmesh_build_data(struct Depsgraph *depsgraph, BKE_sculpt_update_object_before_eval(obedit); } - BKE_editmesh_free_derivedmesh(em); + BKE_editmesh_free_derived_caches(em); Mesh *me_cage; Mesh *me_final; diff --git a/source/blender/blenkernel/intern/action_mirror.c b/source/blender/blenkernel/intern/action_mirror.c index 69e0091444b..48472dfc9b3 100644 --- a/source/blender/blenkernel/intern/action_mirror.c +++ b/source/blender/blenkernel/intern/action_mirror.c @@ -322,6 +322,25 @@ static void action_flip_pchan(Object *ob_arm, /* Move back to bone-space space, using the flipped bone if it exists. */ mul_m4_m4m4(chan_mat, arm_mat_inv, chan_mat); + /* The rest pose having an X-axis that is not mapping to a left/right direction (so aligned + * with the Y or Z axis) creates issues when flipping the pose. Instead of a negative scale on + * the X-axis, it turns into a 180 degree rotation over the Y-axis. + * This has only been observed with bones that can't be flipped, + * hence the check for `pchan_flip`. */ + const float unit_x[4] = {1.0f, 0.0f, 0.0f, 0.0f}; + const bool is_problematic = pchan_flip == NULL && + fabsf(dot_v4v4(pchan->bone->arm_mat[0], unit_x)) <= 1e-6; + if (is_problematic) { + /* Matrix needs to flip both the X and Z axes to come out right. */ + float extra_mat[4][4] = { + {-1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, -1.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f}, + }; + mul_m4_m4m4(chan_mat, extra_mat, chan_mat); + } + BKE_pchan_apply_mat4(&pchan_temp, chan_mat, false); /* Write the values back to the F-curves. */ diff --git a/source/blender/blenkernel/intern/action_test.cc b/source/blender/blenkernel/intern/action_test.cc index cd8751ec358..c02eca966ad 100644 --- a/source/blender/blenkernel/intern/action_test.cc +++ b/source/blender/blenkernel/intern/action_test.cc @@ -30,16 +30,16 @@ namespace blender::bke::tests { TEST(action_groups, ReconstructGroupsWithReordering) { - // Construct an Action with three groups. - bAction action = {0}; - FCurve groupAcurve1 = {0}; - FCurve groupAcurve2 = {0}; - FCurve groupBcurve1 = {0}; - FCurve groupBcurve2 = {0}; - FCurve groupBcurve3 = {0}; - // Group C has no curves intentionally. - FCurve groupDcurve1 = {0}; - FCurve groupDcurve2 = {0}; + /* Construct an Action with three groups. */ + bAction action = {{nullptr}}; + FCurve groupAcurve1 = {nullptr}; + FCurve groupAcurve2 = {nullptr}; + FCurve groupBcurve1 = {nullptr}; + FCurve groupBcurve2 = {nullptr}; + FCurve groupBcurve3 = {nullptr}; + /* Group C has no curves intentionally. */ + FCurve groupDcurve1 = {nullptr}; + FCurve groupDcurve2 = {nullptr}; groupAcurve1.rna_path = (char *)"groupAcurve1"; groupAcurve2.rna_path = (char *)"groupAcurve2"; @@ -54,18 +54,18 @@ TEST(action_groups, ReconstructGroupsWithReordering) BLI_addtail(&action.curves, &groupBcurve1); BLI_addtail(&action.curves, &groupBcurve2); BLI_addtail(&action.curves, &groupDcurve1); - BLI_addtail(&action.curves, &groupBcurve3); // <-- The error that should be corrected. + BLI_addtail(&action.curves, &groupBcurve3); /* <-- The error that should be corrected. */ BLI_addtail(&action.curves, &groupDcurve2); - // Introduce another error type, by changing some `prev` pointers. - groupBcurve1.prev = NULL; + /* Introduce another error type, by changing some `prev` pointers. */ + groupBcurve1.prev = nullptr; groupBcurve3.prev = &groupBcurve2; groupDcurve1.prev = &groupBcurve3; - bActionGroup groupA = {0}; - bActionGroup groupB = {0}; - bActionGroup groupC = {0}; - bActionGroup groupD = {0}; + bActionGroup groupA = {nullptr}; + bActionGroup groupB = {nullptr}; + bActionGroup groupC = {nullptr}; + bActionGroup groupD = {nullptr}; strcpy(groupA.name, "groupA"); strcpy(groupB.name, "groupB"); strcpy(groupC.name, "groupC"); @@ -87,7 +87,7 @@ TEST(action_groups, ReconstructGroupsWithReordering) groupA.channels.first = &groupAcurve1; groupA.channels.last = &groupAcurve2; groupB.channels.first = &groupBcurve1; - groupB.channels.last = &groupBcurve3; // The last channel in group B, after group C curve 1. + groupB.channels.last = &groupBcurve3; /* The last channel in group B, after group C curve 1. */ groupD.channels.first = &groupDcurve1; groupD.channels.last = &groupDcurve2; diff --git a/source/blender/blenkernel/intern/anim_path.c b/source/blender/blenkernel/intern/anim_path.c index e2c2708101b..de470a15041 100644 --- a/source/blender/blenkernel/intern/anim_path.c +++ b/source/blender/blenkernel/intern/anim_path.c @@ -216,7 +216,7 @@ static bool binary_search_anim_path(const float *accum_len_arr, if (UNLIKELY(cur_step == 0)) { /* This should never happen unless there is something horribly wrong. */ CLOG_ERROR(&LOG, "Couldn't find any valid point on the animation path!"); - BLI_assert(!"Couldn't find any valid point on the animation path!"); + BLI_assert_msg(0, "Couldn't find any valid point on the animation path!"); return false; } diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 553453cd891..2879a995ad6 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -621,6 +621,115 @@ static void animsys_evaluate_fcurves(PointerRNA *ptr, } } +/* This function assumes that the quaternion is fully keyed, and is stored in array index order. */ +static void animsys_quaternion_evaluate_fcurves(PathResolvedRNA quat_rna, + FCurve *first_fcurve, + const AnimationEvalContext *anim_eval_context, + float r_quaternion[4]) +{ + FCurve *quat_curve_fcu = first_fcurve; + for (int prop_index = 0; prop_index < 4; ++prop_index, quat_curve_fcu = quat_curve_fcu->next) { + /* Big fat assumption that the quaternion is fully keyed, and stored in order. */ + BLI_assert(STREQ(quat_curve_fcu->rna_path, first_fcurve->rna_path) && + quat_curve_fcu->array_index == prop_index); + + quat_rna.prop_index = prop_index; + r_quaternion[prop_index] = calculate_fcurve(&quat_rna, quat_curve_fcu, anim_eval_context); + } +} + +/* This function assumes that the quaternion is fully keyed, and is stored in array index order. */ +static void animsys_blend_fcurves_quaternion(PathResolvedRNA *anim_rna, + FCurve *first_fcurve, + const AnimationEvalContext *anim_eval_context, + const float blend_factor) +{ + float current_quat[4]; + RNA_property_float_get_array(&anim_rna->ptr, anim_rna->prop, current_quat); + + float target_quat[4]; + animsys_quaternion_evaluate_fcurves(*anim_rna, first_fcurve, anim_eval_context, target_quat); + + float blended_quat[4]; + interp_qt_qtqt(blended_quat, current_quat, target_quat, blend_factor); + + RNA_property_float_set_array(&anim_rna->ptr, anim_rna->prop, blended_quat); +} + +/* LERP between current value (blend_factor=0.0) and the value from the FCurve (blend_factor=1.0) + */ +static void animsys_blend_in_fcurves(PointerRNA *ptr, + ListBase *fcurves, + const AnimationEvalContext *anim_eval_context, + const float blend_factor) +{ + char *channel_to_skip = NULL; + int num_channels_to_skip = 0; + LISTBASE_FOREACH (FCurve *, fcu, fcurves) { + + if (num_channels_to_skip) { + /* For skipping already-handled rotation channels. Rotation channels are handled per group, + * and not per individual channel. */ + BLI_assert(channel_to_skip != NULL); + if (STREQ(channel_to_skip, fcu->rna_path)) { + /* This is indeed the channel we want to skip. */ + num_channels_to_skip--; + continue; + } + } + + if (!is_fcurve_evaluatable(fcu)) { + continue; + } + + PathResolvedRNA anim_rna; + if (!BKE_animsys_rna_path_resolve(ptr, fcu->rna_path, fcu->array_index, &anim_rna)) { + continue; + } + + if (STREQ(RNA_property_identifier(anim_rna.prop), "rotation_quaternion")) { + animsys_blend_fcurves_quaternion(&anim_rna, fcu, anim_eval_context, blend_factor); + + /* Skip the next three channels, because those have already been handled here. */ + MEM_SAFE_FREE(channel_to_skip); + channel_to_skip = BLI_strdup(fcu->rna_path); + num_channels_to_skip = 3; + continue; + } + /* TODO(Sybren): do something similar as above for Euler and Axis/Angle representations. */ + + const float fcurve_value = calculate_fcurve(&anim_rna, fcu, anim_eval_context); + + float current_value; + float value_to_write; + if (BKE_animsys_read_from_rna_path(&anim_rna, ¤t_value)) { + value_to_write = (1 - blend_factor) * current_value + blend_factor * fcurve_value; + + switch (RNA_property_type(anim_rna.prop)) { + case PROP_BOOLEAN: + /* Without this, anything less than 1.0 is converted to 'False' by + * ANIMSYS_FLOAT_AS_BOOL(). This is probably not desirable for blends, where anything + * above a 50% blend should act more like the FCurve than like the current value. */ + case PROP_INT: + case PROP_ENUM: + value_to_write = roundf(value_to_write); + break; + default: + /* All other types are just handled as float, and value_to_write is already correct. */ + break; + } + } + else { + /* Unable to read the current value for blending, so just apply the FCurve value instead. */ + value_to_write = fcurve_value; + } + + BKE_animsys_write_to_rna_path(&anim_rna, value_to_write); + } + + MEM_SAFE_FREE(channel_to_skip); +} + /* ***************************************** */ /* Driver Evaluation */ @@ -769,6 +878,16 @@ void animsys_evaluate_action(PointerRNA *ptr, animsys_evaluate_fcurves(ptr, &act->curves, anim_eval_context, flush_to_original); } +/* Evaluate Action and blend it into the current values of the animated properties. */ +void animsys_blend_in_action(PointerRNA *ptr, + bAction *act, + const AnimationEvalContext *anim_eval_context, + const float blend_factor) +{ + action_idcode_patch_check(ptr->owner_id, act); + animsys_blend_in_fcurves(ptr, &act->curves, anim_eval_context, blend_factor); +} + /* ***************************************** */ /* NLA System - Evaluation */ @@ -1457,7 +1576,7 @@ static float nla_blend_value(const int blendmode, return influence * (lower_value * strip_value) + (1 - influence) * lower_value; case NLASTRIP_MODE_COMBINE: - BLI_assert(!"combine mode"); + BLI_assert_msg(0, "combine mode"); ATTR_FALLTHROUGH; default: @@ -1495,7 +1614,7 @@ static float nla_combine_value(const int mix_mode, return lower_value * powf(strip_value / base_value, influence); default: - BLI_assert(!"invalid mix mode"); + BLI_assert_msg(0, "invalid mix mode"); return lower_value; } } @@ -1546,7 +1665,7 @@ static bool nla_blend_get_inverted_strip_value(const int blendmode, return true; case NLASTRIP_MODE_COMBINE: - BLI_assert(!"combine mode"); + BLI_assert_msg(0, "combine mode"); ATTR_FALLTHROUGH; default: @@ -1602,7 +1721,7 @@ static bool nla_combine_get_inverted_strip_value(const int mix_mode, return true; default: - BLI_assert(!"invalid mix mode"); + BLI_assert_msg(0, "invalid mix mode"); return false; } } @@ -3033,7 +3152,7 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context, NlaEvalChannel *nec = nlaevalchan_verify_key(eval_data, NULL, &key); BLI_assert(nec); if (nec->base_snapshot.length != count) { - BLI_assert(!"invalid value count"); + BLI_assert_msg(0, "invalid value count"); nlaeval_snapshot_free_data(&blended_snapshot); return false; } diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index ebff6015df1..eae331fc7d1 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -779,7 +779,7 @@ const char *BKE_appdir_folder_id_version(const int folder_id, default: path[0] = '\0'; /* in case check_is_dir is false */ ok = false; - BLI_assert(!"incorrect ID"); + BLI_assert_msg(0, "incorrect ID"); break; } return ok ? path : NULL; diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 22d6cfba893..b8ed519e8d1 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2843,7 +2843,7 @@ void BKE_pose_where_is(struct Depsgraph *depsgraph, Scene *scene, Object *ob) * hopefully this is OK. */ BKE_pose_ensure(NULL, ob, arm, true); - ctime = BKE_scene_frame_get(scene); /* not accurate... */ + ctime = BKE_scene_ctime_get(scene); /* not accurate... */ /* In edit-mode or rest-position we read the data from the bones. */ if (arm->edbo || (arm->flag & ARM_RESTPOS)) { diff --git a/source/blender/blenkernel/intern/armature_deform.c b/source/blender/blenkernel/intern/armature_deform.c index bca5503c8d2..5f721b49361 100644 --- a/source/blender/blenkernel/intern/armature_deform.c +++ b/source/blender/blenkernel/intern/armature_deform.c @@ -47,6 +47,7 @@ #include "BKE_action.h" #include "BKE_armature.h" +#include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_lattice.h" @@ -485,7 +486,7 @@ static void armature_deform_coords_impl(const Object *ob_arm, int defbase_len = 0; /* safety for vertexgroup index overflow */ int i, dverts_len = 0; /* safety for vertexgroup overflow */ bool use_dverts = false; - int armature_def_nr; + int armature_def_nr = -1; int cd_dvert_offset = -1; /* in editmode, or not an armature */ @@ -500,11 +501,11 @@ static void armature_deform_coords_impl(const Object *ob_arm, BLI_assert(0); } - /* get the def_nr for the overall armature vertex group if present */ - armature_def_nr = BKE_object_defgroup_name_index(ob_target, defgrp_name); + if (BKE_object_supports_vertex_groups(ob_target)) { + /* get the def_nr for the overall armature vertex group if present */ + armature_def_nr = BKE_object_defgroup_name_index(ob_target, defgrp_name); - if (ELEM(ob_target->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) { - defbase_len = BLI_listbase_count(&ob_target->defbase); + defbase_len = BKE_object_defgroup_count(ob_target); if (ob_target->type == OB_MESH) { if (em_target == NULL) { @@ -528,11 +529,9 @@ static void armature_deform_coords_impl(const Object *ob_arm, dverts_len = gps_target->totpoints; } } - } - /* get a vertex-deform-index to posechannel array */ - if (deformflag & ARM_DEF_VGROUP) { - if (ELEM(ob_target->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) { + /* get a vertex-deform-index to posechannel array */ + if (deformflag & ARM_DEF_VGROUP) { /* if we have a Mesh, only use dverts if it has them */ if (em_target) { cd_dvert_offset = CustomData_get_offset(&em_target->bm->vdata, CD_MDEFORMVERT); @@ -551,7 +550,8 @@ static void armature_deform_coords_impl(const Object *ob_arm, * * - Check whether keeping this consistent across frames gives speedup. */ - for (i = 0, dg = ob_target->defbase.first; dg; i++, dg = dg->next) { + const ListBase *defbase = BKE_object_defgroup_list(ob_target); + for (i = 0, dg = defbase->first; dg; i++, dg = dg->next) { pchan_from_defbase[i] = BKE_pose_channel_find_name(ob_arm->pose, dg->name); /* exclude non-deforming bones */ if (pchan_from_defbase[i]) { diff --git a/source/blender/blenkernel/intern/armature_pose.cc b/source/blender/blenkernel/intern/armature_pose.cc index ca11692372b..09e1c7d6615 100644 --- a/source/blender/blenkernel/intern/armature_pose.cc +++ b/source/blender/blenkernel/intern/armature_pose.cc @@ -26,6 +26,7 @@ #include "BKE_animsys.h" #include "BKE_armature.h" +#include "BLI_function_ref.hh" #include "BLI_set.hh" #include "DNA_action_types.h" @@ -38,16 +39,62 @@ namespace { using BoneNameSet = blender::Set<std::string>; +using ActionApplier = + blender::FunctionRef<void(PointerRNA *, bAction *, const AnimationEvalContext *)>; + // Forward declarations. BoneNameSet pose_apply_find_selected_bones(const bArmature *armature, const bPose *pose); void pose_apply_disable_fcurves_for_unselected_bones(bAction *action, const BoneNameSet &selected_bone_names); void pose_apply_restore_fcurves(bAction *action); + +void pose_apply(struct Object *ob, + struct bAction *action, + struct AnimationEvalContext *anim_eval_context, + ActionApplier applier); + } // namespace -void BKE_pose_apply_action(struct Object *ob, - struct bAction *action, - struct AnimationEvalContext *anim_eval_context) +void BKE_pose_apply_action_selected_bones(struct Object *ob, + struct bAction *action, + struct AnimationEvalContext *anim_eval_context) +{ + auto evaluate_and_apply = + [](PointerRNA *ptr, bAction *act, const AnimationEvalContext *anim_eval_context) { + animsys_evaluate_action(ptr, act, anim_eval_context, false); + }; + + pose_apply(ob, action, anim_eval_context, evaluate_and_apply); +} + +void BKE_pose_apply_action_all_bones(struct Object *ob, + struct bAction *action, + struct AnimationEvalContext *anim_eval_context) +{ + PointerRNA pose_owner_ptr; + RNA_id_pointer_create(&ob->id, &pose_owner_ptr); + animsys_evaluate_action(&pose_owner_ptr, action, anim_eval_context, false); +} + +void BKE_pose_apply_action_blend(struct Object *ob, + struct bAction *action, + struct AnimationEvalContext *anim_eval_context, + const float blend_factor) +{ + auto evaluate_and_blend = [blend_factor](PointerRNA *ptr, + bAction *act, + const AnimationEvalContext *anim_eval_context) { + animsys_blend_in_action(ptr, act, anim_eval_context, blend_factor); + }; + + pose_apply(ob, action, anim_eval_context, evaluate_and_blend); +} + +namespace { +void pose_apply(struct Object *ob, + struct bAction *action, + struct AnimationEvalContext *anim_eval_context, + ActionApplier applier) { bPose *pose = ob->pose; if (pose == nullptr) { @@ -67,14 +114,14 @@ void BKE_pose_apply_action(struct Object *ob, /* Apply the Action. */ PointerRNA pose_owner_ptr; RNA_id_pointer_create(&ob->id, &pose_owner_ptr); - animsys_evaluate_action(&pose_owner_ptr, action, anim_eval_context, false); + + applier(&pose_owner_ptr, action, anim_eval_context); if (limit_to_selected_bones) { pose_apply_restore_fcurves(action); } } -namespace { BoneNameSet pose_apply_find_selected_bones(const bArmature *armature, const bPose *pose) { BoneNameSet selected_bone_names; diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c index 0a195f2ba55..35ae2d2dbef 100644 --- a/source/blender/blenkernel/intern/armature_update.c +++ b/source/blender/blenkernel/intern/armature_update.c @@ -828,7 +828,7 @@ void BKE_pose_eval_init_ik(struct Depsgraph *depsgraph, Scene *scene, Object *ob { DEG_debug_print_eval(depsgraph, __func__, object->id.name, object); BLI_assert(object->type == OB_ARMATURE); - const float ctime = BKE_scene_frame_get(scene); /* not accurate... */ + const float ctime = BKE_scene_ctime_get(scene); /* not accurate... */ bArmature *armature = (bArmature *)object->data; if (armature->flag & ARM_RESTPOS) { return; @@ -869,7 +869,7 @@ void BKE_pose_eval_bone(struct Depsgraph *depsgraph, Scene *scene, Object *objec else { if ((pchan->flag & POSE_DONE) == 0) { /* TODO(sergey): Use time source node for time. */ - float ctime = BKE_scene_frame_get(scene); /* not accurate... */ + float ctime = BKE_scene_ctime_get(scene); /* not accurate... */ BKE_pose_where_is_bone(depsgraph, scene, object, pchan, ctime, 1); } } @@ -897,7 +897,7 @@ void BKE_pose_constraints_evaluate(struct Depsgraph *depsgraph, } else { if ((pchan->flag & POSE_DONE) == 0) { - float ctime = BKE_scene_frame_get(scene); /* not accurate... */ + float ctime = BKE_scene_ctime_get(scene); /* not accurate... */ BKE_pose_where_is_bone(depsgraph, scene, object, pchan, ctime, 1); } } @@ -981,7 +981,7 @@ void BKE_pose_iktree_evaluate(struct Depsgraph *depsgraph, DEG_debug_print_eval_subdata( depsgraph, __func__, object->id.name, object, "rootchan", rootchan->name, rootchan); BLI_assert(object->type == OB_ARMATURE); - const float ctime = BKE_scene_frame_get(scene); /* not accurate... */ + const float ctime = BKE_scene_ctime_get(scene); /* not accurate... */ if (armature->flag & ARM_RESTPOS) { return; } @@ -1002,7 +1002,7 @@ void BKE_pose_splineik_evaluate(struct Depsgraph *depsgraph, DEG_debug_print_eval_subdata( depsgraph, __func__, object->id.name, object, "rootchan", rootchan->name, rootchan); BLI_assert(object->type == OB_ARMATURE); - const float ctime = BKE_scene_frame_get(scene); /* not accurate... */ + const float ctime = BKE_scene_ctime_get(scene); /* not accurate... */ if (armature->flag & ARM_RESTPOS) { return; } @@ -1031,7 +1031,7 @@ void BKE_pose_eval_cleanup(struct Depsgraph *depsgraph, Scene *scene, Object *ob bPose *pose = object->pose; BLI_assert(pose != NULL); UNUSED_VARS_NDEBUG(pose); - const float ctime = BKE_scene_frame_get(scene); /* not accurate... */ + const float ctime = BKE_scene_ctime_get(scene); /* not accurate... */ DEG_debug_print_eval(depsgraph, __func__, object->id.name, object); BLI_assert(object->type == OB_ARMATURE); /* Release the IK tree. */ diff --git a/source/blender/blenkernel/intern/asset.cc b/source/blender/blenkernel/intern/asset.cc index b5a7f5e37a6..f74018b20c5 100644 --- a/source/blender/blenkernel/intern/asset.cc +++ b/source/blender/blenkernel/intern/asset.cc @@ -110,6 +110,11 @@ void BKE_asset_metadata_tag_remove(AssetMetaData *asset_data, AssetTag *tag) BLI_assert(BLI_listbase_count(&asset_data->tags) == asset_data->tot_tags); } +void BKE_asset_library_reference_init_default(AssetLibraryReference *library_ref) +{ + memcpy(library_ref, DNA_struct_default_get(AssetLibraryReference), sizeof(*library_ref)); +} + /* Queries -------------------------------------------- */ PreviewImage *BKE_asset_metadata_preview_get_from_id(const AssetMetaData *UNUSED(asset_data), diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c index 5db45471f0a..ad2be4ffe30 100644 --- a/source/blender/blenkernel/intern/attribute.c +++ b/source/blender/blenkernel/intern/attribute.c @@ -119,7 +119,7 @@ bool BKE_id_attribute_rename(ID *id, ReportList *reports) { if (BKE_id_attribute_required(id, layer)) { - BLI_assert(!"Required attribute name is not editable"); + BLI_assert_msg(0, "Required attribute name is not editable"); return false; } @@ -202,7 +202,7 @@ AttributeDomain BKE_id_attribute_domain(ID *id, CustomDataLayer *layer) } } - BLI_assert(!"Custom data layer not found in geometry"); + BLI_assert_msg(0, "Custom data layer not found in geometry"); return ATTR_DOMAIN_NUM; } @@ -218,7 +218,7 @@ int BKE_id_attribute_data_length(ID *id, CustomDataLayer *layer) } } - BLI_assert(!"Custom data layer not found in geometry"); + BLI_assert_msg(0, "Custom data layer not found in geometry"); return 0; } diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index e84b485c466..97a54f289ee 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -127,7 +127,7 @@ static void blender_version_init(void) version_cycle = ""; } else { - BLI_assert(!"Invalid Blender version cycle"); + BLI_assert_msg(0, "Invalid Blender version cycle"); } BLI_snprintf(blender_version_string, diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 912cc66920e..19721b4313d 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -622,6 +622,7 @@ UserDef *BKE_blendfile_userdef_from_defaults(void) "io_scene_obj", "io_scene_x3d", "cycles", + "pose_library", }; for (int i = 0; i < ARRAY_SIZE(addons); i++) { bAddon *addon = BKE_addon_new(); diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index 30e9ae39b67..eaba5d33a20 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -314,7 +314,7 @@ bool BKE_cachefile_filepath_get(const Main *bmain, if (cache_file->is_sequence && BLI_path_frame_get(r_filepath, &fframe, &frame_len)) { Scene *scene = DEG_get_evaluated_scene(depsgraph); - const float ctime = BKE_scene_frame_get(scene); + const float ctime = BKE_scene_ctime_get(scene); const float fps = (((double)scene->r.frs_sec) / (double)scene->r.frs_sec_base); const float frame = BKE_cachefile_time_offset(cache_file, ctime, fps); diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index 8678a659c0a..0fa58a74f2b 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -262,17 +262,19 @@ static bool do_init_cloth(Object *ob, ClothModifierData *clmd, Mesh *result, int static int do_step_cloth( Depsgraph *depsgraph, Object *ob, ClothModifierData *clmd, Mesh *result, int framenr) { + /* simulate 1 frame forward */ ClothVertex *verts = NULL; Cloth *cloth; ListBase *effectors = NULL; MVert *mvert; unsigned int i = 0; int ret = 0; + bool vert_mass_changed = false; - /* simulate 1 frame forward */ cloth = clmd->clothObject; verts = cloth->verts; mvert = result->mvert; + vert_mass_changed = verts->mass != clmd->sim_parms->mass; /* force any pinned verts to their constrained location. */ for (i = 0; i < clmd->clothObject->mvert_num; i++, verts++) { @@ -283,6 +285,11 @@ static int do_step_cloth( /* Get the current position. */ copy_v3_v3(verts->xconst, mvert[i].co); mul_m4_v3(ob->obmat, verts->xconst); + + if (vert_mass_changed) { + verts->mass = clmd->sim_parms->mass; + SIM_mass_spring_set_implicit_vertex_mass(cloth->implicit, i, verts->mass); + } } effectors = BKE_effectors_create(depsgraph, ob, NULL, clmd->sim_parms->effector_weights, false); diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 7fa1fb6d492..30198297347 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -191,7 +191,7 @@ static ID *collection_owner_get(Main *bmain, ID *id) } } - BLI_assert(!"Embedded collection with no owner. Critical Main inconsistency."); + BLI_assert_msg(0, "Embedded collection with no owner. Critical Main inconsistency."); return NULL; } @@ -522,7 +522,7 @@ bool BKE_collection_delete(Main *bmain, Collection *collection, bool hierarchy) { /* Master collection is not real datablock, can't be removed. */ if (collection->flag & COLLECTION_IS_MASTER) { - BLI_assert(!"Scene master collection can't be deleted"); + BLI_assert_msg(0, "Scene master collection can't be deleted"); return false; } diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index bc993d8d58d..47df31e3a2c 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -2339,7 +2339,7 @@ static void translike_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t break; default: - BLI_assert(!"Unknown Copy Transforms mix mode"); + BLI_assert_msg(0, "Unknown Copy Transforms mix mode"); } } } @@ -2991,7 +2991,7 @@ static void actcon_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targ break; default: - BLI_assert(!"Unknown Action mix mode"); + BLI_assert_msg(0, "Unknown Action mix mode"); } } } diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index 1028790856c..dced945bea0 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -1448,6 +1448,36 @@ int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list) return ctx_data_collection_get(C, "editable_gpencil_strokes", list); } +const AssetLibraryReference *CTX_wm_asset_library(const bContext *C) +{ + return ctx_data_pointer_get(C, "asset_library"); +} + +AssetHandle CTX_wm_asset_handle(const bContext *C, bool *r_is_valid) +{ + AssetHandle *asset_handle_p = + (AssetHandle *)CTX_data_pointer_get_type(C, "asset_handle", &RNA_AssetHandle).data; + if (asset_handle_p) { + *r_is_valid = true; + return *asset_handle_p; + } + + /* If the asset handle was not found in context directly, try if there's an active file with + * asset data there instead. Not nice to have this here, would be better to have this in + * `ED_asset.h`, but we can't include that in BKE. Even better would be not needing this at all + * and being able to have editors return this in the usual `context` callback. But that would + * require returning a non-owning pointer, which we don't have in the Asset Browser (yet). */ + FileDirEntry *file = + (FileDirEntry *)CTX_data_pointer_get_type(C, "active_file", &RNA_FileSelectEntry).data; + if (file && file->asset_data) { + *r_is_valid = true; + return (AssetHandle){.file_data = file}; + } + + *r_is_valid = false; + return (AssetHandle){0}; +} + Depsgraph *CTX_data_depsgraph_pointer(const bContext *C) { Main *bmain = CTX_data_main(C); diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 72ee2587c8a..8e1577ab072 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -335,4 +335,4 @@ void CurveEval::assert_valid_point_attributes() const ATTR_DOMAIN_POINT); } #endif -}
\ No newline at end of file +} diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index a384eec4156..81a63240719 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -3731,7 +3731,7 @@ bool CustomData_bmesh_merge(const CustomData *source, totelem = bm->totface; break; default: /* should never happen */ - BLI_assert(!"invalid type given"); + BLI_assert_msg(0, "invalid type given"); iter_type = BM_VERTS_OF_MESH; totelem = bm->totvert; break; diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index 1b89ce9cdbd..371dd97e8bc 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -1440,7 +1440,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph, if (vgroup_name) { mdef = CustomData_get_layer(&me_dst->vdata, CD_MDEFORMVERT); if (mdef) { - vg_idx = BKE_object_defgroup_name_index(ob_dst, vgroup_name); + vg_idx = BKE_id_defgroup_name_index(&me_dst->id, vgroup_name); } } diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index 19840a70bf0..f7ef84728b6 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -29,6 +29,8 @@ #include "MEM_guardedalloc.h" +#include "DNA_gpencil_types.h" +#include "DNA_lattice_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -64,7 +66,9 @@ bDeformGroup *BKE_object_defgroup_new(Object *ob, const char *name) BLI_strncpy(defgroup->name, name, sizeof(defgroup->name)); - BLI_addtail(&ob->defbase, defgroup); + ListBase *defbase = BKE_object_defgroup_list_mutable(ob); + + BLI_addtail(defbase, defgroup); BKE_object_defgroup_unique_name(defgroup, ob); BKE_object_batch_cache_dirty_tag(ob); @@ -484,18 +488,120 @@ void BKE_defvert_flip_merged(MDeformVert *dvert, const int *flip_map, const int } } +bool BKE_object_supports_vertex_groups(const Object *ob) +{ + const ID *id = (const ID *)ob->data; + if (id == NULL) { + return false; + } + + return ELEM(GS(id->name), ID_ME, ID_LT, ID_GD); +} + +const ListBase *BKE_id_defgroup_list_get(const ID *id) +{ + switch (GS(id->name)) { + case ID_ME: { + const Mesh *me = (const Mesh *)id; + return &me->vertex_group_names; + } + case ID_LT: { + const Lattice *lt = (const Lattice *)id; + return <->vertex_group_names; + } + case ID_GD: { + const bGPdata *gpd = (const bGPdata *)id; + return &gpd->vertex_group_names; + } + default: { + BLI_assert_unreachable(); + } + } + return NULL; +} + +static const int *object_defgroup_active_index_get_p(const Object *ob) +{ + BLI_assert(BKE_object_supports_vertex_groups(ob)); + switch (ob->type) { + case OB_MESH: { + const Mesh *mesh = (const Mesh *)ob->data; + return &mesh->vertex_group_active_index; + } + case OB_LATTICE: { + const Lattice *lattice = (const Lattice *)ob->data; + return &lattice->vertex_group_active_index; + } + case OB_GPENCIL: { + const bGPdata *gpd = (const bGPdata *)ob->data; + return &gpd->vertex_group_active_index; + } + } + return NULL; +} + +ListBase *BKE_id_defgroup_list_get_mutable(ID *id) +{ + /* Cast away const just for the accessor. */ + return (ListBase *)BKE_id_defgroup_list_get(id); +} + bDeformGroup *BKE_object_defgroup_find_name(const Object *ob, const char *name) { - return (name && name[0] != '\0') ? - BLI_findstring(&ob->defbase, name, offsetof(bDeformGroup, name)) : - NULL; + if (name == NULL || name[0] == '\0') { + return NULL; + } + const ListBase *defbase = BKE_object_defgroup_list(ob); + return BLI_findstring(defbase, name, offsetof(bDeformGroup, name)); +} + +int BKE_id_defgroup_name_index(const ID *id, const char *name) +{ + if (name == NULL || name[0] == '\0') { + return -1; + } + const ListBase *defbase = BKE_id_defgroup_list_get(id); + return BLI_findstringindex(defbase, name, offsetof(bDeformGroup, name)); +} + +const ListBase *BKE_object_defgroup_list(const Object *ob) +{ + BLI_assert(BKE_object_supports_vertex_groups(ob)); + return BKE_id_defgroup_list_get((const ID *)ob->data); } int BKE_object_defgroup_name_index(const Object *ob, const char *name) { - return (name && name[0] != '\0') ? - BLI_findstringindex(&ob->defbase, name, offsetof(bDeformGroup, name)) : - -1; + return BKE_id_defgroup_name_index((ID *)ob->data, name); +} + +ListBase *BKE_object_defgroup_list_mutable(Object *ob) +{ + BLI_assert(BKE_object_supports_vertex_groups(ob)); + return BKE_id_defgroup_list_get_mutable((ID *)ob->data); +} + +int BKE_object_defgroup_count(const Object *ob) +{ + return BLI_listbase_count(BKE_object_defgroup_list(ob)); +} + +/** + * \note For historical reasons, the index starts at 1 rather than 0. + */ +int BKE_object_defgroup_active_index_get(const Object *ob) +{ + return *object_defgroup_active_index_get_p(ob); +} + +/** + * \note For historical reasons, the index starts at 1 rather than 0. + */ +void BKE_object_defgroup_active_index_set(Object *ob, const int new_index) +{ + /* Cast away const just for the accessor. */ + int *index = (int *)object_defgroup_active_index_get_p(ob); + *index = new_index; } /** @@ -503,7 +609,8 @@ int BKE_object_defgroup_name_index(const Object *ob, const char *name) */ int *BKE_object_defgroup_flip_map(const Object *ob, int *flip_map_len, const bool use_default) { - int defbase_tot = *flip_map_len = BLI_listbase_count(&ob->defbase); + const ListBase *defbase = BKE_object_defgroup_list(ob); + int defbase_tot = *flip_map_len = BLI_listbase_count(defbase); if (defbase_tot == 0) { return NULL; @@ -517,7 +624,7 @@ int *BKE_object_defgroup_flip_map(const Object *ob, int *flip_map_len, const boo map[i] = -1; } - for (dg = ob->defbase.first, i = 0; dg; dg = dg->next, i++) { + for (dg = defbase->first, i = 0; dg; dg = dg->next, i++) { if (map[i] == -1) { /* may be calculated previously */ /* in case no valid value is found, use this */ @@ -547,7 +654,8 @@ int *BKE_object_defgroup_flip_map_single(const Object *ob, const bool use_default, int defgroup) { - int defbase_tot = *flip_map_len = BLI_listbase_count(&ob->defbase); + const ListBase *defbase = BKE_object_defgroup_list(ob); + int defbase_tot = *flip_map_len = BLI_listbase_count(defbase); if (defbase_tot == 0) { return NULL; @@ -561,7 +669,7 @@ int *BKE_object_defgroup_flip_map_single(const Object *ob, map[i] = use_default ? i : -1; } - dg = BLI_findlink(&ob->defbase, defgroup); + dg = BLI_findlink(defbase, defgroup); BLI_string_flip_side_name(name_flip, dg->name, false, sizeof(name_flip)); if (!STREQ(name_flip, dg->name)) { @@ -578,7 +686,8 @@ int *BKE_object_defgroup_flip_map_single(const Object *ob, int BKE_object_defgroup_flip_index(const Object *ob, int index, const bool use_default) { - bDeformGroup *dg = BLI_findlink(&ob->defbase, index); + const ListBase *defbase = BKE_object_defgroup_list(ob); + bDeformGroup *dg = BLI_findlink(defbase, index); int flip_index = -1; if (dg) { @@ -595,9 +704,10 @@ int BKE_object_defgroup_flip_index(const Object *ob, int index, const bool use_d static bool defgroup_find_name_dupe(const char *name, bDeformGroup *dg, Object *ob) { + const ListBase *defbase = BKE_object_defgroup_list(ob); bDeformGroup *curdef; - for (curdef = ob->defbase.first; curdef; curdef = curdef->next) { + for (curdef = defbase->first; curdef; curdef = curdef->next) { if (dg != curdef) { if (STREQ(curdef->name, name)) { return true; @@ -1189,7 +1299,10 @@ static bool data_transfer_layersmapping_vgroups_multisrc_to_dst(ListBase *r_map, { int idx_src; int idx_dst; - int tot_dst = BLI_listbase_count(&ob_dst->defbase); + const ListBase *src_list = BKE_object_defgroup_list(ob_src); + ListBase *dst_defbase = BKE_object_defgroup_list_mutable(ob_dst); + + int tot_dst = BLI_listbase_count(dst_defbase); const size_t elem_size = sizeof(*((MDeformVert *)NULL)); @@ -1218,7 +1331,7 @@ static bool data_transfer_layersmapping_vgroups_multisrc_to_dst(ListBase *r_map, } else if (use_delete && idx_dst > idx_src) { while (idx_dst-- > idx_src) { - BKE_object_defgroup_remove(ob_dst, ob_dst->defbase.last); + BKE_object_defgroup_remove(ob_dst, dst_defbase->last); } } if (r_map) { @@ -1255,7 +1368,7 @@ static bool data_transfer_layersmapping_vgroups_multisrc_to_dst(ListBase *r_map, if (use_delete) { /* Remove all unused dst vgroups first, simpler in this case. */ - for (dg_dst = ob_dst->defbase.first; dg_dst;) { + for (dg_dst = dst_defbase->first; dg_dst;) { bDeformGroup *dg_dst_next = dg_dst->next; if (BKE_object_defgroup_name_index(ob_src, dg_dst->name) == -1) { @@ -1265,7 +1378,7 @@ static bool data_transfer_layersmapping_vgroups_multisrc_to_dst(ListBase *r_map, } } - for (idx_src = 0, dg_src = ob_src->defbase.first; idx_src < num_layers_src; + for (idx_src = 0, dg_src = src_list->first; idx_src < num_layers_src; idx_src++, dg_src = dg_src->next) { if (!use_layers_src[idx_src]) { continue; @@ -1274,7 +1387,7 @@ static bool data_transfer_layersmapping_vgroups_multisrc_to_dst(ListBase *r_map, if ((idx_dst = BKE_object_defgroup_name_index(ob_dst, dg_src->name)) == -1) { if (use_create) { BKE_object_defgroup_add_name(ob_dst, dg_src->name); - idx_dst = ob_dst->actdef - 1; + idx_dst = BKE_object_defgroup_active_index_get(ob_dst) - 1; } else { /* If we are not allowed to create missing dst vgroups, just skip matching src one. */ @@ -1340,8 +1453,12 @@ bool data_transfer_layersmapping_vgroups(ListBase *r_map, * This implies we may have to handle data layout itself while having NULL data itself, * and even have to support NULL data_src in transfer data code * (we always create a data_dst, though). + * + * Note: Above comment is outdated, but this function was written when that was true. */ - if (BLI_listbase_is_empty(&ob_src->defbase)) { + + const ListBase *src_defbase = BKE_object_defgroup_list(ob_src); + if (BLI_listbase_is_empty(src_defbase)) { if (use_delete) { BKE_object_defgroup_remove_all(ob_dst); } @@ -1361,35 +1478,37 @@ bool data_transfer_layersmapping_vgroups(ListBase *r_map, if (fromlayers >= 0) { idx_src = fromlayers; - if (idx_src >= BLI_listbase_count(&ob_src->defbase)) { + if (idx_src >= BLI_listbase_count(src_defbase)) { /* This can happen when vgroups are removed from source object... * Remapping would be really tricky here, we'd need to go over all objects in * Main every time we delete a vgroup... for now, simpler and safer to abort. */ return false; } } - else if ((idx_src = ob_src->actdef - 1) == -1) { + else if ((idx_src = BKE_object_defgroup_active_index_get(ob_src) - 1) == -1) { return false; } if (tolayers >= 0) { /* NOTE: in this case we assume layer exists! */ idx_dst = tolayers; - BLI_assert(idx_dst < BLI_listbase_count(&ob_dst->defbase)); + const ListBase *dst_defbase = BKE_object_defgroup_list(ob_dst); + BLI_assert(idx_dst < BLI_listbase_count(dst_defbase)); + UNUSED_VARS_NDEBUG(dst_defbase); } else if (tolayers == DT_LAYERS_ACTIVE_DST) { - if ((idx_dst = ob_dst->actdef - 1) == -1) { + if ((idx_dst = BKE_object_defgroup_active_index_get(ob_dst) - 1) == -1) { bDeformGroup *dg_src; if (!use_create) { return true; } - dg_src = BLI_findlink(&ob_src->defbase, idx_src); + dg_src = BLI_findlink(src_defbase, idx_src); BKE_object_defgroup_add_name(ob_dst, dg_src->name); - idx_dst = ob_dst->actdef - 1; + idx_dst = BKE_object_defgroup_active_index_get(ob_dst) - 1; } } else if (tolayers == DT_LAYERS_INDEX_DST) { - int num = BLI_listbase_count(&ob_src->defbase); + int num = BLI_listbase_count(src_defbase); idx_dst = idx_src; if (num <= idx_dst) { if (!use_create) { @@ -1402,13 +1521,13 @@ bool data_transfer_layersmapping_vgroups(ListBase *r_map, } } else if (tolayers == DT_LAYERS_NAME_DST) { - bDeformGroup *dg_src = BLI_findlink(&ob_src->defbase, idx_src); + bDeformGroup *dg_src = BLI_findlink(src_defbase, idx_src); if ((idx_dst = BKE_object_defgroup_name_index(ob_dst, dg_src->name)) == -1) { if (!use_create) { return true; } BKE_object_defgroup_add_name(ob_dst, dg_src->name); - idx_dst = ob_dst->actdef - 1; + idx_dst = BKE_object_defgroup_active_index_get(ob_dst) - 1; } } else { @@ -1531,6 +1650,13 @@ void BKE_defvert_weight_to_rgb(float r_rgb[3], const float weight) /** \name .blend file I/O * \{ */ +void BKE_defbase_blend_write(BlendWriter *writer, const ListBase *defbase) +{ + LISTBASE_FOREACH (bDeformGroup *, defgroup, defbase) { + BLO_write_struct(writer, bDeformGroup, defgroup); + } +} + void BKE_defvert_blend_write(BlendWriter *writer, int count, MDeformVert *dvlist) { if (dvlist == NULL) { diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 6091e774d4a..52996e3bcc7 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -3818,7 +3818,7 @@ static void dynamicPaint_brushMeshCalculateVelocity(Depsgraph *depsgraph, ob, true, SUBFRAME_RECURSION, - BKE_scene_frame_get(scene), + BKE_scene_ctime_get(scene), eModifierType_DynamicPaint); mesh_p = BKE_mesh_copy_for_eval(dynamicPaint_brush_mesh_get(brush), false); numOfVerts_p = mesh_p->totvert; @@ -3834,7 +3834,7 @@ static void dynamicPaint_brushMeshCalculateVelocity(Depsgraph *depsgraph, ob, true, SUBFRAME_RECURSION, - BKE_scene_frame_get(scene), + BKE_scene_ctime_get(scene), eModifierType_DynamicPaint); mesh_c = dynamicPaint_brush_mesh_get(brush); numOfVerts_c = mesh_c->totvert; @@ -3894,7 +3894,7 @@ static void dynamicPaint_brushObjectCalculateVelocity( ob, false, SUBFRAME_RECURSION, - BKE_scene_frame_get(scene), + BKE_scene_ctime_get(scene), eModifierType_DynamicPaint); copy_m4_m4(prev_obmat, ob->obmat); @@ -3906,7 +3906,7 @@ static void dynamicPaint_brushObjectCalculateVelocity( ob, false, SUBFRAME_RECURSION, - BKE_scene_frame_get(scene), + BKE_scene_ctime_get(scene), eModifierType_DynamicPaint); /* calculate speed */ @@ -6271,7 +6271,7 @@ static int dynamicPaint_doStep(Depsgraph *depsgraph, brushObj, true, SUBFRAME_RECURSION, - BKE_scene_frame_get(scene), + BKE_scene_ctime_get(scene), eModifierType_DynamicPaint); } @@ -6312,7 +6312,7 @@ static int dynamicPaint_doStep(Depsgraph *depsgraph, brushObj, true, SUBFRAME_RECURSION, - BKE_scene_frame_get(scene), + BKE_scene_ctime_get(scene), eModifierType_DynamicPaint); } diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index 2eb6b488da2..9cae74e4e9a 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -39,15 +39,14 @@ #include "BKE_mesh_wrapper.h" #include "BKE_object.h" -BMEditMesh *BKE_editmesh_create(BMesh *bm, const bool do_tessellate) +/** + * \note The caller is responsible for ensuring triangulation data, + * typically by calling #BKE_editmesh_looptri_calc. + */ +BMEditMesh *BKE_editmesh_create(BMesh *bm) { BMEditMesh *em = MEM_callocN(sizeof(BMEditMesh), __func__); - em->bm = bm; - if (do_tessellate) { - BKE_editmesh_looptri_calc(em); - } - return em; } @@ -209,7 +208,7 @@ void BKE_editmesh_looptri_and_normals_calc_with_partial(BMEditMesh *em, }); } -void BKE_editmesh_free_derivedmesh(BMEditMesh *em) +void BKE_editmesh_free_derived_caches(BMEditMesh *em) { if (em->mesh_eval_cage) { BKE_id_free(NULL, em->mesh_eval_cage); @@ -223,9 +222,9 @@ void BKE_editmesh_free_derivedmesh(BMEditMesh *em) } /* Does not free the #BMEditMesh struct itself. */ -void BKE_editmesh_free(BMEditMesh *em) +void BKE_editmesh_free_data(BMEditMesh *em) { - BKE_editmesh_free_derivedmesh(em); + BKE_editmesh_free_derived_caches(em); if (em->looptris) { MEM_freeN(em->looptris); diff --git a/source/blender/blenkernel/intern/editmesh_tangent.c b/source/blender/blenkernel/intern/editmesh_tangent.c index e5e639fa3f1..da4ea742656 100644 --- a/source/blender/blenkernel/intern/editmesh_tangent.c +++ b/source/blender/blenkernel/intern/editmesh_tangent.c @@ -25,6 +25,7 @@ #include "DNA_defs.h" #include "DNA_meshdata_types.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_editmesh_tangent.h" #include "BKE_mesh.h" diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 947417af55d..2b48683a3a8 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -1283,10 +1283,10 @@ static void compute_obstaclesemission(Scene *scene, # endif /* Update frame time, this is considering current subframe fraction * BLI_mutex_lock() called in manta_step(), so safe to update subframe here - * TODO(sebbas): Using BKE_scene_frame_get(scene) instead of new DEG_get_ctime(depsgraph) + * TODO(sebbas): Using BKE_scene_ctime_get(scene) instead of new DEG_get_ctime(depsgraph) * as subframes don't work with the latter yet. */ BKE_object_modifier_update_subframe( - depsgraph, scene, effecobj, true, 5, BKE_scene_frame_get(scene), eModifierType_Fluid); + depsgraph, scene, effecobj, true, 5, BKE_scene_ctime_get(scene), eModifierType_Fluid); if (subframes) { obstacles_from_mesh(effecobj, fds, fes, &bb_temp, subframe_dt); @@ -1616,7 +1616,7 @@ static void emit_from_particles(Object *flow_ob, } /* `DEG_get_ctime(depsgraph)` does not give sub-frame time. */ - state.time = BKE_scene_frame_get(scene); + state.time = BKE_scene_ctime_get(scene); if (psys_get_particle_state(&sim, p, &state, 0) == 0) { continue; @@ -2820,10 +2820,10 @@ static void compute_flowsemission(Scene *scene, # endif /* Update frame time, this is considering current subframe fraction * BLI_mutex_lock() called in manta_step(), so safe to update subframe here - * TODO(sebbas): Using BKE_scene_frame_get(scene) instead of new DEG_get_ctime(depsgraph) + * TODO(sebbas): Using BKE_scene_ctime_get(scene) instead of new DEG_get_ctime(depsgraph) * as subframes don't work with the latter yet. */ BKE_object_modifier_update_subframe( - depsgraph, scene, flowobj, true, 5, BKE_scene_frame_get(scene), eModifierType_Fluid); + depsgraph, scene, flowobj, true, 5, BKE_scene_ctime_get(scene), eModifierType_Fluid); /* Emission from particles. */ if (ffs->source == FLUID_FLOW_SOURCE_PARTICLES) { diff --git a/source/blender/blenkernel/intern/fmodifier.c b/source/blender/blenkernel/intern/fmodifier.c index dbd48005e9e..641c003d456 100644 --- a/source/blender/blenkernel/intern/fmodifier.c +++ b/source/blender/blenkernel/intern/fmodifier.c @@ -688,7 +688,7 @@ static float fcm_cycles_time( ofs = lastkey[0]; } } - if ((ELEM(0, side, mode))) { + if (ELEM(0, side, mode)) { return evaltime; } diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 28e46aab732..ef93a3f9b3f 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -53,7 +53,6 @@ GeometryComponent *MeshComponent::copy() const if (mesh_ != nullptr) { new_component->mesh_ = BKE_mesh_copy_for_eval(mesh_, false); new_component->ownership_ = GeometryOwnershipType::Owned; - new_component->vertex_group_names_ = blender::Map(vertex_group_names_); } return new_component; } @@ -67,7 +66,6 @@ void MeshComponent::clear() } mesh_ = nullptr; } - vertex_group_names_.clear(); } bool MeshComponent::has_mesh() const @@ -84,23 +82,6 @@ void MeshComponent::replace(Mesh *mesh, GeometryOwnershipType ownership) ownership_ = ownership; } -/* This function exists for the same reason as #vertex_group_names_. Non-nodes modifiers need to - * be able to replace the mesh data without losing the vertex group names, which may have come - * from another object. */ -void MeshComponent::replace_mesh_but_keep_vertex_group_names(Mesh *mesh, - GeometryOwnershipType ownership) -{ - BLI_assert(this->is_mutable()); - if (mesh_ != nullptr) { - if (ownership_ == GeometryOwnershipType::Owned) { - BKE_id_free(nullptr, mesh_); - } - mesh_ = nullptr; - } - mesh_ = mesh; - ownership_ = ownership; -} - /* Return the mesh and clear the component. The caller takes over responsibility for freeing the * mesh (if the component was responsible before). */ Mesh *MeshComponent::release() @@ -111,28 +92,6 @@ Mesh *MeshComponent::release() return mesh; } -void MeshComponent::copy_vertex_group_names_from_object(const Object &object) -{ - BLI_assert(this->is_mutable()); - vertex_group_names_.clear(); - int index = 0; - LISTBASE_FOREACH (const bDeformGroup *, group, &object.defbase) { - vertex_group_names_.add(group->name, index); - index++; - } -} - -const blender::Map<std::string, int> &MeshComponent::vertex_group_names() const -{ - return vertex_group_names_; -} - -/* This is only exposed for the internal attribute API. */ -blender::Map<std::string, int> &MeshComponent::vertex_group_names() -{ - return vertex_group_names_; -} - /* Get the mesh from this component. This method can be used by multiple threads at the same * time. Therefore, the returned mesh should not be modified. No ownership is transferred. */ const Mesh *MeshComponent::get_for_read() const @@ -864,12 +823,15 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); const Mesh *mesh = mesh_component.get_for_read(); - const int vertex_group_index = mesh_component.vertex_group_names().lookup_default_as( - attribute_name, -1); + if (mesh == nullptr) { + return {}; + } + const int vertex_group_index = BLI_findstringindex( + &mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name)); if (vertex_group_index < 0) { return {}; } - if (mesh == nullptr || mesh->dvert == nullptr) { + if (mesh->dvert == nullptr) { static const float default_value = 0.0f; return {std::make_unique<fn::GVArray_For_SingleValueRef>( CPPType::get<float>(), mesh->totvert, &default_value), @@ -889,8 +851,9 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { if (mesh == nullptr) { return {}; } - const int vertex_group_index = mesh_component.vertex_group_names().lookup_default_as( - attribute_name, -1); + + const int vertex_group_index = BLI_findstringindex( + &mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name)); if (vertex_group_index < 0) { return {}; } @@ -913,16 +876,16 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); MeshComponent &mesh_component = static_cast<MeshComponent &>(component); - - const int vertex_group_index = mesh_component.vertex_group_names().pop_default_as( - attribute_name, -1); - if (vertex_group_index < 0) { - return false; - } Mesh *mesh = mesh_component.get_for_write(); if (mesh == nullptr) { return true; } + + const int vertex_group_index = BLI_findstringindex( + &mesh->vertex_group_names, attribute_name.data(), offsetof(bDeformGroup, name)); + if (vertex_group_index < 0) { + return false; + } if (mesh->dvert == nullptr) { return true; } @@ -938,14 +901,14 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { { BLI_assert(component.type() == GEO_COMPONENT_TYPE_MESH); const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component); - for (const auto item : mesh_component.vertex_group_names().items()) { - const StringRefNull name = item.key; - const int vertex_group_index = item.value; - if (vertex_group_index >= 0) { - AttributeMetaData meta_data{ATTR_DOMAIN_POINT, CD_PROP_FLOAT}; - if (!callback(name, meta_data)) { - return false; - } + const Mesh *mesh = mesh_component.get_for_read(); + if (mesh == nullptr) { + return true; + } + + LISTBASE_FOREACH (const bDeformGroup *, group, &mesh->vertex_group_names) { + if (!callback(group->name, {ATTR_DOMAIN_POINT, CD_PROP_FLOAT})) { + return false; } } return true; diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 01b51d552a9..90a97264c8f 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -48,7 +48,6 @@ static void add_final_mesh_as_geometry_component(const Object &object, GeometryS MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly); - mesh_component.copy_vertex_group_names_from_object(object); } } @@ -566,6 +565,7 @@ static PointCloud *join_pointcloud_position_attribute(Span<GeometryInstanceGroup } PointCloud *new_pointcloud = BKE_pointcloud_new_nomain(totpoint); + MutableSpan new_positions{(float3 *)new_pointcloud->co, new_pointcloud->totpoint}; /* Transform each instance's point locations into the new point cloud. */ int offset = 0; @@ -577,9 +577,7 @@ static PointCloud *join_pointcloud_position_attribute(Span<GeometryInstanceGroup } for (const float4x4 &transform : set_group.transforms) { for (const int i : IndexRange(pointcloud->totpoint)) { - const float3 old_position = pointcloud->co[i]; - const float3 new_position = transform * old_position; - copy_v3_v3(new_pointcloud->co[offset + i], new_position); + new_positions[offset + i] = transform * float3(pointcloud->co[i]); } offset += pointcloud->totpoint; } diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 647db970d09..38397f8f307 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -87,6 +87,8 @@ static void greasepencil_copy_data(Main *UNUSED(bmain), gpd_dst->mat = MEM_dupallocN(gpd_src->mat); } + BKE_defgroup_copy_list(&gpd_dst->vertex_group_names, &gpd_src->vertex_group_names); + /* copy layers */ BLI_listbase_clear(&gpd_dst->layers); LISTBASE_FOREACH (bGPDlayer *, gpl_src, &gpd_src->layers) { @@ -165,6 +167,8 @@ static void greasepencil_blend_write(BlendWriter *writer, ID *id, const void *id BKE_animdata_blend_write(writer, gpd->adt); } + BKE_defbase_blend_write(writer, &gpd->vertex_group_names); + BLO_write_pointer_array(writer, gpd->totcol, gpd->mat); /* write grease-pencil layers to file */ @@ -227,6 +231,8 @@ void BKE_gpencil_blend_read_data(BlendDataReader *reader, bGPdata *gpd) } } + BLO_read_list(reader, &gpd->vertex_group_names); + /* Materials. */ BLO_read_pointer_array(reader, (void **)&gpd->mat); @@ -498,6 +504,8 @@ void BKE_gpencil_free(bGPdata *gpd, bool free_all) /* materials */ MEM_SAFE_FREE(gpd->mat); + BLI_freelistN(&gpd->vertex_group_names); + /* free all data */ if (free_all) { /* clear cache */ @@ -798,32 +806,6 @@ bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[]) /* Utilities for easier bulk-creation of geometry */ /** - * Populate stroke with point data from data buffers. - * \param gps: Grease pencil stroke - * \param array: Flat array of point data values. Each entry has #GP_PRIM_DATABUF_SIZE values. - * \param totpoints: Total of points - * \param mat: 4x4 transform matrix to transform points into the right coordinate space. - */ -void BKE_gpencil_stroke_add_points(bGPDstroke *gps, - const float *array, - const int totpoints, - const float mat[4][4]) -{ - for (int i = 0; i < totpoints; i++) { - bGPDspoint *pt = &gps->points[i]; - const int x = GP_PRIM_DATABUF_SIZE * i; - - pt->x = array[x]; - pt->y = array[x + 1]; - pt->z = array[x + 2]; - mul_m4_v3(mat, &pt->x); - - pt->pressure = array[x + 3]; - pt->strength = array[x + 4]; - } -} - -/** * Create a new stroke, with pre-allocated data buffers. * \param mat_idx: Index of the material * \param totpoints: Total points @@ -2087,8 +2069,9 @@ void BKE_gpencil_vgroup_remove(Object *ob, bDeformGroup *defgroup) { bGPdata *gpd = ob->data; MDeformVert *dvert = NULL; - const int def_nr = BLI_findindex(&ob->defbase, defgroup); - const int totgrp = BLI_listbase_count(&ob->defbase); + + const int def_nr = BLI_findindex(&gpd->vertex_group_names, defgroup); + const int totgrp = BLI_listbase_count(&gpd->vertex_group_names); /* Remove points data */ if (gpd) { @@ -2117,7 +2100,7 @@ void BKE_gpencil_vgroup_remove(Object *ob, bDeformGroup *defgroup) } /* Remove the group */ - BLI_freelinkN(&ob->defbase, defgroup); + BLI_freelinkN(&gpd->vertex_group_names, defgroup); DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); } @@ -2747,7 +2730,7 @@ void BKE_gpencil_visible_stroke_advanced_iter(ViewLayer *view_layer, int cfra) { bGPdata *gpd = (bGPdata *)ob->data; - const bool is_multiedit = ((GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) && (!GPENCIL_PLAY_ON(gpd))); + const bool is_multiedit = (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd) && (!GPENCIL_PLAY_ON(gpd))); const bool is_onion = do_onion && ((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0); const bool is_drawing = (gpd->runtime.sbuffer_used > 0); diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.cc index 4dcd94fdeec..785f63a7ba2 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.c +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -21,22 +21,24 @@ * \ingroup bke */ -#include <math.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> +#include <cmath> +#include <cstddef> +#include <cstdio> +#include <cstdlib> +#include <cstring> #include "CLG_log.h" #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_float3.hh" #include "BLI_ghash.h" #include "BLI_hash.h" #include "BLI_heap.h" #include "BLI_math_vector.h" #include "BLI_polyfill_2d.h" +#include "BLI_span.hh" #include "BLT_translation.h" @@ -61,6 +63,9 @@ #include "DEG_depsgraph_query.h" +using blender::float3; +using blender::Span; + /* GP Object - Boundbox Support */ /** *Get min/max coordinate bounds for single stroke. @@ -75,20 +80,26 @@ bool BKE_gpencil_stroke_minmax(const bGPDstroke *gps, float r_min[3], float r_max[3]) { - const bGPDspoint *pt; - int i; - bool changed = false; - - if (ELEM(NULL, gps, r_min, r_max)) { + if (gps == nullptr) { return false; } - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if ((use_select == false) || (pt->flag & GP_SPOINT_SELECT)) { - minmax_v3v3_v3(r_min, r_max, &pt->x); + bool changed = false; + if (use_select) { + for (const bGPDspoint &pt : Span(gps->points, gps->totpoints)) { + if (pt.flag & GP_SPOINT_SELECT) { + minmax_v3v3_v3(r_min, r_max, &pt.x); + changed = true; + } + } + } + else { + for (const bGPDspoint &pt : Span(gps->points, gps->totpoints)) { + minmax_v3v3_v3(r_min, r_max, &pt.x); changed = true; } } + return changed; } @@ -105,14 +116,14 @@ bool BKE_gpencil_data_minmax(const bGPdata *gpd, float r_min[3], float r_max[3]) INIT_MINMAX(r_min, r_max); - if (gpd == NULL) { + if (gpd == nullptr) { return changed; } LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { bGPDframe *gpf = gpl->actframe; - if (gpf != NULL) { + if (gpf != nullptr) { LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { changed |= BKE_gpencil_stroke_minmax(gps, false, r_min, r_max); } @@ -129,11 +140,11 @@ bool BKE_gpencil_data_minmax(const bGPdata *gpd, float r_min[3], float r_max[3]) */ void BKE_gpencil_centroid_3d(bGPdata *gpd, float r_centroid[3]) { - float min[3], max[3], tot[3]; - + float3 min; + float3 max; BKE_gpencil_data_minmax(gpd, min, max); - add_v3_v3v3(tot, min, max); + const float3 tot = min + max; mul_v3_v3fl(r_centroid, tot, 0.5f); } @@ -153,20 +164,18 @@ void BKE_gpencil_stroke_boundingbox_calc(bGPDstroke *gps) */ static void boundbox_gpencil(Object *ob) { - BoundBox *bb; - bGPdata *gpd; - float min[3], max[3]; - - if (ob->runtime.bb == NULL) { - ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "GPencil boundbox"); + if (ob->runtime.bb == nullptr) { + ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "GPencil boundbox"); } - bb = ob->runtime.bb; - gpd = ob->data; + BoundBox *bb = ob->runtime.bb; + bGPdata *gpd = (bGPdata *)ob->data; + float3 min; + float3 max; if (!BKE_gpencil_data_minmax(gpd, min, max)) { - min[0] = min[1] = min[2] = -1.0f; - max[0] = max[1] = max[2] = 1.0f; + min = float3(-1); + max = float3(1); } BKE_boundbox_init_from_minmax(bb, min, max); @@ -181,8 +190,8 @@ static void boundbox_gpencil(Object *ob) */ BoundBox *BKE_gpencil_boundbox_get(Object *ob) { - if (ELEM(NULL, ob, ob->data)) { - return NULL; + if (ELEM(nullptr, ob, ob->data)) { + return nullptr; } bGPdata *gpd = (bGPdata *)ob->data; @@ -196,9 +205,9 @@ BoundBox *BKE_gpencil_boundbox_get(Object *ob) /* Update orig object's boundbox with re-computed evaluated values. This function can be * called with the evaluated object and need update the original object bound box data * to keep both values synchronized. */ - if (!ELEM(ob_orig, NULL, ob)) { - if (ob_orig->runtime.bb == NULL) { - ob_orig->runtime.bb = MEM_callocN(sizeof(BoundBox), "GPencil boundbox"); + if (!ELEM(ob_orig, nullptr, ob)) { + if (ob_orig->runtime.bb == nullptr) { + ob_orig->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "GPencil boundbox"); } for (int i = 0; i < 8; i++) { copy_v3_v3(ob_orig->runtime.bb->vec[i], ob->runtime.bb->vec[i]); @@ -227,7 +236,7 @@ static int stroke_march_next_point(const bGPDstroke *gps, float step_start[3]; float point[3]; int next_point_index = index_next_pt; - bGPDspoint *pt = NULL; + bGPDspoint *pt = nullptr; if (!(next_point_index < gps->totpoints)) { return -1; @@ -295,7 +304,7 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps, float step_start[3]; float point[3]; int next_point_index = index_next_pt; - bGPDspoint *pt = NULL; + bGPDspoint *pt = nullptr; if (!(next_point_index < gps->totpoints)) { return -1; @@ -336,7 +345,7 @@ static int stroke_march_count(const bGPDstroke *gps, const float dist) int point_count = 0; float point[3]; int next_point_index = 1; - bGPDspoint *pt = NULL; + bGPDspoint *pt = nullptr; pt = &gps->points[0]; copy_v3_v3(point, &pt->x); @@ -369,14 +378,14 @@ static void stroke_defvert_create_nr_list(MDeformVert *dv_list, for (j = 0; j < dv->totweight; j++) { bool found = false; dw = &dv->dw[j]; - for (ld = result->first; ld; ld = ld->next) { + for (ld = (LinkData *)result->first; ld; ld = ld->next) { if (ld->data == POINTER_FROM_INT(dw->def_nr)) { found = true; break; } } if (!found) { - ld = MEM_callocN(sizeof(LinkData), "def_nr_item"); + ld = (LinkData *)MEM_callocN(sizeof(LinkData), "def_nr_item"); ld->data = POINTER_FROM_INT(dw->def_nr); BLI_addtail(result, ld); tw++; @@ -391,14 +400,15 @@ static MDeformVert *stroke_defvert_new_count(int count, int totweight, ListBase { int i, j; LinkData *ld; - MDeformVert *dst = MEM_mallocN(count * sizeof(MDeformVert), "new_deformVert"); + MDeformVert *dst = (MDeformVert *)MEM_mallocN(count * sizeof(MDeformVert), "new_deformVert"); for (i = 0; i < count; i++) { - dst[i].dw = MEM_mallocN(sizeof(MDeformWeight) * totweight, "new_deformWeight"); + dst[i].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * totweight, + "new_deformWeight"); dst[i].totweight = totweight; j = 0; /* re-assign deform groups */ - for (ld = def_nr_list->first; ld; ld = ld->next) { + for (ld = (LinkData *)def_nr_list->first; ld; ld = ld->next) { dst[i].dw[j].def_nr = POINTER_AS_INT(ld->data); j++; } @@ -429,10 +439,10 @@ static void stroke_interpolate_deform_weights( bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, const bool select) { bGPDspoint *pt = gps->points; - bGPDspoint *pt1 = NULL; - bGPDspoint *pt2 = NULL; + bGPDspoint *pt1 = nullptr; + bGPDspoint *pt2 = nullptr; LinkData *ld; - ListBase def_nr_list = {0}; + ListBase def_nr_list = {nullptr}; if (gps->totpoints < 2 || dist < FLT_EPSILON) { return false; @@ -440,12 +450,13 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, /* TODO: Implement feature point preservation. */ int count = stroke_march_count(gps, dist); - bGPDspoint *new_pt = MEM_callocN(sizeof(bGPDspoint) * count, "gp_stroke_points_sampled"); - MDeformVert *new_dv = NULL; + bGPDspoint *new_pt = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * count, + "gp_stroke_points_sampled"); + MDeformVert *new_dv = nullptr; int result_totweight; - if (gps->dvert != NULL) { + if (gps->dvert != nullptr) { stroke_defvert_create_nr_list(gps->dvert, gps->totpoints, &def_nr_list, &result_totweight); new_dv = stroke_defvert_new_count(count, result_totweight, &def_nr_list); } @@ -513,7 +524,7 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, /* Free original weight data. */ BKE_gpencil_free_stroke_weights(gps); MEM_freeN(gps->dvert); - while ((ld = BLI_pophead(&def_nr_list))) { + while ((ld = (LinkData *)BLI_pophead(&def_nr_list))) { MEM_freeN(ld); } @@ -610,26 +621,27 @@ bool BKE_gpencil_stroke_trim_points(bGPDstroke *gps, const int index_from, const if (new_count == 1) { BKE_gpencil_free_stroke_weights(gps); MEM_freeN(gps->points); - gps->points = NULL; - gps->dvert = NULL; + gps->points = nullptr; + gps->dvert = nullptr; gps->totpoints = 0; return false; } - new_pt = MEM_callocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed"); + new_pt = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * new_count, "gp_stroke_points_trimmed"); for (int i = 0; i < new_count; i++) { memcpy(&new_pt[i], &pt[i + index_from], sizeof(bGPDspoint)); } if (gps->dvert) { - new_dv = MEM_callocN(sizeof(MDeformVert) * new_count, "gp_stroke_dverts_trimmed"); + new_dv = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * new_count, + "gp_stroke_dverts_trimmed"); for (int i = 0; i < new_count; i++) { dv = &gps->dvert[i + index_from]; new_dv[i].flag = dv->flag; new_dv[i].totweight = dv->totweight; - new_dv[i].dw = MEM_callocN(sizeof(MDeformWeight) * dv->totweight, - "gp_stroke_dverts_dw_trimmed"); + new_dv[i].dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight, + "gp_stroke_dverts_dw_trimmed"); for (int j = 0; j < dv->totweight; j++) { new_dv[i].dw[j].weight = dv->dw[j].weight; new_dv[i].dw[j].def_nr = dv->dw[j].def_nr; @@ -685,14 +697,14 @@ bool BKE_gpencil_stroke_split(bGPdata *gpd, } if (gps->dvert) { - new_dv = MEM_callocN(sizeof(MDeformVert) * new_count, - "gp_stroke_dverts_remaining(MDeformVert)"); + new_dv = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * new_count, + "gp_stroke_dverts_remaining(MDeformVert)"); for (int i = 0; i < new_count; i++) { dv = &gps->dvert[i + before_index]; new_dv[i].flag = dv->flag; new_dv[i].totweight = dv->totweight; - new_dv[i].dw = MEM_callocN(sizeof(MDeformWeight) * dv->totweight, - "gp_stroke_dverts_dw_remaining(MDeformWeight)"); + new_dv[i].dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight, + "gp_stroke_dverts_dw_remaining(MDeformWeight)"); for (int j = 0; j < dv->totweight; j++) { new_dv[i].dw[j].weight = dv->dw[j].weight; new_dv[i].dw[j].def_nr = dv->dw[j].def_nr; @@ -1301,11 +1313,12 @@ void BKE_gpencil_stroke_fill_triangulate(bGPDstroke *gps) /* allocate memory for temporary areas */ gps->tot_triangles = gps->totpoints - 2; - uint(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles, - "GP Stroke temp triangulation"); - float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints, - "GP Stroke temp 2d points"); - float(*uv)[2] = MEM_mallocN(sizeof(*uv) * gps->totpoints, "GP Stroke temp 2d uv data"); + uint(*tmp_triangles)[3] = (uint(*)[3])MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles, + "GP Stroke temp triangulation"); + float(*points2d)[2] = (float(*)[2])MEM_mallocN(sizeof(*points2d) * gps->totpoints, + "GP Stroke temp 2d points"); + float(*uv)[2] = (float(*)[2])MEM_mallocN(sizeof(*uv) * gps->totpoints, + "GP Stroke temp 2d uv data"); int direction = 0; @@ -1326,8 +1339,8 @@ void BKE_gpencil_stroke_fill_triangulate(bGPDstroke *gps) /* Save triangulation data. */ if (gps->tot_triangles > 0) { MEM_SAFE_FREE(gps->triangles); - gps->triangles = MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles, - "GP Stroke triangulation"); + gps->triangles = (bGPDtriangle *)MEM_callocN(sizeof(*gps->triangles) * gps->tot_triangles, + "GP Stroke triangulation"); for (int i = 0; i < gps->tot_triangles; i++) { memcpy(gps->triangles[i].verts, tmp_triangles[i], sizeof(uint[3])); @@ -1344,7 +1357,7 @@ void BKE_gpencil_stroke_fill_triangulate(bGPDstroke *gps) MEM_freeN(gps->triangles); } - gps->triangles = NULL; + gps->triangles = nullptr; } /* clear memory */ @@ -1359,7 +1372,7 @@ void BKE_gpencil_stroke_fill_triangulate(bGPDstroke *gps) */ void BKE_gpencil_stroke_uv_update(bGPDstroke *gps) { - if (gps == NULL || gps->totpoints == 0) { + if (gps == nullptr || gps->totpoints == 0) { return; } @@ -1379,11 +1392,11 @@ void BKE_gpencil_stroke_uv_update(bGPDstroke *gps) */ void BKE_gpencil_stroke_geometry_update(bGPdata *gpd, bGPDstroke *gps) { - if (gps == NULL) { + if (gps == nullptr) { return; } - if (gps->editcurve != NULL) { + if (gps->editcurve != nullptr) { if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) { /* curve geometry was updated: stroke needs recalculation */ if (gps->flag & GP_STROKE_NEEDS_CURVE_UPDATE) { @@ -1519,20 +1532,20 @@ bool BKE_gpencil_stroke_trim(bGPdata *gpd, bGPDstroke *gps) if (intersect) { /* save points */ - bGPDspoint *old_points = MEM_dupallocN(gps->points); - MDeformVert *old_dvert = NULL; - MDeformVert *dvert_src = NULL; + bGPDspoint *old_points = (bGPDspoint *)MEM_dupallocN(gps->points); + MDeformVert *old_dvert = nullptr; + MDeformVert *dvert_src = nullptr; - if (gps->dvert != NULL) { - old_dvert = MEM_dupallocN(gps->dvert); + if (gps->dvert != nullptr) { + old_dvert = (MDeformVert *)MEM_dupallocN(gps->dvert); } /* resize gps */ int newtot = end - start + 1; - gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot); - if (gps->dvert != NULL) { - gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot); + gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(*gps->points) * newtot); + if (gps->dvert != nullptr) { + gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot); } for (int i = 0; i < newtot; i++) { @@ -1540,7 +1553,7 @@ bool BKE_gpencil_stroke_trim(bGPdata *gpd, bGPDstroke *gps) bGPDspoint *pt_src = &old_points[idx]; bGPDspoint *pt_new = &gps->points[i]; memcpy(pt_new, pt_src, sizeof(bGPDspoint)); - if (gps->dvert != NULL) { + if (gps->dvert != nullptr) { dvert_src = &old_dvert[idx]; MDeformVert *dvert = &gps->dvert[i]; memcpy(dvert, dvert_src, sizeof(MDeformVert)); @@ -1570,8 +1583,8 @@ bool BKE_gpencil_stroke_trim(bGPdata *gpd, bGPDstroke *gps) */ bool BKE_gpencil_stroke_close(bGPDstroke *gps) { - bGPDspoint *pt1 = NULL; - bGPDspoint *pt2 = NULL; + bGPDspoint *pt1 = nullptr; + bGPDspoint *pt2 = nullptr; /* Only can close a stroke with 3 points or more. */ if (gps->totpoints < 3) { @@ -1605,9 +1618,9 @@ bool BKE_gpencil_stroke_close(bGPDstroke *gps) /* Resize stroke array. */ int old_tot = gps->totpoints; gps->totpoints += tot_newpoints; - gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); - if (gps->dvert != NULL) { - gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); + gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); + if (gps->dvert != nullptr) { + gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); } /* Generate new points */ @@ -1629,7 +1642,7 @@ bool BKE_gpencil_stroke_close(bGPDstroke *gps) interp_v4_v4v4(pt->vert_color, pt1->vert_color, pt2->vert_color, step); /* Set weights. */ - if (gps->dvert != NULL) { + if (gps->dvert != nullptr) { MDeformVert *dvert1 = &gps->dvert[old_tot - 1]; MDeformWeight *dw1 = BKE_defvert_ensure_index(dvert1, 0); float weight_1 = dw1 ? dw1->weight : 0.0f; @@ -1663,7 +1676,7 @@ bool BKE_gpencil_stroke_close(bGPDstroke *gps) void BKE_gpencil_dissolve_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, const short tag) { bGPDspoint *pt; - MDeformVert *dvert = NULL; + MDeformVert *dvert = nullptr; int i; int tot = gps->totpoints; /* number of points in new buffer */ @@ -1693,30 +1706,32 @@ void BKE_gpencil_dissolve_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, } else { /* just copy all points to keep into a smaller buffer */ - bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy"); + bGPDspoint *new_points = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * tot, + "new gp stroke points copy"); bGPDspoint *npt = new_points; - MDeformVert *new_dvert = NULL; - MDeformVert *ndvert = NULL; + MDeformVert *new_dvert = nullptr; + MDeformVert *ndvert = nullptr; - if (gps->dvert != NULL) { - new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy"); + if (gps->dvert != nullptr) { + new_dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * tot, + "new gp stroke weights copy"); ndvert = new_dvert; } - (gps->dvert != NULL) ? dvert = gps->dvert : NULL; + (gps->dvert != nullptr) ? dvert = gps->dvert : nullptr; for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { if ((pt->flag & tag) == 0) { *npt = *pt; npt++; - if (gps->dvert != NULL) { + if (gps->dvert != nullptr) { *ndvert = *dvert; - ndvert->dw = MEM_dupallocN(dvert->dw); + ndvert->dw = (MDeformWeight *)MEM_dupallocN(dvert->dw); ndvert++; } } - if (gps->dvert != NULL) { + if (gps->dvert != nullptr) { dvert++; } } @@ -1789,15 +1804,15 @@ void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3]) */ void BKE_gpencil_stroke_simplify_adaptive(bGPdata *gpd, bGPDstroke *gps, float epsilon) { - bGPDspoint *old_points = MEM_dupallocN(gps->points); + bGPDspoint *old_points = (bGPDspoint *)MEM_dupallocN(gps->points); int totpoints = gps->totpoints; - char *marked = NULL; + char *marked = nullptr; char work; int start = 0; int end = gps->totpoints - 1; - marked = MEM_callocN(totpoints, "GP marked array"); + marked = (char *)MEM_callocN(totpoints, "GP marked array"); marked[start] = 1; marked[end] = 1; @@ -1849,11 +1864,11 @@ void BKE_gpencil_stroke_simplify_adaptive(bGPdata *gpd, bGPDstroke *gps, float e } /* adding points marked */ - MDeformVert *old_dvert = NULL; - MDeformVert *dvert_src = NULL; + MDeformVert *old_dvert = nullptr; + MDeformVert *dvert_src = nullptr; - if (gps->dvert != NULL) { - old_dvert = MEM_dupallocN(gps->dvert); + if (gps->dvert != nullptr) { + old_dvert = (MDeformVert *)MEM_dupallocN(gps->dvert); } /* resize gps */ int j = 0; @@ -1863,7 +1878,7 @@ void BKE_gpencil_stroke_simplify_adaptive(bGPdata *gpd, bGPDstroke *gps, float e if ((marked[i]) || (i == 0) || (i == totpoints - 1)) { memcpy(pt, pt_src, sizeof(bGPDspoint)); - if (gps->dvert != NULL) { + if (gps->dvert != nullptr) { dvert_src = &old_dvert[i]; MDeformVert *dvert = &gps->dvert[j]; memcpy(dvert, dvert_src, sizeof(MDeformVert)); @@ -1874,7 +1889,7 @@ void BKE_gpencil_stroke_simplify_adaptive(bGPdata *gpd, bGPDstroke *gps, float e j++; } else { - if (gps->dvert != NULL) { + if (gps->dvert != nullptr) { dvert_src = &old_dvert[i]; BKE_gpencil_free_point_weights(dvert_src); } @@ -1903,12 +1918,12 @@ void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps) } /* save points */ - bGPDspoint *old_points = MEM_dupallocN(gps->points); - MDeformVert *old_dvert = NULL; - MDeformVert *dvert_src = NULL; + bGPDspoint *old_points = (bGPDspoint *)MEM_dupallocN(gps->points); + MDeformVert *old_dvert = nullptr; + MDeformVert *dvert_src = nullptr; - if (gps->dvert != NULL) { - old_dvert = MEM_dupallocN(gps->dvert); + if (gps->dvert != nullptr) { + old_dvert = (MDeformVert *)MEM_dupallocN(gps->dvert); } /* resize gps */ @@ -1918,9 +1933,9 @@ void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps) } newtot += 2; - gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * newtot); - if (gps->dvert != NULL) { - gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot); + gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(*gps->points) * newtot); + if (gps->dvert != nullptr) { + gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot); } int j = 0; @@ -1930,7 +1945,7 @@ void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps) if ((i == 0) || (i == gps->totpoints - 1) || ((i % 2) > 0.0)) { memcpy(pt, pt_src, sizeof(bGPDspoint)); - if (gps->dvert != NULL) { + if (gps->dvert != nullptr) { dvert_src = &old_dvert[i]; MDeformVert *dvert = &gps->dvert[j]; memcpy(dvert, dvert_src, sizeof(MDeformVert)); @@ -1941,7 +1956,7 @@ void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps) j++; } else { - if (gps->dvert != NULL) { + if (gps->dvert != nullptr) { dvert_src = &old_dvert[i]; BKE_gpencil_free_point_weights(dvert_src); } @@ -1966,25 +1981,25 @@ void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps) void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int type) { bGPDspoint *temp_points; - MDeformVert *temp_dverts = NULL; - MDeformVert *dvert = NULL; - MDeformVert *dvert_final = NULL; - MDeformVert *dvert_next = NULL; + MDeformVert *temp_dverts = nullptr; + MDeformVert *dvert = nullptr; + MDeformVert *dvert_final = nullptr; + MDeformVert *dvert_next = nullptr; int totnewpoints, oldtotpoints; int i2; for (int s = 0; s < level; s++) { totnewpoints = gps->totpoints - 1; /* duplicate points in a temp area */ - temp_points = MEM_dupallocN(gps->points); + temp_points = (bGPDspoint *)MEM_dupallocN(gps->points); oldtotpoints = gps->totpoints; /* resize the points arrays */ gps->totpoints += totnewpoints; - gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); - if (gps->dvert != NULL) { - temp_dverts = MEM_dupallocN(gps->dvert); - gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); + gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); + if (gps->dvert != nullptr) { + temp_dverts = (MDeformVert *)MEM_dupallocN(gps->dvert); + gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); } /* move points from last to first to new place */ @@ -2002,7 +2017,7 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int pt_final->runtime.idx_orig = pt->runtime.idx_orig; copy_v4_v4(pt_final->vert_color, pt->vert_color); - if (gps->dvert != NULL) { + if (gps->dvert != nullptr) { dvert = &temp_dverts[i]; dvert_final = &gps->dvert[i2]; dvert_final->totweight = dvert->totweight; @@ -2023,17 +2038,17 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int pt_final->strength = interpf(pt->strength, next->strength, 0.5f); CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt_final->time = interpf(pt->time, next->time, 0.5f); - pt_final->runtime.pt_orig = NULL; + pt_final->runtime.pt_orig = nullptr; pt_final->flag = 0; interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f); - if (gps->dvert != NULL) { + if (gps->dvert != nullptr) { dvert = &temp_dverts[i]; dvert_next = &temp_dverts[i + 1]; dvert_final = &gps->dvert[i2]; dvert_final->totweight = dvert->totweight; - dvert_final->dw = MEM_dupallocN(dvert->dw); + dvert_final->dw = (MDeformWeight *)MEM_dupallocN(dvert->dw); /* interpolate weight values */ for (int d = 0; d < dvert->totweight; d++) { @@ -2055,7 +2070,7 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int /* Move points to smooth stroke (not simple type). */ if (type != GP_SUBDIV_SIMPLE) { /* duplicate points in a temp area with the new subdivide data */ - temp_points = MEM_dupallocN(gps->points); + temp_points = (bGPDspoint *)MEM_dupallocN(gps->points); /* extreme points are not changed */ for (int i = 0; i < gps->totpoints - 2; i++) { @@ -2093,8 +2108,8 @@ void BKE_gpencil_stroke_merge_distance(bGPdata *gpd, const float threshold, const bool use_unselected) { - bGPDspoint *pt = NULL; - bGPDspoint *pt_next = NULL; + bGPDspoint *pt = nullptr; + bGPDspoint *pt_next = nullptr; float tagged = false; /* Use square distance to speed up loop */ const float th_square = threshold * threshold; @@ -2160,7 +2175,7 @@ void BKE_gpencil_stroke_merge_distance(bGPdata *gpd, BKE_gpencil_stroke_geometry_update(gpd, gps); } -typedef struct GpEdge { +struct GpEdge { uint v1, v2; /* Coordinates. */ float v1_co[3], v2_co[3]; @@ -2169,7 +2184,7 @@ typedef struct GpEdge { /* Direction of the segment. */ float vec[3]; int flag; -} GpEdge; +}; static int gpencil_next_edge( GpEdge *gp_edges, int totedges, GpEdge *gped_init, const float threshold, const bool reverse) @@ -2262,13 +2277,13 @@ static void gpencil_generate_edgeloops(Object *ob, /* Arrays for all edge vertices (forward and backward) that form a edge loop. * This is reused for each edgeloop to create gpencil stroke. */ - uint *stroke = MEM_callocN(sizeof(uint) * me->totedge * 2, __func__); - uint *stroke_fw = MEM_callocN(sizeof(uint) * me->totedge, __func__); - uint *stroke_bw = MEM_callocN(sizeof(uint) * me->totedge, __func__); + uint *stroke = (uint *)MEM_callocN(sizeof(uint) * me->totedge * 2, __func__); + uint *stroke_fw = (uint *)MEM_callocN(sizeof(uint) * me->totedge, __func__); + uint *stroke_bw = (uint *)MEM_callocN(sizeof(uint) * me->totedge, __func__); /* Create array with all edges. */ - GpEdge *gp_edges = MEM_callocN(sizeof(GpEdge) * me->totedge, __func__); - GpEdge *gped = NULL; + GpEdge *gp_edges = (GpEdge *)MEM_callocN(sizeof(GpEdge) * me->totedge, __func__); + GpEdge *gped = nullptr; for (int i = 0; i < me->totedge; i++) { MEdge *ed = &me->medge[i]; gped = &gp_edges[i]; @@ -2321,7 +2336,7 @@ static void gpencil_generate_edgeloops(Object *ob, /* Look backward edges. */ int totbw = gpencil_walk_edge(v_table, gp_edges, me->totedge, stroke_bw, e, angle, true); - BLI_ghash_free(v_table, NULL, NULL); + BLI_ghash_free(v_table, nullptr, nullptr); /* Join both arrays. */ int array_len = 0; @@ -2423,7 +2438,7 @@ static int gpencil_material_find_index_by_name(Object *ob, const char *name) { for (int i = 0; i < ob->totcol; i++) { Material *ma = BKE_object_material_get(ob, i + 1); - if ((ma != NULL) && (ma->gp_style != NULL) && (STREQ(ma->id.name + 2, name))) { + if ((ma != nullptr) && (ma->gp_style != nullptr) && (STREQ(ma->id.name + 2, name))) { return i; } } @@ -2474,7 +2489,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain, const bool use_seams, const bool use_faces) { - if (ELEM(NULL, ob_gp, ob_mesh) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == NULL)) { + if (ELEM(nullptr, ob_gp, ob_mesh) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == nullptr)) { return false; } @@ -2511,7 +2526,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain, make_element_name(ob_mesh->id.name + 2, "Fills", 128, element_name); /* Create Layer and Frame. */ bGPDlayer *gpl_fill = BKE_gpencil_layer_named_get(gpd, element_name); - if (gpl_fill == NULL) { + if (gpl_fill == nullptr) { gpl_fill = BKE_gpencil_layer_addnew(gpd, element_name, true, false); } bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get( @@ -2524,11 +2539,11 @@ bool BKE_gpencil_convert_mesh(Main *bmain, int mat_idx = 0; Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1); make_element_name( - ob_mesh->id.name + 2, (ma != NULL) ? ma->id.name + 2 : "Fill", 64, element_name); + ob_mesh->id.name + 2, (ma != nullptr) ? ma->id.name + 2 : "Fill", 64, element_name); mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name); if (mat_idx == -1) { float color[4]; - if (ma != NULL) { + if (ma != nullptr) { copy_v3_v3(color, &ma->r); color[3] = 1.0f; } @@ -2567,7 +2582,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain, /* Create Layer and Frame. */ bGPDlayer *gpl_stroke = BKE_gpencil_layer_named_get(gpd, element_name); - if (gpl_stroke == NULL) { + if (gpl_stroke == nullptr) { gpl_stroke = BKE_gpencil_layer_addnew(gpd, element_name, true, false); } bGPDframe *gpf_stroke = BKE_gpencil_layer_frame_get( @@ -2589,7 +2604,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain, */ void BKE_gpencil_transform(bGPdata *gpd, const float mat[4][4]) { - if (gpd == NULL) { + if (gpd == nullptr) { return; } @@ -2625,7 +2640,7 @@ int BKE_gpencil_stroke_point_count(const bGPdata *gpd) { int total_points = 0; - if (gpd == NULL) { + if (gpd == nullptr) { return 0; } @@ -2650,7 +2665,7 @@ int BKE_gpencil_stroke_point_count(const bGPdata *gpd) /* Used for "move only origins" in object_data_transform.c */ void BKE_gpencil_point_coords_get(bGPdata *gpd, GPencilPointCoordinates *elem_data) { - if (gpd == NULL) { + if (gpd == nullptr) { return; } @@ -2681,7 +2696,7 @@ void BKE_gpencil_point_coords_get(bGPdata *gpd, GPencilPointCoordinates *elem_da /* Used for "move only origins" in object_data_transform.c */ void BKE_gpencil_point_coords_apply(bGPdata *gpd, const GPencilPointCoordinates *elem_data) { - if (gpd == NULL) { + if (gpd == nullptr) { return; } @@ -2717,7 +2732,7 @@ void BKE_gpencil_point_coords_apply_with_mat4(bGPdata *gpd, const GPencilPointCoordinates *elem_data, const float mat[4][4]) { - if (gpd == NULL) { + if (gpd == nullptr) { return; } @@ -2818,24 +2833,24 @@ void BKE_gpencil_stroke_flip(bGPDstroke *gps) * that should be kept when splitting up a stroke. Used in: * gpencil_stroke_delete_tagged_points() */ -typedef struct tGPDeleteIsland { +struct tGPDeleteIsland { int start_idx; int end_idx; -} tGPDeleteIsland; +}; static void gpencil_stroke_join_islands(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps_first, bGPDstroke *gps_last) { - bGPDspoint *pt = NULL; - bGPDspoint *pt_final = NULL; + bGPDspoint *pt = nullptr; + bGPDspoint *pt_final = nullptr; const int totpoints = gps_first->totpoints + gps_last->totpoints; /* create new stroke */ bGPDstroke *join_stroke = BKE_gpencil_stroke_duplicate(gps_first, false, true); - join_stroke->points = MEM_callocN(sizeof(bGPDspoint) * totpoints, __func__); + join_stroke->points = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * totpoints, __func__); join_stroke->totpoints = totpoints; join_stroke->flag &= ~GP_STROKE_CYCLIC; @@ -2868,17 +2883,17 @@ static void gpencil_stroke_join_islands(bGPdata *gpd, } /* Copy over vertex weight data (if available) */ - if ((gps_first->dvert != NULL) || (gps_last->dvert != NULL)) { - join_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * totpoints, __func__); - MDeformVert *dvert_src = NULL; - MDeformVert *dvert_dst = NULL; + if ((gps_first->dvert != nullptr) || (gps_last->dvert != nullptr)) { + join_stroke->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * totpoints, __func__); + MDeformVert *dvert_src = nullptr; + MDeformVert *dvert_dst = nullptr; /* Copy weights (last before). */ e1 = 0; e2 = 0; for (int i = 0; i < totpoints; i++) { dvert_dst = &join_stroke->dvert[i]; - dvert_src = NULL; + dvert_src = nullptr; if (i < gps_last->totpoints) { if (gps_last->dvert) { dvert_src = &gps_last->dvert[e1]; @@ -2893,7 +2908,7 @@ static void gpencil_stroke_join_islands(bGPdata *gpd, } if ((dvert_src) && (dvert_src->dw)) { - dvert_dst->dw = MEM_dupallocN(dvert_src->dw); + dvert_dst->dw = (MDeformWeight *)MEM_dupallocN(dvert_src->dw); } } } @@ -2934,13 +2949,13 @@ bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(bGPdata *gpd, bool select, int limit) { - tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, - "gp_point_islands"); + tGPDeleteIsland *islands = (tGPDeleteIsland *)MEM_callocN( + sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands"); bool in_island = false; int num_islands = 0; - bGPDstroke *new_stroke = NULL; - bGPDstroke *gps_first = NULL; + bGPDstroke *new_stroke = nullptr; + bGPDstroke *gps_first = nullptr; const bool is_cyclic = (bool)(gps->flag & GP_STROKE_CYCLIC); /* First Pass: Identify start/end of islands */ @@ -2982,7 +2997,7 @@ bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(bGPdata *gpd, new_stroke = BKE_gpencil_stroke_duplicate(gps, false, true); /* if cyclic and first stroke, save to join later */ - if ((is_cyclic) && (gps_first == NULL)) { + if ((is_cyclic) && (gps_first == nullptr)) { gps_first = new_stroke; } @@ -2992,17 +3007,17 @@ bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(bGPdata *gpd, new_stroke->totpoints = island->end_idx - island->start_idx + 1; /* Copy over the relevant point data */ - new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, - "gp delete stroke fragment"); + new_stroke->points = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, + "gp delete stroke fragment"); memcpy(new_stroke->points, gps->points + island->start_idx, sizeof(bGPDspoint) * new_stroke->totpoints); /* Copy over vertex weight data (if available) */ - if (gps->dvert != NULL) { + if (gps->dvert != nullptr) { /* Copy over the relevant vertex-weight points */ - new_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * new_stroke->totpoints, - "gp delete stroke fragment weight"); + new_stroke->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * new_stroke->totpoints, + "gp delete stroke fragment weight"); memcpy(new_stroke->dvert, gps->dvert + island->start_idx, sizeof(MDeformVert) * new_stroke->totpoints); @@ -3013,7 +3028,7 @@ bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(bGPdata *gpd, MDeformVert *dvert_src = &gps->dvert[e]; MDeformVert *dvert_dst = &new_stroke->dvert[i]; if (dvert_src->dw) { - dvert_dst->dw = MEM_dupallocN(dvert_src->dw); + dvert_dst->dw = (MDeformWeight *)MEM_dupallocN(dvert_src->dw); } e++; } @@ -3047,7 +3062,7 @@ bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(bGPdata *gpd, /* Add new stroke to the frame or delete if below limit */ if ((limit > 0) && (new_stroke->totpoints <= limit)) { if (gps_first == new_stroke) { - gps_first = NULL; + gps_first = nullptr; } BKE_gpencil_free_stroke(new_stroke); } @@ -3064,7 +3079,7 @@ bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(bGPdata *gpd, } } /* if cyclic, need to join last stroke with first stroke */ - if ((is_cyclic) && (gps_first != NULL) && (gps_first != new_stroke)) { + if ((is_cyclic) && (gps_first != nullptr) && (gps_first != new_stroke)) { gpencil_stroke_join_islands(gpd, gpf, gps_first, new_stroke); } } @@ -3086,13 +3101,13 @@ void BKE_gpencil_curve_delete_tagged_points(bGPdata *gpd, bGPDcurve *gpc, int tag_flags) { - if (gpc == NULL) { + if (gpc == nullptr) { return; } const bool is_cyclic = gps->flag & GP_STROKE_CYCLIC; const int idx_last = gpc->tot_curve_points - 1; - bGPDstroke *gps_first = NULL; - bGPDstroke *gps_last = NULL; + bGPDstroke *gps_first = nullptr; + bGPDstroke *gps_last = nullptr; int idx_start = 0; int idx_end = 0; @@ -3123,11 +3138,11 @@ void BKE_gpencil_curve_delete_tagged_points(bGPdata *gpd, } bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, false, false); - new_stroke->points = NULL; + new_stroke->points = nullptr; new_stroke->flag &= ~GP_STROKE_CYCLIC; new_stroke->editcurve = BKE_gpencil_stroke_editcurve_new(island_length); - if (gps_first == NULL) { + if (gps_first == nullptr) { gps_first = new_stroke; } @@ -3155,15 +3170,15 @@ void BKE_gpencil_curve_delete_tagged_points(bGPdata *gpd, } /* join first and last stroke if cyclic */ - if (is_cyclic && gps_first != NULL && gps_last != NULL && gps_first != gps_last) { + if (is_cyclic && gps_first != nullptr && gps_last != nullptr && gps_first != gps_last) { bGPDcurve *gpc_first = gps_first->editcurve; bGPDcurve *gpc_last = gps_last->editcurve; int first_tot_points = gpc_first->tot_curve_points; int old_tot_points = gpc_last->tot_curve_points; gpc_last->tot_curve_points = first_tot_points + old_tot_points; - gpc_last->curve_points = MEM_recallocN(gpc_last->curve_points, - sizeof(bGPDcurve_point) * gpc_last->tot_curve_points); + gpc_last->curve_points = (bGPDcurve_point *)MEM_recallocN( + gpc_last->curve_points, sizeof(bGPDcurve_point) * gpc_last->tot_curve_points); /* copy data from first to last */ memcpy(gpc_last->curve_points + old_tot_points, gpc_first->curve_points, @@ -3196,14 +3211,16 @@ static void gpencil_stroke_copy_point(bGPDstroke *gps, { bGPDspoint *newpoint; - gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); - if (gps->dvert != NULL) { - gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (gps->totpoints + 1)); + gps->points = (bGPDspoint *)MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); + if (gps->dvert != nullptr) { + gps->dvert = (MDeformVert *)MEM_reallocN(gps->dvert, + sizeof(MDeformVert) * (gps->totpoints + 1)); } else { /* If destination has weight add weight to origin. */ - if (dvert != NULL) { - gps->dvert = MEM_callocN(sizeof(MDeformVert) * (gps->totpoints + 1), __func__); + if (dvert != nullptr) { + gps->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * (gps->totpoints + 1), + __func__); } } @@ -3219,16 +3236,16 @@ static void gpencil_stroke_copy_point(bGPDstroke *gps, newpoint->time = point->time + deltatime; copy_v4_v4(newpoint->vert_color, point->vert_color); - if (gps->dvert != NULL) { + if (gps->dvert != nullptr) { MDeformVert *newdvert = &gps->dvert[gps->totpoints - 1]; - if (dvert != NULL) { + if (dvert != nullptr) { newdvert->totweight = dvert->totweight; - newdvert->dw = MEM_dupallocN(dvert->dw); + newdvert->dw = (MDeformWeight *)MEM_dupallocN(dvert->dw); } else { newdvert->totweight = 0; - newdvert->dw = NULL; + newdvert->dw = nullptr; } } } @@ -3246,7 +3263,7 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a, float deltatime = 0.0f; /* sanity checks */ - if (ELEM(NULL, gps_a, gps_b)) { + if (ELEM(nullptr, gps_a, gps_b)) { return; } @@ -3308,11 +3325,11 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a, point = gps_a->points[gps_a->totpoints - 1]; deltatime = point.time; - gpencil_stroke_copy_point(gps_a, NULL, &point, delta, 0.0f, 0.0f, 0.0f); + gpencil_stroke_copy_point(gps_a, nullptr, &point, delta, 0.0f, 0.0f, 0.0f); /* 2nd: add one head point to finish invisible area */ point = gps_b->points[0]; - gpencil_stroke_copy_point(gps_a, NULL, &point, delta, 0.0f, 0.0f, deltatime); + gpencil_stroke_copy_point(gps_a, nullptr, &point, delta, 0.0f, 0.0f, deltatime); } const float ratio = (fit_thickness && gps_a->thickness > 0.0f) ? @@ -3321,7 +3338,7 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a, /* 3rd: add all points */ for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) { - MDeformVert *dvert = (gps_b->dvert) ? &gps_b->dvert[i] : NULL; + MDeformVert *dvert = (gps_b->dvert) ? &gps_b->dvert[i] : nullptr; gpencil_stroke_copy_point( gps_a, dvert, pt, delta, pt->pressure * ratio, pt->strength, deltatime); } @@ -3340,16 +3357,16 @@ void BKE_gpencil_stroke_copy_to_keyframes( if (gpf->framenum != cfra) { bGPDframe *gpf_new = BKE_gpencil_layer_frame_find(gpl, cfra); - if (gpf_new == NULL) { + if (gpf_new == nullptr) { gpf_new = BKE_gpencil_frame_addnew(gpl, cfra); } - if (gpf_new == NULL) { + if (gpf_new == nullptr) { continue; } bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(gps, true, true); - if (gps_new == NULL) { + if (gps_new == nullptr) { continue; } @@ -3363,38 +3380,38 @@ void BKE_gpencil_stroke_copy_to_keyframes( } /* Free hash table. */ - BLI_ghash_free(frame_list, NULL, NULL); + BLI_ghash_free(frame_list, nullptr, nullptr); } /* Stroke Uniform Subdivide ------------------------------------- */ -typedef struct tSamplePoint { +struct tSamplePoint { struct tSamplePoint *next, *prev; float x, y, z; float pressure, strength, time; float vertex_color[4]; struct MDeformWeight *dw; int totweight; -} tSamplePoint; +}; -typedef struct tSampleEdge { +struct tSampleEdge { float length_sq; tSamplePoint *from; tSamplePoint *to; -} tSampleEdge; +}; /* Helper: creates a tSamplePoint from a bGPDspoint and (optionally) a MDeformVert. */ static tSamplePoint *new_sample_point_from_gp_point(const bGPDspoint *pt, const MDeformVert *dvert) { - tSamplePoint *new_pt = MEM_callocN(sizeof(tSamplePoint), __func__); + tSamplePoint *new_pt = (tSamplePoint *)MEM_callocN(sizeof(tSamplePoint), __func__); copy_v3_v3(&new_pt->x, &pt->x); new_pt->pressure = pt->pressure; new_pt->strength = pt->strength; new_pt->time = pt->time; copy_v4_v4((float *)&new_pt->vertex_color, (float *)&pt->vert_color); - if (dvert != NULL) { + if (dvert != nullptr) { new_pt->totweight = dvert->totweight; - new_pt->dw = MEM_callocN(sizeof(MDeformWeight) * new_pt->totweight, __func__); + new_pt->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * new_pt->totweight, __func__); for (uint i = 0; i < new_pt->totweight; ++i) { MDeformWeight *dw = &new_pt->dw[i]; MDeformWeight *dw_from = &dvert->dw[i]; @@ -3409,7 +3426,7 @@ static tSamplePoint *new_sample_point_from_gp_point(const bGPDspoint *pt, const * the edge. */ static tSampleEdge *new_sample_edge_from_sample_points(tSamplePoint *from, tSamplePoint *to) { - tSampleEdge *new_edge = MEM_callocN(sizeof(tSampleEdge), __func__); + tSampleEdge *new_edge = (tSampleEdge *)MEM_callocN(sizeof(tSampleEdge), __func__); new_edge->from = from; new_edge->to = to; new_edge->length_sq = len_squared_v3v3(&from->x, &to->x); @@ -3431,27 +3448,27 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd, const bool select) { /* Stroke needs at least two points and strictly less points than the target number. */ - if (gps == NULL || gps->totpoints < 2 || gps->totpoints >= target_number) { + if (gps == nullptr || gps->totpoints < 2 || gps->totpoints >= target_number) { return; } const int totpoints = gps->totpoints; - const bool has_dverts = (gps->dvert != NULL); + const bool has_dverts = (gps->dvert != nullptr); const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC); - ListBase points = {NULL, NULL}; + ListBase points = {nullptr, nullptr}; Heap *edges = BLI_heap_new(); /* Add all points into list. */ for (uint32_t i = 0; i < totpoints; ++i) { bGPDspoint *pt = &gps->points[i]; - MDeformVert *dvert = has_dverts ? &gps->dvert[i] : NULL; + MDeformVert *dvert = has_dverts ? &gps->dvert[i] : nullptr; tSamplePoint *sp = new_sample_point_from_gp_point(pt, dvert); BLI_addtail(&points, sp); } /* Iterate over edges and insert them into the heap. */ - for (tSamplePoint *pt = ((tSamplePoint *)points.first)->next; pt != NULL; pt = pt->next) { + for (tSamplePoint *pt = ((tSamplePoint *)points.first)->next; pt != nullptr; pt = pt->next) { tSampleEdge *se = new_sample_edge_from_sample_points(pt->prev, pt); /* BLI_heap is a min-heap, but we need the largest key to be at the top, so we take the * negative of the squared length. */ @@ -3459,8 +3476,8 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd, } if (is_cyclic) { - tSamplePoint *sp_first = points.first; - tSamplePoint *sp_last = points.last; + tSamplePoint *sp_first = (tSamplePoint *)points.first; + tSamplePoint *sp_last = (tSamplePoint *)points.last; tSampleEdge *se = new_sample_edge_from_sample_points(sp_last, sp_first); BLI_heap_insert(edges, -(se->length_sq), se); } @@ -3469,12 +3486,12 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd, BLI_assert(num_points_needed > 0); while (num_points_needed > 0) { - tSampleEdge *se = BLI_heap_pop_min(edges); + tSampleEdge *se = (tSampleEdge *)BLI_heap_pop_min(edges); tSamplePoint *sp = se->from; tSamplePoint *sp_next = se->to; /* Subdivide the edge. */ - tSamplePoint *new_sp = MEM_callocN(sizeof(tSamplePoint), __func__); + tSamplePoint *new_sp = (tSamplePoint *)MEM_callocN(sizeof(tSamplePoint), __func__); interp_v3_v3v3(&new_sp->x, &sp->x, &sp_next->x, 0.5f); new_sp->pressure = interpf(sp->pressure, sp_next->pressure, 0.5f); new_sp->strength = interpf(sp->strength, sp_next->strength, 0.5f); @@ -3485,7 +3502,8 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd, 0.5f); if (sp->dw && sp_next->dw) { new_sp->totweight = MIN2(sp->totweight, sp_next->totweight); - new_sp->dw = MEM_callocN(sizeof(MDeformWeight) * new_sp->totweight, __func__); + new_sp->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * new_sp->totweight, + __func__); for (uint32_t i = 0; i < new_sp->totweight; ++i) { MDeformWeight *dw = &new_sp->dw[i]; MDeformWeight *dw_from = &sp->dw[i]; @@ -3509,13 +3527,13 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd, BLI_heap_free(edges, (HeapFreeFP)MEM_freeN); gps->totpoints = target_number; - gps->points = MEM_recallocN(gps->points, sizeof(bGPDspoint) * gps->totpoints); + gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(bGPDspoint) * gps->totpoints); if (has_dverts) { - gps->dvert = MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints); + gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints); } /* Convert list back to stroke point array. */ - tSamplePoint *sp = points.first; + tSamplePoint *sp = (tSamplePoint *)points.first; for (uint32_t i = 0; i < gps->totpoints && sp; ++i, sp = sp->next) { bGPDspoint *pt = &gps->points[i]; MDeformVert *dvert = &gps->dvert[i]; @@ -3528,7 +3546,7 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd, if (sp->dw) { dvert->totweight = sp->totweight; - dvert->dw = MEM_callocN(sizeof(MDeformWeight) * dvert->totweight, __func__); + dvert->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dvert->totweight, __func__); for (uint32_t j = 0; j < dvert->totweight; ++j) { MDeformWeight *dw = &dvert->dw[j]; MDeformWeight *dw_from = &sp->dw[j]; @@ -3549,7 +3567,7 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd, /* Free the sample points. Important to use the mutable loop here because we are erasing the list * elements. */ LISTBASE_FOREACH_MUTABLE (tSamplePoint *, temp, &points) { - if (temp->dw != NULL) { + if (temp->dw != nullptr) { MEM_freeN(temp->dw); } MEM_SAFE_FREE(temp); @@ -3601,14 +3619,14 @@ void BKE_gpencil_stroke_from_view_space(RegionView3D *rv3d, /* ----------------------------------------------------------------------------- */ /* Stroke to perimeter */ -typedef struct tPerimeterPoint { +struct tPerimeterPoint { struct tPerimeterPoint *next, *prev; float x, y, z; -} tPerimeterPoint; +}; static tPerimeterPoint *new_perimeter_point(const float pt[3]) { - tPerimeterPoint *new_pt = MEM_callocN(sizeof(tPerimeterPoint), __func__); + tPerimeterPoint *new_pt = (tPerimeterPoint *)MEM_callocN(sizeof(tPerimeterPoint), __func__); copy_v3_v3(&new_pt->x, pt); return new_pt; } @@ -3771,14 +3789,14 @@ static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd, { /* sanity check */ if (gps->totpoints < 1) { - return NULL; + return nullptr; } float defaultpixsize = 1000.0f / gpd->pixfactor; float stroke_radius = ((gps->thickness + gpl->line_change) / defaultpixsize) / 2.0f; - ListBase *perimeter_right_side = MEM_callocN(sizeof(ListBase), __func__); - ListBase *perimeter_left_side = MEM_callocN(sizeof(ListBase), __func__); + ListBase *perimeter_right_side = (ListBase *)MEM_callocN(sizeof(ListBase), __func__); + ListBase *perimeter_left_side = (ListBase *)MEM_callocN(sizeof(ListBase), __func__); int num_perimeter_points = 0; bGPDspoint *first = &gps->points[0]; @@ -4018,7 +4036,7 @@ bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d, const float diff_mat[4][4]) { if (gps->totpoints == 0) { - return NULL; + return nullptr; } bGPDstroke *gps_temp = BKE_gpencil_stroke_duplicate(gps, true, false); const bool cyclic = ((gps_temp->flag & GP_STROKE_CYCLIC) != 0); @@ -4026,8 +4044,8 @@ bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d, /* If Cyclic, add a new point. */ if (cyclic && (gps_temp->totpoints > 1)) { gps_temp->totpoints++; - gps_temp->points = MEM_recallocN(gps_temp->points, - sizeof(*gps_temp->points) * gps_temp->totpoints); + gps_temp->points = (bGPDspoint *)MEM_recallocN( + gps_temp->points, sizeof(*gps_temp->points) * gps_temp->totpoints); bGPDspoint *pt_src = &gps_temp->points[0]; bGPDspoint *pt_dst = &gps_temp->points[gps_temp->totpoints - 1]; copy_v3_v3(&pt_dst->x, &pt_src->x); @@ -4043,7 +4061,7 @@ bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d, gpd, gpl, gps_temp, subdivisions, &num_perimeter_points); if (num_perimeter_points == 0) { - return NULL; + return nullptr; } /* Create new stroke. */ diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index 4d9c9878a67..4db527e5b42 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -257,9 +257,13 @@ bool BKE_gpencil_is_first_lineart_in_stack(const Object *ob, const GpencilModifi return false; } -/* apply time modifiers */ -static int gpencil_time_modifier( - Depsgraph *depsgraph, Scene *scene, Object *ob, bGPDlayer *gpl, int cfra, bool is_render) +/* Get Time modifier frame number. */ +int BKE_gpencil_time_modifier_cfra(Depsgraph *depsgraph, + Scene *scene, + Object *ob, + bGPDlayer *gpl, + const int cfra, + const bool is_render) { bGPdata *gpd = ob->data; const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd); @@ -269,7 +273,7 @@ static int gpencil_time_modifier( if (GPENCIL_MODIFIER_ACTIVE(md, is_render)) { const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type); - if ((GPENCIL_MODIFIER_EDIT(md, is_edit)) && (!is_render)) { + if (GPENCIL_MODIFIER_EDIT(md, is_edit) && (!is_render)) { continue; } @@ -665,7 +669,7 @@ static int gpencil_remap_time_get(Depsgraph *depsgraph, Scene *scene, Object *ob int remap_cfra = cfra_eval; if (time_remap) { - remap_cfra = gpencil_time_modifier(depsgraph, scene, ob, gpl, cfra_eval, is_render); + remap_cfra = BKE_gpencil_time_modifier_cfra(depsgraph, scene, ob, gpl, cfra_eval, is_render); } return remap_cfra; @@ -834,7 +838,7 @@ void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob) if (GPENCIL_MODIFIER_ACTIVE(md, is_render)) { const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type); - if ((GPENCIL_MODIFIER_EDIT(md, is_edit)) && (!is_render)) { + if (GPENCIL_MODIFIER_EDIT(md, is_edit) && (!is_render)) { continue; } diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 9eee6231ebf..f4ba1ff8b92 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -1778,7 +1778,7 @@ static bool do_add_image_extension(char *string, } } else { - BLI_assert(!"Unsupported jp2 codec was specified in im_format->jp2_codec"); + BLI_assert_msg(0, "Unsupported jp2 codec was specified in im_format->jp2_codec"); } } else { @@ -1949,7 +1949,7 @@ void BKE_imbuf_to_image_format(struct ImageFormatData *im_format, const ImBuf *i im_format->jp2_codec = R_IMF_JP2_CODEC_J2K; } else { - BLI_assert(!"Unsupported jp2 codec was specified in file type"); + BLI_assert_msg(0, "Unsupported jp2 codec was specified in file type"); } } #endif @@ -3017,7 +3017,7 @@ void BKE_imbuf_write_prepare(ImBuf *ibuf, const ImageFormatData *imf) ibuf->foptions.flag |= JP2_J2K; } else { - BLI_assert(!"Unsupported jp2 codec was specified in im_format->jp2_codec"); + BLI_assert_msg(0, "Unsupported jp2 codec was specified in im_format->jp2_codec"); } } #endif diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 6cc90f86b4a..0f8c9bad798 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -694,7 +694,7 @@ static bool key_pointer_size(const Key *key, const int mode, int *poinsize, int *poinsize = sizeof(float[KEYELEM_ELEM_SIZE_CURVE]); break; default: - BLI_assert(!"invalid 'key->from' ID type"); + BLI_assert_msg(0, "invalid 'key->from' ID type"); return false; } @@ -806,7 +806,7 @@ static void cp_key(const int start, if (freekref) { MEM_freeN(freekref); } - BLI_assert(!"invalid 'cp[1]'"); + BLI_assert_msg(0, "invalid 'cp[1]'"); return; } @@ -984,7 +984,7 @@ static void key_evaluate_relative(const int start, if (freefrom) { MEM_freeN(freefrom); } - BLI_assert(!"invalid 'cp[1]'"); + BLI_assert_msg(0, "invalid 'cp[1]'"); return; } @@ -1204,7 +1204,7 @@ static void do_key(const int start, if (freek4) { MEM_freeN(freek4); } - BLI_assert(!"invalid 'cp[1]'"); + BLI_assert_msg(0, "invalid 'cp[1]'"); return; } @@ -1317,7 +1317,7 @@ static float *get_weights_array(Object *ob, char *vgroup, WeightsArrayCache *cac if (cache) { if (cache->defgroup_weights == NULL) { - int num_defgroup = BLI_listbase_count(&ob->defbase); + int num_defgroup = BKE_object_defgroup_count(ob); cache->defgroup_weights = MEM_callocN(sizeof(*cache->defgroup_weights) * num_defgroup, "cached defgroup weights"); cache->num_defgroup_weights = num_defgroup; @@ -1695,7 +1695,7 @@ void BKE_keyblock_data_set_with_mat4(Key *key, const float mat[4][4]) { if (key->elemsize != sizeof(float[3])) { - BLI_assert(!"Invalid elemsize"); + BLI_assert_msg(0, "Invalid elemsize"); return; } diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index 1357424d5ff..9875d776d33 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -87,6 +87,8 @@ static void lattice_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const i lattice_dst->key->from = &lattice_dst->id; } + BKE_defgroup_copy_list(&lattice_dst->vertex_group_names, &lattice_src->vertex_group_names); + if (lattice_src->dvert) { int tot = lattice_src->pntsu * lattice_src->pntsv * lattice_src->pntsw; lattice_dst->dvert = MEM_mallocN(sizeof(MDeformVert) * tot, "Lattice MDeformVert"); @@ -103,6 +105,8 @@ static void lattice_free_data(ID *id) BKE_lattice_batch_cache_free(lattice); + BLI_freelistN(&lattice->vertex_group_names); + MEM_SAFE_FREE(lattice->def); if (lattice->dvert) { BKE_defvert_array_free(lattice->dvert, lattice->pntsu * lattice->pntsv * lattice->pntsw); @@ -150,6 +154,7 @@ static void lattice_blend_write(BlendWriter *writer, ID *id, const void *id_addr /* direct data */ BLO_write_struct_array(writer, BPoint, lt->pntsu * lt->pntsv * lt->pntsw, lt->def); + BKE_defbase_blend_write(writer, <->vertex_group_names); BKE_defvert_blend_write(writer, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert); } } @@ -161,6 +166,7 @@ static void lattice_blend_read_data(BlendDataReader *reader, ID *id) BLO_read_data_address(reader, <->dvert); BKE_defvert_blend_read(reader, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert); + BLO_read_list(reader, <->vertex_group_names); lt->editlatt = NULL; lt->batch_cache = NULL; diff --git a/source/blender/blenkernel/intern/lattice_deform.c b/source/blender/blenkernel/intern/lattice_deform.c index bd1972e9f1f..f9437eeaffa 100644 --- a/source/blender/blenkernel/intern/lattice_deform.c +++ b/source/blender/blenkernel/intern/lattice_deform.c @@ -112,7 +112,7 @@ LatticeDeformData *BKE_lattice_deform_data_create(const Object *oblatt, const Ob int defgrp_index = -1; const MDeformVert *dvert = BKE_lattice_deform_verts_get(oblatt); if (lt->vgroup[0] && dvert) { - defgrp_index = BKE_object_defgroup_name_index(oblatt, lt->vgroup); + defgrp_index = BKE_id_defgroup_name_index(<->id, lt->vgroup); if (defgrp_index != -1) { lattice_weights = MEM_malloc_arrayN(sizeof(float), num_points, "lattice_weights"); @@ -364,7 +364,7 @@ static void lattice_deform_coords_impl(const Object *ob_lattice, * We want either a Mesh/Lattice with no derived data, or derived data with deformverts. */ if (defgrp_name && defgrp_name[0] && ob_target && ELEM(ob_target->type, OB_MESH, OB_LATTICE)) { - defgrp_index = BKE_object_defgroup_name_index(ob_target, defgrp_name); + defgrp_index = BKE_id_defgroup_name_index((ID *)ob_target->data, defgrp_name); if (defgrp_index != -1) { /* if there's derived data without deformverts, don't use vgroups */ diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index d49eb0d4da8..730c989abc9 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -737,172 +737,193 @@ int BKE_layer_collection_findindex(ViewLayer *view_layer, const LayerCollection * in at least one layer collection. That list is also synchronized here, and * stores state like selection. */ +static void layer_collection_objects_sync(ViewLayer *view_layer, + LayerCollection *layer, + ListBase *r_lb_new_object_bases, + const short collection_restrict, + const short layer_restrict, + const ushort local_collections_bits) +{ + /* No need to sync objects if the collection is excluded. */ + if ((layer->flag & LAYER_COLLECTION_EXCLUDE) != 0) { + return; + } + + LISTBASE_FOREACH (CollectionObject *, cob, &layer->collection->gobject) { + if (cob->ob == NULL) { + continue; + } + + /* Tag linked object as a weak reference so we keep the object + * base pointer on file load and remember hidden state. */ + id_lib_indirect_weak_link(&cob->ob->id); + + void **base_p; + Base *base; + if (BLI_ghash_ensure_p(view_layer->object_bases_hash, cob->ob, &base_p)) { + /* Move from old base list to new base list. Base might have already + * been moved to the new base list and the first/last test ensure that + * case also works. */ + base = *base_p; + if (!ELEM(base, r_lb_new_object_bases->first, r_lb_new_object_bases->last)) { + BLI_remlink(&view_layer->object_bases, base); + BLI_addtail(r_lb_new_object_bases, base); + } + } + else { + /* Create new base. */ + base = object_base_new(cob->ob); + base->local_collections_bits = local_collections_bits; + *base_p = base; + BLI_addtail(r_lb_new_object_bases, base); + } + + if ((collection_restrict & COLLECTION_RESTRICT_VIEWPORT) == 0) { + base->flag_from_collection |= (BASE_ENABLED_VIEWPORT | BASE_VISIBLE_DEPSGRAPH); + if ((layer_restrict & LAYER_COLLECTION_HIDE) == 0) { + base->flag_from_collection |= BASE_VISIBLE_VIEWLAYER; + } + if (((collection_restrict & COLLECTION_RESTRICT_SELECT) == 0)) { + base->flag_from_collection |= BASE_SELECTABLE; + } + } + + if ((collection_restrict & COLLECTION_RESTRICT_RENDER) == 0) { + base->flag_from_collection |= BASE_ENABLED_RENDER; + } + + /* Holdout and indirect only */ + if (layer->flag & LAYER_COLLECTION_HOLDOUT) { + base->flag_from_collection |= BASE_HOLDOUT; + } + if (layer->flag & LAYER_COLLECTION_INDIRECT_ONLY) { + base->flag_from_collection |= BASE_INDIRECT_ONLY; + } + + layer->runtime_flag |= LAYER_COLLECTION_HAS_OBJECTS; + } +} + static void layer_collection_sync(ViewLayer *view_layer, - const ListBase *lb_collections, - ListBase *lb_layer_collections, - ListBase *new_object_bases, - short parent_exclude, - short parent_restrict, - short parent_layer_restrict, - unsigned short parent_local_collections_bits) + const ListBase *lb_children_collections, + ListBase *r_lb_children_layers, + ListBase *r_lb_new_object_bases, + const short parent_layer_flag, + const short parent_collection_restrict, + const short parent_layer_restrict, + const ushort parent_local_collections_bits) { /* TODO: support recovery after removal of intermediate collections, reordering, .. * For local edits we can make editing operating do the appropriate thing, but for * linking we can only sync after the fact. */ /* Remove layer collections that no longer have a corresponding scene collection. */ - LISTBASE_FOREACH_MUTABLE (LayerCollection *, lc, lb_layer_collections) { - /* Note that ID remap can set lc->collection to NULL when deleting collections. */ - Collection *collection = (lc->collection) ? - BLI_findptr(lb_collections, - lc->collection, - offsetof(CollectionChild, collection)) : - NULL; - - if (!collection) { - if (lc == view_layer->active_collection) { + LISTBASE_FOREACH_MUTABLE (LayerCollection *, child_layer, r_lb_children_layers) { + /* Note that ID remap can set child_layer->collection to NULL when deleting collections. */ + Collection *child_collection = (child_layer->collection != NULL) ? + BLI_findptr(lb_children_collections, + child_layer->collection, + offsetof(CollectionChild, collection)) : + NULL; + + if (child_collection == NULL) { + if (child_layer == view_layer->active_collection) { view_layer->active_collection = NULL; } /* Free recursively. */ - layer_collection_free(view_layer, lc); - BLI_freelinkN(lb_layer_collections, lc); + layer_collection_free(view_layer, child_layer); + BLI_freelinkN(r_lb_children_layers, child_layer); } } /* Add layer collections for any new scene collections, and ensure order is the same. */ - ListBase new_lb_layer = {NULL, NULL}; + ListBase lb_new_children_layers = {NULL, NULL}; - LISTBASE_FOREACH (const CollectionChild *, child, lb_collections) { - Collection *collection = child->collection; - LayerCollection *lc = BLI_findptr( - lb_layer_collections, collection, offsetof(LayerCollection, collection)); + LISTBASE_FOREACH (const CollectionChild *, child, lb_children_collections) { + Collection *child_collection = child->collection; + LayerCollection *child_layer = BLI_findptr( + r_lb_children_layers, child_collection, offsetof(LayerCollection, collection)); - if (lc) { - BLI_remlink(lb_layer_collections, lc); - BLI_addtail(&new_lb_layer, lc); + if (child_layer) { + BLI_remlink(r_lb_children_layers, child_layer); + BLI_addtail(&lb_new_children_layers, child_layer); } else { - lc = layer_collection_add(&new_lb_layer, collection); - lc->flag = parent_exclude; + child_layer = layer_collection_add(&lb_new_children_layers, child_collection); + child_layer->flag = parent_layer_flag; } - unsigned short local_collections_bits = parent_local_collections_bits & - lc->local_collections_bits; + const ushort child_local_collections_bits = parent_local_collections_bits & + child_layer->local_collections_bits; /* Tag linked collection as a weak reference so we keep the layer * collection pointer on file load and remember exclude state. */ - id_lib_indirect_weak_link(&collection->id); + id_lib_indirect_weak_link(&child_collection->id); /* Collection restrict is inherited. */ - short child_restrict = parent_restrict; + short child_collection_restrict = parent_collection_restrict; short child_layer_restrict = parent_layer_restrict; - if (!(collection->flag & COLLECTION_IS_MASTER)) { - child_restrict |= collection->flag; - child_layer_restrict |= lc->flag; + if (!(child_collection->flag & COLLECTION_IS_MASTER)) { + child_collection_restrict |= child_collection->flag; + child_layer_restrict |= child_layer->flag; } /* Sync child collections. */ layer_collection_sync(view_layer, - &collection->children, - &lc->layer_collections, - new_object_bases, - lc->flag, - child_restrict, + &child_collection->children, + &child_layer->layer_collections, + r_lb_new_object_bases, + child_layer->flag, + child_collection_restrict, child_layer_restrict, - local_collections_bits); + child_local_collections_bits); - /* Layer collection exclude is not inherited. */ - lc->runtime_flag = 0; - if (lc->flag & LAYER_COLLECTION_EXCLUDE) { + /* Layer collection exclude is not inherited, we can skip the remaining process, including + * object bases synchronization. */ + child_layer->runtime_flag = 0; + if (child_layer->flag & LAYER_COLLECTION_EXCLUDE) { continue; } /* We separate restrict viewport and visible view layer because a layer collection can be * hidden in the view layer yet (locally) visible in a viewport (if it is not restricted). */ - if (child_restrict & COLLECTION_RESTRICT_VIEWPORT) { - lc->runtime_flag |= LAYER_COLLECTION_RESTRICT_VIEWPORT; + if (child_collection_restrict & COLLECTION_RESTRICT_VIEWPORT) { + child_layer->runtime_flag |= LAYER_COLLECTION_RESTRICT_VIEWPORT; } - if (((lc->runtime_flag & LAYER_COLLECTION_RESTRICT_VIEWPORT) == 0) && + if (((child_layer->runtime_flag & LAYER_COLLECTION_RESTRICT_VIEWPORT) == 0) && ((child_layer_restrict & LAYER_COLLECTION_HIDE) == 0)) { - lc->runtime_flag |= LAYER_COLLECTION_VISIBLE_VIEW_LAYER; + child_layer->runtime_flag |= LAYER_COLLECTION_VISIBLE_VIEW_LAYER; } - /* Sync objects, except if collection was excluded. */ - LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) { - if (cob->ob == NULL) { - continue; - } - - /* Tag linked object as a weak reference so we keep the object - * base pointer on file load and remember hidden state. */ - id_lib_indirect_weak_link(&cob->ob->id); - - void **base_p; - Base *base; - if (BLI_ghash_ensure_p(view_layer->object_bases_hash, cob->ob, &base_p)) { - /* Move from old base list to new base list. Base might have already - * been moved to the new base list and the first/last test ensure that - * case also works. */ - base = *base_p; - if (!ELEM(base, new_object_bases->first, new_object_bases->last)) { - BLI_remlink(&view_layer->object_bases, base); - BLI_addtail(new_object_bases, base); - } - } - else { - /* Create new base. */ - base = object_base_new(cob->ob); - base->local_collections_bits = local_collections_bits; - *base_p = base; - BLI_addtail(new_object_bases, base); - } - - if ((child_restrict & COLLECTION_RESTRICT_VIEWPORT) == 0) { - base->flag_from_collection |= (BASE_ENABLED_VIEWPORT | BASE_VISIBLE_DEPSGRAPH); - if ((child_layer_restrict & LAYER_COLLECTION_HIDE) == 0) { - base->flag_from_collection |= BASE_VISIBLE_VIEWLAYER; - } - if (((child_restrict & COLLECTION_RESTRICT_SELECT) == 0)) { - base->flag_from_collection |= BASE_SELECTABLE; - } - } - - if ((child_restrict & COLLECTION_RESTRICT_RENDER) == 0) { - base->flag_from_collection |= BASE_ENABLED_RENDER; - } - - /* Holdout and indirect only */ - if (lc->flag & LAYER_COLLECTION_HOLDOUT) { - base->flag_from_collection |= BASE_HOLDOUT; - } - if (lc->flag & LAYER_COLLECTION_INDIRECT_ONLY) { - base->flag_from_collection |= BASE_INDIRECT_ONLY; - } - - lc->runtime_flag |= LAYER_COLLECTION_HAS_OBJECTS; - } + layer_collection_objects_sync(view_layer, + child_layer, + r_lb_new_object_bases, + child_collection_restrict, + child_layer_restrict, + child_local_collections_bits); } /* Free potentially remaining unused layer collections in old list. * NOTE: While this does not happen in typical situations, some corner cases (like remapping * several different collections to a single one) can lead to this list having extra unused * items. */ - LISTBASE_FOREACH_MUTABLE (LayerCollection *, lc, lb_layer_collections) { + LISTBASE_FOREACH_MUTABLE (LayerCollection *, lc, r_lb_children_layers) { if (lc == view_layer->active_collection) { view_layer->active_collection = NULL; } /* Free recursively. */ layer_collection_free(view_layer, lc); - BLI_freelinkN(lb_layer_collections, lc); + BLI_freelinkN(r_lb_children_layers, lc); } - BLI_assert(BLI_listbase_is_empty(lb_layer_collections)); + BLI_assert(BLI_listbase_is_empty(r_lb_children_layers)); /* Replace layer collection list with new one. */ - *lb_layer_collections = new_lb_layer; - BLI_assert(BLI_listbase_count(lb_collections) == BLI_listbase_count(lb_layer_collections)); + *r_lb_children_layers = lb_new_children_layers; + BLI_assert(BLI_listbase_count(lb_children_collections) == + BLI_listbase_count(r_lb_children_layers)); } /** diff --git a/source/blender/blenkernel/intern/layer_utils.c b/source/blender/blenkernel/intern/layer_utils.c index 10ab0a06dd0..48179e0c3bf 100644 --- a/source/blender/blenkernel/intern/layer_utils.c +++ b/source/blender/blenkernel/intern/layer_utils.c @@ -23,6 +23,7 @@ #include "BLI_array.h" #include "BKE_collection.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_layer.h" diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 2a7a889520c..aad98eeebab 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -499,7 +499,7 @@ bool BKE_lib_id_make_local(Main *bmain, ID *id, const bool test, const int flags return false; } - BLI_assert(!"IDType Missing IDTypeInfo"); + BLI_assert_msg(0, "IDType Missing IDTypeInfo"); return false; } @@ -603,7 +603,7 @@ ID *BKE_id_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int flag) } } else { - BLI_assert(!"IDType Missing IDTypeInfo"); + BLI_assert_msg(0, "IDType Missing IDTypeInfo"); } /* Update ID refcount, remap pointers to self in new ID. */ @@ -1053,7 +1053,7 @@ void *BKE_libblock_alloc_notest(short type) if (size != 0) { return MEM_callocN(size, name); } - BLI_assert(!"Request to allocate unknown data type"); + BLI_assert_msg(0, "Request to allocate unknown data type"); return NULL; } @@ -1134,7 +1134,7 @@ void BKE_libblock_init_empty(ID *id) return; } - BLI_assert(!"IDType Missing IDTypeInfo"); + BLI_assert_msg(0, "IDType Missing IDTypeInfo"); } /* ********** ID session-wise UUID management. ********** */ diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c index 7ea321b0ee1..a9407860c06 100644 --- a/source/blender/blenkernel/intern/lib_id_delete.c +++ b/source/blender/blenkernel/intern/lib_id_delete.c @@ -83,7 +83,7 @@ void BKE_libblock_free_datablock(ID *id, const int UNUSED(flag)) return; } - BLI_assert(!"IDType Missing IDTypeInfo"); + BLI_assert_msg(0, "IDType Missing IDTypeInfo"); } /** diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index b5384186c69..9871bf5dc83 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -94,7 +94,7 @@ BLI_INLINE IDOverrideLibrary *lib_override_get(Main *bmain, ID *id) if (id_type->owner_get != NULL) { return id_type->owner_get(bmain, id)->override_library; } - BLI_assert(!"IDTypeInfo of liboverride-embedded ID with no owner getter"); + BLI_assert_msg(0, "IDTypeInfo of liboverride-embedded ID with no owner getter"); } return id->override_library; } @@ -2126,7 +2126,7 @@ bool BKE_lib_override_library_property_operation_operands_validate( ATTR_FALLTHROUGH; case IDOVERRIDE_LIBRARY_OP_MULTIPLY: if (ptr_storage == NULL || ptr_storage->data == NULL || prop_storage == NULL) { - BLI_assert(!"Missing data to apply differential override operation."); + BLI_assert_msg(0, "Missing data to apply differential override operation."); return false; } ATTR_FALLTHROUGH; @@ -2137,7 +2137,7 @@ bool BKE_lib_override_library_property_operation_operands_validate( case IDOVERRIDE_LIBRARY_OP_REPLACE: if ((ptr_dst == NULL || ptr_dst->data == NULL || prop_dst == NULL) || (ptr_src == NULL || ptr_src->data == NULL || prop_src == NULL)) { - BLI_assert(!"Missing data to apply override operation."); + BLI_assert_msg(0, "Missing data to apply override operation."); return false; } } diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c index d872ecf7578..b09aed82921 100644 --- a/source/blender/blenkernel/intern/lightprobe.c +++ b/source/blender/blenkernel/intern/lightprobe.c @@ -131,7 +131,7 @@ void BKE_lightprobe_type_set(LightProbe *probe, const short lightprobe_type) probe->attenuation_type = LIGHTPROBE_SHAPE_ELIPSOID; break; default: - BLI_assert(!"LightProbe type not configured."); + BLI_assert_msg(0, "LightProbe type not configured."); break; } } diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index ee71d60f1e3..34dd38164c2 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -342,7 +342,7 @@ MaskSplinePoint *BKE_mask_spline_point_array_from_point(MaskSpline *spline, return spline->points_deform; } - BLI_assert(!"wrong array"); + BLI_assert_msg(0, "wrong array"); return NULL; } @@ -707,7 +707,7 @@ void BKE_mask_point_handle(const MaskSplinePoint *point, copy_v2_v2(r_handle, bezt->vec[2]); } else { - BLI_assert(!"Unknown handle passed to BKE_mask_point_handle"); + BLI_assert_msg(0, "Unknown handle passed to BKE_mask_point_handle"); } } @@ -760,7 +760,7 @@ void BKE_mask_point_set_handle(MaskSplinePoint *point, copy_v2_v2(bezt->vec[2], loc); } else { - BLI_assert(!"unknown handle passed to BKE_mask_point_set_handle"); + BLI_assert_msg(0, "unknown handle passed to BKE_mask_point_set_handle"); } } @@ -1003,7 +1003,7 @@ void BKE_mask_point_select_set_handle(MaskSplinePoint *point, point->bezt.f3 |= SELECT; } else { - BLI_assert(!"Wrong which_handle passed to BKE_mask_point_select_set_handle"); + BLI_assert_msg(0, "Wrong which_handle passed to BKE_mask_point_select_set_handle"); } } else { @@ -1018,7 +1018,7 @@ void BKE_mask_point_select_set_handle(MaskSplinePoint *point, point->bezt.f3 &= ~SELECT; } else { - BLI_assert(!"Wrong which_handle passed to BKE_mask_point_select_set_handle"); + BLI_assert_msg(0, "Wrong which_handle passed to BKE_mask_point_select_set_handle"); } } } diff --git a/source/blender/blenkernel/intern/mask_rasterize.c b/source/blender/blenkernel/intern/mask_rasterize.c index af0047680f2..81c161a4a7d 100644 --- a/source/blender/blenkernel/intern/mask_rasterize.c +++ b/source/blender/blenkernel/intern/mask_rasterize.c @@ -937,7 +937,7 @@ void BKE_maskrasterize_handle_init(MaskRasterHandle *mr_handle, ListBase isect_remedgebase = {NULL, NULL}; /* now we have all the splines */ - face_coords = MEM_mallocN((sizeof(float[3])) * sf_vert_tot, "maskrast_face_coords"); + face_coords = MEM_mallocN(sizeof(float[3]) * sf_vert_tot, "maskrast_face_coords"); /* init bounds */ BLI_rctf_init_minmax(&bounds); diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 828e274c01b..c8bf485dade 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -39,6 +39,7 @@ #include "BLI_ghash.h" #include "BLI_hash.h" #include "BLI_linklist.h" +#include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_string.h" @@ -125,6 +126,8 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int mesh_dst->mat = MEM_dupallocN(mesh_src->mat); + BKE_defgroup_copy_list(&mesh_dst->vertex_group_names, &mesh_src->vertex_group_names); + const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE; CustomData_copy(&mesh_src->vdata, &mesh_dst->vdata, mask.vmask, alloc_type, mesh_dst->totvert); CustomData_copy(&mesh_src->edata, &mesh_dst->edata, mask.emask, alloc_type, mesh_dst->totedge); @@ -155,6 +158,8 @@ static void mesh_free_data(ID *id) { Mesh *mesh = (Mesh *)id; + BLI_freelistN(&mesh->vertex_group_names); + BKE_mesh_runtime_clear_cache(mesh); mesh_clear_geometry(mesh); MEM_SAFE_FREE(mesh->mat); @@ -229,6 +234,8 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address BKE_animdata_blend_write(writer, mesh->adt); } + BKE_defbase_blend_write(writer, &mesh->vertex_group_names); + BLO_write_pointer_array(writer, mesh->totcol, mesh->mat); BLO_write_raw(writer, sizeof(MSelect) * mesh->totselect, mesh->mselect); @@ -288,6 +295,7 @@ static void mesh_blend_read_data(BlendDataReader *reader, ID *id) /* Normally BKE_defvert_blend_read should be called in CustomData_blend_read, * but for backwards compatibility in do_versions to work we do it here. */ BKE_defvert_blend_read(reader, mesh->totvert, mesh->dvert); + BLO_read_list(reader, &mesh->vertex_group_names); CustomData_blend_read(reader, &mesh->vdata, mesh->totvert); CustomData_blend_read(reader, &mesh->edata, mesh->totedge); @@ -304,7 +312,7 @@ static void mesh_blend_read_data(BlendDataReader *reader, ID *id) mesh->totselect = 0; } - if ((BLO_read_requires_endian_switch(reader)) && mesh->tface) { + if (BLO_read_requires_endian_switch(reader) && mesh->tface) { TFace *tf = mesh->tface; for (int i = 0; i < mesh->totface; i++, tf++) { BLI_endian_switch_uint32_array(tf->col, 4); @@ -923,6 +931,8 @@ void BKE_mesh_copy_parameters(Mesh *me_dst, const Mesh *me_src) me_dst->texflag = me_src->texflag; copy_v3_v3(me_dst->loc, me_src->loc); copy_v3_v3(me_dst->size, me_src->size); + + me_dst->vertex_group_active_index = me_src->vertex_group_active_index; } /** @@ -938,6 +948,10 @@ void BKE_mesh_copy_parameters_for_eval(Mesh *me_dst, const Mesh *me_src) BKE_mesh_copy_parameters(me_dst, me_src); + /* Copy vertex group names. */ + BLI_assert(BLI_listbase_is_empty(&me_dst->vertex_group_names)); + BKE_defgroup_copy_list(&me_dst->vertex_group_names, &me_src->vertex_group_names); + /* Copy materials. */ if (me_dst->mat != NULL) { MEM_freeN(me_dst->mat); diff --git a/source/blender/blenkernel/intern/multires_inline.h b/source/blender/blenkernel/intern/multires_inline.h index e85aa12781e..f88b5dd3143 100644 --- a/source/blender/blenkernel/intern/multires_inline.h +++ b/source/blender/blenkernel/intern/multires_inline.h @@ -53,7 +53,7 @@ BLI_INLINE void BKE_multires_construct_tangent_matrix(float tangent_matrix[3][3] mul_v3_fl(tangent_matrix[0], -1.0f); } else { - BLI_assert(!"Unhandled corner index"); + BLI_assert_msg(0, "Unhandled corner index"); } cross_v3_v3v3(tangent_matrix[2], dPdu, dPdv); normalize_v3(tangent_matrix[0]); diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.c b/source/blender/blenkernel/intern/multires_unsubdivide.c index 275c9c3e01b..2b73be61259 100644 --- a/source/blender/blenkernel/intern/multires_unsubdivide.c +++ b/source/blender/blenkernel/intern/multires_unsubdivide.c @@ -603,7 +603,7 @@ static void write_loop_in_face_grid( step_y[1] = 0; break; default: - BLI_assert(!"Should never happen"); + BLI_assert_msg(0, "Should never happen"); break; } diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 5bed5b1aaad..85d30fc8c8b 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -399,7 +399,7 @@ static ID *node_owner_get(Main *bmain, ID *id) } } - BLI_assert(!"Embedded node tree with no owner. Critical Main inconsistency."); + BLI_assert_msg(0, "Embedded node tree with no owner. Critical Main inconsistency."); return nullptr; } @@ -506,7 +506,7 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) if (node->storage) { /* could be handlerized at some point, now only 1 exception still */ - if ((ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY)) && + if (ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY) && ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB)) { BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage); } @@ -3143,7 +3143,7 @@ static void free_localized_node_groups(bNodeTree *ntree) } LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if ((ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) && node->id) { + if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP) && node->id) { bNodeTree *ngroup = (bNodeTree *)node->id; ntreeFreeTree(ngroup); MEM_freeN(ngroup); @@ -3335,7 +3335,7 @@ bNodeTree *ntreeLocalize(bNodeTree *ntree) ltree->id.tag |= LIB_TAG_LOCALIZED; LISTBASE_FOREACH (bNode *, node, <ree->nodes) { - if ((ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) && node->id) { + if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP) && node->id) { node->id = (ID *)ntreeLocalize((bNodeTree *)node->id); } } @@ -5112,6 +5112,7 @@ static void registerGeometryNodes() register_node_type_geo_curve_primitive_circle(); register_node_type_geo_curve_primitive_line(); register_node_type_geo_curve_primitive_quadratic_bezier(); + register_node_type_geo_curve_primitive_quadrilateral(); register_node_type_geo_curve_primitive_spiral(); register_node_type_geo_curve_primitive_star(); register_node_type_geo_curve_resample(); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index d8b9b851865..941db80b76c 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -205,7 +205,8 @@ static void object_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const in } else if (ob_dst->mat != NULL || ob_dst->matbits != NULL) { /* This shall not be needed, but better be safe than sorry. */ - BLI_assert(!"Object copy: non-NULL material pointers with zero counter, should not happen."); + BLI_assert_msg( + 0, "Object copy: non-NULL material pointers with zero counter, should not happen."); ob_dst->mat = NULL; ob_dst->matbits = NULL; } @@ -234,7 +235,7 @@ static void object_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const in BKE_pose_rebuild(bmain, ob_dst, ob_dst->data, do_pose_id_user); } } - BKE_defgroup_copy_list(&ob_dst->defbase, &ob_src->defbase); + BKE_object_facemap_copy_list(&ob_dst->fmaps, &ob_src->fmaps); BKE_constraints_copy_ex(&ob_dst->constraints, &ob_src->constraints, flag_subdata, true); @@ -285,7 +286,6 @@ static void object_free_data(ID *id) MEM_SAFE_FREE(ob->iuser); MEM_SAFE_FREE(ob->runtime.bb); - BLI_freelistN(&ob->defbase); BLI_freelistN(&ob->fmaps); if (ob->pose) { BKE_pose_free_ex(ob->pose, false); @@ -510,13 +510,6 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) } } -static void write_defgroups(BlendWriter *writer, ListBase *defbase) -{ - LISTBASE_FOREACH (bDeformGroup *, defgroup, defbase) { - BLO_write_struct(writer, bDeformGroup, defgroup); - } -} - static void write_fmaps(BlendWriter *writer, ListBase *fbase) { LISTBASE_FOREACH (bFaceMap *, fmap, fbase) { @@ -561,7 +554,6 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre } BKE_pose_blend_write(writer, ob->pose, arm); - write_defgroups(writer, &ob->defbase); write_fmaps(writer, &ob->fmaps); BKE_constraint_blend_write(writer, &ob->constraints); animviz_motionpath_blend_write(writer, ob->mpath); @@ -644,7 +636,9 @@ static void object_blend_read_data(BlendDataReader *reader, ID *id) animviz_motionpath_blend_read_data(reader, ob->mpath); } + /* Only for versioning, vertex group names are now stored on object data. */ BLO_read_list(reader, &ob->defbase); + BLO_read_list(reader, &ob->fmaps); /* XXX deprecated - old animation system <<< */ direct_link_nlastrips(reader, &ob->nlastrips); @@ -1539,7 +1533,8 @@ bool BKE_object_modifier_stack_copy(Object *ob_dst, const int flag_subdata) { if ((ob_dst->type == OB_GPENCIL) != (ob_src->type == OB_GPENCIL)) { - BLI_assert(!"Trying to copy a modifier stack between a GPencil object and another type."); + BLI_assert_msg(0, + "Trying to copy a modifier stack between a GPencil object and another type."); return false; } @@ -2901,9 +2896,6 @@ void BKE_object_make_proxy(Main *bmain, Object *ob, Object *target, Object *cob) ob->data = target->data; id_us_plus((ID *)ob->data); /* ensures lib data becomes LIB_TAG_EXTERN */ - /* copy vertex groups */ - BKE_defgroup_copy_list(&ob->defbase, &target->defbase); - /* copy material and index information */ ob->actcol = ob->totcol = 0; if (ob->mat) { @@ -3345,7 +3337,7 @@ static void give_parvert(Object *par, int nr, float vec[3]) } BLI_mutex_unlock(&vparent_lock); #else - BLI_assert(!"Not safe for threading"); + BLI_assert_msg(0, "Not safe for threading"); BM_mesh_elem_table_ensure(em->bm, BM_VERT); #endif } diff --git a/source/blender/blenkernel/intern/object_deform.c b/source/blender/blenkernel/intern/object_deform.c index 1e7624d0d7d..c69326a23c6 100644 --- a/source/blender/blenkernel/intern/object_deform.c +++ b/source/blender/blenkernel/intern/object_deform.c @@ -33,6 +33,7 @@ #include "DNA_armature_types.h" #include "DNA_cloth_types.h" #include "DNA_curve_types.h" +#include "DNA_gpencil_types.h" #include "DNA_lattice_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -123,8 +124,7 @@ bDeformGroup *BKE_object_defgroup_add_name(Object *ob, const char *name) } defgroup = BKE_object_defgroup_new(ob, name); - - ob->actdef = BLI_listbase_count(&ob->defbase); + BKE_object_defgroup_active_index_set(ob, BKE_object_defgroup_count(ob)); return defgroup; } @@ -171,7 +171,8 @@ MDeformVert *BKE_object_defgroup_data_create(ID *id) bool BKE_object_defgroup_clear(Object *ob, bDeformGroup *dg, const bool use_selection) { MDeformVert *dv; - const int def_nr = BLI_findindex(&ob->defbase, dg); + const ListBase *defbase = BKE_object_defgroup_list(ob); + const int def_nr = BLI_findindex(defbase, dg); bool changed = false; if (ob->type == OB_MESH) { @@ -249,7 +250,9 @@ bool BKE_object_defgroup_clear_all(Object *ob, const bool use_selection) bDeformGroup *dg; bool changed = false; - for (dg = ob->defbase.first; dg; dg = dg->next) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + + for (dg = defbase->first; dg; dg = dg->next) { if (BKE_object_defgroup_clear(ob, dg, use_selection)) { changed = true; } @@ -265,7 +268,7 @@ bool BKE_object_defgroup_clear_all(Object *ob, const bool use_selection) static void object_defgroup_remove_update_users(Object *ob, const int idx) { - int i, defbase_tot = BLI_listbase_count(&ob->defbase) + 1; + int i, defbase_tot = BKE_object_defgroup_count(ob) + 1; int *map = MEM_mallocN(sizeof(int) * defbase_tot, "vgroup del"); map[idx] = map[0] = 0; @@ -285,15 +288,18 @@ static void object_defgroup_remove_common(Object *ob, bDeformGroup *dg, const in object_defgroup_remove_update_users(ob, def_nr + 1); /* Remove the group */ - BLI_freelinkN(&ob->defbase, dg); + ListBase *defbase = BKE_object_defgroup_list_mutable(ob); + + BLI_freelinkN(defbase, dg); /* Update the active deform index if necessary */ - if (ob->actdef > def_nr) { - ob->actdef--; + const int active_index = BKE_object_defgroup_active_index_get(ob); + if (active_index > def_nr) { + BKE_object_defgroup_active_index_set(ob, active_index - 1); } /* remove all dverts */ - if (BLI_listbase_is_empty(&ob->defbase)) { + if (BLI_listbase_is_empty(defbase)) { if (ob->type == OB_MESH) { Mesh *me = ob->data; CustomData_free_layer_active(&me->vdata, CD_MDEFORMVERT, me->totvert); @@ -307,8 +313,9 @@ static void object_defgroup_remove_common(Object *ob, bDeformGroup *dg, const in } } } - else if (ob->actdef < 1) { /* Keep a valid active index if we still have some vgroups. */ - ob->actdef = 1; + else if (BKE_object_defgroup_active_index_get(ob) < 1) { + /* Keep a valid active index if we still have some vgroups. */ + BKE_object_defgroup_active_index_set(ob, 1); } } @@ -316,7 +323,9 @@ static void object_defgroup_remove_object_mode(Object *ob, bDeformGroup *dg) { MDeformVert *dvert_array = NULL; int dvert_tot = 0; - const int def_nr = BLI_findindex(&ob->defbase, dg); + const ListBase *defbase = BKE_object_defgroup_list(ob); + + const int def_nr = BLI_findindex(defbase, dg); BLI_assert(def_nr != -1); @@ -347,7 +356,8 @@ static void object_defgroup_remove_object_mode(Object *ob, bDeformGroup *dg) static void object_defgroup_remove_edit_mode(Object *ob, bDeformGroup *dg) { int i; - const int def_nr = BLI_findindex(&ob->defbase, dg); + const ListBase *defbase = BKE_object_defgroup_list(ob); + const int def_nr = BLI_findindex(defbase, dg); BLI_assert(def_nr != -1); @@ -425,7 +435,9 @@ void BKE_object_defgroup_remove(Object *ob, bDeformGroup *defgroup) */ void BKE_object_defgroup_remove_all_ex(struct Object *ob, bool only_unlocked) { - bDeformGroup *dg = (bDeformGroup *)ob->defbase.first; + ListBase *defbase = BKE_object_defgroup_list_mutable(ob); + + bDeformGroup *dg = (bDeformGroup *)defbase->first; const bool edit_mode = BKE_object_is_in_editmode_vgroup(ob); if (dg) { @@ -444,7 +456,7 @@ void BKE_object_defgroup_remove_all_ex(struct Object *ob, bool only_unlocked) dg = next_dg; } } - else { /* ob->defbase is empty... */ + else { /* defbase is empty... */ /* remove all dverts */ if (ob->type == OB_MESH) { Mesh *me = ob->data; @@ -459,7 +471,7 @@ void BKE_object_defgroup_remove_all_ex(struct Object *ob, bool only_unlocked) } } /* Fix counters/indices */ - ob->actdef = 0; + BKE_object_defgroup_active_index_set(ob, 0); } } @@ -478,20 +490,23 @@ void BKE_object_defgroup_remove_all(struct Object *ob) */ int *BKE_object_defgroup_index_map_create(Object *ob_src, Object *ob_dst, int *r_map_len) { + const ListBase *src_defbase = BKE_object_defgroup_list(ob_src); + const ListBase *dst_defbase = BKE_object_defgroup_list(ob_dst); + /* Build src to merged mapping of vgroup indices. */ - if (BLI_listbase_is_empty(&ob_src->defbase) || BLI_listbase_is_empty(&ob_dst->defbase)) { + if (BLI_listbase_is_empty(src_defbase) || BLI_listbase_is_empty(dst_defbase)) { *r_map_len = 0; return NULL; } bDeformGroup *dg_src; - *r_map_len = BLI_listbase_count(&ob_src->defbase); + *r_map_len = BLI_listbase_count(src_defbase); int *vgroup_index_map = MEM_malloc_arrayN( *r_map_len, sizeof(*vgroup_index_map), "defgroup index map create"); bool is_vgroup_remap_needed = false; int i; - for (dg_src = ob_src->defbase.first, i = 0; dg_src; dg_src = dg_src->next, i++) { + for (dg_src = src_defbase->first, i = 0; dg_src; dg_src = dg_src->next, i++) { vgroup_index_map[i] = BKE_object_defgroup_name_index(ob_dst, dg_src->name); is_vgroup_remap_needed = is_vgroup_remap_needed || (vgroup_index_map[i] != i); } @@ -576,17 +591,17 @@ bool BKE_object_defgroup_array_get(ID *id, MDeformVert **dvert_arr, int *dvert_t /** * gets the status of "flag" for each bDeformGroup - * in ob->defbase and returns an array containing them + * in the object data's vertex group list and returns an array containing them */ bool *BKE_object_defgroup_lock_flags_get(Object *ob, const int defbase_tot) { bool is_locked = false; int i; - // int defbase_tot = BLI_listbase_count(&ob->defbase); + ListBase *defbase = BKE_object_defgroup_list_mutable(ob); bool *lock_flags = MEM_mallocN(defbase_tot * sizeof(bool), "defflags"); bDeformGroup *defgroup; - for (i = 0, defgroup = ob->defbase.first; i < defbase_tot && defgroup; + for (i = 0, defgroup = defbase->first; i < defbase_tot && defgroup; defgroup = defgroup->next, i++) { lock_flags[i] = ((defgroup->flag & DG_LOCK_WEIGHT) != 0); is_locked |= lock_flags[i]; @@ -606,17 +621,17 @@ bool *BKE_object_defgroup_validmap_get(Object *ob, const int defbase_tot) bool *defgroup_validmap; GHash *gh; int i, step1 = 1; - // int defbase_tot = BLI_listbase_count(&ob->defbase); + const ListBase *defbase = BKE_object_defgroup_list(ob); VirtualModifierData virtualModifierData; - if (BLI_listbase_is_empty(&ob->defbase)) { + if (BLI_listbase_is_empty(defbase)) { return NULL; } gh = BLI_ghash_str_new_ex(__func__, defbase_tot); /* add all names to a hash table */ - for (dg = ob->defbase.first; dg; dg = dg->next) { + for (dg = defbase->first; dg; dg = dg->next) { BLI_ghash_insert(gh, dg->name, NULL); } @@ -655,7 +670,7 @@ bool *BKE_object_defgroup_validmap_get(Object *ob, const int defbase_tot) defgroup_validmap = MEM_mallocN(sizeof(*defgroup_validmap) * defbase_tot, "wpaint valid map"); /* add all names to a hash table */ - for (dg = ob->defbase.first, i = 0; dg; dg = dg->next, i++) { + for (dg = defbase->first, i = 0; dg; dg = dg->next, i++) { defgroup_validmap[i] = (BLI_ghash_lookup(gh, dg->name) != NULL); } @@ -676,9 +691,11 @@ bool *BKE_object_defgroup_selected_get(Object *ob, int defbase_tot, int *r_dg_fl Object *armob = BKE_object_pose_armature_get(ob); (*r_dg_flags_sel_tot) = 0; + const ListBase *defbase = BKE_object_defgroup_list(ob); + if (armob) { bPose *pose = armob->pose; - for (i = 0, defgroup = ob->defbase.first; i < defbase_tot && defgroup; + for (i = 0, defgroup = defbase->first; i < defbase_tot && defgroup; defgroup = defgroup->next, i++) { bPoseChannel *pchan = BKE_pose_channel_find_name(pose, defgroup->name); if (pchan && (pchan->bone->flag & BONE_SELECTED)) { @@ -774,11 +791,13 @@ void BKE_object_defgroup_mirror_selection(struct Object *ob, bool *dg_flags_sel, int *r_dg_flags_sel_tot) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + bDeformGroup *defgroup; unsigned int i; int i_mirr; - for (i = 0, defgroup = ob->defbase.first; i < defbase_tot && defgroup; + for (i = 0, defgroup = defbase->first; i < defbase_tot && defgroup; defgroup = defgroup->next, i++) { if (dg_selection[i]) { char name_flip[MAXBONENAME]; @@ -804,11 +823,12 @@ bool *BKE_object_defgroup_subset_from_select_type(Object *ob, int *r_subset_count) { bool *defgroup_validmap = NULL; - *r_defgroup_tot = BLI_listbase_count(&ob->defbase); + + *r_defgroup_tot = BKE_object_defgroup_count(ob); switch (subset_type) { case WT_VGROUP_ACTIVE: { - const int def_nr_active = ob->actdef - 1; + const int def_nr_active = BKE_object_defgroup_active_index_get(ob) - 1; defgroup_validmap = MEM_mallocN(*r_defgroup_tot * sizeof(*defgroup_validmap), __func__); memset(defgroup_validmap, false, *r_defgroup_tot * sizeof(*defgroup_validmap)); if ((def_nr_active >= 0) && (def_nr_active < *r_defgroup_tot)) { diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index ab247ef5507..7cdea14e9bd 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -123,7 +123,7 @@ void BKE_object_eval_parent(Depsgraph *depsgraph, Object *ob) void BKE_object_eval_constraints(Depsgraph *depsgraph, Scene *scene, Object *ob) { bConstraintOb *cob; - float ctime = BKE_scene_frame_get(scene); + float ctime = BKE_scene_ctime_get(scene); DEG_debug_print_eval(depsgraph, __func__, ob->id.name, ob); @@ -388,12 +388,31 @@ void BKE_object_batch_cache_dirty_tag(Object *ob) BKE_object_data_batch_cache_dirty_tag(ob->data); } +void BKE_object_data_eval_batch_cache_dirty_tag(Depsgraph *depsgraph, ID *object_data) +{ + DEG_debug_print_eval(depsgraph, __func__, object_data->name, object_data); + BKE_object_data_batch_cache_dirty_tag(object_data); +} + +void BKE_object_data_eval_batch_cache_deform_tag(Depsgraph *depsgraph, ID *object_data) +{ + DEG_debug_print_eval(depsgraph, __func__, object_data->name, object_data); + switch (GS(object_data->name)) { + case ID_ME: + BKE_mesh_batch_cache_dirty_tag((Mesh *)object_data, BKE_MESH_BATCH_DIRTY_DEFORM); + break; + default: + /* Only mesh is currently supported. Fallback to dirty all for other datablocks types. */ + BKE_object_data_batch_cache_dirty_tag(object_data); + break; + } +} + void BKE_object_eval_uber_data(Depsgraph *depsgraph, Scene *scene, Object *ob) { DEG_debug_print_eval(depsgraph, __func__, ob->id.name, ob); BLI_assert(ob->type != OB_ARMATURE); BKE_object_handle_data_update(depsgraph, scene, ob); - BKE_object_batch_cache_dirty_tag(ob); } void BKE_object_eval_ptcache_reset(Depsgraph *depsgraph, Scene *scene, Object *object) diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 7504fbeed19..db1fbb56125 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -3961,7 +3961,7 @@ static ModifierData *object_add_or_copy_particle_system( psys->totpart = 0; psys->flag = PSYS_CURRENT; if (scene != NULL) { - psys->cfra = BKE_scene_frame_to_ctime(scene, CFRA + 1); + psys->cfra = BKE_scene_frame_to_ctime(scene, scene->r.cfra + 1); } DEG_relations_tag_update(bmain); diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index a33c4f414fa..1e0b547e778 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -3025,7 +3025,7 @@ float (*BKE_pbvh_vert_coords_alloc(PBVH *pbvh))[3] void BKE_pbvh_vert_coords_apply(PBVH *pbvh, const float (*vertCos)[3], const int totvert) { if (totvert != pbvh->totvert) { - BLI_assert(!"PBVH: Given deforming vcos number does not natch PBVH vertex number!"); + BLI_assert_msg(0, "PBVH: Given deforming vcos number does not natch PBVH vertex number!"); return; } diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 90018e64f78..9ed5b0230e6 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -2807,8 +2807,8 @@ void BKE_ptcache_id_time( cache = pid->cache; if (timescale) { - time = BKE_scene_frame_get(scene); - nexttime = BKE_scene_frame_to_ctime(scene, CFRA + 1.0f); + time = BKE_scene_ctime_get(scene); + nexttime = BKE_scene_frame_to_ctime(scene, scene->r.cfra + 1); *timescale = MAX2(nexttime - time, 0.0f); } diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 9c6610f88fd..328c54fc21b 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -367,7 +367,7 @@ static Mesh *rigidbody_get_mesh(Object *ob) } /* Just return something sensible so that at least Blender won't crash. */ - BLI_assert(!"Unknown mesh source"); + BLI_assert_msg(0, "Unknown mesh source"); return BKE_object_get_evaluated_mesh(ob); } @@ -1814,8 +1814,9 @@ static void rigidbody_update_simulation(Depsgraph *depsgraph, /* TODO: remove this whole block once we are sure we never get NULL rbo here anymore. */ /* This cannot be done in CoW evaluation context anymore... */ if (rbo == NULL) { - BLI_assert(!"CoW object part of RBW object collection without RB object data, " - "should not happen.\n"); + BLI_assert_msg(0, + "CoW object part of RBW object collection without RB object data, " + "should not happen.\n"); /* Since this object is included in the sim group but doesn't have * rigid body settings (perhaps it was added manually), add! * - assume object to be active? That is the default for newly added settings... @@ -1871,8 +1872,9 @@ static void rigidbody_update_simulation(Depsgraph *depsgraph, /* TODO: remove this whole block once we are sure we never get NULL rbo here anymore. */ /* This cannot be done in CoW evaluation context anymore... */ if (rbc == NULL) { - BLI_assert(!"CoW object part of RBW constraints collection without RB constraint data, " - "should not happen.\n"); + BLI_assert_msg(0, + "CoW object part of RBW constraints collection without RB constraint data, " + "should not happen.\n"); /* Since this object is included in the group but doesn't have * constraint settings (perhaps it was added manually), add! */ diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index b517101b563..318acfc3892 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -2299,10 +2299,7 @@ Object *BKE_scene_camera_switch_find(Scene *scene) return NULL; } - const int cfra = ((scene->r.images == scene->r.framapto) ? - scene->r.cfra : - (int)(scene->r.cfra * - ((float)scene->r.framapto / (float)scene->r.images))); + const int ctime = (int)BKE_scene_ctime_get(scene); int frame = -(MAXFRAME + 1); int min_frame = MAXFRAME + 1; Object *camera = NULL; @@ -2310,11 +2307,11 @@ Object *BKE_scene_camera_switch_find(Scene *scene) LISTBASE_FOREACH (TimeMarker *, m, &scene->markers) { if (m->camera && (m->camera->restrictflag & OB_RESTRICT_RENDER) == 0) { - if ((m->frame <= cfra) && (m->frame > frame)) { + if ((m->frame <= ctime) && (m->frame > frame)) { camera = m->camera; frame = m->frame; - if (frame == cfra) { + if (frame == ctime) { break; } } @@ -2396,13 +2393,13 @@ const char *BKE_scene_find_last_marker_name(const Scene *scene, int frame) return best_marker ? best_marker->name : NULL; } -int BKE_scene_frame_snap_by_seconds(Scene *scene, double interval_in_seconds, int cfra) +int BKE_scene_frame_snap_by_seconds(Scene *scene, double interval_in_seconds, int frame) { const int fps = round_db_to_int(FPS * interval_in_seconds); - const int second_prev = cfra - mod_i(cfra, fps); + const int second_prev = frame - mod_i(frame, fps); const int second_next = second_prev + fps; - const int delta_prev = cfra - second_prev; - const int delta_next = second_next - cfra; + const int delta_prev = frame - second_prev; + const int delta_next = second_next - frame; return (delta_prev < delta_next) ? second_prev : second_next; } @@ -2444,16 +2441,17 @@ bool BKE_scene_validate_setscene(Main *bmain, Scene *sce) return true; } -/** - * This function is needed to cope with fractional frames, needed for motion blur & physics. - */ -float BKE_scene_frame_get(const Scene *scene) +/* Return fractional frame number taking into account subframes and time + * remapping. This the time value used by animation, modifiers and physics + * evaluation. */ +float BKE_scene_ctime_get(const Scene *scene) { return BKE_scene_frame_to_ctime(scene, scene->r.cfra); } -/* This function is used to obtain arbitrary fractional frames */ -float BKE_scene_frame_to_ctime(const Scene *scene, const float frame) +/* Convert integer frame number to fractional frame number taking into account + * subframes and time remapping. */ +float BKE_scene_frame_to_ctime(const Scene *scene, const int frame) { float ctime = frame; ctime += scene->r.subframe; @@ -2461,13 +2459,18 @@ float BKE_scene_frame_to_ctime(const Scene *scene, const float frame) return ctime; } -/** - * Sets the frame int/float components. - */ -void BKE_scene_frame_set(struct Scene *scene, double cfra) + +/* Get current fractional frame based on frame and subframe. */ +float BKE_scene_frame_get(const Scene *scene) +{ + return scene->r.cfra + scene->r.subframe; +} + +/* Set current frame and subframe based on a fractional frame. */ +void BKE_scene_frame_set(Scene *scene, float frame) { double intpart; - scene->r.subframe = modf(cfra, &intpart); + scene->r.subframe = modf((double)frame, &intpart); scene->r.cfra = (int)intpart; } @@ -2737,8 +2740,8 @@ void BKE_scene_graph_update_for_newframe_ex(Depsgraph *depsgraph, const bool cle * edits from callback are properly taken into account. Doing a time update on those would * lose any possible unkeyed changes made by the handler. */ if (pass == 0) { - const float ctime = BKE_scene_frame_get(scene); - DEG_evaluate_on_framechange(depsgraph, ctime); + const float frame = BKE_scene_frame_get(scene); + DEG_evaluate_on_framechange(depsgraph, frame); } else { DEG_evaluate_on_refresh(depsgraph); diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 73658c3184e..c3885b5dcf7 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -682,19 +682,13 @@ void BKE_area_region_free(SpaceType *st, ARegion *region) BKE_area_region_panels_free(®ion->panels); LISTBASE_FOREACH (uiList *, uilst, ®ion->ui_lists) { - if (uilst->dyn_data) { - uiListDyn *dyn_data = uilst->dyn_data; - if (dyn_data->items_filter_flags) { - MEM_freeN(dyn_data->items_filter_flags); - } - if (dyn_data->items_filter_neworder) { - MEM_freeN(dyn_data->items_filter_neworder); - } - MEM_freeN(dyn_data); + if (uilst->dyn_data && uilst->dyn_data->free_runtime_data_fn) { + uilst->dyn_data->free_runtime_data_fn(uilst); } if (uilst->properties) { IDP_FreeProperty(uilst->properties); } + MEM_SAFE_FREE(uilst->dyn_data); } if (region->gizmo_map != NULL) { diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index c01ca0c617c..e4e2ed94b41 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -2704,8 +2704,8 @@ static void mesh_to_softbody(Object *ob) bp = sb->bpoint; defgroup_index = me->dvert ? (sb->vertgroup - 1) : -1; - defgroup_index_mass = me->dvert ? BKE_object_defgroup_name_index(ob, sb->namedVG_Mass) : -1; - defgroup_index_spring = me->dvert ? BKE_object_defgroup_name_index(ob, sb->namedVG_Spring_K) : + defgroup_index_mass = me->dvert ? BKE_id_defgroup_name_index(&me->id, sb->namedVG_Mass) : -1; + defgroup_index_spring = me->dvert ? BKE_id_defgroup_name_index(&me->id, sb->namedVG_Spring_K) : -1; for (a = 0; a < me->totvert; a++, bp++) { @@ -2934,8 +2934,8 @@ static void lattice_to_softbody(Object *ob) bp = sb->bpoint; defgroup_index = lt->dvert ? (sb->vertgroup - 1) : -1; - defgroup_index_mass = lt->dvert ? BKE_object_defgroup_name_index(ob, sb->namedVG_Mass) : -1; - defgroup_index_spring = lt->dvert ? BKE_object_defgroup_name_index(ob, sb->namedVG_Spring_K) : + defgroup_index_mass = lt->dvert ? BKE_id_defgroup_name_index(<->id, sb->namedVG_Mass) : -1; + defgroup_index_spring = lt->dvert ? BKE_id_defgroup_name_index(<->id, sb->namedVG_Spring_K) : -1; /* same code used as for mesh vertices */ diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index f4a9d328d86..fcb992e1535 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -824,7 +824,7 @@ void BKE_sound_set_scene_sound_pan(void *handle, float pan, char animated) void BKE_sound_update_sequencer(Main *main, bSound *sound) { - BLI_assert(!"is not supposed to be used, is weird function."); + BLI_assert_msg(0, "is not supposed to be used, is weird function."); Scene *scene; diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index 02d26ac715b..b6764f65631 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -333,6 +333,46 @@ void BezierSpline::correct_end_tangents() const } } +/** + * De Casteljau Bezier subdivision. + * \param index: The index of the segment's start control point. + * \param next_index: The index of the control point at the end of the segment. Could be 0, + * if the spline is cyclic. + * \param parameter: The factor along the segment, between 0 and 1. Note that this is used + * directly by the calculation, it doesn't correspond to a portion of the evaluated length. + * + * <pre> + * handle_prev handle_next + * x----------------x + * / \ + * / x---O---x \ + * / result \ + * / \ + * O O + * point_prev point_next + * </pre> + */ +BezierSpline::InsertResult BezierSpline::calculate_segment_insertion(const int index, + const int next_index, + const float parameter) +{ + BLI_assert(parameter <= 1.0f && parameter >= 0.0f); + BLI_assert(next_index == 0 || next_index == index + 1); + const float3 &point_prev = positions_[index]; + const float3 &handle_prev = handle_positions_right_[index]; + const float3 &handle_next = handle_positions_left_[next_index]; + const float3 &point_next = positions_[next_index]; + const float3 center_point = float3::interpolate(handle_prev, handle_next, parameter); + + BezierSpline::InsertResult result; + result.handle_prev = float3::interpolate(point_prev, handle_prev, parameter); + result.handle_next = float3::interpolate(handle_next, point_next, parameter); + result.left_handle = float3::interpolate(result.handle_prev, center_point, parameter); + result.right_handle = float3::interpolate(center_point, result.handle_next, parameter); + result.position = float3::interpolate(result.left_handle, result.right_handle, parameter); + return result; +} + static void bezier_forward_difference_3d(const float3 &point_0, const float3 &point_1, const float3 &point_2, diff --git a/source/blender/blenkernel/intern/studiolight.c b/source/blender/blenkernel/intern/studiolight.c index 4cc2d101b02..dc5162f201e 100644 --- a/source/blender/blenkernel/intern/studiolight.c +++ b/source/blender/blenkernel/intern/studiolight.c @@ -1352,7 +1352,7 @@ static void studiolight_irradiance_preview(uint *icon_buffer, StudioLight *sl) ITER_PIXELS_END; } -void BKE_studiolight_default(SolidLight lights[4], float light_ambient[4]) +void BKE_studiolight_default(SolidLight lights[4], float light_ambient[3]) { copy_v3_fl3(light_ambient, 0.0, 0.0, 0.0); diff --git a/source/blender/blenkernel/intern/subdiv.c b/source/blender/blenkernel/intern/subdiv.c index e6b51c586c3..fd32f52351a 100644 --- a/source/blender/blenkernel/intern/subdiv.c +++ b/source/blender/blenkernel/intern/subdiv.c @@ -68,7 +68,7 @@ eSubdivFVarLinearInterpolation BKE_subdiv_fvar_interpolation_from_uv_smooth(int case SUBSURF_UV_SMOOTH_ALL: return SUBDIV_FVAR_LINEAR_INTERPOLATION_NONE; } - BLI_assert(!"Unknown uv smooth flag"); + BLI_assert_msg(0, "Unknown uv smooth flag"); return SUBDIV_FVAR_LINEAR_INTERPOLATION_ALL; } @@ -81,7 +81,7 @@ eSubdivVtxBoundaryInterpolation BKE_subdiv_vtx_boundary_interpolation_from_subsu case SUBSURF_BOUNDARY_SMOOTH_ALL: return SUBDIV_VTX_BOUNDARY_EDGE_ONLY; } - BLI_assert(!"Unknown boundary smooth flag"); + BLI_assert_msg(0, "Unknown boundary smooth flag"); return SUBDIV_VTX_BOUNDARY_EDGE_ONLY; } diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c index 8b672b2cb49..95f51a72b70 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.c +++ b/source/blender/blenkernel/intern/subdiv_ccg.c @@ -1509,7 +1509,7 @@ static SubdivCCGCoord coord_step_inside_from_boundary(const SubdivCCG *subdiv_cc ++result.y; } else { - BLI_assert(!"non-boundary element given"); + BLI_assert_msg(0, "non-boundary element given"); } return result; } diff --git a/source/blender/blenkernel/intern/subdiv_converter.c b/source/blender/blenkernel/intern/subdiv_converter.c index d5c75503500..39b701da262 100644 --- a/source/blender/blenkernel/intern/subdiv_converter.c +++ b/source/blender/blenkernel/intern/subdiv_converter.c @@ -44,7 +44,7 @@ int BKE_subdiv_converter_vtx_boundary_interpolation_from_settings(const SubdivSe case SUBDIV_VTX_BOUNDARY_EDGE_AND_CORNER: return OSD_VTX_BOUNDARY_EDGE_AND_CORNER; } - BLI_assert(!"Unknown vtx boundary interpolation"); + BLI_assert_msg(0, "Unknown vtx boundary interpolation"); return OSD_VTX_BOUNDARY_EDGE_ONLY; } @@ -65,6 +65,6 @@ int BKE_subdiv_converter_vtx_boundary_interpolation_from_settings(const SubdivSe case SUBDIV_FVAR_LINEAR_INTERPOLATION_ALL: return OSD_FVAR_LINEAR_INTERPOLATION_ALL; } - BLI_assert(!"Unknown fvar linear interpolation"); + BLI_assert_msg(0, "Unknown fvar linear interpolation"); return OSD_FVAR_LINEAR_INTERPOLATION_NONE; } diff --git a/source/blender/blenkernel/intern/subdiv_eval.c b/source/blender/blenkernel/intern/subdiv_eval.c index 693827f99ac..0001eb8a205 100644 --- a/source/blender/blenkernel/intern/subdiv_eval.c +++ b/source/blender/blenkernel/intern/subdiv_eval.c @@ -137,7 +137,7 @@ bool BKE_subdiv_eval_refine_from_mesh(Subdiv *subdiv, { if (subdiv->evaluator == NULL) { /* NOTE: This situation is supposed to be handled by begin(). */ - BLI_assert(!"Is not supposed to happen"); + BLI_assert_msg(0, "Is not supposed to happen"); return false; } /* Set coordinates of base mesh vertices. */ diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index f3d6bc4a6e3..068d048fd08 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -1525,7 +1525,7 @@ MovieTrackingMarker *BKE_tracking_marker_get(MovieTrackingTrack *track, int fram const int num_markers = track->markersnr; if (num_markers == 0) { - BLI_assert(!"Detected degenerated track, should never happen."); + BLI_assert_msg(0, "Detected degenerated track, should never happen."); return NULL; } diff --git a/source/blender/blenkernel/intern/tracking_util.c b/source/blender/blenkernel/intern/tracking_util.c index ea0d92cf78e..16b36e94328 100644 --- a/source/blender/blenkernel/intern/tracking_util.c +++ b/source/blender/blenkernel/intern/tracking_util.c @@ -523,7 +523,7 @@ static void distortion_model_parameters_from_options( /* Libmv returned distortion model which is not known to Blender. This is a logical error in code * and Blender side is to be updated to match Libmv. */ - BLI_assert(!"Unknown distortion model"); + BLI_assert_msg(0, "Unknown distortion model"); } /* Fill in Libmv C-API camera intrinsics options from tracking structure. */ diff --git a/source/blender/blenkernel/intern/undo_system.c b/source/blender/blenkernel/intern/undo_system.c index fe2aa701c63..e524bd254bb 100644 --- a/source/blender/blenkernel/intern/undo_system.c +++ b/source/blender/blenkernel/intern/undo_system.c @@ -717,11 +717,31 @@ eUndoStepDir BKE_undosys_step_calc_direction(const UndoStack *ustack, } } - BLI_assert(!"Target undo step not found, this should not happen and may indicate an undo stack corruption"); + BLI_assert_msg(0, + "Target undo step not found, this should not happen and may indicate an undo " + "stack corruption"); return STEP_INVALID; } /** + * When reading undo steps for undo/redo, + * some extra checks are needed when so the correct undo step is decoded. + */ +static UndoStep *undosys_step_iter_first(UndoStep *us_reference, const eUndoStepDir undo_dir) +{ + if (us_reference->type->flags & UNDOTYPE_FLAG_DECODE_ACTIVE_STEP) { + /* Reading this step means an undo action reads undo twice. + * This should be avoided where possible, however some undo systems require it. + * + * Redo skips the current state as this represents the currently loaded state. */ + return (undo_dir == -1) ? us_reference : us_reference->next; + } + + /* Typical case, skip reading the current undo step. */ + return (undo_dir == -1) ? us_reference->prev : us_reference->next; +} + +/** * Undo/Redo until the given `us_target` step becomes the active (currently loaded) one. * * \note Unless `us_target` is a 'skipped' one and `use_skip` is true, `us_target` will become the @@ -786,15 +806,10 @@ bool BKE_undosys_step_load_data_ex(UndoStack *ustack, us_target->type->name, undo_dir); - /* Undo/Redo steps until we reach given target step (or beyond if it has to be skipped), from - * given reference step. - * - * NOTE: Unlike with redo case, where we can expect current active step to fully reflect current - * data status, in undo case we also do reload the active step. - * FIXME: this feels weak, and should probably not be actually needed? Or should also be done in - * redo case? */ + /* Undo/Redo steps until we reach given target step (or beyond if it has to be skipped), + * from given reference step. */ bool is_processing_extra_skipped_steps = false; - for (UndoStep *us_iter = (undo_dir == -1) ? us_reference : us_reference->next; us_iter != NULL; + for (UndoStep *us_iter = undosys_step_iter_first(us_reference, undo_dir); us_iter != NULL; us_iter = (undo_dir == -1) ? us_iter->prev : us_iter->next) { BLI_assert(us_iter != NULL); diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c index f87f1c0428b..d9f02ce4c75 100644 --- a/source/blender/blenkernel/intern/unit.c +++ b/source/blender/blenkernel/intern/unit.c @@ -833,7 +833,7 @@ static char *find_next_op(const char *str, char *remaining_str, int len_max) return remaining_str + i; } } - BLI_assert(!"String should be NULL terminated"); + BLI_assert_msg(0, "String should be NULL terminated"); return remaining_str + i; } diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index 533107b2bf6..5cac149c503 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -29,6 +29,7 @@ #include "BLT_translation.h" +#include "BKE_asset.h" #include "BKE_global.h" #include "BKE_idprop.h" #include "BKE_idtype.h" @@ -53,6 +54,13 @@ /* -------------------------------------------------------------------- */ +static void workspace_init_data(ID *id) +{ + WorkSpace *workspace = (WorkSpace *)id; + + BKE_asset_library_reference_init_default(&workspace->active_asset_library); +} + static void workspace_free_data(ID *id) { WorkSpace *workspace = (WorkSpace *)id; @@ -180,7 +188,7 @@ IDTypeInfo IDType_ID_WS = { .translation_context = BLT_I18NCONTEXT_ID_WORKSPACE, .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_MAKELOCAL | IDTYPE_FLAGS_NO_ANIMDATA, - .init_data = NULL, + .init_data = workspace_init_data, .copy_data = NULL, .free_data = workspace_free_data, .make_local = NULL, diff --git a/source/blender/blenlib/BLI_assert.h b/source/blender/blenlib/BLI_assert.h index 685f526b4ad..6019f0f3566 100644 --- a/source/blender/blenlib/BLI_assert.h +++ b/source/blender/blenlib/BLI_assert.h @@ -31,6 +31,7 @@ extern "C" { /* Utility functions. */ void _BLI_assert_print_pos(const char *file, const int line, const char *function, const char *id); +void _BLI_assert_print_extra(const char *str); void _BLI_assert_print_backtrace(void); void _BLI_assert_abort(void); void _BLI_assert_unreachable_print(const char *file, const int line, const char *function); @@ -61,8 +62,17 @@ void _BLI_assert_unreachable_print(const char *file, const int line, const char _BLI_ASSERT_ABORT(), \ NULL)) : \ NULL) +/** A version of #BLI_assert() to pass an additional message to be printed on failure. */ +# define BLI_assert_msg(a, msg) \ + (void)((!(a)) ? ((_BLI_assert_print_backtrace(), \ + _BLI_ASSERT_PRINT_POS(a), \ + _BLI_assert_print_extra(msg), \ + _BLI_ASSERT_ABORT(), \ + NULL)) : \ + NULL) #else # define BLI_assert(a) ((void)0) +# define BLI_assert_msg(a, msg) ((void)0) #endif #if defined(__cplusplus) @@ -96,7 +106,7 @@ void _BLI_assert_unreachable_print(const char *file, const int line, const char #define BLI_assert_unreachable() \ { \ _BLI_assert_unreachable_print(__FILE__, __LINE__, __func__); \ - BLI_assert(!"This line of code is marked to be unreachable."); \ + BLI_assert_msg(0, "This line of code is marked to be unreachable."); \ } \ ((void)0) diff --git a/source/blender/blenlib/BLI_memarena.h b/source/blender/blenlib/BLI_memarena.h index d7798f12fcc..b2e05b00735 100644 --- a/source/blender/blenlib/BLI_memarena.h +++ b/source/blender/blenlib/BLI_memarena.h @@ -50,6 +50,8 @@ void *BLI_memarena_alloc(struct MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESU void *BLI_memarena_calloc(struct MemArena *ma, size_t size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_ALLOC_SIZE(2); +void BLI_memarena_merge(MemArena *ma_dst, MemArena *ma_src) ATTR_NONNULL(1, 2); + void BLI_memarena_clear(MemArena *ma) ATTR_NONNULL(1); #ifdef __cplusplus diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h index dbe8ec3dcc0..418db14e2f3 100644 --- a/source/blender/blenlib/BLI_task.h +++ b/source/blender/blenlib/BLI_task.h @@ -129,6 +129,9 @@ typedef struct TaskParallelTLS { typedef void (*TaskParallelRangeFunc)(void *__restrict userdata, const int iter, const TaskParallelTLS *__restrict tls); + +typedef void (*TaskParallelInitFunc)(const void *__restrict userdata, void *__restrict chunk); + typedef void (*TaskParallelReduceFunc)(const void *__restrict userdata, void *__restrict chunk_join, void *__restrict chunk); @@ -151,6 +154,10 @@ typedef struct TaskParallelSettings { /* Function called from calling thread once whole range have been * processed. */ + /* Function called to initialize user data chunk, + * typically to allocate data, freed by `func_free`. + */ + TaskParallelInitFunc func_init; /* Function called to join user data chunk into another, to reduce * the result to the original userdata_chunk memory. * The reduce functions should have no side effects, so that they diff --git a/source/blender/blenlib/intern/BLI_assert.c b/source/blender/blenlib/intern/BLI_assert.c index 887f583242f..cebc6f8957f 100644 --- a/source/blender/blenlib/intern/BLI_assert.c +++ b/source/blender/blenlib/intern/BLI_assert.c @@ -31,6 +31,11 @@ void _BLI_assert_print_pos(const char *file, const int line, const char *functio fprintf(stderr, "BLI_assert failed: %s:%d, %s(), at \'%s\'\n", file, line, function, id); } +void _BLI_assert_print_extra(const char *str) +{ + fprintf(stderr, " %s\n", str); +} + void _BLI_assert_unreachable_print(const char *file, const int line, const char *function) { fprintf(stderr, "Code marked as unreachable has been executed. Please report this as a bug.\n"); diff --git a/source/blender/blenlib/intern/BLI_memarena.c b/source/blender/blenlib/intern/BLI_memarena.c index fc381c22315..0ab27a5adad 100644 --- a/source/blender/blenlib/intern/BLI_memarena.c +++ b/source/blender/blenlib/intern/BLI_memarena.c @@ -45,6 +45,7 @@ # define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) UNUSED_VARS(pool, rzB, is_zeroed) # define VALGRIND_DESTROY_MEMPOOL(pool) UNUSED_VARS(pool) # define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) UNUSED_VARS(pool, addr, size) +# define VALGRIND_MOVE_MEMPOOL(pool_a, pool_b) UNUSED_VARS(pool_a, pool_b) #endif struct MemBuf { @@ -179,6 +180,58 @@ void *BLI_memarena_calloc(MemArena *ma, size_t size) } /** + * Transfer ownership of allocated blocks from `ma_src` into `ma_dst`, + * cleaning the contents of `ma_src`. + * + * \note Useful for multi-threaded tasks that need a thread-local #MemArena + * that is kept after the multi-threaded operation is completed. + * + * \note Avoid accumulating memory pools where possible + * as any unused memory in `ma_src` is wasted every merge. + */ +void BLI_memarena_merge(MemArena *ma_dst, MemArena *ma_src) +{ + /* Memory arenas must be compatible. */ + BLI_assert(ma_dst != ma_src); + BLI_assert(ma_dst->align == ma_src->align); + BLI_assert(ma_dst->use_calloc == ma_src->use_calloc); + BLI_assert(ma_dst->bufsize == ma_src->bufsize); + + if (ma_src->bufs == NULL) { + return; + } + + if (UNLIKELY(ma_dst->bufs == NULL)) { + BLI_assert(ma_dst->curbuf == NULL); + ma_dst->bufs = ma_src->bufs; + ma_dst->curbuf = ma_src->curbuf; + ma_dst->cursize = ma_src->cursize; + } + else { + /* Keep the 'ma_dst->curbuf' for simplicity. + * Insert buffers after the first. */ + if (ma_dst->bufs->next != NULL) { + /* Loop over `ma_src` instead of `ma_dst` since it's likely the destination is larger + * when used for accumulating from multiple sources. */ + struct MemBuf *mb_src = ma_src->bufs; + mb_src = ma_src->bufs; + while (mb_src && mb_src->next) { + mb_src = mb_src->next; + } + mb_src->next = ma_dst->bufs->next; + } + ma_dst->bufs->next = ma_src->bufs; + } + + ma_src->bufs = NULL; + ma_src->curbuf = NULL; + ma_src->cursize = 0; + + VALGRIND_MOVE_MEMPOOL(ma_src, ma_dst); + VALGRIND_CREATE_MEMPOOL(ma_src, 0, false); +} + +/** * Clear for reuse, avoids re-allocation when an arena may * otherwise be free'd and recreated. */ diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c index 934c1e44dc3..523bb300247 100644 --- a/source/blender/blenlib/intern/BLI_mempool.c +++ b/source/blender/blenlib/intern/BLI_mempool.c @@ -522,7 +522,7 @@ void BLI_mempool_free(BLI_mempool *pool, void *addr) } } if (!found) { - BLI_assert(!"Attempt to free data which is not in pool.\n"); + BLI_assert_msg(0, "Attempt to free data which is not in pool.\n"); } } diff --git a/source/blender/blenlib/intern/expr_pylike_eval.c b/source/blender/blenlib/intern/expr_pylike_eval.c index a5d4130cb20..4d1ba190c14 100644 --- a/source/blender/blenlib/intern/expr_pylike_eval.c +++ b/source/blender/blenlib/intern/expr_pylike_eval.c @@ -569,7 +569,7 @@ static int opcode_arg_count(eOpCode code) case OPCODE_FUNC3: return 3; default: - BLI_assert(!"unexpected opcode"); + BLI_assert_msg(0, "unexpected opcode"); return -1; } } diff --git a/source/blender/blenlib/intern/math_color.c b/source/blender/blenlib/intern/math_color.c index 263c508c07c..da97e697f2f 100644 --- a/source/blender/blenlib/intern/math_color.c +++ b/source/blender/blenlib/intern/math_color.c @@ -153,7 +153,7 @@ void rgb_to_ycc(float r, float g, float b, float *r_y, float *r_cb, float *r_cr, cr = (0.5f * sr) - (0.41869f * sg) - (0.08131f * sb) + 128.0f; break; default: - BLI_assert(!"invalid colorspace"); + BLI_assert_msg(0, "invalid colorspace"); break; } diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 8969dd4f6b5..4d0dc43ed1e 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1275,7 +1275,7 @@ void BLI_setenv(const char *env, const char *val) { /* free windows */ -#if (defined(WIN32) || defined(WIN64)) +#if (defined(_WIN32) || defined(_WIN64)) uputenv(env, val); #else diff --git a/source/blender/blenlib/intern/system.c b/source/blender/blenlib/intern/system.c index 87330cf4899..66d0b44cfb3 100644 --- a/source/blender/blenlib/intern/system.c +++ b/source/blender/blenlib/intern/system.c @@ -184,7 +184,7 @@ size_t BLI_system_memory_max_in_megabytes(void) /* Maximum addressable bytes on this platform. * * NOTE: Due to the shift arithmetic this is a half of the memory. */ - const size_t limit_bytes_half = (((size_t)1) << ((sizeof(size_t[8])) - 1)); + const size_t limit_bytes_half = (((size_t)1) << (sizeof(size_t[8]) - 1)); /* Convert it to megabytes and return. */ return (limit_bytes_half >> 20) * 2; } diff --git a/source/blender/blenlib/intern/task_iterator.c b/source/blender/blenlib/intern/task_iterator.c index 0ff408ddb0a..06087869685 100644 --- a/source/blender/blenlib/intern/task_iterator.c +++ b/source/blender/blenlib/intern/task_iterator.c @@ -186,6 +186,9 @@ static void task_parallel_iterator_no_threads(const TaskParallelSettings *settin if (use_userdata_chunk) { userdata_chunk_local = MALLOCA(userdata_chunk_size); memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size); + if (settings->func_init != NULL) { + settings->func_init(state->userdata, userdata_chunk_local); + } } /* Also marking it as non-threaded for the iterator callback. */ @@ -247,6 +250,9 @@ static void task_parallel_iterator_do(const TaskParallelSettings *settings, if (use_userdata_chunk) { userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i); memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size); + if (settings->func_init != NULL) { + settings->func_init(state->userdata, userdata_chunk_local); + } } /* Use this pool's pre-allocated tasks. */ BLI_task_pool_push(task_pool, parallel_iterator_func, userdata_chunk_local, false, NULL); @@ -403,11 +409,7 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool, TaskParallelMempoolFunc func, const TaskParallelSettings *settings) { - TaskPool *task_pool; - ParallelMempoolState state; - int i, num_threads, num_tasks; - - if (BLI_mempool_len(mempool) == 0) { + if (UNLIKELY(BLI_mempool_len(mempool) == 0)) { return; } @@ -422,6 +424,9 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool, if (use_userdata_chunk) { userdata_chunk_local = MALLOCA(userdata_chunk_size); memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size); + if (settings->func_init != NULL) { + settings->func_init(userdata, userdata_chunk_local); + } tls.userdata_chunk = userdata_chunk_local; } @@ -442,14 +447,15 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool, return; } - task_pool = BLI_task_pool_create(&state, TASK_PRIORITY_HIGH); - num_threads = BLI_task_scheduler_num_threads(); + ParallelMempoolState state; + TaskPool *task_pool = BLI_task_pool_create(&state, TASK_PRIORITY_HIGH); + const int num_threads = BLI_task_scheduler_num_threads(); /* The idea here is to prevent creating task for each of the loop iterations * and instead have tasks which are evenly distributed across CPU cores and * pull next item to be crunched using the threaded-aware BLI_mempool_iter. */ - num_tasks = num_threads + 2; + const int num_tasks = num_threads + 2; state.userdata = userdata; state.func = func; @@ -461,10 +467,13 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool, ParallelMempoolTaskData *mempool_iterator_data = mempool_iter_threadsafe_create( mempool, (size_t)num_tasks); - for (i = 0; i < num_tasks; i++) { + for (int 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); + if (settings->func_init != NULL) { + settings->func_init(userdata, userdata_chunk_local); + } } mempool_iterator_data[i].tls.userdata_chunk = userdata_chunk_local; @@ -477,7 +486,7 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool, if (use_userdata_chunk) { if ((settings->func_free != NULL) || (settings->func_reduce != NULL)) { - for (i = 0; i < num_tasks; i++) { + for (int i = 0; i < num_tasks; i++) { if (settings->func_reduce) { settings->func_reduce( userdata, userdata_chunk, mempool_iterator_data[i].tls.userdata_chunk); diff --git a/source/blender/blenlib/intern/task_range.cc b/source/blender/blenlib/intern/task_range.cc index 871d04c1f35..8407be2cb2b 100644 --- a/source/blender/blenlib/intern/task_range.cc +++ b/source/blender/blenlib/intern/task_range.cc @@ -156,7 +156,7 @@ int BLI_task_parallel_thread_id(const TaskParallelTLS *UNUSED(tls)) if (thread_id == -1) { thread_id = atomic_fetch_and_add_int32(&tbb_thread_id_counter, 1); if (thread_id >= BLENDER_MAX_THREADS) { - BLI_assert(!"Maximum number of threads exceeded for sculpting"); + BLI_assert_msg(0, "Maximum number of threads exceeded for sculpting"); thread_id = thread_id % BLENDER_MAX_THREADS; } } diff --git a/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc b/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc index 59c4be6d952..08a3818e18f 100644 --- a/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc +++ b/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc @@ -353,27 +353,27 @@ void graph_draw(const std::string &label, const vec2<T> &uco = verts[e.first]; const vec2<T> &vco = verts[e.second]; int strokew = thin_line; - f << "<line fill=\"none\" stroke=\"black\" stroke-width=\"" << strokew << "\" x1=\"" + f << R"(<line fill="none" stroke="black" stroke-width=")" << strokew << "\" x1=\"" << SX(uco[0]) << "\" y1=\"" << SY(uco[1]) << "\" x2=\"" << SX(vco[0]) << "\" y2=\"" << SY(vco[1]) << "\">\n"; f << " <title>[" << e.first << "][" << e.second << "]</title>\n"; f << "</line>\n"; if (draw_edge_labels) { f << "<text x=\"" << SX(0.5 * (uco[0] + vco[0])) << "\" y=\"" << SY(0.5 * (uco[1] + vco[1])) - << "\" font-size=\"small\">"; + << R"(" font-size="small">)"; f << "[" << e.first << "][" << e.second << "]</text>\n"; } } int i = 0; for (const vec2<T> &vco : verts) { - f << "<circle fill=\"black\" cx=\"" << SX(vco[0]) << "\" cy=\"" << SY(vco[1]) << "\" r=\"" + f << R"(<circle fill="black" cx=")" << SX(vco[0]) << "\" cy=\"" << SY(vco[1]) << "\" r=\"" << vert_radius << "\">\n"; f << " <title>[" << i << "]" << vco << "</title>\n"; f << "</circle>\n"; if (draw_vert_labels) { f << "<text x=\"" << SX(vco[0]) + vert_radius << "\" y=\"" << SY(vco[1]) - vert_radius - << "\" font-size=\"small\">[" << i << "]</text>\n"; + << R"(" font-size="small">[)" << i << "]</text>\n"; } ++i; } diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 82c4709f187..0645380c4cb 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -670,7 +670,7 @@ static ARegion *do_versions_find_region(ListBase *regionbase, int regiontype) { ARegion *region = do_versions_find_region_or_null(regionbase, regiontype); if (region == NULL) { - BLI_assert(!"Did not find expected region in versioning"); + BLI_assert_msg(0, "Did not find expected region in versioning"); } return region; } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 5d3bde428c0..313ce734bbc 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -33,10 +33,13 @@ #include "DNA_listBase.h" #include "DNA_modifier_types.h" #include "DNA_text_types.h" +#include "DNA_workspace_types.h" #include "BKE_action.h" #include "BKE_animsys.h" +#include "BKE_asset.h" #include "BKE_collection.h" +#include "BKE_deform.h" #include "BKE_fcurve_driver.h" #include "BKE_lib_id.h" #include "BKE_main.h" @@ -90,6 +93,19 @@ static void assert_sorted_ids(Main *bmain) #endif } +static void move_vertex_group_names_to_object_data(Main *bmain) +{ + LISTBASE_FOREACH (Object *, object, &bmain->objects) { + if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) { + ListBase *new_defbase = BKE_object_defgroup_list_mutable(object); + + /* Clear the list in case the it was already assigned from another object. */ + BLI_freelistN(new_defbase); + *new_defbase = object->defbase; + } + } +} + void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) { if (MAIN_VERSION_ATLEAST(bmain, 300, 0) && !MAIN_VERSION_ATLEAST(bmain, 300, 1)) { @@ -134,6 +150,10 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) assert_sorted_ids(bmain); } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 11)) { + move_vertex_group_names_to_object_data(bmain); + } + /** * Versioning code until next subversion bump goes here. * @@ -499,6 +519,25 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } FOREACH_NODETREE_END; + + { + if (!DNA_struct_elem_find( + fd->filesdna, "WorkSpace", "AssetLibraryReference", "active_asset_library")) { + LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) { + BKE_asset_library_reference_init_default(&workspace->active_asset_library); + } + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 10)) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + ToolSettings *tool_settings = scene->toolsettings; + if (tool_settings->snap_uv_mode & (1 << 4)) { + tool_settings->snap_uv_mode |= (1 << 6); /* SCE_SNAP_MODE_INCREMENT */ + tool_settings->snap_uv_mode &= ~(1 << 4); + } + } } /** @@ -512,5 +551,24 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + /* Convert Surface Deform to sparse-capable bind structure. */ + if (!DNA_struct_elem_find( + fd->filesdna, "SurfaceDeformModifierData", "int", "num_mesh_verts")) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_SurfaceDeform) { + SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md; + if (smd->num_bind_verts && smd->verts) { + smd->num_mesh_verts = smd->num_bind_verts; + + for (unsigned int i = 0; i < smd->num_bind_verts; i++) { + smd->verts[i].vertex_idx = i; + } + } + } + } + } + } } } diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 42b27f57e2c..30587418f84 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -894,6 +894,7 @@ void blo_do_versions_userdef(UserDef *userdef) */ { /* Keep this block, even when empty. */ + BKE_addon_ensure(&userdef->addons, "pose_library"); } LISTBASE_FOREACH (bTheme *, btheme, &userdef->themes) { diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 4802c495ef2..fc29b1d8915 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -304,7 +304,7 @@ static void writedata_do_write(WriteData *wd, const void *mem, size_t memlen) } if (memlen > INT_MAX) { - BLI_assert(!"Cannot write chunks bigger than INT_MAX."); + BLI_assert_msg(0, "Cannot write chunks bigger than INT_MAX."); return; } @@ -538,7 +538,7 @@ static void writedata(WriteData *wd, int filecode, size_t len, const void *adr) } if (len > INT_MAX) { - BLI_assert(!"Cannot write chunks bigger than INT_MAX."); + BLI_assert_msg(0, "Cannot write chunks bigger than INT_MAX."); return; } diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h index 1ae2cf99930..a2b848c3b52 100644 --- a/source/blender/bmesh/bmesh_class.h +++ b/source/blender/bmesh/bmesh_class.h @@ -25,6 +25,8 @@ * that benefit from accessing connectivity information. */ +#include "BLI_assert.h" + /* disable holes for now, * these are ifdef'd because they use more memory and can't be saved in DNA currently */ // #define USE_BMESH_HOLES diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index 6dc1359b5ff..3e5f1b7a086 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -478,7 +478,7 @@ static void bm_vert_attrs_copy( BMesh *bm_src, BMesh *bm_dst, const BMVert *v_src, BMVert *v_dst, CustomDataMask mask_exclude) { if ((bm_src == bm_dst) && (v_src == v_dst)) { - BLI_assert(!"BMVert: source and target match"); + BLI_assert_msg(0, "BMVert: source and target match"); return; } if ((mask_exclude & CD_MASK_NORMAL) == 0) { @@ -493,7 +493,7 @@ static void bm_edge_attrs_copy( BMesh *bm_src, BMesh *bm_dst, const BMEdge *e_src, BMEdge *e_dst, CustomDataMask mask_exclude) { if ((bm_src == bm_dst) && (e_src == e_dst)) { - BLI_assert(!"BMEdge: source and target match"); + BLI_assert_msg(0, "BMEdge: source and target match"); return; } CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->edata, e_dst->head.data, mask_exclude); @@ -505,7 +505,7 @@ static void bm_loop_attrs_copy( BMesh *bm_src, BMesh *bm_dst, const BMLoop *l_src, BMLoop *l_dst, CustomDataMask mask_exclude) { if ((bm_src == bm_dst) && (l_src == l_dst)) { - BLI_assert(!"BMLoop: source and target match"); + BLI_assert_msg(0, "BMLoop: source and target match"); return; } CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->ldata, l_dst->head.data, mask_exclude); @@ -517,7 +517,7 @@ static void bm_face_attrs_copy( BMesh *bm_src, BMesh *bm_dst, const BMFace *f_src, BMFace *f_dst, CustomDataMask mask_exclude) { if ((bm_src == bm_dst) && (f_src == f_dst)) { - BLI_assert(!"BMFace: source and target match"); + BLI_assert_msg(0, "BMFace: source and target match"); return; } if ((mask_exclude & CD_MASK_NORMAL) == 0) { @@ -940,7 +940,7 @@ short BM_edge_flag_to_mflag(BMEdge *e) ((hflag & BM_ELEM_DRAW) ? ME_EDGEDRAW : 0) | ((hflag & BM_ELEM_SMOOTH) == 0 ? ME_SHARP : 0) | ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0) | - ((BM_edge_is_wire(e)) ? ME_LOOSEEDGE : 0) | /* not typical */ + (BM_edge_is_wire(e) ? ME_LOOSEEDGE : 0) | /* not typical */ ME_EDGERENDER); } char BM_face_flag_to_mflag(BMFace *f) diff --git a/source/blender/bmesh/intern/bmesh_log.c b/source/blender/bmesh/intern/bmesh_log.c index 421336a5cbe..abdcfeb8d6c 100644 --- a/source/blender/bmesh/intern/bmesh_log.c +++ b/source/blender/bmesh/intern/bmesh_log.c @@ -1221,7 +1221,7 @@ void BM_log_entry_drop(BMLogEntry *entry) * can go back into the pool. */ } else { - BLI_assert(!"Cannot drop BMLogEntry from middle"); + BLI_assert_msg(0, "Cannot drop BMLogEntry from middle"); } if (log->current_entry == entry) { diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index f133150847e..d9176a7f5fb 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -29,8 +29,8 @@ typedef enum { struct BMAllocTemplate; struct BMLoopNorEditDataArray; -struct MLoopNorSpaceArray; struct BMPartialUpdate; +struct MLoopNorSpaceArray; void BM_mesh_elem_toolflags_ensure(BMesh *bm); void BM_mesh_elem_toolflags_clear(BMesh *bm); diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c index 97d1de39027..dea6561fe9a 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_normals.c +++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c @@ -33,6 +33,7 @@ #include "BLI_task.h" #include "BLI_utildefines.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_global.h" #include "BKE_mesh.h" @@ -630,11 +631,14 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, { const BMVert *v_pivot = l_curr->v; const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co; - const BMVert *v_1 = BM_edge_other_vert(l_curr->e, v_pivot); + const BMVert *v_1 = l_curr->next->v; const float *co_1 = vcos ? vcos[BM_elem_index_get(v_1)] : v_1->co; - const BMVert *v_2 = BM_edge_other_vert(l_curr->prev->e, v_pivot); + const BMVert *v_2 = l_curr->prev->v; const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co; + BLI_assert(v_1 == BM_edge_other_vert(l_curr->e, v_pivot)); + BLI_assert(v_2 == BM_edge_other_vert(l_curr->prev->e, v_pivot)); + sub_v3_v3v3(vec_curr, co_1, co_pivot); normalize_v3(vec_curr); sub_v3_v3v3(vec_prev, co_2, co_pivot); @@ -700,9 +704,11 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, /* Only need to compute previous edge's vector once, * then we can just reuse old current one! */ { - const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot); + const BMVert *v_2 = lfan_pivot->next->v; const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co; + BLI_assert(v_2 == BM_edge_other_vert(e_next, v_pivot)); + sub_v3_v3v3(vec_org, co_2, co_pivot); normalize_v3(vec_org); copy_v3_v3(vec_curr, vec_org); diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h index 25efb6e1aca..543b2eb9c8c 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.h +++ b/source/blender/bmesh/intern/bmesh_polygon.h @@ -20,8 +20,8 @@ * \ingroup bmesh */ -struct Heap; struct BMPartialUpdate; +struct Heap; #include "BLI_compiler_attrs.h" #include "BLI_compiler_compat.h" diff --git a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c index 99e50b35d97..86a7d8153f0 100644 --- a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c +++ b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c @@ -337,7 +337,7 @@ static bool bm_face_split_edgenet_find_loop_walk(BMVert *v_init, /* in rare cases there may be edges with a single connecting vertex */ if (e_next != e_first) { do { - if ((BM_ELEM_API_FLAG_TEST(e_next, EDGE_NET)) && + if (BM_ELEM_API_FLAG_TEST(e_next, EDGE_NET) && (bm_edge_flagged_radial_count(e_next) < 2)) { BMVert *v_next; diff --git a/source/blender/bmesh/intern/bmesh_walkers_impl.c b/source/blender/bmesh/intern/bmesh_walkers_impl.c index 40f09d7e719..e66afcd88d9 100644 --- a/source/blender/bmesh/intern/bmesh_walkers_impl.c +++ b/source/blender/bmesh/intern/bmesh_walkers_impl.c @@ -842,7 +842,7 @@ static void *bmw_IslandManifoldWalker_step(BMWalker *walker) /* utility function to see if an edge is a part of an ngon boundary */ static bool bm_edge_is_single(BMEdge *e) { - return ((BM_edge_is_boundary(e)) && (e->l->f->len > 4) && + return (BM_edge_is_boundary(e) && (e->l->f->len > 4) && (BM_edge_is_boundary(e->l->next->e) || BM_edge_is_boundary(e->l->prev->e))); } diff --git a/source/blender/bmesh/operators/bmo_connect_concave.c b/source/blender/bmesh/operators/bmo_connect_concave.c index bc1111676a3..15ed930afd8 100644 --- a/source/blender/bmesh/operators/bmo_connect_concave.c +++ b/source/blender/bmesh/operators/bmo_connect_concave.c @@ -51,10 +51,10 @@ static int bm_edge_length_cmp(const void *a_, const void *b_) const BMEdge *e_a = *(const void **)a_; const BMEdge *e_b = *(const void **)b_; - int e_a_concave = ((BM_elem_flag_test(e_a->v1, BM_ELEM_TAG)) && - (BM_elem_flag_test(e_a->v2, BM_ELEM_TAG))); - int e_b_concave = ((BM_elem_flag_test(e_b->v1, BM_ELEM_TAG)) && - (BM_elem_flag_test(e_b->v2, BM_ELEM_TAG))); + int e_a_concave = (BM_elem_flag_test(e_a->v1, BM_ELEM_TAG) && + BM_elem_flag_test(e_a->v2, BM_ELEM_TAG)); + int e_b_concave = (BM_elem_flag_test(e_b->v1, BM_ELEM_TAG) && + BM_elem_flag_test(e_b->v2, BM_ELEM_TAG)); /* merge edges between concave edges last since these * are most likely to remain and be the main dividers */ diff --git a/source/blender/bmesh/operators/bmo_edgenet.c b/source/blender/bmesh/operators/bmo_edgenet.c index 8e4b0732feb..7f70c452af3 100644 --- a/source/blender/bmesh/operators/bmo_edgenet.c +++ b/source/blender/bmesh/operators/bmo_edgenet.c @@ -93,7 +93,7 @@ static BMEdge *edge_next(BMesh *bm, BMEdge *e) for (i = 0; i < 2; i++) { BM_ITER_ELEM (e2, &iter, i ? e->v2 : e->v1, BM_EDGES_OF_VERT) { - if ((BMO_edge_flag_test(bm, e2, EDGE_MARK)) && + if (BMO_edge_flag_test(bm, e2, EDGE_MARK) && (BMO_edge_flag_test(bm, e2, EDGE_VIS) == false) && (e2 != e)) { return e2; } diff --git a/source/blender/bmesh/operators/bmo_fill_grid.c b/source/blender/bmesh/operators/bmo_fill_grid.c index 2e09b21c9cc..6734cc60cad 100644 --- a/source/blender/bmesh/operators/bmo_fill_grid.c +++ b/source/blender/bmesh/operators/bmo_fill_grid.c @@ -645,20 +645,20 @@ void bmo_grid_fill_exec(BMesh *bm, BMOperator *op) bm_edgeloop_flag_set(estore_a, BM_ELEM_HIDDEN, true); bm_edgeloop_flag_set(estore_b, BM_ELEM_HIDDEN, true); - if ((BM_mesh_edgeloops_find_path( - bm, &eloops_rail, bm_edge_test_rail_cb, bm, v_a_first, v_b_first)) && - (BM_mesh_edgeloops_find_path( - bm, &eloops_rail, bm_edge_test_rail_cb, bm, v_a_last, v_b_last))) { + if (BM_mesh_edgeloops_find_path( + bm, &eloops_rail, bm_edge_test_rail_cb, bm, v_a_first, v_b_first) && + BM_mesh_edgeloops_find_path( + bm, &eloops_rail, bm_edge_test_rail_cb, bm, v_a_last, v_b_last)) { estore_rail_a = eloops_rail.first; estore_rail_b = eloops_rail.last; } else { BM_mesh_edgeloops_free(&eloops_rail); - if ((BM_mesh_edgeloops_find_path( - bm, &eloops_rail, bm_edge_test_rail_cb, bm, v_a_first, v_b_last)) && - (BM_mesh_edgeloops_find_path( - bm, &eloops_rail, bm_edge_test_rail_cb, bm, v_a_last, v_b_first))) { + if (BM_mesh_edgeloops_find_path( + bm, &eloops_rail, bm_edge_test_rail_cb, bm, v_a_first, v_b_last) && + BM_mesh_edgeloops_find_path( + bm, &eloops_rail, bm_edge_test_rail_cb, bm, v_a_last, v_b_first)) { estore_rail_a = eloops_rail.first; estore_rail_b = eloops_rail.last; BM_edgeloop_flip(bm, estore_b); diff --git a/source/blender/bmesh/operators/bmo_hull.c b/source/blender/bmesh/operators/bmo_hull.c index 0801300b6c2..740815691cc 100644 --- a/source/blender/bmesh/operators/bmo_hull.c +++ b/source/blender/bmesh/operators/bmo_hull.c @@ -465,7 +465,7 @@ static BMVert **hull_verts_from_bullet(plConvexHull hull, hull_verts[i] = input_verts[original_index]; } else { - BLI_assert(!"Unexpected new vertex in hull output"); + BLI_assert_msg(0, "Unexpected new vertex in hull output"); } } diff --git a/source/blender/bmesh/operators/bmo_subdivide_edgering.c b/source/blender/bmesh/operators/bmo_subdivide_edgering.c index d015b715a69..6a80f360f59 100644 --- a/source/blender/bmesh/operators/bmo_subdivide_edgering.c +++ b/source/blender/bmesh/operators/bmo_subdivide_edgering.c @@ -860,12 +860,12 @@ static bool bm_edgering_pair_order_is_flipped(BMesh *UNUSED(bm), /* step around any fan-faces on both sides */ do { v_iter_a_step = v_iter_a_step->next; - } while (v_iter_a_step && ((BM_edge_exists(v_iter_a_step->data, v_iter_b_first->data)) || - (BM_edge_exists(v_iter_a_step->data, v_iter_b_first->next->data)))); + } while (v_iter_a_step && (BM_edge_exists(v_iter_a_step->data, v_iter_b_first->data) || + BM_edge_exists(v_iter_a_step->data, v_iter_b_first->next->data))); do { v_iter_b_step = v_iter_b_step->next; - } while (v_iter_b_step && ((BM_edge_exists(v_iter_b_step->data, v_iter_a_first->data)) || - (BM_edge_exists(v_iter_b_step->data, v_iter_a_first->next->data)))); + } while (v_iter_b_step && (BM_edge_exists(v_iter_b_step->data, v_iter_a_first->data) || + BM_edge_exists(v_iter_b_step->data, v_iter_a_first->next->data))); v_iter_a_step = v_iter_a_step ? v_iter_a_step->prev : lb_a->last; v_iter_b_step = v_iter_b_step ? v_iter_b_step->prev : lb_b->last; diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 28d0aecbb3d..1e8ef9737d3 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -1676,7 +1676,7 @@ static void project_to_edge(const BMEdge *e, float otherco[3]; if (!isect_line_line_v3(e->v1->co, e->v2->co, co_a, co_b, projco, otherco)) { #ifdef BEVEL_ASSERT_PROJECT - BLI_assert(!"project meet failure"); + BLI_assert_msg(0, "project meet failure"); #endif copy_v3_v3(projco, e->v1->co); } @@ -6262,7 +6262,7 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v) break; } default: { - BLI_assert(!"bad bevel offset kind"); + BLI_assert_msg(0, "bad bevel offset kind"); e->offset_l_spec = bp->offset; break; } diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index 857cbf0beee..9f8e6f10215 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -60,9 +60,12 @@ constexpr int COM_data_type_num_channels(const DataType datatype) } constexpr int COM_DATA_TYPE_VALUE_CHANNELS = COM_data_type_num_channels(DataType::Value); +constexpr int COM_DATA_TYPE_VECTOR_CHANNELS = COM_data_type_num_channels(DataType::Vector); constexpr int COM_DATA_TYPE_COLOR_CHANNELS = COM_data_type_num_channels(DataType::Color); +constexpr float COM_VECTOR_ZERO[3] = {0.0f, 0.0f, 0.0f}; constexpr float COM_VALUE_ZERO[1] = {0.0f}; +constexpr float COM_VALUE_ONE[1] = {1.0f}; /** * Utility to get data type for given number of channels. diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc index 61e299c045e..a93820b66dc 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.cc +++ b/source/blender/compositor/intern/COM_CompositorContext.cc @@ -53,7 +53,7 @@ eExecutionModel CompositorContext::get_execution_model() const case 0: return eExecutionModel::Tiled; default: - BLI_assert(!"Invalid execution mode"); + BLI_assert_msg(0, "Invalid execution mode"); } } return eExecutionModel::Tiled; diff --git a/source/blender/compositor/intern/COM_ConstantFolder.cc b/source/blender/compositor/intern/COM_ConstantFolder.cc index a9427013f87..f20324de342 100644 --- a/source/blender/compositor/intern/COM_ConstantFolder.cc +++ b/source/blender/compositor/intern/COM_ConstantFolder.cc @@ -83,7 +83,7 @@ static ConstantOperation *create_constant_operation(DataType data_type, const fl return value_op; } default: { - BLI_assert(!"Non implemented data type"); + BLI_assert_msg(0, "Non implemented data type"); return nullptr; } } diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc index af593b2e1b5..18973bb5a00 100644 --- a/source/blender/compositor/intern/COM_Converter.cc +++ b/source/blender/compositor/intern/COM_Converter.cc @@ -460,6 +460,9 @@ void COM_convert_resolution(NodeOperationBuilder &builder, NodeOperationOutput *fromSocket, NodeOperationInput *toSocket) { + /* Data type conversions are executed before resolutions to ensure convert operations have + * resolution. This method have to ensure same datatypes are linked for new operations. */ + BLI_assert(fromSocket->getDataType() == toSocket->getDataType()); ResizeMode mode = toSocket->getResizeMode(); NodeOperation *toOperation = &toSocket->getOperation(); @@ -515,7 +518,7 @@ void COM_convert_resolution(NodeOperationBuilder &builder, NodeOperation *first = nullptr; ScaleOperation *scaleOperation = nullptr; if (doScale) { - scaleOperation = new ScaleOperation(); + scaleOperation = new ScaleOperation(fromSocket->getDataType()); scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None); scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None); first = scaleOperation; @@ -535,7 +538,7 @@ void COM_convert_resolution(NodeOperationBuilder &builder, builder.addOperation(scaleOperation); } - TranslateOperation *translateOperation = new TranslateOperation(); + TranslateOperation *translateOperation = new TranslateOperation(toSocket->getDataType()); translateOperation->getInputSocket(1)->setResizeMode(ResizeMode::None); translateOperation->getInputSocket(2)->setResizeMode(ResizeMode::None); if (!first) { diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cc b/source/blender/compositor/intern/COM_ExecutionSystem.cc index dfcf76cdd0a..60caf22be1b 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.cc +++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc @@ -79,7 +79,7 @@ ExecutionSystem::ExecutionSystem(RenderData *rd, execution_model_ = new FullFrameExecutionModel(m_context, active_buffers_, m_operations); break; default: - BLI_assert(!"Non implemented execution model"); + BLI_assert_msg(0, "Non implemented execution model"); break; } } diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index 89068a7b734..4ad0872b0b7 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -264,11 +264,14 @@ class MemoryBuffer { x = 0; } if (x >= w) { - x = w; + x = w - 1; } break; case MemoryBufferExtend::Repeat: - x = (x >= 0.0f ? (x % w) : (x % w) + w); + x %= w; + if (x < 0) { + x += w; + } break; } @@ -280,13 +283,19 @@ class MemoryBuffer { y = 0; } if (y >= h) { - y = h; + y = h - 1; } break; case MemoryBufferExtend::Repeat: - y = (y >= 0.0f ? (y % h) : (y % h) + h); + y %= h; + if (y < 0) { + y += h; + } break; } + + x = x + m_rect.xmin; + y = y + m_rect.ymin; } inline void wrap_pixel(float &x, @@ -307,11 +316,14 @@ class MemoryBuffer { x = 0.0f; } if (x >= w) { - x = w; + x = w - 1; } break; case MemoryBufferExtend::Repeat: x = fmodf(x, w); + if (x < 0.0f) { + x += w; + } break; } @@ -323,13 +335,19 @@ class MemoryBuffer { y = 0.0f; } if (y >= h) { - y = h; + y = h - 1; } break; case MemoryBufferExtend::Repeat: y = fmodf(y, h); + if (y < 0.0f) { + y += h; + } break; } + + x = x + m_rect.xmin; + y = y + m_rect.ymin; } inline void read(float *result, diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index 6484c75f364..4e115cb3f2f 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -218,7 +218,7 @@ void NodeOperation::get_area_of_interest(NodeOperation *input_op, return; } } - BLI_assert(!"input_op is not an input operation."); + BLI_assert_msg(0, "input_op is not an input operation."); } /** diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.cc b/source/blender/compositor/intern/COM_OpenCLDevice.cc index 0f6ed0dbd2b..3409c8fa3bc 100644 --- a/source/blender/compositor/intern/COM_OpenCLDevice.cc +++ b/source/blender/compositor/intern/COM_OpenCLDevice.cc @@ -110,7 +110,7 @@ const cl_image_format *OpenCLDevice::determineImageFormat(MemoryBuffer *memoryBu return &IMAGE_FORMAT_COLOR; break; default: - BLI_assert(!"Unsupported num_channels."); + BLI_assert_msg(0, "Unsupported num_channels."); } return &IMAGE_FORMAT_COLOR; diff --git a/source/blender/compositor/nodes/COM_IDMaskNode.cc b/source/blender/compositor/nodes/COM_IDMaskNode.cc index 9798dabd035..b51e79f2dea 100644 --- a/source/blender/compositor/nodes/COM_IDMaskNode.cc +++ b/source/blender/compositor/nodes/COM_IDMaskNode.cc @@ -17,9 +17,9 @@ */ #include "COM_IDMaskNode.h" -#include "COM_AntiAliasOperation.h" #include "COM_ExecutionSystem.h" #include "COM_IDMaskOperation.h" +#include "COM_SMAAOperation.h" namespace blender::compositor { @@ -42,11 +42,27 @@ void IDMaskNode::convertToOperations(NodeConverter &converter, converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); } else { - AntiAliasOperation *antiAliasOperation = new AntiAliasOperation(); - converter.addOperation(antiAliasOperation); + SMAAEdgeDetectionOperation *operation1 = nullptr; - converter.addLink(operation->getOutputSocket(), antiAliasOperation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), antiAliasOperation->getOutputSocket(0)); + operation1 = new SMAAEdgeDetectionOperation(); + converter.addOperation(operation1); + + converter.addLink(operation->getOutputSocket(0), operation1->getInputSocket(0)); + + /* Blending Weight Calculation Pixel Shader (Second Pass). */ + SMAABlendingWeightCalculationOperation *operation2 = + new SMAABlendingWeightCalculationOperation(); + converter.addOperation(operation2); + + converter.addLink(operation1->getOutputSocket(), operation2->getInputSocket(0)); + + /* Neighborhood Blending Pixel Shader (Third Pass). */ + SMAANeighborhoodBlendingOperation *operation3 = new SMAANeighborhoodBlendingOperation(); + converter.addOperation(operation3); + + converter.addLink(operation->getOutputSocket(0), operation3->getInputSocket(0)); + converter.addLink(operation2->getOutputSocket(), operation3->getInputSocket(1)); + converter.mapOutputSocket(getOutputSocket(0), operation3->getOutputSocket()); } } diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.cc b/source/blender/compositor/nodes/COM_RenderLayersNode.cc index 851d0366546..253ca542c04 100644 --- a/source/blender/compositor/nodes/COM_RenderLayersNode.cc +++ b/source/blender/compositor/nodes/COM_RenderLayersNode.cc @@ -107,7 +107,7 @@ void RenderLayersNode::testRenderLink(NodeConverter &converter, type = DataType::Value; break; default: - BLI_assert(!"Unexpected number of channels for pass"); + BLI_assert_msg(0, "Unexpected number of channels for pass"); type = DataType::Value; break; } diff --git a/source/blender/compositor/nodes/COM_TranslateNode.cc b/source/blender/compositor/nodes/COM_TranslateNode.cc index 1b2ce341a66..3a3e98c3472 100644 --- a/source/blender/compositor/nodes/COM_TranslateNode.cc +++ b/source/blender/compositor/nodes/COM_TranslateNode.cc @@ -42,6 +42,7 @@ void TranslateNode::convertToOperations(NodeConverter &converter, NodeOutput *outputSocket = this->getOutputSocket(0); TranslateOperation *operation = new TranslateOperation(); + operation->set_wrapping(data->wrap_axis); if (data->relative) { const RenderData *rd = context.getRenderData(); const float render_size_factor = context.getRenderPercentageAsFactor(); @@ -55,11 +56,8 @@ void TranslateNode::convertToOperations(NodeConverter &converter, converter.mapInputSocket(inputXSocket, operation->getInputSocket(1)); converter.mapInputSocket(inputYSocket, operation->getInputSocket(2)); converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); - - /* FullFrame does not support using WriteBufferOperation. - * TODO: Implement TranslateOperation with wrap support in FullFrame. - */ if (data->wrap_axis && context.get_execution_model() != eExecutionModel::FullFrame) { + /* TODO: To be removed with tiled implementation. */ WriteBufferOperation *writeOperation = new WriteBufferOperation(DataType::Color); WrapOperation *wrapOperation = new WrapOperation(DataType::Color); wrapOperation->setMemoryProxy(writeOperation->getMemoryProxy()); diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cc b/source/blender/compositor/operations/COM_RenderLayersProg.cc index 1ac451b95c2..72e2c92c9cf 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.cc +++ b/source/blender/compositor/operations/COM_RenderLayersProg.cc @@ -43,6 +43,7 @@ RenderLayersProg::RenderLayersProg(const char *passName, DataType type, int elem this->m_inputBuffer = nullptr; this->m_elementsize = elementsize; this->m_rd = nullptr; + layer_buffer_ = nullptr; this->addOutputSocket(type); } @@ -65,6 +66,9 @@ void RenderLayersProg::initExecution() if (rl) { this->m_inputBuffer = RE_RenderLayerGetPass( rl, this->m_passName.c_str(), this->m_viewName); + if (m_inputBuffer) { + layer_buffer_ = new MemoryBuffer(m_inputBuffer, m_elementsize, getWidth(), getHeight()); + } } } } @@ -159,7 +163,7 @@ void RenderLayersProg::executePixelSampled(float output[4], float x, float y, Pi } else { expected_element_size = 0; - BLI_assert(!"Something horribly wrong just happened"); + BLI_assert_msg(0, "Something horribly wrong just happened"); } BLI_assert(expected_element_size == actual_element_size); } @@ -186,6 +190,10 @@ void RenderLayersProg::executePixelSampled(float output[4], float x, float y, Pi void RenderLayersProg::deinitExecution() { this->m_inputBuffer = nullptr; + if (layer_buffer_) { + delete layer_buffer_; + layer_buffer_ = nullptr; + } } void RenderLayersProg::determineResolution(unsigned int resolution[2], @@ -255,6 +263,20 @@ std::unique_ptr<MetaData> RenderLayersProg::getMetaData() return std::move(callback_data.meta_data); } +void RenderLayersProg::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs)) +{ + BLI_assert(output->get_num_channels() >= m_elementsize); + if (layer_buffer_) { + output->copy_from(layer_buffer_, area, 0, m_elementsize, 0); + } + else { + std::unique_ptr<float[]> zero_elem = std::make_unique<float[]>(m_elementsize); + output->fill(area, 0, zero_elem.get(), m_elementsize); + } +} + /* ******** Render Layers AO Operation ******** */ void RenderLayersAOOperation::executePixelSampled(float output[4], float x, @@ -271,6 +293,21 @@ void RenderLayersAOOperation::executePixelSampled(float output[4], output[3] = 1.0f; } +void RenderLayersAOOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs)) +{ + BLI_assert(output->get_num_channels() == COM_DATA_TYPE_COLOR_CHANNELS); + BLI_assert(m_elementsize == COM_DATA_TYPE_COLOR_CHANNELS); + if (layer_buffer_) { + output->copy_from(layer_buffer_, area, 0, COM_DATA_TYPE_VECTOR_CHANNELS, 0); + } + else { + output->fill(area, 0, COM_VECTOR_ZERO, COM_DATA_TYPE_VECTOR_CHANNELS); + } + output->fill(area, 3, COM_VALUE_ONE, COM_DATA_TYPE_VALUE_CHANNELS); +} + /* ******** Render Layers Alpha Operation ******** */ void RenderLayersAlphaProg::executePixelSampled(float output[4], float x, @@ -289,6 +326,20 @@ void RenderLayersAlphaProg::executePixelSampled(float output[4], } } +void RenderLayersAlphaProg::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs)) +{ + BLI_assert(output->get_num_channels() == COM_DATA_TYPE_VALUE_CHANNELS); + BLI_assert(m_elementsize == COM_DATA_TYPE_COLOR_CHANNELS); + if (layer_buffer_) { + output->copy_from(layer_buffer_, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0); + } + else { + output->fill(area, COM_VALUE_ZERO); + } +} + /* ******** Render Layers Depth Operation ******** */ void RenderLayersDepthProg::executePixelSampled(float output[4], float x, @@ -309,4 +360,19 @@ void RenderLayersDepthProg::executePixelSampled(float output[4], } } +void RenderLayersDepthProg::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs)) +{ + BLI_assert(output->get_num_channels() == COM_DATA_TYPE_VALUE_CHANNELS); + BLI_assert(m_elementsize == COM_DATA_TYPE_VALUE_CHANNELS); + if (layer_buffer_) { + output->copy_from(layer_buffer_, area); + } + else { + const float default_depth = 10e10f; + output->fill(area, &default_depth); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.h b/source/blender/compositor/operations/COM_RenderLayersProg.h index 56f83f691e8..dd76a56d645 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.h +++ b/source/blender/compositor/operations/COM_RenderLayersProg.h @@ -20,7 +20,7 @@ #include "BLI_listbase.h" #include "BLI_utildefines.h" -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_scene_types.h" #include "MEM_guardedalloc.h" @@ -33,7 +33,7 @@ namespace blender::compositor { * * \todo Rename to operation. */ -class RenderLayersProg : public NodeOperation { +class RenderLayersProg : public MultiThreadedOperation { protected: /** * Reference to the scene object. @@ -50,8 +50,11 @@ class RenderLayersProg : public NodeOperation { */ const char *m_viewName; + const MemoryBuffer *layer_buffer_; + /** - * cached instance to the float buffer inside the layer + * Cached instance to the float buffer inside the layer. + * TODO: To be removed with tiled implementation. */ float *m_inputBuffer; @@ -126,6 +129,10 @@ class RenderLayersProg : public NodeOperation { void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; std::unique_ptr<MetaData> getMetaData() override; + + virtual void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; class RenderLayersAOOperation : public RenderLayersProg { @@ -135,6 +142,10 @@ class RenderLayersAOOperation : public RenderLayersProg { { } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; class RenderLayersAlphaProg : public RenderLayersProg { @@ -144,6 +155,10 @@ class RenderLayersAlphaProg : public RenderLayersProg { { } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; class RenderLayersDepthProg : public RenderLayersProg { @@ -153,6 +168,10 @@ class RenderLayersDepthProg : public RenderLayersProg { { } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc index c3647a39909..3c753591ced 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.cc +++ b/source/blender/compositor/operations/COM_SMAAOperation.cc @@ -21,6 +21,7 @@ #include "COM_SMAAOperation.h" #include "BLI_math.h" #include "COM_SMAAAreaTexture.h" +#include "BKE_node.h" extern "C" { #include "IMB_colormanagement.h" @@ -166,8 +167,8 @@ SMAAEdgeDetectionOperation::SMAAEdgeDetectionOperation() this->flags.complex = true; this->m_imageReader = nullptr; this->m_valueReader = nullptr; - this->m_threshold = 0.1f; - this->m_contrast_limit = 2.0f; + this->setThreshold(CMP_DEFAULT_SMAA_THRESHOLD); + this->setLocalContrastAdaptationFactor(CMP_DEFAULT_SMAA_CONTRAST_LIMIT); } void SMAAEdgeDetectionOperation::initExecution() @@ -297,7 +298,7 @@ SMAABlendingWeightCalculationOperation::SMAABlendingWeightCalculationOperation() this->addOutputSocket(DataType::Color); this->flags.complex = true; this->m_imageReader = nullptr; - this->m_corner_rounding = 25; + this->setCornerRounding(CMP_DEFAULT_SMAA_CORNER_ROUNDING); } void *SMAABlendingWeightCalculationOperation::initializeTileData(rcti *rect) diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc index 18276fcc072..f03b9fcf34d 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.cc +++ b/source/blender/compositor/operations/COM_ScaleOperation.cc @@ -37,12 +37,16 @@ BaseScaleOperation::BaseScaleOperation() m_variable_size = false; } -ScaleOperation::ScaleOperation() : BaseScaleOperation() +ScaleOperation::ScaleOperation() : ScaleOperation(DataType::Color) { - this->addInputSocket(DataType::Color); +} + +ScaleOperation::ScaleOperation(DataType data_type) : BaseScaleOperation() +{ + this->addInputSocket(data_type); this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->addOutputSocket(data_type); this->setResolutionInputSocketIndex(0); this->m_inputOperation = nullptr; this->m_inputXOperation = nullptr; diff --git a/source/blender/compositor/operations/COM_ScaleOperation.h b/source/blender/compositor/operations/COM_ScaleOperation.h index dc3de3602bf..2f9a7be92e6 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.h +++ b/source/blender/compositor/operations/COM_ScaleOperation.h @@ -55,6 +55,7 @@ class ScaleOperation : public BaseScaleOperation { public: ScaleOperation(); + ScaleOperation(DataType data_type); bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; diff --git a/source/blender/compositor/operations/COM_TranslateOperation.cc b/source/blender/compositor/operations/COM_TranslateOperation.cc index 49135f25320..a3db086e974 100644 --- a/source/blender/compositor/operations/COM_TranslateOperation.cc +++ b/source/blender/compositor/operations/COM_TranslateOperation.cc @@ -20,12 +20,15 @@ namespace blender::compositor { -TranslateOperation::TranslateOperation() +TranslateOperation::TranslateOperation() : TranslateOperation(DataType::Color) { - this->addInputSocket(DataType::Color); +} +TranslateOperation::TranslateOperation(DataType data_type) +{ + this->addInputSocket(data_type); this->addInputSocket(DataType::Value); this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->addOutputSocket(data_type); this->setResolutionInputSocketIndex(0); this->m_inputOperation = nullptr; this->m_inputXOperation = nullptr; @@ -33,6 +36,8 @@ TranslateOperation::TranslateOperation() this->m_isDeltaSet = false; this->m_factorX = 1.0f; this->m_factorY = 1.0f; + this->x_extend_mode_ = MemoryBufferExtend::Clip; + this->y_extend_mode_ = MemoryBufferExtend::Clip; } void TranslateOperation::initExecution() { @@ -83,4 +88,58 @@ void TranslateOperation::setFactorXY(float factorX, float factorY) m_factorY = factorY; } +void TranslateOperation::set_wrapping(int wrapping_type) +{ + switch (wrapping_type) { + case CMP_NODE_WRAP_X: + x_extend_mode_ = MemoryBufferExtend::Repeat; + break; + case CMP_NODE_WRAP_Y: + y_extend_mode_ = MemoryBufferExtend::Repeat; + break; + case CMP_NODE_WRAP_XY: + x_extend_mode_ = MemoryBufferExtend::Repeat; + y_extend_mode_ = MemoryBufferExtend::Repeat; + break; + default: + break; + } +} + +void TranslateOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + if (input_idx == 0) { + ensureDelta(); + r_input_area = output_area; + if (x_extend_mode_ == MemoryBufferExtend::Clip) { + const int delta_x = this->getDeltaX(); + BLI_rcti_translate(&r_input_area, -delta_x, 0); + } + if (y_extend_mode_ == MemoryBufferExtend::Clip) { + const int delta_y = this->getDeltaY(); + BLI_rcti_translate(&r_input_area, 0, -delta_y); + } + } +} + +void TranslateOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + MemoryBuffer *input = inputs[0]; + const int delta_x = this->getDeltaX(); + const int delta_y = this->getDeltaY(); + for (int y = area.ymin; y < area.ymax; y++) { + float *out = output->get_elem(area.xmin, y); + for (int x = area.xmin; x < area.xmax; x++) { + const int input_x = x - delta_x; + const int input_y = y - delta_y; + input->read(out, input_x, input_y, x_extend_mode_, y_extend_mode_); + out += output->elem_stride; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TranslateOperation.h b/source/blender/compositor/operations/COM_TranslateOperation.h index eb3a664159f..ce1965cecef 100644 --- a/source/blender/compositor/operations/COM_TranslateOperation.h +++ b/source/blender/compositor/operations/COM_TranslateOperation.h @@ -18,11 +18,12 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_ConstantOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class TranslateOperation : public NodeOperation { +class TranslateOperation : public MultiThreadedOperation { private: SocketReader *m_inputOperation; SocketReader *m_inputXOperation; @@ -32,9 +33,12 @@ class TranslateOperation : public NodeOperation { bool m_isDeltaSet; float m_factorX; float m_factorY; + MemoryBufferExtend x_extend_mode_; + MemoryBufferExtend y_extend_mode_; public: TranslateOperation(); + TranslateOperation(DataType data_type); bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; @@ -55,16 +59,38 @@ class TranslateOperation : public NodeOperation { inline void ensureDelta() { if (!this->m_isDeltaSet) { - float tempDelta[4]; - this->m_inputXOperation->readSampled(tempDelta, 0, 0, PixelSampler::Nearest); - this->m_deltaX = tempDelta[0]; - this->m_inputYOperation->readSampled(tempDelta, 0, 0, PixelSampler::Nearest); - this->m_deltaY = tempDelta[0]; + if (execution_model_ == eExecutionModel::Tiled) { + float tempDelta[4]; + this->m_inputXOperation->readSampled(tempDelta, 0, 0, PixelSampler::Nearest); + this->m_deltaX = tempDelta[0]; + this->m_inputYOperation->readSampled(tempDelta, 0, 0, PixelSampler::Nearest); + this->m_deltaY = tempDelta[0]; + } + else { + this->m_deltaX = 0; + NodeOperation *x_op = getInputOperation(1); + if (x_op->get_flags().is_constant_operation) { + this->m_deltaX = ((ConstantOperation *)x_op)->get_constant_elem()[0]; + } + this->m_deltaY = 0; + NodeOperation *y_op = getInputOperation(2); + if (y_op->get_flags().is_constant_operation) { + this->m_deltaY = ((ConstantOperation *)y_op)->get_constant_elem()[0]; + } + } + this->m_isDeltaSet = true; } } void setFactorXY(float factorX, float factorY); + void set_wrapping(int wrapping_type); + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h index 27441c9a7ae..749b1bba871 100644 --- a/source/blender/depsgraph/DEG_depsgraph.h +++ b/source/blender/depsgraph/DEG_depsgraph.h @@ -159,7 +159,7 @@ void DEG_ids_restore_recalc(Depsgraph *depsgraph); /* Graph Evaluation ----------------------------- */ /* Frame changed recalculation entry point. */ -void DEG_evaluate_on_framechange(Depsgraph *graph, float ctime); +void DEG_evaluate_on_framechange(Depsgraph *graph, float frame); /* Data changed recalculation entry point. */ void DEG_evaluate_on_refresh(Depsgraph *graph); diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h index 42c9cccceed..c029d203574 100644 --- a/source/blender/depsgraph/DEG_depsgraph_build.h +++ b/source/blender/depsgraph/DEG_depsgraph_build.h @@ -33,6 +33,7 @@ struct Depsgraph; /* ------------------------------------------------ */ struct CacheFile; +struct Collection; struct CustomData_MeshMasks; struct ID; struct Main; @@ -40,7 +41,6 @@ struct Object; struct Scene; struct Simulation; struct bNodeTree; -struct Collection; #include "BLI_sys_types.h" diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc index f4d65698bee..41512168f57 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -97,7 +97,7 @@ bool DepsgraphBuilder::need_pull_base_into_graph(Base *base) property_id = AnimatedPropertyID(&object->id, &RNA_Object, "hide_render"); } else { - BLI_assert(!"Unknown evaluation mode."); + BLI_assert_msg(0, "Unknown evaluation mode."); return false; } return cache_->isPropertyAnimated(&object->id, property_id); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 56168739fae..e561d0b653c 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -114,6 +114,7 @@ #include "SEQ_iterator.h" #include "intern/builder/deg_builder.h" +#include "intern/builder/deg_builder_rna.h" #include "intern/depsgraph.h" #include "intern/depsgraph_tag.h" #include "intern/depsgraph_type.h" @@ -226,7 +227,7 @@ OperationNode *DepsgraphNodeBuilder::add_operation_node(ComponentNode *comp_node comp_node->identifier().c_str(), op_node->identifier().c_str(), op_node); - BLI_assert(!"Should not happen!"); + BLI_assert_msg(0, "Should not happen!"); } return op_node; } @@ -1199,7 +1200,7 @@ void DepsgraphNodeBuilder::build_driver_id_property(ID *id, const char *rna_path if (prop == nullptr) { return; } - if (!RNA_property_is_idprop(prop)) { + if (!rna_prop_affects_parameters_node(&ptr, prop)) { return; } const char *prop_identifier = RNA_property_identifier((PropertyRNA *)prop); @@ -1495,7 +1496,7 @@ void DepsgraphNodeBuilder::build_object_data_geometry(Object *object, bool is_ob add_operation_node( &object->id, NodeType::BATCH_CACHE, - OperationCode::GEOMETRY_SELECT_UPDATE, + OperationCode::BATCH_UPDATE_SELECT, [object_cow](::Depsgraph *depsgraph) { BKE_object_select_update(depsgraph, object_cow); }); } @@ -1516,33 +1517,37 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata, bool if (key) { build_shapekeys(key); } - /* Nodes for result of obdata's evaluation, and geometry - * evaluation on object. */ + + /* Geometry evaluation. */ + /* Entry operation, takes care of initialization, and some other + * relations which needs to be run prior to actual geometry evaluation. */ + op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT); + op_node->set_as_entry(); + + add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DEFORM); + const ID_Type id_type = GS(obdata->name); switch (id_type) { case ID_ME: { - op_node = add_operation_node(obdata, - NodeType::GEOMETRY, - OperationCode::GEOMETRY_EVAL, - [obdata_cow](::Depsgraph *depsgraph) { - BKE_mesh_eval_geometry(depsgraph, (Mesh *)obdata_cow); - }); - op_node->set_as_entry(); + add_operation_node(obdata, + NodeType::GEOMETRY, + OperationCode::GEOMETRY_EVAL, + [obdata_cow](::Depsgraph *depsgraph) { + BKE_mesh_eval_geometry(depsgraph, (Mesh *)obdata_cow); + }); break; } case ID_MB: { - op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); - op_node->set_as_entry(); + add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); break; } case ID_CU: { - op_node = add_operation_node(obdata, - NodeType::GEOMETRY, - OperationCode::GEOMETRY_EVAL, - [obdata_cow](::Depsgraph *depsgraph) { - BKE_curve_eval_geometry(depsgraph, (Curve *)obdata_cow); - }); - op_node->set_as_entry(); + add_operation_node(obdata, + NodeType::GEOMETRY, + OperationCode::GEOMETRY_EVAL, + [obdata_cow](::Depsgraph *depsgraph) { + BKE_curve_eval_geometry(depsgraph, (Curve *)obdata_cow); + }); /* Make sure objects used for bevel.taper are in the graph. * NOTE: This objects might be not linked to the scene. */ Curve *cu = (Curve *)obdata; @@ -1558,51 +1563,45 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata, bool break; } case ID_LT: { - op_node = add_operation_node(obdata, - NodeType::GEOMETRY, - OperationCode::GEOMETRY_EVAL, - [obdata_cow](::Depsgraph *depsgraph) { - BKE_lattice_eval_geometry(depsgraph, (Lattice *)obdata_cow); - }); - op_node->set_as_entry(); + add_operation_node(obdata, + NodeType::GEOMETRY, + OperationCode::GEOMETRY_EVAL, + [obdata_cow](::Depsgraph *depsgraph) { + BKE_lattice_eval_geometry(depsgraph, (Lattice *)obdata_cow); + }); break; } case ID_GD: { /* GPencil evaluation operations. */ - op_node = add_operation_node(obdata, - NodeType::GEOMETRY, - OperationCode::GEOMETRY_EVAL, - [obdata_cow](::Depsgraph *depsgraph) { - BKE_gpencil_frame_active_set(depsgraph, - (bGPdata *)obdata_cow); - }); - op_node->set_as_entry(); + add_operation_node(obdata, + NodeType::GEOMETRY, + OperationCode::GEOMETRY_EVAL, + [obdata_cow](::Depsgraph *depsgraph) { + BKE_gpencil_frame_active_set(depsgraph, (bGPdata *)obdata_cow); + }); break; } case ID_HA: { - op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); - op_node->set_as_entry(); + add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); break; } case ID_PT: { - op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); - op_node->set_as_entry(); + add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); break; } case ID_VO: { /* Volume frame update. */ - op_node = add_operation_node(obdata, - NodeType::GEOMETRY, - OperationCode::GEOMETRY_EVAL, - [obdata_cow](::Depsgraph *depsgraph) { - BKE_volume_eval_geometry(depsgraph, (Volume *)obdata_cow); - }); - op_node->set_as_entry(); + add_operation_node(obdata, + NodeType::GEOMETRY, + OperationCode::GEOMETRY_EVAL, + [obdata_cow](::Depsgraph *depsgraph) { + BKE_volume_eval_geometry(depsgraph, (Volume *)obdata_cow); + }); break; } default: - BLI_assert(!"Should not happen"); + BLI_assert_msg(0, "Should not happen"); break; } op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE); @@ -1612,10 +1611,22 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata, bool /* Batch cache. */ add_operation_node(obdata, NodeType::BATCH_CACHE, - OperationCode::GEOMETRY_SELECT_UPDATE, + OperationCode::BATCH_UPDATE_SELECT, [obdata_cow](::Depsgraph *depsgraph) { BKE_object_data_select_update(depsgraph, obdata_cow); }); + add_operation_node(obdata, + NodeType::BATCH_CACHE, + OperationCode::BATCH_UPDATE_DEFORM, + [obdata_cow](::Depsgraph *depsgraph) { + BKE_object_data_eval_batch_cache_deform_tag(depsgraph, obdata_cow); + }); + add_operation_node(obdata, + NodeType::BATCH_CACHE, + OperationCode::BATCH_UPDATE_ALL, + [obdata_cow](::Depsgraph *depsgraph) { + BKE_object_data_eval_batch_cache_dirty_tag(depsgraph, obdata_cow); + }); } void DepsgraphNodeBuilder::build_armature(bArmature *armature) @@ -1769,7 +1780,7 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) build_nodetree(group_ntree); } else { - BLI_assert(!"Unknown ID type used for node"); + BLI_assert_msg(0, "Unknown ID type used for node"); } } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index c269ad16824..c63b3d825a0 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -326,7 +326,7 @@ void DepsgraphRelationBuilder::add_customdata_mask(Object *object, IDNode *id_node = graph_->find_id_node(&object->id); if (id_node == nullptr) { - BLI_assert(!"ID should always be valid"); + BLI_assert_msg(0, "ID should always be valid"); } else { id_node->customdata_masks |= customdata_masks; @@ -338,7 +338,7 @@ void DepsgraphRelationBuilder::add_special_eval_flag(ID *id, uint32_t flag) { IDNode *id_node = graph_->find_id_node(id); if (id_node == nullptr) { - BLI_assert(!"ID should always be valid"); + BLI_assert_msg(0, "ID should always be valid"); } else { id_node->eval_flags |= flag; @@ -647,7 +647,7 @@ void DepsgraphRelationBuilder::build_collection(LayerCollection *from_layer_coll /* Only create geometry relations to child objects, if they have a geometry component. */ OperationKey object_geometry_key{ - &cob->ob->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL}; + &cob->ob->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT}; if (find_node(object_geometry_key) != nullptr) { add_relation(object_geometry_key, collection_geometry_key, "Collection Geometry"); } @@ -1099,7 +1099,14 @@ void DepsgraphRelationBuilder::build_object_pointcache(Object *object) else { flag = FLAG_GEOMETRY; OperationKey geometry_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); - add_relation(point_cache_key, geometry_key, "Point Cache -> Geometry"); + add_relation(point_cache_key, geometry_key, "Point Cache -> Geometry Eval"); + if (object->data) { + /* Geometry may change, so rebuild the Drawing Cache. */ + OperationKey object_data_batch_all_key( + (ID *)object->data, NodeType::BATCH_CACHE, OperationCode::BATCH_UPDATE_ALL); + add_relation( + point_cache_key, object_data_batch_all_key, "Point Cache -> Batch Update All"); + } } BLI_assert(flag != -1); /* Tag that we did handle that component. */ @@ -1610,7 +1617,7 @@ void DepsgraphRelationBuilder::build_driver_data(ID *id, FCurve *fcu) } } } - if (property_entry_key.prop != nullptr && RNA_property_is_idprop(property_entry_key.prop)) { + if (rna_prop_affects_parameters_node(&property_entry_key.ptr, property_entry_key.prop)) { RNAPathKey property_exit_key(property_entry_key.id, property_entry_key.ptr, property_entry_key.prop, @@ -1713,7 +1720,7 @@ void DepsgraphRelationBuilder::build_driver_id_property(ID *id, const char *rna_ if (prop == nullptr) { return; } - if (!RNA_property_is_idprop(prop)) { + if (!rna_prop_affects_parameters_node(&ptr, prop)) { return; } const char *prop_identifier = RNA_property_identifier((PropertyRNA *)prop); @@ -1868,7 +1875,8 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) void DepsgraphRelationBuilder::build_particle_systems(Object *object) { TimeSourceKey time_src_key; - OperationKey obdata_ubereval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); + OperationKey obdata_ubereval_key( + &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT); OperationKey eval_init_key( &object->id, NodeType::PARTICLE_SYSTEM, OperationCode::PARTICLE_SYSTEM_INIT); OperationKey eval_done_key( @@ -2016,7 +2024,8 @@ void DepsgraphRelationBuilder::build_particle_system_visualization_object(Object { OperationKey psys_key( &object->id, NodeType::PARTICLE_SYSTEM, OperationCode::PARTICLE_SYSTEM_EVAL, psys->name); - OperationKey obdata_ubereval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); + OperationKey obdata_ubereval_key( + &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT); ComponentKey dup_ob_key(&draw_object->id, NodeType::TRANSFORM); add_relation(dup_ob_key, psys_key, "Particle Object Visualization"); if (draw_object->type == OB_MBALL) { @@ -2073,15 +2082,15 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) /* Get nodes for result of obdata's evaluation, and geometry evaluation * on object. */ ComponentKey obdata_geom_key(obdata, NodeType::GEOMETRY); - ComponentKey geom_key(&object->id, NodeType::GEOMETRY); - /* Link components to each other. */ - add_relation(obdata_geom_key, geom_key, "Object Geometry Base Data"); OperationKey obdata_ubereval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); + /* Link components to each other. */ + add_relation(obdata_geom_key, obdata_ubereval_key, "Object Geometry Base Data"); + /* Special case: modifiers evaluation queries scene for various things like * data mask to be used. We add relation here to ensure object is never * evaluated prior to Scene's CoW is ready. */ OperationKey scene_key(&scene_->id, NodeType::PARAMETERS, OperationCode::SCENE_EVAL); - Relation *rel = add_relation(scene_key, obdata_ubereval_key, "CoW Relation"); + Relation *rel = add_relation(scene_key, geom_init_key, "CoW Relation"); rel->flag |= RELATION_FLAG_NO_FLUSH; /* Modifiers */ if (object->modifiers.first != nullptr) { @@ -2091,13 +2100,13 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); if (mti->updateDepsgraph) { - DepsNodeHandle handle = create_node_handle(obdata_ubereval_key); + DepsNodeHandle handle = create_node_handle(geom_init_key); ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle); mti->updateDepsgraph(md, &ctx); } if (BKE_object_modifier_use_time(object, md)) { TimeSourceKey time_src_key; - add_relation(time_src_key, obdata_ubereval_key, "Time Source"); + add_relation(time_src_key, geom_init_key, "Time Source"); } } } @@ -2110,13 +2119,13 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info( (GpencilModifierType)md->type); if (mti->updateDepsgraph) { - DepsNodeHandle handle = create_node_handle(obdata_ubereval_key); + DepsNodeHandle handle = create_node_handle(geom_init_key); ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle); mti->updateDepsgraph(md, &ctx, graph_->mode); } if (BKE_object_modifier_gpencil_use_time(object, md)) { TimeSourceKey time_src_key; - add_relation(time_src_key, obdata_ubereval_key, "Time Source"); + add_relation(time_src_key, geom_init_key, "Time Source"); } } } @@ -2128,13 +2137,13 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) LISTBASE_FOREACH (ShaderFxData *, fx, &object->shader_fx) { const ShaderFxTypeInfo *fxi = BKE_shaderfx_get_info((ShaderFxType)fx->type); if (fxi->updateDepsgraph) { - DepsNodeHandle handle = create_node_handle(obdata_ubereval_key); + DepsNodeHandle handle = create_node_handle(geom_init_key); ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle); fxi->updateDepsgraph(fx, &ctx); } if (BKE_object_shaderfx_use_time(object, fx)) { TimeSourceKey time_src_key; - add_relation(time_src_key, obdata_ubereval_key, "Time Source"); + add_relation(time_src_key, geom_init_key, "Time Source"); } } } @@ -2160,6 +2169,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) add_relation(mom_transform_key, mom_geom_key, "Metaball Motherball Transform -> Geometry"); } else { + ComponentKey geom_key(&object->id, NodeType::GEOMETRY); ComponentKey transform_key(&object->id, NodeType::TRANSFORM); add_relation(geom_key, mom_geom_key, "Metaball Motherball"); add_relation(transform_key, mom_geom_key, "Metaball Motherball"); @@ -2174,9 +2184,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) * Ideally we need to get rid of this relation. */ if (object_particles_depends_on_time(object)) { TimeSourceKey time_key; - OperationKey obdata_ubereval_key( - &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); - add_relation(time_key, obdata_ubereval_key, "Legacy particle time"); + add_relation(time_key, geom_init_key, "Legacy particle time"); } /* Object data data-block. */ build_object_data_geometry_datablock((ID *)object->data); @@ -2198,12 +2206,33 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) add_relation(final_geometry_key, synchronize_key, "Synchronize to Original"); /* Batch cache. */ OperationKey object_data_select_key( - obdata, NodeType::BATCH_CACHE, OperationCode::GEOMETRY_SELECT_UPDATE); + obdata, NodeType::BATCH_CACHE, OperationCode::BATCH_UPDATE_SELECT); OperationKey object_select_key( - &object->id, NodeType::BATCH_CACHE, OperationCode::GEOMETRY_SELECT_UPDATE); + &object->id, NodeType::BATCH_CACHE, OperationCode::BATCH_UPDATE_SELECT); + add_relation(object_data_select_key, object_select_key, "Data Selection -> Object Selection"); + add_relation(final_geometry_key, + object_select_key, + "Object Geometry -> Select Update", + RELATION_FLAG_NO_FLUSH); + + OperationKey object_data_geom_deform_key( + obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DEFORM); + OperationKey object_data_geom_init_key( + obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT); + + OperationKey object_data_batch_deform_key( + obdata, NodeType::BATCH_CACHE, OperationCode::BATCH_UPDATE_DEFORM); + OperationKey object_data_batch_all_key( + obdata, NodeType::BATCH_CACHE, OperationCode::BATCH_UPDATE_ALL); + + add_relation(geom_init_key, object_data_batch_all_key, "Object Geometry -> Batch Update All"); + add_relation( - geom_key, object_select_key, "Object Geometry -> Select Update", RELATION_FLAG_NO_FLUSH); + object_data_geom_init_key, object_data_batch_all_key, "Data Init -> Batch Update All"); + add_relation(object_data_geom_deform_key, + object_data_batch_deform_key, + "Data Deform -> Batch Update Deform"); } void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) @@ -2221,8 +2250,13 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) build_shapekeys(key); } /* Link object data evaluation node to exit operation. */ + OperationKey obdata_geom_deform_key( + obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DEFORM); + OperationKey obdata_geom_init_key(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT); OperationKey obdata_geom_eval_key(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); OperationKey obdata_geom_done_key(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE); + add_relation(obdata_geom_init_key, obdata_geom_eval_key, "ObData Init -> Geom Eval"); + add_relation(obdata_geom_deform_key, obdata_geom_eval_key, "ObData Deform -> Geom Eval"); add_relation(obdata_geom_eval_key, obdata_geom_done_key, "ObData Geom Eval Done"); /* Type-specific links. */ const ID_Type id_type = GS(obdata->name); @@ -2314,7 +2348,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) break; } default: - BLI_assert(!"Should not happen"); + BLI_assert_msg(0, "Should not happen"); break; } } @@ -2497,7 +2531,7 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) add_relation(group_shading_key, shading_key, "Group Node"); } else { - BLI_assert(!"Unknown ID type used for node"); + BLI_assert_msg(0, "Unknown ID type used for node"); } } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc index 2ae29dea213..8e3960e1a15 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc @@ -180,8 +180,7 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, node_identifier.operation_name = ""; node_identifier.operation_name_tag = -1; /* Handling of commonly known scenarios. */ - if (prop != nullptr && RNA_property_is_idprop(prop) && - !RNA_struct_is_a(ptr->type, &RNA_Modifier)) { + if (rna_prop_affects_parameters_node(ptr, prop)) { node_identifier.type = NodeType::PARAMETERS; node_identifier.operation_code = OperationCode::ID_PROPERTY; node_identifier.operation_name = RNA_property_identifier( @@ -398,4 +397,12 @@ RNANodeQueryIDData *RNANodeQuery::ensure_id_data(const ID *id) return id_data.get(); } +bool rna_prop_affects_parameters_node(const PointerRNA *ptr, const PropertyRNA *prop) +{ + return prop != nullptr && RNA_property_is_idprop(prop) && + /* ID properties in the geometry nodes modifier don't affect that parameters node. Instead + they affect the modifier and therefore the geometry node directly. */ + !RNA_struct_is_a(ptr->type, &RNA_NodesModifier); +} + } // namespace blender::deg diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.h b/source/blender/depsgraph/intern/builder/deg_builder_rna.h index d03903d508c..24d7f5f3a30 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.h @@ -108,5 +108,7 @@ class RNANodeQuery { static bool contains(const char *prop_identifier, const char *rna_path_component); }; +bool rna_prop_affects_parameters_node(const PointerRNA *ptr, const PropertyRNA *prop); + } // namespace deg } // namespace blender diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index 6fe7d5f5d8b..4c036417ba0 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -68,7 +68,8 @@ Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluati scene(scene), view_layer(view_layer), mode(mode), - ctime(BKE_scene_frame_get(scene)), + frame(BKE_scene_frame_get(scene)), + ctime(BKE_scene_ctime_get(scene)), scene_cow(nullptr), is_active(false), is_evaluating(false), @@ -267,7 +268,7 @@ ID *Depsgraph::get_cow_id(const ID *id_orig) const * - Object or mesh has material at a slot which is not used (for * example, object has material slot by materials are set to * object data). */ - // BLI_assert(!"Request for non-existing copy-on-write ID"); + // BLI_assert_msg(0, "Request for non-existing copy-on-write ID"); } return (ID *)id_orig; } diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h index ff536c19c05..913b61ca563 100644 --- a/source/blender/depsgraph/intern/depsgraph.h +++ b/source/blender/depsgraph/intern/depsgraph.h @@ -140,7 +140,9 @@ struct Depsgraph { ViewLayer *view_layer; eEvaluationMode mode; - /* Time at which dependency graph is being or was last evaluated. */ + /* Time at which dependency graph is being or was last evaluated. + * frame is the value before, and ctime the value after time remapping. */ + float frame; float ctime; /* Evaluated version of datablocks we access a lot. diff --git a/source/blender/depsgraph/intern/depsgraph_debug.cc b/source/blender/depsgraph/intern/depsgraph_debug.cc index c5e306f3148..3677f80469c 100644 --- a/source/blender/depsgraph/intern/depsgraph_debug.cc +++ b/source/blender/depsgraph/intern/depsgraph_debug.cc @@ -96,7 +96,7 @@ bool DEG_debug_graph_relations_validate(Depsgraph *graph, DEG_graph_build_from_view_layer(temp_depsgraph); if (!DEG_debug_compare(temp_depsgraph, graph)) { fprintf(stderr, "ERROR! Depsgraph wasn't tagged for update when it should have!\n"); - BLI_assert(!"This should not happen!"); + BLI_assert_msg(0, "This should not happen!"); valid = false; } DEG_graph_free(temp_depsgraph); diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc index 1ad3fdbc9da..cc7ce871419 100644 --- a/source/blender/depsgraph/intern/depsgraph_eval.cc +++ b/source/blender/depsgraph/intern/depsgraph_eval.cc @@ -51,7 +51,7 @@ static void deg_flush_updates_and_refresh(deg::Depsgraph *deg_graph) { /* Update the time on the cow scene. */ if (deg_graph->scene_cow) { - BKE_scene_frame_set(deg_graph->scene_cow, deg_graph->ctime); + BKE_scene_frame_set(deg_graph->scene_cow, deg_graph->frame); } deg::deg_graph_flush_updates(deg_graph); @@ -63,10 +63,12 @@ void DEG_evaluate_on_refresh(Depsgraph *graph) { deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph); const Scene *scene = DEG_get_input_scene(graph); - const float ctime = BKE_scene_frame_get(scene); + const float frame = BKE_scene_frame_get(scene); + const float ctime = BKE_scene_ctime_get(scene); - if (ctime != deg_graph->ctime) { + if (deg_graph->frame != frame || ctime != deg_graph->ctime) { deg_graph->tag_time_source(); + deg_graph->frame = frame; deg_graph->ctime = ctime; } @@ -74,10 +76,13 @@ void DEG_evaluate_on_refresh(Depsgraph *graph) } /* Frame-change happened for root scene that graph belongs to. */ -void DEG_evaluate_on_framechange(Depsgraph *graph, float ctime) +void DEG_evaluate_on_framechange(Depsgraph *graph, float frame) { deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph); + const Scene *scene = DEG_get_input_scene(graph); + deg_graph->tag_time_source(); - deg_graph->ctime = ctime; + deg_graph->frame = frame; + deg_graph->ctime = BKE_scene_frame_to_ctime(scene, frame); deg_flush_updates_and_refresh(deg_graph); } diff --git a/source/blender/depsgraph/intern/depsgraph_physics.cc b/source/blender/depsgraph/intern/depsgraph_physics.cc index a2f914b1d89..a70c0c10de0 100644 --- a/source/blender/depsgraph/intern/depsgraph_physics.cc +++ b/source/blender/depsgraph/intern/depsgraph_physics.cc @@ -59,7 +59,7 @@ static ePhysicsRelationType modifier_to_relation_type(unsigned int modifier_type return DEG_PHYSICS_DYNAMIC_BRUSH; } - BLI_assert(!"Unknown collision modifier type"); + BLI_assert_msg(0, "Unknown collision modifier type"); return DEG_PHYSICS_RELATIONS_NUM; } /* Get ID from an ID type object, in a safe manner. This means that object can be nullptr, diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index b00cae87311..34b33e9a6c0 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -117,7 +117,7 @@ void depsgraph_select_tag_to_component_opcode(const ID *id, } else if (is_selectable_data_id_type(id_type)) { *component_type = NodeType::BATCH_CACHE; - *operation_code = OperationCode::GEOMETRY_SELECT_UPDATE; + *operation_code = OperationCode::BATCH_UPDATE_SELECT; } else { *component_type = NodeType::COPY_ON_WRITE; @@ -168,6 +168,11 @@ void depsgraph_tag_to_component_opcode(const ID *id, break; case ID_RECALC_GEOMETRY: depsgraph_geometry_tag_to_component(id, component_type); + *operation_code = OperationCode::GEOMETRY_EVAL_INIT; + break; + case ID_RECALC_GEOMETRY_DEFORM: + depsgraph_geometry_tag_to_component(id, component_type); + *operation_code = OperationCode::GEOMETRY_EVAL_DEFORM; break; case ID_RECALC_ANIMATION: *component_type = NodeType::ANIMATION; @@ -233,7 +238,7 @@ void depsgraph_tag_to_component_opcode(const ID *id, case ID_RECALC_GEOMETRY_ALL_MODES: case ID_RECALC_ALL: case ID_RECALC_PSYS_ALL: - BLI_assert(!"Should not happen"); + BLI_assert_msg(0, "Should not happen"); break; case ID_RECALC_TAG_FOR_UNDO: break; /* Must be ignored by depsgraph. */ @@ -452,7 +457,7 @@ const char *update_source_as_string(eUpdateSource source) case DEG_UPDATE_SOURCE_VISIBILITY: return "VISIBILITY"; } - BLI_assert(!"Should never happen."); + BLI_assert_msg(0, "Should never happen."); return "UNKNOWN"; } @@ -708,6 +713,8 @@ const char *DEG_update_tag_as_string(IDRecalcFlag flag) return "GEOMETRY"; case ID_RECALC_GEOMETRY_ALL_MODES: return "GEOMETRY_ALL_MODES"; + case ID_RECALC_GEOMETRY_DEFORM: + return "GEOMETRY_DEFORM"; case ID_RECALC_ANIMATION: return "ANIMATION"; case ID_RECALC_PSYS_REDO: diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index 6f35143e28f..915b9fedcec 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -223,7 +223,7 @@ bool need_evaluate_operation_at_stage(DepsgraphEvalState *state, case EvaluationStage::SINGLE_THREADED_WORKAROUND: return true; } - BLI_assert(!"Unhandled evaluation stage, should never happen."); + BLI_assert_msg(0, "Unhandled evaluation stage, should never happen."); return false; } diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc index 0d367762b00..346eba5bbc2 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -899,7 +899,7 @@ ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, done = id_copy_inplace_no_main(id_orig, id_cow); } if (!done) { - BLI_assert(!"No idea how to perform CoW on datablock"); + BLI_assert_msg(0, "No idea how to perform CoW on datablock"); } /* Update pointers to nested ID datablocks. */ DEG_COW_PRINT( diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc index 1b24e2b7ad2..2cbb0b52e34 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -144,7 +144,10 @@ inline void flush_handle_component_node(IDNode *id_node, * special component where we don't want all operations to be tagged. * * TODO(sergey): Make this a more generic solution. */ - if (!ELEM(comp_node->type, NodeType::PARTICLE_SETTINGS, NodeType::PARTICLE_SYSTEM)) { + if (!ELEM(comp_node->type, + NodeType::PARTICLE_SETTINGS, + NodeType::PARTICLE_SYSTEM, + NodeType::BATCH_CACHE)) { for (OperationNode *op : comp_node->operations) { op->flag |= DEPSOP_FLAG_NEEDS_UPDATE; } diff --git a/source/blender/depsgraph/intern/node/deg_node.cc b/source/blender/depsgraph/intern/node/deg_node.cc index 0d3563ee3de..fee5342df59 100644 --- a/source/blender/depsgraph/intern/node/deg_node.cc +++ b/source/blender/depsgraph/intern/node/deg_node.cc @@ -48,7 +48,7 @@ const char *nodeClassAsString(NodeClass node_class) case NodeClass::OPERATION: return "OPERATION"; } - BLI_assert(!"Unhandled node class, should never happen."); + BLI_assert_msg(0, "Unhandled node class, should never happen."); return "UNKNOWN"; } @@ -121,7 +121,7 @@ const char *nodeTypeAsString(NodeType type) case NodeType::NUM_TYPES: return "SpecialCase"; } - BLI_assert(!"Unhandled node type, should never happen."); + BLI_assert_msg(0, "Unhandled node type, should never happen."); return "UNKNOWN"; } @@ -177,7 +177,7 @@ eDepsSceneComponentType nodeTypeToSceneComponent(NodeType type) case NodeType::SIMULATION: return DEG_SCENE_COMP_PARAMETERS; } - BLI_assert(!"Unhandled node type, not suppsed to happen."); + BLI_assert_msg(0, "Unhandled node type, not suppsed to happen."); return DEG_SCENE_COMP_PARAMETERS; } @@ -253,7 +253,7 @@ eDepsObjectComponentType nodeTypeToObjectComponent(NodeType type) case NodeType::NUM_TYPES: return DEG_OB_COMP_PARAMETERS; } - BLI_assert(!"Unhandled node type, not suppsed to happen."); + BLI_assert_msg(0, "Unhandled node type, not suppsed to happen."); return DEG_OB_COMP_PARAMETERS; } diff --git a/source/blender/depsgraph/intern/node/deg_node_component.cc b/source/blender/depsgraph/intern/node/deg_node_component.cc index 3573758805c..431bf536b65 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.cc +++ b/source/blender/depsgraph/intern/node/deg_node_component.cc @@ -142,7 +142,7 @@ OperationNode *ComponentNode::get_operation(OperationIDKey key) const "%s: find_operation(%s) failed\n", this->identifier().c_str(), key.identifier().c_str()); - BLI_assert(!"Request for non-existing operation, should not happen"); + BLI_assert_msg(0, "Request for non-existing operation, should not happen"); return nullptr; } return node; @@ -190,7 +190,7 @@ OperationNode *ComponentNode::add_operation(const DepsEvalOperationCb &op, this->identifier().c_str(), op_node->identifier().c_str(), op_node); - BLI_assert(!"Should not happen!"); + BLI_assert_msg(0, "Should not happen!"); } /* attach extra data */ diff --git a/source/blender/depsgraph/intern/node/deg_node_id.cc b/source/blender/depsgraph/intern/node/deg_node_id.cc index 688afe141e9..2b1ebc663fe 100644 --- a/source/blender/depsgraph/intern/node/deg_node_id.cc +++ b/source/blender/depsgraph/intern/node/deg_node_id.cc @@ -53,7 +53,7 @@ const char *linkedStateAsString(eDepsNode_LinkedState_Type linked_state) case DEG_ID_LINKED_DIRECTLY: return "DIRECTLY"; } - BLI_assert(!"Unhandled linked state, should never happen."); + BLI_assert_msg(0, "Unhandled linked state, should never happen."); return "UNKNOWN"; } diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc index 7e57467f905..d98486b83a8 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.cc +++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc @@ -98,6 +98,8 @@ const char *operationCodeAsString(OperationCode opcode) /* Geometry. */ case OperationCode::GEOMETRY_EVAL_INIT: return "GEOMETRY_EVAL_INIT"; + case OperationCode::GEOMETRY_EVAL_DEFORM: + return "GEOMETRY_EVAL_DEFORM"; case OperationCode::GEOMETRY_EVAL: return "GEOMETRY_EVAL"; case OperationCode::GEOMETRY_EVAL_DONE: @@ -160,8 +162,12 @@ const char *operationCodeAsString(OperationCode opcode) case OperationCode::FILE_CACHE_UPDATE: return "FILE_CACHE_UPDATE"; /* Batch cache. */ - case OperationCode::GEOMETRY_SELECT_UPDATE: - return "GEOMETRY_SELECT_UPDATE"; + case OperationCode::BATCH_UPDATE_SELECT: + return "BATCH_UPDATE_SELECT"; + case OperationCode::BATCH_UPDATE_DEFORM: + return "BATCH_UPDATE_DEFORM"; + case OperationCode::BATCH_UPDATE_ALL: + return "BATCH_UPDATE_ALL"; /* Masks. */ case OperationCode::MASK_ANIMATION: return "MASK_ANIMATION"; @@ -205,7 +211,7 @@ const char *operationCodeAsString(OperationCode opcode) case OperationCode::SIMULATION_EVAL: return "SIMULATION_EVAL"; } - BLI_assert(!"Unhandled operation code, should never happen."); + BLI_assert_msg(0, "Unhandled operation code, should never happen."); return "UNKNOWN"; } diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h index a17186da941..b0130d03c69 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.h +++ b/source/blender/depsgraph/intern/node/deg_node_operation.h @@ -100,7 +100,11 @@ enum class OperationCode { /* Initialize evaluation of the geometry. Is an entry operation of geometry * component. */ GEOMETRY_EVAL_INIT, - /* Evaluate the whole geometry, including modifiers. */ + /* Evaluate the geometry, including modifiers, and update only batches that + * are affected by deform operations. */ + GEOMETRY_EVAL_DEFORM, + /* Evaluate the geometry, including modifiers, but don't update the batch + * cache. */ GEOMETRY_EVAL, /* Evaluation of geometry is completely done. */ GEOMETRY_EVAL_DONE, @@ -178,7 +182,9 @@ enum class OperationCode { WORLD_UPDATE, /* Batch caches. -------------------------------------------------------- */ - GEOMETRY_SELECT_UPDATE, + BATCH_UPDATE_SELECT, + BATCH_UPDATE_DEFORM, + BATCH_UPDATE_ALL, /* Masks. --------------------------------------------------------------- */ MASK_ANIMATION, diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index fa97ffb306b..9d4f7865c32 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -481,7 +481,7 @@ static void eevee_render_to_image(void *vedata, time -= shuttertime; break; default: - BLI_assert(!"Invalid motion blur position enum!"); + BLI_assert_msg(0, "Invalid motion blur position enum!"); break; } diff --git a/source/blender/draw/engines/image/image_engine.c b/source/blender/draw/engines/image/image_engine.c index b6fd73e76d1..395d50fbc6b 100644 --- a/source/blender/draw/engines/image/image_engine.c +++ b/source/blender/draw/engines/image/image_engine.c @@ -117,7 +117,7 @@ static void space_image_gpu_texture_get(Image *image, const int sima_flag = sima->flag & ED_space_image_get_display_channel_mask(ibuf); if (sima_flag & SI_SHOW_ZBUF && (ibuf->zbuf || ibuf->zbuf_float || (ibuf->channels == 1))) { if (ibuf->zbuf) { - BLI_assert(!"Integer based depth buffers not supported"); + BLI_assert_msg(0, "Integer based depth buffers not supported"); } else if (ibuf->zbuf_float) { *r_gpu_texture = GPU_texture_create_2d( diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c index ec5d43683d1..9d15f0e176d 100644 --- a/source/blender/draw/engines/overlay/overlay_armature.c +++ b/source/blender/draw/engines/overlay/overlay_armature.c @@ -37,6 +37,7 @@ #include "BKE_action.h" #include "BKE_armature.h" +#include "BKE_deform.h" #include "BKE_modifier.h" #include "DEG_depsgraph_query.h" @@ -2040,7 +2041,8 @@ static void draw_armature_pose(ArmatureDrawContext *ctx) const Object *obact_orig = DEG_get_original_object(draw_ctx->obact); - LISTBASE_FOREACH (bDeformGroup *, dg, &obact_orig->defbase) { + const ListBase *defbase = BKE_object_defgroup_list(obact_orig); + LISTBASE_FOREACH (const bDeformGroup *, dg, defbase) { if (dg->flag & DG_LOCK_WEIGHT) { pchan = BKE_pose_channel_find_name(ob->pose, dg->name); diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.c b/source/blender/draw/engines/overlay/overlay_edit_mesh.c index 7639911286f..a7ed6c777e8 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_mesh.c +++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.c @@ -26,6 +26,7 @@ #include "DNA_mesh_types.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "draw_cache_impl.h" diff --git a/source/blender/draw/engines/overlay/overlay_edit_uv.c b/source/blender/draw/engines/overlay/overlay_edit_uv.c index 01ab47ac1de..c2b130163e8 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_uv.c +++ b/source/blender/draw/engines/overlay/overlay_edit_uv.c @@ -24,6 +24,7 @@ #include "draw_cache_impl.h" #include "draw_manager_text.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_image.h" #include "BKE_layer.h" diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index 81b07b49784..235104245cc 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -200,7 +200,7 @@ static void OVERLAY_cache_init(void *vedata) case CTX_MODE_OBJECT: break; default: - BLI_assert(!"Draw mode invalid"); + BLI_assert_msg(0, "Draw mode invalid"); break; } OVERLAY_antialiasing_cache_init(vedata); diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c index 800d1085505..aaa1a5a6ff6 100644 --- a/source/blender/draw/engines/workbench/workbench_materials.c +++ b/source/blender/draw/engines/workbench/workbench_materials.c @@ -126,7 +126,7 @@ BLI_INLINE void workbench_material_get_image( break; } default: - BLI_assert(!"Node type not supported by workbench"); + BLI_assert_msg(0, "Node type not supported by workbench"); } } } diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index ff3af9b28d1..f5b95ac97ff 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -91,7 +91,7 @@ typedef struct BoundSphere { typedef char DRWViewportEmptyList; #define DRW_VIEWPORT_LIST_SIZE(list) \ - (sizeof(list) == sizeof(DRWViewportEmptyList) ? 0 : ((sizeof(list)) / sizeof(void *))) + (sizeof(list) == sizeof(DRWViewportEmptyList) ? 0 : (sizeof(list) / sizeof(void *))) /* Unused members must be either pass list or 'char *' when not used. */ #define DRW_VIEWPORT_DATA_SIZE(ty) \ diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index 058f859f28d..e2fdfbf5e1c 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -3312,8 +3312,8 @@ GPUBatch *DRW_cache_lattice_wire_get(Object *ob, bool use_weight) Lattice *lt = ob->data; int actdef = -1; - if (use_weight && ob->defbase.first && lt->editlatt->latt->dvert) { - actdef = ob->actdef - 1; + if (use_weight && !BLI_listbase_is_empty(<->vertex_group_names) && lt->editlatt->latt->dvert) { + actdef = lt->vertex_group_active_index - 1; } return DRW_lattice_batch_cache_get_all_edges(lt, use_weight, actdef); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 344150014ed..6d71b01b7e0 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -76,27 +76,23 @@ struct ExtractorRunData { class ExtractorRunDatas : public Vector<ExtractorRunData> { public: - void filter_into(ExtractorRunDatas &result, eMRIterType iter_type) const + void filter_into(ExtractorRunDatas &result, eMRIterType iter_type, const bool is_mesh) const { for (const ExtractorRunData &data : *this) { const MeshExtract *extractor = data.extractor; - if ((iter_type & MR_ITER_LOOPTRI) && extractor->iter_looptri_bm) { - BLI_assert(extractor->iter_looptri_mesh); + if ((iter_type & MR_ITER_LOOPTRI) && *(&extractor->iter_looptri_bm + is_mesh)) { result.append(data); continue; } - if ((iter_type & MR_ITER_POLY) && extractor->iter_poly_bm) { - BLI_assert(extractor->iter_poly_mesh); + if ((iter_type & MR_ITER_POLY) && *(&extractor->iter_poly_bm + is_mesh)) { result.append(data); continue; } - if ((iter_type & MR_ITER_LEDGE) && extractor->iter_ledge_bm) { - BLI_assert(extractor->iter_ledge_mesh); + if ((iter_type & MR_ITER_LEDGE) && *(&extractor->iter_ledge_bm + is_mesh)) { result.append(data); continue; } - if ((iter_type & MR_ITER_LVERT) && extractor->iter_lvert_bm) { - BLI_assert(extractor->iter_lvert_mesh); + if ((iter_type & MR_ITER_LVERT) && *(&extractor->iter_lvert_bm + is_mesh)) { result.append(data); continue; } @@ -427,7 +423,7 @@ BLI_INLINE void extract_task_range_run_iter(const MeshRenderData *mr, return; } - extractors->filter_into(range_data.extractors, iter_type); + extractors->filter_into(range_data.extractors, iter_type, is_mesh); BLI_task_parallel_range(0, stop, &range_data, func, settings); } diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/draw_cache_extract_mesh_private.h index 2424b0f9fee..5f670bdc5ec 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_private.h +++ b/source/blender/draw/intern/draw_cache_extract_mesh_private.h @@ -30,6 +30,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "draw_cache_extract.h" diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c index 7fe543db01f..336ccd40d5c 100644 --- a/source/blender/draw/intern/draw_cache_impl_gpencil.c +++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c @@ -844,8 +844,8 @@ static void gpencil_edit_batches_ensure(Object *ob, GpencilBatchCache *cache, in int vert_len = GPU_vertbuf_get_vertex_len(cache->vbo); gpEditIterData iter; - iter.vgindex = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, iter.vgindex)) { + iter.vgindex = gpd->vertex_group_active_index - 1; + if (!BLI_findlink(&gpd->vertex_group_names, iter.vgindex)) { iter.vgindex = -1; } diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 41047191efe..0c002ff09f2 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -544,8 +544,8 @@ static void drw_mesh_weight_state_extract(Object *ob, /* Extract complete vertex weight group selection state and mode flags. */ memset(wstate, 0, sizeof(*wstate)); - wstate->defgroup_active = ob->actdef - 1; - wstate->defgroup_len = BLI_listbase_count(&ob->defbase); + wstate->defgroup_active = me->vertex_group_active_index - 1; + wstate->defgroup_len = BLI_listbase_count(&me->vertex_group_names); wstate->alert_mode = ts->weightuser; @@ -821,6 +821,17 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) mesh_batch_cache_discard_shaded_tri(cache); mesh_batch_cache_discard_uvedit(cache); break; + case BKE_MESH_BATCH_DIRTY_DEFORM: + FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) { + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.pos_nor); + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.lnor); + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_pos); + GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_nor); + GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.tris); + } + batch_map = MDEPS_CREATE_MAP(vbo.pos_nor, vbo.lnor, vbo.fdots_pos, vbo.fdots_nor, ibo.tris); + mesh_batch_cache_discard_batch(cache, batch_map); + break; case BKE_MESH_BATCH_DIRTY_UVEDIT_ALL: mesh_batch_cache_discard_uvedit(cache); break; diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c index 879b73eab27..5c51f24a435 100644 --- a/source/blender/draw/intern/draw_cache_impl_particles.c +++ b/source/blender/draw/intern/draw_cache_impl_particles.c @@ -1500,7 +1500,7 @@ static void particle_batch_cache_ensure_edit_pos_and_seg(PTCacheEdit *edit, edit, particle, edit->pathcache, 0, edit->totcached, &elb, &data_step); } else { - BLI_assert(!"Hairs are not in edit mode!"); + BLI_assert_msg(0, "Hairs are not in edit mode!"); } hair_cache->indices = GPU_indexbuf_build(&elb); } diff --git a/source/blender/draw/intern/draw_shader.c b/source/blender/draw/intern/draw_shader.c index 9c756065353..121a0acd059 100644 --- a/source/blender/draw/intern/draw_shader.c +++ b/source/blender/draw/intern/draw_shader.c @@ -104,7 +104,7 @@ GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, sh = hair_refine_shader_transform_feedback_workaround_create(refinement); break; default: - BLI_assert(!"Incorrect shader type"); + BLI_assert_msg(0, "Incorrect shader type"); } e_data.hair_refine_sh[refinement] = sh; } diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c index 034378399b9..6af033f3cf2 100644 --- a/source/blender/editors/animation/time_scrub_ui.c +++ b/source/blender/editors/animation/time_scrub_ui.c @@ -109,7 +109,7 @@ static void draw_current_frame(const Scene *scene, if (draw_line) { /* Draw vertical line to from the bottom of the current frame box to the bottom of the screen. */ - const float subframe_x = UI_view2d_view_to_region_x(v2d, BKE_scene_frame_get(scene)); + const float subframe_x = UI_view2d_view_to_region_x(v2d, BKE_scene_ctime_get(scene)); GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt index 0030e78002b..e942bcf2902 100644 --- a/source/blender/editors/armature/CMakeLists.txt +++ b/source/blender/editors/armature/CMakeLists.txt @@ -20,6 +20,7 @@ set(INC ../../blenfont ../../blenkernel ../../blenlib + ../../blenloader ../../blentranslation ../../depsgraph ../../gpu @@ -43,9 +44,11 @@ set(SRC armature_utils.c editarmature_undo.c meshlaplacian.c + pose_backup.c pose_edit.c pose_group.c pose_lib.c + pose_lib_2.c pose_select.c pose_slide.c pose_transform.c diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index d429e51061b..f9950d27e97 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -203,6 +203,10 @@ void POSELIB_OT_pose_move(struct wmOperatorType *ot); void POSELIB_OT_browse_interactive(struct wmOperatorType *ot); void POSELIB_OT_apply_pose(struct wmOperatorType *ot); +/* pose_lib_2.c */ +void POSELIB_OT_apply_pose_asset(struct wmOperatorType *ot); +void POSELIB_OT_blend_pose_asset(struct wmOperatorType *ot); + /* ******************************************************* */ /* Pose Sliding Tools */ /* pose_slide.c */ diff --git a/source/blender/editors/armature/armature_ops.c b/source/blender/editors/armature/armature_ops.c index a0face26bae..fbd89106de5 100644 --- a/source/blender/editors/armature/armature_ops.c +++ b/source/blender/editors/armature/armature_ops.c @@ -131,6 +131,8 @@ void ED_operatortypes_armature(void) /* POSELIB */ WM_operatortype_append(POSELIB_OT_browse_interactive); WM_operatortype_append(POSELIB_OT_apply_pose); + WM_operatortype_append(POSELIB_OT_apply_pose_asset); + WM_operatortype_append(POSELIB_OT_blend_pose_asset); WM_operatortype_append(POSELIB_OT_pose_add); WM_operatortype_append(POSELIB_OT_pose_remove); diff --git a/source/blender/editors/armature/armature_skinning.c b/source/blender/editors/armature/armature_skinning.c index fc9191967f8..ec5c665402b 100644 --- a/source/blender/editors/armature/armature_skinning.c +++ b/source/blender/editors/armature/armature_skinning.c @@ -478,7 +478,7 @@ void ED_object_vgroup_calc_from_armature(ReportList *reports, bArmature *arm = par->data; if (mode == ARM_GROUPS_NAME) { - const int defbase_tot = BLI_listbase_count(&ob->defbase); + const int defbase_tot = BKE_object_defgroup_count(ob); int defbase_add; /* Traverse the bone list, trying to create empty vertex * groups corresponding to the bone. diff --git a/source/blender/editors/armature/editarmature_undo.c b/source/blender/editors/armature/editarmature_undo.c index 725945f8edc..832e75b2a8b 100644 --- a/source/blender/editors/armature/editarmature_undo.c +++ b/source/blender/editors/armature/editarmature_undo.c @@ -206,7 +206,7 @@ static void armature_undosys_step_decode(struct bContext *C, } undoarm_to_editarm(&elem->data, arm); arm->needs_flush_to_id = 1; - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&arm->id, ID_RECALC_GEOMETRY); } /* The first element is always active */ diff --git a/source/blender/editors/armature/pose_backup.c b/source/blender/editors/armature/pose_backup.c new file mode 100644 index 00000000000..dffcd9bdc5a --- /dev/null +++ b/source/blender/editors/armature/pose_backup.c @@ -0,0 +1,139 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edarmature + */ + +#include "ED_armature.h" + +#include <string.h> + +#include "BLI_listbase.h" + +#include "MEM_guardedalloc.h" + +#include "DNA_action_types.h" +#include "DNA_armature_types.h" +#include "DNA_object_types.h" + +#include "BKE_action.h" +#include "BKE_armature.h" +#include "BKE_idprop.h" + +/* simple struct for storing backup info for one pose channel */ +typedef struct PoseChannelBackup { + struct PoseChannelBackup *next, *prev; + + struct bPoseChannel *pchan; /* Pose channel this backup is for. */ + + struct bPoseChannel olddata; /* Backup of pose channel. */ + struct IDProperty *oldprops; /* Backup copy (needs freeing) of pose channel's ID properties. */ +} PoseChannelBackup; + +typedef struct PoseBackup { + bool is_bone_selection_relevant; + ListBase /* PoseChannelBackup* */ backups; +} PoseBackup; + +static PoseBackup *pose_backup_create(const Object *ob, + const bAction *action, + const bool is_bone_selection_relevant) +{ + ListBase backups = {NULL, NULL}; + const bArmature *armature = ob->data; + + /* TODO(Sybren): reuse same approach as in `armature_pose.cc` in this function, as that doesn't + * have the assumption that action group names are bone names. */ + LISTBASE_FOREACH (bActionGroup *, agrp, &action->groups) { + bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, agrp->name); + if (pchan == NULL) { + continue; + } + + if (is_bone_selection_relevant && !PBONE_SELECTED(armature, pchan->bone)) { + continue; + } + + PoseChannelBackup *chan_bak = MEM_callocN(sizeof(*chan_bak), "PoseChannelBackup"); + chan_bak->pchan = pchan; + memcpy(&chan_bak->olddata, chan_bak->pchan, sizeof(chan_bak->olddata)); + + if (pchan->prop) { + chan_bak->oldprops = IDP_CopyProperty(pchan->prop); + } + + BLI_addtail(&backups, chan_bak); + } + + /* PoseBackup is constructed late, so that the above loop can use stack variables. */ + PoseBackup *pose_backup = MEM_callocN(sizeof(*pose_backup), __func__); + pose_backup->is_bone_selection_relevant = is_bone_selection_relevant; + pose_backup->backups = backups; + return pose_backup; +} + +PoseBackup *ED_pose_backup_create_all_bones(const Object *ob, const bAction *action) +{ + return pose_backup_create(ob, action, false); +} + +PoseBackup *ED_pose_backup_create_selected_bones(const Object *ob, const bAction *action) +{ + /* See if bone selection is relevant. */ + bool all_bones_selected = true; + bool no_bones_selected = true; + const bArmature *armature = ob->data; + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + const bool is_selected = PBONE_SELECTED(armature, pchan->bone); + all_bones_selected &= is_selected; + no_bones_selected &= !is_selected; + } + + /* If no bones are selected, act as if all are. */ + const bool is_bone_selection_relevant = !all_bones_selected && !no_bones_selected; + return pose_backup_create(ob, action, is_bone_selection_relevant); +} + +bool ED_pose_backup_is_selection_relevant(const struct PoseBackup *pose_backup) +{ + return pose_backup->is_bone_selection_relevant; +} + +void ED_pose_backup_restore(const PoseBackup *pbd) +{ + LISTBASE_FOREACH (PoseChannelBackup *, chan_bak, &pbd->backups) { + memcpy(chan_bak->pchan, &chan_bak->olddata, sizeof(chan_bak->olddata)); + + if (chan_bak->oldprops) { + IDP_SyncGroupValues(chan_bak->pchan->prop, chan_bak->oldprops); + } + + /* TODO: constraints settings aren't restored yet, + * even though these could change (though not that likely) */ + } +} + +void ED_pose_backup_free(PoseBackup *pbd) +{ + LISTBASE_FOREACH_MUTABLE (PoseChannelBackup *, chan_bak, &pbd->backups) { + if (chan_bak->oldprops) { + IDP_FreeProperty(chan_bak->oldprops); + } + BLI_freelinkN(&pbd->backups, chan_bak); + } + MEM_freeN(pbd); +} diff --git a/source/blender/editors/armature/pose_lib_2.c b/source/blender/editors/armature/pose_lib_2.c new file mode 100644 index 00000000000..eb091296282 --- /dev/null +++ b/source/blender/editors/armature/pose_lib_2.c @@ -0,0 +1,638 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021, Blender Foundation + */ + +/** \file + * \ingroup edarmature + */ + +#include <math.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_string.h" + +#include "BLT_translation.h" + +#include "DNA_armature_types.h" + +#include "BKE_action.h" +#include "BKE_anim_data.h" +#include "BKE_animsys.h" +#include "BKE_armature.h" +#include "BKE_context.h" +#include "BKE_lib_id.h" +#include "BKE_object.h" +#include "BKE_report.h" + +#include "DEG_depsgraph.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_interface.h" + +#include "ED_armature.h" +#include "ED_asset.h" +#include "ED_keyframing.h" +#include "ED_screen.h" + +#include "armature_intern.h" + +typedef enum ePoseBlendState { + POSE_BLEND_INIT, + POSE_BLEND_BLENDING, + POSE_BLEND_ORIGINAL, + POSE_BLEND_CONFIRM, + POSE_BLEND_CANCEL, +} ePoseBlendState; + +typedef struct PoseBlendData { + ePoseBlendState state; + bool needs_redraw; + + struct { + bool use_release_confirm; + int drag_start_xy[2]; + int init_event_type; + + bool cursor_wrap_enabled; + } release_confirm_info; + + /* For temp-loading the Action from the pose library. */ + AssetTempIDConsumer *temp_id_consumer; + + /* Blend factor, interval [0, 1] for interpolating between current and given pose. */ + float blend_factor; + struct PoseBackup *pose_backup; + + Object *ob; /* Object to work on. */ + bAction *act; /* Pose to blend into the current pose. */ + bool free_action; + + Scene *scene; /* For auto-keying. */ + ScrArea *area; /* For drawing status text. */ + + /** Info-text to print in header. */ + char headerstr[UI_MAX_DRAW_STR]; +} PoseBlendData; + +/* Makes a copy of the current pose for restoration purposes - doesn't do constraints currently */ +static void poselib_backup_posecopy(PoseBlendData *pbd) +{ + pbd->pose_backup = ED_pose_backup_create_selected_bones(pbd->ob, pbd->act); + + if (pbd->state == POSE_BLEND_INIT) { + /* Ready for blending now. */ + pbd->state = POSE_BLEND_BLENDING; + } +} + +/* ---------------------------- */ + +/* Auto-key/tag bones affected by the pose Action. */ +static void poselib_keytag_pose(bContext *C, Scene *scene, PoseBlendData *pbd) +{ + if (!autokeyframe_cfra_can_key(scene, &pbd->ob->id)) { + return; + } + + AnimData *adt = BKE_animdata_from_id(&pbd->ob->id); + if (adt != NULL && adt->action != NULL && ID_IS_LINKED(&adt->action->id)) { + /* Changes to linked-in Actions are not allowed. */ + return; + } + + bPose *pose = pbd->ob->pose; + bAction *act = pbd->act; + + KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_WHOLE_CHARACTER_ID); + ListBase dsources = {NULL, NULL}; + + /* start tagging/keying */ + const bArmature *armature = pbd->ob->data; + LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) { + /* only for selected bones unless there aren't any selected, in which case all are included */ + bPoseChannel *pchan = BKE_pose_channel_find_name(pose, agrp->name); + if (pchan == NULL) { + continue; + } + + if (ED_pose_backup_is_selection_relevant(pbd->pose_backup) && + !PBONE_SELECTED(armature, pchan->bone)) { + continue; + } + + /* Add data-source override for the PoseChannel, to be used later. */ + ANIM_relative_keyingset_add_source(&dsources, &pbd->ob->id, &RNA_PoseBone, pchan); + } + + /* Perform actual auto-keying. */ + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); + BLI_freelistN(&dsources); + + /* send notifiers for this */ + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); +} + +/* Apply the relevant changes to the pose */ +static void poselib_blend_apply(bContext *C, wmOperator *op) +{ + PoseBlendData *pbd = (PoseBlendData *)op->customdata; + + if (pbd->state == POSE_BLEND_BLENDING) { + BLI_snprintf(pbd->headerstr, + sizeof(pbd->headerstr), + TIP_("PoseLib blending: \"%s\" at %3.0f%%"), + pbd->act->id.name + 2, + pbd->blend_factor * 100); + ED_area_status_text(pbd->area, pbd->headerstr); + + ED_workspace_status_text( + C, TIP_("Tab: show original pose; Horizontal mouse movement: change blend percentage")); + } + else { + ED_area_status_text(pbd->area, TIP_("PoseLib showing original pose")); + ED_workspace_status_text(C, TIP_("Tab: show blended pose")); + } + + if (!pbd->needs_redraw) { + return; + } + pbd->needs_redraw = false; + + ED_pose_backup_restore(pbd->pose_backup); + + /* The pose needs updating, whether it's for restoring the original pose or for showing the + * result of the blend. */ + DEG_id_tag_update(&pbd->ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, pbd->ob); + + if (pbd->state != POSE_BLEND_BLENDING) { + return; + } + + /* Perform the actual blending. */ + struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph, 0.0f); + BKE_pose_apply_action_blend(pbd->ob, pbd->act, &anim_eval_context, pbd->blend_factor); +} + +/* ---------------------------- */ + +static void poselib_blend_set_factor(PoseBlendData *pbd, const float new_factor) +{ + pbd->blend_factor = CLAMPIS(new_factor, 0.0f, 1.0f); + pbd->needs_redraw = true; +} + +static void poselib_slide_mouse_update_blendfactor(PoseBlendData *pbd, const wmEvent *event) +{ + if (pbd->release_confirm_info.use_release_confirm) { + /* Release confirm calculates factor based on where the dragging was started from. */ + const float range = 300 * U.pixelsize; + const float new_factor = (event->x - pbd->release_confirm_info.drag_start_xy[0]) / range; + poselib_blend_set_factor(pbd, new_factor); + } + else { + const float new_factor = (event->x - pbd->area->v1->vec.x) / ((float)pbd->area->winx); + poselib_blend_set_factor(pbd, new_factor); + } +} + +/* Return operator return value. */ +static int poselib_blend_handle_event(bContext *UNUSED(C), wmOperator *op, const wmEvent *event) +{ + PoseBlendData *pbd = op->customdata; + + if (event->type == MOUSEMOVE) { + poselib_slide_mouse_update_blendfactor(pbd, event); + return OPERATOR_RUNNING_MODAL; + } + + /* Handle the release confirm event directly, it has priority over others. */ + if (pbd->release_confirm_info.use_release_confirm && + (event->type == pbd->release_confirm_info.init_event_type) && (event->val == KM_RELEASE)) { + pbd->state = POSE_BLEND_CONFIRM; + return OPERATOR_RUNNING_MODAL; + } + + /* only accept 'press' event, and ignore 'release', so that we don't get double actions */ + if (ELEM(event->val, KM_PRESS, KM_NOTHING) == 0) { + return OPERATOR_RUNNING_MODAL; + } + + /* NORMAL EVENT HANDLING... */ + /* searching takes priority over normal activity */ + switch (event->type) { + /* Exit - cancel. */ + case EVT_ESCKEY: + case RIGHTMOUSE: + pbd->state = POSE_BLEND_CANCEL; + break; + + /* Exit - confirm. */ + case LEFTMOUSE: + case EVT_RETKEY: + case EVT_PADENTER: + case EVT_SPACEKEY: + pbd->state = POSE_BLEND_CONFIRM; + break; + + /* TODO(Sybren): toggle between original pose and poselib pose. */ + case EVT_TABKEY: + pbd->state = pbd->state == POSE_BLEND_BLENDING ? POSE_BLEND_ORIGINAL : POSE_BLEND_BLENDING; + pbd->needs_redraw = true; + break; + + /* TODO(Sybren): use better UI for slider. */ + } + + return OPERATOR_RUNNING_MODAL; +} + +static void poselib_blend_cursor_update(bContext *C, wmOperator *op) +{ + PoseBlendData *pbd = op->customdata; + + /* Ensure cursor-grab (continuous grabbing) is enabled when using release-confirm. */ + if (pbd->release_confirm_info.use_release_confirm && + !pbd->release_confirm_info.cursor_wrap_enabled) { + WM_cursor_grab_enable(CTX_wm_window(C), WM_CURSOR_WRAP_XY, true, NULL); + pbd->release_confirm_info.cursor_wrap_enabled = true; + } +} + +/* ---------------------------- */ + +static Object *get_poselib_object(bContext *C) +{ + if (C == NULL) { + return NULL; + } + return BKE_object_pose_armature_get(CTX_data_active_object(C)); +} + +static void poselib_tempload_exit(PoseBlendData *pbd) +{ + ED_asset_temp_id_consumer_free(&pbd->temp_id_consumer); +} + +static bAction *poselib_blend_init_get_action(bContext *C, wmOperator *op) +{ + bool asset_handle_valid; + const AssetLibraryReference *asset_library = CTX_wm_asset_library(C); + const AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid); + /* Poll callback should check. */ + BLI_assert((asset_library != NULL) && asset_handle_valid); + + PoseBlendData *pbd = op->customdata; + + pbd->temp_id_consumer = ED_asset_temp_id_consumer_create(&asset_handle); + return (bAction *)ED_asset_temp_id_consumer_ensure_local_id( + pbd->temp_id_consumer, C, asset_library, ID_AC, CTX_data_main(C), op->reports); +} + +static bAction *flip_pose(bContext *C, Object *ob, bAction *action) +{ + bAction *action_copy = (bAction *)BKE_id_copy_ex(NULL, &action->id, NULL, LIB_ID_COPY_LOCALIZE); + + /* Lock the window manager while flipping the pose. Flipping requires temporarily modifying the + * pose, which can cause unwanted visual glitches. */ + wmWindowManager *wm = CTX_wm_manager(C); + const bool interface_was_locked = CTX_wm_interface_locked(C); + WM_set_locked_interface(wm, true); + + BKE_action_flip_with_pose(action_copy, ob); + + WM_set_locked_interface(wm, interface_was_locked); + return action_copy; +} + +/* Return true on success, false if the context isn't suitable. */ +static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent *event) +{ + op->customdata = NULL; + + /* check if valid poselib */ + Object *ob = get_poselib_object(C); + if (ELEM(NULL, ob, ob->pose, ob->data)) { + BKE_report(op->reports, RPT_ERROR, TIP_("Pose lib is only for armatures in pose mode")); + return false; + } + + /* Set up blend state info. */ + PoseBlendData *pbd; + op->customdata = pbd = MEM_callocN(sizeof(PoseBlendData), "PoseLib Preview Data"); + + bAction *action = poselib_blend_init_get_action(C, op); + if (action == NULL) { + return false; + } + + /* Maybe flip the Action. */ + const bool apply_flipped = RNA_boolean_get(op->ptr, "flipped"); + if (apply_flipped) { + action = flip_pose(C, ob, action); + pbd->free_action = true; + } + pbd->act = action; + + /* Get the basic data. */ + pbd->ob = ob; + pbd->ob->pose = ob->pose; + + pbd->scene = CTX_data_scene(C); + pbd->area = CTX_wm_area(C); + + pbd->state = POSE_BLEND_INIT; + pbd->needs_redraw = true; + pbd->blend_factor = RNA_float_get(op->ptr, "blend_factor"); + /* Just to avoid a clang-analyzer warning (false positive), it's set properly below. */ + pbd->release_confirm_info.use_release_confirm = false; + + /* Release confirm data. Only available if there's an event to work with. */ + if (event != NULL) { + PropertyRNA *release_confirm_prop = RNA_struct_find_property(op->ptr, "release_confirm"); + pbd->release_confirm_info.use_release_confirm = (release_confirm_prop != NULL) && + RNA_property_boolean_get(op->ptr, + release_confirm_prop); + } + + if (pbd->release_confirm_info.use_release_confirm) { + BLI_assert(event != NULL); + pbd->release_confirm_info.drag_start_xy[0] = event->x; + pbd->release_confirm_info.drag_start_xy[1] = event->y; + pbd->release_confirm_info.init_event_type = WM_userdef_event_type_from_keymap_type( + event->type); + } + + /* Make backups for blending and restoring the pose. */ + poselib_backup_posecopy(pbd); + + /* Set pose flags to ensure the depsgraph evaluation doesn't overwrite it. */ + pbd->ob->pose->flag &= ~POSE_DO_UNLOCK; + pbd->ob->pose->flag |= POSE_LOCKED; + + return true; +} + +static void poselib_blend_cleanup(bContext *C, wmOperator *op) +{ + PoseBlendData *pbd = op->customdata; + wmWindow *win = CTX_wm_window(C); + + /* Redraw the header so that it doesn't show any of our stuff anymore. */ + ED_area_status_text(pbd->area, NULL); + ED_workspace_status_text(C, NULL); + + /* This signals the depsgraph to unlock and reevaluate the pose on the next evaluation. */ + bPose *pose = pbd->ob->pose; + pose->flag |= POSE_DO_UNLOCK; + + switch (pbd->state) { + case POSE_BLEND_CONFIRM: { + Scene *scene = pbd->scene; + poselib_keytag_pose(C, scene, pbd); + + /* Ensure the redo panel has the actually-used value, instead of the initial value. */ + RNA_float_set(op->ptr, "blend_factor", pbd->blend_factor); + break; + } + + case POSE_BLEND_INIT: + case POSE_BLEND_BLENDING: + case POSE_BLEND_ORIGINAL: + /* Cleanup should not be called directly from these states. */ + BLI_assert(!"poselib_blend_cleanup: unexpected pose blend state"); + BKE_report(op->reports, RPT_ERROR, "Internal pose library error, cancelling operator"); + ATTR_FALLTHROUGH; + case POSE_BLEND_CANCEL: + ED_pose_backup_restore(pbd->pose_backup); + break; + } + + if (pbd->release_confirm_info.cursor_wrap_enabled) { + WM_cursor_grab_disable(win, pbd->release_confirm_info.drag_start_xy); + pbd->release_confirm_info.cursor_wrap_enabled = false; + } + + DEG_id_tag_update(&pbd->ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, pbd->ob); + /* Update mouse-hover highlights. */ + WM_event_add_mousemove(win); +} + +static void poselib_blend_free(wmOperator *op) +{ + PoseBlendData *pbd = op->customdata; + if (pbd == NULL) { + return; + } + + if (pbd->free_action) { + /* Run before #poselib_tempload_exit to avoid any problems from indirectly + * referenced ID pointers. */ + BKE_id_free(NULL, pbd->act); + } + poselib_tempload_exit(pbd); + + /* Must have been dealt with before! */ + BLI_assert(pbd->release_confirm_info.cursor_wrap_enabled == false); + + /* Free temp data for operator */ + ED_pose_backup_free(pbd->pose_backup); + pbd->pose_backup = NULL; + + MEM_SAFE_FREE(op->customdata); +} + +static int poselib_blend_exit(bContext *C, wmOperator *op) +{ + PoseBlendData *pbd = op->customdata; + const ePoseBlendState exit_state = pbd->state; + + poselib_blend_cleanup(C, op); + poselib_blend_free(op); + + if (exit_state == POSE_BLEND_CANCEL) { + return OPERATOR_CANCELLED; + } + return OPERATOR_FINISHED; +} + +/* Cancel previewing operation (called when exiting Blender) */ +static void poselib_blend_cancel(bContext *C, wmOperator *op) +{ + PoseBlendData *pbd = op->customdata; + pbd->state = POSE_BLEND_CANCEL; + poselib_blend_exit(C, op); +} + +/* Main modal status check. */ +static int poselib_blend_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + const int operator_result = poselib_blend_handle_event(C, op, event); + + poselib_blend_cursor_update(C, op); + + const PoseBlendData *pbd = op->customdata; + if (ELEM(pbd->state, POSE_BLEND_CONFIRM, POSE_BLEND_CANCEL)) { + return poselib_blend_exit(C, op); + } + + if (pbd->needs_redraw) { + poselib_blend_apply(C, op); + } + + return operator_result; +} + +/* Modal Operator init. */ +static int poselib_blend_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!poselib_blend_init_data(C, op, event)) { + poselib_blend_free(op); + return OPERATOR_CANCELLED; + } + + /* Do initial apply to have something to look at. */ + poselib_blend_apply(C, op); + + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} + +/* Single-shot apply. */ +static int poselib_blend_exec(bContext *C, wmOperator *op) +{ + if (!poselib_blend_init_data(C, op, NULL)) { + poselib_blend_free(op); + return OPERATOR_CANCELLED; + } + + poselib_blend_apply(C, op); + + PoseBlendData *pbd = op->customdata; + pbd->state = POSE_BLEND_CONFIRM; + return poselib_blend_exit(C, op); +} + +static bool poselib_asset_in_context(bContext *C) +{ + bool asset_handle_valid; + /* Check whether the context provides the asset data needed to add a pose. */ + const AssetLibraryReference *asset_library = CTX_wm_asset_library(C); + AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid); + + return (asset_library != NULL) && asset_handle_valid && + (asset_handle.file_data->blentype == ID_AC); +} + +/* Poll callback for operators that require existing PoseLib data (with poses) to work. */ +static bool poselib_blend_poll(bContext *C) +{ + Object *ob = get_poselib_object(C); + if (ELEM(NULL, ob, ob->pose, ob->data)) { + /* Pose lib is only for armatures in pose mode. */ + return false; + } + + return poselib_asset_in_context(C); +} + +void POSELIB_OT_apply_pose_asset(wmOperatorType *ot) +{ + /* Identifiers: */ + ot->name = "Apply Pose Library Pose"; + ot->idname = "POSELIB_OT_apply_pose_asset"; + ot->description = "Apply the given Pose Action to the rig"; + + /* Callbacks: */ + ot->exec = poselib_blend_exec; + ot->poll = poselib_blend_poll; + + /* Flags: */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties: */ + RNA_def_float_factor(ot->srna, + "blend_factor", + 1.0f, + 0.0f, + 1.0f, + "Blend Factor", + "Amount that the pose is applied on top of the existing poses", + 0.0f, + 1.0f); + RNA_def_boolean(ot->srna, + "flipped", + false, + "Apply Flipped", + "When enabled, applies the pose flipped over the X-axis"); +} + +void POSELIB_OT_blend_pose_asset(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* Identifiers: */ + ot->name = "Blend Pose Library Pose"; + ot->idname = "POSELIB_OT_blend_pose_asset"; + ot->description = "Blend the given Pose Action to the rig"; + + /* Callbacks: */ + ot->invoke = poselib_blend_invoke; + ot->modal = poselib_blend_modal; + ot->cancel = poselib_blend_cancel; + ot->exec = poselib_blend_exec; + ot->poll = poselib_blend_poll; + + /* Flags: */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* Properties: */ + prop = RNA_def_float_factor(ot->srna, + "blend_factor", + 0.0f, + 0.0f, + 1.0f, + "Blend Factor", + "Amount that the pose is applied on top of the existing poses", + 0.0f, + 1.0f); + /* Blending should always start at 0%, and not at whatever percentage was last used. This RNA + * property just exists for symmetry with the Apply operator (and thus simplicity of the rest of + * the code, which can assume this property exists). */ + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + RNA_def_boolean(ot->srna, + "flipped", + false, + "Apply Flipped", + "When enabled, applies the pose flipped over the X-axis"); + prop = RNA_def_boolean(ot->srna, + "release_confirm", + false, + "Confirm on Release", + "Always confirm operation when releasing button"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index f01afcfc578..1a1685e4a01 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -833,7 +833,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) float prevFrameF, nextFrameF; if (!pose_frame_range_from_object_get(pso, pfl->ob, &prevFrameF, &nextFrameF)) { - BLI_assert(!"Invalid pfl data"); + BLI_assert_msg(0, "Invalid pfl data"); return; } diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt index 8c5f91561b7..a27975bc37b 100644 --- a/source/blender/editors/asset/CMakeLists.txt +++ b/source/blender/editors/asset/CMakeLists.txt @@ -19,6 +19,7 @@ set(INC ../include ../../blenkernel ../../blenlib + ../../blenloader ../../makesdna ../../makesrna ../../windowmanager @@ -30,10 +31,14 @@ set(INC_SYS set(SRC asset_edit.cc + asset_list.cc asset_ops.cc + asset_temp_id_consumer.cc ) set(LIB + bf_blenloader + bf_blenkernel ) blender_add_lib(bf_editor_asset "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/asset/asset_edit.cc b/source/blender/editors/asset/asset_edit.cc index d20de4141cb..f4860737193 100644 --- a/source/blender/editors/asset/asset_edit.cc +++ b/source/blender/editors/asset/asset_edit.cc @@ -18,11 +18,18 @@ * \ingroup edasset */ +#include <memory> +#include <string> + #include "BKE_asset.h" #include "BKE_context.h" #include "BKE_lib_id.h" +#include "BLO_readfile.h" + #include "DNA_ID.h" +#include "DNA_asset_types.h" +#include "DNA_space_types.h" #include "UI_interface_icons.h" @@ -30,6 +37,8 @@ #include "ED_asset.h" +using namespace blender; + bool ED_asset_mark_id(const bContext *C, ID *id) { if (id->asset_data) { @@ -45,6 +54,9 @@ bool ED_asset_mark_id(const bContext *C, ID *id) UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true); + /* Important for asset storage to update properly! */ + ED_assetlist_storage_tag_main_data_dirty(); + return true; } @@ -57,6 +69,9 @@ bool ED_asset_clear_id(ID *id) /* Don't clear fake user here, there's no guarantee that it was actually set by * #ED_asset_mark_id(), it might have been something/someone else. */ + /* Important for asset storage to update properly! */ + ED_assetlist_storage_tag_main_data_dirty(); + return true; } @@ -65,3 +80,76 @@ bool ED_asset_can_make_single_from_context(const bContext *C) /* Context needs a "id" pointer to be set for #ASSET_OT_mark()/#ASSET_OT_clear() to use. */ return CTX_data_pointer_get_type_silent(C, "id", &RNA_ID).data != nullptr; } + +/* TODO better place? */ +/* TODO What about the setter and the `itemf` callback? */ +#include "BKE_preferences.h" +#include "DNA_asset_types.h" +#include "DNA_userdef_types.h" +int ED_asset_library_reference_to_enum_value(const AssetLibraryReference *library) +{ + /* Simple case: Predefined repository, just set the value. */ + if (library->type < ASSET_LIBRARY_CUSTOM) { + return library->type; + } + + /* Note that the path isn't checked for validity here. If an invalid library path is used, the + * Asset Browser can give a nice hint on what's wrong. */ + const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index( + &U, library->custom_library_index); + if (user_library) { + return ASSET_LIBRARY_CUSTOM + library->custom_library_index; + } + + BLI_assert(0); + return ASSET_LIBRARY_LOCAL; +} + +AssetLibraryReference ED_asset_library_reference_from_enum_value(int value) +{ + AssetLibraryReference library; + + /* Simple case: Predefined repository, just set the value. */ + if (value < ASSET_LIBRARY_CUSTOM) { + library.type = value; + library.custom_library_index = -1; + BLI_assert(ELEM(value, ASSET_LIBRARY_LOCAL)); + return library; + } + + const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index( + &U, value - ASSET_LIBRARY_CUSTOM); + + /* Note that the path isn't checked for validity here. If an invalid library path is used, the + * Asset Browser can give a nice hint on what's wrong. */ + const bool is_valid = (user_library->name[0] && user_library->path[0]); + if (!user_library) { + library.type = ASSET_LIBRARY_LOCAL; + library.custom_library_index = -1; + } + else if (user_library && is_valid) { + library.custom_library_index = value - ASSET_LIBRARY_CUSTOM; + library.type = ASSET_LIBRARY_CUSTOM; + } + return library; +} + +const char *ED_asset_handle_get_name(const AssetHandle *asset) +{ + return asset->file_data->name; +} + +void ED_asset_handle_get_full_library_path(const bContext *C, + const AssetLibraryReference *asset_library, + const AssetHandle *asset, + char r_full_lib_path[FILE_MAX_LIBEXTRA]) +{ + *r_full_lib_path = '\0'; + + std::string asset_path = ED_assetlist_asset_filepath_get(C, *asset_library, *asset); + if (asset_path.empty()) { + return; + } + + BLO_library_path_explode(asset_path.c_str(), r_full_lib_path, nullptr, nullptr); +} diff --git a/source/blender/editors/asset/asset_list.cc b/source/blender/editors/asset/asset_list.cc new file mode 100644 index 00000000000..dd1c5f360a0 --- /dev/null +++ b/source/blender/editors/asset/asset_list.cc @@ -0,0 +1,637 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + * + * Abstractions to manage runtime asset lists with a global cache for multiple UI elements to + * access. + * Internally this uses the #FileList API and structures from `filelist.c`. This is just because it + * contains most necessary logic already and there's not much time for a more long-term solution. + */ + +#include <optional> +#include <string> + +#include "BKE_asset.h" +#include "BKE_context.h" +#include "BKE_screen.h" + +#include "BLI_function_ref.hh" +#include "BLI_hash.hh" +#include "BLI_map.hh" +#include "BLI_path_util.h" +#include "BLI_utility_mixins.hh" + +#include "DNA_asset_types.h" +#include "DNA_space_types.h" + +#include "BKE_preferences.h" + +#include "ED_asset.h" +#include "ED_fileselect.h" +#include "ED_screen.h" + +#include "WM_api.h" +#include "WM_types.h" + +/* XXX uses private header of file-space. */ +#include "../space_file/filelist.h" + +using namespace blender; + +/** + * Wrapper to add logic to the AssetLibraryReference DNA struct. + */ +class AssetLibraryReferenceWrapper { + const AssetLibraryReference reference_; + + public: + /* Intentionally not `explicit`, allow implicit conversion for convenience. Might have to be + * NOLINT */ + AssetLibraryReferenceWrapper(const AssetLibraryReference &reference); + ~AssetLibraryReferenceWrapper() = default; + + friend bool operator==(const AssetLibraryReferenceWrapper &a, + const AssetLibraryReferenceWrapper &b); + uint64_t hash() const; +}; + +AssetLibraryReferenceWrapper::AssetLibraryReferenceWrapper(const AssetLibraryReference &reference) + : reference_(reference) +{ +} + +bool operator==(const AssetLibraryReferenceWrapper &a, const AssetLibraryReferenceWrapper &b) +{ + return (a.reference_.type == b.reference_.type) && (a.reference_.type == ASSET_LIBRARY_CUSTOM) ? + (a.reference_.custom_library_index == b.reference_.custom_library_index) : + true; +} + +uint64_t AssetLibraryReferenceWrapper::hash() const +{ + uint64_t hash1 = DefaultHash<decltype(reference_.type)>{}(reference_.type); + if (reference_.type != ASSET_LIBRARY_CUSTOM) { + return hash1; + } + + uint64_t hash2 = DefaultHash<decltype(reference_.custom_library_index)>{}( + reference_.custom_library_index); + return hash1 ^ (hash2 * 33); /* Copied from DefaultHash for std::pair. */ +} + +/* -------------------------------------------------------------------- */ +/** \name Asset list API + * + * Internally re-uses #FileList from the File Browser. It does all the heavy lifting already. + * \{ */ + +/** + * RAII wrapper for `FileList` + */ +class FileListWrapper { + static void filelist_free_fn(FileList *list) + { + filelist_free(list); + MEM_freeN(list); + } + + std::unique_ptr<FileList, decltype(&filelist_free_fn)> file_list_; + + public: + explicit FileListWrapper(eFileSelectType filesel_type) + : file_list_(filelist_new(filesel_type), filelist_free_fn) + { + } + FileListWrapper(FileListWrapper &&other) = default; + FileListWrapper &operator=(FileListWrapper &&other) = default; + ~FileListWrapper() + { + /* Destructs the owned pointer. */ + file_list_ = nullptr; + } + + operator FileList *() const + { + return file_list_.get(); + } +}; + +class PreviewTimer { + /* Non-owning! The Window-Manager registers and owns this. */ + wmTimer *timer_ = nullptr; + + public: + void ensureRunning(const bContext *C) + { + if (!timer_) { + timer_ = WM_event_add_timer_notifier( + CTX_wm_manager(C), CTX_wm_window(C), NC_ASSET | ND_ASSET_LIST_PREVIEW, 0.01); + } + } + + void stop(const bContext *C) + { + if (timer_) { + WM_event_remove_timer_notifier(CTX_wm_manager(C), CTX_wm_window(C), timer_); + timer_ = nullptr; + } + } +}; + +class AssetList : NonCopyable { + FileListWrapper filelist_; + AssetLibraryReference library_ref_; + PreviewTimer previews_timer_; + + public: + AssetList() = delete; + AssetList(eFileSelectType filesel_type, const AssetLibraryReference &asset_library_ref); + AssetList(AssetList &&other) = default; + ~AssetList() = default; + + void setup(const AssetFilterSettings *filter_settings = nullptr); + void fetch(const bContext &C); + void ensurePreviewsJob(bContext *C); + void clear(bContext *C); + + bool needsRefetch() const; + void iterate(AssetListIterFn fn) const; + bool listen(const wmNotifier ¬ifier) const; + int size() const; + void tagMainDataDirty() const; + void remapID(ID *id_old, ID *id_new) const; + StringRef filepath() const; +}; + +AssetList::AssetList(eFileSelectType filesel_type, const AssetLibraryReference &asset_library_ref) + : filelist_(filesel_type), library_ref_(asset_library_ref) +{ +} + +void AssetList::setup(const AssetFilterSettings *filter_settings) +{ + FileList *files = filelist_; + + /* TODO there should only be one (FileSelectAssetLibraryUID vs. AssetLibraryReference). */ + FileSelectAssetLibraryUID file_asset_lib_ref; + file_asset_lib_ref.type = library_ref_.type; + file_asset_lib_ref.custom_library_index = library_ref_.custom_library_index; + + bUserAssetLibrary *user_library = nullptr; + + /* Ensure valid repository, or fall-back to local one. */ + if (library_ref_.type == ASSET_LIBRARY_CUSTOM) { + BLI_assert(library_ref_.custom_library_index >= 0); + + user_library = BKE_preferences_asset_library_find_from_index( + &U, library_ref_.custom_library_index); + } + + /* Relevant bits from file_refresh(). */ + /* TODO pass options properly. */ + filelist_setrecursion(files, 1); + filelist_setsorting(files, FILE_SORT_ALPHA, false); + filelist_setlibrary(files, &file_asset_lib_ref); + /* TODO different filtering settings require the list to be reread. That's a no-go for when we + * want to allow showing the same asset library with different filter settings (as in, + * different ID types). The filelist needs to be made smarter somehow, maybe goes together with + * the plan to separate the view (preview caching, filtering, etc. ) from the data. */ + filelist_setfilter_options( + files, + filter_settings != nullptr, + true, + true, /* Just always hide parent, prefer to not add an extra user option for this. */ + FILE_TYPE_BLENDERLIB, + filter_settings ? filter_settings->id_types : FILTER_ID_ALL, + true, + "", + ""); + + char path[FILE_MAXDIR] = ""; + if (user_library) { + BLI_strncpy(path, user_library->path, sizeof(path)); + filelist_setdir(files, path); + } + else { + filelist_setdir(files, path); + } +} + +void AssetList::fetch(const bContext &C) +{ + FileList *files = filelist_; + + if (filelist_needs_force_reset(files)) { + filelist_readjob_stop(files, CTX_wm_manager(&C)); + filelist_clear(files); + } + + if (filelist_needs_reading(files)) { + if (!filelist_pending(files)) { + filelist_readjob_start(files, NC_ASSET | ND_ASSET_LIST_READING, &C); + } + } + filelist_sort(files); + filelist_filter(files); +} + +bool AssetList::needsRefetch() const +{ + return filelist_needs_force_reset(filelist_) || filelist_needs_reading(filelist_); +} + +void AssetList::iterate(AssetListIterFn fn) const +{ + FileList *files = filelist_; + int numfiles = filelist_files_ensure(files); + + for (int i = 0; i < numfiles; i++) { + FileDirEntry *file = filelist_file(files, i); + if (!fn(*file)) { + break; + } + } +} + +void AssetList::ensurePreviewsJob(bContext *C) +{ + FileList *files = filelist_; + int numfiles = filelist_files_ensure(files); + + filelist_cache_previews_set(files, true); + filelist_file_cache_slidingwindow_set(files, 256); + /* TODO fetch all previews for now. */ + filelist_file_cache_block(files, numfiles / 2); + filelist_cache_previews_update(files); + + { + const bool previews_running = filelist_cache_previews_running(files) && + !filelist_cache_previews_done(files); + if (previews_running) { + previews_timer_.ensureRunning(C); + } + else { + /* Preview is not running, no need to keep generating update events! */ + previews_timer_.stop(C); + } + } +} + +void AssetList::clear(bContext *C) +{ + /* Based on #ED_fileselect_clear() */ + + FileList *files = filelist_; + filelist_readjob_stop(files, CTX_wm_manager(C)); + filelist_freelib(files); + filelist_clear(files); + + WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST, nullptr); +} + +/** + * \return True if the asset-list needs a UI redraw. + */ +bool AssetList::listen(const wmNotifier ¬ifier) const +{ + switch (notifier.category) { + case NC_ID: { + if (ELEM(notifier.action, NA_RENAME)) { + return true; + } + break; + } + case NC_ASSET: + if (ELEM(notifier.data, ND_ASSET_LIST, ND_ASSET_LIST_READING, ND_ASSET_LIST_PREVIEW)) { + return true; + } + if (ELEM(notifier.action, NA_ADDED, NA_REMOVED, NA_EDITED)) { + return true; + } + break; + } + + return false; +} + +/** + * \return The number of assets in the list. + */ +int AssetList::size() const +{ + return filelist_files_ensure(filelist_); +} + +void AssetList::tagMainDataDirty() const +{ + if (filelist_needs_reset_on_main_changes(filelist_)) { + /* Full refresh of the file list if local asset data was changed. Refreshing this view + * is cheap and users expect this to be updated immediately. */ + filelist_tag_force_reset(filelist_); + } +} + +void AssetList::remapID(ID * /*id_old*/, ID * /*id_new*/) const +{ + /* Trigger full re-fetch of the file list if main data was changed, don't even attempt remap + * pointers. We could give file list types a id-remap callback, but it's probably not worth it. + * Refreshing local file lists is relatively cheap. */ + tagMainDataDirty(); +} + +StringRef AssetList::filepath() const +{ + return filelist_dir(filelist_); +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Runtime asset list cache + * \{ */ + +/** + * Class managing a global asset list map, each entry being a list for a specific asset library. + */ +class AssetListStorage { + using AssetListMap = Map<AssetLibraryReferenceWrapper, AssetList>; + + public: + /* Purely static class, can't instantiate this. */ + AssetListStorage() = delete; + + static void fetch_library(const AssetLibraryReference &library_reference, + const bContext &C, + const AssetFilterSettings *filter_settings = nullptr); + static void destruct(); + static AssetList *lookup_list(const AssetLibraryReference &library_ref); + static void tagMainDataDirty(); + static void remapID(ID *id_new, ID *id_old); + + private: + static std::optional<eFileSelectType> asset_library_reference_to_fileselect_type( + const AssetLibraryReference &library_reference); + + using is_new_t = bool; + static std::tuple<AssetList &, is_new_t> ensure_list_storage( + const AssetLibraryReference &library_reference, eFileSelectType filesel_type); + + static AssetListMap &global_storage(); +}; + +void AssetListStorage::fetch_library(const AssetLibraryReference &library_reference, + const bContext &C, + const AssetFilterSettings *filter_settings) +{ + std::optional filesel_type = asset_library_reference_to_fileselect_type(library_reference); + if (!filesel_type) { + return; + } + + auto [list, is_new] = ensure_list_storage(library_reference, *filesel_type); + if (is_new || list.needsRefetch()) { + list.setup(filter_settings); + list.fetch(C); + } +} + +void AssetListStorage::destruct() +{ + global_storage().~AssetListMap(); +} + +AssetList *AssetListStorage::lookup_list(const AssetLibraryReference &library_ref) +{ + return global_storage().lookup_ptr(library_ref); +} + +void AssetListStorage::tagMainDataDirty() +{ + for (AssetList &list : global_storage().values()) { + list.tagMainDataDirty(); + } +} + +void AssetListStorage::remapID(ID *id_new, ID *id_old) +{ + for (AssetList &list : global_storage().values()) { + list.remapID(id_new, id_old); + } +} + +std::optional<eFileSelectType> AssetListStorage::asset_library_reference_to_fileselect_type( + const AssetLibraryReference &library_reference) +{ + switch (library_reference.type) { + case ASSET_LIBRARY_CUSTOM: + return FILE_LOADLIB; + case ASSET_LIBRARY_LOCAL: + return FILE_MAIN_ASSET; + } + + return std::nullopt; +} + +std::tuple<AssetList &, AssetListStorage::is_new_t> AssetListStorage::ensure_list_storage( + const AssetLibraryReference &library_reference, eFileSelectType filesel_type) +{ + AssetListMap &storage = global_storage(); + + if (AssetList *list = storage.lookup_ptr(library_reference)) { + return {*list, false}; + } + storage.add(library_reference, AssetList(filesel_type, library_reference)); + return {storage.lookup(library_reference), true}; +} + +/** + * Wrapper for Construct on First Use idiom, to avoid the Static Initialization Fiasco. + */ +AssetListStorage::AssetListMap &AssetListStorage::global_storage() +{ + static AssetListMap global_storage_; + return global_storage_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name C-API + * \{ */ + +/** + * Invoke asset list reading, potentially in a parallel job. Won't wait until the job is done, + * and may return earlier. + */ +void ED_assetlist_storage_fetch(const AssetLibraryReference *library_reference, + const AssetFilterSettings *filter_settings, + const bContext *C) +{ + AssetListStorage::fetch_library(*library_reference, *C, filter_settings); +} + +void ED_assetlist_ensure_previews_job(const AssetLibraryReference *library_reference, bContext *C) +{ + + AssetList *list = AssetListStorage::lookup_list(*library_reference); + if (list) { + list->ensurePreviewsJob(C); + } +} + +void ED_assetlist_clear(const AssetLibraryReference *library_reference, bContext *C) +{ + AssetList *list = AssetListStorage::lookup_list(*library_reference); + if (list) { + list->clear(C); + } +} + +bool ED_assetlist_storage_has_list_for_library(const AssetLibraryReference *library_reference) +{ + return AssetListStorage::lookup_list(*library_reference) != nullptr; +} + +/* TODO expose AssetList with an iterator? */ +void ED_assetlist_iterate(const AssetLibraryReference *library_reference, AssetListIterFn fn) +{ + AssetList *list = AssetListStorage::lookup_list(*library_reference); + if (list) { + list->iterate(fn); + } +} + +/* TODO hack to use the File Browser path, so we can keep all the import logic handled by the asset + * API. Get rid of this once the File Browser is integrated better with the asset list. */ +static const char *assetlist_library_path_from_sfile_get_hack(const bContext *C) +{ + SpaceFile *sfile = CTX_wm_space_file(C); + if (!sfile || !ED_fileselect_is_asset_browser(sfile)) { + return nullptr; + } + + FileAssetSelectParams *asset_select_params = ED_fileselect_get_asset_params(sfile); + if (!asset_select_params) { + return nullptr; + } + + return filelist_dir(sfile->files); +} + +std::string ED_assetlist_asset_filepath_get(const bContext *C, + const AssetLibraryReference &library_reference, + const AssetHandle &asset_handle) +{ + if (asset_handle.file_data->id || !asset_handle.file_data->asset_data) { + return {}; + } + const char *library_path = ED_assetlist_library_path(&library_reference); + if (!library_path) { + library_path = assetlist_library_path_from_sfile_get_hack(C); + } + if (!library_path) { + return {}; + } + const char *asset_relpath = asset_handle.file_data->relpath; + + char path[FILE_MAX_LIBEXTRA]; + BLI_join_dirfile(path, sizeof(path), library_path, asset_relpath); + + return path; +} + +ID *ED_assetlist_asset_local_id_get(const AssetHandle *asset_handle) +{ + return asset_handle->file_data->asset_data ? asset_handle->file_data->id : nullptr; +} + +ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle) +{ + ImBuf *imbuf = filelist_file_getimage(asset_handle->file_data); + if (imbuf) { + return imbuf; + } + + return filelist_geticon_image_ex(asset_handle->file_data); +} + +const char *ED_assetlist_library_path(const AssetLibraryReference *library_reference) +{ + AssetList *list = AssetListStorage::lookup_list(*library_reference); + if (list) { + return list->filepath().data(); + } + return nullptr; +} + +/** + * \return True if the region needs a UI redraw. + */ +bool ED_assetlist_listen(const AssetLibraryReference *library_reference, + const wmNotifier *notifier) +{ + AssetList *list = AssetListStorage::lookup_list(*library_reference); + if (list) { + return list->listen(*notifier); + } + return false; +} + +/** + * \return The number of assets stored in the asset list for \a library_reference, or -1 if there + * is no list fetched for it. + */ +int ED_assetlist_size(const AssetLibraryReference *library_reference) +{ + AssetList *list = AssetListStorage::lookup_list(*library_reference); + if (list) { + return list->size(); + } + return -1; +} + +/** + * Tag all asset lists in the storage that show main data as needing an update (re-fetch). + * + * This only tags the data. If the asset list is visible on screen, the space is still responsible + * for ensuring the necessary redraw. It can use #ED_assetlist_listen() to check if the asset-list + * needs a redraw for a given notifier. + */ +void ED_assetlist_storage_tag_main_data_dirty() +{ + AssetListStorage::tagMainDataDirty(); +} + +/** + * Remapping of ID pointers within the asset lists. Typically called when an ID is deleted to clear + * all references to it (\a id_new is null then). + */ +void ED_assetlist_storage_id_remap(ID *id_old, ID *id_new) +{ + AssetListStorage::remapID(id_old, id_new); +} + +/** + * Can't wait for static deallocation to run. There's nested data allocated with our guarded + * allocator, it will complain about unfreed memory on exit. + */ +void ED_assetlist_storage_exit() +{ + AssetListStorage::destruct(); +} + +/** \} */ diff --git a/source/blender/editors/asset/asset_ops.cc b/source/blender/editors/asset/asset_ops.cc index 8ca1b488a1d..79edd1f8a6a 100644 --- a/source/blender/editors/asset/asset_ops.cc +++ b/source/blender/editors/asset/asset_ops.cc @@ -252,8 +252,41 @@ static void ASSET_OT_clear(wmOperatorType *ot) /* -------------------------------------------------------------------- */ +static bool asset_list_refresh_poll(bContext *C) +{ + const AssetLibraryReference *library = CTX_wm_asset_library(C); + if (!library) { + return false; + } + + return ED_assetlist_storage_has_list_for_library(library); +} + +static int asset_list_refresh_exec(bContext *C, wmOperator *UNUSED(unused)) +{ + const AssetLibraryReference *library = CTX_wm_asset_library(C); + ED_assetlist_clear(library, C); + return OPERATOR_FINISHED; +} + +static void ASSET_OT_list_refresh(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Refresh Asset List"; + ot->description = "Trigger a reread of the assets"; + ot->idname = "ASSET_OT_list_refresh"; + + /* api callbacks */ + ot->exec = asset_list_refresh_exec; + ot->poll = asset_list_refresh_poll; +} + +/* -------------------------------------------------------------------- */ + void ED_operatortypes_asset(void) { WM_operatortype_append(ASSET_OT_mark); WM_operatortype_append(ASSET_OT_clear); + + WM_operatortype_append(ASSET_OT_list_refresh); } diff --git a/source/blender/editors/asset/asset_temp_id_consumer.cc b/source/blender/editors/asset/asset_temp_id_consumer.cc new file mode 100644 index 00000000000..24e1fc86fef --- /dev/null +++ b/source/blender/editors/asset/asset_temp_id_consumer.cc @@ -0,0 +1,113 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + * + * API for temporary loading of asset IDs. + * Uses the `BLO_library_temp_xxx()` API internally. + */ + +#include "DNA_asset_types.h" +#include "DNA_space_types.h" + +#include "BKE_report.h" + +#include "BLI_utility_mixins.hh" + +#include "BLO_readfile.h" + +#include "MEM_guardedalloc.h" + +#include "ED_asset.h" + +using namespace blender; + +class AssetTemporaryIDConsumer : NonCopyable, NonMovable { + const AssetHandle &handle_; + TempLibraryContext *temp_lib_context_ = nullptr; + + public: + AssetTemporaryIDConsumer(const AssetHandle &handle) : handle_(handle) + { + } + ~AssetTemporaryIDConsumer() + { + if (temp_lib_context_) { + BLO_library_temp_free(temp_lib_context_); + } + } + + ID *get_local_id() + { + return ED_assetlist_asset_local_id_get(&handle_); + } + + ID *import_id(const bContext *C, + const AssetLibraryReference &asset_library, + ID_Type id_type, + Main &bmain, + ReportList &reports) + { + const char *asset_name = ED_asset_handle_get_name(&handle_); + char blend_file_path[FILE_MAX_LIBEXTRA]; + ED_asset_handle_get_full_library_path(C, &asset_library, &handle_, blend_file_path); + + temp_lib_context_ = BLO_library_temp_load_id( + &bmain, blend_file_path, id_type, asset_name, &reports); + + if (temp_lib_context_ == nullptr || temp_lib_context_->temp_id == nullptr) { + BKE_reportf(&reports, RPT_ERROR, "Unable to load %s from %s", asset_name, blend_file_path); + return nullptr; + } + + BLI_assert(GS(temp_lib_context_->temp_id->name) == id_type); + return temp_lib_context_->temp_id; + } +}; + +AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const AssetHandle *handle) +{ + if (!handle) { + return nullptr; + } + BLI_assert(handle->file_data->asset_data != nullptr); + return reinterpret_cast<AssetTempIDConsumer *>( + OBJECT_GUARDED_NEW(AssetTemporaryIDConsumer, *handle)); +} + +void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer) +{ + OBJECT_GUARDED_SAFE_DELETE(*consumer, AssetTemporaryIDConsumer); +} + +ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer_, + const bContext *C, + const AssetLibraryReference *asset_library, + ID_Type id_type, + Main *bmain, + ReportList *reports) +{ + if (!(consumer_ && asset_library && bmain && reports)) { + return nullptr; + } + AssetTemporaryIDConsumer *consumer = reinterpret_cast<AssetTemporaryIDConsumer *>(consumer_); + + if (ID *local_id = consumer->get_local_id()) { + return local_id; + } + return consumer->import_id(C, *asset_library, id_type, *bmain, *reports); +} diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index 065763764c1..2be55accd3a 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -485,7 +485,7 @@ Nurb *ED_curve_add_nurbs_primitive( break; default: /* should never happen */ - BLI_assert(!"invalid nurbs type"); + BLI_assert_msg(0, "invalid nurbs type"); return NULL; } diff --git a/source/blender/editors/curve/editcurve_undo.c b/source/blender/editors/curve/editcurve_undo.c index 88f6398567d..210411c6eb5 100644 --- a/source/blender/editors/curve/editcurve_undo.c +++ b/source/blender/editors/curve/editcurve_undo.c @@ -267,7 +267,7 @@ static void curve_undosys_step_decode(struct bContext *C, } undocurve_to_editcurve(bmain, &elem->data, obedit->data, &obedit->shapenr); cu->editnurb->needs_flush_to_id = 1; - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&cu->id, ID_RECALC_GEOMETRY); } /* The first element is always active */ diff --git a/source/blender/editors/curve/editfont_undo.c b/source/blender/editors/curve/editfont_undo.c index b61506d9346..6eaf8971eb0 100644 --- a/source/blender/editors/curve/editfont_undo.c +++ b/source/blender/editors/curve/editfont_undo.c @@ -379,7 +379,7 @@ static void font_undosys_step_decode(struct bContext *C, Curve *cu = obedit->data; undofont_to_editfont(&us->data, cu); - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&cu->id, ID_RECALC_GEOMETRY); ED_undo_object_set_active_or_warn( CTX_data_scene(C), CTX_data_view_layer(C), obedit, us_p->name, &LOG); diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c index d8734c4ae6b..8d60ef3ed12 100644 --- a/source/blender/editors/gpencil/gpencil_add_monkey.c +++ b/source/blender/editors/gpencil/gpencil_add_monkey.c @@ -39,6 +39,32 @@ #include "ED_gpencil.h" +/** + * Populate stroke with point data from data buffers. + * \param gps: Grease pencil stroke + * \param array: Flat array of point data values. Each entry has #GP_PRIM_DATABUF_SIZE values. + * \param totpoints: Total of points + * \param mat: 4x4 transform matrix to transform points into the right coordinate space. + */ +void ED_gpencil_stroke_init_data(bGPDstroke *gps, + const float *array, + const int totpoints, + const float mat[4][4]) +{ + for (int i = 0; i < totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + const int x = GP_PRIM_DATABUF_SIZE * i; + + pt->x = array[x]; + pt->y = array[x + 1]; + pt->z = array[x + 2]; + mul_m4_v3(mat, &pt->x); + + pt->pressure = array[x + 3]; + pt->strength = array[x + 4]; + } +} + /* Definition of the most important info from a color */ typedef struct ColorTemplate { const char *name; @@ -847,115 +873,115 @@ void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4]) /* generate strokes */ gps = BKE_gpencil_stroke_add(frameFills, color_Skin, 270, 75, false); - BKE_gpencil_stroke_add_points(gps, data0, 270, mat); + ED_gpencil_stroke_init_data(gps, data0, 270, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data1, 33, mat); + ED_gpencil_stroke_init_data(gps, data1, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 60, false); - BKE_gpencil_stroke_add_points(gps, data2, 18, mat); + ED_gpencil_stroke_init_data(gps, data2, 18, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 64, 60, false); - BKE_gpencil_stroke_add_points(gps, data3, 64, mat); + ED_gpencil_stroke_init_data(gps, data3, 64, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data4, 33, mat); + ED_gpencil_stroke_init_data(gps, data4, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 64, 60, false); - BKE_gpencil_stroke_add_points(gps, data5, 64, mat); + ED_gpencil_stroke_init_data(gps, data5, 64, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data6, 33, mat); + ED_gpencil_stroke_init_data(gps, data6, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 18, 40, false); - BKE_gpencil_stroke_add_points(gps, data7, 18, mat); + ED_gpencil_stroke_init_data(gps, data7, 18, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Eyes, 49, 60, false); - BKE_gpencil_stroke_add_points(gps, data8, 49, mat); + ED_gpencil_stroke_init_data(gps, data8, 49, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data9, 33, mat); + ED_gpencil_stroke_init_data(gps, data9, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Eyes, 49, 60, false); - BKE_gpencil_stroke_add_points(gps, data10, 49, mat); + ED_gpencil_stroke_init_data(gps, data10, 49, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 40, false); - BKE_gpencil_stroke_add_points(gps, data11, 18, mat); + ED_gpencil_stroke_init_data(gps, data11, 18, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 40, false); - BKE_gpencil_stroke_add_points(gps, data12, 18, mat); + ED_gpencil_stroke_init_data(gps, data12, 18, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data13, 33, mat); + ED_gpencil_stroke_init_data(gps, data13, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data14, 33, mat); + ED_gpencil_stroke_init_data(gps, data14, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 65, 60, false); - BKE_gpencil_stroke_add_points(gps, data15, 65, mat); + ED_gpencil_stroke_init_data(gps, data15, 65, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 34, 60, false); - BKE_gpencil_stroke_add_points(gps, data16, 34, mat); + ED_gpencil_stroke_init_data(gps, data16, 34, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data17, 33, mat); + ED_gpencil_stroke_init_data(gps, data17, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 40, false); - BKE_gpencil_stroke_add_points(gps, data18, 33, mat); + ED_gpencil_stroke_init_data(gps, data18, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 34, 40, false); - BKE_gpencil_stroke_add_points(gps, data19, 34, mat); + ED_gpencil_stroke_init_data(gps, data19, 34, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data20, 33, mat); + ED_gpencil_stroke_init_data(gps, data20, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 64, 60, false); - BKE_gpencil_stroke_add_points(gps, data21, 64, mat); + ED_gpencil_stroke_init_data(gps, data21, 64, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Pupils, 26, 60, false); - BKE_gpencil_stroke_add_points(gps, data22, 26, mat); + ED_gpencil_stroke_init_data(gps, data22, 26, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Pupils, 26, 60, false); - BKE_gpencil_stroke_add_points(gps, data23, 26, mat); + ED_gpencil_stroke_init_data(gps, data23, 26, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data24, 33, mat); + ED_gpencil_stroke_init_data(gps, data24, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 18, 40, false); - BKE_gpencil_stroke_add_points(gps, data25, 18, mat); + ED_gpencil_stroke_init_data(gps, data25, 18, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 18, 40, false); - BKE_gpencil_stroke_add_points(gps, data26, 18, mat); + ED_gpencil_stroke_init_data(gps, data26, 18, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false); - BKE_gpencil_stroke_add_points(gps, data27, 33, mat); + ED_gpencil_stroke_init_data(gps, data27, 33, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); /* update depsgraph */ diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c index e95496b51ee..73c4e64dd9a 100644 --- a/source/blender/editors/gpencil/gpencil_add_stroke.c +++ b/source/blender/editors/gpencil/gpencil_add_stroke.c @@ -235,7 +235,7 @@ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4]) /* generate stroke */ gps = BKE_gpencil_stroke_add(frame_lines, color_black, 175, 75, false); - BKE_gpencil_stroke_add_points(gps, data0, 175, mat); + ED_gpencil_stroke_init_data(gps, data0, 175, mat); BKE_gpencil_stroke_geometry_update(gpd, gps); /* update depsgraph */ diff --git a/source/blender/editors/gpencil/gpencil_armature.c b/source/blender/editors/gpencil/gpencil_armature.c index 24fb0873a86..c800851bb08 100644 --- a/source/blender/editors/gpencil/gpencil_armature.c +++ b/source/blender/editors/gpencil/gpencil_armature.c @@ -384,7 +384,7 @@ static void gpencil_add_verts_to_dgroups( /* loop groups and assign weight */ for (j = 0; j < numbones; j++) { - int def_nr = BLI_findindex(&ob->defbase, dgrouplist[j]); + int def_nr = BLI_findindex(&gpd->vertex_group_names, dgrouplist[j]); if (def_nr < 0) { continue; } @@ -454,7 +454,7 @@ static void gpencil_object_vgroup_calc_from_armature(const bContext *C, bArmature *arm = ob_arm->data; /* always create groups */ - const int defbase_tot = BLI_listbase_count(&ob->defbase); + const int defbase_tot = BKE_object_defgroup_count(ob); int defbase_add; /* Traverse the bone list, trying to create empty vertex * groups corresponding to the bone. diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.c b/source/blender/editors/gpencil/gpencil_bake_animation.c index 1a5e2950e09..2d299230124 100644 --- a/source/blender/editors/gpencil/gpencil_bake_animation.c +++ b/source/blender/editors/gpencil/gpencil_bake_animation.c @@ -38,6 +38,7 @@ #include "BKE_duplilist.h" #include "BKE_gpencil.h" #include "BKE_gpencil_geom.h" +#include "BKE_gpencil_modifier.h" #include "BKE_layer.h" #include "BKE_main.h" #include "BKE_material.h" @@ -303,8 +304,12 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op float matrix[4][4]; BKE_gpencil_layer_transform_matrix_get(depsgraph, elem->ob, gpl_src, matrix); + /* Apply time modifier. */ + int remap_cfra = BKE_gpencil_time_modifier_cfra( + depsgraph, scene, elem->ob, gpl_src, CFRA, false); /* Duplicate frame. */ - bGPDframe *gpf_src = BKE_gpencil_layer_frame_get(gpl_src, CFRA, GP_GETFRAME_USE_PREV); + bGPDframe *gpf_src = BKE_gpencil_layer_frame_get( + gpl_src, remap_cfra, GP_GETFRAME_USE_PREV); if (gpf_src == NULL) { continue; } diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index 2cb1e09d9a6..ee3536c2f3f 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -1409,7 +1409,7 @@ static void gpencil_layer_to_curve(bContext *C, gtd); break; default: - BLI_assert(!"invalid mode"); + BLI_assert_msg(0, "invalid mode"); break; } prev_gps = gps; diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index e272f46d13d..b1e57079d28 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -2165,7 +2165,9 @@ static bool gpencil_vertex_group_poll(bContext *C) Object *ob = CTX_data_active_object(C); if ((ob) && (ob->type == OB_GPENCIL)) { - if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && ob->defbase.first) { + const bGPdata *gpd = (const bGPdata *)ob->data; + if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && + !BLI_listbase_is_empty(&gpd->vertex_group_names)) { if (ELEM(ob->mode, OB_MODE_EDIT_GPENCIL, OB_MODE_SCULPT_GPENCIL)) { return true; } @@ -2180,7 +2182,9 @@ static bool gpencil_vertex_group_weight_poll(bContext *C) Object *ob = CTX_data_active_object(C); if ((ob) && (ob->type == OB_GPENCIL)) { - if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && ob->defbase.first) { + const bGPdata *gpd = (const bGPdata *)ob->data; + if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && + !BLI_listbase_is_empty(&gpd->vertex_group_names)) { if (ob->mode == OB_MODE_WEIGHT_GPENCIL) { return true; } @@ -2333,6 +2337,7 @@ static int gpencil_vertex_group_invert_exec(bContext *C, wmOperator *op) { ToolSettings *ts = CTX_data_tool_settings(C); Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ob->data; /* sanity checks */ if (ELEM(NULL, ts, ob, ob->data)) { @@ -2340,8 +2345,9 @@ static int gpencil_vertex_group_invert_exec(bContext *C, wmOperator *op) } MDeformVert *dvert; - const int def_nr = ob->actdef - 1; - bDeformGroup *defgroup = BLI_findlink(&ob->defbase, def_nr); + const int def_nr = gpd->vertex_group_active_index - 1; + + bDeformGroup *defgroup = BLI_findlink(&gpd->vertex_group_names, def_nr); if (defgroup == NULL) { return OPERATOR_CANCELLED; } @@ -2373,7 +2379,6 @@ static int gpencil_vertex_group_invert_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* notifiers */ - bGPdata *gpd = ob->data; DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); @@ -2403,14 +2408,15 @@ static int gpencil_vertex_group_smooth_exec(bContext *C, wmOperator *op) ToolSettings *ts = CTX_data_tool_settings(C); Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ob->data; /* sanity checks */ if (ELEM(NULL, ts, ob, ob->data)) { return OPERATOR_CANCELLED; } - const int def_nr = ob->actdef - 1; - bDeformGroup *defgroup = BLI_findlink(&ob->defbase, def_nr); + const int def_nr = gpd->vertex_group_active_index - 1; + bDeformGroup *defgroup = BLI_findlink(&gpd->vertex_group_names, def_nr); if (defgroup == NULL) { return OPERATOR_CANCELLED; } @@ -2470,7 +2476,6 @@ static int gpencil_vertex_group_smooth_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* notifiers */ - bGPdata *gpd = ob->data; DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); @@ -2500,6 +2505,7 @@ static int gpencil_vertex_group_normalize_exec(bContext *C, wmOperator *op) { ToolSettings *ts = CTX_data_tool_settings(C); Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ob->data; /* sanity checks */ if (ELEM(NULL, ts, ob, ob->data)) { @@ -2508,8 +2514,8 @@ static int gpencil_vertex_group_normalize_exec(bContext *C, wmOperator *op) MDeformVert *dvert = NULL; MDeformWeight *dw = NULL; - const int def_nr = ob->actdef - 1; - bDeformGroup *defgroup = BLI_findlink(&ob->defbase, def_nr); + const int def_nr = gpd->vertex_group_active_index - 1; + bDeformGroup *defgroup = BLI_findlink(&gpd->vertex_group_names, def_nr); if (defgroup == NULL) { return OPERATOR_CANCELLED; } @@ -2548,7 +2554,6 @@ static int gpencil_vertex_group_normalize_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* notifiers */ - bGPdata *gpd = ob->data; DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); @@ -2576,6 +2581,7 @@ static int gpencil_vertex_group_normalize_all_exec(bContext *C, wmOperator *op) ToolSettings *ts = CTX_data_tool_settings(C); Object *ob = CTX_data_active_object(C); bool lock_active = RNA_boolean_get(op->ptr, "lock_active"); + bGPdata *gpd = ob->data; /* sanity checks */ if (ELEM(NULL, ts, ob, ob->data)) { @@ -2585,8 +2591,8 @@ static int gpencil_vertex_group_normalize_all_exec(bContext *C, wmOperator *op) bDeformGroup *defgroup = NULL; MDeformVert *dvert = NULL; MDeformWeight *dw = NULL; - const int def_nr = ob->actdef - 1; - const int defbase_tot = BLI_listbase_count(&ob->defbase); + const int def_nr = gpd->vertex_group_active_index - 1; + const int defbase_tot = BLI_listbase_count(&gpd->vertex_group_names); if (defbase_tot == 0) { return OPERATOR_CANCELLED; } @@ -2603,7 +2609,7 @@ static int gpencil_vertex_group_normalize_all_exec(bContext *C, wmOperator *op) for (int i = 0; i < gps->totpoints; i++) { dvert = &gps->dvert[i]; for (int v = 0; v < defbase_tot; v++) { - defgroup = BLI_findlink(&ob->defbase, v); + defgroup = BLI_findlink(&gpd->vertex_group_names, v); /* skip NULL or locked groups */ if ((defgroup == NULL) || (defgroup->flag & DG_LOCK_WEIGHT)) { continue; @@ -2629,7 +2635,7 @@ static int gpencil_vertex_group_normalize_all_exec(bContext *C, wmOperator *op) dvert = &gps->dvert[i]; for (int v = 0; v < defbase_tot; v++) { - defgroup = BLI_findlink(&ob->defbase, v); + defgroup = BLI_findlink(&gpd->vertex_group_names, v); /* skip NULL or locked groups */ if ((defgroup == NULL) || (defgroup->flag & DG_LOCK_WEIGHT)) { continue; @@ -2653,7 +2659,6 @@ static int gpencil_vertex_group_normalize_all_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* notifiers */ - bGPdata *gpd = ob->data; DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); @@ -2769,7 +2774,6 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Object *ob_active = CTX_data_active_object(C); - bGPdata *gpd_dst = NULL; bool ok = false; /* Ensure we're in right mode and that the active object is correct */ @@ -2807,7 +2811,7 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - gpd_dst = ob_active->data; + bGPdata *gpd_dst = ob_active->data; Object *ob_dst = ob_active; /* loop and join all data */ @@ -2828,11 +2832,11 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) /* copy vertex groups to the base one's */ int old_idx = 0; - LISTBASE_FOREACH (bDeformGroup *, dg, &ob_iter->defbase) { + LISTBASE_FOREACH (bDeformGroup *, dg, &gpd_src->vertex_group_names) { bDeformGroup *vgroup = MEM_dupallocN(dg); - int idx = BLI_listbase_count(&ob_active->defbase); + int idx = BLI_listbase_count(&gpd_dst->vertex_group_names); BKE_object_defgroup_unique_name(vgroup, ob_active); - BLI_addtail(&ob_active->defbase, vgroup); + BLI_addtail(&gpd_dst->vertex_group_names, vgroup); /* update vertex groups in strokes in original data */ LISTBASE_FOREACH (bGPDlayer *, gpl_src, &gpd->layers) { LISTBASE_FOREACH (bGPDframe *, gpf, &gpl_src->frames) { @@ -2852,8 +2856,9 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) } old_idx++; } - if (ob_active->defbase.first && ob_active->actdef == 0) { - ob_active->actdef = 1; + if (!BLI_listbase_is_empty(&gpd_dst->vertex_group_names) && + gpd_dst->vertex_group_active_index == 0) { + gpd_dst->vertex_group_active_index = 1; } /* add missing materials reading source materials and checking in destination object */ diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 091ff2c16b0..67e1bd5294b 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1522,8 +1522,8 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) pt = gps->points; point2D = (tGPspoint *)tgpf->sbuffer; - const int def_nr = tgpf->ob->actdef - 1; - const bool have_weight = (bool)BLI_findlink(&tgpf->ob->defbase, def_nr); + const int def_nr = tgpf->gpd->vertex_group_active_index - 1; + const bool have_weight = (bool)BLI_findlink(&tgpf->gpd->vertex_group_names, def_nr); if ((ts->gpencil_flags & GP_TOOL_FLAG_CREATE_WEIGHTS) && (have_weight)) { BKE_gpencil_dvert_ensure(gps); diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index 0062e363cdf..8640ffa67cf 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -278,7 +278,7 @@ static void gpencil_stroke_pair_table(bContext *C, tGPDinterpolate_layer *tgpil) { bGPdata *gpd = tgpi->gpd; - const bool only_selected = ((GPENCIL_EDIT_MODE(gpd)) && + const bool only_selected = (GPENCIL_EDIT_MODE(gpd) && ((tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) != 0)); const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); @@ -291,8 +291,7 @@ static void gpencil_stroke_pair_table(bContext *C, LISTBASE_FOREACH (bGPDstroke *, gps_from, &tgpil->prevFrame->strokes) { bGPDstroke *gps_to = NULL; /* only selected */ - if ((GPENCIL_EDIT_MODE(gpd)) && (only_selected) && - ((gps_from->flag & GP_STROKE_SELECT) == 0)) { + if (GPENCIL_EDIT_MODE(gpd) && (only_selected) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { continue; } /* skip strokes that are invalid for current view */ @@ -712,7 +711,7 @@ static bool gpencil_interpolate_set_init_values(bContext *C, wmOperator *op, tGP tgpi->flag, (RNA_enum_get(op->ptr, "layers") == 1), GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS); SET_FLAG_FROM_TEST( tgpi->flag, - ((GPENCIL_EDIT_MODE(tgpi->gpd)) && (RNA_boolean_get(op->ptr, "interpolate_selected_only"))), + (GPENCIL_EDIT_MODE(tgpi->gpd) && (RNA_boolean_get(op->ptr, "interpolate_selected_only"))), GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED); tgpi->flipmode = RNA_enum_get(op->ptr, "flip"); @@ -1249,7 +1248,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) const int step = RNA_int_get(op->ptr, "step"); const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); const bool all_layers = (bool)(RNA_enum_get(op->ptr, "layers") == 1); - const bool only_selected = ((GPENCIL_EDIT_MODE(gpd)) && + const bool only_selected = (GPENCIL_EDIT_MODE(gpd) && (RNA_boolean_get(op->ptr, "interpolate_selected_only") != 0)); eGP_InterpolateFlipMode flipmode = RNA_enum_get(op->ptr, "flip"); @@ -1309,7 +1308,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) LISTBASE_FOREACH (bGPDstroke *, gps_from, &prevFrame->strokes) { bGPDstroke *gps_to = NULL; /* Only selected. */ - if ((GPENCIL_EDIT_MODE(gpd)) && (only_selected) && + if (GPENCIL_EDIT_MODE(gpd) && (only_selected) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { continue; } diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 67d4b7726b5..d6f6dbb2b10 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -938,8 +938,8 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) Depsgraph *depsgraph = p->depsgraph; Object *obact = (Object *)p->ownerPtr.data; RegionView3D *rv3d = p->region->regiondata; - const int def_nr = obact->actdef - 1; - const bool have_weight = (bool)BLI_findlink(&obact->defbase, def_nr); + const int def_nr = gpd->vertex_group_active_index - 1; + const bool have_weight = (bool)BLI_findlink(&gpd->vertex_group_names, def_nr); const char align_flag = ts->gpencil_v3d_align; const bool is_depth = (bool)(align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)); const bool is_lock_axis_view = (bool)(ts->gp_sculpt.lock_axis == 0); diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index a2b4e5dee64..cf49aefe2ea 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -1315,8 +1315,8 @@ static void gpencil_primitive_interaction_end(bContext *C, Brush *brush = tgpi->brush; BrushGpencilSettings *brush_settings = brush->gpencil_settings; - const int def_nr = tgpi->ob->actdef - 1; - const bool have_weight = (bool)BLI_findlink(&tgpi->ob->defbase, def_nr); + const int def_nr = tgpi->gpd->vertex_group_active_index - 1; + const bool have_weight = BLI_findlink(&tgpi->gpd->vertex_group_names, def_nr) != NULL; /* return to normal cursor and header status */ ED_workspace_status_text(C, NULL); diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index efd0f86df03..14caf0c08a7 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -1177,8 +1177,8 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op) gso->object = ob; if (ob) { invert_m4_m4(gso->inv_mat, ob->obmat); - gso->vrgroup = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, gso->vrgroup)) { + gso->vrgroup = gso->gpd->vertex_group_active_index - 1; + if (!BLI_findlink(&gso->gpd->vertex_group_names, gso->vrgroup)) { gso->vrgroup = -1; } /* Check if some modifier can transform the stroke. */ @@ -1352,7 +1352,7 @@ static void gpencil_sculpt_brush_init_stroke(bContext *C, tGP_BrushEditData *gso * - This is useful when animating as it saves that "uh-oh" moment when you realize you've * spent too much time editing the wrong frame. */ - if ((IS_AUTOKEY_ON(scene)) && (gpf->framenum != cfra)) { + if (IS_AUTOKEY_ON(scene) && (gpf->framenum != cfra)) { BKE_gpencil_frame_addcopy(gpl, cfra); /* Need tag to recalculate evaluated data to avoid crashes. */ DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); @@ -1377,7 +1377,7 @@ static float gpencil_sculpt_rotation_eval_get(tGP_BrushEditData *gso, int idx_eval) { /* If multiframe or no modifiers, return 0. */ - if ((GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd)) || (!gso->is_transformed)) { + if (GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd) || (!gso->is_transformed)) { return 0.0f; } @@ -1513,8 +1513,7 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso, } pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt; /* If masked and the point is not selected, skip it. */ - if ((GPENCIL_ANY_SCULPT_MASK(gso->mask)) && - ((pt_active->flag & GP_SPOINT_SELECT) == 0)) { + if (GPENCIL_ANY_SCULPT_MASK(gso->mask) && ((pt_active->flag & GP_SPOINT_SELECT) == 0)) { continue; } index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i; diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index c33b43247fd..69734fa1ba8 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -689,7 +689,7 @@ static int gpencil_select_grouped_exec(bContext *C, wmOperator *op) break; default: - BLI_assert(!"unhandled select grouped gpencil mode"); + BLI_assert_msg(0, "unhandled select grouped gpencil mode"); break; } diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index e6488cfe454..ba3d3b584d7 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -1600,8 +1600,8 @@ void ED_gpencil_vgroup_assign(bContext *C, Object *ob, float weight) { bGPdata *gpd = (bGPdata *)ob->data; const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const int def_nr = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, def_nr)) { + const int def_nr = gpd->vertex_group_active_index - 1; + if (!BLI_findlink(&gpd->vertex_group_names, def_nr)) { return; } @@ -1654,8 +1654,8 @@ void ED_gpencil_vgroup_remove(bContext *C, Object *ob) { bGPdata *gpd = (bGPdata *)ob->data; const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const int def_nr = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, def_nr)) { + const int def_nr = gpd->vertex_group_active_index - 1; + if (!BLI_findlink(&gpd->vertex_group_names, def_nr)) { return; } @@ -1707,8 +1707,8 @@ void ED_gpencil_vgroup_select(bContext *C, Object *ob) { bGPdata *gpd = (bGPdata *)ob->data; const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const int def_nr = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, def_nr)) { + const int def_nr = gpd->vertex_group_active_index - 1; + if (!BLI_findlink(&gpd->vertex_group_names, def_nr)) { return; } @@ -1762,8 +1762,8 @@ void ED_gpencil_vgroup_deselect(bContext *C, Object *ob) { bGPdata *gpd = (bGPdata *)ob->data; const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - const int def_nr = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, def_nr)) { + const int def_nr = gpd->vertex_group_active_index - 1; + if (!BLI_findlink(&gpd->vertex_group_names, def_nr)) { return; } diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c index 16605b6c634..633e371cbd1 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_paint.c +++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c @@ -919,7 +919,7 @@ static bool gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso, pt_active = pt->runtime.pt_orig; if (pt_active != NULL) { /* If masked and the point is not selected, skip it. */ - if ((GPENCIL_ANY_VERTEX_MASK(gso->mask)) && + if (GPENCIL_ANY_VERTEX_MASK(gso->mask) && ((pt_active->flag & GP_SPOINT_SELECT) == 0)) { continue; } diff --git a/source/blender/editors/gpencil/gpencil_weight_paint.c b/source/blender/editors/gpencil/gpencil_weight_paint.c index 6d953a4d8cf..d14322e12b5 100644 --- a/source/blender/editors/gpencil/gpencil_weight_paint.c +++ b/source/blender/editors/gpencil/gpencil_weight_paint.c @@ -252,7 +252,7 @@ static bool brush_draw_apply(tGP_BrushWeightpaintData *gso, } } else { - bDeformGroup *defgroup = BLI_findlink(&gso->object->defbase, gso->vrgroup); + bDeformGroup *defgroup = BLI_findlink(&gso->gpd->vertex_group_names, gso->vrgroup); if (defgroup->flag & DG_LOCK_WEIGHT) { return false; } @@ -308,8 +308,8 @@ static bool gpencil_weightpaint_brush_init(bContext *C, wmOperator *op) gso->scene = scene; gso->object = ob; if (ob) { - gso->vrgroup = ob->actdef - 1; - if (!BLI_findlink(&ob->defbase, gso->vrgroup)) { + gso->vrgroup = gso->gpd->vertex_group_active_index - 1; + if (!BLI_findlink(&gso->gpd->vertex_group_names, gso->vrgroup)) { gso->vrgroup = -1; } } diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 5cf2a9c9dd0..50e53acb376 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -34,9 +34,9 @@ struct ListBase; struct ARegion; struct ARegionType; +struct FModifier; struct Main; struct NlaStrip; -struct FModifier; struct PanelType; struct ReportList; struct ScrArea; diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 85563b76f38..868235c36e5 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -23,6 +23,10 @@ #pragma once +#include <stdbool.h> + +#include "BLI_listbase.h" + #ifdef __cplusplus extern "C" { #endif @@ -41,6 +45,7 @@ struct Scene; struct UndoType; struct View3D; struct ViewLayer; +struct bAction; struct bArmature; struct bContext; struct bPoseChannel; @@ -242,6 +247,17 @@ void ED_mesh_deform_bind_callback(struct MeshDeformModifierData *mmd, int totvert, float cagemat[4][4]); +/* Pose backups, pose_backup.c */ +struct PoseBackup; +/* Create a backup of those bones that are animated in the given action. */ +struct PoseBackup *ED_pose_backup_create_selected_bones( + const struct Object *ob, const struct bAction *action) ATTR_WARN_UNUSED_RESULT; +struct PoseBackup *ED_pose_backup_create_all_bones( + const struct Object *ob, const struct bAction *action) ATTR_WARN_UNUSED_RESULT; +bool ED_pose_backup_is_selection_relevant(const struct PoseBackup *pose_backup); +void ED_pose_backup_restore(const struct PoseBackup *pbd); +void ED_pose_backup_free(struct PoseBackup *pbd); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/ED_asset.h b/source/blender/editors/include/ED_asset.h index dd505167fe5..0058c0615c3 100644 --- a/source/blender/editors/include/ED_asset.h +++ b/source/blender/editors/include/ED_asset.h @@ -20,17 +20,80 @@ #pragma once +#include "DNA_ID_enums.h" + #ifdef __cplusplus extern "C" { #endif +struct AssetFilterSettings; +struct AssetLibraryReference; +struct Main; +struct ReportList; +struct bContext; +struct wmNotifier; + +typedef struct AssetTempIDConsumer AssetTempIDConsumer; + bool ED_asset_mark_id(const struct bContext *C, struct ID *id); bool ED_asset_clear_id(struct ID *id); bool ED_asset_can_make_single_from_context(const struct bContext *C); +int ED_asset_library_reference_to_enum_value(const struct AssetLibraryReference *library); +struct AssetLibraryReference ED_asset_library_reference_from_enum_value(int value); + +const char *ED_asset_handle_get_name(const AssetHandle *asset); +void ED_asset_handle_get_full_library_path(const struct bContext *C, + const AssetLibraryReference *asset_library, + const AssetHandle *asset, + char r_full_lib_path[]); + +AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const AssetHandle *handle); +void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer); +struct ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer, + const struct bContext *C, + const AssetLibraryReference *asset_library, + ID_Type id_type, + struct Main *bmain, + struct ReportList *reports); + +void ED_assetlist_storage_fetch(const struct AssetLibraryReference *library_reference, + const struct AssetFilterSettings *filter_settings, + const struct bContext *C); +void ED_assetlist_ensure_previews_job(const struct AssetLibraryReference *library_reference, + struct bContext *C); +void ED_assetlist_clear(const struct AssetLibraryReference *library_reference, struct bContext *C); +bool ED_assetlist_storage_has_list_for_library(const AssetLibraryReference *library_reference); +void ED_assetlist_storage_tag_main_data_dirty(void); +void ED_assetlist_storage_id_remap(struct ID *id_old, struct ID *id_new); +void ED_assetlist_storage_exit(void); + +ID *ED_assetlist_asset_local_id_get(const AssetHandle *asset_handle); +struct ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle); +const char *ED_assetlist_library_path(const struct AssetLibraryReference *library_reference); + +bool ED_assetlist_listen(const struct AssetLibraryReference *library_reference, + const struct wmNotifier *notifier); +int ED_assetlist_size(const struct AssetLibraryReference *library_reference); + void ED_operatortypes_asset(void); #ifdef __cplusplus } #endif + +/* TODO move to C++ asset-list header? */ +#ifdef __cplusplus + +# include <string> + +std::string ED_assetlist_asset_filepath_get(const bContext *C, + const AssetLibraryReference &library_reference, + const AssetHandle &asset_handle); + +# include "BLI_function_ref.hh" +/* Can return false to stop iterating. */ +using AssetListIterFn = blender::FunctionRef<bool(FileDirEntry &)>; +void ED_assetlist_iterate(const AssetLibraryReference *library_reference, AssetListIterFn fn); +#endif diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index 59b5a1abaa6..8a8d91a570c 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -251,6 +251,13 @@ void ED_gpencil_brush_draw_eraser(struct Brush *brush, int x, int y); /* ----------- Add Primitive Utilities -------------- */ +/* Number of values defining each point in the built-in data buffers for primitives. */ +#define GP_PRIM_DATABUF_SIZE 5 +void ED_gpencil_stroke_init_data(struct bGPDstroke *gps, + const float *array, + const int totpoints, + const float mat[4][4]); + void ED_gpencil_create_blank(struct bContext *C, struct Object *ob, float mat[4][4]); void ED_gpencil_create_monkey(struct bContext *C, struct Object *ob, float mat[4][4]); void ED_gpencil_create_stroke(struct bContext *C, struct Object *ob, float mat[4][4]); diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h index 0493832c06f..673f629d6ef 100644 --- a/source/blender/editors/include/ED_keyframing.h +++ b/source/blender/editors/include/ED_keyframing.h @@ -468,7 +468,7 @@ bool fcurve_is_changed(struct PointerRNA ptr, * Checks whether a keyframe exists for the given ID-block one the given frame. * - It is recommended to call this method over the other keyframe-checkers directly, * in case some detail of the implementation changes... - * - frame: the value of this is quite often result of #BKE_scene_frame_get() + * - frame: the value of this is quite often result of #BKE_scene_ctime_get() */ bool id_frame_has_keyframe(struct ID *id, float frame, short filter); diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 667540b8f63..2b73194afb2 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -86,7 +86,7 @@ void EDBM_mesh_clear(struct BMEditMesh *em); void EDBM_selectmode_to_scene(struct bContext *C); void EDBM_mesh_make(struct Object *ob, const int select_mode, const bool add_key_index); -void EDBM_mesh_free(struct BMEditMesh *em); +void EDBM_mesh_free_data(struct BMEditMesh *em); void EDBM_mesh_load_ex(struct Main *bmain, struct Object *ob, bool free_data); void EDBM_mesh_load(struct Main *bmain, struct Object *ob); diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h index 058d4fa91a3..6e4002fcc0a 100644 --- a/source/blender/editors/include/ED_node.h +++ b/source/blender/editors/include/ED_node.h @@ -31,6 +31,7 @@ struct ID; struct Main; struct Scene; struct ScrArea; +struct SpaceNode; struct Tex; struct View2D; struct bContext; @@ -40,7 +41,6 @@ struct bNodeSocketType; struct bNodeTree; struct bNodeTreeType; struct bNodeType; -struct SpaceNode; typedef enum { NODE_TOP = 1, diff --git a/source/blender/editors/include/ED_particle.h b/source/blender/editors/include/ED_particle.h index 6d0172e724a..5318c653b6d 100644 --- a/source/blender/editors/include/ED_particle.h +++ b/source/blender/editors/include/ED_particle.h @@ -34,9 +34,9 @@ struct ParticleSystem; struct Scene; struct UndoType; struct ViewLayer; -struct wmGenericUserData; struct bContext; struct rcti; +struct wmGenericUserData; /* particle edit mode */ void PE_free_ptcache_edit(struct PTCacheEdit *edit); diff --git a/source/blender/editors/include/ED_spreadsheet.h b/source/blender/editors/include/ED_spreadsheet.h index ff77135a51c..dfa8aa7bfbc 100644 --- a/source/blender/editors/include/ED_spreadsheet.h +++ b/source/blender/editors/include/ED_spreadsheet.h @@ -16,14 +16,14 @@ #pragma once -struct SpreadsheetContext; -struct SpaceSpreadsheet; -struct SpaceNode; struct ID; -struct bNode; struct Main; -struct bContext; struct Object; +struct SpaceNode; +struct SpaceSpreadsheet; +struct SpreadsheetContext; +struct bContext; +struct bNode; #ifdef __cplusplus extern "C" { diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 6901176a587..a25aac5803c 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -25,6 +25,7 @@ #include "BLI_compiler_attrs.h" #include "BLI_sys_types.h" /* size_t */ +#include "BLI_utildefines.h" #include "UI_interface_icons.h" #ifdef __cplusplus @@ -34,6 +35,7 @@ extern "C" { /* Struct Declarations */ struct ARegion; +struct AssetFilterSettings; struct AutoComplete; struct EnumPropertyItem; struct FileSelectParams; @@ -58,6 +60,7 @@ struct bNodeTree; struct bScreen; struct rctf; struct rcti; +struct uiBlockInteraction_Handle; struct uiButSearch; struct uiFontStyle; struct uiList; @@ -365,6 +368,9 @@ typedef enum { /** Buttons with value >= #UI_BTYPE_SEARCH_MENU don't get undo pushes. */ UI_BTYPE_SEARCH_MENU = 41 << 9, UI_BTYPE_EXTRA = 42 << 9, + /** A preview image (#PreviewImage), with text under it. Typically bigger than normal buttons and + * laid out in a grid, e.g. like the File Browser in thumbnail display mode. */ + UI_BTYPE_PREVIEW_TILE = 43 << 9, UI_BTYPE_HOTKEY_EVENT = 46 << 9, /** Non-interactive image, used for splash screen */ UI_BTYPE_IMAGE = 47 << 9, @@ -514,6 +520,54 @@ typedef int (*uiButPushedStateFunc)(struct uiBut *but, const void *arg); typedef void (*uiBlockHandleFunc)(struct bContext *C, void *arg, int event); +/* -------------------------------------------------------------------- */ +/** \name Custom Interaction + * + * Sometimes it's useful to create data that remains available + * while the user interacts with a button. + * + * A common case is dragging a number button or slider + * however this could be used in other cases too. + * \{ */ + +struct uiBlockInteraction_Params { + /** + * When true, this interaction is not modal + * (user clicking on a number button arrows or pasting a value for example). + */ + bool is_click; + /** + * Array of unique event ID's (values from #uiBut.retval). + * There may be more than one for multi-button editing (see #UI_BUT_DRAG_MULTI). + */ + int *unique_retval_ids; + uint unique_retval_ids_len; +}; + +/** Returns 'user_data', freed by #uiBlockInteractionEndFn. */ +typedef void *(*uiBlockInteractionBeginFn)(struct bContext *C, + const struct uiBlockInteraction_Params *params, + void *arg1); +typedef void (*uiBlockInteractionEndFn)(struct bContext *C, + const struct uiBlockInteraction_Params *params, + void *arg1, + void *user_data); +typedef void (*uiBlockInteractionUpdateFn)(struct bContext *C, + const struct uiBlockInteraction_Params *params, + void *arg1, + void *user_data); + +typedef struct uiBlockInteraction_CallbackData { + uiBlockInteractionBeginFn begin_fn; + uiBlockInteractionEndFn end_fn; + uiBlockInteractionUpdateFn update_fn; + void *arg1; +} uiBlockInteraction_CallbackData; + +void UI_block_interaction_set(uiBlock *block, uiBlockInteraction_CallbackData *callbacks); + +/** \} */ + /* Menu Callbacks */ typedef void (*uiMenuCreateFunc)(struct bContext *C, struct uiLayout *layout, void *arg1); @@ -538,6 +592,8 @@ bool UI_block_is_empty_ex(const uiBlock *block, const bool skip_title); bool UI_block_is_empty(const uiBlock *block); bool UI_block_can_add_separator(const uiBlock *block); +struct uiList *UI_list_find_mouse_over(const struct ARegion *region, const struct wmEvent *event); + /* interface_region_menu_popup.c */ /** * Popup Menus @@ -655,6 +711,7 @@ void UI_block_end_ex(const struct bContext *C, uiBlock *block, const int xy[2], void UI_block_end(const struct bContext *C, uiBlock *block); void UI_block_draw(const struct bContext *C, struct uiBlock *block); void UI_blocklist_update_window_matrix(const struct bContext *C, const struct ListBase *lb); +void UI_blocklist_update_view_for_buttons(const struct bContext *C, const struct ListBase *lb); void UI_blocklist_draw(const struct bContext *C, const struct ListBase *lb); void UI_block_update_from_old(const struct bContext *C, struct uiBlock *block); @@ -2145,6 +2202,17 @@ void uiTemplateCacheFile(uiLayout *layout, /* Default UIList class name, keep in sync with its declaration in bl_ui/__init__.py */ #define UI_UL_DEFAULT_CLASS_NAME "UI_UL_list" +enum uiTemplateListFlags { + UI_TEMPLATE_LIST_FLAG_NONE = 0, + UI_TEMPLATE_LIST_SORT_REVERSE = (1 << 0), + UI_TEMPLATE_LIST_SORT_LOCK = (1 << 1), + /* Don't allow resizing the list, i.e. don't add the grip button. */ + UI_TEMPLATE_LIST_NO_GRIP = (1 << 2), + + UI_TEMPLATE_LIST_FLAGS_LAST +}; +ENUM_OPERATORS(enum uiTemplateListFlags, UI_TEMPLATE_LIST_FLAGS_LAST); + void uiTemplateList(uiLayout *layout, struct bContext *C, const char *listtype_name, @@ -2158,8 +2226,23 @@ void uiTemplateList(uiLayout *layout, int maxrows, int layout_type, int columns, - bool sort_reverse, - bool sort_lock); + enum uiTemplateListFlags flags); +struct uiList *uiTemplateList_ex(uiLayout *layout, + struct bContext *C, + const char *listtype_name, + const char *list_id, + struct PointerRNA *dataptr, + const char *propname, + struct PointerRNA *active_dataptr, + const char *active_propname, + const char *item_dyntip_propname, + int rows, + int maxrows, + int layout_type, + int columns, + enum uiTemplateListFlags flags, + void *customdata); + void uiTemplateNodeLink(uiLayout *layout, struct bContext *C, struct bNodeTree *ntree, @@ -2205,6 +2288,27 @@ int uiTemplateRecentFiles(struct uiLayout *layout, int rows); void uiTemplateFileSelectPath(uiLayout *layout, struct bContext *C, struct FileSelectParams *params); +void uiTemplateAssetView(struct uiLayout *layout, + struct bContext *C, + const char *list_id, + struct PointerRNA *asset_library_dataptr, + const char *asset_library_propname, + struct PointerRNA *assets_dataptr, + const char *assets_propname, + struct PointerRNA *active_dataptr, + const char *active_propname, + const struct AssetFilterSettings *filter_settings, + const char *activate_opname, + struct PointerRNA *r_activate_op_properties, + const char *drag_opname, + struct PointerRNA *r_drag_op_properties); + +struct PointerRNA *UI_list_custom_activate_operator_set(struct uiList *ui_list, + const char *opname, + bool create_properties); +struct PointerRNA *UI_list_custom_drag_operator_set(struct uiList *ui_list, + const char *opname, + bool create_properties); /* items */ void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname); @@ -2469,6 +2573,7 @@ typedef struct uiDragColorHandle { void ED_operatortypes_ui(void); void ED_keymap_ui(struct wmKeyConfig *keyconf); +void ED_uilisttypes_ui(void); void UI_drop_color_copy(struct wmDrag *drag, struct wmDropBox *drop); bool UI_drop_color_poll(struct bContext *C, @@ -2569,6 +2674,8 @@ bool UI_editsource_enable_check(void); void UI_editsource_active_but_test(uiBut *but); void UI_editsource_but_replace(const uiBut *old_but, uiBut *new_but); +void UI_but_ensure_in_view(const struct bContext *C, struct ARegion *region, const uiBut *but); + /* UI_butstore_ helpers */ typedef struct uiButStore uiButStore; typedef struct uiButStoreElem uiButStoreElem; diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h index 266a538b6c3..37cf7229ffb 100644 --- a/source/blender/editors/include/UI_interface_icons.h +++ b/source/blender/editors/include/UI_interface_icons.h @@ -105,7 +105,10 @@ int UI_iconfile_get_index(const char *filename); struct PreviewImage *UI_icon_to_preview(int icon_id); -int UI_icon_from_rnaptr(struct bContext *C, struct PointerRNA *ptr, int rnaicon, const bool big); +int UI_icon_from_rnaptr(const struct bContext *C, + struct PointerRNA *ptr, + int rnaicon, + const bool big); int UI_icon_from_idcode(const int idcode); int UI_icon_from_library(const struct ID *id); int UI_icon_from_object_mode(const int mode); diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 2cc3830042c..39dd6143eb9 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -66,6 +66,8 @@ set(SRC interface_region_tooltip.c interface_regions.c interface_style.c + interface_template_asset_view.cc + interface_template_list.cc interface_template_search_menu.c interface_template_search_operator.c interface_templates.c diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 32b86119753..ddde4f5a9dc 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -131,12 +131,10 @@ static bool ui_but_is_unit_radians(const uiBut *but) /* ************* window matrix ************** */ -void ui_block_to_window_fl(const ARegion *region, uiBlock *block, float *r_x, float *r_y) +void ui_block_to_region_fl(const ARegion *region, uiBlock *block, float *r_x, float *r_y) { const int getsizex = BLI_rcti_size_x(®ion->winrct) + 1; const int getsizey = BLI_rcti_size_y(®ion->winrct) + 1; - const int sx = region->winrct.xmin; - const int sy = region->winrct.ymin; float gx = *r_x; float gy = *r_y; @@ -146,14 +144,19 @@ void ui_block_to_window_fl(const ARegion *region, uiBlock *block, float *r_x, fl gy += block->panel->ofsy; } - *r_x = ((float)sx) + - ((float)getsizex) * (0.5f + 0.5f * (gx * block->winmat[0][0] + gy * block->winmat[1][0] + + *r_x = ((float)getsizex) * (0.5f + 0.5f * (gx * block->winmat[0][0] + gy * block->winmat[1][0] + block->winmat[3][0])); - *r_y = ((float)sy) + - ((float)getsizey) * (0.5f + 0.5f * (gx * block->winmat[0][1] + gy * block->winmat[1][1] + + *r_y = ((float)getsizey) * (0.5f + 0.5f * (gx * block->winmat[0][1] + gy * block->winmat[1][1] + block->winmat[3][1])); } +void ui_block_to_window_fl(const ARegion *region, uiBlock *block, float *r_x, float *r_y) +{ + ui_block_to_region_fl(region, block, r_x, r_y); + *r_x += region->winrct.xmin; + *r_y += region->winrct.ymin; +} + void ui_block_to_window(const ARegion *region, uiBlock *block, int *r_x, int *r_y) { float fx = *r_x; @@ -165,6 +168,16 @@ void ui_block_to_window(const ARegion *region, uiBlock *block, int *r_x, int *r_ *r_y = (int)(fy + 0.5f); } +void ui_block_to_region_rctf(const ARegion *region, + uiBlock *block, + rctf *rct_dst, + const rctf *rct_src) +{ + *rct_dst = *rct_src; + ui_block_to_region_fl(region, block, &rct_dst->xmin, &rct_dst->ymin); + ui_block_to_region_fl(region, block, &rct_dst->xmax, &rct_dst->ymax); +} + void ui_block_to_window_rctf(const ARegion *region, uiBlock *block, rctf *rct_dst, @@ -249,6 +262,14 @@ void ui_window_to_region_rcti(const ARegion *region, rcti *rect_dst, const rcti rect_dst->ymax = rct_src->ymax - region->winrct.ymin; } +void ui_window_to_region_rctf(const ARegion *region, rctf *rect_dst, const rctf *rct_src) +{ + rect_dst->xmin = rct_src->xmin - region->winrct.xmin; + rect_dst->xmax = rct_src->xmax - region->winrct.xmin; + rect_dst->ymin = rct_src->ymin - region->winrct.ymin; + rect_dst->ymax = rct_src->ymax - region->winrct.ymin; +} + void ui_region_to_window(const ARegion *region, int *r_x, int *r_y) { *r_x += region->winrct.xmin; @@ -2432,7 +2453,7 @@ bool ui_but_is_rna_valid(uiBut *but) */ bool ui_but_supports_cycling(const uiBut *but) { - return ((ELEM(but->type, UI_BTYPE_ROW, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER, UI_BTYPE_LISTBOX)) || + return (ELEM(but->type, UI_BTYPE_ROW, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER, UI_BTYPE_LISTBOX) || (but->type == UI_BTYPE_MENU && ui_but_menu_step_poll(but)) || (but->type == UI_BTYPE_COLOR && ((uiButColor *)but)->is_pallete_color) || (but->menu_step_func != NULL)); @@ -3440,6 +3461,15 @@ void UI_blocklist_update_window_matrix(const bContext *C, const ListBase *lb) } } +void UI_blocklist_update_view_for_buttons(const bContext *C, const ListBase *lb) +{ + LISTBASE_FOREACH (uiBlock *, block, lb) { + if (block->active) { + ui_but_update_view_for_active(C, block); + } + } +} + void UI_blocklist_draw(const bContext *C, const ListBase *lb) { LISTBASE_FOREACH (uiBlock *, block, lb) { diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 2a7611eabb1..3049e2bd7b8 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -494,7 +494,7 @@ static void ui_but_menu_add_path_operators(uiLayout *layout, PointerRNA *ptr, Pr RNA_string_set(&props_ptr, "filepath", dir); } -bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) +bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *event) { /* ui_but_is_interactive() may let some buttons through that should not get a context menu - it * doesn't make sense for them. */ @@ -1226,6 +1226,20 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) } } + /* UI List item context menu. Scripts can add items to it, by default there's nothing shown. */ + ARegion *region = CTX_wm_region(C); + const bool is_inside_listbox = ui_list_find_mouse_over(region, event) != NULL; + const bool is_inside_listrow = is_inside_listbox ? + ui_list_row_find_mouse_over(region, event->x, event->y) != + NULL : + false; + if (is_inside_listrow) { + MenuType *mt = WM_menutype_find("UI_MT_list_item_context_menu", true); + if (mt) { + UI_menutype_draw(C, mt, uiLayoutColumn(layout, false)); + } + } + MenuType *mt = WM_menutype_find("WM_MT_button_context", true); if (mt) { UI_menutype_draw(C, mt, uiLayoutColumn(layout, false)); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 743f646d4df..4f8bb6342f7 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -35,10 +35,12 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "BLI_array_utils.h" #include "BLI_linklist.h" #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_rect.h" +#include "BLI_sort_utils.h" #include "BLI_string.h" #include "BLI_string_cursor_utf8.h" #include "BLI_string_utf8.h" @@ -170,6 +172,20 @@ static bool ui_but_find_select_in_enum__cmp(const uiBut *but_a, const uiBut *but static void ui_textedit_string_set(uiBut *but, struct uiHandleButtonData *data, const char *str); static void button_tooltip_timer_reset(bContext *C, uiBut *but); +static void ui_block_interaction_begin_ensure(bContext *C, + uiBlock *block, + struct uiHandleButtonData *data, + const bool is_click); +static struct uiBlockInteraction_Handle *ui_block_interaction_begin(struct bContext *C, + uiBlock *block, + const bool is_click); +static void ui_block_interaction_end(struct bContext *C, + uiBlockInteraction_CallbackData *callbacks, + struct uiBlockInteraction_Handle *interaction); +static void ui_block_interaction_update(struct bContext *C, + uiBlockInteraction_CallbackData *callbacks, + struct uiBlockInteraction_Handle *interaction); + #ifdef USE_KEYNAV_LIMIT static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEvent *event); static bool ui_mouse_motion_keynav_test(struct uiKeyNavLock *keynav, const wmEvent *event); @@ -225,6 +241,19 @@ typedef enum uiMenuScrollType { MENU_SCROLL_BOTTOM, } uiMenuScrollType; +typedef struct uiBlockInteraction_Handle { + struct uiBlockInteraction_Params params; + void *user_data; + /** + * This is shared between #uiHandleButtonData and #uiAfterFunc, + * the last user runs the end callback and frees the data. + * + * This is needed as the order of freeing changes depending on + * accepting/canceling the operation. + */ + int user_count; +} uiBlockInteraction_Handle; + #ifdef USE_ALLSELECT /* Unfortunately there's no good way handle more generally: @@ -430,6 +459,8 @@ typedef struct uiHandleButtonData { uiSelectContextStore select_others; #endif + struct uiBlockInteraction_Handle *custom_interaction_handle; + /* Text field undo. */ struct uiUndoStack_Text *undo_stack_text; @@ -471,6 +502,9 @@ typedef struct uiAfterFunc { void *search_arg; uiFreeArgFunc search_arg_free_fn; + uiBlockInteraction_CallbackData custom_interaction_callbacks; + uiBlockInteraction_Handle *custom_interaction_handle; + bContextStore *context; char undostr[BKE_UNDO_STR_MAX]; @@ -733,23 +767,34 @@ static uiAfterFunc *ui_afterfunc_new(void) * For executing operators after the button is pressed. * (some non operator buttons need to trigger operators), see: T37795. * + * \param context_but: A button from which to get the context from (`uiBut.context`) for the + * operator execution. + * + * \note Ownership over \a properties is moved here. The #uiAfterFunc owns it now. * \note Can only call while handling buttons. */ -PointerRNA *ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext, bool create_props) +static void ui_handle_afterfunc_add_operator_ex(wmOperatorType *ot, + PointerRNA **properties, + int opcontext, + const uiBut *context_but) { - PointerRNA *ptr = NULL; uiAfterFunc *after = ui_afterfunc_new(); after->optype = ot; after->opcontext = opcontext; + if (properties) { + after->opptr = *properties; + *properties = NULL; + } - if (create_props) { - ptr = MEM_callocN(sizeof(PointerRNA), __func__); - WM_operator_properties_create_ptr(ptr, ot); - after->opptr = ptr; + if (context_but && context_but->context) { + after->context = CTX_store_copy(context_but->context); } +} - return ptr; +void ui_handle_afterfunc_add_operator(wmOperatorType *ot, int opcontext) +{ + ui_handle_afterfunc_add_operator_ex(ot, NULL, opcontext, NULL); } static void popup_check(bContext *C, wmOperator *op) @@ -769,72 +814,95 @@ static bool ui_afterfunc_check(const uiBlock *block, const uiBut *but) (block->handle && block->handle->popup_op)); } +/** + * These functions are postponed and only executed after all other + * handling is done, i.e. menus are closed, in order to avoid conflicts + * with these functions removing the buttons we are working with. + */ static void ui_apply_but_func(bContext *C, uiBut *but) { uiBlock *block = but->block; + if (!ui_afterfunc_check(block, but)) { + return; + } - /* these functions are postponed and only executed after all other - * handling is done, i.e. menus are closed, in order to avoid conflicts - * with these functions removing the buttons we are working with */ - - if (ui_afterfunc_check(block, but)) { - uiAfterFunc *after = ui_afterfunc_new(); + uiAfterFunc *after = ui_afterfunc_new(); - if (but->func && ELEM(but, but->func_arg1, but->func_arg2)) { - /* exception, this will crash due to removed button otherwise */ - but->func(C, but->func_arg1, but->func_arg2); - } - else { - after->func = but->func; - } + if (but->func && ELEM(but, but->func_arg1, but->func_arg2)) { + /* exception, this will crash due to removed button otherwise */ + but->func(C, but->func_arg1, but->func_arg2); + } + else { + after->func = but->func; + } - after->func_arg1 = but->func_arg1; - after->func_arg2 = but->func_arg2; + after->func_arg1 = but->func_arg1; + after->func_arg2 = but->func_arg2; - after->funcN = but->funcN; - after->func_argN = (but->func_argN) ? MEM_dupallocN(but->func_argN) : NULL; + after->funcN = but->funcN; + after->func_argN = (but->func_argN) ? MEM_dupallocN(but->func_argN) : NULL; - after->rename_func = but->rename_func; - after->rename_arg1 = but->rename_arg1; - after->rename_orig = but->rename_orig; /* needs free! */ + after->rename_func = but->rename_func; + after->rename_arg1 = but->rename_arg1; + after->rename_orig = but->rename_orig; /* needs free! */ - after->handle_func = block->handle_func; - after->handle_func_arg = block->handle_func_arg; - after->retval = but->retval; + after->handle_func = block->handle_func; + after->handle_func_arg = block->handle_func_arg; + after->retval = but->retval; - if (but->type == UI_BTYPE_BUT_MENU) { - after->butm_func = block->butm_func; - after->butm_func_arg = block->butm_func_arg; - after->a2 = but->a2; - } + if (but->type == UI_BTYPE_BUT_MENU) { + after->butm_func = block->butm_func; + after->butm_func_arg = block->butm_func_arg; + after->a2 = but->a2; + } - if (block->handle) { - after->popup_op = block->handle->popup_op; - } + if (block->handle) { + after->popup_op = block->handle->popup_op; + } - after->optype = but->optype; - after->opcontext = but->opcontext; - after->opptr = but->opptr; + after->optype = but->optype; + after->opcontext = but->opcontext; + after->opptr = but->opptr; - after->rnapoin = but->rnapoin; - after->rnaprop = but->rnaprop; + after->rnapoin = but->rnapoin; + after->rnaprop = but->rnaprop; - if (but->type == UI_BTYPE_SEARCH_MENU) { - uiButSearch *search_but = (uiButSearch *)but; - after->search_arg_free_fn = search_but->arg_free_fn; - after->search_arg = search_but->arg; - search_but->arg_free_fn = NULL; - search_but->arg = NULL; - } + if (but->type == UI_BTYPE_SEARCH_MENU) { + uiButSearch *search_but = (uiButSearch *)but; + after->search_arg_free_fn = search_but->arg_free_fn; + after->search_arg = search_but->arg; + search_but->arg_free_fn = NULL; + search_but->arg = NULL; + } - if (but->context) { - after->context = CTX_store_copy(but->context); + if (but->active != NULL) { + uiHandleButtonData *data = but->active; + if (data->custom_interaction_handle != NULL) { + after->custom_interaction_callbacks = block->custom_interaction_callbacks; + after->custom_interaction_handle = data->custom_interaction_handle; + + /* Ensure this callback runs once and last. */ + uiAfterFunc *after_prev = after->prev; + if (after_prev && + (after_prev->custom_interaction_handle == data->custom_interaction_handle)) { + after_prev->custom_interaction_handle = NULL; + memset(&after_prev->custom_interaction_callbacks, + 0x0, + sizeof(after_prev->custom_interaction_callbacks)); + } + else { + after->custom_interaction_handle->user_count++; + } } + } - but->optype = NULL; - but->opcontext = 0; - but->opptr = NULL; + if (but->context) { + after->context = CTX_store_copy(but->context); } + + but->optype = NULL; + but->opcontext = 0; + but->opptr = NULL; } /* typically call ui_apply_but_undo(), ui_apply_but_autokey() */ @@ -997,6 +1065,18 @@ static void ui_apply_but_funcs_after(bContext *C) after.search_arg_free_fn(after.search_arg); } + if (after.custom_interaction_handle != NULL) { + after.custom_interaction_handle->user_count--; + BLI_assert(after.custom_interaction_handle->user_count >= 0); + if (after.custom_interaction_handle->user_count == 0) { + ui_block_interaction_update( + C, &after.custom_interaction_callbacks, after.custom_interaction_handle); + ui_block_interaction_end( + C, &after.custom_interaction_callbacks, after.custom_interaction_handle); + } + after.custom_interaction_handle = NULL; + } + ui_afterfunc_update_preferences_dirty(&after); if (after.undostr[0]) { @@ -1076,6 +1156,42 @@ static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleBu data->applied = true; } +/** + * \note Ownership of \a properties is moved here. The #uiAfterFunc owns it now. + * + * \param context_but: The button to use context from when calling or polling the operator. + * + * \returns true if the operator was executed, otherwise false. + */ +static bool ui_list_invoke_item_operator(bContext *C, + const uiBut *context_but, + wmOperatorType *ot, + PointerRNA **properties) +{ + if (!ui_but_context_poll_operator(C, ot, context_but)) { + return false; + } + + /* Allow the context to be set from the hovered button, so the list item draw callback can set + * context for the operators. */ + ui_handle_afterfunc_add_operator_ex(ot, properties, WM_OP_INVOKE_DEFAULT, context_but); + return true; +} + +static void ui_apply_but_LISTROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data) +{ + uiBut *listbox = ui_list_find_from_row(data->region, but); + if (listbox) { + uiList *list = listbox->custom_data; + if (list && list->dyn_data->custom_activate_optype) { + ui_list_invoke_item_operator( + C, but, list->dyn_data->custom_activate_optype, &list->dyn_data->custom_activate_opptr); + } + } + + ui_apply_but_ROW(C, block, but, data); +} + static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data) { if (!data->str) { @@ -1548,7 +1664,7 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const */ if (drag_info->is_xy_lock_init == false) { /* first store the buttons original coords */ - uiBut *but = ui_but_find_mouse_over_ex(region, xy_input[0], xy_input[1], true); + uiBut *but = ui_but_find_mouse_over_ex(region, xy_input[0], xy_input[1], true, NULL, NULL); if (but) { if (but->flag & UI_BUT_DRAG_LOCK) { @@ -1619,7 +1735,7 @@ static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); uiBut *but = ui_but_find_mouse_over_ex( - region, drag_info->xy_init[0], drag_info->xy_init[1], true); + region, drag_info->xy_init[0], drag_info->xy_init[1], true, NULL, NULL); if (but) { ui_apply_but_undo(but); @@ -2181,9 +2297,11 @@ static void ui_apply_but( ui_apply_but_TOG(C, but, data); break; case UI_BTYPE_ROW: - case UI_BTYPE_LISTROW: ui_apply_but_ROW(C, block, but, data); break; + case UI_BTYPE_LISTROW: + ui_apply_but_LISTROW(C, block, but, data); + break; case UI_BTYPE_DATASETROW: ui_apply_but_ROW(C, block, but, data); break; @@ -2283,6 +2401,11 @@ static void ui_apply_but( uiButCurveProfile *but_profile = (uiButCurveProfile *)but; but_profile->edit_profile = editprofile; } + + if (data->custom_interaction_handle != NULL) { + ui_block_interaction_update( + C, &block->custom_interaction_callbacks, data->custom_interaction_handle); + } } /** \} */ @@ -3396,6 +3519,11 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) ui_but_update(but); + /* Popup blocks don't support moving after creation, so don't change the view for them. */ + if (!data->searchbox) { + UI_but_ensure_in_view(C, data->region, but); + } + WM_cursor_modal_set(win, WM_CURSOR_TEXT_EDIT); #ifdef WITH_INPUT_IME @@ -4210,7 +4338,7 @@ static uiBut *ui_but_list_row_text_activate(bContext *C, uiButtonActivateType activate_type) { ARegion *region = CTX_wm_region(C); - uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->x, event->y, true); + uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->x, event->y, true, NULL, NULL); if (labelbut && labelbut->type == UI_BTYPE_TEXT && !(labelbut->flag & UI_BUT_DISABLED)) { /* exit listrow */ @@ -4704,6 +4832,15 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con if (but->dragpoin && but->imb && ui_but_contains_point_px_icon(but, data->region, event)) { ret = WM_UI_HANDLER_CONTINUE; } + /* Same special case handling for UI lists. Return CONTINUE so that a tweak or CLICK event + * will be sent for the list to work with. */ + const uiBut *listbox = ui_list_find_mouse_over(data->region, event); + if (listbox) { + const uiList *ui_list = listbox->custom_data; + if (ui_list && ui_list->dyn_data->custom_drag_optype) { + ret = WM_UI_HANDLER_CONTINUE; + } + } button_activate_state(C, but, BUTTON_STATE_EXIT); return ret; } @@ -4852,6 +4989,8 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but, return changed; } + ui_block_interaction_begin_ensure(but->block->evil_C, but->block, data, false); + if (ui_but_is_cursor_warp(but)) { const float softmin = but->softmin; const float softmax = but->softmax; @@ -5362,6 +5501,8 @@ static bool ui_numedit_but_SLI(uiBut *but, return changed; } + ui_block_interaction_begin_ensure(but->block->evil_C, but->block, data, false); + const PropertyScaleType scale_type = ui_but_scale_type(but); softmin = but->softmin; @@ -6435,7 +6576,7 @@ static void ui_ndofedit_but_HSVCUBE(uiButHSVCube *hsv_but, CLAMP(hsv[2], hsv_but->but.softmin, hsv_but->but.softmax); break; default: - BLI_assert(!"invalid hsv type"); + BLI_assert_msg(0, "invalid hsv type"); break; } @@ -7773,7 +7914,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * if ((event->type == RIGHTMOUSE) && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey) && (event->val == KM_PRESS)) { /* RMB has two options now */ - if (ui_popup_context_menu_for_button(C, but)) { + if (ui_popup_context_menu_for_button(C, but, event)) { return WM_UI_HANDLER_BREAK; } } @@ -7853,6 +7994,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * case UI_BTYPE_IMAGE: case UI_BTYPE_PROGRESS_BAR: case UI_BTYPE_NODE_SOCKET: + case UI_BTYPE_PREVIEW_TILE: retval = ui_do_but_EXIT(C, but, data, event); break; case UI_BTYPE_HISTOGRAM: @@ -8239,6 +8381,16 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s but->flag &= ~UI_SELECT; } + if (state == BUTTON_STATE_TEXT_EDITING) { + ui_block_interaction_begin_ensure(C, but->block, data, true); + } + else if (state == BUTTON_STATE_EXIT) { + if (data->state == BUTTON_STATE_NUM_EDITING) { + /* This happens on pasting values for example. */ + ui_block_interaction_begin_ensure(C, but->block, data, true); + } + } + data->state = state; if (state != BUTTON_STATE_EXIT) { @@ -8467,6 +8619,21 @@ static void button_activate_exit( ED_region_tag_redraw_no_rebuild(data->region); ED_region_tag_refresh_ui(data->region); + if ((but->flag & UI_BUT_DRAG_MULTI) == 0) { + if (data->custom_interaction_handle != NULL) { + /* Should only set when the button is modal. */ + BLI_assert(but->active != NULL); + data->custom_interaction_handle->user_count--; + + BLI_assert(data->custom_interaction_handle->user_count >= 0); + if (data->custom_interaction_handle->user_count == 0) { + ui_block_interaction_end( + C, &but->block->custom_interaction_callbacks, data->custom_interaction_handle); + } + data->custom_interaction_handle = NULL; + } + } + /* clean up button */ if (but->active) { MEM_freeN(but->active); @@ -8722,6 +8889,26 @@ void UI_context_update_anim_flag(const bContext *C) } } +/** + * In some cases we may want to update the view (#View2D) in-between layout definition and drawing. + * E.g. to make sure a button is visible while editing. + */ +void ui_but_update_view_for_active(const bContext *C, const uiBlock *block) +{ + uiBut *active_but = ui_block_active_but_get(block); + if (!active_but || !active_but->active || !active_but->changed || active_but->block != block) { + return; + } + /* If there is a search popup attached to the button, don't change the view. The popups don't + * support updating the position to the button position nicely. */ + uiHandleButtonData *data = active_but->active; + if (data->searchbox) { + return; + } + + UI_but_ensure_in_view(C, active_but->active->region, active_but); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -9186,6 +9373,149 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) return retval; } +/** + * Activate the underlying list-row button, so the row is highlighted. + * Early exits if \a activate_dragging is true, but the custom drag operator fails to execute. + * Gives the wanted behavior where the item is activated on a tweak event when the custom drag + * operator is executed. + */ +static int ui_list_activate_hovered_row(bContext *C, + ARegion *region, + const uiList *ui_list, + const wmEvent *event, + bool activate_dragging) +{ + const bool do_drag = activate_dragging && ui_list->dyn_data->custom_drag_optype; + + if (do_drag) { + const uiBut *hovered_but = ui_but_find_mouse_over(region, event); + if (!ui_list_invoke_item_operator(C, + hovered_but, + ui_list->dyn_data->custom_drag_optype, + &ui_list->dyn_data->custom_drag_opptr)) { + return WM_UI_HANDLER_CONTINUE; + } + } + + const int *mouse_xy = ISTWEAK(event->type) ? &event->prevclickx : &event->x; + uiBut *listrow = ui_list_row_find_mouse_over(region, mouse_xy[0], mouse_xy[1]); + if (listrow) { + wmOperatorType *custom_activate_optype = ui_list->dyn_data->custom_activate_optype; + + /* Hacky: Ensure the custom activate operator is not called when the custom drag operator + * was. Only one should run! */ + if (activate_dragging && do_drag) { + ((uiList *)ui_list)->dyn_data->custom_activate_optype = NULL; + } + + /* Simulate click on listrow button itself (which may be overlapped by another button). Also + * calls the custom activate operator (ui_list->custom_activate_opname). */ + UI_but_execute(C, region, listrow); + + ((uiList *)ui_list)->dyn_data->custom_activate_optype = custom_activate_optype; + } + + return WM_UI_HANDLER_BREAK; +} + +static bool ui_list_is_hovering_draggable_but(bContext *C, + const uiList *list, + const ARegion *region, + const wmEvent *event) +{ + /* On a tweak event, uses the coordinates from where tweaking was started. */ + const int *mouse_xy = ISTWEAK(event->type) ? &event->prevclickx : &event->x; + const uiBut *hovered_but = ui_but_find_mouse_over_ex( + region, mouse_xy[0], mouse_xy[1], false, NULL, NULL); + + if (list->dyn_data->custom_drag_optype) { + if (ui_but_context_poll_operator(C, list->dyn_data->custom_drag_optype, hovered_but)) { + return true; + } + } + + return (hovered_but && hovered_but->dragpoin); +} + +static int ui_list_handle_click_drag(bContext *C, + const uiList *ui_list, + ARegion *region, + const wmEvent *event) +{ + if (!ELEM(event->type, LEFTMOUSE, EVT_TWEAK_L)) { + return WM_HANDLER_CONTINUE; + } + + int retval = WM_HANDLER_CONTINUE; + + const bool is_draggable = ui_list_is_hovering_draggable_but(C, ui_list, region, event); + bool activate = false; + bool activate_dragging = false; + + if (event->type == EVT_TWEAK_L) { + if (is_draggable) { + activate_dragging = true; + activate = true; + } + } + /* #KM_CLICK is only sent after an uncaught release event, so the foreground button gets all + * regular events (including mouse presses to start dragging) and this part only kicks in if it + * hasn't handled the release event. Note that if there's no overlaid button, the row selects + * on the press event already via regular #UI_BTYPE_LISTROW handling. */ + else if ((event->type == LEFTMOUSE) && (event->val == KM_CLICK)) { + activate = true; + } + + if (activate) { + retval = ui_list_activate_hovered_row(C, region, ui_list, event, activate_dragging); + } + + return retval; +} + +static void ui_list_activate_row_from_index( + bContext *C, ARegion *region, uiBut *listbox, uiList *ui_list, int index) +{ + uiBut *new_active_row = ui_list_row_find_from_index(region, index, listbox); + if (new_active_row) { + /* Preferred way to update the active item, also calls the custom activate operator + * (#uiList.custom_activate_opname). */ + UI_but_execute(C, region, new_active_row); + } + else { + /* A bit ugly, set the active index in RNA directly. That's because a button that's + * scrolled away in the list box isn't created at all. + * The custom activate operator (#uiList.custom_activate_opname) is not called in this case + * (which may need the row button context).*/ + RNA_property_int_set(&listbox->rnapoin, listbox->rnaprop, index); + RNA_property_update(C, &listbox->rnapoin, listbox->rnaprop); + ui_apply_but_undo(listbox); + } + + ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM; +} + +static int ui_list_get_increment(const uiList *ui_list, const int type, const int columns) +{ + int increment = 0; + + /* Handle column offsets for grid layouts. */ + if (ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY) && + ELEM(ui_list->layout_type, UILST_LAYOUT_GRID, UILST_LAYOUT_BIG_PREVIEW_GRID)) { + increment = (type == EVT_UPARROWKEY) ? -columns : columns; + } + else { + /* Left or right in grid layouts or any direction in single column layouts increments by 1. */ + increment = ELEM(type, EVT_UPARROWKEY, EVT_LEFTARROWKEY, WHEELUPMOUSE) ? -1 : 1; + } + + if ((ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0) { + increment *= -1; + } + + return increment; +} + static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *region, uiBut *listbox) { int retval = WM_UI_HANDLER_CONTINUE; @@ -9219,22 +9549,19 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi } } - if (val == KM_PRESS) { - if ((ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY) && + if (ELEM(event->type, LEFTMOUSE, EVT_TWEAK_L)) { + retval = ui_list_handle_click_drag(C, ui_list, region, event); + } + else if (val == KM_PRESS) { + if ((ELEM(type, EVT_UPARROWKEY, EVT_DOWNARROWKEY, EVT_LEFTARROWKEY, EVT_RIGHTARROWKEY) && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) || ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->ctrl && !IS_EVENT_MOD(event, shift, alt, oskey)))) { const int value_orig = RNA_property_int_get(&listbox->rnapoin, listbox->rnaprop); - int value, min, max, inc; + int value, min, max; - /* activate up/down the list */ value = value_orig; - if ((ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0) { - inc = ELEM(type, EVT_UPARROWKEY, WHEELUPMOUSE) ? 1 : -1; - } - else { - inc = ELEM(type, EVT_UPARROWKEY, WHEELUPMOUSE) ? -1 : 1; - } + const int inc = ui_list_get_increment(ui_list, type, dyn_data->columns); if (dyn_data->items_filter_neworder || dyn_data->items_filter_flags) { /* If we have a display order different from @@ -9281,12 +9608,7 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi CLAMP(value, min, max); if (value != value_orig) { - RNA_property_int_set(&listbox->rnapoin, listbox->rnaprop, value); - RNA_property_update(C, &listbox->rnapoin, listbox->rnaprop); - - ui_apply_but_undo(listbox); - - ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM; + ui_list_activate_row_from_index(C, region, listbox, ui_list, value); redraw = true; } retval = WM_UI_HANDLER_BREAK; @@ -11314,3 +11636,100 @@ bool UI_but_active_drop_color(bContext *C) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name UI Block Interaction API + * \{ */ + +void UI_block_interaction_set(uiBlock *block, uiBlockInteraction_CallbackData *callbacks) +{ + block->custom_interaction_callbacks = *callbacks; +} + +static uiBlockInteraction_Handle *ui_block_interaction_begin(bContext *C, + uiBlock *block, + const bool is_click) +{ + BLI_assert(block->custom_interaction_callbacks.begin_fn != NULL); + uiBlockInteraction_Handle *interaction = MEM_callocN(sizeof(*interaction), __func__); + + int unique_retval_ids_len = 0; + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + if (but->active || (but->flag & UI_BUT_DRAG_MULTI)) { + unique_retval_ids_len++; + } + } + + int *unique_retval_ids = MEM_mallocN(sizeof(*unique_retval_ids) * unique_retval_ids_len, + __func__); + unique_retval_ids_len = 0; + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + if (but->active || (but->flag & UI_BUT_DRAG_MULTI)) { + unique_retval_ids[unique_retval_ids_len++] = but->retval; + } + } + + if (unique_retval_ids_len > 1) { + qsort(unique_retval_ids, unique_retval_ids_len, sizeof(int), BLI_sortutil_cmp_int); + unique_retval_ids_len = BLI_array_deduplicate_ordered(unique_retval_ids, + unique_retval_ids_len); + unique_retval_ids = MEM_reallocN(unique_retval_ids, + sizeof(*unique_retval_ids) * unique_retval_ids_len); + } + + interaction->params.is_click = is_click; + interaction->params.unique_retval_ids = unique_retval_ids; + interaction->params.unique_retval_ids_len = unique_retval_ids_len; + + interaction->user_data = block->custom_interaction_callbacks.begin_fn( + C, &interaction->params, block->custom_interaction_callbacks.arg1); + return interaction; +} + +static void ui_block_interaction_end(bContext *C, + uiBlockInteraction_CallbackData *callbacks, + uiBlockInteraction_Handle *interaction) +{ + BLI_assert(callbacks->end_fn != NULL); + callbacks->end_fn(C, &interaction->params, callbacks->arg1, interaction->user_data); + MEM_freeN(interaction->params.unique_retval_ids); + MEM_freeN(interaction); +} + +static void ui_block_interaction_update(bContext *C, + uiBlockInteraction_CallbackData *callbacks, + uiBlockInteraction_Handle *interaction) +{ + BLI_assert(callbacks->update_fn != NULL); + callbacks->update_fn(C, &interaction->params, callbacks->arg1, interaction->user_data); +} + +/** + * \note #ui_block_interaction_begin cannot be called when setting the button state + * (e.g. #BUTTON_STATE_NUM_EDITING) for the following reasons. + * + * - Other buttons may still be activated using #UI_BUT_DRAG_MULTI + * which is necessary before gathering all the #uiBut.retval values to initialize + * #uiBlockInteraction_Params.unique_retval_ids. + * - When clicking on a number button it's not known if the event is a click or a drag. + * + * Instead, it must be called immediately before the drag action begins. + */ +static void ui_block_interaction_begin_ensure(bContext *C, + uiBlock *block, + uiHandleButtonData *data, + const bool is_click) +{ + if (data->custom_interaction_handle) { + return; + } + if (block->custom_interaction_callbacks.begin_fn == NULL) { + return; + } + + uiBlockInteraction_Handle *interaction = ui_block_interaction_begin(C, block, is_click); + interaction->user_count = 1; + data->custom_interaction_handle = interaction; +} + +/** \} */ diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index aa957afbf8f..43ac646f053 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -1500,7 +1500,7 @@ static void icon_draw_rect(float x, /* sanity check */ if (w <= 0 || h <= 0 || w > 2000 || h > 2000) { printf("%s: icons are %i x %i pixels?\n", __func__, w, h); - BLI_assert(!"invalid icon size"); + BLI_assert_msg(0, "invalid icon size"); return; } /* modulate color */ @@ -2201,7 +2201,7 @@ int UI_icon_from_library(const ID *id) return ICON_NONE; } -int UI_icon_from_rnaptr(bContext *C, PointerRNA *ptr, int rnaicon, const bool big) +int UI_icon_from_rnaptr(const bContext *C, PointerRNA *ptr, int rnaicon, const bool big) { ID *id = NULL; diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index a05afb6e542..a07f924e65b 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -511,6 +511,9 @@ struct uiBlock { uiBlockHandleFunc handle_func; void *handle_func_arg; + /** Custom interaction data. */ + uiBlockInteraction_CallbackData custom_interaction_callbacks; + /** Custom extra event handling. */ int (*block_event_func)(const struct bContext *C, struct uiBlock *, const struct wmEvent *); @@ -594,11 +597,19 @@ typedef struct uiSafetyRct { void ui_fontscale(short *points, float aspect); +extern void ui_block_to_region_fl(const struct ARegion *region, + uiBlock *block, + float *r_x, + float *r_y); extern void ui_block_to_window_fl(const struct ARegion *region, uiBlock *block, float *x, float *y); extern void ui_block_to_window(const struct ARegion *region, uiBlock *block, int *x, int *y); +extern void ui_block_to_region_rctf(const struct ARegion *region, + uiBlock *block, + rctf *rct_dst, + const rctf *rct_src); extern void ui_block_to_window_rctf(const struct ARegion *region, uiBlock *block, rctf *rct_dst, @@ -617,6 +628,9 @@ extern void ui_window_to_region(const struct ARegion *region, int *x, int *y); extern void ui_window_to_region_rcti(const struct ARegion *region, rcti *rect_dst, const rcti *rct_src); +extern void ui_window_to_region_rctf(const struct ARegion *region, + rctf *rect_dst, + const rctf *rct_src); extern void ui_region_to_window(const struct ARegion *region, int *x, int *y); extern void ui_region_winrct_get_no_margin(const struct ARegion *region, struct rcti *r_rect); @@ -928,9 +942,7 @@ const char *ui_textedit_undo(struct uiUndoStack_Text *undo_stack, int *r_cursor_index); /* interface_handlers.c */ -PointerRNA *ui_handle_afterfunc_add_operator(struct wmOperatorType *ot, - int opcontext, - bool create_props); +extern void ui_handle_afterfunc_add_operator(struct wmOperatorType *ot, int opcontext); extern void ui_pan_to_scroll(const struct wmEvent *event, int *type, int *val); extern void ui_but_activate_event(struct bContext *C, struct ARegion *region, uiBut *but); extern void ui_but_activate_over(struct bContext *C, struct ARegion *region, uiBut *but); @@ -943,6 +955,7 @@ extern void ui_but_execute_end(struct bContext *C, uiBut *but, void *active_back); extern void ui_but_active_free(const struct bContext *C, uiBut *but); +extern void ui_but_update_view_for_active(const struct bContext *C, const uiBlock *block); extern int ui_but_menu_direction(uiBut *but); extern void ui_but_text_password_hide(char password_str[128], uiBut *but, const bool restore); extern uiBut *ui_but_find_select_in_enum(uiBut *but, int direction); @@ -1042,8 +1055,18 @@ void ui_draw_menu_item(const struct uiFontStyle *fstyle, int state, uiMenuItemSeparatorType separator_type, int *r_xmax); -void ui_draw_preview_item( - const struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state); +void ui_draw_preview_item(const struct uiFontStyle *fstyle, + rcti *rect, + const char *name, + int iconid, + int state, + eFontStyle_Align text_align); +void ui_draw_preview_item_stateless(const struct uiFontStyle *fstyle, + rcti *rect, + const char *name, + int iconid, + const uchar text_col[4], + eFontStyle_Align text_align); #define UI_TEXT_MARGIN_X 0.4f #define UI_POPUP_MARGIN (UI_DPI_FAC * 12) @@ -1125,19 +1148,32 @@ bool ui_but_contains_point_px_icon(const uiBut *but, bool ui_but_contains_point_px(const uiBut *but, const struct ARegion *region, int x, int y) ATTR_WARN_UNUSED_RESULT; -uiBut *ui_list_find_mouse_over(struct ARegion *region, +uiBut *ui_list_find_mouse_over(const struct ARegion *region, const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT; - +uiBut *ui_list_find_from_row(const struct ARegion *region, + const uiBut *row_but) ATTR_WARN_UNUSED_RESULT; +uiBut *ui_list_row_find_mouse_over(const struct ARegion *region, + int x, + int y) ATTR_WARN_UNUSED_RESULT; +uiBut *ui_list_row_find_from_index(const struct ARegion *region, + const int index, + uiBut *listbox) ATTR_WARN_UNUSED_RESULT; + +typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata); uiBut *ui_but_find_mouse_over_ex(const struct ARegion *region, const int x, const int y, - const bool labeledit) ATTR_WARN_UNUSED_RESULT; + const bool labeledit, + const uiButFindPollFn find_poll, + const void *find_custom_data) ATTR_WARN_UNUSED_RESULT; uiBut *ui_but_find_mouse_over(const struct ARegion *region, const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT; uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) ATTR_WARN_UNUSED_RESULT; -uiBut *ui_list_find_mouse_over_ex(struct ARegion *region, int x, int y) ATTR_WARN_UNUSED_RESULT; +uiBut *ui_list_find_mouse_over_ex(const struct ARegion *region, + int x, + int y) ATTR_WARN_UNUSED_RESULT; bool ui_but_contains_password(const uiBut *but) ATTR_WARN_UNUSED_RESULT; @@ -1149,6 +1185,7 @@ uiBut *ui_but_next(uiBut *but) ATTR_WARN_UNUSED_RESULT; uiBut *ui_but_first(uiBlock *block) ATTR_WARN_UNUSED_RESULT; uiBut *ui_but_last(uiBlock *block) ATTR_WARN_UNUSED_RESULT; +uiBut *ui_block_active_but_get(const uiBlock *block); bool ui_block_is_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; bool ui_block_is_popover(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; bool ui_block_is_pie_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; @@ -1176,7 +1213,7 @@ struct ARegion *ui_screen_region_find_mouse_over(struct bScreen *screen, const struct wmEvent *event); /* interface_context_menu.c */ -bool ui_popup_context_menu_for_button(struct bContext *C, uiBut *but); +bool ui_popup_context_menu_for_button(struct bContext *C, uiBut *but, const struct wmEvent *event); void ui_popup_context_menu_for_panel(struct bContext *C, struct ARegion *region, struct Panel *panel); @@ -1204,6 +1241,9 @@ void UI_OT_eyedropper_driver(struct wmOperatorType *ot); /* interface_eyedropper_gpencil_color.c */ void UI_OT_eyedropper_gpencil_color(struct wmOperatorType *ot); +/* interface_template_asset_view.cc */ +struct uiListType *UI_UL_asset_view(void); + /** * For use with #ui_rna_collection_search_update_fn. */ diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 4afe232e33e..376a41ff9bb 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -75,6 +75,32 @@ #include "ED_text.h" /* -------------------------------------------------------------------- */ +/** \name Immediate redraw helper + * + * Generally handlers shouldn't do any redrawing, that includes the layout/button definitions. That + * violates the Model-View-Controller pattern. + * + * But there are some operators which really need to re-run the layout definitions for various + * reasons. For example, "Edit Source" does it to find out which exact Python code added a button. + * Other operators may need to access buttons that aren't currently visible. In Blender's UI code + * design that typically means just not adding the button in the first place, for a particular + * redraw. So the operator needs to change context and re-create the layout, so the button becomes + * available to act on. + * + * \{ */ + +static void ui_region_redraw_immediately(bContext *C, ARegion *region) +{ + ED_region_do_layout(C, region); + WM_draw_region_viewport_bind(region); + ED_region_do_draw(C, region); + WM_draw_region_viewport_unbind(region); + region->do_draw = false; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Copy Data Path Operator * \{ */ @@ -1379,11 +1405,7 @@ static int editsource_exec(bContext *C, wmOperator *op) ui_editsource_active_but_set(but); /* redraw and get active button python info */ - ED_region_do_layout(C, region); - WM_draw_region_viewport_bind(region); - ED_region_do_draw(C, region); - WM_draw_region_viewport_unbind(region); - region->do_draw = false; + ui_region_redraw_immediately(C, region); for (BLI_ghashIterator_init(&ghi, ui_editsource_info->hash); BLI_ghashIterator_done(&ghi) == false; @@ -1836,6 +1858,64 @@ static void UI_OT_drop_color(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name UI List Search Operator + * \{ */ + +static bool ui_list_focused_poll(bContext *C) +{ + const ARegion *region = CTX_wm_region(C); + const wmWindow *win = CTX_wm_window(C); + const uiList *list = UI_list_find_mouse_over(region, win->eventstate); + + return list != NULL; +} + +/** + * Ensure the filter options are set to be visible in the UI list. + * \return if the visibility changed, requiring a redraw. + */ +static bool ui_list_unhide_filter_options(uiList *list) +{ + if (list->filter_flag & UILST_FLT_SHOW) { + /* Nothing to be done. */ + return false; + } + + list->filter_flag |= UILST_FLT_SHOW; + return true; +} + +static int ui_list_start_filter_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + uiList *list = UI_list_find_mouse_over(region, event); + /* Poll should check. */ + BLI_assert(list != NULL); + + if (ui_list_unhide_filter_options(list)) { + ui_region_redraw_immediately(C, region); + } + + if (!UI_textbutton_activate_rna(C, region, list, "filter_name")) { + return OPERATOR_CANCELLED; + } + + return OPERATOR_FINISHED; +} + +static void UI_OT_list_start_filter(wmOperatorType *ot) +{ + ot->name = "List Filter"; + ot->idname = "UI_OT_list_start_filter"; + ot->description = "Start entering filter text for the list in focus"; + + ot->invoke = ui_list_start_filter_invoke; + ot->poll = ui_list_focused_poll; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Operator & Keymap Registration * \{ */ @@ -1860,6 +1940,8 @@ void ED_operatortypes_ui(void) WM_operatortype_append(UI_OT_button_execute); WM_operatortype_append(UI_OT_button_string_clear); + WM_operatortype_append(UI_OT_list_start_filter); + /* external */ WM_operatortype_append(UI_OT_eyedropper_color); WM_operatortype_append(UI_OT_eyedropper_colorramp); diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 38dc91fb57f..97d01ac3763 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -2653,7 +2653,7 @@ static void panel_activate_state(const bContext *C, Panel *panel, const uiHandle /* Initiate edge panning during drags for scrolling beyond the initial region view. */ wmOperatorType *ot = WM_operatortype_find("VIEW2D_OT_edge_pan", true); - ui_handle_afterfunc_add_operator(ot, WM_OP_INVOKE_DEFAULT, true); + ui_handle_afterfunc_add_operator(ot, WM_OP_INVOKE_DEFAULT); } else if (state == PANEL_STATE_ANIMATION) { panel_set_flag_recursive(panel, PNL_SELECT, false); diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 025c242d0fc..8534c95b6fd 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -266,11 +266,29 @@ bool ui_but_contains_point_px_icon(const uiBut *but, ARegion *region, const wmEv return BLI_rcti_isect_pt(&rect, x, y); } +static uiBut *ui_but_find(const ARegion *region, + const uiButFindPollFn find_poll, + const void *find_custom_data) +{ + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { + if (find_poll && find_poll(but, find_custom_data) == false) { + continue; + } + return but; + } + } + + return NULL; +} + /* x and y are only used in case event is NULL... */ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, const int x, const int y, - const bool labeledit) + const bool labeledit, + const uiButFindPollFn find_poll, + const void *find_custom_data) { uiBut *butover = NULL; @@ -282,6 +300,9 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, ui_window_to_block_fl(region, block, &mx, &my); LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { + if (find_poll && find_poll(but, find_custom_data) == false) { + continue; + } if (ui_but_is_interactive(but, labeledit)) { if (but->pie_dir != UI_RADIAL_NONE) { if (ui_but_isect_pie_seg(block, but)) { @@ -310,7 +331,7 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, uiBut *ui_but_find_mouse_over(const ARegion *region, const wmEvent *event) { - return ui_but_find_mouse_over_ex(region, event->x, event->y, event->ctrl != 0); + return ui_but_find_mouse_over_ex(region, event->x, event->y, event->ctrl != 0, NULL, NULL); } uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) @@ -351,7 +372,7 @@ uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) return butover; } -uiBut *ui_list_find_mouse_over_ex(ARegion *region, int x, int y) +uiBut *ui_list_find_mouse_over_ex(const ARegion *region, int x, int y) { if (!ui_region_contains_point_px(region, x, y)) { return NULL; @@ -369,11 +390,77 @@ uiBut *ui_list_find_mouse_over_ex(ARegion *region, int x, int y) return NULL; } -uiBut *ui_list_find_mouse_over(ARegion *region, const wmEvent *event) +uiBut *ui_list_find_mouse_over(const ARegion *region, const wmEvent *event) { + if (event == NULL) { + /* If there is no info about the mouse, just act as if there is nothing underneath it. */ + return NULL; + } return ui_list_find_mouse_over_ex(region, event->x, event->y); } +uiList *UI_list_find_mouse_over(const ARegion *region, const wmEvent *event) +{ + uiBut *list_but = ui_list_find_mouse_over(region, event); + if (!list_but) { + return NULL; + } + + return list_but->custom_data; +} + +static bool ui_list_contains_row(const uiBut *listbox_but, const uiBut *listrow_but) +{ + BLI_assert(listbox_but->type == UI_BTYPE_LISTBOX); + BLI_assert(listrow_but->type == UI_BTYPE_LISTROW); + /* The list box and its rows have the same RNA data (active data pointer/prop). */ + return ui_but_rna_equals(listbox_but, listrow_but); +} + +static bool ui_but_is_listbox_with_row(const uiBut *but, const void *customdata) +{ + const uiBut *row_but = customdata; + return (but->type == UI_BTYPE_LISTBOX) && ui_list_contains_row(but, row_but); +} + +uiBut *ui_list_find_from_row(const ARegion *region, const uiBut *row_but) +{ + return ui_but_find(region, ui_but_is_listbox_with_row, row_but); +} + +static bool ui_but_is_listrow(const uiBut *but, const void *UNUSED(customdata)) +{ + return but->type == UI_BTYPE_LISTROW; +} + +uiBut *ui_list_row_find_mouse_over(const ARegion *region, const int x, const int y) +{ + return ui_but_find_mouse_over_ex(region, x, y, false, ui_but_is_listrow, NULL); +} + +struct ListRowFindIndexData { + int index; + uiBut *listbox; +}; + +static bool ui_but_is_listrow_at_index(const uiBut *but, const void *customdata) +{ + const struct ListRowFindIndexData *find_data = customdata; + + return ui_but_is_listrow(but, NULL) && ui_list_contains_row(find_data->listbox, but) && + (but->hardmax == find_data->index); +} + +uiBut *ui_list_row_find_from_index(const ARegion *region, const int index, uiBut *listbox) +{ + BLI_assert(listbox->type == UI_BTYPE_LISTBOX); + struct ListRowFindIndexData data = { + .index = index, + .listbox = listbox, + }; + return ui_but_find(region, ui_but_is_listrow_at_index, &data); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -485,6 +572,17 @@ size_t ui_but_tip_len_only_first_line(const uiBut *but) /** \name Block (#uiBlock) State * \{ */ +uiBut *ui_block_active_but_get(const uiBlock *block) +{ + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + if (but->active) { + return but; + } + } + + return NULL; +} + bool ui_block_is_menu(const uiBlock *block) { return (((block->flag & UI_BLOCK_LOOP) != 0) && @@ -588,10 +686,9 @@ uiBlock *ui_block_find_mouse_over(const ARegion *region, const wmEvent *event, b uiBut *ui_region_find_active_but(ARegion *region) { LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { - LISTBASE_FOREACH (uiBut *, but, &block->buttons) { - if (but->active) { - return but; - } + uiBut *but = ui_block_active_but_get(block); + if (but) { + return but; } } diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index c35dbc5d7a6..c863b1f8bdf 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -599,8 +599,12 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region) ui_searchbox_butrect(&rect, data, a); /* widget itself */ - ui_draw_preview_item( - &data->fstyle, &rect, data->items.names[a], data->items.icons[a], state); + ui_draw_preview_item(&data->fstyle, + &rect, + data->items.names[a], + data->items.icons[a], + state, + UI_STYLE_TEXT_LEFT); } /* indicate more */ @@ -684,13 +688,13 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region) if (data->items.more) { ui_searchbox_butrect(&rect, data, data->items.maxitem - 1); GPU_blend(GPU_BLEND_ALPHA); - UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymin - 9, ICON_TRIA_DOWN); + UI_icon_draw(BLI_rcti_size_x(&rect) / 2, rect.ymin - 9, ICON_TRIA_DOWN); GPU_blend(GPU_BLEND_NONE); } if (data->items.offset) { ui_searchbox_butrect(&rect, data, 0); GPU_blend(GPU_BLEND_ALPHA); - UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymax - 7, ICON_TRIA_UP); + UI_icon_draw(BLI_rcti_size_x(&rect) / 2, rect.ymax - 7, ICON_TRIA_UP); GPU_blend(GPU_BLEND_NONE); } } @@ -986,13 +990,13 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe if (data->items.more) { ui_searchbox_butrect(&rect, data, data->items.maxitem - 1); GPU_blend(GPU_BLEND_ALPHA); - UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymin - 9, ICON_TRIA_DOWN); + UI_icon_draw(BLI_rcti_size_x(&rect) / 2, rect.ymin - 9, ICON_TRIA_DOWN); GPU_blend(GPU_BLEND_NONE); } if (data->items.offset) { ui_searchbox_butrect(&rect, data, 0); GPU_blend(GPU_BLEND_ALPHA); - UI_icon_draw((BLI_rcti_size_x(&rect)) / 2, rect.ymax - 7, ICON_TRIA_UP); + UI_icon_draw(BLI_rcti_size_x(&rect) / 2, rect.ymax - 7, ICON_TRIA_UP); GPU_blend(GPU_BLEND_NONE); } } diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc new file mode 100644 index 00000000000..5a05813f947 --- /dev/null +++ b/source/blender/editors/interface/interface_template_asset_view.cc @@ -0,0 +1,272 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edinterface + */ + +#include "DNA_space_types.h" +#include "DNA_userdef_types.h" + +#include "BKE_screen.h" + +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_string_ref.hh" + +#include "BLO_readfile.h" + +#include "ED_asset.h" +#include "ED_screen.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "interface_intern.h" + +struct AssetViewListData { + AssetLibraryReference asset_library; + bScreen *screen; +}; + +static void asset_view_item_but_drag_set(uiBut *but, + AssetViewListData *list_data, + AssetHandle *asset_handle) +{ + ID *id = asset_handle->file_data->id; + if (id != nullptr) { + UI_but_drag_set_id(but, id); + return; + } + + const blender::StringRef asset_list_path = ED_assetlist_library_path(&list_data->asset_library); + char blend_path[FILE_MAX_LIBEXTRA]; + + char path[FILE_MAX_LIBEXTRA]; + BLI_join_dirfile(path, sizeof(path), asset_list_path.data(), asset_handle->file_data->relpath); + if (BLO_library_path_explode(path, blend_path, nullptr, nullptr)) { + ImBuf *imbuf = ED_assetlist_asset_image_get(asset_handle); + UI_but_drag_set_asset(but, + asset_handle->file_data->name, + BLI_strdup(blend_path), + asset_handle->file_data->blentype, + FILE_ASSET_IMPORT_APPEND, + asset_handle->file_data->preview_icon_id, + imbuf, + 1.0f); + } +} + +static void asset_view_draw_item(uiList *ui_list, + bContext *UNUSED(C), + uiLayout *layout, + PointerRNA *UNUSED(dataptr), + PointerRNA *itemptr, + int UNUSED(icon), + PointerRNA *UNUSED(active_dataptr), + const char *UNUSED(active_propname), + int UNUSED(index), + int UNUSED(flt_flag)) +{ + AssetViewListData *list_data = (AssetViewListData *)ui_list->dyn_data->customdata; + + BLI_assert(RNA_struct_is_a(itemptr->type, &RNA_AssetHandle)); + AssetHandle *asset_handle = (AssetHandle *)itemptr->data; + + uiLayoutSetContextPointer(layout, "asset_handle", itemptr); + + uiBlock *block = uiLayoutGetBlock(layout); + /* TODO ED_fileselect_init_layout(). Share somehow? */ + const float size_x = (96.0f / 20.0f) * UI_UNIT_X; + const float size_y = (96.0f / 20.0f) * UI_UNIT_Y; + uiBut *but = uiDefIconTextBut(block, + UI_BTYPE_PREVIEW_TILE, + 0, + asset_handle->file_data->preview_icon_id, + asset_handle->file_data->name, + 0, + 0, + size_x, + size_y, + nullptr, + 0, + 0, + 0, + 0, + ""); + ui_def_but_icon(but, + asset_handle->file_data->preview_icon_id, + /* NOLINTNEXTLINE: bugprone-suspicious-enum-usage */ + UI_HAS_ICON | UI_BUT_ICON_PREVIEW); + if (!ui_list->dyn_data->custom_drag_optype) { + asset_view_item_but_drag_set(but, list_data, asset_handle); + } +} + +static void asset_view_listener(uiList *ui_list, wmRegionListenerParams *params) +{ + AssetViewListData *list_data = (AssetViewListData *)ui_list->dyn_data->customdata; + const wmNotifier *notifier = params->notifier; + + switch (notifier->category) { + case NC_ID: { + if (ELEM(notifier->action, NA_RENAME)) { + ED_assetlist_storage_tag_main_data_dirty(); + } + break; + } + } + + if (ED_assetlist_listen(&list_data->asset_library, params->notifier)) { + ED_region_tag_redraw(params->region); + } +} + +uiListType *UI_UL_asset_view() +{ + uiListType *list_type = (uiListType *)MEM_callocN(sizeof(*list_type), __func__); + + BLI_strncpy(list_type->idname, "UI_UL_asset_view", sizeof(list_type->idname)); + list_type->draw_item = asset_view_draw_item; + list_type->listener = asset_view_listener; + + return list_type; +} + +static void asset_view_template_refresh_asset_collection( + const AssetLibraryReference &asset_library, + PointerRNA &assets_dataptr, + const char *assets_propname) +{ + PropertyRNA *assets_prop = RNA_struct_find_property(&assets_dataptr, assets_propname); + if (!assets_prop) { + RNA_warning("Asset collection not found"); + return; + } + if (!RNA_struct_is_a(RNA_property_pointer_type(&assets_dataptr, assets_prop), + &RNA_AssetHandle)) { + RNA_warning("Expected a collection property for AssetHandle items"); + return; + } + + RNA_property_collection_clear(&assets_dataptr, assets_prop); + + ED_assetlist_iterate(&asset_library, [&](FileDirEntry &file) { + PointerRNA itemptr, fileptr; + RNA_property_collection_add(&assets_dataptr, assets_prop, &itemptr); + + RNA_pointer_create(nullptr, &RNA_FileSelectEntry, &file, &fileptr); + RNA_pointer_set(&itemptr, "file_data", fileptr); + + /* Copy name from file to asset-handle name ID-property. */ + char name[MAX_NAME]; + PropertyRNA *file_name_prop = RNA_struct_name_property(fileptr.type); + RNA_property_string_get(&fileptr, file_name_prop, name); + PropertyRNA *asset_name_prop = RNA_struct_name_property(&RNA_AssetHandle); + RNA_property_string_set(&itemptr, asset_name_prop, name); + + return true; + }); +} + +void uiTemplateAssetView(uiLayout *layout, + bContext *C, + const char *list_id, + PointerRNA *asset_library_dataptr, + const char *asset_library_propname, + PointerRNA *assets_dataptr, + const char *assets_propname, + PointerRNA *active_dataptr, + const char *active_propname, + const AssetFilterSettings *filter_settings, + const char *activate_opname, + PointerRNA *r_activate_op_properties, + const char *drag_opname, + PointerRNA *r_drag_op_properties) +{ + if (!list_id || !list_id[0]) { + RNA_warning("Asset view needs a valid identifier"); + return; + } + + uiLayout *col = uiLayoutColumn(layout, false); + + PropertyRNA *asset_library_prop = RNA_struct_find_property(asset_library_dataptr, + asset_library_propname); + AssetLibraryReference asset_library = ED_asset_library_reference_from_enum_value( + RNA_property_enum_get(asset_library_dataptr, asset_library_prop)); + + uiLayout *row = uiLayoutRow(col, true); + uiItemFullR(row, asset_library_dataptr, asset_library_prop, RNA_NO_INDEX, 0, 0, "", 0); + if (asset_library.type != ASSET_LIBRARY_LOCAL) { + uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_list_refresh"); + } + + ED_assetlist_storage_fetch(&asset_library, filter_settings, C); + ED_assetlist_ensure_previews_job(&asset_library, C); + const int tot_items = ED_assetlist_size(&asset_library); + + asset_view_template_refresh_asset_collection(asset_library, *assets_dataptr, assets_propname); + + AssetViewListData *list_data = (AssetViewListData *)MEM_mallocN(sizeof(*list_data), + "AssetViewListData"); + list_data->asset_library = asset_library; + list_data->screen = CTX_wm_screen(C); + + /* TODO can we have some kind of model-view API to handle referencing, filtering and lazy loading + * (of previews) of the items? */ + uiList *list = uiTemplateList_ex(col, + C, + "UI_UL_asset_view", + list_id, + assets_dataptr, + assets_propname, + active_dataptr, + active_propname, + nullptr, + tot_items, + 0, + UILST_LAYOUT_BIG_PREVIEW_GRID, + 0, + UI_TEMPLATE_LIST_NO_GRIP, + list_data); + if (!list) { + /* List creation failed. */ + MEM_freeN(list_data); + return; + } + + if (activate_opname) { + PointerRNA *ptr = UI_list_custom_activate_operator_set( + list, activate_opname, r_activate_op_properties != nullptr); + if (r_activate_op_properties && ptr) { + *r_activate_op_properties = *ptr; + } + } + if (drag_opname) { + PointerRNA *ptr = UI_list_custom_drag_operator_set( + list, drag_opname, r_drag_op_properties != nullptr); + if (r_drag_op_properties && ptr) { + *r_drag_op_properties = *ptr; + } + } +} diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc new file mode 100644 index 00000000000..eaab33e32c9 --- /dev/null +++ b/source/blender/editors/interface/interface_template_list.cc @@ -0,0 +1,1310 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edinterface + */ + +#include <cstdlib> +#include <cstring> + +#include "BLI_fnmatch.h" +#include "BLI_listbase.h" +#include "BLI_math_base.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "BKE_screen.h" + +#include "BLT_translation.h" + +#include "ED_screen.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_view2d.h" + +#include "WM_api.h" + +#include "interface_intern.h" + +/** + * The validated data that was passed to #uiTemplateList (typically through Python). + * Populated through #ui_template_list_data_retrieve(). + */ +struct TemplateListInputData { + PointerRNA dataptr; + PropertyRNA *prop; + PointerRNA active_dataptr; + PropertyRNA *activeprop; + const char *item_dyntip_propname; + + /* Index as stored in the input property. I.e. the index before sorting. */ + int active_item_idx; +}; + +/** + * Internal wrapper for a single item in the list (well, actually stored as a vector). + */ +struct _uilist_item { + PointerRNA item; + int org_idx; + int flt_flag; +}; + +/** + * Container for the item vector and additional info. + */ +struct TemplateListItems { + _uilist_item *item_vec; + /* Index of the active item following visual order. I.e. unlike + * TemplateListInputData.active_item_idx, this is the index after sorting. */ + int active_item_idx; + int tot_items; +}; + +struct TemplateListLayoutDrawData { + uiListDrawItemFunc draw_item; + uiListDrawFilterFunc draw_filter; + + int rows; + int maxrows; + int columns; +}; + +struct TemplateListVisualInfo { + int visual_items; /* Visual number of items (i.e. number of items we have room to display). */ + int start_idx; /* Index of first item to display. */ + int end_idx; /* Index of last item to display + 1. */ +}; + +static void uilist_draw_item_default(struct uiList *ui_list, + struct bContext *UNUSED(C), + struct uiLayout *layout, + struct PointerRNA *UNUSED(dataptr), + struct PointerRNA *itemptr, + int icon, + struct PointerRNA *UNUSED(active_dataptr), + const char *UNUSED(active_propname), + int UNUSED(index), + int UNUSED(flt_flag)) +{ + PropertyRNA *nameprop = RNA_struct_name_property(itemptr->type); + + /* Simplest one! */ + switch (ui_list->layout_type) { + case UILST_LAYOUT_GRID: + uiItemL(layout, "", icon); + break; + case UILST_LAYOUT_DEFAULT: + case UILST_LAYOUT_COMPACT: + default: + if (nameprop) { + uiItemFullR(layout, itemptr, nameprop, RNA_NO_INDEX, 0, UI_ITEM_R_NO_BG, "", icon); + } + else { + uiItemL(layout, "", icon); + } + break; + } +} + +static void uilist_draw_filter_default(struct uiList *ui_list, + struct bContext *UNUSED(C), + struct uiLayout *layout) +{ + PointerRNA listptr; + RNA_pointer_create(nullptr, &RNA_UIList, ui_list, &listptr); + + uiLayout *row = uiLayoutRow(layout, false); + + uiLayout *subrow = uiLayoutRow(row, true); + uiItemR(subrow, &listptr, "filter_name", 0, "", ICON_NONE); + uiItemR(subrow, + &listptr, + "use_filter_invert", + UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, + "", + ICON_ARROW_LEFTRIGHT); + + if ((ui_list->filter_sort_flag & UILST_FLT_SORT_LOCK) == 0) { + subrow = uiLayoutRow(row, true); + uiItemR(subrow, + &listptr, + "use_filter_sort_alpha", + UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, + "", + ICON_NONE); + uiItemR(subrow, + &listptr, + "use_filter_sort_reverse", + UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, + "", + (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) ? ICON_SORT_DESC : ICON_SORT_ASC); + } +} + +struct StringCmp { + char name[MAX_IDPROP_NAME]; + int org_idx; +}; + +static int cmpstringp(const void *p1, const void *p2) +{ + /* Case-insensitive comparison. */ + return BLI_strcasecmp(((StringCmp *)p1)->name, ((StringCmp *)p2)->name); +} + +static void uilist_filter_items_default(struct uiList *ui_list, + struct bContext *UNUSED(C), + struct PointerRNA *dataptr, + const char *propname) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + PropertyRNA *prop = RNA_struct_find_property(dataptr, propname); + + const char *filter_raw = ui_list->filter_byname; + char *filter = (char *)filter_raw, filter_buff[32], *filter_dyn = nullptr; + const bool filter_exclude = (ui_list->filter_flag & UILST_FLT_EXCLUDE) != 0; + const bool order_by_name = (ui_list->filter_sort_flag & UILST_FLT_SORT_MASK) == + UILST_FLT_SORT_ALPHA; + const int len = RNA_property_collection_length(dataptr, prop); + + dyn_data->items_shown = dyn_data->items_len = len; + + if (len && (order_by_name || filter_raw[0])) { + StringCmp *names = nullptr; + int order_idx = 0, i = 0; + + if (order_by_name) { + names = static_cast<StringCmp *>(MEM_callocN(sizeof(StringCmp) * len, "StringCmp")); + } + if (filter_raw[0]) { + const size_t slen = strlen(filter_raw); + + dyn_data->items_filter_flags = static_cast<int *>( + MEM_callocN(sizeof(int) * len, "items_filter_flags")); + dyn_data->items_shown = 0; + + /* Implicitly add heading/trailing wildcards if needed. */ + if (slen + 3 <= sizeof(filter_buff)) { + filter = filter_buff; + } + else { + filter = filter_dyn = static_cast<char *>( + MEM_mallocN((slen + 3) * sizeof(char), "filter_dyn")); + } + BLI_strncpy_ensure_pad(filter, filter_raw, '*', slen + 3); + } + + RNA_PROP_BEGIN (dataptr, itemptr, prop) { + bool do_order = false; + + char *namebuf = RNA_struct_name_get_alloc(&itemptr, nullptr, 0, nullptr); + const char *name = namebuf ? namebuf : ""; + + if (filter[0]) { + /* Case-insensitive! */ + if (fnmatch(filter, name, FNM_CASEFOLD) == 0) { + dyn_data->items_filter_flags[i] = UILST_FLT_ITEM; + if (!filter_exclude) { + dyn_data->items_shown++; + do_order = order_by_name; + } + // printf("%s: '%s' matches '%s'\n", __func__, name, filter); + } + else if (filter_exclude) { + dyn_data->items_shown++; + do_order = order_by_name; + } + } + else { + do_order = order_by_name; + } + + if (do_order) { + names[order_idx].org_idx = order_idx; + BLI_strncpy(names[order_idx++].name, name, MAX_IDPROP_NAME); + } + + /* free name */ + if (namebuf) { + MEM_freeN(namebuf); + } + i++; + } + RNA_PROP_END; + + if (order_by_name) { + int new_idx; + /* NOTE: order_idx equals either to ui_list->items_len if no filtering done, + * or to ui_list->items_shown if filter is enabled, + * or to (ui_list->items_len - ui_list->items_shown) if filtered items are excluded. + * This way, we only sort items we actually intend to draw! + */ + qsort(names, order_idx, sizeof(StringCmp), cmpstringp); + + dyn_data->items_filter_neworder = static_cast<int *>( + MEM_mallocN(sizeof(int) * order_idx, "items_filter_neworder")); + for (new_idx = 0; new_idx < order_idx; new_idx++) { + dyn_data->items_filter_neworder[names[new_idx].org_idx] = new_idx; + } + } + + if (filter_dyn) { + MEM_freeN(filter_dyn); + } + if (names) { + MEM_freeN(names); + } + } +} + +static void uilist_free_dyn_data(uiList *ui_list) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + if (!dyn_data) { + return; + } + + if (dyn_data->custom_activate_opptr) { + WM_operator_properties_free(dyn_data->custom_activate_opptr); + MEM_freeN(dyn_data->custom_activate_opptr); + } + if (dyn_data->custom_drag_opptr) { + WM_operator_properties_free(dyn_data->custom_drag_opptr); + MEM_freeN(dyn_data->custom_drag_opptr); + } + + MEM_SAFE_FREE(dyn_data->items_filter_flags); + MEM_SAFE_FREE(dyn_data->items_filter_neworder); + MEM_SAFE_FREE(dyn_data->customdata); +} + +/** + * Validate input parameters and initialize \a r_data from that. Plus find the list-type and return + * it in \a r_list_type. + * + * \return false if the input data isn't valid. Will also raise an RNA warning in that case. + */ +static bool ui_template_list_data_retrieve(const char *listtype_name, + const char *list_id, + PointerRNA *dataptr, + const char *propname, + PointerRNA *active_dataptr, + const char *active_propname, + const char *item_dyntip_propname, + TemplateListInputData *r_input_data, + uiListType **r_list_type) +{ + memset(r_input_data, 0, sizeof(*r_input_data)); + + /* Forbid default UI_UL_DEFAULT_CLASS_NAME list class without a custom list_id! */ + if (STREQ(UI_UL_DEFAULT_CLASS_NAME, listtype_name) && !(list_id && list_id[0])) { + RNA_warning("template_list using default '%s' UIList class must provide a custom list_id", + UI_UL_DEFAULT_CLASS_NAME); + return false; + } + + if (!active_dataptr->data) { + RNA_warning("No active data"); + return false; + } + + r_input_data->dataptr = *dataptr; + if (dataptr->data) { + r_input_data->prop = RNA_struct_find_property(dataptr, propname); + if (!r_input_data->prop) { + RNA_warning("Property not found: %s.%s", RNA_struct_identifier(dataptr->type), propname); + return false; + } + } + + r_input_data->active_dataptr = *active_dataptr; + r_input_data->activeprop = RNA_struct_find_property(active_dataptr, active_propname); + if (!r_input_data->activeprop) { + RNA_warning( + "Property not found: %s.%s", RNA_struct_identifier(active_dataptr->type), active_propname); + return false; + } + + if (r_input_data->prop) { + const PropertyType type = RNA_property_type(r_input_data->prop); + if (type != PROP_COLLECTION) { + RNA_warning("Expected a collection data property"); + return false; + } + } + + const PropertyType activetype = RNA_property_type(r_input_data->activeprop); + if (activetype != PROP_INT) { + RNA_warning("Expected an integer active data property"); + return false; + } + + /* Find the uiList type. */ + if (!(*r_list_type = WM_uilisttype_find(listtype_name, false))) { + RNA_warning("List type %s not found", listtype_name); + return false; + } + + r_input_data->active_item_idx = RNA_property_int_get(&r_input_data->active_dataptr, + r_input_data->activeprop); + r_input_data->item_dyntip_propname = item_dyntip_propname; + + return true; +} + +static void ui_template_list_collect_items(PointerRNA *list_ptr, + PropertyRNA *list_prop, + uiListDyn *dyn_data, + int filter_exclude, + bool order_reverse, + int activei, + TemplateListItems *r_items) +{ + int i = 0; + int reorder_i = 0; + bool activei_mapping_pending = true; + + RNA_PROP_BEGIN (list_ptr, itemptr, list_prop) { + if (!dyn_data->items_filter_flags || + ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) { + int new_order_idx; + if (dyn_data->items_filter_neworder) { + new_order_idx = dyn_data->items_filter_neworder[reorder_i++]; + new_order_idx = order_reverse ? dyn_data->items_shown - new_order_idx - 1 : new_order_idx; + } + else { + new_order_idx = order_reverse ? dyn_data->items_shown - ++reorder_i : reorder_i++; + } + // printf("%s: ii: %d\n", __func__, ii); + r_items->item_vec[new_order_idx].item = itemptr; + r_items->item_vec[new_order_idx].org_idx = i; + r_items->item_vec[new_order_idx].flt_flag = dyn_data->items_filter_flags ? + dyn_data->items_filter_flags[i] : + 0; + + if (activei_mapping_pending && activei == i) { + activei = new_order_idx; + /* So that we do not map again activei! */ + activei_mapping_pending = false; + } +#if 0 /* For now, do not alter active element, even if it will be hidden... */ + else if (activei < i) { + /* We do not want an active but invisible item! + * Only exception is when all items are filtered out... + */ + if (prev_order_idx >= 0) { + activei = prev_order_idx; + RNA_property_int_set(active_dataptr, activeprop, prev_i); + } + else { + activei = new_order_idx; + RNA_property_int_set(active_dataptr, activeprop, i); + } + } + prev_i = i; + prev_ii = new_order_idx; +#endif + } + i++; + } + RNA_PROP_END; + + /* If mapping is still pending, no active item was found. Mark as invalid (-1) */ + r_items->active_item_idx = activei_mapping_pending ? -1 : activei; +} + +/** + * Create the UI-list representation of the list items, sorted and filtered if needed. + */ +static void ui_template_list_collect_display_items(bContext *C, + uiList *ui_list, + TemplateListInputData *input_data, + const uiListFilterItemsFunc filter_items_fn, + TemplateListItems *r_items) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + memset(r_items, 0, sizeof(*r_items)); + + /* Filter list items! (not for compact layout, though) */ + if (input_data->dataptr.data && input_data->prop) { + const int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE; + const bool order_reverse = (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0; + int items_shown; +#if 0 + int prev_ii = -1, prev_i; +#endif + + if (ui_list->layout_type == UILST_LAYOUT_COMPACT) { + dyn_data->items_len = dyn_data->items_shown = RNA_property_collection_length( + &input_data->dataptr, input_data->prop); + } + else { + // printf("%s: filtering...\n", __func__); + filter_items_fn(ui_list, C, &input_data->dataptr, RNA_property_identifier(input_data->prop)); + // printf("%s: filtering done.\n", __func__); + } + + items_shown = dyn_data->items_shown; + if (items_shown >= 0) { + r_items->item_vec = static_cast<_uilist_item *>( + MEM_mallocN(sizeof(*r_items->item_vec) * items_shown, __func__)); + // printf("%s: items shown: %d.\n", __func__, items_shown); + + ui_template_list_collect_items(&input_data->dataptr, + input_data->prop, + dyn_data, + filter_exclude, + order_reverse, + input_data->active_item_idx, + r_items); + } + if (dyn_data->items_shown >= 0) { + r_items->tot_items = dyn_data->items_shown; + } + else { + r_items->tot_items = dyn_data->items_len; + } + } +} + +static void ui_template_list_free_items(TemplateListItems *items) +{ + if (items->item_vec) { + MEM_freeN(items->item_vec); + } +} + +static void uilist_prepare(uiList *ui_list, + const TemplateListItems *items, + const TemplateListLayoutDrawData *layout_data, + TemplateListVisualInfo *r_visual_info) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + const bool use_auto_size = (ui_list->list_grip < + (layout_data->rows - UI_LIST_AUTO_SIZE_THRESHOLD)); + + int actual_rows = layout_data->rows; + int actual_maxrows = layout_data->maxrows; + int columns = layout_data->columns; + + /* default rows */ + if (actual_rows <= 0) { + actual_rows = 5; + } + dyn_data->visual_height_min = actual_rows; + if (actual_maxrows < actual_rows) { + actual_maxrows = max_ii(actual_rows, 5); + } + if (columns <= 0) { + columns = 9; + } + + int activei_row; + if (columns > 1) { + dyn_data->height = (int)ceil((double)items->tot_items / (double)columns); + activei_row = (int)floor((double)items->active_item_idx / (double)columns); + } + else { + dyn_data->height = items->tot_items; + activei_row = items->active_item_idx; + } + + dyn_data->columns = columns; + + if (!use_auto_size) { + /* No auto-size, yet we clamp at min size! */ + actual_rows = max_ii(ui_list->list_grip, actual_rows); + } + else if ((actual_rows != actual_maxrows) && (dyn_data->height > actual_rows)) { + /* Expand size if needed and possible. */ + actual_rows = min_ii(dyn_data->height, actual_maxrows); + } + + /* If list length changes or list is tagged to check this, + * and active is out of view, scroll to it. */ + if ((ui_list->list_last_len != items->tot_items) || + (ui_list->flag & UILST_SCROLL_TO_ACTIVE_ITEM)) { + if (activei_row < ui_list->list_scroll) { + ui_list->list_scroll = activei_row; + } + else if (activei_row >= ui_list->list_scroll + actual_rows) { + ui_list->list_scroll = activei_row - actual_rows + 1; + } + ui_list->flag &= ~UILST_SCROLL_TO_ACTIVE_ITEM; + } + + const int max_scroll = max_ii(0, dyn_data->height - actual_rows); + CLAMP(ui_list->list_scroll, 0, max_scroll); + ui_list->list_last_len = items->tot_items; + dyn_data->visual_height = actual_rows; + r_visual_info->visual_items = actual_rows * columns; + r_visual_info->start_idx = ui_list->list_scroll * columns; + r_visual_info->end_idx = min_ii(r_visual_info->start_idx + actual_rows * columns, + items->tot_items); +} + +static void uilist_resize_update_cb(bContext *C, void *arg1, void *UNUSED(arg2)) +{ + uiList *ui_list = static_cast<uiList *>(arg1); + uiListDyn *dyn_data = ui_list->dyn_data; + + /* This way we get diff in number of additional items to show (positive) or hide (negative). */ + const int diff = round_fl_to_int((float)(dyn_data->resize - dyn_data->resize_prev) / + (float)UI_UNIT_Y); + + if (diff != 0) { + ui_list->list_grip += diff; + dyn_data->resize_prev += diff * UI_UNIT_Y; + ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM; + } + + /* In case uilist is in popup, we need special refreshing */ + ED_region_tag_refresh_ui(CTX_wm_menu(C)); +} + +static void *uilist_item_use_dynamic_tooltip(PointerRNA *itemptr, const char *propname) +{ + if (propname && propname[0] && itemptr && itemptr->data) { + PropertyRNA *prop = RNA_struct_find_property(itemptr, propname); + + if (prop && (RNA_property_type(prop) == PROP_STRING)) { + return RNA_property_string_get_alloc(itemptr, prop, nullptr, 0, nullptr); + } + } + return nullptr; +} + +static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const char *tip) +{ + char *dyn_tooltip = static_cast<char *>(argN); + return BLI_sprintfN("%s - %s", tip, dyn_tooltip); +} + +/** + * \note Note that \a layout_type may be null. + */ +static uiList *ui_list_ensure(bContext *C, + uiListType *ui_list_type, + const char *list_id, + int layout_type, + bool sort_reverse, + bool sort_lock) +{ + /* Allows to work in popups. */ + ARegion *region = CTX_wm_menu(C); + if (region == nullptr) { + region = CTX_wm_region(C); + } + + /* Find or add the uiList to the current Region. */ + + char full_list_id[UI_MAX_NAME_STR]; + WM_uilisttype_to_full_list_id(ui_list_type, list_id, full_list_id); + + uiList *ui_list = static_cast<uiList *>( + BLI_findstring(®ion->ui_lists, full_list_id, offsetof(uiList, list_id))); + + if (!ui_list) { + ui_list = static_cast<uiList *>(MEM_callocN(sizeof(uiList), "uiList")); + BLI_strncpy(ui_list->list_id, full_list_id, sizeof(ui_list->list_id)); + BLI_addtail(®ion->ui_lists, ui_list); + ui_list->list_grip = -UI_LIST_AUTO_SIZE_THRESHOLD; /* Force auto size by default. */ + if (sort_reverse) { + ui_list->filter_sort_flag |= UILST_FLT_SORT_REVERSE; + } + if (sort_lock) { + ui_list->filter_sort_flag |= UILST_FLT_SORT_LOCK; + } + } + + if (!ui_list->dyn_data) { + ui_list->dyn_data = static_cast<uiListDyn *>( + MEM_callocN(sizeof(uiListDyn), "uiList.dyn_data")); + } + uiListDyn *dyn_data = ui_list->dyn_data; + /* Note that this isn't a `uiListType` callback, it's stored in the runtime list data. Otherwise + * the runtime data could leak when the type is unregistered (e.g. on "Reload Scripts"). */ + dyn_data->free_runtime_data_fn = uilist_free_dyn_data; + + /* Because we can't actually pass type across save&load... */ + ui_list->type = ui_list_type; + ui_list->layout_type = layout_type; + + /* Reset filtering data. */ + MEM_SAFE_FREE(dyn_data->items_filter_flags); + MEM_SAFE_FREE(dyn_data->items_filter_neworder); + dyn_data->items_len = dyn_data->items_shown = -1; + + return ui_list; +} + +static void ui_template_list_layout_draw(bContext *C, + uiList *ui_list, + uiLayout *layout, + TemplateListInputData *input_data, + TemplateListItems *items, + const TemplateListLayoutDrawData *layout_data, + const enum uiTemplateListFlags flags) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + const char *active_propname = RNA_property_identifier(input_data->activeprop); + + uiLayout *glob = nullptr, *box, *row, *col, *subrow, *sub, *overlap; + char numstr[32]; + int rnaicon = ICON_NONE, icon = ICON_NONE; + uiBut *but; + + uiBlock *block = uiLayoutGetBlock(layout); + + /* get icon */ + if (input_data->dataptr.data && input_data->prop) { + StructRNA *ptype = RNA_property_pointer_type(&input_data->dataptr, input_data->prop); + rnaicon = RNA_struct_ui_icon(ptype); + } + + TemplateListVisualInfo visual_info; + switch (ui_list->layout_type) { + case UILST_LAYOUT_DEFAULT: { + /* layout */ + box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop); + glob = uiLayoutColumn(box, true); + row = uiLayoutRow(glob, false); + col = uiLayoutColumn(row, true); + + TemplateListLayoutDrawData adjusted_layout_data = *layout_data; + adjusted_layout_data.columns = 1; + /* init numbers */ + uilist_prepare(ui_list, items, &adjusted_layout_data, &visual_info); + + int i = 0; + if (input_data->dataptr.data && input_data->prop) { + /* create list items */ + for (i = visual_info.start_idx; i < visual_info.end_idx; i++) { + PointerRNA *itemptr = &items->item_vec[i].item; + void *dyntip_data; + const int org_i = items->item_vec[i].org_idx; + const int flt_flag = items->item_vec[i].flt_flag; + uiBlock *subblock = uiLayoutGetBlock(col); + + overlap = uiLayoutOverlap(col); + + UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM); + + /* list item behind label & other buttons */ + uiLayoutRow(overlap, false); + + but = uiDefButR_prop(subblock, + UI_BTYPE_LISTROW, + 0, + "", + 0, + 0, + UI_UNIT_X * 10, + UI_UNIT_Y, + &input_data->active_dataptr, + input_data->activeprop, + 0, + 0, + org_i, + 0, + 0, + TIP_("Double click to rename")); + if ((dyntip_data = uilist_item_use_dynamic_tooltip(itemptr, + input_data->item_dyntip_propname))) { + UI_but_func_tooltip_set(but, uilist_item_tooltip_func, dyntip_data, MEM_freeN); + } + + sub = uiLayoutRow(overlap, false); + + icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); + if (icon == ICON_DOT) { + icon = ICON_NONE; + } + layout_data->draw_item(ui_list, + C, + sub, + &input_data->dataptr, + itemptr, + icon, + &input_data->active_dataptr, + active_propname, + org_i, + flt_flag); + + /* Items should be able to set context pointers for the layout. But the list-row button + * swallows events, so it needs the context storage too for handlers to see it. */ + but->context = uiLayoutGetContextStore(sub); + + /* If we are "drawing" active item, set all labels as active. */ + if (i == items->active_item_idx) { + ui_layout_list_set_labels_active(sub); + } + + UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM); + } + } + + /* add dummy buttons to fill space */ + for (; i < visual_info.start_idx + visual_info.visual_items; i++) { + uiItemL(col, "", ICON_NONE); + } + + /* add scrollbar */ + if (items->tot_items > visual_info.visual_items) { + uiLayoutColumn(row, false); + uiDefButI(block, + UI_BTYPE_SCROLL, + 0, + "", + 0, + 0, + V2D_SCROLL_WIDTH, + UI_UNIT_Y * dyn_data->visual_height, + &ui_list->list_scroll, + 0, + dyn_data->height - dyn_data->visual_height, + dyn_data->visual_height, + 0, + ""); + } + } break; + case UILST_LAYOUT_COMPACT: + row = uiLayoutRow(layout, true); + + if ((input_data->dataptr.data && input_data->prop) && (dyn_data->items_shown > 0) && + (items->active_item_idx >= 0) && (items->active_item_idx < dyn_data->items_shown)) { + PointerRNA *itemptr = &items->item_vec[items->active_item_idx].item; + const int org_i = items->item_vec[items->active_item_idx].org_idx; + + icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); + if (icon == ICON_DOT) { + icon = ICON_NONE; + } + layout_data->draw_item(ui_list, + C, + row, + &input_data->dataptr, + itemptr, + icon, + &input_data->active_dataptr, + active_propname, + org_i, + 0); + } + /* if list is empty, add in dummy button */ + else { + uiItemL(row, "", ICON_NONE); + } + + /* next/prev button */ + BLI_snprintf(numstr, sizeof(numstr), "%d :", dyn_data->items_shown); + but = uiDefIconTextButR_prop(block, + UI_BTYPE_NUM, + 0, + 0, + numstr, + 0, + 0, + UI_UNIT_X * 5, + UI_UNIT_Y, + &input_data->active_dataptr, + input_data->activeprop, + 0, + 0, + 0, + 0, + 0, + ""); + if (dyn_data->items_shown == 0) { + UI_but_flag_enable(but, UI_BUT_DISABLED); + } + break; + case UILST_LAYOUT_GRID: { + box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop); + glob = uiLayoutColumn(box, true); + row = uiLayoutRow(glob, false); + col = uiLayoutColumn(row, true); + subrow = nullptr; /* Quite gcc warning! */ + + uilist_prepare(ui_list, items, layout_data, &visual_info); + + int i = 0; + if (input_data->dataptr.data && input_data->prop) { + /* create list items */ + for (i = visual_info.start_idx; i < visual_info.end_idx; i++) { + PointerRNA *itemptr = &items->item_vec[i].item; + const int org_i = items->item_vec[i].org_idx; + const int flt_flag = items->item_vec[i].flt_flag; + + /* create button */ + if (!(i % layout_data->columns)) { + subrow = uiLayoutRow(col, false); + } + + uiBlock *subblock = uiLayoutGetBlock(subrow); + overlap = uiLayoutOverlap(subrow); + + UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM); + + /* list item behind label & other buttons */ + uiLayoutRow(overlap, false); + + but = uiDefButR_prop(subblock, + UI_BTYPE_LISTROW, + 0, + "", + 0, + 0, + UI_UNIT_X * 10, + UI_UNIT_Y, + &input_data->active_dataptr, + input_data->activeprop, + 0, + 0, + org_i, + 0, + 0, + nullptr); + UI_but_drawflag_enable(but, UI_BUT_NO_TOOLTIP); + + sub = uiLayoutRow(overlap, false); + + icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); + layout_data->draw_item(ui_list, + C, + sub, + &input_data->dataptr, + itemptr, + icon, + &input_data->active_dataptr, + active_propname, + org_i, + flt_flag); + + /* If we are "drawing" active item, set all labels as active. */ + if (i == items->active_item_idx) { + ui_layout_list_set_labels_active(sub); + } + + UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM); + } + } + + /* add dummy buttons to fill space */ + for (; i < visual_info.start_idx + visual_info.visual_items; i++) { + if (!(i % layout_data->columns)) { + subrow = uiLayoutRow(col, false); + } + uiItemL(subrow, "", ICON_NONE); + } + + /* add scrollbar */ + if (items->tot_items > visual_info.visual_items) { + /* col = */ uiLayoutColumn(row, false); + uiDefButI(block, + UI_BTYPE_SCROLL, + 0, + "", + 0, + 0, + V2D_SCROLL_WIDTH, + UI_UNIT_Y * dyn_data->visual_height, + &ui_list->list_scroll, + 0, + dyn_data->height - dyn_data->visual_height, + dyn_data->visual_height, + 0, + ""); + } + break; + } + case UILST_LAYOUT_BIG_PREVIEW_GRID: + box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop); + /* For grip button. */ + glob = uiLayoutColumn(box, true); + /* For scrollbar. */ + row = uiLayoutRow(glob, false); + + /* TODO ED_fileselect_init_layout(). Share somehow? */ + float size_x = (96.0f / 20.0f) * UI_UNIT_X; + float size_y = (96.0f / 20.0f) * UI_UNIT_Y; + + const int cols_per_row = MAX2((uiLayoutGetWidth(box) - V2D_SCROLL_WIDTH) / size_x, 1); + uiLayout *grid = uiLayoutGridFlow(row, true, cols_per_row, true, true, true); + + TemplateListLayoutDrawData adjusted_layout_data = *layout_data; + adjusted_layout_data.columns = cols_per_row; + uilist_prepare(ui_list, items, &adjusted_layout_data, &visual_info); + + if (input_data->dataptr.data && input_data->prop) { + /* create list items */ + for (int i = visual_info.start_idx; i < visual_info.end_idx; i++) { + PointerRNA *itemptr = &items->item_vec[i].item; + const int org_i = items->item_vec[i].org_idx; + const int flt_flag = items->item_vec[i].flt_flag; + + overlap = uiLayoutOverlap(grid); + col = uiLayoutColumn(overlap, false); + + uiBlock *subblock = uiLayoutGetBlock(col); + UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM); + + but = uiDefButR_prop(subblock, + UI_BTYPE_LISTROW, + 0, + "", + 0, + 0, + size_x, + size_y, + &input_data->active_dataptr, + input_data->activeprop, + 0, + 0, + org_i, + 0, + 0, + nullptr); + UI_but_drawflag_enable(but, UI_BUT_NO_TOOLTIP); + + col = uiLayoutColumn(overlap, false); + + icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); + layout_data->draw_item(ui_list, + C, + col, + &input_data->dataptr, + itemptr, + icon, + &input_data->active_dataptr, + active_propname, + org_i, + flt_flag); + + /* Items should be able to set context pointers for the layout. But the list-row button + * swallows events, so it needs the context storage too for handlers to see it. */ + but->context = uiLayoutGetContextStore(col); + + /* If we are "drawing" active item, set all labels as active. */ + if (i == items->active_item_idx) { + ui_layout_list_set_labels_active(col); + } + + UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM); + } + } + + if (items->tot_items > visual_info.visual_items) { + /* col = */ uiLayoutColumn(row, false); + uiDefButI(block, + UI_BTYPE_SCROLL, + 0, + "", + 0, + 0, + V2D_SCROLL_WIDTH, + size_y * dyn_data->visual_height, + &ui_list->list_scroll, + 0, + dyn_data->height - dyn_data->visual_height, + dyn_data->visual_height, + 0, + ""); + } + break; + } + + if (glob) { + const bool add_grip_but = (flags & UI_TEMPLATE_LIST_NO_GRIP) == 0; + + /* About #UI_BTYPE_GRIP drag-resize: + * We can't directly use results from a grip button, since we have a + * rather complex behavior here (sizing by discrete steps and, overall, auto-size feature). + * Since we *never* know whether we are grip-resizing or not + * (because there is no callback for when a button enters/leaves its "edit mode"), + * we use the fact that grip-controlled value (dyn_data->resize) is completely handled + * by the grip during the grab resize, so settings its value here has no effect at all. + * + * It is only meaningful when we are not resizing, + * in which case this gives us the correct "init drag" value. + * Note we cannot affect `dyn_data->resize_prev here`, + * since this value is not controlled by the grip! + */ + dyn_data->resize = dyn_data->resize_prev + + (dyn_data->visual_height - ui_list->list_grip) * UI_UNIT_Y; + + row = uiLayoutRow(glob, true); + uiBlock *subblock = uiLayoutGetBlock(row); + UI_block_emboss_set(subblock, UI_EMBOSS_NONE); + + if (ui_list->filter_flag & UILST_FLT_SHOW) { + but = uiDefIconButBitI(subblock, + UI_BTYPE_TOGGLE, + UILST_FLT_SHOW, + 0, + ICON_DISCLOSURE_TRI_DOWN, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y * 0.5f, + &(ui_list->filter_flag), + 0, + 0, + 0, + 0, + TIP_("Hide filtering options")); + UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */ + + if (add_grip_but) { + but = uiDefIconButI(subblock, + UI_BTYPE_GRIP, + 0, + ICON_GRIP, + 0, + 0, + UI_UNIT_X * 10.0f, + UI_UNIT_Y * 0.5f, + &dyn_data->resize, + 0.0, + 0.0, + 0, + 0, + ""); + UI_but_func_set(but, uilist_resize_update_cb, ui_list, nullptr); + } + + UI_block_emboss_set(subblock, UI_EMBOSS); + + col = uiLayoutColumn(glob, false); + subblock = uiLayoutGetBlock(col); + uiDefBut(subblock, + UI_BTYPE_SEPR, + 0, + "", + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y * 0.05f, + nullptr, + 0.0, + 0.0, + 0, + 0, + ""); + + layout_data->draw_filter(ui_list, C, col); + } + else { + but = uiDefIconButBitI(subblock, + UI_BTYPE_TOGGLE, + UILST_FLT_SHOW, + 0, + ICON_DISCLOSURE_TRI_RIGHT, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y * 0.5f, + &(ui_list->filter_flag), + 0, + 0, + 0, + 0, + TIP_("Show filtering options")); + UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */ + + if (add_grip_but) { + but = uiDefIconButI(subblock, + UI_BTYPE_GRIP, + 0, + ICON_GRIP, + 0, + 0, + UI_UNIT_X * 10.0f, + UI_UNIT_Y * 0.5f, + &dyn_data->resize, + 0.0, + 0.0, + 0, + 0, + ""); + UI_but_func_set(but, uilist_resize_update_cb, ui_list, nullptr); + } + + UI_block_emboss_set(subblock, UI_EMBOSS); + } + } +} + +uiList *uiTemplateList_ex(uiLayout *layout, + bContext *C, + const char *listtype_name, + const char *list_id, + PointerRNA *dataptr, + const char *propname, + PointerRNA *active_dataptr, + const char *active_propname, + const char *item_dyntip_propname, + int rows, + int maxrows, + int layout_type, + int columns, + enum uiTemplateListFlags flags, + void *customdata) +{ + TemplateListInputData input_data = {nullptr}; + uiListType *ui_list_type; + if (!ui_template_list_data_retrieve(listtype_name, + list_id, + dataptr, + propname, + active_dataptr, + active_propname, + item_dyntip_propname, + &input_data, + &ui_list_type)) { + return nullptr; + } + + uiListDrawItemFunc draw_item = ui_list_type->draw_item ? ui_list_type->draw_item : + uilist_draw_item_default; + uiListDrawFilterFunc draw_filter = ui_list_type->draw_filter ? ui_list_type->draw_filter : + uilist_draw_filter_default; + uiListFilterItemsFunc filter_items = ui_list_type->filter_items ? ui_list_type->filter_items : + uilist_filter_items_default; + + uiList *ui_list = ui_list_ensure(C, + ui_list_type, + list_id, + layout_type, + flags & UI_TEMPLATE_LIST_SORT_REVERSE, + flags & UI_TEMPLATE_LIST_SORT_LOCK); + uiListDyn *dyn_data = ui_list->dyn_data; + + MEM_SAFE_FREE(dyn_data->customdata); + dyn_data->customdata = customdata; + + /* When active item changed since last draw, scroll to it. */ + if (input_data.active_item_idx != ui_list->list_last_activei) { + ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM; + ui_list->list_last_activei = input_data.active_item_idx; + } + + TemplateListItems items; + ui_template_list_collect_display_items(C, ui_list, &input_data, filter_items, &items); + + TemplateListLayoutDrawData layout_data; + layout_data.draw_item = draw_item; + layout_data.draw_filter = draw_filter; + layout_data.rows = rows; + layout_data.maxrows = maxrows; + layout_data.columns = columns; + + ui_template_list_layout_draw(C, ui_list, layout, &input_data, &items, &layout_data, flags); + + ui_template_list_free_items(&items); + + return ui_list; +} + +void uiTemplateList(uiLayout *layout, + bContext *C, + const char *listtype_name, + const char *list_id, + PointerRNA *dataptr, + const char *propname, + PointerRNA *active_dataptr, + const char *active_propname, + const char *item_dyntip_propname, + int rows, + int maxrows, + int layout_type, + int columns, + enum uiTemplateListFlags flags) +{ + uiTemplateList_ex(layout, + C, + listtype_name, + list_id, + dataptr, + propname, + active_dataptr, + active_propname, + item_dyntip_propname, + rows, + maxrows, + layout_type, + columns, + flags, + nullptr); +} + +/** + * \return: A RNA pointer for the operator properties. + */ +PointerRNA *UI_list_custom_activate_operator_set(uiList *ui_list, + const char *opname, + bool create_properties) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + dyn_data->custom_activate_optype = WM_operatortype_find(opname, false); + if (!dyn_data->custom_activate_optype) { + return nullptr; + } + + if (create_properties) { + WM_operator_properties_alloc(&dyn_data->custom_activate_opptr, nullptr, opname); + } + + return dyn_data->custom_activate_opptr; +} + +/** + * \return: A RNA pointer for the operator properties. + */ +PointerRNA *UI_list_custom_drag_operator_set(uiList *ui_list, + const char *opname, + bool create_properties) +{ + uiListDyn *dyn_data = ui_list->dyn_data; + dyn_data->custom_drag_optype = WM_operatortype_find(opname, false); + if (!dyn_data->custom_drag_optype) { + return nullptr; + } + + if (create_properties) { + WM_operator_properties_alloc(&dyn_data->custom_drag_opptr, nullptr, opname); + } + + return dyn_data->custom_drag_opptr; +} + +/* -------------------------------------------------------------------- */ + +/** \name List-types Registration + * \{ */ + +void ED_uilisttypes_ui(void) +{ + WM_uilisttype_add(UI_UL_asset_view()); +} + +/** \} */ diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c index f01dca7712c..3105891142f 100644 --- a/source/blender/editors/interface/interface_template_search_menu.c +++ b/source/blender/editors/interface/interface_template_search_menu.c @@ -1037,7 +1037,7 @@ static void menu_search_update_fn(const bContext *UNUSED(C), static bool ui_search_menu_create_context_menu(struct bContext *C, void *arg, void *active, - const struct wmEvent *UNUSED(event)) + const struct wmEvent *event) { struct MenuSearch_Data *data = arg; struct MenuSearch_Item *item = active; @@ -1058,7 +1058,7 @@ static bool ui_search_menu_create_context_menu(struct bContext *C, CTX_wm_region_set(C, item->wm_context->region); } - if (ui_popup_context_menu_for_button(C, but)) { + if (ui_popup_context_menu_for_button(C, but, event)) { has_menu = true; } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 9c17486aea4..766840909cc 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -5645,887 +5645,6 @@ void uiTemplateLayers(uiLayout *layout, /** \} */ /* -------------------------------------------------------------------- */ -/** \name List Template - * \{ */ - -static void uilist_draw_item_default(struct uiList *ui_list, - struct bContext *UNUSED(C), - struct uiLayout *layout, - struct PointerRNA *UNUSED(dataptr), - struct PointerRNA *itemptr, - int icon, - struct PointerRNA *UNUSED(active_dataptr), - const char *UNUSED(active_propname), - int UNUSED(index), - int UNUSED(flt_flag)) -{ - PropertyRNA *nameprop = RNA_struct_name_property(itemptr->type); - - /* Simplest one! */ - switch (ui_list->layout_type) { - case UILST_LAYOUT_GRID: - uiItemL(layout, "", icon); - break; - case UILST_LAYOUT_DEFAULT: - case UILST_LAYOUT_COMPACT: - default: - if (nameprop) { - uiItemFullR(layout, itemptr, nameprop, RNA_NO_INDEX, 0, UI_ITEM_R_NO_BG, "", icon); - } - else { - uiItemL(layout, "", icon); - } - break; - } -} - -static void uilist_draw_filter_default(struct uiList *ui_list, - struct bContext *UNUSED(C), - struct uiLayout *layout) -{ - PointerRNA listptr; - RNA_pointer_create(NULL, &RNA_UIList, ui_list, &listptr); - - uiLayout *row = uiLayoutRow(layout, false); - - uiLayout *subrow = uiLayoutRow(row, true); - uiItemR(subrow, &listptr, "filter_name", 0, "", ICON_NONE); - uiItemR(subrow, - &listptr, - "use_filter_invert", - UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, - "", - ICON_ARROW_LEFTRIGHT); - - if ((ui_list->filter_sort_flag & UILST_FLT_SORT_LOCK) == 0) { - subrow = uiLayoutRow(row, true); - uiItemR(subrow, - &listptr, - "use_filter_sort_alpha", - UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, - "", - ICON_NONE); - uiItemR(subrow, - &listptr, - "use_filter_sort_reverse", - UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, - "", - (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) ? ICON_SORT_DESC : ICON_SORT_ASC); - } -} - -typedef struct { - char name[MAX_IDPROP_NAME]; - int org_idx; -} StringCmp; - -static int cmpstringp(const void *p1, const void *p2) -{ - /* Case-insensitive comparison. */ - return BLI_strcasecmp(((StringCmp *)p1)->name, ((StringCmp *)p2)->name); -} - -static void uilist_filter_items_default(struct uiList *ui_list, - struct bContext *UNUSED(C), - struct PointerRNA *dataptr, - const char *propname) -{ - uiListDyn *dyn_data = ui_list->dyn_data; - PropertyRNA *prop = RNA_struct_find_property(dataptr, propname); - - const char *filter_raw = ui_list->filter_byname; - char *filter = (char *)filter_raw, filter_buff[32], *filter_dyn = NULL; - const bool filter_exclude = (ui_list->filter_flag & UILST_FLT_EXCLUDE) != 0; - const bool order_by_name = (ui_list->filter_sort_flag & UILST_FLT_SORT_MASK) == - UILST_FLT_SORT_ALPHA; - const int len = RNA_property_collection_length(dataptr, prop); - - dyn_data->items_shown = dyn_data->items_len = len; - - if (len && (order_by_name || filter_raw[0])) { - StringCmp *names = NULL; - int order_idx = 0, i = 0; - - if (order_by_name) { - names = MEM_callocN(sizeof(StringCmp) * len, "StringCmp"); - } - if (filter_raw[0]) { - const size_t slen = strlen(filter_raw); - - dyn_data->items_filter_flags = MEM_callocN(sizeof(int) * len, "items_filter_flags"); - dyn_data->items_shown = 0; - - /* Implicitly add heading/trailing wildcards if needed. */ - if (slen + 3 <= sizeof(filter_buff)) { - filter = filter_buff; - } - else { - filter = filter_dyn = MEM_mallocN((slen + 3) * sizeof(char), "filter_dyn"); - } - BLI_strncpy_ensure_pad(filter, filter_raw, '*', slen + 3); - } - - RNA_PROP_BEGIN (dataptr, itemptr, prop) { - bool do_order = false; - - char *namebuf = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL); - const char *name = namebuf ? namebuf : ""; - - if (filter[0]) { - /* Case-insensitive! */ - if (fnmatch(filter, name, FNM_CASEFOLD) == 0) { - dyn_data->items_filter_flags[i] = UILST_FLT_ITEM; - if (!filter_exclude) { - dyn_data->items_shown++; - do_order = order_by_name; - } - // printf("%s: '%s' matches '%s'\n", __func__, name, filter); - } - else if (filter_exclude) { - dyn_data->items_shown++; - do_order = order_by_name; - } - } - else { - do_order = order_by_name; - } - - if (do_order) { - names[order_idx].org_idx = order_idx; - BLI_strncpy(names[order_idx++].name, name, MAX_IDPROP_NAME); - } - - /* free name */ - if (namebuf) { - MEM_freeN(namebuf); - } - i++; - } - RNA_PROP_END; - - if (order_by_name) { - int new_idx; - /* NOTE: order_idx equals either to ui_list->items_len if no filtering done, - * or to ui_list->items_shown if filter is enabled, - * or to (ui_list->items_len - ui_list->items_shown) if filtered items are excluded. - * This way, we only sort items we actually intend to draw! - */ - qsort(names, order_idx, sizeof(StringCmp), cmpstringp); - - dyn_data->items_filter_neworder = MEM_mallocN(sizeof(int) * order_idx, - "items_filter_neworder"); - for (new_idx = 0; new_idx < order_idx; new_idx++) { - dyn_data->items_filter_neworder[names[new_idx].org_idx] = new_idx; - } - } - - if (filter_dyn) { - MEM_freeN(filter_dyn); - } - if (names) { - MEM_freeN(names); - } - } -} - -typedef struct { - PointerRNA item; - int org_idx; - int flt_flag; -} _uilist_item; - -typedef struct { - int visual_items; /* Visual number of items (i.e. number of items we have room to display). */ - int start_idx; /* Index of first item to display. */ - int end_idx; /* Index of last item to display + 1. */ -} uiListLayoutdata; - -static void uilist_prepare(uiList *ui_list, - int len, - int activei, - int rows, - int maxrows, - int columns, - uiListLayoutdata *layoutdata) -{ - uiListDyn *dyn_data = ui_list->dyn_data; - const bool use_auto_size = (ui_list->list_grip < (rows - UI_LIST_AUTO_SIZE_THRESHOLD)); - - /* default rows */ - if (rows <= 0) { - rows = 5; - } - dyn_data->visual_height_min = rows; - if (maxrows < rows) { - maxrows = max_ii(rows, 5); - } - if (columns <= 0) { - columns = 9; - } - - int activei_row; - if (columns > 1) { - dyn_data->height = (int)ceil((double)len / (double)columns); - activei_row = (int)floor((double)activei / (double)columns); - } - else { - dyn_data->height = len; - activei_row = activei; - } - - if (!use_auto_size) { - /* No auto-size, yet we clamp at min size! */ - maxrows = rows = max_ii(ui_list->list_grip, rows); - } - else if ((rows != maxrows) && (dyn_data->height > rows)) { - /* Expand size if needed and possible. */ - rows = min_ii(dyn_data->height, maxrows); - } - - /* If list length changes or list is tagged to check this, - * and active is out of view, scroll to it. */ - if (ui_list->list_last_len != len || ui_list->flag & UILST_SCROLL_TO_ACTIVE_ITEM) { - if (activei_row < ui_list->list_scroll) { - ui_list->list_scroll = activei_row; - } - else if (activei_row >= ui_list->list_scroll + rows) { - ui_list->list_scroll = activei_row - rows + 1; - } - ui_list->flag &= ~UILST_SCROLL_TO_ACTIVE_ITEM; - } - - const int max_scroll = max_ii(0, dyn_data->height - rows); - CLAMP(ui_list->list_scroll, 0, max_scroll); - ui_list->list_last_len = len; - dyn_data->visual_height = rows; - layoutdata->visual_items = rows * columns; - layoutdata->start_idx = ui_list->list_scroll * columns; - layoutdata->end_idx = min_ii(layoutdata->start_idx + rows * columns, len); -} - -static void uilist_resize_update_cb(bContext *C, void *arg1, void *UNUSED(arg2)) -{ - uiList *ui_list = arg1; - uiListDyn *dyn_data = ui_list->dyn_data; - - /* This way we get diff in number of additional items to show (positive) or hide (negative). */ - const int diff = round_fl_to_int((float)(dyn_data->resize - dyn_data->resize_prev) / - (float)UI_UNIT_Y); - - if (diff != 0) { - ui_list->list_grip += diff; - dyn_data->resize_prev += diff * UI_UNIT_Y; - ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM; - } - - /* In case uilist is in popup, we need special refreshing */ - ED_region_tag_refresh_ui(CTX_wm_menu(C)); -} - -static void *uilist_item_use_dynamic_tooltip(PointerRNA *itemptr, const char *propname) -{ - if (propname && propname[0] && itemptr && itemptr->data) { - PropertyRNA *prop = RNA_struct_find_property(itemptr, propname); - - if (prop && (RNA_property_type(prop) == PROP_STRING)) { - return RNA_property_string_get_alloc(itemptr, prop, NULL, 0, NULL); - } - } - return NULL; -} - -static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const char *tip) -{ - char *dyn_tooltip = argN; - return BLI_sprintfN("%s - %s", tip, dyn_tooltip); -} - -void uiTemplateList(uiLayout *layout, - bContext *C, - const char *listtype_name, - const char *list_id, - PointerRNA *dataptr, - const char *propname, - PointerRNA *active_dataptr, - const char *active_propname, - const char *item_dyntip_propname, - int rows, - int maxrows, - int layout_type, - int columns, - bool sort_reverse, - bool sort_lock) -{ - PropertyRNA *prop = NULL, *activeprop; - _uilist_item *items_ptr = NULL; - uiLayout *glob = NULL, *box, *row, *col, *subrow, *sub, *overlap; - uiBut *but; - - uiListLayoutdata layoutdata; - char ui_list_id[UI_MAX_NAME_STR]; - char numstr[32]; - int rnaicon = ICON_NONE, icon = ICON_NONE; - int i = 0, activei = 0; - int len = 0; - - /* validate arguments */ - /* Forbid default UI_UL_DEFAULT_CLASS_NAME list class without a custom list_id! */ - if (STREQ(UI_UL_DEFAULT_CLASS_NAME, listtype_name) && !(list_id && list_id[0])) { - RNA_warning("template_list using default '%s' UIList class must provide a custom list_id", - UI_UL_DEFAULT_CLASS_NAME); - return; - } - - uiBlock *block = uiLayoutGetBlock(layout); - - if (!active_dataptr->data) { - RNA_warning("No active data"); - return; - } - - if (dataptr->data) { - prop = RNA_struct_find_property(dataptr, propname); - if (!prop) { - RNA_warning("Property not found: %s.%s", RNA_struct_identifier(dataptr->type), propname); - return; - } - } - - activeprop = RNA_struct_find_property(active_dataptr, active_propname); - if (!activeprop) { - RNA_warning( - "Property not found: %s.%s", RNA_struct_identifier(active_dataptr->type), active_propname); - return; - } - - if (prop) { - const PropertyType type = RNA_property_type(prop); - if (type != PROP_COLLECTION) { - RNA_warning("Expected a collection data property"); - return; - } - } - - const PropertyType activetype = RNA_property_type(activeprop); - if (activetype != PROP_INT) { - RNA_warning("Expected an integer active data property"); - return; - } - - /* get icon */ - if (dataptr->data && prop) { - StructRNA *ptype = RNA_property_pointer_type(dataptr, prop); - rnaicon = RNA_struct_ui_icon(ptype); - } - - /* get active data */ - activei = RNA_property_int_get(active_dataptr, activeprop); - - /* Find the uiList type. */ - uiListType *ui_list_type = WM_uilisttype_find(listtype_name, false); - - if (ui_list_type == NULL) { - RNA_warning("List type %s not found", listtype_name); - return; - } - - uiListDrawItemFunc draw_item = ui_list_type->draw_item ? ui_list_type->draw_item : - uilist_draw_item_default; - uiListDrawFilterFunc draw_filter = ui_list_type->draw_filter ? ui_list_type->draw_filter : - uilist_draw_filter_default; - uiListFilterItemsFunc filter_items = ui_list_type->filter_items ? ui_list_type->filter_items : - uilist_filter_items_default; - - /* Find or add the uiList to the current Region. */ - /* We tag the list id with the list type... */ - BLI_snprintf( - ui_list_id, sizeof(ui_list_id), "%s_%s", ui_list_type->idname, list_id ? list_id : ""); - - /* Allows to work in popups. */ - ARegion *region = CTX_wm_menu(C); - if (region == NULL) { - region = CTX_wm_region(C); - } - uiList *ui_list = BLI_findstring(®ion->ui_lists, ui_list_id, offsetof(uiList, list_id)); - - if (!ui_list) { - ui_list = MEM_callocN(sizeof(uiList), "uiList"); - BLI_strncpy(ui_list->list_id, ui_list_id, sizeof(ui_list->list_id)); - BLI_addtail(®ion->ui_lists, ui_list); - ui_list->list_grip = -UI_LIST_AUTO_SIZE_THRESHOLD; /* Force auto size by default. */ - if (sort_reverse) { - ui_list->filter_sort_flag |= UILST_FLT_SORT_REVERSE; - } - if (sort_lock) { - ui_list->filter_sort_flag |= UILST_FLT_SORT_LOCK; - } - } - - if (!ui_list->dyn_data) { - ui_list->dyn_data = MEM_callocN(sizeof(uiListDyn), "uiList.dyn_data"); - } - uiListDyn *dyn_data = ui_list->dyn_data; - - /* Because we can't actually pass type across save&load... */ - ui_list->type = ui_list_type; - ui_list->layout_type = layout_type; - - /* Reset filtering data. */ - MEM_SAFE_FREE(dyn_data->items_filter_flags); - MEM_SAFE_FREE(dyn_data->items_filter_neworder); - dyn_data->items_len = dyn_data->items_shown = -1; - - /* When active item changed since last draw, scroll to it. */ - if (activei != ui_list->list_last_activei) { - ui_list->flag |= UILST_SCROLL_TO_ACTIVE_ITEM; - ui_list->list_last_activei = activei; - } - - /* Filter list items! (not for compact layout, though) */ - if (dataptr->data && prop) { - const int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE; - const bool order_reverse = (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0; - int items_shown, idx = 0; -#if 0 - int prev_ii = -1, prev_i; -#endif - - if (layout_type == UILST_LAYOUT_COMPACT) { - dyn_data->items_len = dyn_data->items_shown = RNA_property_collection_length(dataptr, prop); - } - else { - // printf("%s: filtering...\n", __func__); - filter_items(ui_list, C, dataptr, propname); - // printf("%s: filtering done.\n", __func__); - } - - items_shown = dyn_data->items_shown; - if (items_shown >= 0) { - bool activei_mapping_pending = true; - items_ptr = MEM_mallocN(sizeof(_uilist_item) * items_shown, __func__); - // printf("%s: items shown: %d.\n", __func__, items_shown); - RNA_PROP_BEGIN (dataptr, itemptr, prop) { - if (!dyn_data->items_filter_flags || - ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) { - int ii; - if (dyn_data->items_filter_neworder) { - ii = dyn_data->items_filter_neworder[idx++]; - ii = order_reverse ? items_shown - ii - 1 : ii; - } - else { - ii = order_reverse ? items_shown - ++idx : idx++; - } - // printf("%s: ii: %d\n", __func__, ii); - items_ptr[ii].item = itemptr; - items_ptr[ii].org_idx = i; - items_ptr[ii].flt_flag = dyn_data->items_filter_flags ? dyn_data->items_filter_flags[i] : - 0; - - if (activei_mapping_pending && activei == i) { - activei = ii; - /* So that we do not map again activei! */ - activei_mapping_pending = false; - } -#if 0 /* For now, do not alter active element, even if it will be hidden... */ - else if (activei < i) { - /* We do not want an active but invisible item! - * Only exception is when all items are filtered out... - */ - if (prev_ii >= 0) { - activei = prev_ii; - RNA_property_int_set(active_dataptr, activeprop, prev_i); - } - else { - activei = ii; - RNA_property_int_set(active_dataptr, activeprop, i); - } - } - prev_i = i; - prev_ii = ii; -#endif - } - i++; - } - RNA_PROP_END; - - if (activei_mapping_pending) { - /* No active item found, set to 'invalid' -1 value... */ - activei = -1; - } - } - if (dyn_data->items_shown >= 0) { - len = dyn_data->items_shown; - } - else { - len = dyn_data->items_len; - } - } - - switch (layout_type) { - case UILST_LAYOUT_DEFAULT: - /* layout */ - box = uiLayoutListBox(layout, ui_list, active_dataptr, activeprop); - glob = uiLayoutColumn(box, true); - row = uiLayoutRow(glob, false); - col = uiLayoutColumn(row, true); - - /* init numbers */ - uilist_prepare(ui_list, len, activei, rows, maxrows, 1, &layoutdata); - - if (dataptr->data && prop) { - /* create list items */ - for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) { - PointerRNA *itemptr = &items_ptr[i].item; - void *dyntip_data; - const int org_i = items_ptr[i].org_idx; - const int flt_flag = items_ptr[i].flt_flag; - uiBlock *subblock = uiLayoutGetBlock(col); - - overlap = uiLayoutOverlap(col); - - UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM); - - /* list item behind label & other buttons */ - sub = uiLayoutRow(overlap, false); - - but = uiDefButR_prop(subblock, - UI_BTYPE_LISTROW, - 0, - "", - 0, - 0, - UI_UNIT_X * 10, - UI_UNIT_Y, - active_dataptr, - activeprop, - 0, - 0, - org_i, - 0, - 0, - TIP_("Double click to rename")); - if ((dyntip_data = uilist_item_use_dynamic_tooltip(itemptr, item_dyntip_propname))) { - UI_but_func_tooltip_set(but, uilist_item_tooltip_func, dyntip_data, MEM_freeN); - } - - sub = uiLayoutRow(overlap, false); - - icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); - if (icon == ICON_DOT) { - icon = ICON_NONE; - } - draw_item(ui_list, - C, - sub, - dataptr, - itemptr, - icon, - active_dataptr, - active_propname, - org_i, - flt_flag); - - /* Items should be able to set context pointers for the layout. But the list-row button - * swallows events, so it needs the context storage too for handlers to see it. */ - but->context = uiLayoutGetContextStore(sub); - - /* If we are "drawing" active item, set all labels as active. */ - if (i == activei) { - ui_layout_list_set_labels_active(sub); - } - - UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM); - } - } - - /* add dummy buttons to fill space */ - for (; i < layoutdata.start_idx + layoutdata.visual_items; i++) { - uiItemL(col, "", ICON_NONE); - } - - /* add scrollbar */ - if (len > layoutdata.visual_items) { - col = uiLayoutColumn(row, false); - uiDefButI(block, - UI_BTYPE_SCROLL, - 0, - "", - 0, - 0, - V2D_SCROLL_WIDTH, - UI_UNIT_Y * dyn_data->visual_height, - &ui_list->list_scroll, - 0, - dyn_data->height - dyn_data->visual_height, - dyn_data->visual_height, - 0, - ""); - } - break; - case UILST_LAYOUT_COMPACT: - row = uiLayoutRow(layout, true); - - if ((dataptr->data && prop) && (dyn_data->items_shown > 0) && (activei >= 0) && - (activei < dyn_data->items_shown)) { - PointerRNA *itemptr = &items_ptr[activei].item; - const int org_i = items_ptr[activei].org_idx; - - icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); - if (icon == ICON_DOT) { - icon = ICON_NONE; - } - draw_item( - ui_list, C, row, dataptr, itemptr, icon, active_dataptr, active_propname, org_i, 0); - } - /* if list is empty, add in dummy button */ - else { - uiItemL(row, "", ICON_NONE); - } - - /* next/prev button */ - BLI_snprintf(numstr, sizeof(numstr), "%d :", dyn_data->items_shown); - but = uiDefIconTextButR_prop(block, - UI_BTYPE_NUM, - 0, - 0, - numstr, - 0, - 0, - UI_UNIT_X * 5, - UI_UNIT_Y, - active_dataptr, - activeprop, - 0, - 0, - 0, - 0, - 0, - ""); - if (dyn_data->items_shown == 0) { - UI_but_flag_enable(but, UI_BUT_DISABLED); - } - break; - case UILST_LAYOUT_GRID: - box = uiLayoutListBox(layout, ui_list, active_dataptr, activeprop); - glob = uiLayoutColumn(box, true); - row = uiLayoutRow(glob, false); - col = uiLayoutColumn(row, true); - subrow = NULL; /* Quite gcc warning! */ - - uilist_prepare(ui_list, len, activei, rows, maxrows, columns, &layoutdata); - - if (dataptr->data && prop) { - /* create list items */ - for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) { - PointerRNA *itemptr = &items_ptr[i].item; - const int org_i = items_ptr[i].org_idx; - const int flt_flag = items_ptr[i].flt_flag; - - /* create button */ - if (!(i % columns)) { - subrow = uiLayoutRow(col, false); - } - - uiBlock *subblock = uiLayoutGetBlock(subrow); - overlap = uiLayoutOverlap(subrow); - - UI_block_flag_enable(subblock, UI_BLOCK_LIST_ITEM); - - /* list item behind label & other buttons */ - sub = uiLayoutRow(overlap, false); - - but = uiDefButR_prop(subblock, - UI_BTYPE_LISTROW, - 0, - "", - 0, - 0, - UI_UNIT_X * 10, - UI_UNIT_Y, - active_dataptr, - activeprop, - 0, - 0, - org_i, - 0, - 0, - NULL); - UI_but_drawflag_enable(but, UI_BUT_NO_TOOLTIP); - - sub = uiLayoutRow(overlap, false); - - icon = UI_icon_from_rnaptr(C, itemptr, rnaicon, false); - draw_item(ui_list, - C, - sub, - dataptr, - itemptr, - icon, - active_dataptr, - active_propname, - org_i, - flt_flag); - - /* If we are "drawing" active item, set all labels as active. */ - if (i == activei) { - ui_layout_list_set_labels_active(sub); - } - - UI_block_flag_disable(subblock, UI_BLOCK_LIST_ITEM); - } - } - - /* add dummy buttons to fill space */ - for (; i < layoutdata.start_idx + layoutdata.visual_items; i++) { - if (!(i % columns)) { - subrow = uiLayoutRow(col, false); - } - uiItemL(subrow, "", ICON_NONE); - } - - /* add scrollbar */ - if (len > layoutdata.visual_items) { - /* col = */ uiLayoutColumn(row, false); - uiDefButI(block, - UI_BTYPE_SCROLL, - 0, - "", - 0, - 0, - V2D_SCROLL_WIDTH, - UI_UNIT_Y * dyn_data->visual_height, - &ui_list->list_scroll, - 0, - dyn_data->height - dyn_data->visual_height, - dyn_data->visual_height, - 0, - ""); - } - break; - } - - if (glob) { - /* About #UI_BTYPE_GRIP drag-resize: - * We can't directly use results from a grip button, since we have a - * rather complex behavior here (sizing by discrete steps and, overall, auto-size feature). - * Since we *never* know whether we are grip-resizing or not - * (because there is no callback for when a button enters/leaves its "edit mode"), - * we use the fact that grip-controlled value (dyn_data->resize) is completely handled - * by the grip during the grab resize, so settings its value here has no effect at all. - * - * It is only meaningful when we are not resizing, - * in which case this gives us the correct "init drag" value. - * Note we cannot affect `dyn_data->resize_prev here`, - * since this value is not controlled by the grip! - */ - dyn_data->resize = dyn_data->resize_prev + - (dyn_data->visual_height - ui_list->list_grip) * UI_UNIT_Y; - - row = uiLayoutRow(glob, true); - uiBlock *subblock = uiLayoutGetBlock(row); - UI_block_emboss_set(subblock, UI_EMBOSS_NONE); - - if (ui_list->filter_flag & UILST_FLT_SHOW) { - but = uiDefIconButBitI(subblock, - UI_BTYPE_TOGGLE, - UILST_FLT_SHOW, - 0, - ICON_DISCLOSURE_TRI_DOWN, - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y * 0.5f, - &(ui_list->filter_flag), - 0, - 0, - 0, - 0, - TIP_("Hide filtering options")); - UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */ - - but = uiDefIconButI(subblock, - UI_BTYPE_GRIP, - 0, - ICON_GRIP, - 0, - 0, - UI_UNIT_X * 10.0f, - UI_UNIT_Y * 0.5f, - &dyn_data->resize, - 0.0, - 0.0, - 0, - 0, - ""); - UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL); - - UI_block_emboss_set(subblock, UI_EMBOSS); - - col = uiLayoutColumn(glob, false); - subblock = uiLayoutGetBlock(col); - uiDefBut(subblock, - UI_BTYPE_SEPR, - 0, - "", - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y * 0.05f, - NULL, - 0.0, - 0.0, - 0, - 0, - ""); - - draw_filter(ui_list, C, col); - } - else { - but = uiDefIconButBitI(subblock, - UI_BTYPE_TOGGLE, - UILST_FLT_SHOW, - 0, - ICON_DISCLOSURE_TRI_RIGHT, - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y * 0.5f, - &(ui_list->filter_flag), - 0, - 0, - 0, - 0, - TIP_("Show filtering options")); - UI_but_flag_disable(but, UI_BUT_UNDO); /* skip undo on screen buttons */ - - but = uiDefIconButI(subblock, - UI_BTYPE_GRIP, - 0, - ICON_GRIP, - 0, - 0, - UI_UNIT_X * 10.0f, - UI_UNIT_Y * 0.5f, - &dyn_data->resize, - 0.0, - 0.0, - 0, - 0, - ""); - UI_but_func_set(but, uilist_resize_update_cb, ui_list, NULL); - - UI_block_emboss_set(subblock, UI_EMBOSS); - } - } - - if (items_ptr) { - MEM_freeN(items_ptr); - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Running Jobs Template * \{ */ diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index 7ea02226f02..93a790b53d0 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -29,6 +29,8 @@ #include "DNA_object_types.h" #include "DNA_screen_types.h" +#include "ED_screen.h" + #include "BLI_alloca.h" #include "BLI_listbase.h" #include "BLI_math.h" @@ -38,6 +40,7 @@ #include "BLT_translation.h" +#include "BKE_context.h" #include "BKE_lib_id.h" #include "BKE_report.h" @@ -48,6 +51,7 @@ #include "UI_interface.h" #include "UI_interface_icons.h" #include "UI_resources.h" +#include "UI_view2d.h" #include "WM_api.h" #include "WM_types.h" @@ -774,6 +778,98 @@ bool UI_but_online_manual_id_from_active(const struct bContext *C, char *r_str, } /* -------------------------------------------------------------------- */ + +static rctf ui_but_rect_to_view(const uiBut *but, const ARegion *region, const View2D *v2d) +{ + rctf region_rect; + ui_block_to_region_rctf(region, but->block, ®ion_rect, &but->rect); + + rctf view_rect; + UI_view2d_region_to_view_rctf(v2d, ®ion_rect, &view_rect); + + return view_rect; +} + +/** + * To get a margin (typically wanted), add the margin to \a rect directly. + * + * Based on #file_ensure_inside_viewbounds(), could probably share code. + * + * \return true if anything changed. + */ +static bool ui_view2d_cur_ensure_rect_in_view(View2D *v2d, const rctf *rect) +{ + const float rect_width = BLI_rctf_size_x(rect); + const float rect_height = BLI_rctf_size_y(rect); + + rctf *cur = &v2d->cur; + const float cur_width = BLI_rctf_size_x(cur); + const float cur_height = BLI_rctf_size_y(cur); + + bool changed = false; + + /* Snap to bottom edge. Also use if rect is higher than view bounds (could be a parameter). */ + if ((cur->ymin > rect->ymin) || (rect_height > cur_height)) { + cur->ymin = rect->ymin; + cur->ymax = cur->ymin + cur_height; + changed = true; + } + /* Snap to upper edge. */ + else if (cur->ymax < rect->ymax) { + cur->ymax = rect->ymax; + cur->ymin = cur->ymax - cur_height; + changed = true; + } + /* Snap to left edge. Also use if rect is wider than view bounds. */ + else if ((cur->xmin > rect->xmin) || (rect_width > cur_width)) { + cur->xmin = rect->xmin; + cur->xmax = cur->xmin + cur_width; + changed = true; + } + /* Snap to right edge. */ + else if (cur->xmax < rect->xmax) { + cur->xmax = rect->xmax; + cur->xmin = cur->xmax - cur_width; + changed = true; + } + else { + BLI_assert(BLI_rctf_inside_rctf(cur, rect)); + } + + return changed; +} + +/** + * Adjust the view so the rectangle of \a but is in view, with some extra margin. + * + * It's important that this is only executed after buttons received their final #uiBut.rect. E.g. + * #UI_panels_end() modifies them, so if that is executed, this function must not be called before + * it. + * + * \param region: The region the button is placed in. Make sure this is actually the one the button + * is placed in, not just the context region. + */ +void UI_but_ensure_in_view(const bContext *C, ARegion *region, const uiBut *but) +{ + View2D *v2d = ®ion->v2d; + /* Uninitialized view or region that doesn't use View2D. */ + if ((v2d->flag & V2D_IS_INIT) == 0) { + return; + } + + rctf rect = ui_but_rect_to_view(but, region, v2d); + + const int margin = UI_UNIT_X * 0.5f; + BLI_rctf_pad(&rect, margin, margin); + + const bool changed = ui_view2d_cur_ensure_rect_in_view(v2d, &rect); + if (changed) { + UI_view2d_curRect_changed(C, v2d); + ED_region_tag_redraw_no_rebuild(region); + } +} + +/* -------------------------------------------------------------------- */ /** \name Button Store * * Modal Button Store API. diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 7b2e0fec1d0..bcd945b21a9 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -105,6 +105,7 @@ typedef enum { /* specials */ UI_WTYPE_ICON, UI_WTYPE_ICON_LABEL, + UI_WTYPE_PREVIEW_TILE, UI_WTYPE_SWATCH, UI_WTYPE_RGB_PICKER, UI_WTYPE_UNITVEC, @@ -1377,8 +1378,6 @@ static int ui_but_draw_menu_icon(const uiBut *but) static void widget_draw_icon( const uiBut *but, BIFIconID icon, float alpha, const rcti *rect, const uchar mono_color[4]) { - float xs = 0.0f, ys = 0.0f; - if (but->flag & UI_BUT_ICON_PREVIEW) { GPU_blend(GPU_BLEND_ALPHA); widget_draw_preview(icon, alpha, rect); @@ -1420,6 +1419,7 @@ static void widget_draw_icon( if (icon && icon != ICON_BLANK1) { const float ofs = 1.0f / aspect; + float xs, ys; if (but->drawflag & UI_BUT_ICON_LEFT) { /* special case - icon_only pie buttons */ @@ -3116,7 +3116,7 @@ void ui_draw_gradient(const rcti *rect, copy_v3_v3(col1[3], col1[2]); break; default: - BLI_assert(!"invalid 'type' argument"); + BLI_assert_msg(0, "invalid 'type' argument"); hsv_to_rgb(1.0, 1.0, 1.0, &col1[2][0], &col1[2][1], &col1[2][2]); copy_v3_v3(col1[0], col1[2]); copy_v3_v3(col1[1], col1[2]); @@ -4019,6 +4019,14 @@ static void widget_textbut(uiWidgetColors *wcol, rcti *rect, int state, int roun widgetbase_draw(&wtb, wcol); } +static void widget_preview_tile( + uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign)) +{ + const uiStyle *style = UI_style_get(); + ui_draw_preview_item_stateless( + &style->widget, rect, but->drawstr, but->icon, wcol->text, UI_STYLE_TEXT_CENTER); +} + static void widget_menuiconbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), @@ -4484,6 +4492,13 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) wt.custom = widget_icon_has_anim; break; + case UI_WTYPE_PREVIEW_TILE: + wt.draw = NULL; + /* Drawn via the `custom` callback. */ + wt.text = NULL; + wt.custom = widget_preview_tile; + break; + case UI_WTYPE_SWATCH: wt.custom = widget_swatch; break; @@ -4779,6 +4794,10 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu wt = widget_type(UI_WTYPE_BOX); break; + case UI_BTYPE_PREVIEW_TILE: + wt = widget_type(UI_WTYPE_PREVIEW_TILE); + break; + case UI_BTYPE_EXTRA: widget_draw_extra_mask(C, but, widget_type(UI_WTYPE_BOX), rect); break; @@ -4932,13 +4951,15 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu wt->draw(&wt->wcol, rect, state, roundboxalign); } - if (use_alpha_blend) { - GPU_blend(GPU_BLEND_ALPHA); - } + if (wt->text) { + if (use_alpha_blend) { + GPU_blend(GPU_BLEND_ALPHA); + } - wt->text(fstyle, &wt->wcol, but, rect); - if (use_alpha_blend) { - GPU_blend(GPU_BLEND_NONE); + wt->text(fstyle, &wt->wcol, but, rect); + if (use_alpha_blend) { + GPU_blend(GPU_BLEND_NONE); + } } } @@ -5375,7 +5396,7 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, } } else { - BLI_assert(!"Unknwon menu item separator type"); + BLI_assert_msg(0, "Unknwon menu item separator type"); } if (fstyle->kerning == 1) { @@ -5460,17 +5481,20 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, } } -void ui_draw_preview_item( - const uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state) +/** + * Version of #ui_draw_preview_item() that does not draw the menu background and item text based on + * state. It just draws the preview and text directly. + */ +void ui_draw_preview_item_stateless(const uiFontStyle *fstyle, + rcti *rect, + const char *name, + int iconid, + const uchar text_col[4], + eFontStyle_Align text_align) { rcti trect = *rect; const float text_size = UI_UNIT_Y; float font_dims[2] = {0.0f, 0.0f}; - uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM); - - /* drawing button background */ - wt->state(wt, state, 0, UI_EMBOSS_UNDEFINED); - wt->draw(&wt->wcol, rect, 0, 0); /* draw icon in rect above the space reserved for the label */ rect->ymin += text_size; @@ -5482,8 +5506,6 @@ void ui_draw_preview_item( fstyle->uifont_id, name, BLF_DRAW_STR_DUMMY_MAX, &font_dims[0], &font_dims[1]); /* text rect */ - trect.xmin += 0; - trect.xmax = trect.xmin + font_dims[0] + U.widget_unit / 2; trect.ymin += U.widget_unit / 2; trect.ymax = trect.ymin + font_dims[1]; if (trect.xmax > rect->xmax - PREVIEW_PAD) { @@ -5502,11 +5524,27 @@ void ui_draw_preview_item( UI_fontstyle_draw(fstyle, &trect, drawstr, - wt->wcol.text, + text_col, &(struct uiFontStyleDraw_Params){ - .align = UI_STYLE_TEXT_CENTER, + .align = text_align, }); } } +void ui_draw_preview_item(const uiFontStyle *fstyle, + rcti *rect, + const char *name, + int iconid, + int state, + eFontStyle_Align text_align) +{ + uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM); + + /* drawing button background */ + wt->state(wt, state, 0, UI_EMBOSS_UNDEFINED); + wt->draw(&wt->wcol, rect, 0, 0); + + ui_draw_preview_item_stateless(fstyle, rect, name, iconid, wt->wcol.text, text_align); +} + /** \} */ diff --git a/source/blender/editors/interface/view2d_edge_pan.c b/source/blender/editors/interface/view2d_edge_pan.c index ca32a754f1d..1d300c7b275 100644 --- a/source/blender/editors/interface/view2d_edge_pan.c +++ b/source/blender/editors/interface/view2d_edge_pan.c @@ -160,7 +160,7 @@ static float edge_pan_speed(View2DEdgePanData *vpd, distance = min - event_loc; } else { - BLI_assert(!"Calculating speed outside of pan zones"); + BLI_assert_msg(0, "Calculating speed outside of pan zones"); return 0.0f; } float distance_factor = distance / (vpd->speed_ramp * U.widget_unit); diff --git a/source/blender/editors/io/io_gpencil.h b/source/blender/editors/io/io_gpencil.h index b347be00412..428b09f0e9c 100644 --- a/source/blender/editors/io/io_gpencil.h +++ b/source/blender/editors/io/io_gpencil.h @@ -25,8 +25,8 @@ */ struct ARegion; -struct bContext; struct View3D; +struct bContext; struct wmOperatorType; void WM_OT_gpencil_import_svg(struct wmOperatorType *ot); diff --git a/source/blender/editors/lattice/editlattice_select.c b/source/blender/editors/lattice/editlattice_select.c index e73a02840c9..e6f190f335b 100644 --- a/source/blender/editors/lattice/editlattice_select.c +++ b/source/blender/editors/lattice/editlattice_select.c @@ -514,7 +514,7 @@ static int lattice_select_ungrouped_exec(bContext *C, wmOperator *op) BPoint *bp; int a, tot; - if (BLI_listbase_is_empty(&obedit->defbase) || lt->dvert == NULL) { + if (BLI_listbase_is_empty(<->vertex_group_names) || lt->dvert == NULL) { continue; } diff --git a/source/blender/editors/lattice/editlattice_undo.c b/source/blender/editors/lattice/editlattice_undo.c index d92a81179cc..23eaf991fd3 100644 --- a/source/blender/editors/lattice/editlattice_undo.c +++ b/source/blender/editors/lattice/editlattice_undo.c @@ -240,7 +240,7 @@ static void lattice_undosys_step_decode(struct bContext *C, } undolatt_to_editlatt(&elem->data, lt->editlatt); lt->editlatt->needs_flush_to_id = 1; - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(<->id, ID_RECALC_GEOMETRY); } /* The first element is always active */ diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c index 3476f1ca735..6fa7457ce14 100644 --- a/source/blender/editors/mask/mask_ops.c +++ b/source/blender/editors/mask/mask_ops.c @@ -390,7 +390,7 @@ static void select_sliding_point(Mask *mask, point->bezt.f3 |= SELECT; break; default: - BLI_assert(!"Unexpected situation in select_sliding_point()"); + BLI_assert_msg(0, "Unexpected situation in select_sliding_point()"); } mask_layer->act_spline = spline; diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index 942fe143787..09b17acf56d 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -30,6 +30,7 @@ #include "BKE_context.h" #include "BKE_curve.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c index db35742fa78..38e6bb80c0f 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.c +++ b/source/blender/editors/mesh/editmesh_mask_extract.c @@ -129,7 +129,7 @@ static int geometry_extract_apply(bContext *C, .calc_face_normal = true, })); - BMEditMesh *em = BKE_editmesh_create(bm, false); + BMEditMesh *em = BKE_editmesh_create(bm); /* Generate the tags for deleting geometry in the extracted object. */ tag_fn(bm, params); @@ -209,7 +209,7 @@ static int geometry_extract_apply(bContext *C, }), mesh); - BKE_editmesh_free(em); + BKE_editmesh_free_data(em); MEM_freeN(em); if (new_mesh->totvert == 0) { diff --git a/source/blender/editors/mesh/editmesh_path.c b/source/blender/editors/mesh/editmesh_path.c index 593545ddcef..30a453a32ee 100644 --- a/source/blender/editors/mesh/editmesh_path.c +++ b/source/blender/editors/mesh/editmesh_path.c @@ -29,6 +29,7 @@ #include "DNA_windowmanager_types.h" #ifdef WITH_FREESTYLE +# include "BKE_customdata.h" # include "DNA_meshdata_types.h" #endif diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 6753c235a8d..830c9abb41e 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -35,6 +35,8 @@ #include "BLI_utildefines_stack.h" #include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_layer.h" #include "BKE_report.h" @@ -4739,10 +4741,11 @@ static bool edbm_select_ungrouped_poll(bContext *C) BMEditMesh *em = BKE_editmesh_from_object(obedit); const int cd_dvert_offset = CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT); + const ListBase *defbase = BKE_object_defgroup_list(obedit); if ((em->selectmode & SCE_SELECT_VERTEX) == 0) { CTX_wm_operator_poll_msg_set(C, "Must be in vertex selection mode"); } - else if (BLI_listbase_is_empty(&obedit->defbase) || cd_dvert_offset == -1) { + else if (BLI_listbase_is_empty(defbase) || cd_dvert_offset == -1) { CTX_wm_operator_poll_msg_set(C, "No weights/vertex groups on object"); } else { diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c index ff4db96061f..c452f7a7487 100644 --- a/source/blender/editors/mesh/editmesh_select_similar.c +++ b/source/blender/editors/mesh/editmesh_select_similar.c @@ -29,6 +29,8 @@ #include "BLI_math.h" #include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_layer.h" #include "BKE_material.h" @@ -1041,7 +1043,7 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op) if (cd_dvert_offset == -1) { continue; } - defbase_len = BLI_listbase_count(&ob->defbase); + defbase_len = BKE_object_defgroup_count(ob); if (defbase_len == 0) { continue; } @@ -1090,8 +1092,10 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op) /* We store the names of the vertex groups, so we can select * vertex groups with the same name in different objects. */ + const ListBase *defbase = BKE_object_defgroup_list(ob); + int i = 0; - LISTBASE_FOREACH (bDeformGroup *, dg, &ob->defbase) { + LISTBASE_FOREACH (bDeformGroup *, dg, defbase) { if (BLI_BITMAP_TEST(defbase_selected, i)) { BLI_gset_add(gset, dg->name); } @@ -1128,7 +1132,8 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op) if (cd_dvert_offset == -1) { continue; } - defbase_len = BLI_listbase_count(&ob->defbase); + const ListBase *defbase = BKE_object_defgroup_list(ob); + defbase_len = BLI_listbase_count(defbase); if (defbase_len == 0) { continue; } @@ -1141,7 +1146,7 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op) GSetIterator gs_iter; GSET_ITER (gs_iter, gset) { const char *name = BLI_gsetIterator_getKey(&gs_iter); - int vgroup_id = BLI_findstringindex(&ob->defbase, name, offsetof(bDeformGroup, name)); + int vgroup_id = BLI_findstringindex(defbase, name, offsetof(bDeformGroup, name)); if (vgroup_id != -1) { BLI_BITMAP_ENABLE(defbase_selected, vgroup_id); found_any = true; diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 2e85817b9d0..4278d3906ff 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -44,6 +44,7 @@ #include "BLI_string.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_key.h" @@ -5743,7 +5744,7 @@ static int edbm_decimate_exec(bContext *C, wmOperator *op) float *vweights = MEM_mallocN(sizeof(*vweights) * bm->totvert, __func__); { const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT); - const int defbase_act = obedit->actdef - 1; + const int defbase_act = BKE_object_defgroup_active_index_get(obedit) - 1; if (use_vertex_group && (cd_dvert_offset == -1)) { BKE_report(op->reports, RPT_WARNING, "No active vertex group"); diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 512481cdbfe..8dc7a1d7675 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -33,6 +33,7 @@ #include "BLI_listbase.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_key.h" #include "BKE_layer.h" @@ -671,7 +672,7 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key * em->bm->shapenr = um->shapenr; - EDBM_mesh_free(em); + EDBM_mesh_free_data(em); bm = BM_mesh_create(&allocsize, &((struct BMeshCreateParams){ @@ -681,13 +682,21 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key * BM_mesh_bm_from_me(NULL, bm, &um->me, (&(struct BMeshFromMeshParams){ - .calc_face_normal = true, + /* Handled with tessellation. */ + .calc_face_normal = false, .active_shapekey = um->shapenr, })); - em_tmp = BKE_editmesh_create(bm, true); + em_tmp = BKE_editmesh_create(bm); *em = *em_tmp; + /* Calculate face normals and tessellation at once since it's multi-threaded. + * The vertex normals are stored in the undo-mesh, so this doesn't need to be updated. */ + BKE_editmesh_looptri_calc_ex(em, + &(const struct BMeshCalcTessellation_Params){ + .face_normals = true, + }); + em->selectmode = um->selectmode; bm->selectmode = um->selectmode; @@ -865,7 +874,7 @@ static void mesh_undosys_step_decode(struct bContext *C, BMEditMesh *em = me->edit_mesh; undomesh_to_editmesh(&elem->data, obedit, em, me->key); em->needs_flush_to_id = 1; - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY); } /* The first element is always active */ diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index f4456f0b465..d765f6b50af 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -305,17 +305,13 @@ void EDBM_mesh_make(Object *ob, const int select_mode, const bool add_key_index) if (me->edit_mesh) { /* this happens when switching shape keys */ - EDBM_mesh_free(me->edit_mesh); + EDBM_mesh_free_data(me->edit_mesh); MEM_freeN(me->edit_mesh); } - /* currently executing operators re-tessellates, so we can avoid doing here - * but at some point it may need to be added back. */ -#if 0 - me->edit_mesh = BKE_editmesh_create(bm, true); -#else - me->edit_mesh = BKE_editmesh_create(bm, false); -#endif + /* Executing operators re-tessellates, + * so we can avoid doing here but at some point it may need to be added back. */ + me->edit_mesh = BKE_editmesh_create(bm); me->edit_mesh->selectmode = me->edit_mesh->bm->selectmode = select_mode; me->edit_mesh->mat_nr = (ob->actcol > 0) ? ob->actcol - 1 : 0; @@ -326,7 +322,8 @@ void EDBM_mesh_make(Object *ob, const int select_mode, const bool add_key_index) /** * \warning This can invalidate the #Mesh runtime cache of other objects (for linked duplicates). - * Most callers should run #DEG_id_tag_update on \a ob->data, see: T46738, T46913 + * Most callers should run #DEG_id_tag_update on `ob->data`, see: T46738, T46913. + * This ensures #BKE_object_free_derived_caches runs on all objects that use this mesh. */ void EDBM_mesh_load_ex(Main *bmain, Object *ob, bool free_data) { @@ -347,25 +344,6 @@ void EDBM_mesh_load_ex(Main *bmain, Object *ob, bool free_data) .calc_object_remap = true, .update_shapekey_indices = !free_data, })); - - /* Free derived mesh. usually this would happen through depsgraph but there - * are exceptions like file save that will not cause this, and we want to - * avoid ending up with an invalid derived mesh then. - * - * Do it for all objects which shares the same mesh datablock, since their - * derived meshes might also be referencing data which was just freed, - * - * Annoying enough, but currently seems most efficient way to avoid access - * of freed data on scene update, especially in cases when there are dependency - * cycles. - */ -#if 0 - for (Object *other_object = bmain->objects.first; other_object != NULL; other_object = other_object->id.next) { - if (other_object->data == ob->data) { - BKE_object_free_derived_caches(other_object); - } - } -#endif } void EDBM_mesh_clear(BMEditMesh *em) @@ -373,8 +351,8 @@ void EDBM_mesh_clear(BMEditMesh *em) /* clear bmesh */ BM_mesh_clear(em->bm); - /* free derived meshes */ - BKE_editmesh_free_derivedmesh(em); + /* Free evaluated meshes & cache. */ + BKE_editmesh_free_derived_caches(em); /* free tessellation data */ em->tottri = 0; @@ -390,9 +368,9 @@ void EDBM_mesh_load(Main *bmain, Object *ob) } /** - * Should only be called on the active editmesh, otherwise call #BKE_editmesh_free + * Should only be called on the active edit-mesh, otherwise call #BKE_editmesh_free_data. */ -void EDBM_mesh_free(BMEditMesh *em) +void EDBM_mesh_free_data(BMEditMesh *em) { /* These tables aren't used yet, so it's not strictly necessary * to 'end' them but if someone tries to start using them, @@ -400,7 +378,7 @@ void EDBM_mesh_free(BMEditMesh *em) ED_mesh_mirror_spatial_table_end(NULL); ED_mesh_mirror_topo_table_end(NULL); - BKE_editmesh_free(em); + BKE_editmesh_free_data(em); } /** \} */ @@ -1472,8 +1450,8 @@ void EDBM_update(Mesh *mesh, const struct EDBMUpdate_Params *params) BM_lnorspace_invalidate(em->bm, false); em->bm->spacearr_dirty &= ~BM_SPACEARR_BMO_SET; } - /* don't keep stale derivedMesh data around, see: T38872. */ - BKE_editmesh_free_derivedmesh(em); + /* Don't keep stale evaluated mesh data around, see: T38872. */ + BKE_editmesh_free_derived_caches(em); #ifdef DEBUG { diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index 2b78d83a9f5..73b3fb9724e 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -34,6 +34,7 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_mesh.h" #include "BKE_report.h" diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index ca68c47cf8e..27fb21e1dfb 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -475,16 +475,17 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) me = ob_iter->data; /* Join this object's vertex groups to the base one's */ - for (dg = ob_iter->defbase.first; dg; dg = dg->next) { + for (dg = me->vertex_group_names.first; dg; dg = dg->next) { /* See if this group exists in the object (if it doesn't, add it to the end) */ if (!BKE_object_defgroup_find_name(ob, dg->name)) { odg = MEM_mallocN(sizeof(bDeformGroup), "join deformGroup"); memcpy(odg, dg, sizeof(bDeformGroup)); - BLI_addtail(&ob->defbase, odg); + BLI_addtail(&mesh_active->vertex_group_names, odg); } } - if (ob->defbase.first && ob->actdef == 0) { - ob->actdef = 1; + if (!BLI_listbase_is_empty(&mesh_active->vertex_group_names) && + me->vertex_group_active_index == 0) { + me->vertex_group_active_index = 1; } /* Join this object's face maps to the base one's. */ @@ -1473,19 +1474,21 @@ bool ED_mesh_pick_vert( MDeformVert *ED_mesh_active_dvert_get_em(Object *ob, BMVert **r_eve) { - if (ob->mode & OB_MODE_EDIT && ob->type == OB_MESH && ob->defbase.first) { + if (ob->mode & OB_MODE_EDIT && ob->type == OB_MESH) { Mesh *me = ob->data; - BMesh *bm = me->edit_mesh->bm; - const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT); + if (!BLI_listbase_is_empty(&me->vertex_group_names)) { + BMesh *bm = me->edit_mesh->bm; + const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT); - if (cd_dvert_offset != -1) { - BMVert *eve = BM_mesh_active_vert_get(bm); + if (cd_dvert_offset != -1) { + BMVert *eve = BM_mesh_active_vert_get(bm); - if (eve) { - if (r_eve) { - *r_eve = eve; + if (eve) { + if (r_eve) { + *r_eve = eve; + } + return BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); } - return BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); } } } diff --git a/source/blender/editors/metaball/editmball_undo.c b/source/blender/editors/metaball/editmball_undo.c index a8b471a7c92..f7b53b5513f 100644 --- a/source/blender/editors/metaball/editmball_undo.c +++ b/source/blender/editors/metaball/editmball_undo.c @@ -215,7 +215,7 @@ static void mball_undosys_step_decode(struct bContext *C, } undomball_to_editmball(&elem->data, mb); mb->needs_flush_to_id = 1; - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); + DEG_id_tag_update(&mb->id, ID_RECALC_GEOMETRY); } /* The first element is always active */ diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c index 742c7abb7ff..6251fb799c5 100644 --- a/source/blender/editors/object/object_data_transfer.c +++ b/source/blender/editors/object/object_data_transfer.c @@ -33,6 +33,7 @@ #include "BKE_context.h" #include "BKE_data_transfer.h" +#include "BKE_deform.h" #include "BKE_mesh_mapping.h" #include "BKE_mesh_remap.h" #include "BKE_mesh_runtime.h" @@ -122,9 +123,14 @@ static const EnumPropertyItem *dt_layers_select_src_itemf(bContext *C, RNA_enum_items_add_value( &item, &totitem, rna_enum_dt_layers_select_src_items, DT_LAYERS_ALL_SRC); - if (data_type == DT_TYPE_MDEFORMVERT) { - Object *ob_src = CTX_data_active_object(C); + Object *ob_src = CTX_data_active_object(C); + if (ob_src == NULL) { + RNA_enum_item_end(&item, &totitem); + *r_free = true; + return item; + } + if (data_type == DT_TYPE_MDEFORMVERT && BKE_object_supports_vertex_groups(ob_src)) { if (BKE_object_pose_armature_get(ob_src)) { RNA_enum_items_add_value( &item, &totitem, rna_enum_dt_layers_select_src_items, DT_LAYERS_VGROUP_SRC_BONE_SELECT); @@ -132,66 +138,57 @@ static const EnumPropertyItem *dt_layers_select_src_itemf(bContext *C, &item, &totitem, rna_enum_dt_layers_select_src_items, DT_LAYERS_VGROUP_SRC_BONE_DEFORM); } - if (ob_src) { - bDeformGroup *dg; - int i; + const bDeformGroup *dg; + int i; - RNA_enum_item_add_separator(&item, &totitem); + RNA_enum_item_add_separator(&item, &totitem); - for (i = 0, dg = ob_src->defbase.first; dg; i++, dg = dg->next) { - tmp_item.value = i; - tmp_item.identifier = tmp_item.name = dg->name; - RNA_enum_item_add(&item, &totitem, &tmp_item); - } + const ListBase *defbase = BKE_object_defgroup_list(ob_src); + for (i = 0, dg = defbase->first; dg; i++, dg = dg->next) { + tmp_item.value = i; + tmp_item.identifier = tmp_item.name = dg->name; + RNA_enum_item_add(&item, &totitem, &tmp_item); } } else if (data_type == DT_TYPE_SHAPEKEY) { /* TODO */ } else if (data_type == DT_TYPE_UV) { - Object *ob_src = CTX_data_active_object(C); - - if (ob_src) { - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Object *ob_src_eval = DEG_get_evaluated_object(depsgraph, ob_src); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Object *ob_src_eval = DEG_get_evaluated_object(depsgraph, ob_src); - CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH; - cddata_masks.lmask |= CD_MASK_MLOOPUV; - Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_src_eval, &cddata_masks); - int num_data = CustomData_number_of_layers(&me_eval->ldata, CD_MLOOPUV); + CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH; + cddata_masks.lmask |= CD_MASK_MLOOPUV; + Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_src_eval, &cddata_masks); + int num_data = CustomData_number_of_layers(&me_eval->ldata, CD_MLOOPUV); - RNA_enum_item_add_separator(&item, &totitem); + RNA_enum_item_add_separator(&item, &totitem); - for (int i = 0; i < num_data; i++) { - tmp_item.value = i; - tmp_item.identifier = tmp_item.name = CustomData_get_layer_name( - &me_eval->ldata, CD_MLOOPUV, i); - RNA_enum_item_add(&item, &totitem, &tmp_item); - } + for (int i = 0; i < num_data; i++) { + tmp_item.value = i; + tmp_item.identifier = tmp_item.name = CustomData_get_layer_name( + &me_eval->ldata, CD_MLOOPUV, i); + RNA_enum_item_add(&item, &totitem, &tmp_item); } } else if (data_type == DT_TYPE_VCOL) { - Object *ob_src = CTX_data_active_object(C); - - if (ob_src) { - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); - Object *ob_src_eval = DEG_get_evaluated_object(depsgraph, ob_src); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Object *ob_src_eval = DEG_get_evaluated_object(depsgraph, ob_src); - CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH; - cddata_masks.lmask |= CD_MASK_MLOOPCOL; - Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_src_eval, &cddata_masks); - int num_data = CustomData_number_of_layers(&me_eval->ldata, CD_MLOOPCOL); + CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH; + cddata_masks.lmask |= CD_MASK_MLOOPCOL; + Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_src_eval, &cddata_masks); + int num_data = CustomData_number_of_layers(&me_eval->ldata, CD_MLOOPCOL); - RNA_enum_item_add_separator(&item, &totitem); + RNA_enum_item_add_separator(&item, &totitem); - for (int i = 0; i < num_data; i++) { - tmp_item.value = i; - tmp_item.identifier = tmp_item.name = CustomData_get_layer_name( - &me_eval->ldata, CD_MLOOPCOL, i); - RNA_enum_item_add(&item, &totitem, &tmp_item); - } + for (int i = 0; i < num_data; i++) { + tmp_item.value = i; + tmp_item.identifier = tmp_item.name = CustomData_get_layer_name( + &me_eval->ldata, CD_MLOOPCOL, i); + RNA_enum_item_add(&item, &totitem, &tmp_item); } } diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 1cb6a5c8ff5..6108691b2f1 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -557,7 +557,7 @@ static bool ED_object_editmode_load_free_ex(Main *bmain, } if (free_data) { - EDBM_mesh_free(me->edit_mesh); + EDBM_mesh_free_data(me->edit_mesh); MEM_freeN(me->edit_mesh); me->edit_mesh = NULL; } diff --git a/source/blender/editors/object/object_hook.c b/source/blender/editors/object/object_hook.c index fcee2818b22..5065a2c00f0 100644 --- a/source/blender/editors/object/object_hook.c +++ b/source/blender/editors/object/object_hook.c @@ -105,14 +105,13 @@ static int return_editmesh_indexar(BMEditMesh *em, int *r_tot, int **r_indexar, static bool return_editmesh_vgroup(Object *obedit, BMEditMesh *em, char *r_name, float r_cent[3]) { - const int cd_dvert_offset = obedit->actdef ? + const int active_index = BKE_object_defgroup_active_index_get(obedit); + const int cd_dvert_offset = active_index ? CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT) : -1; - zero_v3(r_cent); - if (cd_dvert_offset != -1) { - const int defgrp_index = obedit->actdef - 1; + const int defgrp_index = active_index - 1; int totvert = 0; MDeformVert *dvert; @@ -129,7 +128,8 @@ static bool return_editmesh_vgroup(Object *obedit, BMEditMesh *em, char *r_name, } } if (totvert) { - bDeformGroup *dg = BLI_findlink(&obedit->defbase, defgrp_index); + const ListBase *defbase = BKE_object_defgroup_list(obedit); + bDeformGroup *dg = BLI_findlink(defbase, defgrp_index); BLI_strncpy(r_name, dg->name, sizeof(dg->name)); mul_v3_fl(r_cent, 1.0f / (float)totvert); return true; diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 5bf04e195fe..5a3a28b5a3f 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -247,7 +247,6 @@ void OBJECT_OT_vertex_group_assign_new(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_remove_from(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_select(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_deselect(struct wmOperatorType *ot); -void OBJECT_OT_vertex_group_copy_to_linked(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_copy_to_selected(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_copy(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_normalize(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index dc60d4df7d1..a438c760d3b 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -201,7 +201,6 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_vertex_group_remove_from); WM_operatortype_append(OBJECT_OT_vertex_group_select); WM_operatortype_append(OBJECT_OT_vertex_group_deselect); - WM_operatortype_append(OBJECT_OT_vertex_group_copy_to_linked); WM_operatortype_append(OBJECT_OT_vertex_group_copy_to_selected); WM_operatortype_append(OBJECT_OT_vertex_group_copy); WM_operatortype_append(OBJECT_OT_vertex_group_normalize); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index d7177eabcba..c61965b3e23 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2022,7 +2022,7 @@ static void single_obdata_users( break; default: printf("ERROR %s: can't copy %s\n", __func__, id->name); - BLI_assert(!"This should never happen."); + BLI_assert_msg(0, "This should never happen."); /* We need to end the FOREACH_OBJECT_FLAG_BEGIN iterator to prevent memory leak. */ BKE_scene_objects_iterator_end(&iter_macro); diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index e350653e178..4c4727f51ee 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -1152,51 +1152,53 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) for (int object_index = 0; object_index < num_objects; object_index++) { Object *ob = objects[object_index]; + if (ob->flag & OB_DONE) { + continue; + } - if ((ob->flag & OB_DONE) == 0) { - bool do_inverse_offset = false; - ob->flag |= OB_DONE; + bool do_inverse_offset = false; + ob->flag |= OB_DONE; - if (centermode == ORIGIN_TO_CURSOR) { - copy_v3_v3(cent, cursor); - invert_m4_m4(ob->imat, ob->obmat); - mul_m4_v3(ob->imat, cent); - } + if (centermode == ORIGIN_TO_CURSOR) { + copy_v3_v3(cent, cursor); + invert_m4_m4(ob->imat, ob->obmat); + mul_m4_v3(ob->imat, cent); + } - if (ob->data == NULL) { - /* special support for dupligroups */ - if ((ob->transflag & OB_DUPLICOLLECTION) && ob->instance_collection && - (ob->instance_collection->id.tag & LIB_TAG_DOIT) == 0) { - if (ID_IS_LINKED(ob->instance_collection)) { - tot_lib_error++; + if (ob->data == NULL) { + /* special support for dupligroups */ + if ((ob->transflag & OB_DUPLICOLLECTION) && ob->instance_collection && + (ob->instance_collection->id.tag & LIB_TAG_DOIT) == 0) { + if (ID_IS_LINKED(ob->instance_collection)) { + tot_lib_error++; + } + else { + if (centermode == ORIGIN_TO_CURSOR) { + /* done */ } else { - if (centermode == ORIGIN_TO_CURSOR) { - /* done */ - } - else { - float min[3], max[3]; - /* only bounds support */ - INIT_MINMAX(min, max); - BKE_object_minmax_dupli(depsgraph, scene, ob, min, max, true); - mid_v3_v3v3(cent, min, max); - invert_m4_m4(ob->imat, ob->obmat); - mul_m4_v3(ob->imat, cent); - } + float min[3], max[3]; + /* only bounds support */ + INIT_MINMAX(min, max); + BKE_object_minmax_dupli(depsgraph, scene, ob, min, max, true); + mid_v3_v3v3(cent, min, max); + invert_m4_m4(ob->imat, ob->obmat); + mul_m4_v3(ob->imat, cent); + } - add_v3_v3(ob->instance_collection->instance_offset, cent); + add_v3_v3(ob->instance_collection->instance_offset, cent); - tot_change++; - ob->instance_collection->id.tag |= LIB_TAG_DOIT; - do_inverse_offset = true; - } + tot_change++; + ob->instance_collection->id.tag |= LIB_TAG_DOIT; + do_inverse_offset = true; } } - else if (ID_IS_LINKED(ob->data)) { - tot_lib_error++; - } - - if (obedit == NULL && ob->type == OB_MESH) { + } + else if (ID_IS_LINKED(ob->data)) { + tot_lib_error++; + } + else if (ob->type == OB_MESH) { + if (obedit == NULL) { Mesh *me = ob->data; if (centermode == ORIGIN_TO_CURSOR) { @@ -1222,265 +1224,265 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) me->id.tag |= LIB_TAG_DOIT; do_inverse_offset = true; } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { - Curve *cu = ob->data; + } + else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + Curve *cu = ob->data; - if (centermode == ORIGIN_TO_CURSOR) { - /* done */ - } - else if (around == V3D_AROUND_CENTER_BOUNDS) { - BKE_curve_center_bounds(cu, cent); - } - else { /* #V3D_AROUND_CENTER_MEDIAN. */ - BKE_curve_center_median(cu, cent); - } + if (centermode == ORIGIN_TO_CURSOR) { + /* done */ + } + else if (around == V3D_AROUND_CENTER_BOUNDS) { + BKE_curve_center_bounds(cu, cent); + } + else { /* #V3D_AROUND_CENTER_MEDIAN. */ + BKE_curve_center_median(cu, cent); + } - /* don't allow Z change if curve is 2D */ - if ((ob->type == OB_CURVE) && !(cu->flag & CU_3D)) { - cent[2] = 0.0; - } + /* don't allow Z change if curve is 2D */ + if ((ob->type == OB_CURVE) && !(cu->flag & CU_3D)) { + cent[2] = 0.0; + } - negate_v3_v3(cent_neg, cent); - BKE_curve_translate(cu, cent_neg, 1); + negate_v3_v3(cent_neg, cent); + BKE_curve_translate(cu, cent_neg, 1); - tot_change++; - cu->id.tag |= LIB_TAG_DOIT; - do_inverse_offset = true; + tot_change++; + cu->id.tag |= LIB_TAG_DOIT; + do_inverse_offset = true; - if (obedit) { - if (centermode == GEOMETRY_TO_ORIGIN) { - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); - } - break; + if (obedit) { + if (centermode == GEOMETRY_TO_ORIGIN) { + DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); } + break; } - else if (ob->type == OB_FONT) { - /* Get from bounding-box. */ + } + else if (ob->type == OB_FONT) { + /* Get from bounding-box. */ - Curve *cu = ob->data; + Curve *cu = ob->data; - if (ob->runtime.bb == NULL && (centermode != ORIGIN_TO_CURSOR)) { - /* Do nothing. */ + if (ob->runtime.bb == NULL && (centermode != ORIGIN_TO_CURSOR)) { + /* Do nothing. */ + } + else { + if (centermode == ORIGIN_TO_CURSOR) { + /* Done. */ } else { - if (centermode == ORIGIN_TO_CURSOR) { - /* Done. */ - } - else { - /* extra 0.5 is the height o above line */ - cent[0] = 0.5f * (ob->runtime.bb->vec[4][0] + ob->runtime.bb->vec[0][0]); - cent[1] = 0.5f * (ob->runtime.bb->vec[0][1] + ob->runtime.bb->vec[2][1]); - } + /* extra 0.5 is the height o above line */ + cent[0] = 0.5f * (ob->runtime.bb->vec[4][0] + ob->runtime.bb->vec[0][0]); + cent[1] = 0.5f * (ob->runtime.bb->vec[0][1] + ob->runtime.bb->vec[2][1]); + } - cent[2] = 0.0f; + cent[2] = 0.0f; - cu->xof = cu->xof - cent[0]; - cu->yof = cu->yof - cent[1]; + cu->xof = cu->xof - cent[0]; + cu->yof = cu->yof - cent[1]; - tot_change++; - cu->id.tag |= LIB_TAG_DOIT; - do_inverse_offset = true; - } + tot_change++; + cu->id.tag |= LIB_TAG_DOIT; + do_inverse_offset = true; } - else if (ob->type == OB_ARMATURE) { - bArmature *arm = ob->data; + } + else if (ob->type == OB_ARMATURE) { + bArmature *arm = ob->data; - if (ID_REAL_USERS(arm) > 1) { + if (ID_REAL_USERS(arm) > 1) { #if 0 BKE_report(op->reports, RPT_ERROR, "Cannot apply to a multi user armature"); return; #endif - tot_multiuser_arm_error++; - } - else { - /* Function to recenter armatures in editarmature.c - * Bone + object locations are handled there. - */ - ED_armature_origin_set(bmain, ob, cursor, centermode, around); + tot_multiuser_arm_error++; + } + else { + /* Function to recenter armatures in editarmature.c + * Bone + object locations are handled there. + */ + ED_armature_origin_set(bmain, ob, cursor, centermode, around); - tot_change++; - arm->id.tag |= LIB_TAG_DOIT; - /* do_inverse_offset = true; */ /* docenter_armature() handles this */ + tot_change++; + arm->id.tag |= LIB_TAG_DOIT; + /* do_inverse_offset = true; */ /* docenter_armature() handles this */ - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - BKE_object_transform_copy(ob_eval, ob); - BKE_armature_copy_bone_transforms(ob_eval->data, ob->data); - BKE_object_where_is_calc(depsgraph, scene, ob_eval); - BKE_pose_where_is(depsgraph, scene, ob_eval); /* needed for bone parents */ + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + BKE_object_transform_copy(ob_eval, ob); + BKE_armature_copy_bone_transforms(ob_eval->data, ob->data); + BKE_object_where_is_calc(depsgraph, scene, ob_eval); + BKE_pose_where_is(depsgraph, scene, ob_eval); /* needed for bone parents */ - ignore_parent_tx(bmain, depsgraph, scene, ob); + ignore_parent_tx(bmain, depsgraph, scene, ob); - if (obedit) { - break; - } + if (obedit) { + break; } } - else if (ob->type == OB_MBALL) { - MetaBall *mb = ob->data; + } + else if (ob->type == OB_MBALL) { + MetaBall *mb = ob->data; - if (centermode == ORIGIN_TO_CURSOR) { - /* done */ - } - else if (around == V3D_AROUND_CENTER_BOUNDS) { - BKE_mball_center_bounds(mb, cent); - } - else { /* #V3D_AROUND_CENTER_MEDIAN. */ - BKE_mball_center_median(mb, cent); - } + if (centermode == ORIGIN_TO_CURSOR) { + /* done */ + } + else if (around == V3D_AROUND_CENTER_BOUNDS) { + BKE_mball_center_bounds(mb, cent); + } + else { /* #V3D_AROUND_CENTER_MEDIAN. */ + BKE_mball_center_median(mb, cent); + } - negate_v3_v3(cent_neg, cent); - BKE_mball_translate(mb, cent_neg); + negate_v3_v3(cent_neg, cent); + BKE_mball_translate(mb, cent_neg); - tot_change++; - mb->id.tag |= LIB_TAG_DOIT; - do_inverse_offset = true; + tot_change++; + mb->id.tag |= LIB_TAG_DOIT; + do_inverse_offset = true; - if (obedit) { - if (centermode == GEOMETRY_TO_ORIGIN) { - DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); - } - break; + if (obedit) { + if (centermode == GEOMETRY_TO_ORIGIN) { + DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY); } + break; } - else if (ob->type == OB_LATTICE) { - Lattice *lt = ob->data; + } + else if (ob->type == OB_LATTICE) { + Lattice *lt = ob->data; - if (centermode == ORIGIN_TO_CURSOR) { - /* done */ - } - else if (around == V3D_AROUND_CENTER_BOUNDS) { - BKE_lattice_center_bounds(lt, cent); - } - else { /* #V3D_AROUND_CENTER_MEDIAN. */ - BKE_lattice_center_median(lt, cent); - } + if (centermode == ORIGIN_TO_CURSOR) { + /* done */ + } + else if (around == V3D_AROUND_CENTER_BOUNDS) { + BKE_lattice_center_bounds(lt, cent); + } + else { /* #V3D_AROUND_CENTER_MEDIAN. */ + BKE_lattice_center_median(lt, cent); + } - negate_v3_v3(cent_neg, cent); - BKE_lattice_translate(lt, cent_neg, 1); + negate_v3_v3(cent_neg, cent); + BKE_lattice_translate(lt, cent_neg, 1); - tot_change++; - lt->id.tag |= LIB_TAG_DOIT; - do_inverse_offset = true; - } - else if (ob->type == OB_GPENCIL) { - bGPdata *gpd = ob->data; - float gpcenter[3]; - if (gpd) { - if (centermode == ORIGIN_TO_GEOMETRY) { - zero_v3(gpcenter); - BKE_gpencil_centroid_3d(gpd, gpcenter); - add_v3_v3(gpcenter, ob->obmat[3]); - } - if (centermode == ORIGIN_TO_CURSOR) { - copy_v3_v3(gpcenter, cursor); - } - if (ELEM(centermode, ORIGIN_TO_GEOMETRY, ORIGIN_TO_CURSOR)) { - bGPDspoint *pt; - float imat[3][3], bmat[3][3]; - float offset_global[3]; - float offset_local[3]; - int i; - - sub_v3_v3v3(offset_global, gpcenter, ob->obmat[3]); - copy_m3_m4(bmat, obact->obmat); - invert_m3_m3(imat, bmat); - mul_m3_v3(imat, offset_global); - mul_v3_m3v3(offset_local, imat, offset_global); - - float diff_mat[4][4]; - float inverse_diff_mat[4][4]; - - /* recalculate all strokes - * (all layers are considered without evaluating lock attributes) */ - LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { - /* calculate difference matrix */ - BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); - /* undo matrix */ - invert_m4_m4(inverse_diff_mat, diff_mat); - LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - float mpt[3]; - mul_v3_m4v3(mpt, inverse_diff_mat, &pt->x); - sub_v3_v3(mpt, offset_local); - mul_v3_m4v3(&pt->x, diff_mat, mpt); - } + tot_change++; + lt->id.tag |= LIB_TAG_DOIT; + do_inverse_offset = true; + } + else if (ob->type == OB_GPENCIL) { + bGPdata *gpd = ob->data; + float gpcenter[3]; + if (gpd) { + if (centermode == ORIGIN_TO_GEOMETRY) { + zero_v3(gpcenter); + BKE_gpencil_centroid_3d(gpd, gpcenter); + add_v3_v3(gpcenter, ob->obmat[3]); + } + if (centermode == ORIGIN_TO_CURSOR) { + copy_v3_v3(gpcenter, cursor); + } + if (ELEM(centermode, ORIGIN_TO_GEOMETRY, ORIGIN_TO_CURSOR)) { + bGPDspoint *pt; + float imat[3][3], bmat[3][3]; + float offset_global[3]; + float offset_local[3]; + int i; + + sub_v3_v3v3(offset_global, gpcenter, ob->obmat[3]); + copy_m3_m4(bmat, obact->obmat); + invert_m3_m3(imat, bmat); + mul_m3_v3(imat, offset_global); + mul_v3_m3v3(offset_local, imat, offset_global); + + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + + /* recalculate all strokes + * (all layers are considered without evaluating lock attributes) */ + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + /* calculate difference matrix */ + BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat); + /* undo matrix */ + invert_m4_m4(inverse_diff_mat, diff_mat); + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + float mpt[3]; + mul_v3_m4v3(mpt, inverse_diff_mat, &pt->x); + sub_v3_v3(mpt, offset_local); + mul_v3_m4v3(&pt->x, diff_mat, mpt); } } } - tot_change++; - if (centermode == ORIGIN_TO_GEOMETRY) { - copy_v3_v3(ob->loc, gpcenter); - } - DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); - - ob->id.tag |= LIB_TAG_DOIT; - do_inverse_offset = true; } - else { - BKE_report(op->reports, - RPT_WARNING, - "Grease Pencil Object does not support this set origin option"); + tot_change++; + if (centermode == ORIGIN_TO_GEOMETRY) { + copy_v3_v3(ob->loc, gpcenter); } + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + + ob->id.tag |= LIB_TAG_DOIT; + do_inverse_offset = true; + } + else { + BKE_report(op->reports, + RPT_WARNING, + "Grease Pencil Object does not support this set origin option"); } } + } - /* offset other selected objects */ - if (do_inverse_offset && (centermode != GEOMETRY_TO_ORIGIN)) { - float obmat[4][4]; + /* offset other selected objects */ + if (do_inverse_offset && (centermode != GEOMETRY_TO_ORIGIN)) { + float obmat[4][4]; - /* was the object data modified - * NOTE: the functions above must set 'cent'. */ + /* was the object data modified + * NOTE: the functions above must set 'cent'. */ - /* convert the offset to parent space */ - BKE_object_to_mat4(ob, obmat); - mul_v3_mat3_m4v3(centn, obmat, cent); /* omit translation part */ + /* convert the offset to parent space */ + BKE_object_to_mat4(ob, obmat); + mul_v3_mat3_m4v3(centn, obmat, cent); /* omit translation part */ - add_v3_v3(ob->loc, centn); + add_v3_v3(ob->loc, centn); - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - BKE_object_transform_copy(ob_eval, ob); - BKE_object_where_is_calc(depsgraph, scene, ob_eval); - if (ob->type == OB_ARMATURE) { - /* needed for bone parents */ - BKE_armature_copy_bone_transforms(ob_eval->data, ob->data); - BKE_pose_where_is(depsgraph, scene, ob_eval); - } - - ignore_parent_tx(bmain, depsgraph, scene, ob); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + BKE_object_transform_copy(ob_eval, ob); + BKE_object_where_is_calc(depsgraph, scene, ob_eval); + if (ob->type == OB_ARMATURE) { + /* needed for bone parents */ + BKE_armature_copy_bone_transforms(ob_eval->data, ob->data); + BKE_pose_where_is(depsgraph, scene, ob_eval); + } - /* other users? */ - // CTX_DATA_BEGIN (C, Object *, ob_other, selected_editable_objects) - //{ - - /* use existing context looper */ - for (int other_object_index = 0; other_object_index < num_objects; other_object_index++) { - Object *ob_other = objects[other_object_index]; - - if ((ob_other->flag & OB_DONE) == 0 && - ((ob->data && (ob->data == ob_other->data)) || - (ob->instance_collection == ob_other->instance_collection && - (ob->transflag | ob_other->transflag) & OB_DUPLICOLLECTION))) { - ob_other->flag |= OB_DONE; - DEG_id_tag_update(&ob_other->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - - mul_v3_mat3_m4v3(centn, ob_other->obmat, cent); /* omit translation part */ - add_v3_v3(ob_other->loc, centn); - - Object *ob_other_eval = DEG_get_evaluated_object(depsgraph, ob_other); - BKE_object_transform_copy(ob_other_eval, ob_other); - BKE_object_where_is_calc(depsgraph, scene, ob_other_eval); - if (ob_other->type == OB_ARMATURE) { - /* needed for bone parents */ - BKE_armature_copy_bone_transforms(ob_eval->data, ob->data); - BKE_pose_where_is(depsgraph, scene, ob_other_eval); - } - ignore_parent_tx(bmain, depsgraph, scene, ob_other); + ignore_parent_tx(bmain, depsgraph, scene, ob); + + /* other users? */ + // CTX_DATA_BEGIN (C, Object *, ob_other, selected_editable_objects) + //{ + + /* use existing context looper */ + for (int other_object_index = 0; other_object_index < num_objects; other_object_index++) { + Object *ob_other = objects[other_object_index]; + + if ((ob_other->flag & OB_DONE) == 0 && + ((ob->data && (ob->data == ob_other->data)) || + (ob->instance_collection == ob_other->instance_collection && + (ob->transflag | ob_other->transflag) & OB_DUPLICOLLECTION))) { + ob_other->flag |= OB_DONE; + DEG_id_tag_update(&ob_other->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + + mul_v3_mat3_m4v3(centn, ob_other->obmat, cent); /* omit translation part */ + add_v3_v3(ob_other->loc, centn); + + Object *ob_other_eval = DEG_get_evaluated_object(depsgraph, ob_other); + BKE_object_transform_copy(ob_other_eval, ob_other); + BKE_object_where_is_calc(depsgraph, scene, ob_other_eval); + if (ob_other->type == OB_ARMATURE) { + /* needed for bone parents */ + BKE_armature_copy_bone_transforms(ob_eval->data, ob->data); + BKE_pose_where_is(depsgraph, scene, ob_other_eval); } + ignore_parent_tx(bmain, depsgraph, scene, ob_other); } - // CTX_DATA_END; } + // CTX_DATA_END; } } MEM_freeN(objects); diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 37075a29b92..f64f95c5322 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -130,7 +130,7 @@ bool ED_vgroup_sync_from_pose(Object *ob) if (arm->act_bone) { int def_num = BKE_object_defgroup_name_index(ob, arm->act_bone->name); if (def_num != -1) { - ob->actdef = def_num + 1; + BKE_object_defgroup_active_index_set(ob, def_num + 1); return true; } } @@ -389,11 +389,16 @@ bool ED_vgroup_array_copy(Object *ob, Object *ob_from) int dvert_tot_from; int dvert_tot; int i; - int defbase_tot_from = BLI_listbase_count(&ob_from->defbase); - int defbase_tot = BLI_listbase_count(&ob->defbase); + ListBase *defbase_dst = BKE_object_defgroup_list_mutable(ob); + const ListBase *defbase_src = BKE_object_defgroup_list(ob_from); + + int defbase_tot_from = BLI_listbase_count(defbase_src); + int defbase_tot = BLI_listbase_count(defbase_dst); bool new_vgroup = false; - if (ob == ob_from) { + BLI_assert(ob != ob_from); + + if (ob->data == ob_from->data) { return true; } @@ -429,9 +434,9 @@ bool ED_vgroup_array_copy(Object *ob, Object *ob_from) } /* do the copy */ - BLI_freelistN(&ob->defbase); - BLI_duplicatelist(&ob->defbase, &ob_from->defbase); - ob->actdef = ob_from->actdef; + BLI_freelistN(defbase_dst); + BLI_duplicatelist(defbase_dst, defbase_src); + BKE_object_defgroup_active_index_set(ob, BKE_object_defgroup_active_index_get(ob_from)); if (defbase_tot_from < defbase_tot) { /* correct vgroup indices because the number of vgroups is being reduced. */ @@ -882,7 +887,8 @@ void ED_vgroup_vert_add(Object *ob, bDeformGroup *dg, int vertnum, float weight, /* add the vert to the deform group with the * specified assign mode */ - const int def_nr = BLI_findindex(&ob->defbase, dg); + const ListBase *defbase = BKE_object_defgroup_list(ob); + const int def_nr = BLI_findindex(defbase, dg); MDeformVert *dv = NULL; int tot; @@ -913,7 +919,8 @@ void ED_vgroup_vert_remove(Object *ob, bDeformGroup *dg, int vertnum) /* TODO(campbell): This is slow in a loop, better pass def_nr directly, * but leave for later. */ - const int def_nr = BLI_findindex(&ob->defbase, dg); + const ListBase *defbase = BKE_object_defgroup_list(ob); + const int def_nr = BLI_findindex(defbase, dg); if (def_nr != -1) { MDeformVert *dvert = NULL; @@ -989,7 +996,8 @@ static float get_vert_def_nr(Object *ob, const int def_nr, const int vertnum) float ED_vgroup_vert_weight(Object *ob, bDeformGroup *dg, int vertnum) { - const int def_nr = BLI_findindex(&ob->defbase, dg); + const ListBase *defbase = BKE_object_defgroup_list(ob); + const int def_nr = BLI_findindex(defbase, dg); if (def_nr == -1) { return -1; @@ -1000,9 +1008,9 @@ float ED_vgroup_vert_weight(Object *ob, bDeformGroup *dg, int vertnum) void ED_vgroup_select_by_name(Object *ob, const char *name) { - /* NOTE: ob->actdef==0 signals on painting to create a new one, + /* NOTE: actdef==0 signals on painting to create a new one, * if a bone in posemode is selected */ - ob->actdef = BKE_object_defgroup_name_index(ob, name) + 1; + BKE_object_defgroup_active_index_set(ob, BKE_object_defgroup_name_index(ob, name) + 1); } /** \} */ @@ -1014,9 +1022,10 @@ void ED_vgroup_select_by_name(Object *ob, const char *name) /* only in editmode */ static void vgroup_select_verts(Object *ob, int select) { - const int def_nr = ob->actdef - 1; + const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1; - if (!BLI_findlink(&ob->defbase, def_nr)) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + if (!BLI_findlink(defbase, def_nr)) { return; } @@ -1111,7 +1120,9 @@ static void vgroup_duplicate(Object *ob) MDeformVert **dvert_array = NULL; int i, idg, icdg, dvert_tot = 0; - dg = BLI_findlink(&ob->defbase, (ob->actdef - 1)); + ListBase *defbase = BKE_object_defgroup_list_mutable(ob); + + dg = BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1); if (!dg) { return; } @@ -1127,11 +1138,11 @@ static void vgroup_duplicate(Object *ob) BLI_strncpy(cdg->name, name, sizeof(cdg->name)); BKE_object_defgroup_unique_name(cdg, ob); - BLI_addtail(&ob->defbase, cdg); + BLI_addtail(defbase, cdg); - idg = (ob->actdef - 1); - ob->actdef = BLI_listbase_count(&ob->defbase); - icdg = (ob->actdef - 1); + idg = BKE_object_defgroup_active_index_get(ob) - 1; + BKE_object_defgroup_active_index_set(ob, BLI_listbase_count(defbase)); + icdg = BKE_object_defgroup_active_index_get(ob) - 1; /* TODO(campbell): we might want to allow only copy selected verts here? */ ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, false); @@ -1157,11 +1168,12 @@ static bool vgroup_normalize(Object *ob) MDeformWeight *dw; MDeformVert *dv, **dvert_array = NULL; int dvert_tot = 0; - const int def_nr = ob->actdef - 1; + const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1; const bool use_vert_sel = vertex_group_use_vert_sel(ob); - if (!BLI_findlink(&ob->defbase, def_nr)) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + if (!BLI_findlink(defbase, def_nr)) { return false; } @@ -1639,7 +1651,7 @@ static bool vgroup_normalize_all(Object *ob, { MDeformVert *dv, **dvert_array = NULL; int i, dvert_tot = 0; - const int def_nr = ob->actdef - 1; + const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1; const bool use_vert_sel = vertex_group_use_vert_sel(ob); @@ -1651,7 +1663,8 @@ static bool vgroup_normalize_all(Object *ob, ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, use_vert_sel); if (dvert_array) { - const int defbase_tot = BLI_listbase_count(&ob->defbase); + const ListBase *defbase = BKE_object_defgroup_list(ob); + const int defbase_tot = BLI_listbase_count(defbase); bool *lock_flags = BKE_object_defgroup_lock_flags_get(ob, defbase_tot); bool changed = false; @@ -1742,7 +1755,7 @@ static const EnumPropertyItem vgroup_lock_mask[] = { static bool *vgroup_selected_get(Object *ob) { - int sel_count = 0, defbase_tot = BLI_listbase_count(&ob->defbase); + int sel_count = 0, defbase_tot = BKE_object_defgroup_count(ob); bool *mask; if (ob->mode & OB_MODE_WEIGHT_PAINT) { @@ -1759,8 +1772,9 @@ static bool *vgroup_selected_get(Object *ob) mask = MEM_callocN(defbase_tot * sizeof(bool), __func__); } - if (sel_count == 0 && ob->actdef >= 1 && ob->actdef <= defbase_tot) { - mask[ob->actdef - 1] = true; + const int actdef = BKE_object_defgroup_active_index_get(ob); + if (sel_count == 0 && actdef >= 1 && actdef <= defbase_tot) { + mask[actdef - 1] = true; } return mask; @@ -1775,11 +1789,12 @@ static void vgroup_lock_all(Object *ob, int action, int mask) if (mask != VGROUP_MASK_ALL) { selected = vgroup_selected_get(ob); } + const ListBase *defbase = BKE_object_defgroup_list(ob); if (action == VGROUP_TOGGLE) { action = VGROUP_LOCK; - for (dg = ob->defbase.first, i = 0; dg; dg = dg->next, i++) { + for (dg = defbase->first, i = 0; dg; dg = dg->next, i++) { switch (mask) { case VGROUP_MASK_INVERT_UNSELECTED: case VGROUP_MASK_SELECTED: @@ -1802,7 +1817,7 @@ static void vgroup_lock_all(Object *ob, int action, int mask) } } - for (dg = ob->defbase.first, i = 0; dg; dg = dg->next, i++) { + for (dg = defbase->first, i = 0; dg; dg = dg->next, i++) { switch (mask) { case VGROUP_MASK_SELECTED: if (!selected[i]) { @@ -2379,13 +2394,15 @@ void ED_vgroup_mirror(Object *ob, MDeformVert *dvert, *dvert_mirr; char sel, sel_mirr; int *flip_map = NULL, flip_map_len; - const int def_nr = ob->actdef - 1; + const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1; int totmirr = 0, totfail = 0; *r_totmirr = *r_totfail = 0; + const ListBase *defbase = BKE_object_defgroup_list(ob); + if ((mirror_weights == false && flip_vgroups == false) || - (BLI_findlink(&ob->defbase, def_nr) == NULL)) { + (BLI_findlink(defbase, def_nr) == NULL)) { return; } @@ -2569,7 +2586,8 @@ cleanup: static void vgroup_delete_active(Object *ob) { - bDeformGroup *dg = BLI_findlink(&ob->defbase, ob->actdef - 1); + const ListBase *defbase = BKE_object_defgroup_list(ob); + bDeformGroup *dg = BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1); if (!dg) { return; } @@ -2580,9 +2598,10 @@ static void vgroup_delete_active(Object *ob) /* only in editmode */ static void vgroup_assign_verts(Object *ob, const float weight) { - const int def_nr = ob->actdef - 1; + const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1; - if (!BLI_findlink(&ob->defbase, def_nr)) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + if (!BLI_findlink(defbase, def_nr)) { return; } @@ -2700,7 +2719,8 @@ static bool vertex_group_poll_ex(bContext *C, Object *ob) return false; } - if (BLI_listbase_is_empty(&ob->defbase)) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + if (BLI_listbase_is_empty(defbase)) { CTX_wm_operator_poll_msg_set(C, "Object has no vertex groups"); return false; } @@ -2823,8 +2843,10 @@ static bool vertex_group_vert_select_unlocked_poll(bContext *C) return false; } - if (ob->actdef != 0) { - bDeformGroup *dg = BLI_findlink(&ob->defbase, ob->actdef - 1); + const int def_nr = BKE_object_defgroup_active_index_get(ob); + if (def_nr != 0) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + const bDeformGroup *dg = BLI_findlink(defbase, def_nr - 1); if (dg) { return !(dg->flag & DG_LOCK_WEIGHT); } @@ -3025,8 +3047,8 @@ static int vertex_group_remove_from_exec(bContext *C, wmOperator *op) } } else { - bDeformGroup *dg = BLI_findlink(&ob->defbase, ob->actdef - 1); - + const ListBase *defbase = BKE_object_defgroup_list(ob); + bDeformGroup *dg = BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1); if ((dg == NULL) || (BKE_object_defgroup_clear(ob, dg, !use_all_verts) == false)) { return OPERATOR_CANCELLED; } @@ -3860,55 +3882,6 @@ void OBJECT_OT_vertex_group_mirror(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Vertex Group Copy to Linked Operator - * \{ */ - -static int vertex_group_copy_to_linked_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Scene *scene = CTX_data_scene(C); - Object *ob_active = ED_object_context(C); - int retval = OPERATOR_CANCELLED; - - FOREACH_SCENE_OBJECT_BEGIN (scene, ob_iter) { - if (ob_iter->type == ob_active->type) { - if (ob_iter != ob_active && ob_iter->data == ob_active->data) { - BLI_freelistN(&ob_iter->defbase); - BLI_duplicatelist(&ob_iter->defbase, &ob_active->defbase); - ob_iter->actdef = ob_active->actdef; - - DEG_id_tag_update(&ob_iter->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob_iter); - WM_event_add_notifier(C, NC_GEOM | ND_VERTEX_GROUP, ob_iter->data); - - retval = OPERATOR_FINISHED; - } - } - } - FOREACH_SCENE_OBJECT_END; - - return retval; -} - -void OBJECT_OT_vertex_group_copy_to_linked(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Copy Vertex Groups to Linked"; - ot->idname = "OBJECT_OT_vertex_group_copy_to_linked"; - ot->description = - "Replace vertex groups of all users of the same geometry data by vertex groups of active " - "object"; - - /* api callbacks */ - ot->poll = vertex_group_poll; - ot->exec = vertex_group_copy_to_linked_exec; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Vertex Group Copy to Selected Operator * \{ */ @@ -3919,7 +3892,7 @@ static int vertex_group_copy_to_selected_exec(bContext *C, wmOperator *op) int fail = 0; CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { - if (obact != ob) { + if (obact != ob && BKE_object_supports_vertex_groups(ob)) { if (ED_vgroup_array_copy(ob, obact)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); DEG_relations_tag_update(CTX_data_main(C)); @@ -3936,8 +3909,8 @@ static int vertex_group_copy_to_selected_exec(bContext *C, wmOperator *op) if ((changed_tot == 0 && fail == 0) || fail) { BKE_reportf(op->reports, RPT_ERROR, - "Copy vertex groups to selected: %d done, %d failed (object data must have " - "matching indices)", + "Copy vertex groups to selected: %d done, %d failed (object data must support " + "vertex groups and have matching indices)", changed_tot, fail); } @@ -3972,7 +3945,7 @@ static int set_active_group_exec(bContext *C, wmOperator *op) int nr = RNA_enum_get(op->ptr, "group"); BLI_assert(nr + 1 >= 0); - ob->actdef = nr + 1; + BKE_object_defgroup_active_index_set(ob, nr + 1); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GEOM | ND_VERTEX_GROUP, ob); @@ -3999,7 +3972,8 @@ static const EnumPropertyItem *vgroup_itemf(bContext *C, return DummyRNA_NULL_items; } - for (a = 0, def = ob->defbase.first; def; def = def->next, a++) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + for (a = 0, def = defbase->first; def; def = def->next, a++) { tmp.value = a; tmp.icon = ICON_GROUP_VERTEX; tmp.identifier = def->name; @@ -4048,13 +4022,13 @@ void OBJECT_OT_vertex_group_set_active(wmOperatorType *ot) * with the order of vgroups then call vgroup_do_remap after */ static char *vgroup_init_remap(Object *ob) { - bDeformGroup *def; - int defbase_tot = BLI_listbase_count(&ob->defbase); + const ListBase *defbase = BKE_object_defgroup_list(ob); + int defbase_tot = BLI_listbase_count(defbase); char *name_array = MEM_mallocN(MAX_VGROUP_NAME * sizeof(char) * defbase_tot, "sort vgroups"); char *name; name = name_array; - for (def = ob->defbase.first; def; def = def->next) { + for (const bDeformGroup *def = defbase->first; def; def = def->next) { BLI_strncpy(name, def->name, MAX_VGROUP_NAME); name += MAX_VGROUP_NAME; } @@ -4065,8 +4039,9 @@ static char *vgroup_init_remap(Object *ob) static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op) { MDeformVert *dvert = NULL; - bDeformGroup *def; - int defbase_tot = BLI_listbase_count(&ob->defbase); + const bDeformGroup *def; + const ListBase *defbase = BKE_object_defgroup_list(ob); + int defbase_tot = BLI_listbase_count(defbase); /* Needs a dummy index at the start. */ int *sort_map_update = MEM_mallocN(sizeof(int) * (defbase_tot + 1), "sort vgroups"); @@ -4076,8 +4051,8 @@ static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op) int i; name = name_array; - for (def = ob->defbase.first, i = 0; def; def = def->next, i++) { - sort_map[i] = BLI_findstringindex(&ob->defbase, name, offsetof(bDeformGroup, name)); + for (def = defbase->first, i = 0; def; def = def->next, i++) { + sort_map[i] = BLI_findstringindex(defbase, name, offsetof(bDeformGroup, name)); name += MAX_VGROUP_NAME; BLI_assert(sort_map[i] != -1); @@ -4130,8 +4105,9 @@ static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op) sort_map_update[0] = 0; BKE_object_defgroup_remap_update_users(ob, sort_map_update); - BLI_assert(sort_map_update[ob->actdef] >= 0); - ob->actdef = sort_map_update[ob->actdef]; + BLI_assert(sort_map_update[BKE_object_defgroup_active_index_get(ob)] >= 0); + BKE_object_defgroup_active_index_set(ob, + sort_map_update[BKE_object_defgroup_active_index_get(ob)]); MEM_freeN(sort_map_update); @@ -4159,6 +4135,7 @@ static void vgroup_sort_bone_hierarchy(Object *ob, ListBase *bonebase) bonebase = &armature->bonebase; } } + ListBase *defbase = BKE_object_defgroup_list_mutable(ob); if (bonebase != NULL) { Bone *bone; @@ -4167,8 +4144,8 @@ static void vgroup_sort_bone_hierarchy(Object *ob, ListBase *bonebase) vgroup_sort_bone_hierarchy(ob, &bone->childbase); if (dg != NULL) { - BLI_remlink(&ob->defbase, dg); - BLI_addhead(&ob->defbase, dg); + BLI_remlink(defbase, dg); + BLI_addhead(defbase, dg); } } } @@ -4189,10 +4166,12 @@ static int vertex_group_sort_exec(bContext *C, wmOperator *op) /* Init remapping. */ name_array = vgroup_init_remap(ob); + ListBase *defbase = BKE_object_defgroup_list_mutable(ob); + /* Sort vgroup names. */ switch (sort_type) { case SORT_TYPE_NAME: - BLI_listbase_sort(&ob->defbase, vgroup_sort_name); + BLI_listbase_sort(defbase, vgroup_sort_name); break; case SORT_TYPE_BONEHIERARCHY: vgroup_sort_bone_hierarchy(ob, NULL); @@ -4250,14 +4229,16 @@ static int vgroup_move_exec(bContext *C, wmOperator *op) int dir = RNA_enum_get(op->ptr, "direction"); int ret = OPERATOR_FINISHED; - def = BLI_findlink(&ob->defbase, ob->actdef - 1); + ListBase *defbase = BKE_object_defgroup_list_mutable(ob); + + def = BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1); if (!def) { return OPERATOR_CANCELLED; } name_array = vgroup_init_remap(ob); - if (BLI_listbase_link_move(&ob->defbase, def, dir)) { + if (BLI_listbase_link_move(defbase, def, dir)) { ret = vgroup_do_remap(ob, name_array, op); if (ret != OPERATOR_CANCELLED) { @@ -4376,7 +4357,8 @@ static void vgroup_copy_active_to_sel_single(Object *ob, const int def_nr) static bool check_vertex_group_accessible(wmOperator *op, Object *ob, int def_nr) { - bDeformGroup *dg = BLI_findlink(&ob->defbase, def_nr); + const ListBase *defbase = BKE_object_defgroup_list(ob); + bDeformGroup *dg = BLI_findlink(defbase, def_nr); if (!dg) { BKE_report(op->reports, RPT_ERROR, "Invalid vertex group index"); @@ -4498,7 +4480,7 @@ static int vertex_weight_set_active_exec(bContext *C, wmOperator *op) const int wg_index = RNA_int_get(op->ptr, "weight_group"); if (wg_index != -1) { - ob->actdef = wg_index + 1; + BKE_object_defgroup_active_index_set(ob, wg_index + 1); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); } diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index e8f69aded5b..c7fa2a0ec87 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -54,7 +54,9 @@ #include "DNA_space_types.h" #include "DNA_world_types.h" +#include "BKE_animsys.h" #include "BKE_appdir.h" +#include "BKE_armature.h" #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_context.h" @@ -93,6 +95,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "ED_armature.h" #include "ED_datafiles.h" #include "ED_render.h" #include "ED_screen.h" @@ -151,6 +154,10 @@ typedef struct IconPreview { void *owner; ID *id, *id_copy; /* May be NULL! (see ICON_TYPE_PREVIEW case in #ui_icon_ensure_deferred()) */ ListBase sizes; + + /* May be NULL, is used for rendering IDs that require some other object for it to be applied on + * before the ID can be represented as an image, for example when rendering an Action. */ + struct Object *active_object; } IconPreview; /** \} */ @@ -253,7 +260,7 @@ static const char *preview_collection_name(const char pr_type) case MA_ATMOS: return "Atmosphere"; default: - BLI_assert(!"Unknown preview type"); + BLI_assert_msg(0, "Unknown preview type"); return ""; } } @@ -335,7 +342,7 @@ static ID *duplicate_ids(ID *id, const bool allow_failure) return NULL; default: if (!allow_failure) { - BLI_assert(!"ID type preview not supported."); + BLI_assert_msg(0, "ID type preview not supported."); } return NULL; } @@ -813,9 +820,50 @@ static void object_preview_render(IconPreview *preview, IconPreviewSize *preview /** \name Action Preview * \{ */ -/* Render a pose. It is assumed that the pose has already been applied and that the scene camera is - * capturing the pose. In other words, this function just renders from the scene camera without - * evaluating the Action stored in preview->id. */ +static struct PoseBackup *action_preview_render_prepare(IconPreview *preview) +{ + Object *object = preview->active_object; + if (object == NULL) { + WM_report(RPT_WARNING, "No active object, unable to apply the Action before rendering"); + return NULL; + } + if (object->pose == NULL) { + WM_reportf(RPT_WARNING, + "Object %s has no pose, unable to apply the Action before rendering", + object->id.name + 2); + return NULL; + } + + /* Create a backup of the current pose. */ + struct bAction *action = (struct bAction *)preview->id; + struct PoseBackup *pose_backup = ED_pose_backup_create_all_bones(object, action); + + /* Apply the Action as pose, so that it can be rendered. This assumes the Action represents a + * single pose, and that thus the evaluation time doesn't matter. */ + AnimationEvalContext anim_eval_context = {preview->depsgraph, 0.0f}; + BKE_pose_apply_action_all_bones(object, action, &anim_eval_context); + + /* Force evaluation of the new pose, before the preview is rendered. */ + DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY); + DEG_evaluate_on_refresh(preview->depsgraph); + + return pose_backup; +} + +static void action_preview_render_cleanup(IconPreview *preview, struct PoseBackup *pose_backup) +{ + if (pose_backup == NULL) { + return; + } + ED_pose_backup_restore(pose_backup); + ED_pose_backup_free(pose_backup); + + DEG_id_tag_update(&preview->active_object->id, ID_RECALC_GEOMETRY); +} + +/* Render a pose from the scene camera. It is assumed that the scene camera is + * capturing the pose. The pose is applied temporarily to the current object + * before rendering. */ static void action_preview_render(IconPreview *preview, IconPreviewSize *preview_sized) { char err_out[256] = ""; @@ -827,6 +875,9 @@ static void action_preview_render(IconPreview *preview, IconPreviewSize *preview BLI_assert(depsgraph != NULL); BLI_assert(preview->scene == DEG_get_input_scene(depsgraph)); + /* Apply the pose before getting the evaluated scene, so that the new pose is evaluated. */ + struct PoseBackup *pose_backup = action_preview_render_prepare(preview); + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); Object *camera_eval = scene_eval->camera; if (camera_eval == NULL) { @@ -850,6 +901,8 @@ static void action_preview_render(IconPreview *preview, IconPreviewSize *preview NULL, err_out); + action_preview_render_cleanup(preview, pose_backup); + if (err_out[0] != '\0') { printf("Error rendering Action %s preview: %s\n", preview->id->name + 2, err_out); } @@ -1469,7 +1522,7 @@ static int icon_previewimg_size_index_get(const IconPreviewSize *icon_size, } } - BLI_assert(!"The searched icon size does not match any in the preview image"); + BLI_assert_msg(0, "The searched icon size does not match any in the preview image"); return -1; } @@ -1625,6 +1678,7 @@ void ED_preview_icon_render( /* Control isn't given back to the caller until the preview is done. So we don't need to copy * the ID to avoid thread races. */ ip.id_copy = duplicate_ids(id, true); + ip.active_object = CTX_data_active_object(C); icon_preview_add_size(&ip, rect, sizex, sizey); @@ -1666,6 +1720,7 @@ void ED_preview_icon_job( ip->bmain = CTX_data_main(C); ip->depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ip->scene = DEG_get_input_scene(ip->depsgraph); + ip->active_object = CTX_data_active_object(C); ip->owner = owner; ip->id = id; ip->id_copy = duplicate_ids(id, false); diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 9595a66173b..d2b1ebdad78 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -2418,7 +2418,7 @@ static void paste_mtex_copybuf(ID *id) mtex = &(((FreestyleLineStyle *)id)->mtex[(int)((FreestyleLineStyle *)id)->texact]); break; default: - BLI_assert(!"invalid id type"); + BLI_assert_msg(0, "invalid id type"); return; } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 39de63c22a9..c351ade9954 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -161,6 +161,12 @@ void ED_region_do_listen(wmRegionListenerParams *params) if (region->type && region->type->listener) { region->type->listener(params); } + + LISTBASE_FOREACH (uiList *, list, ®ion->ui_lists) { + if (list->type && list->type->listener) { + list->type->listener(list, params); + } + } } /* only exported for WM */ @@ -3018,6 +3024,8 @@ void ED_region_panels_layout_ex(const bContext *C, y = -y; } + UI_blocklist_update_view_for_buttons(C, ®ion->uiblocks); + if (update_tot_size) { /* this also changes the 'cur' */ UI_view2d_totRect_set(v2d, x, y); @@ -3672,7 +3680,7 @@ static void region_visible_rect_calc(ARegion *region, rcti *rect) /* Skip floating. */ } else { - BLI_assert(!"Region overlap with unknown alignment"); + BLI_assert_msg(0, "Region overlap with unknown alignment"); } } } diff --git a/source/blender/editors/screen/area_query.c b/source/blender/editors/screen/area_query.c index d569e56e11b..fd4f3964398 100644 --- a/source/blender/editors/screen/area_query.c +++ b/source/blender/editors/screen/area_query.c @@ -88,7 +88,7 @@ bool ED_region_panel_category_gutter_calc_rect(const ARegion *region, rcti *r_re r_region_gutter->xmin = r_region_gutter->xmax - category_tabs_width; } else { - BLI_assert(!"Unsupported alignment"); + BLI_assert_msg(0, "Unsupported alignment"); } return true; } diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index e366760a55d..f651fd4fb61 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -126,7 +126,7 @@ void immDrawPixelsTexScaled_clipping(IMMDrawPixelsTexState *state, components = 1; } else { - BLI_assert(!"Incompatible format passed to immDrawPixels"); + BLI_assert_msg(0, "Incompatible format passed to immDrawPixels"); return; } @@ -426,7 +426,7 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf, format = GPU_RGBA16F; } else { - BLI_assert(!"Incompatible number of channels for GLSL display"); + BLI_assert_msg(0, "Incompatible number of channels for GLSL display"); } if (format != 0) { diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index d50962a56a9..3ce2f326dca 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -36,6 +36,7 @@ #include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" #include "BLI_ghash.h" #include "BLI_listbase.h" @@ -111,6 +112,8 @@ const char *screen_context_dir[] = { "selected_editable_fcurves", "active_editable_fcurve", "selected_editable_keyframes", + "ui_list", + "asset_library", NULL, }; @@ -1024,6 +1027,23 @@ static eContextResult screen_ctx_selected_editable_keyframes(const bContext *C, return CTX_RESULT_NO_DATA; } +static eContextResult screen_ctx_asset_library(const bContext *C, bContextDataResult *result) +{ + WorkSpace *workspace = CTX_wm_workspace(C); + CTX_data_pointer_set( + result, &workspace->id, &RNA_AssetLibraryReference, &workspace->active_asset_library); + return CTX_RESULT_OK; +} + +static eContextResult screen_ctx_ui_list(const bContext *C, bContextDataResult *result) +{ + wmWindow *win = CTX_wm_window(C); + ARegion *region = CTX_wm_region(C); + uiList *list = UI_list_find_mouse_over(region, win->eventstate); + CTX_data_pointer_set(result, NULL, &RNA_UIList, list); + return CTX_RESULT_OK; +} + /* Registry of context callback functions. */ typedef eContextResult (*context_callback)(const bContext *C, bContextDataResult *result); @@ -1098,6 +1118,8 @@ static void ensure_ed_screen_context_functions(void) register_context_function("selected_visible_fcurves", screen_ctx_selected_visible_fcurves); register_context_function("active_editable_fcurve", screen_ctx_active_editable_fcurve); register_context_function("selected_editable_keyframes", screen_ctx_selected_editable_keyframes); + register_context_function("asset_library", screen_ctx_asset_library); + register_context_function("ui_list", screen_ctx_ui_list); } /* Entry point for the screen context. */ diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index ffa6f6ac962..23b90171a1d 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -784,11 +784,10 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, bool do_random = false; bool do_partial_update = false; - bool update_color = ((brush->flag & BRUSH_USE_GRADIENT) && - ((ELEM(brush->gradient_stroke_mode, - BRUSH_GRADIENT_SPACING_REPEAT, - BRUSH_GRADIENT_SPACING_CLAMP)) || - (cache->last_pressure != pressure))); + bool update_color = ((brush->flag & BRUSH_USE_GRADIENT) && (ELEM(brush->gradient_stroke_mode, + BRUSH_GRADIENT_SPACING_REPEAT, + BRUSH_GRADIENT_SPACING_CLAMP) || + (cache->last_pressure != pressure))); float tex_rotation = -brush->mtex.rot; float mask_rotation = -brush->mask_mtex.rot; diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 2484f382ed4..709e04d807d 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -752,7 +752,7 @@ static int vert_select_ungrouped_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); Mesh *me = ob->data; - if (BLI_listbase_is_empty(&ob->defbase) || (me->dvert == NULL)) { + if (BLI_listbase_is_empty(&me->vertex_group_names) || (me->dvert == NULL)) { BKE_report(op->reports, RPT_ERROR, "No weights/vertex groups on object"); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 5c1cc6cadcf..9387b84f437 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -1607,13 +1607,13 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo /* check if we are attempting to paint onto a locked vertex group, * and other options disallow it from doing anything useful */ bDeformGroup *dg; - dg = BLI_findlink(&ob->defbase, vgroup_index.active); + dg = BLI_findlink(&me->vertex_group_names, vgroup_index.active); if (dg->flag & DG_LOCK_WEIGHT) { BKE_report(op->reports, RPT_WARNING, "Active group is locked, aborting"); return false; } if (vgroup_index.mirror != -1) { - dg = BLI_findlink(&ob->defbase, vgroup_index.mirror); + dg = BLI_findlink(&me->vertex_group_names, vgroup_index.mirror); if (dg->flag & DG_LOCK_WEIGHT) { BKE_report(op->reports, RPT_WARNING, "Mirror group is locked, aborting"); return false; @@ -1622,7 +1622,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo } /* check that multipaint groups are unlocked */ - defbase_tot = BLI_listbase_count(&ob->defbase); + defbase_tot = BLI_listbase_count(&me->vertex_group_names); defbase_sel = BKE_object_defgroup_selected_get(ob, defbase_tot, &defbase_tot_sel); if (ts->multipaint && defbase_tot_sel > 1) { @@ -1636,7 +1636,7 @@ static bool wpaint_stroke_test_start(bContext *C, wmOperator *op, const float mo for (i = 0; i < defbase_tot; i++) { if (defbase_sel[i]) { - dg = BLI_findlink(&ob->defbase, i); + dg = BLI_findlink(&me->vertex_group_names, i); if (dg->flag & DG_LOCK_WEIGHT) { BKE_report(op->reports, RPT_WARNING, "Multipaint group is locked, aborting"); MEM_freeN(defbase_sel); @@ -2498,7 +2498,7 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke) for (psys = ob->particlesystem.first; psys; psys = psys->next) { for (i = 0; i < PSYS_TOT_VG; i++) { - if (psys->vgroup[i] == ob->actdef) { + if (psys->vgroup[i] == BKE_object_defgroup_active_index_get(ob)) { psys->recalc |= ID_RECALC_PSYS_RESET; break; } diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c index 96d22fe4a21..9f023dd6e63 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c @@ -149,7 +149,7 @@ static bool vertex_paint_from_weight(Object *ob) /* TODO: respect selection. */ /* TODO: Do we want to take weights from evaluated mesh instead? 2.7x was not doing it anyway. */ mp = me->mpoly; - vgroup_active = ob->actdef - 1; + vgroup_active = me->vertex_group_active_index - 1; for (int i = 0; i < me->totpoly; i++, mp++) { MLoopCol *lcol = &me->mloopcol[mp->loopstart]; uint j = 0; diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c index 5e047283534..cb8dc838422 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c @@ -183,7 +183,7 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even ED_view3d_viewcontext_init(C, &vc, depsgraph); me = BKE_mesh_from_object(vc.obact); - if (me && me->dvert && vc.v3d && vc.rv3d && (vc.obact->actdef != 0)) { + if (me && me->dvert && vc.v3d && vc.rv3d && (me->vertex_group_active_index != 0)) { const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; int v_idx_best = -1; uint index; @@ -213,9 +213,9 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even if (v_idx_best != -1) { /* should always be valid */ ToolSettings *ts = vc.scene->toolsettings; Brush *brush = BKE_paint_brush(&ts->wpaint->paint); - const int vgroup_active = vc.obact->actdef - 1; + const int vgroup_active = me->vertex_group_active_index - 1; float vgroup_weight = BKE_defvert_find_weight(&me->dvert[v_idx_best], vgroup_active); - const int defbase_tot = BLI_listbase_count(&vc.obact->defbase); + const int defbase_tot = BLI_listbase_count(&me->vertex_group_names); bool use_lock_relative = ts->wpaint_lock_relative; bool *defbase_locked = NULL, *defbase_unlocked = NULL; @@ -331,8 +331,8 @@ static const EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C, ED_view3d_viewcontext_init(C, &vc, depsgraph); me = BKE_mesh_from_object(vc.obact); - if (me && me->dvert && vc.v3d && vc.rv3d && vc.obact->defbase.first) { - const int defbase_tot = BLI_listbase_count(&vc.obact->defbase); + if (me && me->dvert && vc.v3d && vc.rv3d && me->vertex_group_names.first) { + const int defbase_tot = BLI_listbase_count(&me->vertex_group_names); const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; int *groups = MEM_callocN(defbase_tot * sizeof(int), "groups"); bool found = false; @@ -372,7 +372,7 @@ static const EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C, int totitem = 0; int i = 0; bDeformGroup *dg; - for (dg = vc.obact->defbase.first; dg && i < defbase_tot; i++, dg = dg->next) { + for (dg = me->vertex_group_names.first; dg && i < defbase_tot; i++, dg = dg->next) { if (groups[i]) { item_tmp.identifier = item_tmp.name = dg->name; item_tmp.value = i; @@ -401,7 +401,7 @@ static int weight_sample_group_exec(bContext *C, wmOperator *op) ED_view3d_viewcontext_init(C, &vc, depsgraph); BLI_assert(type + 1 >= 0); - vc.obact->actdef = type + 1; + BKE_object_defgroup_active_index_set(vc.obact, type + 1); DEG_id_tag_update(&vc.obact->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, vc.obact); @@ -458,7 +458,7 @@ static bool weight_paint_set(Object *ob, float paintweight) return false; } - vgroup_active = ob->actdef - 1; + vgroup_active = BKE_object_defgroup_active_index_get(ob) - 1; /* if mirror painting, find the other group */ if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) { @@ -815,7 +815,7 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op) data.sco_start = sco_start; data.sco_end = sco_end; data.sco_line_div = 1.0f / len_v2v2(sco_start, sco_end); - data.def_nr = ob->actdef - 1; + data.def_nr = BKE_object_defgroup_active_index_get(ob) - 1; data.use_select = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0; data.vert_cache = vert_cache; data.vert_visit = NULL; @@ -863,7 +863,7 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op) } if (scene->toolsettings->auto_normalize) { - const int vgroup_num = BLI_listbase_count(&ob->defbase); + const int vgroup_num = BLI_listbase_count(&me->vertex_group_names); bool *vgroup_validmap = BKE_object_defgroup_validmap_get(ob, vgroup_num); if (vgroup_validmap != NULL) { MDeformVert *dvert = me->dvert; diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c index d6a118bbd59..19ffa0c952d 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c @@ -79,8 +79,10 @@ bool ED_wpaint_ensure_data(bContext *C, WM_event_add_notifier(C, NC_GEOM | ND_DATA, me); } + const ListBase *defbase = BKE_object_defgroup_list(ob); + /* this happens on a Bone select, when no vgroup existed yet */ - if (ob->actdef <= 0) { + if (me->vertex_group_active_index <= 0) { Object *modob; if ((modob = BKE_modifiers_is_deformed_by_armature(ob))) { Bone *actbone = ((bArmature *)modob->data)->act_bone; @@ -94,32 +96,33 @@ bool ED_wpaint_ensure_data(bContext *C, DEG_relations_tag_update(CTX_data_main(C)); } else { - int actdef = 1 + BLI_findindex(&ob->defbase, dg); + + int actdef = 1 + BLI_findindex(defbase, dg); BLI_assert(actdef >= 0); - ob->actdef = actdef; + me->vertex_group_active_index = actdef; } } } } } - if (BLI_listbase_is_empty(&ob->defbase)) { + if (BLI_listbase_is_empty(defbase)) { BKE_object_defgroup_add(ob); DEG_relations_tag_update(CTX_data_main(C)); } /* ensure we don't try paint onto an invalid group */ - if (ob->actdef <= 0) { + if (me->vertex_group_active_index <= 0) { BKE_report(reports, RPT_WARNING, "No active vertex group for painting, aborting"); return false; } if (vgroup_index) { - vgroup_index->active = ob->actdef - 1; + vgroup_index->active = me->vertex_group_active_index - 1; } if (flag & WPAINT_ENSURE_MIRROR) { if (ME_USING_MIRROR_X_VERTEX_GROUPS(me)) { - int mirror = ED_wpaint_mirror_vgroup_ensure(ob, ob->actdef - 1); + int mirror = ED_wpaint_mirror_vgroup_ensure(ob, me->vertex_group_active_index - 1); if (vgroup_index) { vgroup_index->mirror = mirror; } @@ -133,7 +136,8 @@ bool ED_wpaint_ensure_data(bContext *C, /* mirror_vgroup is set to -1 when invalid */ int ED_wpaint_mirror_vgroup_ensure(Object *ob, const int vgroup_active) { - bDeformGroup *defgroup = BLI_findlink(&ob->defbase, vgroup_active); + const ListBase *defbase = BKE_object_defgroup_list(ob); + bDeformGroup *defgroup = BLI_findlink(defbase, vgroup_active); if (defgroup) { int mirrdef; @@ -143,7 +147,7 @@ int ED_wpaint_mirror_vgroup_ensure(Object *ob, const int vgroup_active) mirrdef = BKE_object_defgroup_name_index(ob, name_flip); if (mirrdef == -1) { if (BKE_object_defgroup_new(ob, name_flip)) { - mirrdef = BLI_listbase_count(&ob->defbase) - 1; + mirrdef = BLI_listbase_count(defbase) - 1; } } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 0bae6162f3a..f7785df0d3c 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -3103,7 +3103,7 @@ static float brush_strength(const Sculpt *sd, case BRUSH_MASK_SMOOTH: return alpha * pressure * feather; } - BLI_assert(!"Not supposed to happen"); + BLI_assert_msg(0, "Not supposed to happen"); return 0.0f; case SCULPT_TOOL_CREASE: diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.c b/source/blender/editors/sculpt_paint/sculpt_automasking.c index f3b03805b27..135d9af1af2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.c +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.c @@ -260,8 +260,8 @@ static void SCULPT_topology_automasking_init(Sculpt *sd, Brush *brush = BKE_paint_brush(&sd->paint); if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { - BLI_assert(!"Topology masking: pmap missing"); - return; + BLI_assert_msg(0, "Topology masking: pmap missing"); + return NULL; } const int totvert = SCULPT_vertex_count_get(ss); @@ -302,8 +302,8 @@ static void sculpt_face_sets_automasking_init(Sculpt *sd, } if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { - BLI_assert(!"Face Sets automasking: pmap missing"); - return; + BLI_assert_msg(0, "Face Sets automasking: pmap missing"); + return NULL; } int tot_vert = SCULPT_vertex_count_get(ss); @@ -329,8 +329,8 @@ void SCULPT_boundary_automasking_init(Object *ob, SculptSession *ss = ob->sculpt; if (!ss->pmap) { - BLI_assert(!"Boundary Edges masking: pmap missing"); - return; + BLI_assert_msg(0, "Boundary Edges masking: pmap missing"); + return NULL; } const int totvert = SCULPT_vertex_count_get(ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index 82818632782..6a37fd55562 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -676,7 +676,7 @@ void SCULPT_smooth(Sculpt *sd, #endif if (type == PBVH_FACES && !ss->pmap) { - BLI_assert(!"sculpt smooth: pmap missing"); + BLI_assert_msg(0, "sculpt smooth: pmap missing"); return; } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 831ee3a6163..df4b43d21a2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -1025,7 +1025,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase case SCULPT_UNDO_DYNTOPO_BEGIN: case SCULPT_UNDO_DYNTOPO_END: case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: - printf("Dynamic topology should've already been handled\n"); + BLI_assert_msg(0, "Dynamic topology should've already been handled"); break; } } @@ -1361,7 +1361,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt case SCULPT_UNDO_DYNTOPO_BEGIN: case SCULPT_UNDO_DYNTOPO_END: case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: - printf("Dynamic topology should've already been handled\n"); + BLI_assert_msg(0, "Dynamic topology should've already been handled"); case SCULPT_UNDO_GEOMETRY: case SCULPT_UNDO_FACE_SETS: break; @@ -1757,7 +1757,7 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType case SCULPT_UNDO_DYNTOPO_BEGIN: case SCULPT_UNDO_DYNTOPO_END: case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: - printf("Dynamic topology should've already been handled\n"); + BLI_assert_msg(0, "Dynamic topology should've already been handled"); case SCULPT_UNDO_GEOMETRY: case SCULPT_UNDO_FACE_SETS: break; @@ -2017,7 +2017,7 @@ void ED_sculpt_undosys_type(UndoType *ut) ut->step_decode = sculpt_undosys_step_decode; ut->step_free = sculpt_undosys_step_free; - ut->flags = 0; + ut->flags = UNDOTYPE_FLAG_DECODE_ACTIVE_STEP; ut->step_size = sizeof(SculptUndoStep); } diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c index 9e69b0a72db..d69c7ab8d48 100644 --- a/source/blender/editors/space_action/action_data.c +++ b/source/blender/editors/space_action/action_data.c @@ -813,7 +813,7 @@ static int action_layer_next_exec(bContext *C, wmOperator *op) NlaTrack *act_track; Scene *scene = CTX_data_scene(C); - float ctime = BKE_scene_frame_get(scene); + float ctime = BKE_scene_ctime_get(scene); /* Get active track */ act_track = BKE_nlatrack_find_tweaked(adt); @@ -925,7 +925,7 @@ static int action_layer_prev_exec(bContext *C, wmOperator *op) NlaTrack *nlt; Scene *scene = CTX_data_scene(C); - float ctime = BKE_scene_frame_get(scene); + float ctime = BKE_scene_ctime_get(scene); /* Sanity Check */ if (adt == NULL) { diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index adb824b8934..b3b3eafb6e7 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -129,6 +129,8 @@ void ED_spacetypes_init(void) ED_screen_user_menu_register(); + ED_uilisttypes_ui(); + /* Gizmo types. */ ED_gizmotypes_button_2d(); ED_gizmotypes_dial_3d(); diff --git a/source/blender/editors/space_buttons/buttons_intern.h b/source/blender/editors/space_buttons/buttons_intern.h index 7564fa4b930..9cb363ff0c9 100644 --- a/source/blender/editors/space_buttons/buttons_intern.h +++ b/source/blender/editors/space_buttons/buttons_intern.h @@ -34,8 +34,8 @@ struct Tex; struct bContext; struct bContextDataResult; struct bNode; -struct bNodeTree; struct bNodeSocket; +struct bNodeTree; struct wmOperatorType; struct SpaceProperties_Runtime { diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c index 2da13646a8b..67b4fd61d38 100644 --- a/source/blender/editors/space_clip/clip_editor.c +++ b/source/blender/editors/space_clip/clip_editor.c @@ -1025,7 +1025,7 @@ static void prefetch_startjob(void *pjv, short *stop, short *do_update, float *p progress); } else { - BLI_assert(!"Unknown movie clip source when prefetching frames"); + BLI_assert_msg(0, "Unknown movie clip source when prefetching frames"); } } diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index faa4b3cc9cc..a314a85491d 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -1105,7 +1105,7 @@ bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region) return false; } /* Check if the library exists. */ - if ((asset_params->asset_library.type == FILE_ASSET_LIBRARY_LOCAL) || + if ((asset_params->asset_library.type == ASSET_LIBRARY_LOCAL) || filelist_is_dir(sfile->files, asset_params->base_params.dir)) { return false; } diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 0e15538e03b..492a189fc81 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -334,6 +334,7 @@ typedef struct FileListEntryCache { /* Previews handling. */ TaskPool *previews_pool; ThreadQueue *previews_done; + size_t previews_todo_count; } FileListEntryCache; /* FileListCache.flags */ @@ -1050,7 +1051,7 @@ static bool filelist_compare_asset_libraries(const FileSelectAssetLibraryUID *li if (library_a->type != library_b->type) { return false; } - if (library_a->type == FILE_ASSET_LIBRARY_CUSTOM) { + if (library_a->type == ASSET_LIBRARY_CUSTOM) { /* Don't only check the index, also check that it's valid. */ bUserAssetLibrary *library_ptr_a = BKE_preferences_asset_library_find_from_index( &U, library_a->custom_library_index); @@ -1153,7 +1154,7 @@ ImBuf *filelist_file_getimage(const FileDirEntry *file) return file->preview_icon_id ? BKE_icon_imbuf_get_buffer(file->preview_icon_id) : NULL; } -static ImBuf *filelist_geticon_image_ex(FileDirEntry *file) +ImBuf *filelist_geticon_image_ex(const FileDirEntry *file) { ImBuf *ibuf = NULL; @@ -1494,6 +1495,7 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat /* That way task freeing function won't free th preview, since it does not own it anymore. */ atomic_cas_ptr((void **)&preview_taskdata->preview, preview, NULL); BLI_thread_queue_push(cache->previews_done, preview); + atomic_fetch_and_sub_z(&cache->previews_todo_count, 1); } // printf("%s: End (%d)...\n", __func__, threadid); @@ -1520,6 +1522,7 @@ static void filelist_cache_preview_ensure_running(FileListEntryCache *cache) if (!cache->previews_pool) { cache->previews_pool = BLI_task_pool_create_background(cache, TASK_PRIORITY_LOW); cache->previews_done = BLI_thread_queue_init(); + cache->previews_todo_count = 0; IMB_thumb_locks_acquire(); } @@ -1553,6 +1556,7 @@ static void filelist_cache_previews_free(FileListEntryCache *cache) BLI_task_pool_free(cache->previews_pool); cache->previews_pool = NULL; cache->previews_done = NULL; + cache->previews_todo_count = 0; IMB_thumb_locks_release(); } @@ -1633,6 +1637,8 @@ static void filelist_cache_init(FileListEntryCache *cache, size_t cache_size) cache->size = cache_size; cache->flags = FLC_IS_INIT; + cache->previews_todo_count = 0; + /* We cannot translate from non-main thread, so init translated strings once from here. */ IMB_thumb_ensure_translations(); } @@ -2384,7 +2390,8 @@ void filelist_cache_previews_set(FileList *filelist, const bool use_previews) if (use_previews && (filelist->flags & FL_IS_READY)) { cache->flags |= FLC_PREVIEWS_ACTIVE; - BLI_assert((cache->previews_pool == NULL) && (cache->previews_done == NULL)); + BLI_assert((cache->previews_pool == NULL) && (cache->previews_done == NULL) && + (cache->previews_todo_count == 0)); // printf("%s: Init Previews...\n", __func__); @@ -2457,6 +2464,18 @@ bool filelist_cache_previews_running(FileList *filelist) return (cache->previews_pool != NULL); } +bool filelist_cache_previews_done(FileList *filelist) +{ + FileListEntryCache *cache = &filelist->filelist_cache; + if ((cache->flags & FLC_PREVIEWS_ACTIVE) == 0) { + /* There are no previews. */ + return false; + } + + return (cache->previews_pool == NULL) || (cache->previews_done == NULL) || + (cache->previews_todo_count == (size_t)BLI_thread_queue_len(cache->previews_done)); +} + /* would recognize .blend as well */ static bool file_is_blend_backup(const char *str) { @@ -3432,7 +3451,7 @@ static void filelist_readjob_free(void *flrjv) MEM_freeN(flrj); } -void filelist_readjob_start(FileList *filelist, const bContext *C) +void filelist_readjob_start(FileList *filelist, const int space_notifier, const bContext *C) { Main *bmain = CTX_data_main(C); wmJob *wm_job; @@ -3464,7 +3483,7 @@ void filelist_readjob_start(FileList *filelist, const bContext *C) filelist_readjob_endjob(flrj); filelist_readjob_free(flrj); - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST | NA_JOB_FINISHED, NULL); + WM_event_add_notifier(C, space_notifier | NA_JOB_FINISHED, NULL); return; } @@ -3476,10 +3495,7 @@ void filelist_readjob_start(FileList *filelist, const bContext *C) WM_JOB_PROGRESS, WM_JOB_TYPE_FILESEL_READDIR); WM_jobs_customdata_set(wm_job, flrj, filelist_readjob_free); - WM_jobs_timer(wm_job, - 0.01, - NC_SPACE | ND_SPACE_FILE_LIST, - NC_SPACE | ND_SPACE_FILE_LIST | NA_JOB_FINISHED); + WM_jobs_timer(wm_job, 0.01, space_notifier, space_notifier | NA_JOB_FINISHED); WM_jobs_callbacks( wm_job, filelist_readjob_startjob, NULL, filelist_readjob_update, filelist_readjob_endjob); diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index 0aace74e621..cb98cf6e74a 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -79,6 +79,7 @@ void filelist_init_icons(void); void filelist_free_icons(void); struct ImBuf *filelist_getimage(struct FileList *filelist, const int index); struct ImBuf *filelist_file_getimage(const FileDirEntry *file); +struct ImBuf *filelist_geticon_image_ex(const FileDirEntry *file); struct ImBuf *filelist_geticon_image(struct FileList *filelist, const int index); int filelist_geticon(struct FileList *filelist, const int index, const bool is_main); @@ -144,13 +145,16 @@ struct BlendHandle *filelist_lib(struct FileList *filelist); bool filelist_islibrary(struct FileList *filelist, char *dir, char **r_group); void filelist_freelib(struct FileList *filelist); -void filelist_readjob_start(struct FileList *filelist, const struct bContext *C); +void filelist_readjob_start(struct FileList *filelist, + int space_notifier, + const struct bContext *C); void filelist_readjob_stop(struct FileList *filelist, struct wmWindowManager *wm); int filelist_readjob_running(struct FileList *filelist, struct wmWindowManager *wm); bool filelist_cache_previews_update(struct FileList *filelist); void filelist_cache_previews_set(struct FileList *filelist, const bool use_previews); bool filelist_cache_previews_running(struct FileList *filelist); +bool filelist_cache_previews_done(struct FileList *filelist); #ifdef __cplusplus } diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index b7accbf71e5..7bc83e8fc79 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -118,7 +118,7 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile) asset_params = sfile->asset_params = MEM_callocN(sizeof(*asset_params), "FileAssetSelectParams"); asset_params->base_params.details_flags = U_default.file_space_data.details_flags; - asset_params->asset_library.type = FILE_ASSET_LIBRARY_LOCAL; + asset_params->asset_library.type = ASSET_LIBRARY_LOCAL; asset_params->asset_library.custom_library_index = -1; asset_params->import_type = FILE_ASSET_IMPORT_APPEND; } @@ -378,7 +378,7 @@ FileSelectParams *ED_fileselect_ensure_active_params(SpaceFile *sfile) return &sfile->asset_params->base_params; } - BLI_assert(!"Invalid browse mode set in file space."); + BLI_assert_msg(0, "Invalid browse mode set in file space."); return NULL; } @@ -399,7 +399,7 @@ FileSelectParams *ED_fileselect_get_active_params(const SpaceFile *sfile) return (FileSelectParams *)sfile->asset_params; } - BLI_assert(!"Invalid browse mode set in file space."); + BLI_assert_msg(0, "Invalid browse mode set in file space."); return NULL; } @@ -420,26 +420,26 @@ static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params) bUserAssetLibrary *user_library = NULL; /* Ensure valid repository, or fall-back to local one. */ - if (library->type == FILE_ASSET_LIBRARY_CUSTOM) { + if (library->type == ASSET_LIBRARY_CUSTOM) { BLI_assert(library->custom_library_index >= 0); user_library = BKE_preferences_asset_library_find_from_index(&U, library->custom_library_index); if (!user_library) { - library->type = FILE_ASSET_LIBRARY_LOCAL; + library->type = ASSET_LIBRARY_LOCAL; } } switch (library->type) { - case FILE_ASSET_LIBRARY_LOCAL: + case ASSET_LIBRARY_LOCAL: base_params->dir[0] = '\0'; break; - case FILE_ASSET_LIBRARY_CUSTOM: + case ASSET_LIBRARY_CUSTOM: BLI_assert(user_library); BLI_strncpy(base_params->dir, user_library->path, sizeof(base_params->dir)); break; } - base_params->type = (library->type == FILE_ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : FILE_LOADLIB; + base_params->type = (library->type == ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : FILE_LOADLIB; } void fileselect_refresh_params(SpaceFile *sfile) diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 05d484d8e2e..31c7dee294b 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -367,7 +367,7 @@ static void file_refresh(const bContext *C, ScrArea *area) if (filelist_needs_reading(sfile->files)) { if (!filelist_pending(sfile->files)) { - filelist_readjob_start(sfile->files, C); + filelist_readjob_start(sfile->files, NC_SPACE | ND_SPACE_FILE_LIST, C); } } @@ -868,7 +868,12 @@ static void file_space_subtype_item_extend(bContext *UNUSED(C), } } -static const char *file_context_dir[] = {"active_file", "id", NULL}; +static const char *file_context_dir[] = { + "active_file", + "asset_library", + "id", + NULL, +}; static int /*eContextResult*/ file_context(const bContext *C, const char *member, @@ -899,6 +904,23 @@ static int /*eContextResult*/ file_context(const bContext *C, CTX_data_pointer_set(result, &screen->id, &RNA_FileSelectEntry, file); return CTX_RESULT_OK; } + if (CTX_data_equals(member, "asset_library")) { + FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); + if (!asset_params) { + return CTX_RESULT_NO_DATA; + } + + BLI_STATIC_ASSERT(offsetof(FileSelectAssetLibraryUID, type) == + offsetof(AssetLibraryReference, type), + "Expected FileSelectAssetLibraryUID to match AssetLibraryReference"); + BLI_STATIC_ASSERT(offsetof(FileSelectAssetLibraryUID, custom_library_index) == + offsetof(AssetLibraryReference, custom_library_index), + "Expected FileSelectAssetLibraryUID to match AssetLibraryReference"); + + CTX_data_pointer_set( + result, &screen->id, &RNA_AssetLibraryReference, &asset_params->asset_library); + return CTX_RESULT_OK; + } if (CTX_data_equals(member, "id")) { const FileDirEntry *file = filelist_file(sfile->files, params->active_file); if (file == NULL) { diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c index 56592cbbd1b..af88bbced9c 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -1259,14 +1259,15 @@ static void graph_draw_driver_debug(bAnimContext *ac, ID *id, FCurve *fcu) immUniformColor3f(0.9f, 0.9f, 0.9f); immUniform1f("dash_width", 10.0f); immUniform1f("dash_factor", 0.5f); + GPU_line_width(1.0f); - immBegin(GPU_PRIM_LINES, (y >= v2d->cur.ymin) ? 4 : 2); + immBegin(GPU_PRIM_LINES, (y <= v2d->cur.ymax) ? 4 : 2); /* x-axis lookup */ co[0] = x; - if (y >= v2d->cur.ymin) { - co[1] = v2d->cur.ymin - 1.0f; + if (y <= v2d->cur.ymax) { + co[1] = v2d->cur.ymax + 1.0f; immVertex2fv(shdr_pos, co); co[1] = y; diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 1421be41124..a853efb1ace 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -859,7 +859,7 @@ static int graphkeys_box_select_exec(bContext *C, wmOperator *op) * as frame-range one is often used for tweaking timing when "blocking", * while channels is not that useful. */ - if ((BLI_rcti_size_x(&rect)) >= (BLI_rcti_size_y(&rect))) { + if (BLI_rcti_size_x(&rect) >= BLI_rcti_size_y(&rect)) { mode = BEZT_OK_FRAMERANGE; } else { diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 50b0ea75052..4779a82948d 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -1013,14 +1013,14 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, bool color_ma uiLayoutRow(col, true), imfptr, "color_mode", UI_ITEM_R_EXPAND, IFACE_("Color"), ICON_NONE); /* only display depth setting if multiple depths can be used */ - if ((ELEM(depth_ok, - R_IMF_CHAN_DEPTH_1, - R_IMF_CHAN_DEPTH_8, - R_IMF_CHAN_DEPTH_10, - R_IMF_CHAN_DEPTH_12, - R_IMF_CHAN_DEPTH_16, - R_IMF_CHAN_DEPTH_24, - R_IMF_CHAN_DEPTH_32)) == 0) { + if (ELEM(depth_ok, + R_IMF_CHAN_DEPTH_1, + R_IMF_CHAN_DEPTH_8, + R_IMF_CHAN_DEPTH_10, + R_IMF_CHAN_DEPTH_12, + R_IMF_CHAN_DEPTH_16, + R_IMF_CHAN_DEPTH_24, + R_IMF_CHAN_DEPTH_32) == 0) { uiItemR(uiLayoutRow(col, true), imfptr, "color_depth", UI_ITEM_R_EXPAND, NULL, ICON_NONE); } diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 6b9821745c7..dad354ba8ee 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1995,7 +1995,7 @@ static bool image_save_as_draw_check_prop(PointerRNA *ptr, return !(STREQ(prop_id, "filepath") || STREQ(prop_id, "directory") || STREQ(prop_id, "filename") || /* when saving a copy, relative path has no effect */ - ((STREQ(prop_id, "relative_path")) && RNA_boolean_get(ptr, "copy"))); + (STREQ(prop_id, "relative_path") && RNA_boolean_get(ptr, "copy"))); } static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op) diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c index 082f66b57af..cc6effd0f71 100644 --- a/source/blender/editors/space_image/image_undo.c +++ b/source/blender/editors/space_image/image_undo.c @@ -1006,7 +1006,7 @@ void ED_image_undosys_type(UndoType *ut) * specific case, see `image_undosys_step_encode` code. We cannot specify * `UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE` though, as it can be called with a NULL context by * current code. */ - ut->flags = 0; + ut->flags = UNDOTYPE_FLAG_DECODE_ACTIVE_STEP; ut->step_size = sizeof(ImageUndoStep); } diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 243652da608..df3afb42ab2 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -1953,8 +1953,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi 0, 0, 0, - false, - false); + UI_TEMPLATE_LIST_FLAG_NONE); RNA_property_collection_lookup_int( ptr, RNA_struct_find_property(ptr, "layer_slots"), active_index, &active_input_ptr); } @@ -1972,8 +1971,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi 0, 0, 0, - false, - false); + UI_TEMPLATE_LIST_FLAG_NONE); RNA_property_collection_lookup_int( ptr, RNA_struct_find_property(ptr, "file_slots"), active_index, &active_input_ptr); } @@ -3911,8 +3909,10 @@ static struct { GPUVertBuf *inst_vbo; uint p0_id, p1_id, p2_id, p3_id; uint colid_id, muted_id; + uint dim_factor_id; GPUVertBufRaw p0_step, p1_step, p2_step, p3_step; GPUVertBufRaw colid_step, muted_step; + GPUVertBufRaw dim_factor_step; uint count; bool enabled; } g_batch_link; @@ -3927,6 +3927,8 @@ static void nodelink_batch_reset() g_batch_link.inst_vbo, g_batch_link.colid_id, &g_batch_link.colid_step); GPU_vertbuf_attr_get_raw_data( g_batch_link.inst_vbo, g_batch_link.muted_id, &g_batch_link.muted_step); + GPU_vertbuf_attr_get_raw_data( + g_batch_link.inst_vbo, g_batch_link.dim_factor_id, &g_batch_link.dim_factor_step); g_batch_link.count = 0; } @@ -4044,6 +4046,8 @@ static void nodelink_batch_init() &format_inst, "colid_doarrow", GPU_COMP_U8, 4, GPU_FETCH_INT); g_batch_link.muted_id = GPU_vertformat_attr_add( &format_inst, "domuted", GPU_COMP_U8, 2, GPU_FETCH_INT); + g_batch_link.dim_factor_id = GPU_vertformat_attr_add( + &format_inst, "dim_factor", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); g_batch_link.inst_vbo = GPU_vertbuf_create_with_format_ex(&format_inst, GPU_USAGE_STREAM); /* Alloc max count but only draw the range we need. */ GPU_vertbuf_data_alloc(g_batch_link.inst_vbo, NODELINK_GROUP_SIZE); @@ -4119,7 +4123,8 @@ static void nodelink_batch_add_link(const SpaceNode *snode, int th_col2, int th_col3, bool drawarrow, - bool drawmuted) + bool drawmuted, + float dim_factor) { /* Only allow these colors. If more is needed, you need to modify the shader accordingly. */ BLI_assert(ELEM(th_col1, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT)); @@ -4138,6 +4143,7 @@ static void nodelink_batch_add_link(const SpaceNode *snode, colid[3] = drawarrow; char *muted = (char *)GPU_vertbuf_raw_step(&g_batch_link.muted_step); muted[0] = drawmuted; + *(float *)GPU_vertbuf_raw_step(&g_batch_link.dim_factor_step) = dim_factor; if (g_batch_link.count == NODELINK_GROUP_SIZE) { nodelink_batch_draw(snode); @@ -4152,6 +4158,8 @@ void node_draw_link_bezier(const View2D *v2d, int th_col2, int th_col3) { + const float dim_factor = node_link_dim_factor(v2d, link); + float vec[4][2]; const bool highlighted = link->flag & NODE_LINK_TEMP_HIGHLIGHT; if (node_link_bezier_handles(v2d, snode, link, vec)) { @@ -4164,8 +4172,17 @@ void node_draw_link_bezier(const View2D *v2d, if (g_batch_link.enabled && !highlighted) { /* Add link to batch. */ - nodelink_batch_add_link( - snode, vec[0], vec[1], vec[2], vec[3], th_col1, th_col2, th_col3, drawarrow, drawmuted); + nodelink_batch_add_link(snode, + vec[0], + vec[1], + vec[2], + vec[3], + th_col1, + th_col2, + th_col3, + drawarrow, + drawmuted, + dim_factor); } else { /* Draw single link. */ @@ -4190,6 +4207,7 @@ void node_draw_link_bezier(const View2D *v2d, GPU_batch_uniform_1f(batch, "arrowSize", ARROW_SIZE); GPU_batch_uniform_1i(batch, "doArrow", drawarrow); GPU_batch_uniform_1i(batch, "doMuted", drawmuted); + GPU_batch_uniform_1f(batch, "dim_factor", dim_factor); GPU_batch_draw(batch); } } diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index c167744de01..9264c9d3572 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -260,7 +260,7 @@ static int add_reroute_exec(bContext *C, wmOperator *op) BLI_listbase_clear(&input_links); for (link = (bNodeLink *)ntree->links.first; link; link = link->next) { - if (nodeLinkIsHidden(link)) { + if (node_link_is_hidden_or_dimmed(®ion->v2d, link)) { continue; } if (add_reroute_intersect_check(link, mcoords, i, insert_point)) { diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index d4f178603b8..7eca5c0c933 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -45,6 +45,7 @@ #include "BLT_translation.h" #include "BKE_context.h" +#include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" @@ -826,6 +827,149 @@ void node_socket_color_get( } } +struct SocketTooltipData { + bNodeTree *ntree; + bNode *node; + bNodeSocket *socket; +}; + +static void create_inspection_string_for_generic_value(const geo_log::GenericValueLog &value_log, + std::stringstream &ss) +{ + auto id_to_inspection_string = [&](ID *id) { + ss << (id ? id->name + 2 : TIP_("None")) << " (" << BKE_idtype_idcode_to_name(GS(id->name)) + << ")"; + }; + + const GPointer value = value_log.value(); + if (value.is_type<int>()) { + ss << *value.get<int>() << TIP_(" (Integer)"); + } + else if (value.is_type<float>()) { + ss << *value.get<float>() << TIP_(" (Float)"); + } + else if (value.is_type<blender::float3>()) { + ss << *value.get<blender::float3>() << TIP_(" (Vector)"); + } + else if (value.is_type<bool>()) { + ss << (*value.get<bool>() ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)"); + } + else if (value.is_type<std::string>()) { + ss << *value.get<std::string>() << TIP_(" (String)"); + } + else if (value.is_type<Object *>()) { + id_to_inspection_string((ID *)*value.get<Object *>()); + } + else if (value.is_type<Material *>()) { + id_to_inspection_string((ID *)*value.get<Material *>()); + } + else if (value.is_type<Tex *>()) { + id_to_inspection_string((ID *)*value.get<Tex *>()); + } + else if (value.is_type<Collection *>()) { + id_to_inspection_string((ID *)*value.get<Collection *>()); + } +} + +static void create_inspection_string_for_geometry(const geo_log::GeometryValueLog &value_log, + std::stringstream &ss) +{ + Span<GeometryComponentType> component_types = value_log.component_types(); + if (component_types.is_empty()) { + ss << TIP_("Empty Geometry"); + return; + } + + auto to_string = [](int value) { + char str[16]; + BLI_str_format_int_grouped(str, value); + return std::string(str); + }; + + ss << TIP_("Geometry:\n"); + for (GeometryComponentType type : component_types) { + const char *line_end = (type == component_types.last()) ? "" : ".\n"; + switch (type) { + case GEO_COMPONENT_TYPE_MESH: { + const geo_log::GeometryValueLog::MeshInfo &mesh_info = *value_log.mesh_info; + char line[256]; + BLI_snprintf(line, + sizeof(line), + TIP_("\u2022 Mesh: %s vertices, %s edges, %s faces"), + to_string(mesh_info.tot_verts).c_str(), + to_string(mesh_info.tot_edges).c_str(), + to_string(mesh_info.tot_faces).c_str()); + ss << line << line_end; + break; + } + case GEO_COMPONENT_TYPE_POINT_CLOUD: { + const geo_log::GeometryValueLog::PointCloudInfo &pointcloud_info = + *value_log.pointcloud_info; + char line[256]; + BLI_snprintf(line, + sizeof(line), + TIP_("\u2022 Point Cloud: %s points"), + to_string(pointcloud_info.tot_points).c_str()); + ss << line << line_end; + break; + } + case GEO_COMPONENT_TYPE_CURVE: { + const geo_log::GeometryValueLog::CurveInfo &curve_info = *value_log.curve_info; + char line[256]; + BLI_snprintf(line, + sizeof(line), + TIP_("\u2022 Curve: %s splines"), + to_string(curve_info.tot_splines).c_str()); + ss << line << line_end; + break; + } + case GEO_COMPONENT_TYPE_INSTANCES: { + const geo_log::GeometryValueLog::InstancesInfo &instances_info = *value_log.instances_info; + char line[256]; + BLI_snprintf(line, + sizeof(line), + TIP_("\u2022 Instances: %s"), + to_string(instances_info.tot_instances).c_str()); + ss << line << line_end; + break; + } + case GEO_COMPONENT_TYPE_VOLUME: { + ss << TIP_("\u2022 Volume") << line_end; + break; + } + } + } +} + +static std::optional<std::string> create_socket_inspection_string(bContext *C, + bNodeTree &UNUSED(ntree), + bNode &node, + bNodeSocket &socket) +{ + SpaceNode *snode = CTX_wm_space_node(C); + const geo_log::SocketLog *socket_log = geo_log::ModifierLog::find_socket_by_node_editor_context( + *snode, node, socket); + if (socket_log == nullptr) { + return {}; + } + const geo_log::ValueLog *value_log = socket_log->value(); + if (value_log == nullptr) { + return {}; + } + + std::stringstream ss; + if (const geo_log::GenericValueLog *generic_value_log = + dynamic_cast<const geo_log::GenericValueLog *>(value_log)) { + create_inspection_string_for_generic_value(*generic_value_log, ss); + } + else if (const geo_log::GeometryValueLog *geo_value_log = + dynamic_cast<const geo_log::GeometryValueLog *>(value_log)) { + create_inspection_string_for_geometry(*geo_value_log, ss); + } + + return ss.str(); +} + static void node_socket_draw_nested(const bContext *C, bNodeTree *ntree, PointerRNA *node_ptr, @@ -855,6 +999,55 @@ static void node_socket_draw_nested(const bContext *C, shape_id, size_id, outline_col_id); + + if (ntree->type != NTREE_GEOMETRY) { + /* Only geometry nodes has socket value tooltips currently. */ + return; + } + + bNode *node = (bNode *)node_ptr->data; + uiBlock *block = node->block; + + /* Ideally sockets themselves should be buttons, but they aren't currently. So add an invisible + * button on top of them for the tooltip. */ + const eUIEmbossType old_emboss = UI_block_emboss_get(block); + UI_block_emboss_set(block, UI_EMBOSS_NONE); + uiBut *but = uiDefIconBut(block, + UI_BTYPE_BUT, + 0, + ICON_NONE, + sock->locx - size / 2, + sock->locy - size / 2, + size, + size, + nullptr, + 0, + 0, + 0, + 0, + nullptr); + + SocketTooltipData *data = (SocketTooltipData *)MEM_mallocN(sizeof(SocketTooltipData), __func__); + data->ntree = ntree; + data->node = (bNode *)node_ptr->data; + data->socket = sock; + + UI_but_func_tooltip_set( + but, + [](bContext *C, void *argN, const char *UNUSED(tip)) { + SocketTooltipData *data = (SocketTooltipData *)argN; + std::optional<std::string> str = create_socket_inspection_string( + C, *data->ntree, *data->node, *data->socket); + if (str.has_value()) { + return BLI_strdup(str->c_str()); + } + return BLI_strdup(TIP_("The socket value has not been computed yet")); + }, + data, + MEM_freeN); + /* Disable the button so that clicks on it are ignored the the link operator still works. */ + UI_but_flag_enable(but, UI_BUT_DISABLED); + UI_block_emboss_set(block, old_emboss); } /** diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index 5dd935bdd76..af9c888cbf7 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -21,6 +21,8 @@ * \ingroup spnode */ +#include <algorithm> + #include "MEM_guardedalloc.h" #include "DNA_light_types.h" @@ -1226,6 +1228,32 @@ int node_find_indicated_socket( return 0; } +/* ****************** Link Dimming *********************** */ + +float node_link_dim_factor(const View2D *v2d, const bNodeLink *link) +{ + if (link->fromsock == nullptr || link->tosock == nullptr) { + return 1.0f; + } + + const float min_endpoint_distance = std::min( + std::max(BLI_rctf_length_x(&v2d->cur, link->fromsock->locx), + BLI_rctf_length_y(&v2d->cur, link->fromsock->locy)), + std::max(BLI_rctf_length_x(&v2d->cur, link->tosock->locx), + BLI_rctf_length_y(&v2d->cur, link->tosock->locy))); + + if (min_endpoint_distance == 0.0f) { + return 1.0f; + } + const float viewport_width = BLI_rctf_size_x(&v2d->cur); + return std::clamp(1.0f - min_endpoint_distance / viewport_width * 10.0f, 0.05f, 1.0f); +} + +bool node_link_is_hidden_or_dimmed(const View2D *v2d, const bNodeLink *link) +{ + return nodeLinkIsHidden(link) || node_link_dim_factor(v2d, link) < 0.5f; +} + /* ****************** Duplicate *********************** */ static void node_duplicate_reparent_recursive(bNode *node) diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index 09e5a110a45..df20420e472 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -265,6 +265,8 @@ int node_find_indicated_socket(struct SpaceNode *snode, struct bNodeSocket **sockp, const float cursor[2], int in_out); +float node_link_dim_factor(const struct View2D *v2d, const struct bNodeLink *link); +bool node_link_is_hidden_or_dimmed(const struct View2D *v2d, const struct bNodeLink *link); void NODE_OT_duplicate(struct wmOperatorType *ot); void NODE_OT_delete(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index aadf93961e9..725c872e98f 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -36,6 +36,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_screen.h" #include "ED_node.h" /* own include */ #include "ED_render.h" @@ -1332,7 +1333,7 @@ static int cut_links_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), bmain); LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode->edittree->links) { - if (nodeLinkIsHidden(link)) { + if (node_link_is_hidden_or_dimmed(®ion->v2d, link)) { continue; } @@ -1429,7 +1430,7 @@ static int mute_links_exec(bContext *C, wmOperator *op) /* Count intersected links and clear test flag. */ int tot = 0; LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - if (nodeLinkIsHidden(link)) { + if (node_link_is_hidden_or_dimmed(®ion->v2d, link)) { continue; } link->flag &= ~NODE_LINK_TEST; @@ -1443,7 +1444,7 @@ static int mute_links_exec(bContext *C, wmOperator *op) /* Mute links. */ LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - if (nodeLinkIsHidden(link) || (link->flag & NODE_LINK_TEST)) { + if (node_link_is_hidden_or_dimmed(®ion->v2d, link) || (link->flag & NODE_LINK_TEST)) { continue; } @@ -1458,7 +1459,7 @@ static int mute_links_exec(bContext *C, wmOperator *op) /* Clear remaining test flags. */ LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - if (nodeLinkIsHidden(link)) { + if (node_link_is_hidden_or_dimmed(®ion->v2d, link)) { continue; } link->flag &= ~NODE_LINK_TEST; @@ -1894,9 +1895,11 @@ static bool ed_node_link_conditions(ScrArea *area, return false; } + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); + /* test node for links */ LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - if (nodeLinkIsHidden(link)) { + if (node_link_is_hidden_or_dimmed(®ion->v2d, link)) { continue; } @@ -1927,13 +1930,15 @@ void ED_node_link_intersect_test(ScrArea *area, int test) return; } + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); + /* find link to select/highlight */ bNodeLink *selink = nullptr; float dist_best = FLT_MAX; LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { float coord_array[NODE_LINK_RESOL + 1][2]; - if (nodeLinkIsHidden(link)) { + if (node_link_is_hidden_or_dimmed(®ion->v2d, link)) { continue; } diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index f248711633e..35015356f0b 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -42,6 +42,7 @@ #include "BKE_collection.h" #include "BKE_constraint.h" #include "BKE_context.h" +#include "BKE_deform.h" #include "BKE_gpencil.h" #include "BKE_gpencil_modifier.h" #include "BKE_layer.h" @@ -450,7 +451,7 @@ static void tree_element_defgroup_activate(bContext *C, TreeElement *te, TreeSto /* id in tselem is object */ Object *ob = (Object *)tselem->id; BLI_assert(te->index + 1 >= 0); - ob->actdef = te->index + 1; + BKE_object_defgroup_active_index_set(ob, te->index + 1); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); @@ -830,7 +831,7 @@ static eOLDrawState tree_element_defgroup_state_get(const ViewLayer *view_layer, { const Object *ob = (const Object *)tselem->id; if (ob == OBACT(view_layer)) { - if (ob->actdef == te->index + 1) { + if (BKE_object_defgroup_active_index_get(ob) == te->index + 1) { return OL_DRAWSEL_NORMAL; } } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 334c901b6d3..c5ec656080a 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -1,4 +1,4 @@ -/* +/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 @@ -62,6 +62,7 @@ #include "BLT_translation.h" #include "BKE_armature.h" +#include "BKE_deform.h" #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_main.h" @@ -552,17 +553,20 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner, } /* vertex groups */ - if (!BLI_listbase_is_empty(&ob->defbase)) { - TreeElement *tenla = outliner_add_element( - space_outliner, &te->subtree, ob, te, TSE_DEFGROUP_BASE, 0); - tenla->name = IFACE_("Vertex Groups"); + if (ELEM(ob->type, OB_MESH, OB_GPENCIL, OB_LATTICE)) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + if (!BLI_listbase_is_empty(defbase)) { + TreeElement *tenla = outliner_add_element( + space_outliner, &te->subtree, ob, te, TSE_DEFGROUP_BASE, 0); + tenla->name = IFACE_("Vertex Groups"); - int index; - LISTBASE_FOREACH_INDEX (bDeformGroup *, defgroup, &ob->defbase, index) { - TreeElement *ten = outliner_add_element( - space_outliner, &tenla->subtree, ob, tenla, TSE_DEFGROUP, index); - ten->name = defgroup->name; - ten->directdata = defgroup; + int index; + LISTBASE_FOREACH_INDEX (bDeformGroup *, defgroup, defbase, index) { + TreeElement *ten = outliner_add_element( + space_outliner, &tenla->subtree, ob, tenla, TSE_DEFGROUP, index); + ten->name = defgroup->name; + ten->directdata = defgroup; + } } } @@ -588,7 +592,7 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner, switch (GS(id->name)) { case ID_LI: case ID_SCE: - BLI_assert(!"ID type expected to be expanded through new tree-element design"); + BLI_assert_msg(0, "ID type expected to be expanded through new tree-element design"); break; case ID_OB: { outliner_add_object_contents(space_outliner, te, tselem, (Object *)id); @@ -901,12 +905,13 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, } else if (type == TSE_SOME_ID) { if (!te->type) { - BLI_assert(!"Expected this ID type to be ported to new Outliner tree-element design"); + BLI_assert_msg(0, "Expected this ID type to be ported to new Outliner tree-element design"); } } else if (ELEM(type, TSE_LIBRARY_OVERRIDE_BASE, TSE_LIBRARY_OVERRIDE)) { if (!te->type) { - BLI_assert(!"Expected override types to be ported to new Outliner tree-element design"); + BLI_assert_msg(0, + "Expected override types to be ported to new Outliner tree-element design"); } } else { diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc index 3059f8bfe0c..a17bf174a74 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc @@ -150,27 +150,25 @@ TreeElement *TreeDisplayOverrideLibrary::add_library_contents(Main &mainvar, } /* Create data-block list parent element on demand. */ - if (id != nullptr) { - TreeElement *ten; + TreeElement *ten; - if (filter_id_type) { - ten = tenlib; - } - else { - ten = outliner_add_element( - &space_outliner_, &tenlib->subtree, lbarray[a], nullptr, TSE_ID_BASE, 0); - ten->directdata = lbarray[a]; - ten->name = outliner_idcode_to_plural(GS(id->name)); - } + if (filter_id_type) { + ten = tenlib; + } + else { + ten = outliner_add_element( + &space_outliner_, &tenlib->subtree, lbarray[a], nullptr, TSE_ID_BASE, 0); + ten->directdata = lbarray[a]; + ten->name = outliner_idcode_to_plural(GS(id->name)); + } - for (ID *id : List<ID>(lbarray[a])) { - if (override_library_id_filter_poll(lib, id)) { - TreeElement *override_tree_element = outliner_add_element( - &space_outliner_, &ten->subtree, id, ten, TSE_LIBRARY_OVERRIDE_BASE, 0); + for (ID *id : List<ID>(lbarray[a])) { + if (override_library_id_filter_poll(lib, id)) { + TreeElement *override_tree_element = outliner_add_element( + &space_outliner_, &ten->subtree, id, ten, TSE_LIBRARY_OVERRIDE_BASE, 0); - if (BLI_listbase_is_empty(&override_tree_element->subtree)) { - outliner_free_tree_element(override_tree_element, &ten->subtree); - } + if (BLI_listbase_is_empty(&override_tree_element->subtree)) { + outliner_free_tree_element(override_tree_element, &ten->subtree); } } } diff --git a/source/blender/editors/space_outliner/tree/tree_element_id.cc b/source/blender/editors/space_outliner/tree/tree_element_id.cc index ce99b954204..7ff5a3285f1 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_id.cc @@ -85,7 +85,7 @@ TreeElementID *TreeElementID::createFromID(TreeElement &legacy_te, ID &id) return new TreeElementID(legacy_te, id); /* Deprecated */ case ID_IP: - BLI_assert(!"Outliner trying to build tree-element for deprecated ID type"); + BLI_assert_msg(0, "Outliner trying to build tree-element for deprecated ID type"); return nullptr; } diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index ac31e0e7c37..1239286d4da 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -260,7 +260,7 @@ static void load_data_init_from_operator(SeqLoadData *load_data, bContext *C, wm RNA_PROP_BEGIN (op->ptr, itemptr, prop) { char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0); BLI_strncpy(load_data->name, filename, sizeof(load_data->name)); - BLI_snprintf(load_data->path, sizeof(load_data->path), "%s%s", directory, filename); + BLI_join_dirfile(load_data->path, sizeof(load_data->path), directory, filename); MEM_freeN(filename); break; } diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 8371a634a78..cdbe5bc63ce 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -1556,7 +1556,7 @@ static void *sequencer_OCIO_transform_ibuf(const bContext *C, *r_format = GPU_RGB16F; } else { - BLI_assert(!"Incompatible number of channels for float buffer in sequencer"); + BLI_assert_msg(0, "Incompatible number of channels for float buffer in sequencer"); *r_format = GPU_RGBA16F; display_buffer = NULL; } diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 45c6931364d..4b26469aad3 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -2414,6 +2414,7 @@ static int sequencer_copy_exec(bContext *C, wmOperator *op) (LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_FREE_NO_MAIN)); seqbase_clipboard_frame = scene->r.cfra; + SEQ_clipboard_active_seq_name_store(scene); /* Remove anything that references the current scene. */ LISTBASE_FOREACH (Sequence *, seq, &seqbase_clipboard) { @@ -2504,6 +2505,10 @@ static int sequencer_paste_exec(bContext *C, wmOperator *op) BLI_movelisttolist(ed->seqbasep, &nseqbase); for (iseq = iseq_first; iseq; iseq = iseq->next) { + if (SEQ_clipboard_pasted_seq_was_active(iseq)) { + SEQ_select_active_set(scene, iseq); + } + /* Make sure, that pasted strips have unique names. */ SEQ_ensure_unique_name(iseq, scene); /* Translate after name has been changed, otherwise this will affect animdata of original @@ -2929,6 +2934,23 @@ void SEQUENCER_OT_change_path(struct wmOperatorType *ot) /** \name Export Subtitles Operator * \{ */ +/** Comparison function suitable to be used with BLI_listbase_sort(). */ +static int seq_cmp_time_startdisp_channel(const void *a, const void *b) +{ + Sequence *seq_a = (Sequence *)a; + Sequence *seq_b = (Sequence *)b; + + int seq_a_start = SEQ_transform_get_left_handle_frame(seq_a); + int seq_b_start = SEQ_transform_get_left_handle_frame(seq_b); + + /** If strips have the same start frame favor the one with a higher channel. **/ + if (seq_a_start == seq_b_start) { + return seq_a->machine > seq_b->machine; + } + + return (seq_a_start > seq_b_start); +} + static int sequencer_export_subtitles_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) @@ -2998,7 +3020,7 @@ static int sequencer_export_subtitles_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - BLI_listbase_sort(&text_seq, SEQ_time_cmp_time_startdisp); + BLI_listbase_sort(&text_seq, seq_cmp_time_startdisp_channel); /* Open and write file. */ file = BLI_fopen(filepath, "w"); diff --git a/source/blender/editors/space_sequencer/sequencer_scopes.c b/source/blender/editors/space_sequencer/sequencer_scopes.c index 8cc8b4a007b..5d857f62b47 100644 --- a/source/blender/editors/space_sequencer/sequencer_scopes.c +++ b/source/blender/editors/space_sequencer/sequencer_scopes.c @@ -624,8 +624,6 @@ static void vectorscope_put_cross(uchar r, uchar g, uchar b, char *tgt, int w, i { float rgb[3], yuv[3]; char *p; - int x = 0; - int y = 0; rgb[0] = (float)r / 255.0f; rgb[1] = (float)g / 255.0f; @@ -638,8 +636,8 @@ static void vectorscope_put_cross(uchar r, uchar g, uchar b, char *tgt, int w, i r = 255; } - for (y = -size; y <= size; y++) { - for (x = -size; x <= size; x++) { + for (int y = -size; y <= size; y++) { + for (int x = -size; x <= size; x++) { char *q = p + 4 * (y * w + x); q[0] = r; q[1] = g; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh index c9b73aabf96..680da9b6794 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh @@ -22,8 +22,8 @@ #include "BLI_float2.hh" #include "BLI_float3.hh" -struct Object; struct Collection; +struct Object; namespace blender::ed::spreadsheet { diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 44b17b8c391..e38c70afd0f 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -414,7 +414,6 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread Mesh *mesh = (Mesh *)object_orig->data; mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly); } - mesh_component.copy_vertex_group_names_from_object(*object_orig); } else if (object_orig->type == OB_POINTCLOUD) { PointCloud *pointcloud = (PointCloud *)object_orig->data; @@ -432,7 +431,6 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread BKE_mesh_wrapper_ensure_mdata(mesh); MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); mesh_component.replace(mesh, GeometryOwnershipType::ReadOnly); - mesh_component.copy_vertex_group_names_from_object(*object_eval); } else { if (BLI_listbase_count(&sspreadsheet->context_path) == 1) { diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh index d9e6d882c2a..19906d73e7f 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.hh @@ -23,9 +23,9 @@ #include "spreadsheet_dataset_layout.hh" struct ARegion; -struct uiBlock; struct View2D; struct bContext; +struct uiBlock; namespace blender::ed::spreadsheet { diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh b/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh index 647587ec8b0..9accd1d3d09 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_draw.hh @@ -18,9 +18,9 @@ #include "BLI_vector.hh" -struct uiBlock; -struct bContext; struct ARegion; +struct bContext; +struct uiBlock; namespace blender::ed::spreadsheet { diff --git a/source/blender/editors/space_text/text_format_lua.c b/source/blender/editors/space_text/text_format_lua.c index 16eb66624ce..0cd2d9baa0b 100644 --- a/source/blender/editors/space_text/text_format_lua.c +++ b/source/blender/editors/space_text/text_format_lua.c @@ -165,9 +165,9 @@ static char txtfmt_lua_format_identifier(const char *str) /* Keep aligned args for readability. */ /* clang-format off */ - if ((txtfmt_lua_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL; - } else if ((txtfmt_lua_find_keyword(str)) != -1) { fmt = FMT_TYPE_KEYWORD; - } else { fmt = FMT_TYPE_DEFAULT; + if (txtfmt_lua_find_specialvar(str) != -1) { fmt = FMT_TYPE_SPECIAL; + } else if (txtfmt_lua_find_keyword(str) != -1) { fmt = FMT_TYPE_KEYWORD; + } else { fmt = FMT_TYPE_DEFAULT; } /* clang-format on */ diff --git a/source/blender/editors/space_text/text_format_osl.c b/source/blender/editors/space_text/text_format_osl.c index 1a024779a83..97d9ec546ca 100644 --- a/source/blender/editors/space_text/text_format_osl.c +++ b/source/blender/editors/space_text/text_format_osl.c @@ -189,11 +189,11 @@ static char txtfmt_osl_format_identifier(const char *str) /* Keep aligned args for readability. */ /* clang-format off */ - if ((txtfmt_osl_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL; - } else if ((txtfmt_osl_find_builtinfunc(str)) != -1) { fmt = FMT_TYPE_KEYWORD; - } else if ((txtfmt_osl_find_reserved(str)) != -1) { fmt = FMT_TYPE_RESERVED; - } else if ((txtfmt_osl_find_preprocessor(str)) != -1) { fmt = FMT_TYPE_DIRECTIVE; - } else { fmt = FMT_TYPE_DEFAULT; + if (txtfmt_osl_find_specialvar(str) != -1) { fmt = FMT_TYPE_SPECIAL; + } else if (txtfmt_osl_find_builtinfunc(str) != -1) { fmt = FMT_TYPE_KEYWORD; + } else if (txtfmt_osl_find_reserved(str) != -1) { fmt = FMT_TYPE_RESERVED; + } else if (txtfmt_osl_find_preprocessor(str) != -1) { fmt = FMT_TYPE_DIRECTIVE; + } else { fmt = FMT_TYPE_DEFAULT; } /* clang-format on */ diff --git a/source/blender/editors/space_text/text_format_pov.c b/source/blender/editors/space_text/text_format_pov.c index 1200dda7533..ea3d0ec1478 100644 --- a/source/blender/editors/space_text/text_format_pov.c +++ b/source/blender/editors/space_text/text_format_pov.c @@ -762,11 +762,11 @@ static char txtfmt_pov_format_identifier(const char *str) /* Keep aligned args for readability. */ /* clang-format off */ - if ((txtfmt_pov_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL; - } else if ((txtfmt_pov_find_keyword(str)) != -1) { fmt = FMT_TYPE_KEYWORD; - } else if ((txtfmt_pov_find_reserved_keywords(str)) != -1) { fmt = FMT_TYPE_RESERVED; - } else if ((txtfmt_pov_find_reserved_builtins(str)) != -1) { fmt = FMT_TYPE_DIRECTIVE; - } else { fmt = FMT_TYPE_DEFAULT; + if (txtfmt_pov_find_specialvar(str) != -1) { fmt = FMT_TYPE_SPECIAL; + } else if (txtfmt_pov_find_keyword(str) != -1) { fmt = FMT_TYPE_KEYWORD; + } else if (txtfmt_pov_find_reserved_keywords(str) != -1) { fmt = FMT_TYPE_RESERVED; + } else if (txtfmt_pov_find_reserved_builtins(str) != -1) { fmt = FMT_TYPE_DIRECTIVE; + } else { fmt = FMT_TYPE_DEFAULT; } /* clang-format on */ diff --git a/source/blender/editors/space_text/text_format_pov_ini.c b/source/blender/editors/space_text/text_format_pov_ini.c index 1c6a93d2d7a..259ad02a6b7 100644 --- a/source/blender/editors/space_text/text_format_pov_ini.c +++ b/source/blender/editors/space_text/text_format_pov_ini.c @@ -347,10 +347,10 @@ static int txtfmt_ini_find_bool(const char *string) static char txtfmt_pov_ini_format_identifier(const char *str) { char fmt; - if ((txtfmt_ini_find_keyword(str)) != -1) { + if (txtfmt_ini_find_keyword(str) != -1) { fmt = FMT_TYPE_KEYWORD; } - else if ((txtfmt_ini_find_reserved(str)) != -1) { + else if (txtfmt_ini_find_reserved(str) != -1) { fmt = FMT_TYPE_RESERVED; } else { diff --git a/source/blender/editors/space_text/text_format_py.c b/source/blender/editors/space_text/text_format_py.c index 2717c0bf5b0..e2a01a8d85d 100644 --- a/source/blender/editors/space_text/text_format_py.c +++ b/source/blender/editors/space_text/text_format_py.c @@ -315,10 +315,10 @@ static char txtfmt_py_format_identifier(const char *str) /* Keep aligned args for readability. */ /* clang-format off */ - if ((txtfmt_py_find_specialvar(str)) != -1) { fmt = FMT_TYPE_SPECIAL; - } else if ((txtfmt_py_find_builtinfunc(str)) != -1) { fmt = FMT_TYPE_KEYWORD; - } else if ((txtfmt_py_find_decorator(str)) != -1) { fmt = FMT_TYPE_RESERVED; - } else { fmt = FMT_TYPE_DEFAULT; + if (txtfmt_py_find_specialvar(str) != -1) { fmt = FMT_TYPE_SPECIAL; + } else if (txtfmt_py_find_builtinfunc(str) != -1) { fmt = FMT_TYPE_KEYWORD; + } else if (txtfmt_py_find_decorator(str) != -1) { fmt = FMT_TYPE_RESERVED; + } else { fmt = FMT_TYPE_DEFAULT; } /* clang-format on */ diff --git a/source/blender/editors/space_text/text_undo.c b/source/blender/editors/space_text/text_undo.c index f55db8c3cc9..80af7d8c9f6 100644 --- a/source/blender/editors/space_text/text_undo.c +++ b/source/blender/editors/space_text/text_undo.c @@ -265,7 +265,7 @@ void ED_text_undosys_type(UndoType *ut) ut->step_foreach_ID_ref = text_undosys_foreach_ID_ref; - ut->flags = UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE; + ut->flags = UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE | UNDOTYPE_FLAG_DECODE_ACTIVE_STEP; ut->step_size = sizeof(TextUndoStep); } diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index 2e203d06b12..3428a738dde 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -39,6 +39,8 @@ #include "BLT_translation.h" +#include "BLI_array_utils.h" +#include "BLI_bitmap.h" #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -112,10 +114,94 @@ typedef struct { float ob_dims[3]; /* Floats only (treated as an array). */ TransformMedian ve_median, median; + bool tag_for_update; } TransformProperties; #define TRANSFORM_MEDIAN_ARRAY_LEN (sizeof(TransformMedian) / sizeof(float)) +static TransformProperties *v3d_transform_props_ensure(View3D *v3d); + +/* -------------------------------------------------------------------- */ +/** \name Edit Mesh Partial Updates + * \{ */ + +static void *editmesh_partial_update_begin_fn(struct bContext *UNUSED(C), + const struct uiBlockInteraction_Params *params, + void *arg1) +{ + const int retval_test = B_TRANSFORM_PANEL_MEDIAN; + if (BLI_array_findindex( + params->unique_retval_ids, params->unique_retval_ids_len, &retval_test) == -1) { + return NULL; + } + + BMEditMesh *em = arg1; + + int verts_mask_count = 0; + BMIter iter; + BMVert *eve; + int i; + + BLI_bitmap *verts_mask = BLI_BITMAP_NEW(em->bm->totvert, __func__); + BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) { + if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + continue; + } + BLI_BITMAP_ENABLE(verts_mask, i); + verts_mask_count += 1; + } + + BMPartialUpdate *bmpinfo = BM_mesh_partial_create_from_verts_group_single( + em->bm, + &(BMPartialUpdate_Params){ + .do_tessellate = true, + .do_normals = true, + }, + verts_mask, + verts_mask_count); + + MEM_freeN(verts_mask); + + return bmpinfo; +} + +static void editmesh_partial_update_end_fn(struct bContext *UNUSED(C), + const struct uiBlockInteraction_Params *UNUSED(params), + void *UNUSED(arg1), + void *user_data) +{ + BMPartialUpdate *bmpinfo = user_data; + if (bmpinfo == NULL) { + return; + } + BM_mesh_partial_destroy(bmpinfo); +} + +static void editmesh_partial_update_update_fn( + struct bContext *C, + const struct uiBlockInteraction_Params *UNUSED(params), + void *arg1, + void *user_data) +{ + BMPartialUpdate *bmpinfo = user_data; + if (bmpinfo == NULL) { + return; + } + + View3D *v3d = CTX_wm_view3d(C); + TransformProperties *tfp = v3d_transform_props_ensure(v3d); + if (tfp->tag_for_update == false) { + return; + } + tfp->tag_for_update = false; + + BMEditMesh *em = arg1; + + BKE_editmesh_looptri_and_normals_calc_with_partial(em, bmpinfo); +} + +/** \} */ + /* Helper function to compute a median changed value, * when the value should be clamped in [0.0, 1.0]. * Returns either 0.0, 1.0 (both can be applied directly), a positive scale factor @@ -840,6 +926,20 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float } UI_block_align_end(block); + + if (ob->type == OB_MESH) { + Mesh *me = ob->data; + BMEditMesh *em = me->edit_mesh; + if (em != NULL) { + UI_block_interaction_set(block, + &(uiBlockInteraction_CallbackData){ + .begin_fn = editmesh_partial_update_begin_fn, + .end_fn = editmesh_partial_update_end_fn, + .update_fn = editmesh_partial_update_update_fn, + .arg1 = em, + }); + } + } } else { /* apply */ memcpy(&ve_median_basis, &tfp->ve_median, sizeof(tfp->ve_median)); @@ -927,9 +1027,8 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float } if (apply_vcos) { - /* TODO: use the #BKE_editmesh_looptri_and_normals_calc_with_partial - * This requires begin/end states for UI interaction (which currently aren't supported). */ - BKE_editmesh_looptri_and_normals_calc(em); + /* Tell the update callback to run. */ + tfp->tag_for_update = true; } /* Edges */ @@ -1211,7 +1310,9 @@ static void view3d_panel_vgroup(const bContext *C, Panel *panel) vgroup_validmap = BKE_object_defgroup_subset_from_select_type( ob, subset_type, &vgroup_tot, &subset_count); - for (i = 0, dg = ob->defbase.first; dg; i++, dg = dg->next) { + const ListBase *defbase = BKE_object_defgroup_list(ob); + + for (i = 0, dg = defbase->first; dg; i++, dg = dg->next) { bool locked = (dg->flag & DG_LOCK_WEIGHT) != 0; if (vgroup_validmap[i]) { MDeformWeight *dw = BKE_defvert_find_index(dv, i); @@ -1237,7 +1338,7 @@ static void view3d_panel_vgroup(const bContext *C, Panel *panel) but_ptr = UI_but_operator_ptr_get(but); RNA_int_set(but_ptr, "weight_group", i); UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT); - if (ob->actdef != i + 1) { + if (BKE_object_defgroup_active_index_get(ob) != i + 1) { UI_but_flag_enable(but, UI_BUT_INACTIVE); } xco += x; diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index ea9d9a8c010..c97ba7ba7e9 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1404,7 +1404,7 @@ static void draw_selected_name( /* color depends on whether there is a keyframe */ if (id_frame_has_keyframe( - (ID *)ob, /* BKE_scene_frame_get(scene) */ (float)cfra, ANIMFILTER_KEYS_LOCAL)) { + (ID *)ob, /* BKE_scene_ctime_get(scene) */ (float)cfra, ANIMFILTER_KEYS_LOCAL)) { UI_FontThemeColor(font_id, TH_TIME_KEYFRAME); } else if (ED_gpencil_has_keyframe_v3d(scene, ob, cfra)) { diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 210dd7311a4..ecf43c734e2 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -1378,7 +1378,7 @@ static bool view3d_lasso_select(bContext *C, changed = do_lasso_select_meta(vc, mcoords, mcoords_len, sel_op); break; default: - BLI_assert(!"lasso select on incorrect object type"); + BLI_assert_msg(0, "lasso select on incorrect object type"); break; } @@ -3604,7 +3604,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op) } break; default: - BLI_assert(!"box select on incorrect object type"); + BLI_assert_msg(0, "box select on incorrect object type"); break; } changed_multi |= changed; diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c index a19e92f229a..4482e5897ca 100644 --- a/source/blender/editors/space_view3d/view3d_snap.c +++ b/source/blender/editors/space_view3d/view3d_snap.c @@ -511,47 +511,47 @@ static int snap_selected_to_location(bContext *C, for (int ob_index = 0; ob_index < objects_len; ob_index++) { Object *ob = objects[ob_index]; + if (ob->parent && BKE_object_flag_test_recursive(ob->parent, OB_DONE)) { + continue; + } - if ((ob->parent && BKE_object_flag_test_recursive(ob->parent, OB_DONE)) == 0) { - - float cursor_parent[3]; /* parent-relative */ - - if (use_offset) { - add_v3_v3v3(cursor_parent, ob->obmat[3], offset_global); - } - else { - copy_v3_v3(cursor_parent, snap_target_global); - } + float cursor_parent[3]; /* parent-relative */ - sub_v3_v3(cursor_parent, ob->obmat[3]); + if (use_offset) { + add_v3_v3v3(cursor_parent, ob->obmat[3], offset_global); + } + else { + copy_v3_v3(cursor_parent, snap_target_global); + } - if (ob->parent) { - float originmat[3][3], parentmat[4][4]; - /* Use the evaluated object here because sometimes - * `ob->parent->runtime.curve_cache` is required. */ - BKE_scene_graph_evaluated_ensure(depsgraph, bmain); - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + sub_v3_v3(cursor_parent, ob->obmat[3]); - BKE_object_get_parent_matrix(ob_eval, ob_eval->parent, parentmat); - mul_m3_m4m4(originmat, parentmat, ob->parentinv); - invert_m3_m3(imat, originmat); - mul_m3_v3(imat, cursor_parent); - } - if ((ob->protectflag & OB_LOCK_LOCX) == 0) { - ob->loc[0] += cursor_parent[0]; - } - if ((ob->protectflag & OB_LOCK_LOCY) == 0) { - ob->loc[1] += cursor_parent[1]; - } - if ((ob->protectflag & OB_LOCK_LOCZ) == 0) { - ob->loc[2] += cursor_parent[2]; - } + if (ob->parent) { + float originmat[3][3], parentmat[4][4]; + /* Use the evaluated object here because sometimes + * `ob->parent->runtime.curve_cache` is required. */ + BKE_scene_graph_evaluated_ensure(depsgraph, bmain); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + + BKE_object_get_parent_matrix(ob_eval, ob_eval->parent, parentmat); + mul_m3_m4m4(originmat, parentmat, ob->parentinv); + invert_m3_m3(imat, originmat); + mul_m3_v3(imat, cursor_parent); + } + if ((ob->protectflag & OB_LOCK_LOCX) == 0) { + ob->loc[0] += cursor_parent[0]; + } + if ((ob->protectflag & OB_LOCK_LOCY) == 0) { + ob->loc[1] += cursor_parent[1]; + } + if ((ob->protectflag & OB_LOCK_LOCZ) == 0) { + ob->loc[2] += cursor_parent[2]; + } - /* auto-keyframing */ - ED_autokeyframe_object(C, scene, ob, ks); + /* auto-keyframing */ + ED_autokeyframe_object(C, scene, ob, ks); - DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); - } + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); } if (objects) { diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index d34cc6f424f..efcf7d587e1 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -78,7 +78,7 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2]); bool transdata_check_local_islands(TransInfo *t, short around) { - return ((around == V3D_AROUND_LOCAL_ORIGINS) && ((ELEM(t->obedit_type, OB_MESH, OB_GPENCIL)))); + return ((around == V3D_AROUND_LOCAL_ORIGINS) && (ELEM(t->obedit_type, OB_MESH, OB_GPENCIL))); } /* ************************** SPACE DEPENDENT CODE **************************** */ diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index c1ee6edfef6..383f9870714 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -2067,6 +2067,27 @@ static void tc_mesh_transdata_mirror_apply(TransDataContainer *tc) } } +static bool tc_mesh_is_deform_only_update(TransInfo *t, TransDataContainer *tc) +{ + if (tc->custom.type.data && + ((struct TransCustomDataMesh *)tc->custom.type.data)->cd_layer_correct) { + return false; + } + + Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(t->depsgraph, (ID *)tc->obedit->data); + Mesh *mesh_eval_cage = me_eval->edit_mesh->mesh_eval_cage; + Mesh *mesh_eval_final = me_eval->edit_mesh->mesh_eval_final; + if (mesh_eval_cage && !mesh_eval_cage->runtime.is_original) { + return false; + } + if (mesh_eval_final && mesh_eval_final != mesh_eval_cage && + !mesh_eval_final->runtime.is_original) { + return false; + } + + return me_eval->runtime.deformed_only; +} + void recalcData_mesh(TransInfo *t) { bool is_canceling = t->state == TRANS_CANCEL; @@ -2094,7 +2115,10 @@ void recalcData_mesh(TransInfo *t) tc_mesh_partial_types_calc(t, &partial_state); FOREACH_TRANS_DATA_CONTAINER (t, tc) { - DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY); + const bool is_deform_only = tc_mesh_is_deform_only_update(t, tc); + + DEG_id_tag_update(tc->obedit->data, + is_deform_only ? ID_RECALC_GEOMETRY_DEFORM : ID_RECALC_GEOMETRY); tc_mesh_partial_update(t, tc, &partial_state); } diff --git a/source/blender/editors/transform/transform_convert_mesh_edge.c b/source/blender/editors/transform/transform_convert_mesh_edge.c index 3b1191a3401..2db3e259153 100644 --- a/source/blender/editors/transform/transform_convert_mesh_edge.c +++ b/source/blender/editors/transform/transform_convert_mesh_edge.c @@ -28,6 +28,7 @@ #include "BLI_math.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_mesh.h" diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c index d91a2a8be4b..61397b6ef4b 100644 --- a/source/blender/editors/transform/transform_convert_mesh_uv.c +++ b/source/blender/editors/transform/transform_convert_mesh_uv.c @@ -30,6 +30,7 @@ #include "BLI_math.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "BKE_mesh_mapping.h" diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index c217478bd04..ee6cb391fdc 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -153,7 +153,7 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob) if (t->mode != TFM_DUMMY && ob->rigidbody_object) { float rot[3][3], scale[3]; - float ctime = BKE_scene_frame_get(scene); + float ctime = BKE_scene_ctime_get(scene); /* only use rigid body transform if simulation is running, * avoids problems with initial setup of rigid bodies */ @@ -978,7 +978,7 @@ void special_aftertrans_update__object(bContext *C, TransInfo *t) /* restore rigid body transform */ if (ob->rigidbody_object && canceled) { - float ctime = BKE_scene_frame_get(t->scene); + float ctime = BKE_scene_ctime_get(t->scene); if (BKE_rigidbody_check_sim_running(t->scene->rigidbody_world, ctime)) { BKE_rigidbody_aftertrans_update(ob, td->ext->oloc, diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index a6f5aba5a1d..17512c79d03 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -262,8 +262,16 @@ static void free_transform_custom_data(TransCustomData *custom_data) /* Canceled, need to update the strips display. */ static void seq_transform_cancel(TransInfo *t, SeqCollection *transformed_strips) { + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false)); + Sequence *seq; SEQ_ITERATOR_FOREACH (seq, transformed_strips) { + /* Handle pre-existing overlapping strips even when operator is canceled. + * This is necessary for SEQUENCER_OT_duplicate_move macro for example. */ + if (SEQ_transform_test_overlap(seqbase, seq)) { + SEQ_transform_seqbase_shuffle(seqbase, seq, t->scene); + } + SEQ_time_update_sequence_bounds(t->scene, seq); } } diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 575d9c0828e..aaac8e21cb9 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -910,7 +910,7 @@ void calculateCenterCursor2D(TransInfo *t, float r_center[2]) BKE_mask_coord_from_movieclip(space_clip->clip, &space_clip->user, co, cursor); } else { - BLI_assert(!"Shall not happen"); + BLI_assert_msg(0, "Shall not happen"); } r_center[0] = co[0] * t->aspect[0]; diff --git a/source/blender/editors/transform/transform_mode_edge_seq_slide.c b/source/blender/editors/transform/transform_mode_edge_seq_slide.c index fe853440c96..a8f7fc43b5e 100644 --- a/source/blender/editors/transform/transform_mode_edge_seq_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_seq_slide.c @@ -129,7 +129,7 @@ static void applySeqSlide(TransInfo *t, const int UNUSED(mval[2])) transform_convert_sequencer_channel_clamp(t, values_final); if (t->con.mode & CON_APPLY) { - t->con.applyVec(t, NULL, NULL, t->values, values_final); + t->con.applyVec(t, NULL, NULL, values_final, values_final); } } diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c index bdd074666a2..066a2853dc7 100644 --- a/source/blender/editors/transform/transform_mode_edge_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_slide.c @@ -107,7 +107,7 @@ static TransDataContainer *edge_slide_container_first_ok(TransInfo *t) return tc; } } - BLI_assert(!"Should never happen, at least one EdgeSlideData should be valid"); + BLI_assert_msg(0, "Should never happen, at least one EdgeSlideData should be valid"); return NULL; } diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c index d9395e5e960..d0b730383d5 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.c +++ b/source/blender/editors/transform/transform_snap_sequencer.c @@ -236,6 +236,11 @@ void transform_snap_sequencer_data_free(TransSeqSnapData *data) bool transform_snap_sequencer_calc(TransInfo *t) { + /* Prevent snapping when constrained to Y axis. */ + if (t->con.mode & CON_APPLY && t->con.mode & CON_AXIS1) { + return false; + } + const TransSeqSnapData *snap_data = t->tsnap.seq_context; int best_dist = MAXFRAME, best_target_frame = 0, best_source_frame = 0; diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 00efdb87fea..3e0029156c1 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -50,6 +50,7 @@ #include "BLO_blend_validate.h" +#include "ED_asset.h" #include "ED_gpencil.h" #include "ED_object.h" #include "ED_outliner.h" @@ -268,6 +269,8 @@ static void ed_undo_step_post(bContext *C, WM_toolsystem_refresh_active(C); WM_toolsystem_refresh_screen_all(bmain); + ED_assetlist_storage_tag_main_data_dirty(); + if (CLOG_CHECK(&LOG, 1)) { BKE_undosys_print(wm->undo_stack); } @@ -321,7 +324,7 @@ static int ed_undo_step_by_name(bContext *C, const char *undo_name, ReportList * /* FIXME: See comments in `ed_undo_step_direction`. */ if (ED_gpencil_session_active()) { - BLI_assert(!"Not implemented currently."); + BLI_assert_msg(0, "Not implemented currently."); } wmWindowManager *wm = CTX_wm_manager(C); @@ -369,7 +372,7 @@ static int ed_undo_step_by_index(bContext *C, const int undo_index, ReportList * /* FIXME: See comments in `ed_undo_step_direction`. */ if (ED_gpencil_session_active()) { - BLI_assert(!"Not implemented currently."); + BLI_assert_msg(0, "Not implemented currently."); } wmWindowManager *wm = CTX_wm_manager(C); @@ -692,7 +695,7 @@ int ED_undo_operator_repeat(bContext *C, wmOperator *op) CTX_wm_region_set(C, region_win); } - if ((WM_operator_repeat_check(C, op)) && (WM_operator_poll(C, op->type)) && + if (WM_operator_repeat_check(C, op) && WM_operator_poll(C, op->type) && /* NOTE: undo/redo can't run if there are jobs active, * check for screen jobs only so jobs like material/texture/world preview * (which copy their data), won't stop redo, see T29579], diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 7bbdc58474f..73f328f85d7 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -46,6 +46,7 @@ #include "DEG_depsgraph.h" #include "ED_armature.h" +#include "ED_asset.h" #include "ED_image.h" #include "ED_mesh.h" #include "ED_object.h" @@ -169,6 +170,8 @@ void ED_editors_init(bContext *C) ED_space_image_paint_update(bmain, wm, scene); } + ED_assetlist_storage_tag_main_data_dirty(); + SWAP(int, reports->flag, reports_flag_prev); wm->op_undo_depth--; } diff --git a/source/blender/editors/util/ed_util_ops.cc b/source/blender/editors/util/ed_util_ops.cc index 462f7768f81..7d32d252718 100644 --- a/source/blender/editors/util/ed_util_ops.cc +++ b/source/blender/editors/util/ed_util_ops.cc @@ -36,6 +36,7 @@ #include "BLT_translation.h" +#include "ED_asset.h" #include "ED_render.h" #include "ED_undo.h" #include "ED_util.h" @@ -131,9 +132,11 @@ static int lib_id_generate_preview_exec(bContext *C, wmOperator *UNUSED(op)) if (preview) { BKE_previewimg_clear(preview); } + UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true); WM_event_add_notifier(C, NC_ASSET | NA_EDITED, nullptr); + ED_assetlist_storage_tag_main_data_dirty(); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/util/select_utils.c b/source/blender/editors/util/select_utils.c index 4e8cf1e92e6..5681edd2f5c 100644 --- a/source/blender/editors/util/select_utils.c +++ b/source/blender/editors/util/select_utils.c @@ -41,7 +41,7 @@ int ED_select_op_action(const eSelectOp sel_op, const bool is_select, const bool case SEL_OP_XOR: return (is_select && is_inside) ? 0 : ((!is_select && is_inside) ? 1 : -1); } - BLI_assert(!"invalid sel_op"); + BLI_assert_msg(0, "invalid sel_op"); return -1; } /** @@ -67,7 +67,7 @@ int ED_select_op_action_deselected(const eSelectOp sel_op, case SEL_OP_XOR: return (is_select && is_inside) ? 0 : ((!is_select && is_inside) ? 1 : -1); } - BLI_assert(!"invalid sel_op"); + BLI_assert_msg(0, "invalid sel_op"); return -1; } diff --git a/source/blender/editors/uvedit/uvedit_islands.c b/source/blender/editors/uvedit/uvedit_islands.c index 93948b5ae1b..56bcbc63de1 100644 --- a/source/blender/editors/uvedit/uvedit_islands.c +++ b/source/blender/editors/uvedit/uvedit_islands.c @@ -36,6 +36,7 @@ #include "BLI_math.h" #include "BLI_rect.h" +#include "BKE_customdata.h" #include "BKE_editmesh.h" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 28853bcdedf..535a0e00347 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -1928,6 +1928,11 @@ static StitchState *stitch_init(bContext *C, state->obedit = obedit; state->em = em; + /* Workaround for sync-select & face-select mode which implies all selected faces are detached, + * for stitch this isn't useful behavior, see T86924. */ + const int selectmode_orig = scene->toolsettings->selectmode; + scene->toolsettings->selectmode = SCE_SELECT_VERTEX; + /* in uv synch selection, all uv's are visible */ if (ts->uv_flag & UV_SYNC_SELECTION) { state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, false, true, true); @@ -1935,6 +1940,9 @@ static StitchState *stitch_init(bContext *C, else { state->element_map = BM_uv_element_map_create(state->em->bm, scene, true, false, true, true); } + + scene->toolsettings->selectmode = selectmode_orig; + if (!state->element_map) { state_delete(state); return NULL; @@ -1989,7 +1997,7 @@ static StitchState *stitch_init(bContext *C, /* Now, on to generate our uv connectivity data */ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (!(ts->uv_flag & UV_SYNC_SELECTION) && - ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || !BM_elem_flag_test(efa, BM_ELEM_SELECT))) { + (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) || !BM_elem_flag_test(efa, BM_ELEM_SELECT))) { continue; } @@ -2172,8 +2180,8 @@ static StitchState *stitch_init(bContext *C, "uv_stitch_selection_stack"); BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!(ts->uv_flag & UV_SYNC_SELECTION) && ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || - !BM_elem_flag_test(efa, BM_ELEM_SELECT))) { + if (!(ts->uv_flag & UV_SYNC_SELECTION) && + (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) || !BM_elem_flag_test(efa, BM_ELEM_SELECT))) { continue; } diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 0a668144f09..84be69b7c4f 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -315,7 +315,7 @@ static ParamHandle *construct_param_handle(const Scene *scene, BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { - if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || + if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) || (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { continue; } @@ -404,7 +404,7 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene, BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { - if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || + if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) || (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { continue; } diff --git a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp index cd0059f3c21..afb23690a84 100644 --- a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp +++ b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp @@ -2285,7 +2285,7 @@ struct less_SVertex2D { Vec3r A = x->point2D(); Vec3r B = y->point2D(); for (unsigned int i = 0; i < 3; i++) { - if ((fabs(A[i] - B[i])) < epsilon) { + if (fabs(A[i] - B[i]) < epsilon) { continue; } if (A[i] < B[i]) { diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 247b0b3f57b..1d4370ed3a9 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -575,9 +575,9 @@ BLI_INLINE int lineart_LineIntersectTest2d( } struct Depsgraph; -struct Scene; -struct LineartRenderBuffer; struct LineartGpencilModifierData; +struct LineartRenderBuffer; +struct Scene; void MOD_lineart_destroy_render_data(struct LineartGpencilModifierData *lmd); @@ -602,8 +602,8 @@ LineartBoundingArea *MOD_lineart_get_parent_bounding_area(LineartRenderBuffer *r LineartBoundingArea *MOD_lineart_get_bounding_area(LineartRenderBuffer *rb, double x, double y); -struct bGPDlayer; struct bGPDframe; +struct bGPDlayer; void MOD_lineart_gpencil_generate(LineartCache *cache, struct Depsgraph *depsgraph, diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index c1cca629992..30ac08c3f9d 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -1697,7 +1697,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu } if (rb->remove_doubles) { - BMEditMesh *em = BKE_editmesh_create(bm, false); + BMEditMesh *em = BKE_editmesh_create(bm); BMOperator findop, weldop; /* See bmesh_opdefines.c and bmesh_operators.c for op names and argument formatting. */ @@ -3443,9 +3443,9 @@ static bool lineart_bounding_area_triangle_intersect(LineartRenderBuffer *fb, return true; } - if ((lineart_bounding_area_edge_intersect(fb, FBC1, FBC2, ba)) || - (lineart_bounding_area_edge_intersect(fb, FBC2, FBC3, ba)) || - (lineart_bounding_area_edge_intersect(fb, FBC3, FBC1, ba))) { + if (lineart_bounding_area_edge_intersect(fb, FBC1, FBC2, ba) || + lineart_bounding_area_edge_intersect(fb, FBC2, FBC3, ba) || + lineart_bounding_area_edge_intersect(fb, FBC3, FBC1, ba)) { return true; } @@ -4218,9 +4218,6 @@ static void lineart_gpencil_generate(LineartCache *cache, /* (!orig_col && !orig_ob) means the whole scene is selected. */ - float mat[4][4]; - unit_m4(mat); - int enabled_types = cache->rb_edge_types; bool invert_input = modifier_flags & LRT_GPENCIL_INVERT_SOURCE_VGROUP; bool match_output = modifier_flags & LRT_GPENCIL_MATCH_OUTPUT_VGROUP; @@ -4272,29 +4269,20 @@ static void lineart_gpencil_generate(LineartCache *cache, /* Preserved: If we ever do asynchronous generation, this picked flag should be set here. */ // ec->picked = 1; - int array_idx = 0; - int count = MOD_lineart_chain_count(ec); + const int count = MOD_lineart_chain_count(ec); bGPDstroke *gps = BKE_gpencil_stroke_add(gpf, color_idx, count, thickness, false); - float *stroke_data = MEM_callocN(sizeof(float) * count * GP_PRIM_DATABUF_SIZE, - "line art add stroke"); - - LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) { - stroke_data[array_idx] = eci->gpos[0]; - stroke_data[array_idx + 1] = eci->gpos[1]; - stroke_data[array_idx + 2] = eci->gpos[2]; - mul_m4_v3(gp_obmat_inverse, &stroke_data[array_idx]); - stroke_data[array_idx + 3] = 1; /* thickness. */ - stroke_data[array_idx + 4] = opacity; /* hardness?. */ - array_idx += 5; + int i; + LISTBASE_FOREACH_INDEX (LineartEdgeChainItem *, eci, &ec->chain, i) { + bGPDspoint *point = &gps->points[i]; + mul_v3_m4v3(&point->x, gp_obmat_inverse, eci->gpos); + point->pressure = 1.0f; + point->strength = opacity; } - BKE_gpencil_stroke_add_points(gps, stroke_data, count, mat); BKE_gpencil_dvert_ensure(gps); gps->mat_nr = max_ii(material_nr, 0); - MEM_freeN(stroke_data); - if (source_vgname && vgname) { Object *eval_ob = DEG_get_evaluated_object(depsgraph, ec->object_ref); int gpdg = -1; @@ -4303,7 +4291,7 @@ static void lineart_gpencil_generate(LineartCache *cache, int dindex = 0; Mesh *me = (Mesh *)eval_ob->data; if (me->dvert) { - LISTBASE_FOREACH (bDeformGroup *, db, &eval_ob->defbase) { + LISTBASE_FOREACH (bDeformGroup *, db, &me->vertex_group_names) { if ((!source_vgname) || strstr(db->name, source_vgname) == db->name) { if (match_output) { gpdg = BKE_object_defgroup_name_index(gpencil_object, db->name); diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h index 9d109320f09..70ff4a373dd 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h @@ -33,10 +33,10 @@ #include <math.h> #include <string.h> -struct LineartStaticMemPool; -struct LineartStaticMemPoolNode; struct LineartEdge; struct LineartRenderBuffer; +struct LineartStaticMemPool; +struct LineartStaticMemPoolNode; void *lineart_list_append_pointer_pool(ListBase *h, struct LineartStaticMemPool *smp, void *data); void *lineart_list_append_pointer_pool_sized(ListBase *h, diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc index 1293cc0953d..4bb13d01c2d 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.cc +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -609,7 +609,13 @@ GPUOffScreen *GPU_offscreen_create( } if ((depth && !ofs->depth) || !ofs->color) { - BLI_snprintf(err_out, 256, "GPUTexture: Texture allocation failed."); + const char error[] = "GPUTexture: Texture allocation failed."; + if (err_out) { + BLI_snprintf(err_out, 256, error); + } + else { + fprintf(stderr, error); + } GPU_offscreen_free(ofs); return nullptr; } diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index dad03ab22e5..585cb296bac 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -88,7 +88,7 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType name = outnode->name; input = outnode->inputs.first; - if ((STR_ELEM(name, "set_value", "set_rgb", "set_rgba")) && (input->type == type)) { + if (STR_ELEM(name, "set_value", "set_rgb", "set_rgba") && (input->type == type)) { input = MEM_dupallocN(outnode->inputs.first); if (input->link) { input->link->users++; @@ -174,7 +174,7 @@ static const char *gpu_uniform_set_function_from_type(eNodeSocketDatatype type) case SOCK_RGBA: return "set_rgba"; default: - BLI_assert(!"No gpu function for non-supported eNodeSocketDatatype"); + BLI_assert_msg(0, "No gpu function for non-supported eNodeSocketDatatype"); return NULL; } } diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index f7a4d672b02..6564cbda694 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -154,7 +154,7 @@ void Texture::attach_to(FrameBuffer *fb, GPUAttachmentType type) return; } } - BLI_assert(!"GPU: Error: Texture: Not enough attachment"); + BLI_assert_msg(0, "GPU: Error: Texture: Not enough attachment"); } void Texture::detach_from(FrameBuffer *fb) @@ -166,7 +166,7 @@ void Texture::detach_from(FrameBuffer *fb) return; } } - BLI_assert(!"GPU: Error: Texture: Framebuffer is not attached"); + BLI_assert_msg(0, "GPU: Error: Texture: Framebuffer is not attached"); } void Texture::update(eGPUDataFormat format, const void *data) diff --git a/source/blender/gpu/intern/gpu_uniform_buffer.cc b/source/blender/gpu/intern/gpu_uniform_buffer.cc index 3edb090d81c..3a9269d1753 100644 --- a/source/blender/gpu/intern/gpu_uniform_buffer.cc +++ b/source/blender/gpu/intern/gpu_uniform_buffer.cc @@ -114,11 +114,11 @@ static void buffer_from_list_inputs_sort(ListBase *inputs) if (input->type == GPU_MAT3) { /* Alignment for mat3 is not handled currently, so not supported */ - BLI_assert(!"mat3 not supported in UBO"); + BLI_assert_msg(0, "mat3 not supported in UBO"); continue; } if (input->type > MAX_UBO_GPU_TYPE) { - BLI_assert(!"GPU type not supported in UBO"); + BLI_assert_msg(0, "GPU type not supported in UBO"); continue; } diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index ddf31b6ffa7..96bf1ec40b0 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -248,7 +248,7 @@ void *GPU_viewport_engine_data_create(GPUViewport *viewport, void *engine_type) } } - BLI_assert(!"Too many draw engines enabled at the same time"); + BLI_assert_msg(0, "Too many draw engines enabled at the same time"); return NULL; } diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index f90c37e5c50..42b85da1f93 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -90,7 +90,7 @@ void GLBackend::platform_init() device |= GPU_DEVICE_INTEL_UHD; } } - else if ((strstr(renderer, "Mesa DRI R")) || + else if (strstr(renderer, "Mesa DRI R") || (strstr(renderer, "Radeon") && strstr(vendor, "X.Org")) || (strstr(renderer, "AMD") && strstr(vendor, "X.Org")) || (strstr(renderer, "Gallium ") && strstr(renderer, " on ATI ")) || diff --git a/source/blender/gpu/opengl/gl_framebuffer.cc b/source/blender/gpu/opengl/gl_framebuffer.cc index e87b22985bd..8da114d9270 100644 --- a/source/blender/gpu/opengl/gl_framebuffer.cc +++ b/source/blender/gpu/opengl/gl_framebuffer.cc @@ -268,7 +268,7 @@ void GLFrameBuffer::bind(bool enabled_srgb) } if (context_ != GLContext::get()) { - BLI_assert(!"Trying to use the same frame-buffer in multiple context"); + BLI_assert_msg(0, "Trying to use the same frame-buffer in multiple context"); return; } @@ -379,7 +379,7 @@ void GLFrameBuffer::clear_attachment(GPUAttachmentType type, glClearBufferfv(GL_DEPTH, 0, &depth); } else { - BLI_assert(!"Unhandled data format"); + BLI_assert_msg(0, "Unhandled data format"); } } else { @@ -395,7 +395,7 @@ void GLFrameBuffer::clear_attachment(GPUAttachmentType type, glClearBufferiv(GL_COLOR, slot, (GLint *)clear_value); break; default: - BLI_assert(!"Unhandled data format"); + BLI_assert_msg(0, "Unhandled data format"); break; } } diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl index 9ce2a1be015..aae7f641af8 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl @@ -18,6 +18,7 @@ in vec2 P2; in vec2 P3; in ivec4 colid_doarrow; in ivec2 domuted; +in float dim_factor; uniform vec4 colors[6]; @@ -39,6 +40,7 @@ uniform vec2 bezierPts[4]; uniform vec4 colors[3]; uniform bool doArrow; uniform bool doMuted; +uniform float dim_factor; # define colShadow colors[0] # define colStart colors[1] @@ -98,6 +100,8 @@ void main(void) } } + finalColor[3] *= dim_factor; + /* Expand into a line */ gl_Position.xy += exp_axis * expandSize * expand_dist; diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 2cfce7b1ba0..d527aca184c 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -69,8 +69,8 @@ extern "C" { * \attention defined in ??? */ struct ImBuf; -struct rcti; struct rctf; +struct rcti; /** * diff --git a/source/blender/imbuf/intern/bmp.c b/source/blender/imbuf/intern/bmp.c index ad72f373d12..70bb70ec4fa 100644 --- a/source/blender/imbuf/intern/bmp.c +++ b/source/blender/imbuf/intern/bmp.c @@ -395,9 +395,8 @@ bool imb_savebmp(ImBuf *ibuf, const char *filepath, int UNUSED(flags)) } } } - if (ofile) { - fflush(ofile); - fclose(ofile); - } + + fflush(ofile); + fclose(ofile); return 1; } diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 71e513fb405..2cc44ebc67b 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -1542,7 +1542,7 @@ static void display_buffer_apply_get_linear_buffer(DisplayBufferThread *handle, rgba_uchar_to_float(fp, cp); } else { - BLI_assert(!"Buffers of 3 or 4 channels are only supported here"); + BLI_assert_msg(0, "Buffers of 3 or 4 channels are only supported here"); } } @@ -3437,7 +3437,7 @@ static void partial_buffer_update_rect(ImBuf *ibuf, pixel[0] = linear_buffer[linear_index]; } else { - BLI_assert(!"Unsupported number of channels in partial buffer update"); + BLI_assert_msg(0, "Unsupported number of channels in partial buffer update"); } } else if (byte_buffer) { diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 71137a408d2..27195b294d6 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -357,7 +357,7 @@ int IMB_proxy_size_to_array_index(IMB_Proxy_Size pr_size) case IMB_PROXY_100: return 3; default: - BLI_assert(!"Unhandled proxy size enum!"); + BLI_assert_msg(0, "Unhandled proxy size enum!"); return -1; } } @@ -376,7 +376,7 @@ int IMB_timecode_to_array_index(IMB_Timecode_Type tc) case IMB_TC_RECORD_RUN_NO_GAPS: return 3; default: - BLI_assert(!"Unhandled timecode type enum!"); + BLI_assert_msg(0, "Unhandled timecode type enum!"); return -1; } } diff --git a/source/blender/imbuf/intern/scaling.c b/source/blender/imbuf/intern/scaling.c index 4a964c64917..757ec5f4b41 100644 --- a/source/blender/imbuf/intern/scaling.c +++ b/source/blender/imbuf/intern/scaling.c @@ -1195,22 +1195,9 @@ static ImBuf *scaleupx(struct ImBuf *ibuf, int newx) { uchar *rect, *_newrect = NULL, *newrect; float *rectf, *_newrectf = NULL, *newrectf; - float sample, add; - float val_a, nval_a, diff_a; - float val_b, nval_b, diff_b; - float val_g, nval_g, diff_g; - float val_r, nval_r, diff_r; - float val_af, nval_af, diff_af; - float val_bf, nval_bf, diff_bf; - float val_gf, nval_gf, diff_gf; - float val_rf, nval_rf, diff_rf; int x, y; bool do_rect = false, do_float = false; - val_a = nval_a = diff_a = val_b = nval_b = diff_b = 0; - val_g = nval_g = diff_g = val_r = nval_r = diff_r = 0; - val_af = nval_af = diff_af = val_bf = nval_bf = diff_bf = 0; - val_gf = nval_gf = diff_gf = val_rf = nval_rf = diff_rf = 0; if (ibuf == NULL) { return NULL; } @@ -1236,119 +1223,158 @@ static ImBuf *scaleupx(struct ImBuf *ibuf, int newx) } } - add = (ibuf->x - 1.001) / (newx - 1.0); - rect = (uchar *)ibuf->rect; rectf = (float *)ibuf->rect_float; newrect = _newrect; newrectf = _newrectf; - for (y = ibuf->y; y > 0; y--) { - - sample = 0; - + /* Special case, copy all columns, needed since the scaling logic assumes there is at least + * two rows to interpolate between causing out of bounds read for 1px images, see T70356. */ + if (UNLIKELY(ibuf->x == 1)) { if (do_rect) { - val_a = rect[0]; - nval_a = rect[4]; - diff_a = nval_a - val_a; - val_a += 0.5f; - - val_b = rect[1]; - nval_b = rect[5]; - diff_b = nval_b - val_b; - val_b += 0.5f; - - val_g = rect[2]; - nval_g = rect[6]; - diff_g = nval_g - val_g; - val_g += 0.5f; - - val_r = rect[3]; - nval_r = rect[7]; - diff_r = nval_r - val_r; - val_r += 0.5f; - - rect += 8; + for (y = ibuf->y; y > 0; y--) { + for (x = newx; x > 0; x--) { + memcpy(newrect, rect, sizeof(char[4])); + newrect += 4; + } + rect += 4; + } } if (do_float) { - val_af = rectf[0]; - nval_af = rectf[4]; - diff_af = nval_af - val_af; + for (y = ibuf->y; y > 0; y--) { + for (x = newx; x > 0; x--) { + memcpy(newrectf, rectf, sizeof(float[4])); + newrectf += 4; + } + rectf += 4; + } + } + } + else { + const float add = (ibuf->x - 1.001) / (newx - 1.0); + float sample; - val_bf = rectf[1]; - nval_bf = rectf[5]; - diff_bf = nval_bf - val_bf; + float val_a, nval_a, diff_a; + float val_b, nval_b, diff_b; + float val_g, nval_g, diff_g; + float val_r, nval_r, diff_r; + float val_af, nval_af, diff_af; + float val_bf, nval_bf, diff_bf; + float val_gf, nval_gf, diff_gf; + float val_rf, nval_rf, diff_rf; - val_gf = rectf[2]; - nval_gf = rectf[6]; - diff_gf = nval_gf - val_gf; + val_a = nval_a = diff_a = val_b = nval_b = diff_b = 0; + val_g = nval_g = diff_g = val_r = nval_r = diff_r = 0; + val_af = nval_af = diff_af = val_bf = nval_bf = diff_bf = 0; + val_gf = nval_gf = diff_gf = val_rf = nval_rf = diff_rf = 0; - val_rf = rectf[3]; - nval_rf = rectf[7]; - diff_rf = nval_rf - val_rf; + for (y = ibuf->y; y > 0; y--) { - rectf += 8; - } - for (x = newx; x > 0; x--) { - if (sample >= 1.0f) { - sample -= 1.0f; + sample = 0; - if (do_rect) { - val_a = nval_a; - nval_a = rect[0]; - diff_a = nval_a - val_a; - val_a += 0.5f; - - val_b = nval_b; - nval_b = rect[1]; - diff_b = nval_b - val_b; - val_b += 0.5f; - - val_g = nval_g; - nval_g = rect[2]; - diff_g = nval_g - val_g; - val_g += 0.5f; - - val_r = nval_r; - nval_r = rect[3]; - diff_r = nval_r - val_r; - val_r += 0.5f; - rect += 4; - } - if (do_float) { - val_af = nval_af; - nval_af = rectf[0]; - diff_af = nval_af - val_af; + if (do_rect) { + val_a = rect[0]; + nval_a = rect[4]; + diff_a = nval_a - val_a; + val_a += 0.5f; + + val_b = rect[1]; + nval_b = rect[5]; + diff_b = nval_b - val_b; + val_b += 0.5f; + + val_g = rect[2]; + nval_g = rect[6]; + diff_g = nval_g - val_g; + val_g += 0.5f; + + val_r = rect[3]; + nval_r = rect[7]; + diff_r = nval_r - val_r; + val_r += 0.5f; + + rect += 8; + } + if (do_float) { + val_af = rectf[0]; + nval_af = rectf[4]; + diff_af = nval_af - val_af; - val_bf = nval_bf; - nval_bf = rectf[1]; - diff_bf = nval_bf - val_bf; + val_bf = rectf[1]; + nval_bf = rectf[5]; + diff_bf = nval_bf - val_bf; - val_gf = nval_gf; - nval_gf = rectf[2]; - diff_gf = nval_gf - val_gf; + val_gf = rectf[2]; + nval_gf = rectf[6]; + diff_gf = nval_gf - val_gf; - val_rf = nval_rf; - nval_rf = rectf[3]; - diff_rf = nval_rf - val_rf; - rectf += 4; - } - } - if (do_rect) { - newrect[0] = val_a + sample * diff_a; - newrect[1] = val_b + sample * diff_b; - newrect[2] = val_g + sample * diff_g; - newrect[3] = val_r + sample * diff_r; - newrect += 4; + val_rf = rectf[3]; + nval_rf = rectf[7]; + diff_rf = nval_rf - val_rf; + + rectf += 8; } - if (do_float) { - newrectf[0] = val_af + sample * diff_af; - newrectf[1] = val_bf + sample * diff_bf; - newrectf[2] = val_gf + sample * diff_gf; - newrectf[3] = val_rf + sample * diff_rf; - newrectf += 4; + for (x = newx; x > 0; x--) { + if (sample >= 1.0f) { + sample -= 1.0f; + + if (do_rect) { + val_a = nval_a; + nval_a = rect[0]; + diff_a = nval_a - val_a; + val_a += 0.5f; + + val_b = nval_b; + nval_b = rect[1]; + diff_b = nval_b - val_b; + val_b += 0.5f; + + val_g = nval_g; + nval_g = rect[2]; + diff_g = nval_g - val_g; + val_g += 0.5f; + + val_r = nval_r; + nval_r = rect[3]; + diff_r = nval_r - val_r; + val_r += 0.5f; + rect += 4; + } + if (do_float) { + val_af = nval_af; + nval_af = rectf[0]; + diff_af = nval_af - val_af; + + val_bf = nval_bf; + nval_bf = rectf[1]; + diff_bf = nval_bf - val_bf; + + val_gf = nval_gf; + nval_gf = rectf[2]; + diff_gf = nval_gf - val_gf; + + val_rf = nval_rf; + nval_rf = rectf[3]; + diff_rf = nval_rf - val_rf; + rectf += 4; + } + } + if (do_rect) { + newrect[0] = val_a + sample * diff_a; + newrect[1] = val_b + sample * diff_b; + newrect[2] = val_g + sample * diff_g; + newrect[3] = val_r + sample * diff_r; + newrect += 4; + } + if (do_float) { + newrectf[0] = val_af + sample * diff_af; + newrectf[1] = val_bf + sample * diff_bf; + newrectf[2] = val_gf + sample * diff_gf; + newrectf[3] = val_rf + sample * diff_rf; + newrectf += 4; + } + sample += add; } - sample += add; } } @@ -1371,22 +1397,9 @@ static ImBuf *scaleupy(struct ImBuf *ibuf, int newy) { uchar *rect, *_newrect = NULL, *newrect; float *rectf, *_newrectf = NULL, *newrectf; - float sample, add; - float val_a, nval_a, diff_a; - float val_b, nval_b, diff_b; - float val_g, nval_g, diff_g; - float val_r, nval_r, diff_r; - float val_af, nval_af, diff_af; - float val_bf, nval_bf, diff_bf; - float val_gf, nval_gf, diff_gf; - float val_rf, nval_rf, diff_rf; int x, y, skipx; bool do_rect = false, do_float = false; - val_a = nval_a = diff_a = val_b = nval_b = diff_b = 0; - val_g = nval_g = diff_g = val_r = nval_r = diff_r = 0; - val_af = nval_af = diff_af = val_bf = nval_bf = diff_bf = 0; - val_gf = nval_gf = diff_gf = val_rf = nval_rf = diff_rf = 0; if (ibuf == NULL) { return NULL; } @@ -1412,126 +1425,159 @@ static ImBuf *scaleupy(struct ImBuf *ibuf, int newy) } } - add = (ibuf->y - 1.001) / (newy - 1.0); - skipx = 4 * ibuf->x; - rect = (uchar *)ibuf->rect; rectf = (float *)ibuf->rect_float; newrect = _newrect; newrectf = _newrectf; - for (x = ibuf->x; x > 0; x--) { + skipx = 4 * ibuf->x; - sample = 0; + /* Special case, copy all rows, needed since the scaling logic assumes there is at least + * two rows to interpolate between causing out of bounds read for 1px images, see T70356. */ + if (UNLIKELY(ibuf->y == 1)) { if (do_rect) { - rect = ((uchar *)ibuf->rect) + 4 * (x - 1); - newrect = _newrect + 4 * (x - 1); - - val_a = rect[0]; - nval_a = rect[skipx]; - diff_a = nval_a - val_a; - val_a += 0.5f; - - val_b = rect[1]; - nval_b = rect[skipx + 1]; - diff_b = nval_b - val_b; - val_b += 0.5f; - - val_g = rect[2]; - nval_g = rect[skipx + 2]; - diff_g = nval_g - val_g; - val_g += 0.5f; - - val_r = rect[3]; - nval_r = rect[skipx + 3]; - diff_r = nval_r - val_r; - val_r += 0.5f; - - rect += 2 * skipx; + for (y = newy; y > 0; y--) { + memcpy(newrect, rect, sizeof(char) * skipx); + newrect += skipx; + } } if (do_float) { - rectf = ibuf->rect_float + 4 * (x - 1); - newrectf = _newrectf + 4 * (x - 1); - - val_af = rectf[0]; - nval_af = rectf[skipx]; - diff_af = nval_af - val_af; + for (y = newy; y > 0; y--) { + memcpy(newrectf, rectf, sizeof(float) * skipx); + newrectf += skipx; + } + } + } + else { + const float add = (ibuf->y - 1.001) / (newy - 1.0); + float sample; + + float val_a, nval_a, diff_a; + float val_b, nval_b, diff_b; + float val_g, nval_g, diff_g; + float val_r, nval_r, diff_r; + float val_af, nval_af, diff_af; + float val_bf, nval_bf, diff_bf; + float val_gf, nval_gf, diff_gf; + float val_rf, nval_rf, diff_rf; + + val_a = nval_a = diff_a = val_b = nval_b = diff_b = 0; + val_g = nval_g = diff_g = val_r = nval_r = diff_r = 0; + val_af = nval_af = diff_af = val_bf = nval_bf = diff_bf = 0; + val_gf = nval_gf = diff_gf = val_rf = nval_rf = diff_rf = 0; + + for (x = ibuf->x; x > 0; x--) { + sample = 0; + if (do_rect) { + rect = ((uchar *)ibuf->rect) + 4 * (x - 1); + newrect = _newrect + 4 * (x - 1); + + val_a = rect[0]; + nval_a = rect[skipx]; + diff_a = nval_a - val_a; + val_a += 0.5f; + + val_b = rect[1]; + nval_b = rect[skipx + 1]; + diff_b = nval_b - val_b; + val_b += 0.5f; + + val_g = rect[2]; + nval_g = rect[skipx + 2]; + diff_g = nval_g - val_g; + val_g += 0.5f; + + val_r = rect[3]; + nval_r = rect[skipx + 3]; + diff_r = nval_r - val_r; + val_r += 0.5f; + + rect += 2 * skipx; + } + if (do_float) { + rectf = ibuf->rect_float + 4 * (x - 1); + newrectf = _newrectf + 4 * (x - 1); - val_bf = rectf[1]; - nval_bf = rectf[skipx + 1]; - diff_bf = nval_bf - val_bf; + val_af = rectf[0]; + nval_af = rectf[skipx]; + diff_af = nval_af - val_af; - val_gf = rectf[2]; - nval_gf = rectf[skipx + 2]; - diff_gf = nval_gf - val_gf; + val_bf = rectf[1]; + nval_bf = rectf[skipx + 1]; + diff_bf = nval_bf - val_bf; - val_rf = rectf[3]; - nval_rf = rectf[skipx + 3]; - diff_rf = nval_rf - val_rf; + val_gf = rectf[2]; + nval_gf = rectf[skipx + 2]; + diff_gf = nval_gf - val_gf; - rectf += 2 * skipx; - } + val_rf = rectf[3]; + nval_rf = rectf[skipx + 3]; + diff_rf = nval_rf - val_rf; - for (y = newy; y > 0; y--) { - if (sample >= 1.0f) { - sample -= 1.0f; + rectf += 2 * skipx; + } + for (y = newy; y > 0; y--) { + if (sample >= 1.0f) { + sample -= 1.0f; + + if (do_rect) { + val_a = nval_a; + nval_a = rect[0]; + diff_a = nval_a - val_a; + val_a += 0.5f; + + val_b = nval_b; + nval_b = rect[1]; + diff_b = nval_b - val_b; + val_b += 0.5f; + + val_g = nval_g; + nval_g = rect[2]; + diff_g = nval_g - val_g; + val_g += 0.5f; + + val_r = nval_r; + nval_r = rect[3]; + diff_r = nval_r - val_r; + val_r += 0.5f; + rect += skipx; + } + if (do_float) { + val_af = nval_af; + nval_af = rectf[0]; + diff_af = nval_af - val_af; + + val_bf = nval_bf; + nval_bf = rectf[1]; + diff_bf = nval_bf - val_bf; + + val_gf = nval_gf; + nval_gf = rectf[2]; + diff_gf = nval_gf - val_gf; + + val_rf = nval_rf; + nval_rf = rectf[3]; + diff_rf = nval_rf - val_rf; + rectf += skipx; + } + } if (do_rect) { - val_a = nval_a; - nval_a = rect[0]; - diff_a = nval_a - val_a; - val_a += 0.5f; - - val_b = nval_b; - nval_b = rect[1]; - diff_b = nval_b - val_b; - val_b += 0.5f; - - val_g = nval_g; - nval_g = rect[2]; - diff_g = nval_g - val_g; - val_g += 0.5f; - - val_r = nval_r; - nval_r = rect[3]; - diff_r = nval_r - val_r; - val_r += 0.5f; - rect += skipx; + newrect[0] = val_a + sample * diff_a; + newrect[1] = val_b + sample * diff_b; + newrect[2] = val_g + sample * diff_g; + newrect[3] = val_r + sample * diff_r; + newrect += skipx; } if (do_float) { - val_af = nval_af; - nval_af = rectf[0]; - diff_af = nval_af - val_af; - - val_bf = nval_bf; - nval_bf = rectf[1]; - diff_bf = nval_bf - val_bf; - - val_gf = nval_gf; - nval_gf = rectf[2]; - diff_gf = nval_gf - val_gf; - - val_rf = nval_rf; - nval_rf = rectf[3]; - diff_rf = nval_rf - val_rf; - rectf += skipx; + newrectf[0] = val_af + sample * diff_af; + newrectf[1] = val_bf + sample * diff_bf; + newrectf[2] = val_gf + sample * diff_gf; + newrectf[3] = val_rf + sample * diff_rf; + newrectf += skipx; } + sample += add; } - if (do_rect) { - newrect[0] = val_a + sample * diff_a; - newrect[1] = val_b + sample * diff_b; - newrect[2] = val_g + sample * diff_g; - newrect[3] = val_r + sample * diff_r; - newrect += skipx; - } - if (do_float) { - newrectf[0] = val_af + sample * diff_af; - newrectf[1] = val_bf + sample * diff_bf; - newrectf[2] = val_gf + sample * diff_gf; - newrectf[3] = val_rf + sample * diff_rf; - newrectf += skipx; - } - sample += add; } } diff --git a/source/blender/io/alembic/exporter/abc_custom_props.cc b/source/blender/io/alembic/exporter/abc_custom_props.cc index f5593e7ee30..4ea2fd03fff 100644 --- a/source/blender/io/alembic/exporter/abc_custom_props.cc +++ b/source/blender/io/alembic/exporter/abc_custom_props.cc @@ -141,7 +141,7 @@ void CustomPropertiesExporter::write_idparray(const IDProperty *idp_array) continue; } std::cerr << "Custom property " << idp_array->name << " has elements of varying type"; - BLI_assert(!"Mixed type IDP_ARRAY custom property found"); + BLI_assert_msg(0, "Mixed type IDP_ARRAY custom property found"); } #endif diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc index e2be241c144..174b2abb90f 100644 --- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc +++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc @@ -229,7 +229,7 @@ ABCAbstractWriter *ABCHierarchyIterator::create_data_writer_for_object_type( case OB_GPENCIL: return nullptr; case OB_TYPE_MAX: - BLI_assert(!"OB_TYPE_MAX should not be used"); + BLI_assert_msg(0, "OB_TYPE_MAX should not be used"); return nullptr; } diff --git a/source/blender/io/alembic/exporter/abc_writer_instance.cc b/source/blender/io/alembic/exporter/abc_writer_instance.cc index 1737e8c091e..353705f2c1d 100644 --- a/source/blender/io/alembic/exporter/abc_writer_instance.cc +++ b/source/blender/io/alembic/exporter/abc_writer_instance.cc @@ -59,7 +59,7 @@ Alembic::Abc::OCompoundProperty ABCInstanceWriter::abc_prop_for_custom_props() OObject ABCInstanceWriter::get_alembic_object() const { /* There is no OObject for an instance. */ - BLI_assert(!"ABCInstanceWriter cannot return its Alembic OObject"); + BLI_assert_msg(0, "ABCInstanceWriter cannot return its Alembic OObject"); return OObject(); } diff --git a/source/blender/io/collada/ControllerExporter.cpp b/source/blender/io/collada/ControllerExporter.cpp index 6f0d422dbe2..e61ed47adee 100644 --- a/source/blender/io/collada/ControllerExporter.cpp +++ b/source/blender/io/collada/ControllerExporter.cpp @@ -29,6 +29,7 @@ #include "BKE_action.h" #include "BKE_armature.h" +#include "BKE_deform.h" #include "BKE_global.h" #include "BKE_idprop.h" #include "BKE_lib_id.h" @@ -194,9 +195,9 @@ void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm) add_bind_shape_mat(ob); - std::string joints_source_id = add_joints_source(ob_arm, &ob->defbase, controller_id); - std::string inv_bind_mat_source_id = add_inv_bind_mats_source( - ob_arm, &ob->defbase, controller_id); + const ListBase *defbase = BKE_object_defgroup_list(ob); + std::string joints_source_id = add_joints_source(ob_arm, defbase, controller_id); + std::string inv_bind_mat_source_id = add_inv_bind_mats_source(ob_arm, defbase, controller_id); std::list<int> vcounts; std::list<int> joints; @@ -207,9 +208,9 @@ void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm) /* def group index -> joint index */ std::vector<int> joint_index_by_def_index; - bDeformGroup *def; + const bDeformGroup *def; - for (def = (bDeformGroup *)ob->defbase.first, i = 0, j = 0; def; def = def->next, i++) { + for (def = (const bDeformGroup *)defbase->first, i = 0, j = 0; def; def = def->next, i++) { if (is_bone_defgroup(ob_arm, def)) { joint_index_by_def_index.push_back(j++); } @@ -269,7 +270,7 @@ void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm) } std::string weights_source_id = add_weights_source(me, controller_id, weights); - add_joints_element(&ob->defbase, joints_source_id, inv_bind_mat_source_id); + add_joints_element(defbase, joints_source_id, inv_bind_mat_source_id); add_vertex_weights_element(weights_source_id, joints_source_id, vcounts, joints); BKE_id_free(nullptr, me); @@ -392,7 +393,7 @@ void ControllerExporter::add_weight_extras(Key *key) } } -void ControllerExporter::add_joints_element(ListBase *defbase, +void ControllerExporter::add_joints_element(const ListBase *defbase, const std::string &joints_source_id, const std::string &inv_bind_mat_source_id) { @@ -431,7 +432,7 @@ void ControllerExporter::add_bind_shape_mat(Object *ob) } std::string ControllerExporter::add_joints_source(Object *ob_arm, - ListBase *defbase, + const ListBase *defbase, const std::string &controller_id) { std::string source_id = controller_id + JOINTS_SOURCE_ID_SUFFIX; @@ -468,7 +469,7 @@ std::string ControllerExporter::add_joints_source(Object *ob_arm, } std::string ControllerExporter::add_inv_bind_mats_source(Object *ob_arm, - ListBase *defbase, + const ListBase *defbase, const std::string &controller_id) { std::string source_id = controller_id + BIND_POSES_SOURCE_ID_SUFFIX; @@ -568,13 +569,13 @@ std::string ControllerExporter::add_inv_bind_mats_source(Object *ob_arm, return source_id; } -Bone *ControllerExporter::get_bone_from_defgroup(Object *ob_arm, bDeformGroup *def) +Bone *ControllerExporter::get_bone_from_defgroup(Object *ob_arm, const bDeformGroup *def) { bPoseChannel *pchan = BKE_pose_channel_find_name(ob_arm->pose, def->name); return pchan ? pchan->bone : nullptr; } -bool ControllerExporter::is_bone_defgroup(Object *ob_arm, bDeformGroup *def) +bool ControllerExporter::is_bone_defgroup(Object *ob_arm, const bDeformGroup *def) { return get_bone_from_defgroup(ob_arm, def) != nullptr; } diff --git a/source/blender/io/collada/ControllerExporter.h b/source/blender/io/collada/ControllerExporter.h index 6a377a4119e..0aec9e8d179 100644 --- a/source/blender/io/collada/ControllerExporter.h +++ b/source/blender/io/collada/ControllerExporter.h @@ -97,7 +97,7 @@ class ControllerExporter : public COLLADASW::LibraryControllers, void export_morph_controller(Object *ob, Key *key); - void add_joints_element(ListBase *defbase, + void add_joints_element(const ListBase *defbase, const std::string &joints_source_id, const std::string &inv_bind_mat_source_id); @@ -110,16 +110,16 @@ class ControllerExporter : public COLLADASW::LibraryControllers, void add_weight_extras(Key *key); std::string add_joints_source(Object *ob_arm, - ListBase *defbase, + const ListBase *defbase, const std::string &controller_id); std::string add_inv_bind_mats_source(Object *ob_arm, - ListBase *defbase, + const ListBase *defbase, const std::string &controller_id); - Bone *get_bone_from_defgroup(Object *ob_arm, bDeformGroup *def); + Bone *get_bone_from_defgroup(Object *ob_arm, const bDeformGroup *def); - bool is_bone_defgroup(Object *ob_arm, bDeformGroup *def); + bool is_bone_defgroup(Object *ob_arm, const bDeformGroup *def); std::string add_weights_source(Mesh *me, const std::string &controller_id, diff --git a/source/blender/io/collada/SkinInfo.cpp b/source/blender/io/collada/SkinInfo.cpp index c2f17174d75..f0e1c5e4c26 100644 --- a/source/blender/io/collada/SkinInfo.cpp +++ b/source/blender/io/collada/SkinInfo.cpp @@ -36,6 +36,7 @@ #include "DNA_scene_types.h" #include "BKE_action.h" +#include "BKE_deform.h" #include "BKE_object.h" #include "BKE_object_deform.h" @@ -289,7 +290,8 @@ void SkinInfo::link_armature(bContext *C, /* -1 means "weight towards the bind shape", we just don't assign it to any group */ if (joint != -1) { - bDeformGroup *def = (bDeformGroup *)BLI_findlink(&ob->defbase, joint); + const ListBase *defbase = BKE_object_defgroup_list(ob); + bDeformGroup *def = (bDeformGroup *)BLI_findlink(defbase, joint); ED_vgroup_vert_add(ob, def, vertex, weights[joint_weight], WEIGHT_REPLACE); } diff --git a/source/blender/io/gpencil/gpencil_io.h b/source/blender/io/gpencil/gpencil_io.h index 24b13479359..fab867b38b3 100644 --- a/source/blender/io/gpencil/gpencil_io.h +++ b/source/blender/io/gpencil/gpencil_io.h @@ -27,9 +27,9 @@ extern "C" { #endif struct ARegion; -struct bContext; struct Object; struct View3D; +struct bContext; typedef struct GpencilIOParams { bContext *C; diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.hh b/source/blender/io/gpencil/intern/gpencil_io_base.hh index c8d85d08f7b..02758883f19 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_base.hh +++ b/source/blender/io/gpencil/intern/gpencil_io_base.hh @@ -37,9 +37,9 @@ struct Object; struct RegionView3D; struct Scene; -struct bGPdata; struct bGPDlayer; struct bGPDstroke; +struct bGPdata; using blender::Vector; diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_svg.hh b/source/blender/io/gpencil/intern/gpencil_io_import_svg.hh index 0e9271dd2c6..99e8b1ed4fd 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_import_svg.hh +++ b/source/blender/io/gpencil/intern/gpencil_io_import_svg.hh @@ -24,10 +24,10 @@ #include "gpencil_io_import_base.hh" struct GpencilIOParams; -struct NSVGshape; struct NSVGpath; -struct bGPdata; +struct NSVGshape; struct bGPDframe; +struct bGPdata; #define SVG_IMPORTER_NAME "SVG Import for Grease Pencil" #define SVG_IMPORTER_VERSION "v1.0" diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc index 66dfc21441e..a9cba7f36d9 100644 --- a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc +++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc @@ -121,7 +121,7 @@ AbstractHierarchyWriter *USDHierarchyIterator::create_data_writer(const Hierarch case OB_GPENCIL: return nullptr; case OB_TYPE_MAX: - BLI_assert(!"OB_TYPE_MAX should not be used"); + BLI_assert_msg(0, "OB_TYPE_MAX should not be used"); return nullptr; } diff --git a/source/blender/io/usd/intern/usd_writer_abstract.cc b/source/blender/io/usd/intern/usd_writer_abstract.cc index 5e66136abf1..6965ecf6249 100644 --- a/source/blender/io/usd/intern/usd_writer_abstract.cc +++ b/source/blender/io/usd/intern/usd_writer_abstract.cc @@ -128,7 +128,7 @@ bool USDAbstractWriter::mark_as_instance(const HierarchyContext &context, const if (context.export_path == context.original_export_path) { printf("USD ref error: export path is reference path: %s\n", context.export_path.c_str()); - BLI_assert(!"USD reference error"); + BLI_assert_msg(0, "USD reference error"); return false; } diff --git a/source/blender/io/usd/intern/usd_writer_camera.cc b/source/blender/io/usd/intern/usd_writer_camera.cc index 677be9a7fc4..50d644241df 100644 --- a/source/blender/io/usd/intern/usd_writer_camera.cc +++ b/source/blender/io/usd/intern/usd_writer_camera.cc @@ -61,7 +61,7 @@ static void camera_sensor_size_for_render(const Camera *camera, *r_sensor_y = camera->sensor_y; break; case CAMERA_SENSOR_FIT_AUTO: - BLI_assert(!"Camera fit should be either horizontal or vertical"); + BLI_assert_msg(0, "Camera fit should be either horizontal or vertical"); break; } } diff --git a/source/blender/io/usd/intern/usd_writer_light.cc b/source/blender/io/usd/intern/usd_writer_light.cc index f77c51c22ec..7ffae1dd398 100644 --- a/source/blender/io/usd/intern/usd_writer_light.cc +++ b/source/blender/io/usd/intern/usd_writer_light.cc @@ -87,7 +87,7 @@ void USDLightWriter::do_write(HierarchyContext &context) usd_light = pxr::UsdLuxDistantLight::Define(stage, usd_path); break; default: - BLI_assert(!"is_supported() returned true for unsupported light type"); + BLI_assert_msg(0, "is_supported() returned true for unsupported light type"); } /* Scale factor to get to somewhat-similar illumination. Since the USDViewer had similar diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index c9d652ad03d..43969bf0768 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -621,9 +621,9 @@ typedef enum IDRecalcFlag { * When a collection gets tagged with this flag, all objects depending on the geometry and * transforms on any of the objects in the collection are updated. */ ID_RECALC_GEOMETRY = (1 << 1), - - /* ** Animation or time changed and animation is to be re-evaluated. ** */ - ID_RECALC_ANIMATION = (1 << 2), + /* Same as #ID_RECALC_GEOMETRY, but instead of tagging the batch cache as `dirty_all`, just tags + what matches the deform cache. */ + ID_RECALC_GEOMETRY_DEFORM = (1 << 2), /* ** Particle system changed. ** */ /* Only do pathcache etc. */ @@ -683,6 +683,9 @@ typedef enum IDRecalcFlag { * have to be copied on every update. */ ID_RECALC_PARAMETERS = (1 << 21), + /* ** Animation or time changed and animation is to be re-evaluated. ** */ + ID_RECALC_ANIMATION = (1 << 22), + /* Input has changed and datablock is to be reload from disk. * Applies to movie clips to inform that copy-on-written version is to be refreshed for the new * input file or for color space changes. */ diff --git a/source/blender/makesdna/DNA_asset_defaults.h b/source/blender/makesdna/DNA_asset_defaults.h index ff00ba79cf0..ce01563f619 100644 --- a/source/blender/makesdna/DNA_asset_defaults.h +++ b/source/blender/makesdna/DNA_asset_defaults.h @@ -32,6 +32,13 @@ 0 \ } +#define _DNA_DEFAULT_AssetLibraryReference \ + { \ + .type = ASSET_LIBRARY_LOCAL, \ + /* Not needed really (should be ignored for #ASSET_LIBRARY_LOCAL), but helps debugging. */ \ + .custom_library_index = -1, \ + } + /** \} */ /* clang-format on */ diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index 697d25653f8..3907c158573 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -20,6 +20,7 @@ #pragma once +#include "DNA_defs.h" #include "DNA_listBase.h" #ifdef __cplusplus @@ -36,6 +37,16 @@ typedef struct AssetTag { char name[64]; /* MAX_NAME */ } AssetTag; +# +# +typedef struct AssetFilterSettings { + /** Tags to match against. These are newly allocated, and compared against the + * #AssetMetaData.tags. + * TODO not used and doesn't do anything yet. */ + ListBase tags; /* AssetTag */ + uint64_t id_types; /* rna_enum_id_type_filter_items */ +} AssetFilterSettings; + /** * \brief The meta-data of an asset. * By creating and giving this for a data-block (#ID.asset_data), the data-block becomes an asset. @@ -62,6 +73,52 @@ typedef struct AssetMetaData { char _pad[4]; } AssetMetaData; +typedef enum eAssetLibraryType { + /* For the future. Display assets bundled with Blender by default. */ + // ASSET_LIBRARY_BUNDLED = 0, + /** Display assets from the current session (current "Main"). */ + ASSET_LIBRARY_LOCAL = 1, + /* For the future. Display assets for the current project. */ + // ASSET_LIBRARY_PROJECT = 2, + + /** Display assets from custom asset libraries, as defined in the preferences + * (#bUserAssetLibrary). The name will be taken from #FileSelectParams.asset_library.idname + * then. + * In RNA, we add the index of the custom library to this to identify it by index. So keep + * this last! */ + ASSET_LIBRARY_CUSTOM = 100, +} eAssetLibraryType; + +/* TODO copy of FileSelectAssetLibraryUID */ +/** + * Information to identify a asset library. May be either one of the predefined types (current + * 'Main', builtin library, project library), or a custom type as defined in the Preferences. + * + * If the type is set to #ASSET_LIBRARY_CUSTOM, `custom_library_index` must be set to identify the + * custom library. Otherwise it is not used. + */ +typedef struct AssetLibraryReference { + short type; /* eAssetLibraryType */ + char _pad1[2]; + /** + * If showing a custom asset library (#ASSET_LIBRARY_CUSTOM), this is the index of the + * #bUserAssetLibrary within #UserDef.asset_libraries. + * Should be ignored otherwise (but better set to -1 then, for sanity and debugging). + */ + int custom_library_index; +} AssetLibraryReference; + +/** + * Not part of the core design, we should try to get rid of it. Only needed to wrap FileDirEntry + * into a type with PropertyGroup as base, so we can have an RNA collection of #AssetHandle's to + * pass to the UI. + */ +# +# +typedef struct AssetHandle { + struct FileDirEntry *file_data; +} AssetHandle; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index 3732de6c0ec..520fc6c1b00 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -35,6 +35,7 @@ extern "C" { #define MAXTEXTBOX 256 /* used in readfile.c and editfont.c */ struct AnimData; +struct CurveEval; struct CurveProfile; struct EditFont; struct GHash; @@ -43,7 +44,6 @@ struct Key; struct Material; struct Object; struct VFont; -struct CurveEval; /* These two Lines with # tell makesdna this struct can be excluded. */ # diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index c573de6b54e..380d8ad1249 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -33,8 +33,8 @@ extern "C" { struct AnimData; struct Curve; -struct MDeformVert; struct Curve; +struct MDeformVert; #define GP_DEFAULT_PIX_FACTOR 1.0f #define GP_DEFAULT_GRID_LINES 4 @@ -666,6 +666,9 @@ typedef struct bGPdata { /** List of bGPDpalette's - Deprecated (2.78 - 2.79 only). */ ListBase palettes DNA_DEPRECATED; + /** List of bDeformGroup names and flag only. */ + ListBase vertex_group_names; + /* 3D Viewport/Appearance Settings */ /** Factor to define pixel size conversion. */ float pixfactor; @@ -715,7 +718,8 @@ typedef struct bGPdata { /** Stroke selection last index. Used to generate a unique selection index. */ int select_last_index; - char _pad3[4]; + + int vertex_group_active_index; bGPgrid grid; diff --git a/source/blender/makesdna/DNA_lattice_types.h b/source/blender/makesdna/DNA_lattice_types.h index 48eb8d90702..361893db893 100644 --- a/source/blender/makesdna/DNA_lattice_types.h +++ b/source/blender/makesdna/DNA_lattice_types.h @@ -72,6 +72,11 @@ typedef struct Lattice { struct MDeformVert *dvert; /** Multiply the influence, MAX_VGROUP_NAME. */ char vgroup[64]; + /** List of bDeformGroup names and flag only. */ + ListBase vertex_group_names; + int vertex_group_active_index; + + char _pad0[4]; struct EditLatt *editlatt; void *batch_cache; diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 2f089b28048..c54c086affd 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -166,6 +166,8 @@ typedef struct Mesh { struct MEdge *medge; /** Deform-group vertices. */ struct MDeformVert *dvert; + /** List of bDeformGroup names and flag only. */ + ListBase vertex_group_names; /* array of colors for the tessellated faces, must be number of tessellated * faces * 4 in length */ @@ -189,7 +191,7 @@ typedef struct Mesh { /* END BMESH ONLY */ int attributes_active_index; - int _pad3; + int vertex_group_active_index; /* the last selected vertex/edge/face are used for the active face however * this means the active face must always be selected, this is to keep track diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h index f6dac88051b..1b3dbd148df 100644 --- a/source/blender/makesdna/DNA_modifier_defaults.h +++ b/source/blender/makesdna/DNA_modifier_defaults.h @@ -647,7 +647,8 @@ .target = NULL, \ .verts = NULL, \ .falloff = 4.0f, \ - .numverts = 0, \ + .num_mesh_verts = 0, \ + .num_bind_verts = 0, \ .numpoly = 0, \ .flags = 0, \ .mat = _DNA_DEFAULT_UNIT_M4, \ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 79f9556e007..0dc5ec86187 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -2181,7 +2181,7 @@ typedef struct SDefBind { typedef struct SDefVert { SDefBind *binds; unsigned int numbinds; - char _pad[4]; + unsigned int vertex_idx; } SDefVert; typedef struct SurfaceDeformModifierData { @@ -2193,11 +2193,10 @@ typedef struct SurfaceDeformModifierData { /** Vertex bind data. */ SDefVert *verts; float falloff; - unsigned int numverts, numpoly; + unsigned int num_mesh_verts, num_bind_verts, numpoly; int flags; float mat[4][4]; float strength; - char _pad[4]; char defgrp_name[64]; } SurfaceDeformModifierData; @@ -2205,10 +2204,9 @@ typedef struct SurfaceDeformModifierData { enum { /* This indicates "do bind on next modifier evaluation" as well as "is bound". */ MOD_SDEF_BIND = (1 << 0), - MOD_SDEF_INVERT_VGROUP = (1 << 1) - - /* MOD_SDEF_USES_LOOPTRI = (1 << 1), */ /* UNUSED */ - /* MOD_SDEF_HAS_CONCAVE = (1 << 2), */ /* UNUSED */ + MOD_SDEF_INVERT_VGROUP = (1 << 1), + /* Only store bind data for nonzero vgroup weights at the time of bind. */ + MOD_SDEF_SPARSE_BIND = (1 << 2), }; /* Surface Deform vertex bind modes */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 1a3415bf74e..94176d946b3 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -37,6 +37,8 @@ struct Collection; struct ID; struct Image; struct ListBase; +struct Material; +struct Tex; struct bGPdata; struct bNodeInstanceHash; struct bNodeLink; @@ -44,8 +46,6 @@ struct bNodePreview; struct bNodeTreeExec; struct bNodeType; struct uiBlock; -struct Tex; -struct Material; #define NODE_MAXSTR 64 @@ -1370,6 +1370,11 @@ typedef struct NodeGeometryCurvePrimitiveCircle { uint8_t mode; } NodeGeometryCurvePrimitiveCircle; +typedef struct NodeGeometryCurvePrimitiveQuad { + /* GeometryNodeCurvePrimitiveQuadMode. */ + uint8_t mode; +} NodeGeometryCurvePrimitiveQuad; + typedef struct NodeGeometryCurveResample { /* GeometryNodeCurveSampleMode. */ uint8_t mode; @@ -1920,6 +1925,14 @@ typedef enum GeometryNodeCurvePrimitiveLineMode { GEO_NODE_CURVE_PRIMITIVE_LINE_MODE_DIRECTION = 1 } GeometryNodeCurvePrimitiveLineMode; +typedef enum GeometryNodeCurvePrimitiveQuadMode { + GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE = 0, + GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_PARALLELOGRAM = 1, + GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_TRAPEZOID = 2, + GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_KITE = 3, + GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_POINTS = 4, +} GeometryNodeCurvePrimitiveQuadMode; + typedef enum GeometryNodeCurvePrimitiveBezierSegmentMode { GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_POSITION = 0, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_OFFSET = 1, diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 605cd28c793..dd31e85647d 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -272,8 +272,7 @@ typedef struct Object { ListBase constraintChannels DNA_DEPRECATED; /* XXX deprecated... old animation system */ ListBase effect DNA_DEPRECATED; /* XXX deprecated... keep for readfile */ - /** List of bDeformGroup (vertex groups) names and flag only. */ - ListBase defbase; + ListBase defbase DNA_DEPRECATED; /* Only for versioning, moved to object data. */ /** List of ModifierData structures. */ ListBase modifiers; /** List of GpencilModifierData structures. */ @@ -375,7 +374,7 @@ typedef struct Object { /** Custom index, for renderpasses. */ short index; /** Current deformation group, NOTE: index starts at 1. */ - unsigned short actdef; + unsigned short actdef DNA_DEPRECATED; /** Current face map, NOTE: index starts at 1. */ unsigned short actfmap; char _pad2[2]; diff --git a/source/blender/makesdna/DNA_pointcache_types.h b/source/blender/makesdna/DNA_pointcache_types.h index 669c8500677..7de0bb29c46 100644 --- a/source/blender/makesdna/DNA_pointcache_types.h +++ b/source/blender/makesdna/DNA_pointcache_types.h @@ -131,32 +131,34 @@ typedef struct PointCache { void (*free_edit)(struct PTCacheEdit *edit); } PointCache; -/* pointcache->flag */ -#define PTCACHE_BAKED (1 << 0) -#define PTCACHE_OUTDATED (1 << 1) -#define PTCACHE_SIMULATION_VALID (1 << 2) -#define PTCACHE_BAKING (1 << 3) -//#define PTCACHE_BAKE_EDIT (1 << 4) -//#define PTCACHE_BAKE_EDIT_ACTIVE (1 << 5) -#define PTCACHE_DISK_CACHE (1 << 6) -///* removed since 2.64 - T30974, could be added back in a more useful way */ -//#define PTCACHE_QUICK_CACHE (1 << 7) -#define PTCACHE_FRAMES_SKIPPED (1 << 8) -#define PTCACHE_EXTERNAL (1 << 9) -#define PTCACHE_READ_INFO (1 << 10) -/** Don't use the filename of the blend-file the data is linked from (write a local cache). */ -#define PTCACHE_IGNORE_LIBPATH (1 << 11) -/** - * High resolution cache is saved for smoke for backwards compatibility, - * so set this flag to know it's a "fake" cache. - */ -#define PTCACHE_FAKE_SMOKE (1 << 12) -#define PTCACHE_IGNORE_CLEAR (1 << 13) +enum { + /* pointcache->flag */ + PTCACHE_BAKED = 1 << 0, + PTCACHE_OUTDATED = 1 << 1, + PTCACHE_SIMULATION_VALID = 1 << 2, + PTCACHE_BAKING = 1 << 3, + // PTCACHE_BAKE_EDIT = 1 << 4, + // PTCACHE_BAKE_EDIT_ACTIVE = 1 << 5, + PTCACHE_DISK_CACHE = 1 << 6, + /* removed since 2.64 - T30974, could be added back in a more useful way */ + // PTCACHE_QUICK_CACHE = 1 << 7, + PTCACHE_FRAMES_SKIPPED = 1 << 8, + PTCACHE_EXTERNAL = 1 << 9, + PTCACHE_READ_INFO = 1 << 10, + /** Don't use the filename of the blend-file the data is linked from (write a local cache). */ + PTCACHE_IGNORE_LIBPATH = 1 << 11, + /** + * High resolution cache is saved for smoke for backwards compatibility, + * so set this flag to know it's a "fake" cache. + */ + PTCACHE_FAKE_SMOKE = 1 << 12, + PTCACHE_IGNORE_CLEAR = 1 << 13, -#define PTCACHE_FLAG_INFO_DIRTY (1 << 14) + PTCACHE_FLAG_INFO_DIRTY = 1 << 14, -/* PTCACHE_OUTDATED + PTCACHE_FRAMES_SKIPPED */ -#define PTCACHE_REDO_NEEDED 258 + PTCACHE_REDO_NEEDED = PTCACHE_OUTDATED | PTCACHE_FRAMES_SKIPPED, + PTCACHE_FLAGS_COPY = PTCACHE_DISK_CACHE | PTCACHE_EXTERNAL | PTCACHE_IGNORE_LIBPATH, +}; #define PTCACHE_COMPRESS_NO 0 #define PTCACHE_COMPRESS_LZO 1 diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 670e84e0c7a..5bd9cc7a999 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -43,6 +43,7 @@ struct SpaceLink; struct SpaceType; struct uiBlock; struct uiLayout; +struct uiList; struct wmDrawBuffer; struct wmTimer; struct wmTooltipState; @@ -246,11 +247,16 @@ typedef struct PanelCategoryStack { char idname[64]; } PanelCategoryStack; +typedef void (*uiListFreeRuntimeDataFunc)(struct uiList *ui_list); + /* uiList dynamic data... */ /* These two Lines with # tell makesdna this struct can be excluded. */ # # typedef struct uiListDyn { + /** Callback to free UI data when freeing UI-Lists in BKE. */ + uiListFreeRuntimeDataFunc free_runtime_data_fn; + /** Number of rows needed to draw all elements. */ int height; /** Actual visual height of the list (in rows). */ @@ -258,6 +264,9 @@ typedef struct uiListDyn { /** Minimal visual height of the list (in rows). */ int visual_height_min; + /** Number of columns drawn for grid layouts. */ + int columns; + /** Number of items in collection. */ int items_len; /** Number of items actually visible after filtering. */ @@ -270,11 +279,19 @@ typedef struct uiListDyn { int resize; int resize_prev; + /** Allocated custom data. Freed together with the #uiList (and when re-assigning). */ + void *customdata; + /* Filtering data. */ /** Items_len length. */ int *items_filter_flags; /** Org_idx -> new_idx, items_len length. */ int *items_filter_neworder; + + struct wmOperatorType *custom_drag_optype; + struct PointerRNA *custom_drag_opptr; + struct wmOperatorType *custom_activate_optype; + struct PointerRNA *custom_activate_opptr; } uiListDyn; typedef struct uiList { /* some list UI data need to be saved in file */ @@ -301,6 +318,12 @@ typedef struct uiList { /* some list UI data need to be saved in file */ int filter_flag; int filter_sort_flag; + /** Operator executed when activating an item. */ + const char *custom_activate_opname; + /** Operator executed when dragging an item (item gets activated too, without running + * custom_activate_opname above). */ + const char *custom_drag_opname; + /* Custom sub-classes properties. */ IDProperty *properties; @@ -584,6 +607,7 @@ enum { UILST_LAYOUT_DEFAULT = 0, UILST_LAYOUT_COMPACT = 1, UILST_LAYOUT_GRID = 2, + UILST_LAYOUT_BIG_PREVIEW_GRID = 3, }; /** #uiList.flag */ diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 1bd4c9233e3..55dc51e0632 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -43,9 +43,9 @@ extern "C" { struct Ipo; struct MovieClip; struct Scene; +struct SequenceLookup; struct VFont; struct bSound; -struct SequenceLookup; /* strlens; 256= FILE_MAXFILE, 768= FILE_MAXDIR */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 73a44ec16bb..b990de29ff3 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -700,14 +700,14 @@ typedef enum eSpaceSeq_OverlayType { * Information to identify a asset library. May be either one of the predefined types (current * 'Main', builtin library, project library), or a custom type as defined in the Preferences. * - * If the type is set to #FILE_ASSET_LIBRARY_CUSTOM, idname must have the name to identify the + * If the type is set to #ASSET_LIBRARY_CUSTOM, idname must have the name to identify the * custom library. Otherwise idname is not used. */ typedef struct FileSelectAssetLibraryUID { short type; /* eFileAssetLibrary_Type */ char _pad[2]; /** - * If showing a custom asset library (#FILE_ASSET_LIBRARY_CUSTOM), this is the index of the + * If showing a custom asset library (#ASSET_LIBRARY_CUSTOM), this is the index of the * #bUserAssetLibrary within #UserDef.asset_libraries. * Should be ignored otherwise (but better set to -1 then, for sanity and debugging). */ @@ -885,22 +885,6 @@ typedef enum eFileBrowse_Mode { FILE_BROWSE_MODE_ASSETS = 1, } eFileBrowse_Mode; -typedef enum eFileAssetLibrary_Type { - /* For the future. Display assets bundled with Blender by default. */ - // FILE_ASSET_LIBRARY_BUNDLED = 0, - /** Display assets from the current session (current "Main"). */ - FILE_ASSET_LIBRARY_LOCAL = 1, - /* For the future. Display assets for the current project. */ - // FILE_ASSET_LIBRARY_PROJECT = 2, - - /** Display assets from custom asset libraries, as defined in the preferences - * (#bUserAssetLibrary). The name will be taken from #FileSelectParams.asset_library.idname - * then. - * In RNA, we add the index of the custom library to this to identify it by index. So keep - * this last! */ - FILE_ASSET_LIBRARY_CUSTOM = 100, -} eFileAssetLibrary_Type; - /* FileSelectParams.display */ enum eFileDisplayType { /** Internal (not exposed to users): Keep whatever display type was used during the last File diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h index 0fce331fadf..9ed01a7dbcc 100644 --- a/source/blender/makesdna/DNA_workspace_types.h +++ b/source/blender/makesdna/DNA_workspace_types.h @@ -23,6 +23,7 @@ #pragma once #include "DNA_ID.h" +#include "DNA_asset_types.h" #ifdef __cplusplus extern "C" { @@ -135,6 +136,10 @@ typedef struct WorkSpace { /** Info text from modal operators (runtime). */ char *status_text; + + /** Workspace-wide active asset library, for asset UIs to use (e.g. asset view UI template). The + * Asset Browser has its own and doesn't use this. */ + AssetLibraryReference active_asset_library; } WorkSpace; /** diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index 03f7dbf6489..a573e2f9e8c 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -152,6 +152,7 @@ /* DNA_asset_defaults.h */ SDNA_DEFAULT_DECL_STRUCT(AssetMetaData); +SDNA_DEFAULT_DECL_STRUCT(AssetLibraryReference); /* DNA_armature_defaults.h */ SDNA_DEFAULT_DECL_STRUCT(bArmature); @@ -348,6 +349,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { /* DNA_asset_defaults.h */ SDNA_DEFAULT_DECL(AssetMetaData), + SDNA_DEFAULT_DECL(AssetLibraryReference), /* DNA_armature_defaults.h */ SDNA_DEFAULT_DECL(bArmature), diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c index 84a77e9553e..d23b9441822 100644 --- a/source/blender/makesdna/intern/dna_genfile.c +++ b/source/blender/makesdna/intern/dna_genfile.c @@ -1398,7 +1398,7 @@ static void init_reconstruct_step_for_member(const SDNA *oldsdna, r_step->data.cast_pointer.array_len = shared_array_length; } else { - BLI_assert(!"invalid pointer size"); + BLI_assert_msg(0, "invalid pointer size"); r_step->type = RECONSTRUCT_STEP_INIT_ZERO; } break; diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index 735be0c10bf..d363e40e4f0 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -136,4 +136,5 @@ DNA_STRUCT_RENAME_ELEM(wmWindow, global_area_map, global_areas) DNA_STRUCT_RENAME_ELEM(LineartGpencilModifierData, line_types, edge_types) DNA_STRUCT_RENAME_ELEM(LineartGpencilModifierData, transparency_flags, mask_switches) DNA_STRUCT_RENAME_ELEM(LineartGpencilModifierData, transparency_mask, material_mask_bits) +DNA_STRUCT_RENAME_ELEM(SurfaceDeformModifierData, numverts, num_bind_verts) DNA_STRUCT_RENAME_ELEM(MaterialLineArt, transparency_mask, material_mask_bits) diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 782d0924d21..97615016016 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -71,6 +71,8 @@ extern StructRNA RNA_ArrayGpencilModifier; extern StructRNA RNA_ArrayModifier; extern StructRNA RNA_Attribute; extern StructRNA RNA_AttributeGroup; +extern StructRNA RNA_AssetHandle; +extern StructRNA RNA_AssetLibraryReference; extern StructRNA RNA_AssetMetaData; extern StructRNA RNA_AssetTag; extern StructRNA RNA_BackgroundImage; @@ -811,6 +813,7 @@ void RNA_struct_py_type_set(StructRNA *srna, void *py_type); void *RNA_struct_blender_type_get(StructRNA *srna); void RNA_struct_blender_type_set(StructRNA *srna, void *blender_type); +struct IDProperty **RNA_struct_idprops_p(PointerRNA *ptr); struct IDProperty *RNA_struct_idprops(PointerRNA *ptr, bool create); bool RNA_struct_idprops_check(StructRNA *srna); bool RNA_struct_idprops_register_check(const StructRNA *type); diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index c8010a0e1ae..d544083a749 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -245,6 +245,22 @@ extern const EnumPropertyItem *rna_enum_attribute_domain_itemf(struct ID *id, bo extern const EnumPropertyItem rna_enum_collection_color_items[]; +/** + * For ID filters (#FILTER_ID_AC, #FILTER_ID_AR, ...) an int isn't enough. This version allows 64 + * bit integers. So can't use the regular #EnumPropertyItem. Would be nice if RNA supported this + * itself. + * + * Meant to be used with #RNA_def_property_boolean_sdna() which supports 64 bit flags as well. + */ +struct IDFilterEnumPropertyItem { + const uint64_t flag; + const char *identifier; + const int icon; + const char *name; + const char *description; +}; +extern const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[]; + /* API calls */ int rna_node_tree_type_to_enum(struct bNodeTreeType *typeinfo); int rna_node_tree_idname_to_enum(const char *idname); diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 694636f0c94..6df03d19538 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -126,6 +126,97 @@ static const EnumPropertyItem rna_enum_override_library_property_operation_items {0, NULL, 0, NULL, NULL}, }; +/** + * \note Uses #IDFilterEnumPropertyItem, not EnumPropertyItem, to support 64 bit items. + */ +const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = { + /* Datablocks */ + {FILTER_ID_AC, "filter_action", ICON_ANIM_DATA, "Actions", "Show Action data-blocks"}, + {FILTER_ID_AR, + "filter_armature", + ICON_ARMATURE_DATA, + "Armatures", + "Show Armature data-blocks"}, + {FILTER_ID_BR, "filter_brush", ICON_BRUSH_DATA, "Brushes", "Show Brushes data-blocks"}, + {FILTER_ID_CA, "filter_camera", ICON_CAMERA_DATA, "Cameras", "Show Camera data-blocks"}, + {FILTER_ID_CF, "filter_cachefile", ICON_FILE, "Cache Files", "Show Cache File data-blocks"}, + {FILTER_ID_CU, "filter_curve", ICON_CURVE_DATA, "Curves", "Show Curve data-blocks"}, + {FILTER_ID_GD, + "filter_grease_pencil", + ICON_GREASEPENCIL, + "Grease Pencil", + "Show Grease pencil data-blocks"}, + {FILTER_ID_GR, + "filter_group", + ICON_OUTLINER_COLLECTION, + "Collections", + "Show Collection data-blocks"}, + {FILTER_ID_HA, "filter_hair", ICON_HAIR_DATA, "Hairs", "Show/hide Hair data-blocks"}, + {FILTER_ID_IM, "filter_image", ICON_IMAGE_DATA, "Images", "Show Image data-blocks"}, + {FILTER_ID_LA, "filter_light", ICON_LIGHT_DATA, "Lights", "Show Light data-blocks"}, + {FILTER_ID_LP, + "filter_light_probe", + ICON_OUTLINER_DATA_LIGHTPROBE, + "Light Probes", + "Show Light Probe data-blocks"}, + {FILTER_ID_LS, + "filter_linestyle", + ICON_LINE_DATA, + "Freestyle Linestyles", + "Show Freestyle's Line Style data-blocks"}, + {FILTER_ID_LT, "filter_lattice", ICON_LATTICE_DATA, "Lattices", "Show Lattice data-blocks"}, + {FILTER_ID_MA, + "filter_material", + ICON_MATERIAL_DATA, + "Materials", + "Show Material data-blocks"}, + {FILTER_ID_MB, "filter_metaball", ICON_META_DATA, "Metaballs", "Show Metaball data-blocks"}, + {FILTER_ID_MC, + "filter_movie_clip", + ICON_TRACKER_DATA, + "Movie Clips", + "Show Movie Clip data-blocks"}, + {FILTER_ID_ME, "filter_mesh", ICON_MESH_DATA, "Meshes", "Show Mesh data-blocks"}, + {FILTER_ID_MSK, "filter_mask", ICON_MOD_MASK, "Masks", "Show Mask data-blocks"}, + {FILTER_ID_NT, "filter_node_tree", ICON_NODETREE, "Node Trees", "Show Node Tree data-blocks"}, + {FILTER_ID_OB, "filter_object", ICON_OBJECT_DATA, "Objects", "Show Object data-blocks"}, + {FILTER_ID_PA, + "filter_particle_settings", + ICON_PARTICLE_DATA, + "Particles Settings", + "Show Particle Settings data-blocks"}, + {FILTER_ID_PAL, "filter_palette", ICON_COLOR, "Palettes", "Show Palette data-blocks"}, + {FILTER_ID_PC, + "filter_paint_curve", + ICON_CURVE_BEZCURVE, + "Paint Curves", + "Show Paint Curve data-blocks"}, + {FILTER_ID_PT, + "filter_pointcloud", + ICON_POINTCLOUD_DATA, + "Point Clouds", + "Show/hide Point Cloud data-blocks"}, + {FILTER_ID_SCE, "filter_scene", ICON_SCENE_DATA, "Scenes", "Show Scene data-blocks"}, + {FILTER_ID_SIM, + "filter_simulation", + ICON_PHYSICS, + "Simulations", + "Show Simulation data-blocks"}, /* TODO: Use correct icon. */ + {FILTER_ID_SPK, "filter_speaker", ICON_SPEAKER, "Speakers", "Show Speaker data-blocks"}, + {FILTER_ID_SO, "filter_sound", ICON_SOUND, "Sounds", "Show Sound data-blocks"}, + {FILTER_ID_TE, "filter_texture", ICON_TEXTURE_DATA, "Textures", "Show Texture data-blocks"}, + {FILTER_ID_TXT, "filter_text", ICON_TEXT, "Texts", "Show Text data-blocks"}, + {FILTER_ID_VF, "filter_font", ICON_FONT_DATA, "Fonts", "Show Font data-blocks"}, + {FILTER_ID_VO, "filter_volume", ICON_VOLUME_DATA, "Volumes", "Show/hide Volume data-blocks"}, + {FILTER_ID_WO, "filter_world", ICON_WORLD_DATA, "Worlds", "Show World data-blocks"}, + {FILTER_ID_WS, + "filter_work_space", + ICON_WORKSPACE, + "Workspaces", + "Show workspace data-blocks"}, + {0, NULL, 0, NULL, NULL}, +}; + #ifdef RNA_RUNTIME # include "DNA_anim_types.h" @@ -493,9 +584,10 @@ StructRNA *rna_ID_refine(PointerRNA *ptr) return ID_code_to_RNA_type(GS(id->name)); } -IDProperty *rna_ID_idprops(PointerRNA *ptr, bool create) +IDProperty **rna_ID_idprops(PointerRNA *ptr) { - return IDP_GetProperties(ptr->data, create); + ID *id = (ID *)ptr->data; + return &id->properties; } void rna_ID_fake_user_set(PointerRNA *ptr, bool value) @@ -510,9 +602,9 @@ void rna_ID_fake_user_set(PointerRNA *ptr, bool value) } } -IDProperty *rna_PropertyGroup_idprops(PointerRNA *ptr, bool UNUSED(create)) +IDProperty **rna_PropertyGroup_idprops(PointerRNA *ptr) { - return ptr->data; + return (IDProperty **)&ptr->data; } void rna_PropertyGroup_unregister(Main *UNUSED(bmain), StructRNA *type) @@ -1162,12 +1254,12 @@ static PointerRNA rna_IDPreview_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_ImagePreview, prv_img); } -static IDProperty *rna_IDPropertyWrapPtr_idprops(PointerRNA *ptr, bool UNUSED(create)) +static IDProperty **rna_IDPropertyWrapPtr_idprops(PointerRNA *ptr) { if (ptr == NULL) { return NULL; } - return ptr->data; + return (IDProperty **)&ptr->data; } static void rna_Library_version_get(PointerRNA *ptr, int *value) diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index a0a84bf4fc9..0285ef44e17 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -369,15 +369,32 @@ static bool rna_idproperty_ui_set_default(PointerRNA *ptr, return true; } -IDProperty *RNA_struct_idprops(PointerRNA *ptr, bool create) +IDProperty **RNA_struct_idprops_p(PointerRNA *ptr) { StructRNA *type = ptr->type; + if (type == NULL) { + return NULL; + } + if (type->idproperties == NULL) { + return NULL; + } - if (type && type->idproperties) { - return type->idproperties(ptr, create); + return type->idproperties(ptr); +} + +IDProperty *RNA_struct_idprops(PointerRNA *ptr, bool create) +{ + IDProperty **property_ptr = RNA_struct_idprops_p(ptr); + if (property_ptr == NULL) { + return NULL; } - return NULL; + if (create && *property_ptr == NULL) { + IDPropertyTemplate val = {0}; + *property_ptr = IDP_New(IDP_GROUP, &val, __func__); + } + + return *property_ptr; } bool RNA_struct_idprops_check(StructRNA *srna) @@ -2026,11 +2043,9 @@ bool RNA_property_enum_item_from_value( bool RNA_property_enum_item_from_value_gettexted( bContext *C, PointerRNA *ptr, PropertyRNA *prop, const int value, EnumPropertyItem *r_item) { - bool result; + const bool result = RNA_property_enum_item_from_value(C, ptr, prop, value, r_item); - result = RNA_property_enum_item_from_value(C, ptr, prop, value, r_item); - - if (!(prop->flag & PROP_ENUM_NO_TRANSLATE)) { + if (result && !(prop->flag & PROP_ENUM_NO_TRANSLATE)) { if (BLT_translate_iface()) { r_item->name = BLT_pgettext(prop->translation_context, r_item->name); } @@ -5849,12 +5864,12 @@ ID *RNA_find_real_ID_and_path(Main *bmain, ID *id, const char **r_path) *r_path = "collection"; break; default: - BLI_assert(!"Missing handling of embedded id type."); + BLI_assert_msg(0, "Missing handling of embedded id type."); } } if (id_type->owner_get == NULL) { - BLI_assert(!"Missing handling of embedded id type."); + BLI_assert_msg(0, "Missing handling of embedded id type."); return id; } return id_type->owner_get(bmain, id); diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index 816fc68195f..3912c873fd0 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -422,7 +422,9 @@ static int rna_property_override_diff(Main *bmain, bool override_changed = false; eRNAOverrideMatch diff_flags = flags; - if (!RNA_property_overridable_get(&prop_a->ptr, prop_a->rawprop)) { + if (!RNA_property_overridable_get(&prop_a->ptr, prop_a->rawprop) || + (!ELEM(RNA_property_type(prop_a->rawprop), PROP_POINTER, PROP_COLLECTION) && + !RNA_property_editable_flag(&prop_a->ptr, prop_a->rawprop))) { diff_flags &= ~RNA_OVERRIDE_COMPARE_CREATE; } const int diff = override_diff(bmain, @@ -865,7 +867,7 @@ bool RNA_struct_override_matches(Main *bmain, else { /* Too noisy for now, this triggers on runtime props like transform matrices etc. */ #if 0 - BLI_assert(!"We have differences between reference and " + BLI_assert_msg(0, "We have differences between reference and " "overriding data on non-editable property."); #endif matching = false; diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c index 7114e21beff..49d02524e43 100644 --- a/source/blender/makesrna/intern/rna_armature.c +++ b/source/blender/makesrna/intern/rna_armature.c @@ -260,28 +260,16 @@ static char *rna_Bone_path(PointerRNA *ptr) return BLI_sprintfN("bones[\"%s\"]", name_esc); } -static IDProperty *rna_Bone_idprops(PointerRNA *ptr, bool create) +static IDProperty **rna_Bone_idprops(PointerRNA *ptr) { Bone *bone = ptr->data; - - if (create && !bone->prop) { - IDPropertyTemplate val = {0}; - bone->prop = IDP_New(IDP_GROUP, &val, "RNA_Bone ID properties"); - } - - return bone->prop; + return &bone->prop; } -static IDProperty *rna_EditBone_idprops(PointerRNA *ptr, bool create) +static IDProperty **rna_EditBone_idprops(PointerRNA *ptr) { EditBone *ebone = ptr->data; - - if (create && !ebone->prop) { - IDPropertyTemplate val = {0}; - ebone->prop = IDP_New(IDP_GROUP, &val, "RNA_EditBone ID properties"); - } - - return ebone->prop; + return &ebone->prop; } static void rna_bone_layer_set(int *layer, const bool *values) diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c index 1af53e95cc9..0020d90ba1a 100644 --- a/source/blender/makesrna/intern/rna_asset.c +++ b/source/blender/makesrna/intern/rna_asset.c @@ -25,6 +25,7 @@ #include "DNA_asset_types.h" #include "DNA_defs.h" +#include "DNA_space_types.h" #include "rna_internal.h" @@ -35,6 +36,8 @@ # include "BLI_listbase.h" +# include "ED_asset.h" + # include "RNA_access.h" static AssetTag *rna_AssetMetaData_tag_new(AssetMetaData *asset_data, @@ -75,16 +78,10 @@ static void rna_AssetMetaData_tag_remove(AssetMetaData *asset_data, RNA_POINTER_INVALIDATE(tag_ptr); } -static IDProperty *rna_AssetMetaData_idprops(PointerRNA *ptr, bool create) +static IDProperty **rna_AssetMetaData_idprops(PointerRNA *ptr) { AssetMetaData *asset_data = ptr->data; - - if (create && !asset_data->properties) { - IDPropertyTemplate val = {0}; - asset_data->properties = IDP_New(IDP_GROUP, &val, "RNA_AssetMetaData group"); - } - - return asset_data->properties; + return &asset_data->properties; } static void rna_AssetMetaData_description_get(PointerRNA *ptr, char *value) @@ -129,6 +126,105 @@ static void rna_AssetMetaData_active_tag_range( *max = *softmax = MAX2(asset_data->tot_tags - 1, 0); } +static PointerRNA rna_AssetHandle_file_data_get(PointerRNA *ptr) +{ + AssetHandle *asset_handle = ptr->data; + return rna_pointer_inherit_refine(ptr, &RNA_FileSelectEntry, asset_handle->file_data); +} + +static void rna_AssetHandle_get_full_library_path( + // AssetHandle *asset, + bContext *C, + FileDirEntry *asset_file, + AssetLibraryReference *library, + char r_result[/*FILE_MAX_LIBEXTRA*/]) +{ + AssetHandle asset = {.file_data = asset_file}; + ED_asset_handle_get_full_library_path(C, library, &asset, r_result); +} + +static PointerRNA rna_AssetHandle_local_id_get(PointerRNA *ptr) +{ + const AssetHandle *asset = ptr->data; + ID *id = ED_assetlist_asset_local_id_get(asset); + return rna_pointer_inherit_refine(ptr, &RNA_ID, id); +} + +static void rna_AssetHandle_file_data_set(PointerRNA *ptr, + PointerRNA value, + struct ReportList *UNUSED(reports)) +{ + AssetHandle *asset_handle = ptr->data; + asset_handle->file_data = value.data; +} + +int rna_asset_library_reference_get(const AssetLibraryReference *library) +{ + return ED_asset_library_reference_to_enum_value(library); +} + +void rna_asset_library_reference_set(AssetLibraryReference *library, int value) +{ + *library = ED_asset_library_reference_from_enum_value(value); +} + +const EnumPropertyItem *rna_asset_library_reference_itemf(bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + const EnumPropertyItem predefined_items[] = { + /* For the future. */ + // {ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"}, + {ASSET_LIBRARY_LOCAL, + "LOCAL", + ICON_BLENDER, + "Current File", + "Show the assets currently available in this Blender session"}, + {0, NULL, 0, NULL, NULL}, + }; + + EnumPropertyItem *item = NULL; + int totitem = 0; + + /* Add separator if needed. */ + if (!BLI_listbase_is_empty(&U.asset_libraries)) { + const EnumPropertyItem sepr = {0, "", 0, "Custom", NULL}; + RNA_enum_item_add(&item, &totitem, &sepr); + } + + int i = 0; + for (bUserAssetLibrary *user_library = U.asset_libraries.first; user_library; + user_library = user_library->next, i++) { + /* Note that the path itself isn't checked for validity here. If an invalid library path is + * used, the Asset Browser can give a nice hint on what's wrong. */ + const bool is_valid = (user_library->name[0] && user_library->path[0]); + if (!is_valid) { + continue; + } + + /* Use library path as description, it's a nice hint for users. */ + EnumPropertyItem tmp = {ASSET_LIBRARY_CUSTOM + i, + user_library->name, + ICON_NONE, + user_library->name, + user_library->path}; + RNA_enum_item_add(&item, &totitem, &tmp); + } + + if (totitem) { + const EnumPropertyItem sepr = {0, "", 0, "Built-in", NULL}; + RNA_enum_item_add(&item, &totitem, &sepr); + } + + /* Add predefined items. */ + RNA_enum_items_add(&item, &totitem, predefined_items); + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + return item; +} + #else static void rna_def_asset_tag(BlenderRNA *brna) @@ -215,12 +311,87 @@ static void rna_def_asset_data(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Active Tag", "Index of the tag set for editing"); } +static void rna_def_asset_handle_api(StructRNA *srna) +{ + FunctionRNA *func; + PropertyRNA *parm; + + func = RNA_def_function(srna, "get_full_library_path", "rna_AssetHandle_get_full_library_path"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + /* TODO temporarily static function, for until .py can receive the asset handle from context + * properly. `asset_file_handle` should go away too then. */ + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "asset_file_handle", "FileSelectEntry", "", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, + "asset_library", + "AssetLibraryReference", + "", + "The asset library containing the given asset, only valid if the asset " + "library is external (i.e. not the \"Current File\" one"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_string(func, "result", NULL, FILE_MAX_LIBEXTRA, "result", ""); + RNA_def_parameter_flags(parm, PROP_THICK_WRAP, 0); + RNA_def_function_output(func, parm); +} + +static void rna_def_asset_handle(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "AssetHandle", "PropertyGroup"); + RNA_def_struct_ui_text(srna, "Asset Handle", "Reference to some asset"); + + /* TODO why is this editable? There probably shouldn't be a setter. */ + prop = RNA_def_property(srna, "file_data", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_struct_type(prop, "FileSelectEntry"); + RNA_def_property_pointer_funcs( + prop, "rna_AssetHandle_file_data_get", "rna_AssetHandle_file_data_set", NULL, NULL); + RNA_def_property_ui_text(prop, "File Entry", "File data used to refer to the asset"); + + prop = RNA_def_property(srna, "local_id", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "ID"); + RNA_def_property_pointer_funcs(prop, "rna_AssetHandle_local_id_get", NULL, NULL, NULL); + RNA_def_property_ui_text(prop, + "", + "The local data-block this asset represents; only valid if that is a " + "data-block in this file"); + RNA_def_property_flag(prop, PROP_HIDDEN); + + rna_def_asset_handle_api(srna); +} + +static void rna_def_asset_library_reference(BlenderRNA *brna) +{ + StructRNA *srna = RNA_def_struct(brna, "AssetLibraryReference", NULL); + RNA_def_struct_ui_text( + srna, "Asset Library Reference", "Identifier to refere to the asset library"); +} + +/** + * \note the UI text and updating has to be set by the caller. + */ +PropertyRNA *rna_def_asset_library_reference_common(struct StructRNA *srna, + const char *get, + const char *set) +{ + PropertyRNA *prop = RNA_def_property(srna, "active_asset_library", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, DummyRNA_NULL_items); + RNA_def_property_enum_funcs(prop, get, set, "rna_asset_library_reference_itemf"); + + return prop; +} + void RNA_def_asset(BlenderRNA *brna) { RNA_define_animate_sdna(false); rna_def_asset_tag(brna); rna_def_asset_data(brna); + rna_def_asset_library_reference(brna); + rna_def_asset_handle(brna); RNA_define_animate_sdna(true); } diff --git a/source/blender/makesrna/intern/rna_cloth.c b/source/blender/makesrna/intern/rna_cloth.c index 9e57368f8f9..2bc00dd5af5 100644 --- a/source/blender/makesrna/intern/rna_cloth.c +++ b/source/blender/makesrna/intern/rna_cloth.c @@ -652,7 +652,6 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "mass", PROP_FLOAT, PROP_UNIT_MASS); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_text(prop, "Vertex Mass", "The mass of each vertex on the cloth material"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, 0, "rna_cloth_update"); prop = RNA_def_property(srna, "vertex_group_mass", PROP_STRING, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_context.c b/source/blender/makesrna/intern/rna_context.c index 4079406e64b..9da08de2168 100644 --- a/source/blender/makesrna/intern/rna_context.c +++ b/source/blender/makesrna/intern/rna_context.c @@ -58,6 +58,8 @@ const EnumPropertyItem rna_enum_context_mode_items[] = { #ifdef RNA_RUNTIME +# include "DNA_asset_types.h" + # ifdef WITH_PYTHON # include "BPY_extern.h" # endif @@ -134,6 +136,20 @@ static PointerRNA rna_Context_gizmo_group_get(PointerRNA *ptr) return newptr; } +static PointerRNA rna_Context_asset_file_handle_get(PointerRNA *ptr) +{ + bContext *C = (bContext *)ptr->data; + bool is_handle_valid; + AssetHandle asset_handle = CTX_wm_asset_handle(C, &is_handle_valid); + if (!is_handle_valid) { + return PointerRNA_NULL; + } + + PointerRNA newptr; + RNA_pointer_create(NULL, &RNA_FileSelectEntry, asset_handle.file_data, &newptr); + return newptr; +} + static PointerRNA rna_Context_main_get(PointerRNA *ptr) { bContext *C = (bContext *)ptr->data; @@ -281,6 +297,17 @@ void RNA_def_context(BlenderRNA *brna) RNA_def_property_struct_type(prop, "GizmoGroup"); RNA_def_property_pointer_funcs(prop, "rna_Context_gizmo_group_get", NULL, NULL, NULL); + /* TODO can't expose AssetHandle, since there is no permanent storage to it (so we can't + * return a pointer). Instead provide the FileDirEntry pointer it wraps. */ + prop = RNA_def_property(srna, "asset_file_handle", PROP_POINTER, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_struct_type(prop, "FileSelectEntry"); + RNA_def_property_pointer_funcs(prop, "rna_Context_asset_file_handle_get", NULL, NULL, NULL); + RNA_def_property_ui_text(prop, + "", + "The file of an active asset. Avoid using this, it will be replaced by " + "a proper AssetHandle design"); + /* Data */ prop = RNA_def_property(srna, "blend_data", PROP_POINTER, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index 1a0d4ebb7e6..fadce9e3c89 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -52,7 +52,7 @@ static CLG_LogRef LOG = {"rna.define"}; # define ASSERT_SOFT_HARD_LIMITS \ if (softmin < hardmin || softmax > hardmax) { \ CLOG_ERROR(&LOG, "error with soft/hard limits: %s.%s", CONTAINER_RNA_ID(cont), identifier); \ - BLI_assert(!"invalid soft/hard limits"); \ + BLI_assert_msg(0, "invalid soft/hard limits"); \ } \ (void)0 #else diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index bfe9d4bb77c..9dc08430307 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -30,6 +30,7 @@ #define RNA_MAGIC ((int)~0) +struct AssetLibraryReference; struct FreestyleSettings; struct ID; struct IDOverrideLibrary; @@ -266,6 +267,16 @@ void rna_def_mtex_common(struct BlenderRNA *brna, void rna_def_texpaint_slots(struct BlenderRNA *brna, struct StructRNA *srna); void rna_def_view_layer_common(struct BlenderRNA *brna, struct StructRNA *srna, const bool scene); +PropertyRNA *rna_def_asset_library_reference_common(struct StructRNA *srna, + const char *get, + const char *set); +int rna_asset_library_reference_get(const struct AssetLibraryReference *library); +void rna_asset_library_reference_set(struct AssetLibraryReference *library, int value); +const EnumPropertyItem *rna_asset_library_reference_itemf(struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + bool *r_free); + void rna_def_actionbone_group_common(struct StructRNA *srna, int update_flag, const char *update_cb); @@ -276,10 +287,10 @@ void rna_ID_name_get(struct PointerRNA *ptr, char *value); int rna_ID_name_length(struct PointerRNA *ptr); void rna_ID_name_set(struct PointerRNA *ptr, const char *value); struct StructRNA *rna_ID_refine(struct PointerRNA *ptr); -struct IDProperty *rna_ID_idprops(struct PointerRNA *ptr, bool create); +struct IDProperty **rna_ID_idprops(struct PointerRNA *ptr); void rna_ID_fake_user_set(struct PointerRNA *ptr, bool value); void **rna_ID_instance(PointerRNA *ptr); -struct IDProperty *rna_PropertyGroup_idprops(struct PointerRNA *ptr, bool create); +struct IDProperty **rna_PropertyGroup_idprops(struct PointerRNA *ptr); void rna_PropertyGroup_unregister(struct Main *bmain, struct StructRNA *type); struct StructRNA *rna_PropertyGroup_register(struct Main *bmain, struct ReportList *reports, diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h index ee60b199d64..479306e8c06 100644 --- a/source/blender/makesrna/intern/rna_internal_types.h +++ b/source/blender/makesrna/intern/rna_internal_types.h @@ -54,7 +54,7 @@ typedef void (*ContextPropUpdateFunc)(struct bContext *C, typedef void (*ContextUpdateFunc)(struct bContext *C, struct PointerRNA *ptr); typedef int (*EditableFunc)(struct PointerRNA *ptr, const char **r_info); typedef int (*ItemEditableFunc)(struct PointerRNA *ptr, int index); -typedef struct IDProperty *(*IDPropertiesFunc)(struct PointerRNA *ptr, bool create); +typedef struct IDProperty **(*IDPropertiesFunc)(struct PointerRNA *ptr); typedef struct StructRNA *(*StructRefineFunc)(struct PointerRNA *ptr); typedef char *(*StructPathFunc)(struct PointerRNA *ptr); @@ -559,7 +559,7 @@ struct StructRNA { */ StructInstanceFunc instance; - /* callback to get id properties */ + /** Return the location of the struct's pointer to the root group IDProperty. */ IDPropertiesFunc idproperties; /* functions of this struct */ diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c index b4253ab9236..0414afe1514 100644 --- a/source/blender/makesrna/intern/rna_layer.c +++ b/source/blender/makesrna/intern/rna_layer.c @@ -119,16 +119,10 @@ static char *rna_ViewLayer_path(PointerRNA *ptr) return BLI_sprintfN("view_layers[\"%s\"]", name_esc); } -static IDProperty *rna_ViewLayer_idprops(PointerRNA *ptr, bool create) +static IDProperty **rna_ViewLayer_idprops(PointerRNA *ptr) { ViewLayer *view_layer = (ViewLayer *)ptr->data; - - if (create && !view_layer->id_properties) { - IDPropertyTemplate val = {0}; - view_layer->id_properties = IDP_New(IDP_GROUP, &val, "ViewLayer ID properties"); - } - - return view_layer->id_properties; + return &view_layer->id_properties; } static bool rna_LayerCollection_visible_get(LayerCollection *layer_collection, bContext *C) diff --git a/source/blender/makesrna/intern/rna_mask.c b/source/blender/makesrna/intern/rna_mask.c index 896e253049b..8c7d9698a67 100644 --- a/source/blender/makesrna/intern/rna_mask.c +++ b/source/blender/makesrna/intern/rna_mask.c @@ -505,7 +505,7 @@ static void rna_MaskSpline_points_add(ID *id, MaskSpline *spline, int count) if (!layer) { /* Shall not happen actually */ - BLI_assert(!"No layer found for the spline"); + BLI_assert_msg(0, "No layer found for the spline"); return; } diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 49c2cdf5692..a35755473f9 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -602,6 +602,7 @@ const EnumPropertyItem rna_enum_axis_flag_xyz_items[] = { # include "BKE_cachefile.h" # include "BKE_context.h" +# include "BKE_deform.h" # include "BKE_mesh_runtime.h" # include "BKE_modifier.h" # include "BKE_object.h" @@ -1250,12 +1251,13 @@ static const EnumPropertyItem *rna_DataTransferModifier_layers_select_src_itemf( # endif if (ob_src) { - bDeformGroup *dg; + const bDeformGroup *dg; int i; RNA_enum_item_add_separator(&item, &totitem); - for (i = 0, dg = ob_src->defbase.first; dg; i++, dg = dg->next) { + const ListBase *defbase = BKE_object_defgroup_list(ob_src); + for (i = 0, dg = defbase->first; dg; i++, dg = dg->next) { tmp_item.value = i; tmp_item.identifier = tmp_item.name = dg->name; RNA_enum_item_add(&item, &totitem, &tmp_item); @@ -1349,12 +1351,13 @@ static const EnumPropertyItem *rna_DataTransferModifier_layers_select_dst_itemf( Object *ob_dst = CTX_data_active_object(C); /* XXX Is this OK? */ if (ob_dst) { - bDeformGroup *dg; + const bDeformGroup *dg; int i; RNA_enum_item_add_separator(&item, &totitem); - for (i = 0, dg = ob_dst->defbase.first; dg; i++, dg = dg->next) { + const ListBase *defbase = BKE_object_defgroup_list(ob_dst); + for (i = 0, dg = defbase->first; dg; i++, dg = dg->next) { tmp_item.value = i; tmp_item.identifier = tmp_item.name = dg->name; RNA_enum_item_add(&item, &totitem, &tmp_item); @@ -1618,15 +1621,11 @@ static void rna_NodesModifier_node_group_update(Main *bmain, Scene *scene, Point MOD_nodes_update_interface(object, nmd); } -static IDProperty *rna_NodesModifier_properties(PointerRNA *ptr, bool create) +static IDProperty **rna_NodesModifier_properties(PointerRNA *ptr) { NodesModifierData *nmd = ptr->data; NodesModifierSettings *settings = &nmd->settings; - if (create && settings->properties == NULL) { - IDPropertyTemplate val = {0}; - settings->properties = IDP_New(IDP_GROUP, &val, "Nodes Modifier Settings"); - } - return settings->properties; + return &settings->properties; } #else @@ -6915,6 +6914,15 @@ static void rna_def_modifier_surfacedeform(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "use_sparse_bind", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_SDEF_SPARSE_BIND); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text( + prop, + "Sparse Bind", + "Only record binding data for vertices matching the vertex group at the time of bind"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, -100, 100); RNA_def_property_ui_range(prop, -100, 100, 10, 2); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 5c363a3e6b6..4712f4a0a0b 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -2366,16 +2366,10 @@ static StructRNA *rna_FunctionNode_register(Main *bmain, return nt->rna_ext.srna; } -static IDProperty *rna_Node_idprops(PointerRNA *ptr, bool create) +static IDProperty **rna_Node_idprops(PointerRNA *ptr) { bNode *node = ptr->data; - - if (create && !node->prop) { - IDPropertyTemplate val = {0}; - node->prop = IDP_New(IDP_GROUP, &val, "RNA_Node ID properties"); - } - - return node->prop; + return &node->prop; } static void rna_Node_parent_set(PointerRNA *ptr, @@ -2834,16 +2828,10 @@ static char *rna_NodeSocket_path(PointerRNA *ptr) } } -static IDProperty *rna_NodeSocket_idprops(PointerRNA *ptr, bool create) +static IDProperty **rna_NodeSocket_idprops(PointerRNA *ptr) { bNodeSocket *sock = ptr->data; - - if (create && !sock->prop) { - IDPropertyTemplate val = {0}; - sock->prop = IDP_New(IDP_GROUP, &val, "RNA_NodeSocket ID properties"); - } - - return sock->prop; + return &sock->prop; } static PointerRNA rna_NodeSocket_node_get(PointerRNA *ptr) @@ -3150,16 +3138,10 @@ static char *rna_NodeSocketInterface_path(PointerRNA *ptr) return NULL; } -static IDProperty *rna_NodeSocketInterface_idprops(PointerRNA *ptr, bool create) +static IDProperty **rna_NodeSocketInterface_idprops(PointerRNA *ptr) { bNodeSocket *sock = ptr->data; - - if (create && !sock->prop) { - IDPropertyTemplate val = {0}; - sock->prop = IDP_New(IDP_GROUP, &val, "RNA_NodeSocketInterface ID properties"); - } - - return sock->prop; + return &sock->prop; } static void rna_NodeSocketInterface_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) @@ -4406,7 +4388,7 @@ static int point_density_particle_color_source_from_shader( case SHD_POINTDENSITY_COLOR_PARTVEL: return TEX_PD_COLOR_PARTVEL; default: - BLI_assert(!"Unknown color source"); + BLI_assert_msg(0, "Unknown color source"); return TEX_PD_COLOR_CONSTANT; } } @@ -4422,7 +4404,7 @@ static int point_density_vertex_color_source_from_shader( case SHD_POINTDENSITY_COLOR_VERTNOR: return TEX_PD_COLOR_VERTNOR; default: - BLI_assert(!"Unknown color source"); + BLI_assert_msg(0, "Unknown color source"); return TEX_PD_COLOR_CONSTANT; } } @@ -9947,6 +9929,45 @@ static void def_geo_switch(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_curve_primitive_quadrilateral(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem mode_items[] = { + {GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE, + "RECTANGLE", + 0, + "Rectangle", + "Create a rectangle"}, + {GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_PARALLELOGRAM, + "PARALLELOGRAM", + 0, + "Parallelogram", + "Create a parallelogram"}, + {GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_TRAPEZOID, + "TRAPEZOID", + 0, + "Trapezoid", + "Create a trapezoid"}, + {GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_KITE, "KITE", 0, "Kite", "Create a Kite / Dart"}, + {GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_POINTS, + "POINTS", + 0, + "Points", + "Create a quadrilateral from four points"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurvePrimitiveQuad", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_enum_default(prop, GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE); + RNA_def_property_ui_text(prop, "Mode", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_curve_resample(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 7c012922c2c..ed681291e29 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -790,9 +790,27 @@ static void rna_Object_dup_collection_set(PointerRNA *ptr, } } +static void rna_Object_vertex_groups_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + Object *ob = (Object *)ptr->data; + if (!BKE_object_supports_vertex_groups(ob)) { + iter->valid = 0; + return; + } + + ListBase *defbase = BKE_object_defgroup_list_mutable(ob); + iter->valid = defbase != NULL; + + rna_iterator_listbase_begin(iter, defbase, NULL); +} + static void rna_VertexGroup_name_set(PointerRNA *ptr, const char *value) { Object *ob = (Object *)ptr->owner_id; + if (!BKE_object_supports_vertex_groups(ob)) { + return; + } + bDeformGroup *dg = (bDeformGroup *)ptr->data; BLI_strncpy_utf8(dg->name, value, sizeof(dg->name)); BKE_object_defgroup_unique_name(dg, ob); @@ -801,15 +819,25 @@ static void rna_VertexGroup_name_set(PointerRNA *ptr, const char *value) static int rna_VertexGroup_index_get(PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; + if (!BKE_object_supports_vertex_groups(ob)) { + return -1; + } - return BLI_findindex(&ob->defbase, ptr->data); + const ListBase *defbase = BKE_object_defgroup_list(ob); + return BLI_findindex(defbase, ptr->data); } static PointerRNA rna_Object_active_vertex_group_get(PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; + if (!BKE_object_supports_vertex_groups(ob)) { + return PointerRNA_NULL; + } + + const ListBase *defbase = BKE_object_defgroup_list(ob); + return rna_pointer_inherit_refine( - ptr, &RNA_VertexGroup, BLI_findlink(&ob->defbase, ob->actdef - 1)); + ptr, &RNA_VertexGroup, BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1)); } static void rna_Object_active_vertex_group_set(PointerRNA *ptr, @@ -817,7 +845,13 @@ static void rna_Object_active_vertex_group_set(PointerRNA *ptr, struct ReportList *reports) { Object *ob = (Object *)ptr->owner_id; - int index = BLI_findindex(&ob->defbase, value.data); + if (!BKE_object_supports_vertex_groups(ob)) { + return; + } + + const ListBase *defbase = BKE_object_defgroup_list(ob); + + int index = BLI_findindex(defbase, value.data); if (index == -1) { BKE_reportf(reports, RPT_ERROR, @@ -827,19 +861,27 @@ static void rna_Object_active_vertex_group_set(PointerRNA *ptr, return; } - ob->actdef = index + 1; + BKE_object_defgroup_active_index_set(ob, index + 1); } static int rna_Object_active_vertex_group_index_get(PointerRNA *ptr) { Object *ob = (Object *)ptr->owner_id; - return ob->actdef - 1; + if (!BKE_object_supports_vertex_groups(ob)) { + return -1; + } + + return BKE_object_defgroup_active_index_get(ob) - 1; } static void rna_Object_active_vertex_group_index_set(PointerRNA *ptr, int value) { Object *ob = (Object *)ptr->owner_id; - ob->actdef = value + 1; + if (!BKE_object_supports_vertex_groups(ob)) { + return; + } + + BKE_object_defgroup_active_index_set(ob, value + 1); } static void rna_Object_active_vertex_group_index_range( @@ -848,15 +890,24 @@ static void rna_Object_active_vertex_group_index_range( Object *ob = (Object *)ptr->owner_id; *min = 0; - *max = max_ii(0, BLI_listbase_count(&ob->defbase) - 1); + if (!BKE_object_supports_vertex_groups(ob)) { + *max = 0; + return; + } + const ListBase *defbase = BKE_object_defgroup_list(ob); + *max = max_ii(0, BLI_listbase_count(defbase) - 1); } void rna_object_vgroup_name_index_get(PointerRNA *ptr, char *value, int index) { Object *ob = (Object *)ptr->owner_id; - bDeformGroup *dg; + if (!BKE_object_supports_vertex_groups(ob)) { + value[0] = '\0'; + return; + } - dg = BLI_findlink(&ob->defbase, index - 1); + const ListBase *defbase = BKE_object_defgroup_list(ob); + const bDeformGroup *dg = BLI_findlink(defbase, index - 1); if (dg) { BLI_strncpy(value, dg->name, sizeof(dg->name)); @@ -869,21 +920,34 @@ void rna_object_vgroup_name_index_get(PointerRNA *ptr, char *value, int index) int rna_object_vgroup_name_index_length(PointerRNA *ptr, int index) { Object *ob = (Object *)ptr->owner_id; - bDeformGroup *dg; + if (!BKE_object_supports_vertex_groups(ob)) { + return 0; + } - dg = BLI_findlink(&ob->defbase, index - 1); + const ListBase *defbase = BKE_object_defgroup_list(ob); + bDeformGroup *dg = BLI_findlink(defbase, index - 1); return (dg) ? strlen(dg->name) : 0; } void rna_object_vgroup_name_index_set(PointerRNA *ptr, const char *value, short *index) { Object *ob = (Object *)ptr->owner_id; + if (!BKE_object_supports_vertex_groups(ob)) { + *index = -1; + return; + } + *index = BKE_object_defgroup_name_index(ob, value) + 1; } void rna_object_vgroup_name_set(PointerRNA *ptr, const char *value, char *result, int maxlen) { Object *ob = (Object *)ptr->owner_id; + if (!BKE_object_supports_vertex_groups(ob)) { + result[0] = '\0'; + return; + } + bDeformGroup *dg = BKE_object_defgroup_find_name(ob, value); if (dg) { /* No need for BLI_strncpy_utf8, since this matches an existing group. */ @@ -1933,16 +1997,25 @@ static void rna_Object_boundbox_get(PointerRNA *ptr, float *values) } } +static bool check_object_vgroup_support_and_warn(const Object *ob, + const char *op_name, + ReportList *reports) +{ + if (!BKE_object_supports_vertex_groups(ob)) { + const char *ob_type_name = "Unknown"; + RNA_enum_name_from_value(rna_enum_object_type_items, ob->type, &ob_type_name); + BKE_reportf(reports, RPT_ERROR, "%s is not supported for '%s' objects", op_name, ob_type_name); + return false; + } + return true; +} + static bDeformGroup *rna_Object_vgroup_new(Object *ob, Main *bmain, ReportList *reports, const char *name) { - if (!OB_TYPE_SUPPORT_VGROUP(ob->type)) { - const char *ob_type_name = "Unknown"; - RNA_enum_name_from_value(rna_enum_object_type_items, ob->type, &ob_type_name); - BKE_reportf( - reports, RPT_ERROR, "VertexGroups.new(): is not supported for '%s' objects", ob_type_name); + if (!check_object_vgroup_support_and_warn(ob, "VertexGroups.new()", reports)) { return NULL; } @@ -1959,8 +2032,14 @@ static void rna_Object_vgroup_remove(Object *ob, ReportList *reports, PointerRNA *defgroup_ptr) { + if (!check_object_vgroup_support_and_warn(ob, "VertexGroups.remove()", reports)) { + return; + } + bDeformGroup *defgroup = defgroup_ptr->data; - if (BLI_findindex(&ob->defbase, defgroup) == -1) { + ListBase *defbase = BKE_object_defgroup_list_mutable(ob); + + if (BLI_findindex(defbase, defgroup) == -1) { BKE_reportf(reports, RPT_ERROR, "DeformGroup '%s' not in object '%s'", @@ -1976,8 +2055,12 @@ static void rna_Object_vgroup_remove(Object *ob, WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob); } -static void rna_Object_vgroup_clear(Object *ob, Main *bmain) +static void rna_Object_vgroup_clear(Object *ob, Main *bmain, ReportList *reports) { + if (!check_object_vgroup_support_and_warn(ob, "VertexGroups.clear()", reports)) { + return; + } + BKE_object_defgroup_remove_all(ob); DEG_relations_tag_update(bmain); @@ -2687,7 +2770,6 @@ static void rna_def_object_vertex_groups(BlenderRNA *brna, PropertyRNA *cprop) prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_int_sdna(prop, NULL, "actdef"); RNA_def_property_int_funcs(prop, "rna_Object_active_vertex_group_index_get", "rna_Object_active_vertex_group_index_set", @@ -2712,7 +2794,7 @@ static void rna_def_object_vertex_groups(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); func = RNA_def_function(srna, "clear", "rna_Object_vgroup_clear"); - RNA_def_function_flag(func, FUNC_USE_MAIN); + RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS); RNA_def_function_ui_description(func, "Delete all vertex groups from object"); } @@ -3274,7 +3356,15 @@ static void rna_def_object(BlenderRNA *brna) /* vertex groups */ prop = RNA_def_property(srna, "vertex_groups", PROP_COLLECTION, PROP_NONE); - RNA_def_property_collection_sdna(prop, NULL, "defbase", NULL); + RNA_def_property_collection_funcs(prop, + "rna_Object_vertex_groups_begin", + "rna_iterator_listbase_next", + "rna_iterator_listbase_end", + "rna_iterator_listbase_get", + NULL, + NULL, + NULL, + NULL); RNA_def_property_struct_type(prop, "VertexGroup"); RNA_def_property_override_clear_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Vertex Groups", "Vertex groups of the object"); diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index f8a98eb1753..98d59bf3a1a 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -133,8 +133,9 @@ static bool rna_Cache_get_valid_owner_ID(PointerRNA *ptr, Object **ob, Scene **s *scene = (Scene *)ptr->owner_id; break; default: - BLI_assert(!"Trying to get PTCacheID from an invalid ID type " - "(Only scenes and objects are supported)."); + BLI_assert_msg(0, + "Trying to get PTCacheID from an invalid ID type " + "(Only scenes and objects are supported)."); break; } diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index 8edb80f68c5..de4cfb2b61a 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -1430,9 +1430,10 @@ static void psys_vg_name_get__internal(PointerRNA *ptr, char *value, int index) { Object *ob = (Object *)ptr->owner_id; ParticleSystem *psys = (ParticleSystem *)ptr->data; + const ListBase *defbase = BKE_object_defgroup_list(ob); if (psys->vgroup[index] > 0) { - bDeformGroup *defGroup = BLI_findlink(&ob->defbase, psys->vgroup[index] - 1); + bDeformGroup *defGroup = BLI_findlink(defbase, psys->vgroup[index] - 1); if (defGroup) { strcpy(value, defGroup->name); @@ -1448,7 +1449,8 @@ static int psys_vg_name_len__internal(PointerRNA *ptr, int index) ParticleSystem *psys = (ParticleSystem *)ptr->data; if (psys->vgroup[index] > 0) { - bDeformGroup *defGroup = BLI_findlink(&ob->defbase, psys->vgroup[index] - 1); + const ListBase *defbase = BKE_object_defgroup_list(ob); + bDeformGroup *defGroup = BLI_findlink(defbase, psys->vgroup[index] - 1); if (defGroup) { return strlen(defGroup->name); diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index bb4939a010b..ee509fa92d4 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -224,16 +224,10 @@ static void rna_BoneGroup_name_set(PointerRNA *ptr, const char *value) sizeof(agrp->name)); } -static IDProperty *rna_PoseBone_idprops(PointerRNA *ptr, bool create) +static IDProperty **rna_PoseBone_idprops(PointerRNA *ptr) { bPoseChannel *pchan = ptr->data; - - if (create && !pchan->prop) { - IDPropertyTemplate val = {0}; - pchan->prop = IDP_New(IDP_GROUP, &val, "RNA_PoseBone group"); - } - - return pchan->prop; + return &pchan->prop; } static void rna_Pose_ik_solver_set(struct PointerRNA *ptr, int value) diff --git a/source/blender/makesrna/intern/rna_pose_api.c b/source/blender/makesrna/intern/rna_pose_api.c index 0d35365c2d8..1e1667f0ae8 100644 --- a/source/blender/makesrna/intern/rna_pose_api.c +++ b/source/blender/makesrna/intern/rna_pose_api.c @@ -117,7 +117,7 @@ static void rna_Pose_apply_pose_from_action(ID *pose_owner, Object *pose_owner_ob = (Object *)pose_owner; AnimationEvalContext anim_eval_context = {CTX_data_depsgraph_pointer(C), evaluation_time}; - BKE_pose_apply_action(pose_owner_ob, action, &anim_eval_context); + BKE_pose_apply_action_selected_bones(pose_owner_ob, action, &anim_eval_context); /* Do NOT tag with ID_RECALC_ANIMATION, as that would overwrite the just-applied pose. */ DEG_id_tag_update(pose_owner, ID_RECALC_GEOMETRY); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 0a91d5f01bc..5c9c7b50339 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3116,6 +3116,7 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Use Snapping", "Snap to strip edges or current frame"); RNA_def_property_ui_icon(prop, ICON_SNAP_OFF, 1); RNA_def_property_boolean_default(prop, true); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* Publish message-bus. */ prop = RNA_def_property(srna, "snap_elements", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "snap_mode"); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 5f6456d3d1e..b1f0b0d760f 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -734,16 +734,10 @@ static char *rna_Sequence_path(PointerRNA *ptr) } } -static IDProperty *rna_Sequence_idprops(PointerRNA *ptr, bool create) +static IDProperty **rna_Sequence_idprops(PointerRNA *ptr) { Sequence *seq = ptr->data; - - if (create && !seq->prop) { - IDPropertyTemplate val = {0}; - seq->prop = IDP_New(IDP_GROUP, &val, "Sequence ID properties"); - } - - return seq->prop; + return &seq->prop; } static bool rna_MovieSequence_reload_if_needed(ID *scene_id, Sequence *seq, Main *bmain) diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index 8aab0c079a3..057f49c0319 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -30,6 +30,8 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "SEQ_edit.h" + #include "rna_internal.h" #ifdef RNA_RUNTIME @@ -99,6 +101,24 @@ static void rna_Sequences_move_strip_to_meta( WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene); } +static Sequence *rna_Sequence_split( + ID *id, Sequence *seq, Main *bmain, int frame, int split_method) +{ + Scene *scene = (Scene *)id; + Editing *ed = SEQ_editing_get(scene, false); + ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); + + Sequence *r_seq = SEQ_edit_strip_split(bmain, scene, seqbase, seq, frame, split_method); + + /* Update depsgraph. */ + DEG_relations_tag_update(bmain); + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); + + WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene); + + return r_seq; +} + static Sequence *rna_Sequences_new_clip(ID *id, ListBase *seqbase, Main *bmain, @@ -635,6 +655,12 @@ void RNA_api_sequence_strip(StructRNA *srna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem seq_split_method_items[] = { + {SEQ_SPLIT_SOFT, "SOFT", 0, "Soft", ""}, + {SEQ_SPLIT_HARD, "HARD", 0, "Hard", ""}, + {0, NULL, 0, NULL, NULL}, + }; + func = RNA_def_function(srna, "update", "rna_Sequence_update_rnafunc"); RNA_def_function_flag(func, FUNC_USE_SELF_ID); RNA_def_function_ui_description(func, "Update the strip dimensions"); @@ -676,6 +702,18 @@ void RNA_api_sequence_strip(StructRNA *srna) "Invalidate cached images for strip and all dependent strips"); parm = RNA_def_enum(func, "type", seq_cahce_type_items, 0, "Type", "Cache Type"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + + func = RNA_def_function(srna, "split", "rna_Sequence_split"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN); + RNA_def_function_ui_description(func, "Split Sequence"); + parm = RNA_def_int( + func, "frame", 0, INT_MIN, INT_MAX, "", "Frame where to split the strip", INT_MIN, INT_MAX); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_enum(func, "split_method", seq_split_method_items, 0, "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + /* Retirn type. */ + parm = RNA_def_pointer(func, "sequence", "Sequence", "", "Right side Sequence"); + RNA_def_function_return(func, parm); } void RNA_api_sequence_elements(BlenderRNA *brna, PropertyRNA *cprop) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 11110dd154a..f2d2b190d87 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -509,6 +509,7 @@ static const EnumPropertyItem rna_enum_curve_display_handle_items[] = { #ifdef RNA_RUNTIME # include "DNA_anim_types.h" +# include "DNA_asset_types.h" # include "DNA_scene_types.h" # include "DNA_screen_types.h" # include "DNA_userdef_types.h" @@ -1041,16 +1042,10 @@ static bool rna_RegionView3D_is_orthographic_side_view_get(PointerRNA *ptr) return RV3D_VIEW_IS_AXIS(rv3d->view); } -static IDProperty *rna_View3DShading_idprops(PointerRNA *ptr, bool create) +static IDProperty **rna_View3DShading_idprops(PointerRNA *ptr) { View3DShading *shading = ptr->data; - - if (create && !shading->prop) { - IDPropertyTemplate val = {0}; - shading->prop = IDP_New(IDP_GROUP, &val, "View3DShading ID properties"); - } - - return shading->prop; + return &shading->prop; } static void rna_3DViewShading_type_update(Main *bmain, Scene *scene, PointerRNA *ptr) @@ -2567,6 +2562,8 @@ static PointerRNA rna_FileSelectParams_filter_id_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_FileSelectIDFilter, ptr->data); } +/* TODO use rna_def_asset_library_reference_common() */ + static int rna_FileAssetSelectParams_asset_library_get(PointerRNA *ptr) { FileAssetSelectParams *params = ptr->data; @@ -2574,7 +2571,7 @@ static int rna_FileAssetSelectParams_asset_library_get(PointerRNA *ptr) BLI_assert(ptr->type == &RNA_FileAssetSelectParams); /* Simple case: Predefined repo, just set the value. */ - if (params->asset_library.type < FILE_ASSET_LIBRARY_CUSTOM) { + if (params->asset_library.type < ASSET_LIBRARY_CUSTOM) { return params->asset_library.type; } @@ -2583,11 +2580,11 @@ static int rna_FileAssetSelectParams_asset_library_get(PointerRNA *ptr) const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index( &U, params->asset_library.custom_library_index); if (user_library) { - return FILE_ASSET_LIBRARY_CUSTOM + params->asset_library.custom_library_index; + return ASSET_LIBRARY_CUSTOM + params->asset_library.custom_library_index; } BLI_assert(0); - return FILE_ASSET_LIBRARY_LOCAL; + return ASSET_LIBRARY_LOCAL; } static void rna_FileAssetSelectParams_asset_library_set(PointerRNA *ptr, int value) @@ -2595,26 +2592,26 @@ static void rna_FileAssetSelectParams_asset_library_set(PointerRNA *ptr, int val FileAssetSelectParams *params = ptr->data; /* Simple case: Predefined repo, just set the value. */ - if (value < FILE_ASSET_LIBRARY_CUSTOM) { + if (value < ASSET_LIBRARY_CUSTOM) { params->asset_library.type = value; params->asset_library.custom_library_index = -1; - BLI_assert(ELEM(value, FILE_ASSET_LIBRARY_LOCAL)); + BLI_assert(ELEM(value, ASSET_LIBRARY_LOCAL)); return; } const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index( - &U, value - FILE_ASSET_LIBRARY_CUSTOM); + &U, value - ASSET_LIBRARY_CUSTOM); /* Note that the path isn't checked for validity here. If an invalid library path is used, the * Asset Browser can give a nice hint on what's wrong. */ const bool is_valid = (user_library->name[0] && user_library->path[0]); if (!user_library) { - params->asset_library.type = FILE_ASSET_LIBRARY_LOCAL; + params->asset_library.type = ASSET_LIBRARY_LOCAL; params->asset_library.custom_library_index = -1; } else if (user_library && is_valid) { - params->asset_library.custom_library_index = value - FILE_ASSET_LIBRARY_CUSTOM; - params->asset_library.type = FILE_ASSET_LIBRARY_CUSTOM; + params->asset_library.custom_library_index = value - ASSET_LIBRARY_CUSTOM; + params->asset_library.type = ASSET_LIBRARY_CUSTOM; } } @@ -2623,8 +2620,8 @@ static const EnumPropertyItem *rna_FileAssetSelectParams_asset_library_itemf( { const EnumPropertyItem predefined_items[] = { /* For the future. */ - // {FILE_ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"}, - {FILE_ASSET_LIBRARY_LOCAL, + // {ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"}, + {ASSET_LIBRARY_LOCAL, "LOCAL", ICON_BLENDER, "Current File", @@ -2652,7 +2649,7 @@ static const EnumPropertyItem *rna_FileAssetSelectParams_asset_library_itemf( } /* Use library path as description, it's a nice hint for users. */ - EnumPropertyItem tmp = {FILE_ASSET_LIBRARY_CUSTOM + i, + EnumPropertyItem tmp = {ASSET_LIBRARY_CUSTOM + i, user_library->name, ICON_NONE, user_library->name, @@ -2697,6 +2694,32 @@ static int rna_FileBrowser_FileSelectEntry_name_length(PointerRNA *ptr) return (int)strlen(entry->name); } +static const EnumPropertyItem *rna_FileBrowser_FileSelectEntry_id_type_itemf( + bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *UNUSED(r_free)) +{ + const FileDirEntry *entry = ptr->data; + if (entry->blentype == 0) { + static const EnumPropertyItem none_items[] = { + {0, "NONE", 0, "None", ""}, + }; + return none_items; + } + + return rna_enum_id_type_items; +} + +static int rna_FileBrowser_FileSelectEntry_id_type_get(PointerRNA *ptr) +{ + const FileDirEntry *entry = ptr->data; + return entry->blentype; +} + +static PointerRNA rna_FileBrowser_FileSelectEntry_local_id_get(PointerRNA *ptr) +{ + const FileDirEntry *entry = ptr->data; + return rna_pointer_inherit_refine(ptr, &RNA_ID, entry->id); +} + static int rna_FileBrowser_FileSelectEntry_preview_icon_id_get(PointerRNA *ptr) { const FileDirEntry *entry = ptr->data; @@ -2721,7 +2744,7 @@ static StructRNA *rna_FileBrowser_params_typef(PointerRNA *ptr) return &RNA_FileAssetSelectParams; } - BLI_assert(!"Could not identify file select parameters"); + BLI_assert_msg(0, "Could not identify file select parameters"); return NULL; } @@ -3194,6 +3217,45 @@ static const EnumPropertyItem dt_uv_items[] = { {0, NULL, 0, NULL, NULL}, }; +static struct IDFilterEnumPropertyItem rna_enum_space_file_id_filter_categories[] = { + /* Categories */ + {FILTER_ID_SCE, "category_scene", ICON_SCENE_DATA, "Scenes", "Show scenes"}, + {FILTER_ID_AC, "category_animation", ICON_ANIM_DATA, "Animations", "Show animation data"}, + {FILTER_ID_OB | FILTER_ID_GR, + "category_object", + ICON_OUTLINER_COLLECTION, + "Objects & Collections", + "Show objects and collections"}, + {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME | FILTER_ID_HA | + FILTER_ID_PT | FILTER_ID_VO, + "category_geometry", + ICON_NODETREE, + "Geometry", + "Show meshes, curves, lattice, armatures and metaballs data"}, + {FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE, + "category_shading", + ICON_MATERIAL_DATA, + "Shading", + "Show materials, nodetrees, textures and Freestyle's linestyles"}, + {FILTER_ID_IM | FILTER_ID_MC | FILTER_ID_MSK | FILTER_ID_SO, + "category_image", + ICON_IMAGE_DATA, + "Images & Sounds", + "Show images, movie clips, sounds and masks"}, + {FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_LP | FILTER_ID_SPK | FILTER_ID_WO, + "category_environment", + ICON_WORLD_DATA, + "Environment", + "Show worlds, lights, cameras and speakers"}, + {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | + FILTER_ID_VF | FILTER_ID_CF | FILTER_ID_WS, + "category_misc", + ICON_GREASEPENCIL, + "Miscellaneous", + "Show other data types"}, + {0, NULL, 0, NULL, NULL}, +}; + static void rna_def_space_generic_show_region_toggles(StructRNA *srna, int region_type_mask) { PropertyRNA *prop; @@ -6141,142 +6203,6 @@ static void rna_def_space_console(BlenderRNA *brna) /* Filter for datablock types in link/append. */ static void rna_def_fileselect_idfilter(BlenderRNA *brna) { - struct IDFilterBoolean { - /* 64 bit, so we can't use bitflag enum. */ - const uint64_t flag; - const char *identifier; - const int icon; - const char *name; - const char *description; - }; - - static const struct IDFilterBoolean booleans[] = { - /* Datablocks */ - {FILTER_ID_AC, "filter_action", ICON_ANIM_DATA, "Actions", "Show Action data-blocks"}, - {FILTER_ID_AR, - "filter_armature", - ICON_ARMATURE_DATA, - "Armatures", - "Show Armature data-blocks"}, - {FILTER_ID_BR, "filter_brush", ICON_BRUSH_DATA, "Brushes", "Show Brushes data-blocks"}, - {FILTER_ID_CA, "filter_camera", ICON_CAMERA_DATA, "Cameras", "Show Camera data-blocks"}, - {FILTER_ID_CF, "filter_cachefile", ICON_FILE, "Cache Files", "Show Cache File data-blocks"}, - {FILTER_ID_CU, "filter_curve", ICON_CURVE_DATA, "Curves", "Show Curve data-blocks"}, - {FILTER_ID_GD, - "filter_grease_pencil", - ICON_GREASEPENCIL, - "Grease Pencil", - "Show Grease pencil data-blocks"}, - {FILTER_ID_GR, - "filter_group", - ICON_OUTLINER_COLLECTION, - "Collections", - "Show Collection data-blocks"}, - {FILTER_ID_HA, "filter_hair", ICON_HAIR_DATA, "Hairs", "Show/hide Hair data-blocks"}, - {FILTER_ID_IM, "filter_image", ICON_IMAGE_DATA, "Images", "Show Image data-blocks"}, - {FILTER_ID_LA, "filter_light", ICON_LIGHT_DATA, "Lights", "Show Light data-blocks"}, - {FILTER_ID_LP, - "filter_light_probe", - ICON_OUTLINER_DATA_LIGHTPROBE, - "Light Probes", - "Show Light Probe data-blocks"}, - {FILTER_ID_LS, - "filter_linestyle", - ICON_LINE_DATA, - "Freestyle Linestyles", - "Show Freestyle's Line Style data-blocks"}, - {FILTER_ID_LT, "filter_lattice", ICON_LATTICE_DATA, "Lattices", "Show Lattice data-blocks"}, - {FILTER_ID_MA, - "filter_material", - ICON_MATERIAL_DATA, - "Materials", - "Show Material data-blocks"}, - {FILTER_ID_MB, "filter_metaball", ICON_META_DATA, "Metaballs", "Show Metaball data-blocks"}, - {FILTER_ID_MC, - "filter_movie_clip", - ICON_TRACKER_DATA, - "Movie Clips", - "Show Movie Clip data-blocks"}, - {FILTER_ID_ME, "filter_mesh", ICON_MESH_DATA, "Meshes", "Show Mesh data-blocks"}, - {FILTER_ID_MSK, "filter_mask", ICON_MOD_MASK, "Masks", "Show Mask data-blocks"}, - {FILTER_ID_NT, - "filter_node_tree", - ICON_NODETREE, - "Node Trees", - "Show Node Tree data-blocks"}, - {FILTER_ID_OB, "filter_object", ICON_OBJECT_DATA, "Objects", "Show Object data-blocks"}, - {FILTER_ID_PA, - "filter_particle_settings", - ICON_PARTICLE_DATA, - "Particles Settings", - "Show Particle Settings data-blocks"}, - {FILTER_ID_PAL, "filter_palette", ICON_COLOR, "Palettes", "Show Palette data-blocks"}, - {FILTER_ID_PC, - "filter_paint_curve", - ICON_CURVE_BEZCURVE, - "Paint Curves", - "Show Paint Curve data-blocks"}, - {FILTER_ID_PT, - "filter_pointcloud", - ICON_POINTCLOUD_DATA, - "Point Clouds", - "Show/hide Point Cloud data-blocks"}, - {FILTER_ID_SCE, "filter_scene", ICON_SCENE_DATA, "Scenes", "Show Scene data-blocks"}, - {FILTER_ID_SIM, - "filter_simulation", - ICON_PHYSICS, - "Simulations", - "Show Simulation data-blocks"}, /* TODO: Use correct icon. */ - {FILTER_ID_SPK, "filter_speaker", ICON_SPEAKER, "Speakers", "Show Speaker data-blocks"}, - {FILTER_ID_SO, "filter_sound", ICON_SOUND, "Sounds", "Show Sound data-blocks"}, - {FILTER_ID_TE, "filter_texture", ICON_TEXTURE_DATA, "Textures", "Show Texture data-blocks"}, - {FILTER_ID_TXT, "filter_text", ICON_TEXT, "Texts", "Show Text data-blocks"}, - {FILTER_ID_VF, "filter_font", ICON_FONT_DATA, "Fonts", "Show Font data-blocks"}, - {FILTER_ID_VO, "filter_volume", ICON_VOLUME_DATA, "Volumes", "Show/hide Volume data-blocks"}, - {FILTER_ID_WO, "filter_world", ICON_WORLD_DATA, "Worlds", "Show World data-blocks"}, - {FILTER_ID_WS, - "filter_work_space", - ICON_WORKSPACE, - "Workspaces", - "Show workspace data-blocks"}, - - /* Categories */ - {FILTER_ID_SCE, "category_scene", ICON_SCENE_DATA, "Scenes", "Show scenes"}, - {FILTER_ID_AC, "category_animation", ICON_ANIM_DATA, "Animations", "Show animation data"}, - {FILTER_ID_OB | FILTER_ID_GR, - "category_object", - ICON_OUTLINER_COLLECTION, - "Objects & Collections", - "Show objects and collections"}, - {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME | FILTER_ID_HA | - FILTER_ID_PT | FILTER_ID_VO, - "category_geometry", - ICON_NODETREE, - "Geometry", - "Show meshes, curves, lattice, armatures and metaballs data"}, - {FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE, - "category_shading", - ICON_MATERIAL_DATA, - "Shading", - "Show materials, nodetrees, textures and Freestyle's linestyles"}, - {FILTER_ID_IM | FILTER_ID_MC | FILTER_ID_MSK | FILTER_ID_SO, - "category_image", - ICON_IMAGE_DATA, - "Images & Sounds", - "Show images, movie clips, sounds and masks"}, - {FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_LP | FILTER_ID_SPK | FILTER_ID_WO, - "category_environment", - ICON_WORLD_DATA, - "Environment", - "Show worlds, lights, cameras and speakers"}, - {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | - FILTER_ID_VF | FILTER_ID_CF | FILTER_ID_WS, - "category_misc", - ICON_GREASEPENCIL, - "Miscellaneous", - "Show other data types"}, - - {0, NULL, 0, NULL, NULL}}; StructRNA *srna = RNA_def_struct(brna, "FileSelectIDFilter", NULL); RNA_def_struct_sdna(srna, "FileSelectParams"); @@ -6284,12 +6210,23 @@ static void rna_def_fileselect_idfilter(BlenderRNA *brna) RNA_def_struct_ui_text( srna, "File Select ID Filter", "Which ID types to show/hide, when browsing a library"); - for (int i = 0; booleans[i].identifier; i++) { - PropertyRNA *prop = RNA_def_property(srna, booleans[i].identifier, PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "filter_id", booleans[i].flag); - RNA_def_property_ui_text(prop, booleans[i].name, booleans[i].description); - RNA_def_property_ui_icon(prop, booleans[i].icon, 0); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); + const struct IDFilterEnumPropertyItem *individual_ids_and_categories[] = { + rna_enum_id_type_filter_items, + rna_enum_space_file_id_filter_categories, + NULL, + }; + for (uint i = 0; individual_ids_and_categories[i]; i++) { + for (int j = 0; individual_ids_and_categories[i][j].identifier; j++) { + PropertyRNA *prop = RNA_def_property( + srna, individual_ids_and_categories[i][j].identifier, PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna( + prop, NULL, "filter_id", individual_ids_and_categories[i][j].flag); + RNA_def_property_ui_text(prop, + individual_ids_and_categories[i][j].name, + individual_ids_and_categories[i][j].description); + RNA_def_property_ui_icon(prop, individual_ids_and_categories[i][j].icon, 0); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); + } } } @@ -6309,6 +6246,28 @@ static void rna_def_fileselect_entry(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_struct_name_property(srna, prop); + prop = RNA_def_property(srna, "id_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_id_type_items); + RNA_def_property_enum_funcs(prop, + "rna_FileBrowser_FileSelectEntry_id_type_get", + NULL, + "rna_FileBrowser_FileSelectEntry_id_type_itemf"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, + "Data-block Type", + "The type of the data-block, if the file represents one ('NONE' otherwise)"); + + prop = RNA_def_property(srna, "local_id", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "ID"); + RNA_def_property_pointer_funcs( + prop, "rna_FileBrowser_FileSelectEntry_local_id_get", NULL, NULL, NULL); + RNA_def_property_ui_text(prop, + "", + "The local data-block this file represents; only valid if that is a " + "data-block in this file"); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_int( srna, "preview_icon_id", @@ -6545,7 +6504,7 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - /* XXX copied from rna_def_fileselect_idfilter. */ + /* XXX copied from rna_enum_id_type_filter_items. */ static const EnumPropertyItem asset_category_items[] = { {FILTER_ID_SCE, "SCENES", ICON_SCENE_DATA, "Scenes", "Show scenes"}, {FILTER_ID_AC, "ANIMATIONS", ICON_ANIM_DATA, "Animations", "Show animation data"}, diff --git a/source/blender/makesrna/intern/rna_timeline.c b/source/blender/makesrna/intern/rna_timeline.c index c0bd9fd92a2..223e6389942 100644 --- a/source/blender/makesrna/intern/rna_timeline.c +++ b/source/blender/makesrna/intern/rna_timeline.c @@ -33,16 +33,10 @@ # include "BKE_idprop.h" # include "WM_api.h" -static IDProperty *rna_TimelineMarker_idprops(PointerRNA *ptr, bool create) +static IDProperty **rna_TimelineMarker_idprops(PointerRNA *ptr) { TimeMarker *marker = ptr->data; - - if (create && marker->prop == NULL) { - IDPropertyTemplate val = {0}; - marker->prop = IDP_New(IDP_GROUP, &val, "Marker ID properties"); - } - - return marker->prop; + return &marker->prop; } static void rna_TimelineMarker_update(Main *UNUSED(bmain), diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index 98bed5a7891..a88b100435a 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -452,15 +452,31 @@ static unsigned int rna_UIList_filter_const_FILTER_ITEM_get(PointerRNA *UNUSED(p return UILST_FLT_ITEM; } -static IDProperty *rna_UIList_idprops(PointerRNA *ptr, bool create) +static IDProperty **rna_UIList_idprops(PointerRNA *ptr) { uiList *ui_list = (uiList *)ptr->data; - if (create && !ui_list->properties) { - IDPropertyTemplate val = {0}; - ui_list->properties = IDP_New(IDP_GROUP, &val, "RNA_UIList IDproperties group"); + return &ui_list->properties; +} + +static void rna_UIList_list_id_get(PointerRNA *ptr, char *value) +{ + uiList *ui_list = (uiList *)ptr->data; + if (!ui_list->type) { + value[0] = '\0'; + return; + } + + strcpy(value, WM_uilisttype_list_id_get(ui_list->type, ui_list)); +} + +static int rna_UIList_list_id_length(PointerRNA *ptr) +{ + uiList *ui_list = (uiList *)ptr->data; + if (!ui_list->type) { + return 0; } - return ui_list->properties; + return strlen(WM_uilisttype_list_id_get(ui_list->type, ui_list)); } static void uilist_draw_item(uiList *ui_list, @@ -638,7 +654,7 @@ static void uilist_filter_items(uiList *ui_list, RNA_parameter_list_free(&list); } -static void rna_UIList_unregister(Main *UNUSED(bmain), StructRNA *type) +static void rna_UIList_unregister(Main *bmain, StructRNA *type) { uiListType *ult = RNA_struct_blender_type_get(type); @@ -649,7 +665,7 @@ static void rna_UIList_unregister(Main *UNUSED(bmain), StructRNA *type) RNA_struct_free_extension(type, &ult->rna_ext); RNA_struct_free(&BLENDER_RNA, type); - WM_uilisttype_freelink(ult); + WM_uilisttype_remove_ptr(bmain, ult); /* update while blender is running */ WM_main_add_notifier(NC_WINDOW, NULL); @@ -1019,7 +1035,7 @@ static void rna_Panel_bl_description_set(PointerRNA *ptr, const char *value) BLI_strncpy(str, value, RNA_DYN_DESCR_MAX); /* utf8 already ensured */ } else { - BLI_assert(!"setting the bl_description on a non-builtin panel"); + BLI_assert_msg(0, "setting the bl_description on a non-builtin panel"); } } @@ -1031,7 +1047,7 @@ static void rna_Menu_bl_description_set(PointerRNA *ptr, const char *value) BLI_strncpy(str, value, RNA_DYN_DESCR_MAX); /* utf8 already ensured */ } else { - BLI_assert(!"setting the bl_description on a non-builtin menu"); + BLI_assert_msg(0, "setting the bl_description on a non-builtin menu"); } } @@ -1540,6 +1556,16 @@ static void rna_def_uilist(BlenderRNA *brna) "script, then bl_idname = \"OBJECT_UL_vgroups\")"); /* Data */ + /* Note that this is the "non-full" list-ID as obtained through #WM_uilisttype_list_id_get(), + * which differs from the (internal) `uiList.list_id`. */ + prop = RNA_def_property(srna, "list_id", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_funcs(prop, "rna_UIList_list_id_get", "rna_UIList_list_id_length", NULL); + RNA_def_property_ui_text(prop, + "List Name", + "Identifier of the list, if any was passed to the \"list_id\" " + "parameter of \"template_list()\""); + prop = RNA_def_property(srna, "layout_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_uilist_layout_type_items); RNA_def_property_clear_flag(prop, PROP_EDITABLE); diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index aa235b599b7..e06cc39a88b 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -50,6 +50,8 @@ const EnumPropertyItem rna_enum_icon_items[] = { #ifdef RNA_RUNTIME +# include "DNA_asset_types.h" + const char *rna_translate_ui_text( const char *text, const char *text_ctxt, StructRNA *type, PropertyRNA *prop, bool translate) { @@ -525,6 +527,46 @@ static void rna_uiTemplateAnyID(uiLayout *layout, uiTemplateAnyID(layout, ptr, propname, proptypename, name); } +void rna_uiTemplateList(uiLayout *layout, + struct bContext *C, + const char *listtype_name, + const char *list_id, + struct PointerRNA *dataptr, + const char *propname, + struct PointerRNA *active_dataptr, + const char *active_propname, + const char *item_dyntip_propname, + const int rows, + const int maxrows, + const int layout_type, + const int columns, + const bool sort_reverse, + const bool sort_lock) +{ + int flags = UI_TEMPLATE_LIST_FLAG_NONE; + if (sort_reverse) { + flags |= UI_TEMPLATE_LIST_SORT_REVERSE; + } + if (sort_lock) { + flags |= UI_TEMPLATE_LIST_SORT_LOCK; + } + + uiTemplateList(layout, + C, + listtype_name, + list_id, + dataptr, + propname, + active_dataptr, + active_propname, + item_dyntip_propname, + rows, + maxrows, + layout_type, + columns, + flags); +} + static void rna_uiTemplateCacheFile(uiLayout *layout, bContext *C, PointerRNA *ptr, @@ -570,6 +612,69 @@ static void rna_uiTemplateEventFromKeymapItem( uiTemplateEventFromKeymapItem(layout, name, kmi, true); } +static void rna_uiTemplateAssetView(uiLayout *layout, + bContext *C, + const char *list_id, + PointerRNA *asset_library_dataptr, + const char *asset_library_propname, + PointerRNA *assets_dataptr, + const char *assets_propname, + PointerRNA *active_dataptr, + const char *active_propname, + int filter_id_types, + const char *activate_opname, + PointerRNA *r_activate_op_properties, + const char *drag_opname, + PointerRNA *r_drag_op_properties) +{ + AssetFilterSettings filter_settings = { + .id_types = filter_id_types ? filter_id_types : FILTER_ID_ALL, + }; + uiTemplateAssetView(layout, + C, + list_id, + asset_library_dataptr, + asset_library_propname, + assets_dataptr, + assets_propname, + active_dataptr, + active_propname, + &filter_settings, + activate_opname, + r_activate_op_properties, + drag_opname, + r_drag_op_properties); +} + +/** + * XXX Remove filter items that require more than 32 bits for storage. RNA enums don't support + * that currently. + */ +static const EnumPropertyItem *rna_uiTemplateAssetView_filter_id_types_itemf( + bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +{ + EnumPropertyItem *items = NULL; + int totitem = 0; + + for (int i = 0; rna_enum_id_type_filter_items[i].identifier; i++) { + if (rna_enum_id_type_filter_items[i].flag > (1ULL << 31)) { + continue; + } + + EnumPropertyItem tmp = {0, "", 0, "", ""}; + tmp.value = rna_enum_id_type_filter_items[i].flag; + tmp.identifier = rna_enum_id_type_filter_items[i].identifier; + tmp.icon = rna_enum_id_type_filter_items[i].icon; + tmp.name = rna_enum_id_type_filter_items[i].name; + tmp.description = rna_enum_id_type_filter_items[i].description; + RNA_enum_item_add(&items, &totitem, &tmp); + } + RNA_enum_item_end(&items, &totitem); + + *r_free = true; + return items; +} + static uiLayout *rna_uiLayoutRowWithHeading( uiLayout *layout, bool align, const char *heading, const char *heading_ctxt, bool translate) { @@ -1508,7 +1613,7 @@ void RNA_api_ui_layout(StructRNA *srna) parm = RNA_def_pointer(func, "clip_user", "MovieClipUser", "", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); - func = RNA_def_function(srna, "template_list", "uiTemplateList"); + func = RNA_def_function(srna, "template_list", "rna_uiTemplateList"); RNA_def_function_ui_description(func, "Item. A list widget to display data, e.g. vertexgroups."); RNA_def_function_flag(func, FUNC_USE_CONTEXT); parm = RNA_def_string(func, "listtype_name", NULL, 0, "", "Identifier of the list type to use"); @@ -1689,6 +1794,81 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_property_ui_text(parm, "Item", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); api_ui_item_common_text(func); + + func = RNA_def_function(srna, "template_asset_view", "rna_uiTemplateAssetView"); + RNA_def_function_ui_description(func, "Item. A scrollable list of assets in a grid view"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + parm = RNA_def_string(func, + "list_id", + NULL, + 0, + "", + "Identifier of this asset view. Necessary to tell apart different asset " + "views and to idenify an asset view read from a .blend"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, + "asset_library_dataptr", + "AnyType", + "", + "Data from which to take the active asset library property"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + parm = RNA_def_string( + func, "asset_library_propname", NULL, 0, "", "Identifier of the asset library property"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer( + func, "assets_dataptr", "AnyType", "", "Data from which to take the asset list property"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + parm = RNA_def_string( + func, "assets_propname", NULL, 0, "", "Identifier of the asset list property"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, + "active_dataptr", + "AnyType", + "", + "Data from which to take the integer property, index of the active item"); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); + parm = RNA_def_string( + func, + "active_propname", + NULL, + 0, + "", + "Identifier of the integer property in active_data, index of the active item"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_property(func, "filter_id_types", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(parm, DummyRNA_NULL_items); + RNA_def_property_enum_funcs(parm, NULL, NULL, "rna_uiTemplateAssetView_filter_id_types_itemf"); + RNA_def_property_flag(parm, PROP_ENUM_FLAG); + RNA_def_string(func, + "activate_operator", + NULL, + 0, + "", + "Name of a custom operator to invoke when activating an item"); + parm = RNA_def_pointer( + func, + "activate_operator_properties", + "OperatorProperties", + "", + "Operator properties to fill in for the custom activate operator passed to the template"); + RNA_def_parameter_flags(parm, 0, PARM_RNAPTR); + RNA_def_function_output(func, parm); + RNA_def_string(func, + "drag_operator", + NULL, + 0, + "", + "Name of a custom operator to invoke when starting to drag an item. Never " + "invoked together with the `active_operator` (if set), it's either the drag or " + "the activate one"); + parm = RNA_def_pointer( + func, + "drag_operator_properties", + "OperatorProperties", + "", + "Operator properties to fill in for the custom drag operator passed to the template"); + RNA_def_parameter_flags(parm, 0, PARM_RNAPTR); + RNA_def_function_output(func, parm); } #endif diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 6d8c9ec5ec2..cae29714ff6 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -809,14 +809,9 @@ static const EnumPropertyItem *rna_lang_enum_properties_itemf(bContext *UNUSED(C } # endif -static IDProperty *rna_AddonPref_idprops(PointerRNA *ptr, bool create) +static IDProperty **rna_AddonPref_idprops(PointerRNA *ptr) { - if (create && !ptr->data) { - IDPropertyTemplate val = {0}; - ptr->data = IDP_New(IDP_GROUP, &val, "RNA_AddonPreferences group"); - } - - return ptr->data; + return (IDProperty **)&ptr->data; } static PointerRNA rna_Addon_preferences_get(PointerRNA *ptr) @@ -1105,7 +1100,7 @@ int rna_show_statusbar_vram_editable(struct PointerRNA *UNUSED(ptr), const char static size_t max_memory_in_megabytes(void) { /* Maximum addressable bytes on this platform. */ - const size_t limit_bytes = (((size_t)1) << ((sizeof(size_t[8])) - 1)); + const size_t limit_bytes = (((size_t)1) << (sizeof(size_t[8]) - 1)); /* Convert it to megabytes and return. */ return (limit_bytes >> 20); } diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index a563541b968..2a4abac04f8 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -568,14 +568,9 @@ static StructRNA *rna_OperatorProperties_refine(PointerRNA *ptr) } } -static IDProperty *rna_OperatorProperties_idprops(PointerRNA *ptr, bool create) +static IDProperty **rna_OperatorProperties_idprops(PointerRNA *ptr) { - if (create && !ptr->data) { - IDPropertyTemplate val = {0}; - ptr->data = IDP_New(IDP_GROUP, &val, "RNA_OperatorProperties group"); - } - - return ptr->data; + return (IDProperty **)&ptr->data; } static void rna_Operator_name_get(PointerRNA *ptr, char *value) @@ -1120,13 +1115,9 @@ static PointerRNA rna_wmKeyConfig_preferences_get(PointerRNA *ptr) } } -static IDProperty *rna_wmKeyConfigPref_idprops(PointerRNA *ptr, bool create) +static IDProperty **rna_wmKeyConfigPref_idprops(PointerRNA *ptr) { - if (create && !ptr->data) { - IDPropertyTemplate val = {0}; - ptr->data = IDP_New(IDP_GROUP, &val, "RNA_KeyConfigPreferences group"); - } - return ptr->data; + return (IDProperty **)&ptr->data; } static void rna_wmKeyConfigPref_unregister(Main *UNUSED(bmain), StructRNA *type) @@ -1774,7 +1765,7 @@ static void rna_Operator_bl_idname_set(PointerRNA *ptr, const char *value) BLI_strncpy(str, value, OP_MAX_TYPENAME); /* utf8 already ensured */ } else { - BLI_assert(!"setting the bl_idname on a non-builtin operator"); + BLI_assert_msg(0, "setting the bl_idname on a non-builtin operator"); } } @@ -1786,7 +1777,7 @@ static void rna_Operator_bl_label_set(PointerRNA *ptr, const char *value) BLI_strncpy(str, value, OP_MAX_TYPENAME); /* utf8 already ensured */ } else { - BLI_assert(!"setting the bl_label on a non-builtin operator"); + BLI_assert_msg(0, "setting the bl_label on a non-builtin operator"); } } diff --git a/source/blender/makesrna/intern/rna_wm_gizmo.c b/source/blender/makesrna/intern/rna_wm_gizmo.c index 98920bbd518..6a1574f3dbe 100644 --- a/source/blender/makesrna/intern/rna_wm_gizmo.c +++ b/source/blender/makesrna/intern/rna_wm_gizmo.c @@ -244,7 +244,7 @@ static void rna_Gizmo_bl_idname_set(PointerRNA *ptr, const char *value) BLI_strncpy(str, value, MAX_NAME); /* utf8 already ensured */ } else { - BLI_assert(!"setting the bl_idname on a non-builtin operator"); + BLI_assert_msg(0, "setting the bl_idname on a non-builtin operator"); } } @@ -293,14 +293,9 @@ static StructRNA *rna_GizmoProperties_refine(PointerRNA *ptr) } } -static IDProperty *rna_GizmoProperties_idprops(PointerRNA *ptr, bool create) +static IDProperty **rna_GizmoProperties_idprops(PointerRNA *ptr) { - if (create && !ptr->data) { - IDPropertyTemplate val = {0}; - ptr->data = IDP_New(IDP_GROUP, &val, "RNA_GizmoProperties group"); - } - - return ptr->data; + return (IDProperty **)&ptr->data; } static PointerRNA rna_Gizmo_properties_get(PointerRNA *ptr) @@ -585,14 +580,9 @@ static StructRNA *rna_GizmoGroupProperties_refine(PointerRNA *ptr) } } -static IDProperty *rna_GizmoGroupProperties_idprops(PointerRNA *ptr, bool create) +static IDProperty **rna_GizmoGroupProperties_idprops(PointerRNA *ptr) { - if (create && !ptr->data) { - IDPropertyTemplate val = {0}; - ptr->data = IDP_New(IDP_GROUP, &val, "RNA_GizmoGroupProperties group"); - } - - return ptr->data; + return (IDProperty **)&ptr->data; } static wmGizmo *rna_GizmoGroup_gizmo_new(wmGizmoGroup *gzgroup, @@ -653,7 +643,7 @@ static void rna_GizmoGroup_bl_idname_set(PointerRNA *ptr, const char *value) BLI_strncpy(str, value, MAX_NAME); /* utf8 already ensured */ } else { - BLI_assert(!"setting the bl_idname on a non-builtin operator"); + BLI_assert_msg(0, "setting the bl_idname on a non-builtin operator"); } } @@ -665,7 +655,7 @@ static void rna_GizmoGroup_bl_label_set(PointerRNA *ptr, const char *value) BLI_strncpy(str, value, MAX_NAME); /* utf8 already ensured */ } else { - BLI_assert(!"setting the bl_label on a non-builtin operator"); + BLI_assert_msg(0, "setting the bl_label on a non-builtin operator"); } } diff --git a/source/blender/makesrna/intern/rna_workspace.c b/source/blender/makesrna/intern/rna_workspace.c index 6b52a38c2da..b053bb0ff62 100644 --- a/source/blender/makesrna/intern/rna_workspace.c +++ b/source/blender/makesrna/intern/rna_workspace.c @@ -107,6 +107,18 @@ static void rna_WorkSpace_owner_ids_clear(WorkSpace *workspace) WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_REMOVED, workspace); } +static int rna_WorkSpace_active_asset_library_get(PointerRNA *ptr) +{ + const WorkSpace *workspace = ptr->data; + return rna_asset_library_reference_get(&workspace->active_asset_library); +} + +static void rna_WorkSpace_active_asset_library_set(PointerRNA *ptr, int value) +{ + WorkSpace *workspace = ptr->data; + rna_asset_library_reference_set(&workspace->active_asset_library, value); +} + static bToolRef *rna_WorkSpace_tools_from_tkey(WorkSpace *workspace, const bToolKey *tkey, bool create) @@ -407,6 +419,14 @@ static void rna_def_workspace(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Use UI Tags", "Filter the UI by tags"); RNA_def_property_update(prop, 0, "rna_window_update_all"); + prop = rna_def_asset_library_reference_common( + srna, "rna_WorkSpace_active_asset_library_get", "rna_WorkSpace_active_asset_library_set"); + RNA_def_property_ui_text(prop, + "Asset Library", + "Active asset library to show in the UI, not used by the Asset Browser " + "(which has its own active asset library)"); + RNA_def_property_update(prop, NC_ASSET | ND_ASSET_LIST_READING, NULL); + RNA_api_workspace(srna); } diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c index 649d36e3d57..828b8b79664 100644 --- a/source/blender/modifiers/intern/MOD_armature.c +++ b/source/blender/modifiers/intern/MOD_armature.c @@ -37,6 +37,7 @@ #include "BKE_action.h" #include "BKE_armature.h" #include "BKE_context.h" +#include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" @@ -122,7 +123,8 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte /* If neither vertex groups nor envelopes are used, the modifier has no bone dependencies. */ if ((amd->deformflag & ARM_DEF_VGROUP) != 0) { /* Enumerate groups that match existing bones. */ - LISTBASE_FOREACH (bDeformGroup *, dg, &ctx->object->defbase) { + const ListBase *defbase = BKE_object_defgroup_list(ctx->object); + LISTBASE_FOREACH (bDeformGroup *, dg, defbase) { if (BKE_pose_channel_find_name(amd->object->pose, dg->name) != NULL) { /* Can't check BONE_NO_DEFORM because it can be animated. */ DEG_add_bone_relation( diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index e7733932f2c..c6bb8ca0670 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -109,7 +109,7 @@ static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *u { BooleanModifierData *bmd = (BooleanModifierData *)md; - walk(userData, ob, (ID **)&bmd->collection, IDWALK_CB_NOP); + walk(userData, ob, (ID **)&bmd->collection, IDWALK_CB_USER); walk(userData, ob, (ID **)&bmd->object, IDWALK_CB_NOP); } diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c index c38e5126f6b..52f21e3d3d0 100644 --- a/source/blender/modifiers/intern/MOD_build.c +++ b/source/blender/modifiers/intern/MOD_build.c @@ -101,7 +101,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, struct range_vn_i(faceMap, numPoly_src, 0); struct Scene *scene = DEG_get_input_scene(ctx->depsgraph); - frac = (BKE_scene_frame_get(scene) - bmd->start) / bmd->length; + frac = (BKE_scene_ctime_get(scene) - bmd->start) / bmd->length; CLAMP(frac, 0.0f, 1.0f); if (bmd->flag & MOD_BUILD_FLAG_REVERSE) { frac = 1.0f - frac; diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index 40d027f3044..4487adcfdda 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -206,6 +206,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla tclmd->point_cache->step = clmd->point_cache->step; tclmd->point_cache->startframe = clmd->point_cache->startframe; tclmd->point_cache->endframe = clmd->point_cache->endframe; + tclmd->point_cache->flag |= (clmd->point_cache->flag & PTCACHE_FLAGS_COPY); } } diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c index 20dbb299767..aae6d257766 100644 --- a/source/blender/modifiers/intern/MOD_curve.c +++ b/source/blender/modifiers/intern/MOD_curve.c @@ -167,7 +167,7 @@ static void deformVertsEM(ModifierData *md, int defgrp_index = -1; if (ctx->object->type == OB_MESH && cmd->name[0] != '\0') { - defgrp_index = BKE_object_defgroup_name_index(ctx->object, cmd->name); + defgrp_index = BKE_id_defgroup_name_index(&mesh->id, cmd->name); if (defgrp_index != -1) { use_dverts = true; } diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c index 545e23221ec..bf197dca7e5 100644 --- a/source/blender/modifiers/intern/MOD_explode.c +++ b/source/blender/modifiers/intern/MOD_explode.c @@ -919,7 +919,7 @@ static Mesh *explodeMesh(ExplodeModifierData *emd, EdgeHashIterator *ehi; float *vertco = NULL, imat[4][4]; float rot[4]; - float cfra; + float ctime; /* float timestep; */ const int *facepa = emd->facepa; int totdup = 0, totvert = 0, totface = 0, totpart = 0, delface = 0; @@ -940,7 +940,7 @@ static Mesh *explodeMesh(ExplodeModifierData *emd, /* timestep = psys_get_timestep(&sim); */ - cfra = BKE_scene_frame_get(scene); + ctime = BKE_scene_ctime_get(scene); /* hash table for vertice <-> particle relations */ vertpahash = BLI_edgehash_new(__func__); @@ -962,7 +962,7 @@ static Mesh *explodeMesh(ExplodeModifierData *emd, /* do mindex + totvert to ensure the vertex index to be the first * with BLI_edgehashIterator_getKey */ - if (pa == NULL || cfra < pa->time) { + if (pa == NULL || ctime < pa->time) { mindex = totvert + totpart; } else { @@ -1022,7 +1022,7 @@ static Mesh *explodeMesh(ExplodeModifierData *emd, psys_get_birth_coords(&sim, pa, &birth, 0, 0); - state.time = cfra; + state.time = ctime; psys_get_particle_state(&sim, ed_v2, &state, 1); vertco = explode->mvert[v].co; @@ -1076,7 +1076,7 @@ static Mesh *explodeMesh(ExplodeModifierData *emd, orig_v4 = source.v4; /* Same as above in the first loop over mesh's faces. */ - if (pa == NULL || cfra < pa->time) { + if (pa == NULL || ctime < pa->time) { mindex = totvert + totpart; } else { @@ -1096,7 +1096,7 @@ static Mesh *explodeMesh(ExplodeModifierData *emd, /* override uv channel for particle age */ if (mtface) { - float age = (pa != NULL) ? (cfra - pa->time) / pa->lifetime : 0.0f; + float age = (pa != NULL) ? (ctime - pa->time) / pa->lifetime : 0.0f; /* Clamp to this range to avoid flipping to the other side of the coordinates. */ CLAMP(age, 0.001f, 0.999f); diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index a77f6cfe8ba..9aa8e3dd7c8 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -119,7 +119,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte /* A vertex will be in the mask if a selected bone influences it more than a certain threshold. */ static void compute_vertex_mask__armature_mode(MDeformVert *dvert, - Object *ob, + Mesh *mesh, Object *armature_ob, float threshold, MutableSpan<bool> r_vertex_mask) @@ -127,7 +127,7 @@ static void compute_vertex_mask__armature_mode(MDeformVert *dvert, /* Element i is true if there is a selected bone that uses vertex group i. */ Vector<bool> selected_bone_uses_group; - for (bDeformGroup *def : ListBaseWrapper<bDeformGroup>(ob->defbase)) { + LISTBASE_FOREACH (bDeformGroup *, def, &mesh->vertex_group_names) { bPoseChannel *pchan = BKE_pose_channel_find_name(armature_ob->pose, def->name); bool bone_for_group_exists = pchan && pchan->bone && (pchan->bone->flag & BONE_SELECTED); selected_bone_uses_group.append(bone_for_group_exists); @@ -325,10 +325,9 @@ void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, * 2. Find edges and polygons only using those vertices. * 3. Create a new mesh that only uses the found vertices, edges and polygons. */ -static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) +static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx), Mesh *mesh) { MaskModifierData *mmd = reinterpret_cast<MaskModifierData *>(md); - Object *ob = ctx->object; const bool invert_mask = mmd->flag & MOD_MASK_INV; /* Return empty or input mesh when there are no vertex groups. */ @@ -339,7 +338,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * /* Quick test to see if we can return early. */ if (!(ELEM(mmd->mode, MOD_MASK_MODE_ARM, MOD_MASK_MODE_VGROUP)) || (mesh->totvert == 0) || - BLI_listbase_is_empty(&ob->defbase)) { + BLI_listbase_is_empty(&mesh->vertex_group_names)) { return mesh; } @@ -348,15 +347,15 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * Object *armature_ob = mmd->ob_arm; /* Return input mesh if there is no armature with bones. */ - if (ELEM(NULL, armature_ob, armature_ob->pose, ob->defbase.first)) { + if (ELEM(NULL, armature_ob, armature_ob->pose)) { return mesh; } vertex_mask = Array<bool>(mesh->totvert); - compute_vertex_mask__armature_mode(dvert, ob, armature_ob, mmd->threshold, vertex_mask); + compute_vertex_mask__armature_mode(dvert, mesh, armature_ob, mmd->threshold, vertex_mask); } else { - int defgrp_index = BKE_object_defgroup_name_index(ob, mmd->vgroup); + int defgrp_index = BKE_id_defgroup_name_index(&mesh->id, mmd->vgroup); /* Return input mesh if the vertex group does not exist. */ if (defgrp_index == -1) { diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c index 6ec3277ee7a..e0507320628 100644 --- a/source/blender/modifiers/intern/MOD_meshcache.c +++ b/source/blender/modifiers/intern/MOD_meshcache.c @@ -104,20 +104,20 @@ static void meshcache_do(MeshCacheModifierData *mcmd, /* -------------------------------------------------------------------- */ /* Interpret Time (the reading functions also do some of this ) */ if (mcmd->play_mode == MOD_MESHCACHE_PLAY_CFEA) { - const float cfra = BKE_scene_frame_get(scene); + const float ctime = BKE_scene_ctime_get(scene); switch (mcmd->time_mode) { case MOD_MESHCACHE_TIME_FRAME: { - time = cfra; + time = ctime; break; } case MOD_MESHCACHE_TIME_SECONDS: { - time = cfra / fps; + time = ctime / fps; break; } case MOD_MESHCACHE_TIME_FACTOR: default: { - time = cfra / fps; + time = ctime / fps; break; } } diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 9af2472f2c6..87fce26c45e 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -1018,8 +1018,7 @@ static void modifyGeometry(ModifierData *md, static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) { GeometrySet geometry_set = GeometrySet::create_with_mesh(mesh, GeometryOwnershipType::Editable); - geometry_set.get_component_for_write<MeshComponent>().copy_vertex_group_names_from_object( - *ctx->object); + modifyGeometry(md, ctx, geometry_set); if (ctx->flag & MOD_APPLY_TO_BASE_MESH) { diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index 18cc1ce6c86..e652eb8353d 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -1177,7 +1177,7 @@ class GeometryNodesEvaluator { to_sockets.append(to_socket); } }; - auto handle_skipped_socket_fn = [&, this](const DSocket socket) { + auto handle_skipped_socket_fn = [&](const DSocket socket) { sockets_to_log_to.append(socket); }; from_socket.foreach_target_socket(handle_target_socket_fn, handle_skipped_socket_fn); diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c index c00d8ea7fa8..b48cbf233ef 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.c +++ b/source/blender/modifiers/intern/MOD_particlesystem.c @@ -120,7 +120,6 @@ static void deformVerts(ModifierData *md, Mesh *mesh_src = mesh; ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; ParticleSystem *psys = NULL; - /* float cfra = BKE_scene_frame_get(md->scene); */ /* UNUSED */ if (ctx->object->particlesystem.first) { psys = psmd->psys; diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c index a2fcbe35dcc..e97190b1878 100644 --- a/source/blender/modifiers/intern/MOD_solidify_extrude.c +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -242,9 +242,8 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex MDeformVert *dvert; const bool defgrp_invert = (smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0; int defgrp_index; - const int shell_defgrp_index = BKE_object_defgroup_name_index(ctx->object, - smd->shell_defgrp_name); - const int rim_defgrp_index = BKE_object_defgroup_name_index(ctx->object, smd->rim_defgrp_name); + const int shell_defgrp_index = BKE_id_defgroup_name_index(&mesh->id, smd->shell_defgrp_name); + const int rim_defgrp_index = BKE_id_defgroup_name_index(&mesh->id, smd->rim_defgrp_name); /* array size is doubled in case of using a shell */ const uint stride = do_shell ? 2 : 1; diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index 1a7daa10e0f..b872f04b60f 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -181,9 +181,8 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, MDeformVert *dvert; const bool defgrp_invert = (smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0; int defgrp_index; - const int shell_defgrp_index = BKE_object_defgroup_name_index(ctx->object, - smd->shell_defgrp_name); - const int rim_defgrp_index = BKE_object_defgroup_name_index(ctx->object, smd->rim_defgrp_name); + const int shell_defgrp_index = BKE_id_defgroup_name_index(&mesh->id, smd->shell_defgrp_name); + const int rim_defgrp_index = BKE_id_defgroup_name_index(&mesh->id, smd->rim_defgrp_name); MOD_get_vgroup(ctx->object, mesh, smd->defgrp_name, &dvert, &defgrp_index); diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c index dd011a293ee..ec6de8f8387 100644 --- a/source/blender/modifiers/intern/MOD_surfacedeform.c +++ b/source/blender/modifiers/intern/MOD_surfacedeform.c @@ -94,6 +94,11 @@ typedef struct SDefBindCalcData { float imat[4][4]; const float falloff; int success; + /** Vertex group lookup data. */ + const MDeformVert *const dvert; + int const defgrp_index; + bool const invert_vgroup; + bool const sparse_bind; } SDefBindCalcData; /** @@ -218,7 +223,7 @@ static void freeData(ModifierData *md) SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md; if (smd->verts) { - for (int i = 0; i < smd->numverts; i++) { + for (int i = 0; i < smd->num_bind_verts; i++) { if (smd->verts[i].binds) { for (int j = 0; j < smd->verts[i].numbinds; j++) { MEM_SAFE_FREE(smd->verts[i].binds[j].vert_inds); @@ -243,7 +248,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla if (smd->verts) { tsmd->verts = MEM_dupallocN(smd->verts); - for (int i = 0; i < smd->numverts; i++) { + for (int i = 0; i < smd->num_bind_verts; i++) { if (smd->verts[i].binds) { tsmd->verts[i].binds = MEM_dupallocN(smd->verts[i].binds); @@ -963,12 +968,32 @@ static void bindVert(void *__restrict userdata, SDefBindPoly *bpoly; SDefBind *sdbind; + sdvert->vertex_idx = index; + if (data->success != MOD_SDEF_BIND_RESULT_SUCCESS) { sdvert->binds = NULL; sdvert->numbinds = 0; return; } + if (data->sparse_bind) { + float weight = 0.0f; + + if (data->dvert && data->defgrp_index != -1) { + weight = BKE_defvert_find_weight(&data->dvert[index], data->defgrp_index); + } + + if (data->invert_vgroup) { + weight = 1.0f - weight; + } + + if (weight <= 0) { + sdvert->binds = NULL; + sdvert->numbinds = 0; + return; + } + } + copy_v3_v3(point_co, data->vertexCos[index]); bwdata = computeBindWeights(data, point_co); @@ -1135,6 +1160,21 @@ static void bindVert(void *__restrict userdata, freeBindData(bwdata); } +/* Remove vertices without bind data from the bind array. */ +static void compactSparseBinds(SurfaceDeformModifierData *smd) +{ + smd->num_bind_verts = 0; + + for (uint i = 0; i < smd->num_mesh_verts; i++) { + if (smd->verts[i].numbinds > 0) { + smd->verts[smd->num_bind_verts++] = smd->verts[i]; + } + } + + smd->verts = MEM_reallocN_id( + smd->verts, sizeof(*smd->verts) * smd->num_bind_verts, "SDefBindVerts (sparse)"); +} + static bool surfacedeformBind(Object *ob, SurfaceDeformModifierData *smd_orig, SurfaceDeformModifierData *smd_eval, @@ -1142,7 +1182,8 @@ static bool surfacedeformBind(Object *ob, uint numverts, uint tnumpoly, uint tnumverts, - Mesh *target) + Mesh *target, + Mesh *mesh) { BVHTreeFromMesh treeData = {NULL}; const MVert *mvert = target->mvert; @@ -1205,9 +1246,15 @@ static bool surfacedeformBind(Object *ob, return false; } - smd_orig->numverts = numverts; + smd_orig->num_mesh_verts = numverts; smd_orig->numpoly = tnumpoly; + int defgrp_index; + MDeformVert *dvert; + MOD_get_vgroup(ob, mesh, smd_orig->defgrp_name, &dvert, &defgrp_index); + const bool invert_vgroup = (smd_orig->flags & MOD_SDEF_INVERT_VGROUP) != 0; + const bool sparse_bind = (smd_orig->flags & MOD_SDEF_SPARSE_BIND) != 0; + SDefBindCalcData data = { .treeData = &treeData, .vert_edges = vert_edges, @@ -1221,6 +1268,10 @@ static bool surfacedeformBind(Object *ob, .vertexCos = vertexCos, .falloff = smd_orig->falloff, .success = MOD_SDEF_BIND_RESULT_SUCCESS, + .dvert = dvert, + .defgrp_index = defgrp_index, + .invert_vgroup = invert_vgroup, + .sparse_bind = sparse_bind, }; if (data.targetCos == NULL) { @@ -1242,6 +1293,13 @@ static bool surfacedeformBind(Object *ob, MEM_freeN(data.targetCos); + if (sparse_bind) { + compactSparseBinds(smd_orig); + } + else { + smd_orig->num_bind_verts = numverts; + } + if (data.success == MOD_SDEF_BIND_RESULT_MEM_ERR) { BKE_modifier_set_error(ob, (ModifierData *)smd_eval, "Out of memory"); freeData((ModifierData *)smd_orig); @@ -1267,6 +1325,11 @@ static bool surfacedeformBind(Object *ob, BKE_modifier_set_error(ob, (ModifierData *)smd_eval, "Target contains invalid polygons"); freeData((ModifierData *)smd_orig); } + else if (smd_orig->num_bind_verts == 0 || !smd_orig->verts) { + data.success = MOD_SDEF_BIND_RESULT_GENERIC_ERR; + BKE_modifier_set_error(ob, (ModifierData *)smd_eval, "No vertices were bound"); + freeData((ModifierData *)smd_orig); + } freeAdjacencyMap(vert_edges, adj_array, edge_polys); free_bvhtree_from_mesh(&treeData); @@ -1281,14 +1344,15 @@ static void deformVert(void *__restrict userdata, const SDefDeformData *const data = (SDefDeformData *)userdata; const SDefBind *sdbind = data->bind_verts[index].binds; const int num_binds = data->bind_verts[index].numbinds; - float *const vertexCos = data->vertexCos[index]; + const unsigned int vertex_idx = data->bind_verts[index].vertex_idx; + float *const vertexCos = data->vertexCos[vertex_idx]; float norm[3], temp[3], offset[3]; /* Retrieve the value of the weight vertex group if specified. */ float weight = 1.0f; if (data->dvert && data->defgrp_index != -1) { - weight = BKE_defvert_find_weight(&data->dvert[index], data->defgrp_index); + weight = BKE_defvert_find_weight(&data->dvert[vertex_idx], data->defgrp_index); if (data->invert_vgroup) { weight = 1.0f - weight; @@ -1423,7 +1487,8 @@ static void surfacedeformModifier_do(ModifierData *md, /* Avoid converting edit-mesh data, binding is an exception. */ BKE_mesh_wrapper_ensure_mdata(target); - if (!surfacedeformBind(ob, smd_orig, smd, vertexCos, numverts, tnumpoly, tnumverts, target)) { + if (!surfacedeformBind( + ob, smd_orig, smd, vertexCos, numverts, tnumpoly, tnumverts, target, mesh)) { smd->flags &= ~MOD_SDEF_BIND; } /* Early abort, this is binding 'call', no need to perform whole evaluation. */ @@ -1431,8 +1496,9 @@ static void surfacedeformModifier_do(ModifierData *md, } /* Poly count checks */ - if (smd->numverts != numverts) { - BKE_modifier_set_error(ob, md, "Vertices changed from %u to %u", smd->numverts, numverts); + if (smd->num_mesh_verts != numverts) { + BKE_modifier_set_error( + ob, md, "Vertices changed from %u to %u", smd->num_mesh_verts, numverts); return; } if (smd->numpoly != tnumpoly) { @@ -1468,8 +1534,8 @@ static void surfacedeformModifier_do(ModifierData *md, TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = (numverts > 10000); - BLI_task_parallel_range(0, numverts, &data, deformVert, &settings); + settings.use_threading = (smd->num_bind_verts > 10000); + BLI_task_parallel_range(0, smd->num_bind_verts, &data, deformVert, &settings); MEM_freeN(data.targetCos); } @@ -1554,6 +1620,11 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", NULL); + col = uiLayoutColumn(layout, false); + uiLayoutSetEnabled(col, !is_bound); + uiLayoutSetActive(col, !is_bound && RNA_string_length(ptr, "vertex_group") != 0); + uiItemR(col, ptr, "use_sparse_bind", 0, NULL, ICON_NONE); + uiItemS(layout); col = uiLayoutColumn(layout, false); @@ -1576,10 +1647,10 @@ static void blendWrite(BlendWriter *writer, const ModifierData *md) { const SurfaceDeformModifierData *smd = (const SurfaceDeformModifierData *)md; - BLO_write_struct_array(writer, SDefVert, smd->numverts, smd->verts); + BLO_write_struct_array(writer, SDefVert, smd->num_bind_verts, smd->verts); if (smd->verts) { - for (int i = 0; i < smd->numverts; i++) { + for (int i = 0; i < smd->num_bind_verts; i++) { BLO_write_struct_array(writer, SDefBind, smd->verts[i].numbinds, smd->verts[i].binds); if (smd->verts[i].binds) { @@ -1607,7 +1678,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md) BLO_read_data_address(reader, &smd->verts); if (smd->verts) { - for (int i = 0; i < smd->numverts; i++) { + for (int i = 0; i < smd->num_bind_verts; i++) { BLO_read_data_address(reader, &smd->verts[i].binds); if (smd->verts[i].binds) { diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index 9aed4fbfc5f..5b97d0eb259 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -254,15 +254,22 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob, void MOD_get_vgroup( Object *ob, struct Mesh *mesh, const char *name, MDeformVert **dvert, int *defgrp_index) { - *defgrp_index = BKE_object_defgroup_name_index(ob, name); - *dvert = NULL; - - if (*defgrp_index != -1) { - if (ob->type == OB_LATTICE) { + if (mesh) { + *defgrp_index = BKE_id_defgroup_name_index(&mesh->id, name); + if (*defgrp_index != -1) { + *dvert = mesh->dvert; + } + else { + *dvert = NULL; + } + } + else { + *defgrp_index = BKE_object_defgroup_name_index(ob, name); + if (*defgrp_index != -1 && ob->type == OB_LATTICE) { *dvert = BKE_lattice_deform_verts_get(ob); } - else if (mesh) { - *dvert = mesh->dvert; + else { + *dvert = NULL; } } } diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.c b/source/blender/modifiers/intern/MOD_weightvg_util.c index c5e2ecb9660..696c4c855c7 100644 --- a/source/blender/modifiers/intern/MOD_weightvg_util.c +++ b/source/blender/modifiers/intern/MOD_weightvg_util.c @@ -230,7 +230,7 @@ void weightvg_do_mask(const ModifierEvalContext *ctx, MEM_freeN(tex_co); } - else if ((ref_didx = BKE_object_defgroup_name_index(ob, defgrp_name)) != -1) { + else if ((ref_didx = BKE_id_defgroup_name_index(&mesh->id, defgrp_name)) != -1) { MDeformVert *dvert = NULL; /* Check whether we want to set vgroup weights from a constant weight factor or a vertex diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index b5f72c88800..093fa118ee0 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -194,12 +194,12 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * /* Check if we can just return the original mesh. * Must have verts and therefore verts assigned to vgroups to do anything useful! */ - if ((numVerts == 0) || BLI_listbase_is_empty(&ctx->object->defbase)) { + if ((numVerts == 0) || BLI_listbase_is_empty(&mesh->vertex_group_names)) { return mesh; } /* Get vgroup idx from its name. */ - const int defgrp_index = BKE_object_defgroup_name_index(ctx->object, wmd->defgrp_name); + const int defgrp_index = BKE_id_defgroup_name_index(&mesh->id, wmd->defgrp_name); if (defgrp_index == -1) { return mesh; } diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c index a71a2f3b480..7aae089fa18 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -245,19 +245,19 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * /* Check if we can just return the original mesh. * Must have verts and therefore verts assigned to vgroups to do anything useful! */ - if ((numVerts == 0) || BLI_listbase_is_empty(&ctx->object->defbase)) { + if ((numVerts == 0) || BLI_listbase_is_empty(&mesh->vertex_group_names)) { return mesh; } /* Get vgroup idx from its name. */ - const int defgrp_index = BKE_object_defgroup_name_index(ctx->object, wmd->defgrp_name_a); + const int defgrp_index = BKE_id_defgroup_name_index(&mesh->id, wmd->defgrp_name_a); if (defgrp_index == -1) { return mesh; } /* Get second vgroup idx from its name, if given. */ int defgrp_index_other = -1; if (wmd->defgrp_name_b[0] != '\0') { - defgrp_index_other = BKE_object_defgroup_name_index(ctx->object, wmd->defgrp_name_b); + defgrp_index_other = BKE_id_defgroup_name_index(&mesh->id, wmd->defgrp_name_b); if (defgrp_index_other == -1) { return mesh; } diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index b0d2f52f78c..6e78774269a 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -468,7 +468,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * /* Check if we can just return the original mesh. * Must have verts and therefore verts assigned to vgroups to do anything useful! */ - if ((numVerts == 0) || BLI_listbase_is_empty(&ctx->object->defbase)) { + if ((numVerts == 0) || BLI_listbase_is_empty(&mesh->vertex_group_names)) { return mesh; } @@ -479,11 +479,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } /* Get vgroup idx from its name. */ - defgrp_index = BKE_object_defgroup_name_index(ob, wmd->defgrp_name); + defgrp_index = BKE_id_defgroup_name_index(&mesh->id, wmd->defgrp_name); if (defgrp_index == -1) { return mesh; } - const bool has_mdef = CustomData_has_layer(&mesh->vdata, CD_MDEFORMVERT); /* If no vertices were ever added to an object's vgroup, dvert might be NULL. */ /* As this modifier never add vertices to vgroup, just return. */ diff --git a/source/blender/modifiers/intern/MOD_weld.c b/source/blender/modifiers/intern/MOD_weld.c index 1590f342666..fe2d699aea8 100644 --- a/source/blender/modifiers/intern/MOD_weld.c +++ b/source/blender/modifiers/intern/MOD_weld.c @@ -1573,11 +1573,12 @@ struct WeldVertexCluster { uint merged_verts; }; -static Mesh *weldModifier_doWeld(WeldModifierData *wmd, const ModifierEvalContext *ctx, Mesh *mesh) +static Mesh *weldModifier_doWeld(WeldModifierData *wmd, + const ModifierEvalContext *UNUSED(ctx), + Mesh *mesh) { Mesh *result = mesh; - Object *ob = ctx->object; BLI_bitmap *v_mask = NULL; int v_mask_act = 0; @@ -1590,7 +1591,7 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, const ModifierEvalContex totvert = mesh->totvert; /* Vertex Group. */ - const int defgrp_index = BKE_object_defgroup_name_index(ob, wmd->defgrp_name); + const int defgrp_index = BKE_id_defgroup_name_index(&mesh->id, wmd->defgrp_name); if (defgrp_index != -1) { MDeformVert *dvert, *dv; dvert = CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c index c294b8f69ba..55fcfed8e98 100644 --- a/source/blender/modifiers/intern/MOD_wireframe.c +++ b/source/blender/modifiers/intern/MOD_wireframe.c @@ -76,7 +76,7 @@ static Mesh *WireframeModifier_do(WireframeModifierData *wmd, Object *ob, Mesh * Mesh *result; BMesh *bm; - const int defgrp_index = BKE_object_defgroup_name_index(ob, wmd->defgrp_name); + const int defgrp_index = BKE_id_defgroup_name_index(&mesh->id, wmd->defgrp_name); bm = BKE_mesh_to_bmesh_ex(ob, mesh, &(struct BMeshCreateParams){0}, diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index ec060de916b..dc19508be04 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -170,6 +170,7 @@ set(SRC geometry/nodes/node_geo_curve_primitive_circle.cc geometry/nodes/node_geo_curve_primitive_line.cc geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc + geometry/nodes/node_geo_curve_primitive_quadrilateral.cc geometry/nodes/node_geo_curve_primitive_spiral.cc geometry/nodes/node_geo_curve_primitive_star.cc geometry/nodes/node_geo_curve_resample.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index d93ecf36442..ad3a838f4c0 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -57,6 +57,7 @@ void register_node_type_geo_curve_primitive_bezier_segment(void); void register_node_type_geo_curve_primitive_circle(void); void register_node_type_geo_curve_primitive_line(void); void register_node_type_geo_curve_primitive_quadratic_bezier(void); +void register_node_type_geo_curve_primitive_quadrilateral(void); void register_node_type_geo_curve_primitive_spiral(void); void register_node_type_geo_curve_primitive_star(void); void register_node_type_geo_curve_resample(void); diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index 1c4590f96ac..b85862a0176 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -90,6 +90,24 @@ class GeometryValueLog : public ValueLog { std::unique_ptr<GeometrySet> full_geometry_; public: + struct MeshInfo { + int tot_verts, tot_edges, tot_faces; + }; + struct CurveInfo { + int tot_splines; + }; + struct PointCloudInfo { + int tot_points; + }; + struct InstancesInfo { + int tot_instances; + }; + + std::optional<MeshInfo> mesh_info; + std::optional<CurveInfo> curve_info; + std::optional<PointCloudInfo> pointcloud_info; + std::optional<InstancesInfo> instances_info; + GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry); Span<GeometryAttributeInfo> attributes() const diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 6266126a7b0..73d4a002991 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -295,6 +295,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Cu DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_LINE, def_geo_curve_primitive_line, "CURVE_PRIMITIVE_LINE", CurvePrimitiveLine, "Curve Line", "") +DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_primitive_quadrilateral, "CURVE_PRIMITIVE_QUADRILATERAL", CurvePrimitiveQuadrilateral, "Quadrilateral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMITIVE_QUADRATIC_BEZIER", CurveQuadraticBezier, "Quadratic Bezier", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "") diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c index 7437496d878..fa276e9a794 100644 --- a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c +++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.c @@ -42,9 +42,9 @@ static void node_composit_init_antialiasing(bNodeTree *UNUSED(ntree), bNode *nod { NodeAntiAliasingData *data = MEM_callocN(sizeof(NodeAntiAliasingData), "node antialiasing data"); - data->threshold = 1.0f; - data->contrast_limit = 0.2f; - data->corner_rounding = 0.25f; + data->threshold = CMP_DEFAULT_SMAA_THRESHOLD; + data->contrast_limit = CMP_DEFAULT_SMAA_CONTRAST_LIMIT; + data->corner_rounding = CMP_DEFAULT_SMAA_CORNER_ROUNDING; node->storage = data; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index b1b17a321b8..4286db52115 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -82,7 +82,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords) copy_v3_v3(result->mvert[i].co, co); } else { - BLI_assert(!"Unexpected new vertex in hull output"); + BLI_assert_msg(0, "Unexpected new vertex in hull output"); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index 1b9e9af3e2d..ae947b7aeed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -83,9 +83,10 @@ static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3) } static std::unique_ptr<CurveEval> create_point_circle_curve( - const float3 p1, const float3 p2, const float3 p3, const int resolution, float center_out[3]) + const float3 p1, const float3 p2, const float3 p3, const int resolution, float3 &r_center) { if (colinear_f3_f3_f3(p1, p2, p3)) { + r_center = float3(0); return nullptr; } @@ -118,6 +119,7 @@ static std::unique_ptr<CurveEval> create_point_circle_curve( /* If the 3 planes do not intersect at one point, just return empty geometry. */ if (!isect_plane_plane_plane_v3(plane_1, plane_2, plane_3, center)) { + r_center = float3(0); return nullptr; } @@ -141,7 +143,7 @@ static std::unique_ptr<CurveEval> create_point_circle_curve( curve->add_spline(std::move(spline)); curve->attributes.reallocate(curve->splines().size()); - copy_v3_v3(center_out, center); + r_center = center; return curve; } @@ -179,18 +181,13 @@ static void geo_node_curve_primitive_circle_exec(GeoNodeExecParams params) std::unique_ptr<CurveEval> curve; if (mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS) { - float center_point[3]; + float3 center_point; curve = create_point_circle_curve(params.extract_input<float3>("Point 1"), params.extract_input<float3>("Point 2"), params.extract_input<float3>("Point 3"), std::max(params.extract_input<int>("Resolution"), 3), center_point); - if (curve) { - params.set_output("Center", float3(center_point)); - } - else { - params.set_output("Center", float3(0, 0, 0)); - } + params.set_output("Center", center_point); } else if (mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS) { curve = create_radius_circle_curve(std::max(params.extract_input<int>("Resolution"), 3), diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc index 419a7af1ba0..eed3a998ef6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc @@ -143,4 +143,4 @@ void register_node_type_geo_curve_primitive_line() ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_line_exec; ntype.draw_buttons = geo_node_curve_primitive_line_layout; nodeRegisterType(&ntype); -}
\ No newline at end of file +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc new file mode 100644 index 00000000000..5f3f159c305 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc @@ -0,0 +1,241 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" +#include "UI_interface.h" +#include "UI_resources.h" +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_curve_primitive_quadrilateral_in[] = { + {SOCK_FLOAT, N_("Width"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Height"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Bottom Width"), 4.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Top Width"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Offset"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Bottom Height"), 3.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Top Height"), 1.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Point 1"), -1.0f, 1.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Point 2"), 1.0f, 1.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Point 3"), 1.0f, -1.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, + {SOCK_VECTOR, N_("Point 4"), -1.0f, -1.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_primitive_quadrilateral_out[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +static void geo_node_curve_primitive_quadrilateral_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void geo_node_curve_primitive_quadrilateral_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurvePrimitiveQuad *data = (NodeGeometryCurvePrimitiveQuad *)MEM_callocN( + sizeof(NodeGeometryCurvePrimitiveQuad), __func__); + data->mode = GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE; + node->storage = data; +} + +namespace blender::nodes { + +static void geo_node_curve_primitive_quadrilateral_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryCurvePrimitiveQuad &node_storage = *(NodeGeometryCurvePrimitiveQuad *)node->storage; + GeometryNodeCurvePrimitiveQuadMode mode = static_cast<GeometryNodeCurvePrimitiveQuadMode>( + node_storage.mode); + + bNodeSocket *width = ((bNodeSocket *)node->inputs.first); + bNodeSocket *height = width->next; + bNodeSocket *bottom = height->next; + bNodeSocket *top = bottom->next; + bNodeSocket *offset = top->next; + bNodeSocket *bottom_height = offset->next; + bNodeSocket *top_height = bottom_height->next; + bNodeSocket *p1 = top_height->next; + bNodeSocket *p2 = p1->next; + bNodeSocket *p3 = p2->next; + bNodeSocket *p4 = p3->next; + + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + nodeSetSocketAvailability(sock, false); + } + + if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE) { + nodeSetSocketAvailability(width, true); + nodeSetSocketAvailability(height, true); + } + else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_PARALLELOGRAM) { + nodeSetSocketAvailability(width, true); + nodeSetSocketAvailability(height, true); + nodeSetSocketAvailability(offset, true); + } + else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_TRAPEZOID) { + nodeSetSocketAvailability(bottom, true); + nodeSetSocketAvailability(top, true); + nodeSetSocketAvailability(offset, true); + nodeSetSocketAvailability(height, true); + } + else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_KITE) { + nodeSetSocketAvailability(width, true); + nodeSetSocketAvailability(bottom_height, true); + nodeSetSocketAvailability(top_height, true); + } + else if (mode == GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_POINTS) { + nodeSetSocketAvailability(p1, true); + nodeSetSocketAvailability(p2, true); + nodeSetSocketAvailability(p3, true); + nodeSetSocketAvailability(p4, true); + } +} + +static void create_rectangle_curve(MutableSpan<float3> positions, + const float height, + const float width) +{ + positions[0] = float3(width / 2.0f, -height / 2.0f, 0.0f); + positions[1] = float3(-width / 2.0f, -height / 2.0f, 0.0f); + positions[2] = float3(-width / 2.0f, height / 2.0f, 0.0f); + positions[3] = float3(width / 2.0f, height / 2.0f, 0.0f); +} + +static void create_points_curve(MutableSpan<float3> positions, + const float3 &p1, + const float3 &p2, + const float3 &p3, + const float3 &p4) +{ + positions[0] = p1; + positions[1] = p2; + positions[2] = p3; + positions[3] = p4; +} + +static void create_parallelogram_curve(MutableSpan<float3> positions, + const float height, + const float width, + const float offset) +{ + positions[0] = float3(width / 2.0f - offset / 2.0f, -height / 2.0f, 0.0f); + positions[1] = float3(-width / 2.0f - offset / 2.0f, -height / 2.0f, 0.0f); + positions[2] = float3(-width / 2.0f + offset / 2.0f, height / 2.0f, 0.0f); + positions[3] = float3(width / 2.0f + offset / 2.0f, height / 2.0f, 0.0f); +} +static void create_trapezoid_curve(MutableSpan<float3> positions, + const float bottom, + const float top, + const float offset, + const float height) +{ + positions[0] = float3(bottom / 2.0f, -height / 2.0f, 0.0f); + positions[1] = float3(-bottom / 2.0f, -height / 2.0f, 0.0f); + positions[2] = float3(-top / 2.0f + offset, height / 2.0f, 0.0f); + positions[3] = float3(top / 2.0f + offset, height / 2.0f, 0.0f); +} + +static void create_kite_curve(MutableSpan<float3> positions, + const float width, + const float bottom_height, + const float top_height) +{ + positions[0] = float3(-width / 2.0f, 0, 0); + positions[1] = float3(0, top_height, 0); + positions[2] = float3(width / 2, 0, 0); + positions[3] = float3(0, -bottom_height, 0); +} + +static void geo_node_curve_primitive_quadrilateral_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurvePrimitiveQuad &node_storage = + *(NodeGeometryCurvePrimitiveQuad *)(params.node()).storage; + const GeometryNodeCurvePrimitiveQuadMode mode = static_cast<GeometryNodeCurvePrimitiveQuadMode>( + node_storage.mode); + + std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>(); + std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>(); + spline->resize(4); + spline->radii().fill(1.0f); + spline->tilts().fill(0.0f); + spline->set_cyclic(true); + MutableSpan<float3> positions = spline->positions(); + + switch (mode) { + case GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_RECTANGLE: + create_rectangle_curve(positions, + std::max(params.extract_input<float>("Height"), 0.0f), + std::max(params.extract_input<float>("Width"), 0.0f)); + break; + + case GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_PARALLELOGRAM: + create_parallelogram_curve(positions, + std::max(params.extract_input<float>("Height"), 0.0f), + std::max(params.extract_input<float>("Width"), 0.0f), + params.extract_input<float>("Offset")); + break; + case GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_TRAPEZOID: + create_trapezoid_curve(positions, + std::max(params.extract_input<float>("Bottom Width"), 0.0f), + std::max(params.extract_input<float>("Top Width"), 0.0f), + params.extract_input<float>("Offset"), + std::max(params.extract_input<float>("Height"), 0.0f)); + break; + case GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_KITE: + create_kite_curve(positions, + std::max(params.extract_input<float>("Width"), 0.0f), + std::max(params.extract_input<float>("Bottom Height"), 0.0f), + params.extract_input<float>("Top Height")); + break; + case GEO_NODE_CURVE_PRIMITIVE_QUAD_MODE_POINTS: + create_points_curve(positions, + params.extract_input<float3>("Point 1"), + params.extract_input<float3>("Point 2"), + params.extract_input<float3>("Point 3"), + params.extract_input<float3>("Point 4")); + break; + default: + params.set_output("Curve", GeometrySet()); + return; + } + + curve->add_spline(std::move(spline)); + curve->attributes.reallocate(curve->splines().size()); + params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_primitive_quadrilateral() +{ + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, "Quadrilateral", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, + geo_node_curve_primitive_quadrilateral_in, + geo_node_curve_primitive_quadrilateral_out); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_primitive_quadrilateral_exec; + ntype.draw_buttons = geo_node_curve_primitive_quadrilateral_layout; + node_type_update(&ntype, blender::nodes::geo_node_curve_primitive_quadrilateral_update); + node_type_init(&ntype, geo_node_curve_primitive_quadrilateral_init); + node_type_storage(&ntype, + "NodeGeometryCurvePrimitiveQuad", + node_free_standard_storage, + node_copy_standard_storage); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 3de2604cd0a..7908c26e2dc 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -116,38 +116,6 @@ static void subdivide_attribute(Span<T> src, } /** - * De Casteljau Bezier subdivision. - * - * <pre> - * handle_prev handle_next - * O----------------O - * / \ - * / x---O---x \ - * / new_* \ - * / \ - * O O - * point_prev point_next - * </pre> - */ -static void calculate_new_bezier_point(const float3 &point_prev, - float3 &handle_prev, - float3 &new_left_handle, - float3 &new_position, - float3 &new_right_handle, - float3 &handle_next, - const float3 &point_next, - const float parameter) -{ - const float3 center_point = float3::interpolate(handle_prev, handle_next, parameter); - - handle_prev = float3::interpolate(point_prev, handle_prev, parameter); - handle_next = float3::interpolate(handle_next, point_next, parameter); - new_left_handle = float3::interpolate(handle_prev, center_point, parameter); - new_right_handle = float3::interpolate(center_point, handle_next, parameter); - new_position = float3::interpolate(new_left_handle, new_right_handle, parameter); -} - -/** * In order to generate a Bezier spline with the same shape as the input spline, apply the * De Casteljau algorithm iteratively for the provided number of cuts, constantly updating the * previous result point's right handle and the left handle at the end of the segment. @@ -171,6 +139,10 @@ static void subdivide_bezier_segment(const BezierSpline &src, { const bool is_last_cyclic_segment = index == (src.size() - 1); const int next_index = is_last_cyclic_segment ? 0 : index + 1; + + /* The first point in the segment is always copied. */ + dst_positions[offset] = src_positions[index]; + if (src.segment_is_vector(index)) { if (is_last_cyclic_segment) { dst_type_left.first() = BezierSpline::HandleType::Vector; @@ -178,7 +150,6 @@ static void subdivide_bezier_segment(const BezierSpline &src, dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector); dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector); - dst_positions[offset] = src_positions[index]; const float factor_delta = 1.0f / result_size; for (const int cut : IndexRange(result_size)) { const float factor = cut * factor_delta; @@ -194,21 +165,38 @@ static void subdivide_bezier_segment(const BezierSpline &src, dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free); const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size; - dst_positions[offset] = src_positions[index]; - dst_handles_right[offset] = src_handles_right[index]; - dst_handles_left[i_segment_last] = src_handles_left[next_index]; + + /* Create a Bezier segment to update iteratively for every subdivision + * and references to the meaningful values for ease of use. */ + BezierSpline temp; + temp.resize(2); + float3 &segment_start = temp.positions().first(); + float3 &segment_end = temp.positions().last(); + float3 &handle_prev = temp.handle_positions_right().first(); + float3 &handle_next = temp.handle_positions_left().last(); + segment_start = src_positions[index]; + segment_end = src_positions[next_index]; + handle_prev = src_handles_right[index]; + handle_next = src_handles_left[next_index]; for (const int cut : IndexRange(result_size - 1)) { const float parameter = 1.0f / (result_size - cut); - calculate_new_bezier_point(dst_positions[offset + cut], - dst_handles_right[offset + cut], - dst_handles_left[offset + cut + 1], - dst_positions[offset + cut + 1], - dst_handles_right[offset + cut + 1], - dst_handles_left[i_segment_last], - src_positions[next_index], - parameter); + const BezierSpline::InsertResult insert = temp.calculate_segment_insertion(0, 1, parameter); + + /* Copy relevant temporary data to the result. */ + dst_handles_right[offset + cut] = insert.handle_prev; + dst_handles_left[offset + cut + 1] = insert.left_handle; + dst_positions[offset + cut + 1] = insert.position; + + /* Update the segment to prepare it for the next subdivision. */ + segment_start = insert.position; + handle_prev = insert.right_handle; + handle_next = insert.handle_next; } + + /* Copy the handles for the last segment from the temporary spline. */ + dst_handles_right[offset + result_size - 1] = handle_prev; + dst_handles_left[i_segment_last] = handle_next; } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index b1da2dcd3c4..8c697275f88 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -627,7 +627,7 @@ static void delete_mesh_selection(MeshComponent &component, mesh_out = nullptr; break; } - component.replace_mesh_but_keep_vertex_group_names(mesh_out); + component.replace(mesh_out); } static void geo_node_delete_geometry_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc index 7492582c8cf..245d7800621 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc @@ -91,7 +91,7 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) BKE_mesh_calc_normals(mesh_out); MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); - mesh_component.replace_mesh_but_keep_vertex_group_names(mesh_out); + mesh_component.replace(mesh_out); BKE_subdiv_free(subdiv); diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 20ffcdb516d..f0f032ed8f4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -96,7 +96,7 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) BKE_mesh_calc_normals(mesh_out); MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); - mesh_component.replace_mesh_but_keep_vertex_group_names(mesh_out); + mesh_component.replace(mesh_out); // BKE_subdiv_stats_print(&subdiv->stats); BKE_subdiv_free(subdiv); diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index 85182b67c8a..3024cc51cad 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -150,6 +150,37 @@ GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_ful 8); for (const GeometryComponent *component : geometry_set.get_components_for_read()) { component_types_.append(component->type()); + switch (component->type()) { + case GEO_COMPONENT_TYPE_MESH: { + const MeshComponent &mesh_component = *(const MeshComponent *)component; + MeshInfo &info = this->mesh_info.emplace(); + info.tot_verts = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT); + info.tot_edges = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); + info.tot_faces = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE); + break; + } + case GEO_COMPONENT_TYPE_CURVE: { + const CurveComponent &curve_component = *(const CurveComponent *)component; + CurveInfo &info = this->curve_info.emplace(); + info.tot_splines = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE); + break; + } + case GEO_COMPONENT_TYPE_POINT_CLOUD: { + const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component; + PointCloudInfo &info = this->pointcloud_info.emplace(); + info.tot_points = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT); + break; + } + case GEO_COMPONENT_TYPE_INSTANCES: { + const InstancesComponent &instances_component = *(const InstancesComponent *)component; + InstancesInfo &info = this->instances_info.emplace(); + info.tot_instances = instances_component.instances_amount(); + break; + } + case GEO_COMPONENT_TYPE_VOLUME: { + break; + } + } } if (log_full_geometry) { full_geometry_ = std::make_unique<GeometrySet>(geometry_set); diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index 5755a14f14d..bfd1ad02d36 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -30,6 +30,9 @@ namespace blender::nodes { void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::string message) const { + if (provider_->logger == nullptr) { + return; + } LocalGeoLogger &local_logger = provider_->logger->local(); local_logger.log_node_warning(provider_->dnode, type, std::move(message)); } diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index bed4d60382d..9ce9d6fc273 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -461,7 +461,7 @@ bool NodeTreeRef::has_undefined_nodes_or_sockets() const return true; } } - return true; + return false; } std::string NodeTreeRef::to_dot() const diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h index 90f54c50a6d..84d804f8bdf 100644 --- a/source/blender/python/BPY_extern.h +++ b/source/blender/python/BPY_extern.h @@ -20,8 +20,8 @@ #pragma once -struct AnimationEvalContext; struct ARegionType; +struct AnimationEvalContext; struct ChannelDriver; /* DNA_anim_types.h */ struct ID; /* DNA_ID.h */ struct ListBase; /* DNA_listBase.h */ diff --git a/source/blender/python/bmesh/bmesh_py_ops_call.c b/source/blender/python/bmesh/bmesh_py_ops_call.c index 3d5aabcfda9..24887b24eb6 100644 --- a/source/blender/python/bmesh/bmesh_py_ops_call.c +++ b/source/blender/python/bmesh/bmesh_py_ops_call.c @@ -608,7 +608,7 @@ static PyObject *bpy_slot_to_py(BMesh *bm, BMOpSlot *slot) /* keep switch in same order as above */ switch (slot->slot_type) { case BMO_OP_SLOT_BOOL: - item = PyBool_FromLong((BMO_SLOT_AS_BOOL(slot))); + item = PyBool_FromLong(BMO_SLOT_AS_BOOL(slot)); break; case BMO_OP_SLOT_INT: item = PyLong_FromLong(BMO_SLOT_AS_INT(slot)); diff --git a/source/blender/python/bmesh/bmesh_py_utils.c b/source/blender/python/bmesh/bmesh_py_utils.c index c1e28182c53..1c3334f1adc 100644 --- a/source/blender/python/bmesh/bmesh_py_utils.c +++ b/source/blender/python/bmesh/bmesh_py_utils.c @@ -189,7 +189,7 @@ static PyObject *bpy_bm_utils_vert_dissolve(PyObject *UNUSED(self), PyObject *ar bm = py_vert->bm; - return PyBool_FromLong((BM_vert_dissolve(bm, py_vert->v))); + return PyBool_FromLong(BM_vert_dissolve(bm, py_vert->v)); } PyDoc_STRVAR(bpy_bm_utils_vert_splice_doc, diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c index 7c5c1122aea..d66643c5d61 100644 --- a/source/blender/python/intern/bpy_app_handlers.c +++ b/source/blender/python/intern/bpy_app_handlers.c @@ -225,7 +225,7 @@ PyObject *BPY_app_handlers_struct(void) #endif if (PyType_Ready(&BPyPersistent_Type) < 0) { - BLI_assert(!"error initializing 'bpy.app.handlers.persistent'"); + BLI_assert_msg(0, "error initializing 'bpy.app.handlers.persistent'"); } PyStructSequence_InitType(&BlenderAppCbType, &app_cb_info_desc); @@ -283,7 +283,7 @@ void BPY_app_handlers_reset(const short do_all) for (i = PyList_GET_SIZE(ls) - 1; i >= 0; i--) { - if ((PyFunction_Check((item = PyList_GET_ITEM(ls, i)))) && + if (PyFunction_Check((item = PyList_GET_ITEM(ls, i))) && (dict_ptr = _PyObject_GetDictPtr(item)) && (*dict_ptr) && (PyDict_GetItem(*dict_ptr, perm_id_str) != NULL)) { /* keep */ diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c index 28a97c3fa3b..d9a357c5e2e 100644 --- a/source/blender/python/intern/bpy_library_load.c +++ b/source/blender/python/intern/bpy_library_load.c @@ -66,6 +66,8 @@ typedef struct { char relpath[FILE_MAX]; char abspath[FILE_MAX]; /* absolute path */ BlendHandle *blo_handle; + /* Referenced by `blo_handle`, so stored here to keep alive for long enough. */ + BlendFileReadReport bf_reports; int flag; PyObject *dict; /* Borrowed reference to the `bmain`, taken from the RNA instance of #RNA_BlendDataLibraries. @@ -259,7 +261,8 @@ static PyObject *bpy_lib_enter(BPy_Library *self) BKE_reports_init(&reports, RPT_STORE); BlendFileReadReport bf_reports = {.reports = &reports}; - self->blo_handle = BLO_blendhandle_from_file(self->abspath, &bf_reports); + self->bf_reports = bf_reports; + self->blo_handle = BLO_blendhandle_from_file(self->abspath, &self->bf_reports); if (self->blo_handle == NULL) { if (BPy_reports_to_error(&reports, PyExc_IOError, true) != -1) { diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index 14e25e02d84..f332d547965 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -1779,7 +1779,7 @@ static const EnumPropertyItem *enum_items_from_py(PyObject *seq_fast, item = seq_fast_items[i]; - if ((PyTuple_CheckExact(item)) && (item_size = PyTuple_GET_SIZE(item)) && + if (PyTuple_CheckExact(item) && (item_size = PyTuple_GET_SIZE(item)) && (item_size >= 3 && item_size <= 5) && (tmp.identifier = PyUnicode_AsUTF8AndSize(PyTuple_GET_ITEM(item, 0), &id_str_size)) && (tmp.name = PyUnicode_AsUTF8AndSize(PyTuple_GET_ITEM(item, 1), &name_str_size)) && diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index e6479df2fa8..e6f3e509469 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -2728,7 +2728,7 @@ static PyObject *pyrna_prop_array_subscript_slice(BPy_PropertyArrayRNA *self, break; } default: - BLI_assert(!"Invalid array type"); + BLI_assert_msg(0, "Invalid array type"); PyErr_SetString(PyExc_TypeError, "not an array type"); Py_DECREF(tuple); @@ -4253,6 +4253,56 @@ static PyObject *pyrna_struct_dir(BPy_StructRNA *self) return ret; } +PyDoc_STRVAR(pyrna_struct_id_properties_ensure_doc, + ".. method:: id_properties_ensure()\n" + " :return: the parent group for an RNA struct's custom IDProperties.\n" + " :rtype: :class:`bpy.types.IDPropertyGroup`\n"); +static PyObject *pyrna_struct_id_properties_ensure(BPy_StructRNA *self) +{ + PYRNA_STRUCT_CHECK_OBJ(self); + + if (RNA_struct_idprops_check(self->ptr.type) == 0) { + PyErr_SetString(PyExc_TypeError, "This type doesn't support IDProperties"); + return NULL; + } + + IDProperty *idprops = RNA_struct_idprops(&self->ptr, true); + + /* This is a paranoid check that theoretically might not be necessary. + * It allows the possibility that some structs can't ensure IDProperties. */ + if (idprops == NULL) { + return Py_None; + } + + BPy_IDProperty *group = PyObject_New(BPy_IDProperty, &BPy_IDGroup_Type); + group->owner_id = self->ptr.owner_id; + group->prop = idprops; + group->parent = NULL; + return (PyObject *)group; +} + +PyDoc_STRVAR(pyrna_struct_id_properties_clear_doc, + ".. method:: id_properties_clear()\n" + " :return: Remove the parent group for an RNA struct's custom IDProperties.\n"); +static PyObject *pyrna_struct_id_properties_clear(BPy_StructRNA *self) +{ + PYRNA_STRUCT_CHECK_OBJ(self); + + if (RNA_struct_idprops_check(self->ptr.type) == 0) { + PyErr_SetString(PyExc_TypeError, "This type doesn't support IDProperties"); + return NULL; + } + + IDProperty **idprops = RNA_struct_idprops_p(&self->ptr); + + if (*idprops) { + IDP_FreeProperty(*idprops); + *idprops = NULL; + } + + Py_RETURN_NONE; +} + /* ---------------getattr-------------------------------------------- */ static PyObject *pyrna_struct_getattro(BPy_StructRNA *self, PyObject *pyname) { @@ -4325,7 +4375,7 @@ static PyObject *pyrna_struct_getattro(BPy_StructRNA *self, PyObject *pyname) } default: /* Should never happen. */ - BLI_assert(!"Invalid context type"); + BLI_assert_msg(0, "Invalid context type"); PyErr_Format(PyExc_AttributeError, "bpy_struct: Context type invalid %d, can't get \"%.200s\" from context", @@ -5339,7 +5389,7 @@ static PyObject *foreach_getset(BPy_PropertyRNA *self, PyObject *args, int set) break; case PROP_RAW_UNSET: /* Should never happen. */ - BLI_assert(!"Invalid array type - set"); + BLI_assert_msg(0, "Invalid array type - set"); break; } @@ -5403,7 +5453,7 @@ static PyObject *foreach_getset(BPy_PropertyRNA *self, PyObject *args, int set) break; default: /* PROP_RAW_UNSET */ /* Should never happen. */ - BLI_assert(!"Invalid array type - get"); + BLI_assert_msg(0, "Invalid array type - get"); item = Py_None; Py_INCREF(item); break; @@ -5743,6 +5793,14 @@ static struct PyMethodDef pyrna_struct_methods[] = { METH_VARARGS | METH_CLASS, pyrna_struct_bl_rna_get_subclass_doc}, {"__dir__", (PyCFunction)pyrna_struct_dir, METH_NOARGS, NULL}, + {"id_properties_ensure", + (PyCFunction)pyrna_struct_id_properties_ensure, + METH_NOARGS, + pyrna_struct_id_properties_ensure_doc}, + {"id_properties_clear", + (PyCFunction)pyrna_struct_id_properties_clear, + METH_NOARGS, + pyrna_struct_id_properties_clear_doc}, /* experimental */ /* unused for now */ diff --git a/source/blender/python/mathutils/mathutils_Color.c b/source/blender/python/mathutils/mathutils_Color.c index 819874e4355..7546f2ef730 100644 --- a/source/blender/python/mathutils/mathutils_Color.c +++ b/source/blender/python/mathutils/mathutils_Color.c @@ -570,7 +570,7 @@ static PyObject *Color_mul(PyObject *v1, PyObject *v2) } } else { - BLI_assert(!"internal error"); + BLI_assert_msg(0, "internal error"); } PyErr_Format(PyExc_TypeError, diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index dc98e3313c9..8b8130f3cc2 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -2998,7 +2998,7 @@ static int Matrix_translation_set(MatrixObject *self, PyObject *value, void *UNU return -1; } - if ((mathutils_array_parse(tvec, 3, 3, value, "Matrix.translation")) == -1) { + if (mathutils_array_parse(tvec, 3, 3, value, "Matrix.translation") == -1) { return -1; } diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c index 86ed88d37ab..657cd1f606b 100644 --- a/source/blender/render/intern/engine.c +++ b/source/blender/render/intern/engine.c @@ -679,7 +679,7 @@ static void engine_depsgraph_init(RenderEngine *engine, ViewLayer *view_layer) DRW_render_context_enable(engine->re); } - DEG_evaluate_on_framechange(depsgraph, CFRA); + DEG_evaluate_on_framechange(depsgraph, BKE_scene_frame_get(scene)); if (use_gpu_context) { DRW_render_context_disable(engine->re); diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index bc03158f036..6329901b4ce 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -1828,7 +1828,7 @@ void RE_SetReports(Render *re, ReportList *reports) static void render_update_depsgraph(Render *re) { Scene *scene = re->scene; - DEG_evaluate_on_framechange(re->pipeline_depsgraph, CFRA); + DEG_evaluate_on_framechange(re->pipeline_depsgraph, BKE_scene_frame_get(scene)); BKE_scene_update_sound(re->pipeline_depsgraph, re->main); } @@ -2410,7 +2410,7 @@ void RE_RenderAnim(Render *re, * -sergey- */ { - float ctime = BKE_scene_frame_get(scene); + float ctime = BKE_scene_ctime_get(scene); AnimData *adt = BKE_animdata_from_id(&scene->id); const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct( re->pipeline_depsgraph, ctime); diff --git a/source/blender/render/intern/texture_pointdensity.c b/source/blender/render/intern/texture_pointdensity.c index c0ff00d4f59..31d5bf67f28 100644 --- a/source/blender/render/intern/texture_pointdensity.c +++ b/source/blender/render/intern/texture_pointdensity.c @@ -169,7 +169,7 @@ static void pointdensity_cache_psys( ParticleCacheKey *cache; ParticleSimulationData sim = {NULL}; ParticleData *pa = NULL; - float cfra = BKE_scene_frame_get(scene); + float cfra = BKE_scene_ctime_get(scene); int i /*, Childexists*/ /* UNUSED */; int total_particles; int data_used; @@ -347,9 +347,9 @@ static void pointdensity_cache_vertex_weight(PointDensity *pd, if (!mdef) { return; } - mdef_index = BKE_object_defgroup_name_index(ob, pd->vertex_attribute_name); + mdef_index = BKE_id_defgroup_name_index(&mesh->id, pd->vertex_attribute_name); if (mdef_index < 0) { - mdef_index = ob->actdef - 1; + mdef_index = BKE_object_defgroup_active_index_get(ob) - 1; } if (mdef_index < 0) { return; @@ -782,7 +782,7 @@ static void particle_system_minmax(Depsgraph *depsgraph, float max[3]) { const float size[3] = {radius, radius, radius}; - const float cfra = BKE_scene_frame_get(scene); + const float cfra = BKE_scene_ctime_get(scene); ParticleSettings *part = psys->part; ParticleSimulationData sim = {NULL}; ParticleData *pa = NULL; diff --git a/source/blender/sequencer/SEQ_clipboard.h b/source/blender/sequencer/SEQ_clipboard.h index 4b2bf69a8ac..ea7f01e6ae3 100644 --- a/source/blender/sequencer/SEQ_clipboard.h +++ b/source/blender/sequencer/SEQ_clipboard.h @@ -29,12 +29,16 @@ extern "C" { struct ListBase; struct Main; +struct Scene; +struct Sequence; extern struct ListBase seqbase_clipboard; extern int seqbase_clipboard_frame; void SEQ_clipboard_pointers_store(struct Main *bmain, struct ListBase *seqbase); void SEQ_clipboard_pointers_restore(struct ListBase *seqbase, struct Main *bmain); void SEQ_clipboard_free(void); +void SEQ_clipboard_active_seq_name_store(struct Scene *scene); +bool SEQ_clipboard_pasted_seq_was_active(struct Sequence *pasted_seq); #ifdef __cplusplus } diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h index aa2e182e1c0..e4c9f20f736 100644 --- a/source/blender/sequencer/SEQ_iterator.h +++ b/source/blender/sequencer/SEQ_iterator.h @@ -30,9 +30,9 @@ extern "C" { #include "BLI_ghash.h" struct Editing; -struct Sequence; struct GSet; struct GSetIterator; +struct Sequence; #define SEQ_ITERATOR_FOREACH(var, collection) \ for (SeqIterator iter = {{{NULL}}}; \ diff --git a/source/blender/sequencer/SEQ_proxy.h b/source/blender/sequencer/SEQ_proxy.h index b06adef2802..7bfe932ff1c 100644 --- a/source/blender/sequencer/SEQ_proxy.h +++ b/source/blender/sequencer/SEQ_proxy.h @@ -34,8 +34,8 @@ struct ListBase; struct Main; struct Scene; struct SeqIndexBuildContext; -struct Sequence; struct SeqRenderData; +struct Sequence; bool SEQ_proxy_rebuild_context(struct Main *bmain, struct Depsgraph *depsgraph, diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h index 706f4064bf3..f4338d13c8f 100644 --- a/source/blender/sequencer/SEQ_sequencer.h +++ b/source/blender/sequencer/SEQ_sequencer.h @@ -32,8 +32,8 @@ extern "C" { struct Editing; struct Scene; struct Sequence; -struct SequencerToolSettings; struct SequenceLookup; +struct SequencerToolSettings; /* RNA enums, just to be more readable */ enum { diff --git a/source/blender/sequencer/SEQ_time.h b/source/blender/sequencer/SEQ_time.h index 593a8fe7bf2..732e9bb985a 100644 --- a/source/blender/sequencer/SEQ_time.h +++ b/source/blender/sequencer/SEQ_time.h @@ -44,7 +44,6 @@ int SEQ_time_find_next_prev_edit(struct Scene *scene, const bool do_unselected); void SEQ_time_update_sequence(struct Scene *scene, struct Sequence *seq); void SEQ_time_update_sequence_bounds(struct Scene *scene, struct Sequence *seq); -int SEQ_time_cmp_time_startdisp(const void *a, const void *b); bool SEQ_time_strip_intersects_frame(const struct Sequence *seq, const int timeline_frame); void SEQ_time_update_meta_strip_range(struct Scene *scene, struct Sequence *seq_meta); diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h index 837a2de5742..9ff827333be 100644 --- a/source/blender/sequencer/SEQ_transform.h +++ b/source/blender/sequencer/SEQ_transform.h @@ -29,8 +29,8 @@ extern "C" { struct ListBase; struct Scene; -struct Sequence; struct SeqCollection; +struct Sequence; int SEQ_transform_get_left_handle_frame(struct Sequence *seq); int SEQ_transform_get_right_handle_frame(struct Sequence *seq); diff --git a/source/blender/sequencer/intern/clipboard.c b/source/blender/sequencer/intern/clipboard.c index e3f82dd4bb0..9e702a4e60b 100644 --- a/source/blender/sequencer/intern/clipboard.c +++ b/source/blender/sequencer/intern/clipboard.c @@ -24,6 +24,8 @@ * \ingroup bke */ +#include <string.h> + #include "MEM_guardedalloc.h" #include "DNA_scene_types.h" @@ -31,6 +33,7 @@ #include "DNA_sound_types.h" #include "BLI_listbase.h" +#include "BLI_string.h" #include "BKE_main.h" #include "BKE_movieclip.h" @@ -38,6 +41,7 @@ #include "BKE_sound.h" #include "SEQ_clipboard.h" +#include "SEQ_select.h" #include "sequencer.h" @@ -55,6 +59,7 @@ ListBase seqbase_clipboard; int seqbase_clipboard_frame; +static char seq_clipboard_active_seq_name[SEQ_NAME_MAXSTR]; void seq_clipboard_pointers_free(struct ListBase *seqbase); @@ -177,3 +182,26 @@ void SEQ_clipboard_pointers_restore(ListBase *seqbase, Main *bmain) SEQ_clipboard_pointers_restore(&seq->seqbase, bmain); } } + +void SEQ_clipboard_active_seq_name_store(Scene *scene) +{ + Sequence *active_seq = SEQ_select_active_get(scene); + if (active_seq != NULL) { + STRNCPY(seq_clipboard_active_seq_name, active_seq->name); + } + else { + seq_clipboard_active_seq_name[0] = '\0'; + } +} + +/** + * Check if strip was active when it was copied. User should restrict this check to pasted strips + * before ensuring original name, because strip name comparison is used to check. + * + * \param pasted_seq: Strip that is pasted(duplicated) from clipboard + * \return true if strip was active, false otherwise + */ +bool SEQ_clipboard_pasted_seq_was_active(Sequence *pasted_seq) +{ + return STREQ(pasted_seq->name, seq_clipboard_active_seq_name); +} diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c index a6e4b6ea7ed..604c9900355 100644 --- a/source/blender/sequencer/intern/image_cache.c +++ b/source/blender/sequencer/intern/image_cache.c @@ -521,7 +521,7 @@ static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header) BLI_fseek(file, 0LL, SEEK_SET); const size_t num_items_read = fread(header, sizeof(*header), 1, file); if (num_items_read < 1) { - BLI_assert(!"unable to read disk cache header"); + BLI_assert_msg(0, "unable to read disk cache header"); perror("unable to read disk cache header"); return false; } diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 28ff78cecf6..aba84743621 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -370,7 +370,7 @@ int seq_get_shown_sequences(ListBase *seqbase, const int strip_count = BLI_gset_len(collection->set); if (strip_count > MAXSEQ) { - BLI_assert(!"Too many strips, this shouldn't happen"); + BLI_assert_msg(0, "Too many strips, this shouldn't happen"); return 0; } diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c index b9278b9f971..0dc8dfa10d7 100644 --- a/source/blender/sequencer/intern/strip_edit.c +++ b/source/blender/sequencer/intern/strip_edit.c @@ -407,6 +407,8 @@ Sequence *SEQ_edit_strip_split(Main *bmain, BLI_addtail(&left_strips, seq); } + SEQ_collection_free(collection); + /* Sort list, so that no strip can depend on next strip in list. * This is important for SEQ_time_update_sequence functionality. */ SEQ_sort(&left_strips); diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index ecd6b0c833d..20e2421ea88 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -257,15 +257,6 @@ void SEQ_time_update_sequence(Scene *scene, Sequence *seq) } } -/** Comparison function suitable to be used with BLI_listbase_sort()... */ -int SEQ_time_cmp_time_startdisp(const void *a, const void *b) -{ - const Sequence *seq_a = a; - const Sequence *seq_b = b; - - return (seq_a->startdisp > seq_b->startdisp); -} - int SEQ_time_find_next_prev_edit(Scene *scene, int timeline_frame, const short side, diff --git a/source/blender/simulation/SIM_mass_spring.h b/source/blender/simulation/SIM_mass_spring.h index 43de8b155cf..b3299258209 100644 --- a/source/blender/simulation/SIM_mass_spring.h +++ b/source/blender/simulation/SIM_mass_spring.h @@ -45,6 +45,8 @@ void SIM_mass_spring_solver_free(struct Implicit_Data *id); int SIM_mass_spring_solver_numvert(struct Implicit_Data *id); int SIM_cloth_solver_init(struct Object *ob, struct ClothModifierData *clmd); +void SIM_mass_spring_set_implicit_vertex_mass(struct Implicit_Data *data, int index, float mass); + void SIM_cloth_solver_free(struct ClothModifierData *clmd); int SIM_cloth_solve(struct Depsgraph *depsgraph, struct Object *ob, diff --git a/source/blender/simulation/intern/SIM_mass_spring.cpp b/source/blender/simulation/intern/SIM_mass_spring.cpp index cf654ebff07..ca01120eecb 100644 --- a/source/blender/simulation/intern/SIM_mass_spring.cpp +++ b/source/blender/simulation/intern/SIM_mass_spring.cpp @@ -203,7 +203,7 @@ int SIM_cloth_solver_init(Object *UNUSED(ob), ClothModifierData *clmd) cloth->implicit = id = SIM_mass_spring_solver_create(cloth->mvert_num, nondiag); for (i = 0; i < cloth->mvert_num; i++) { - SIM_mass_spring_set_vertex_mass(id, i, verts[i].mass); + SIM_mass_spring_set_implicit_vertex_mass(id, i, verts[i].mass); } for (i = 0; i < cloth->mvert_num; i++) { @@ -213,6 +213,10 @@ int SIM_cloth_solver_init(Object *UNUSED(ob), ClothModifierData *clmd) return 1; } +void SIM_mass_spring_set_implicit_vertex_mass(Implicit_Data *data, int index, float mass){ + SIM_mass_spring_set_vertex_mass(data, index, mass); +} + void SIM_cloth_solver_free(ClothModifierData *clmd) { Cloth *cloth = clmd->clothObject; diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 1f4598d33fe..1c994707ca9 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -618,9 +618,14 @@ void WM_operator_type_modal_from_exec_for_object_edit_coords(struct wmOperatorTy void WM_uilisttype_init(void); struct uiListType *WM_uilisttype_find(const char *idname, bool quiet); bool WM_uilisttype_add(struct uiListType *ult); -void WM_uilisttype_freelink(struct uiListType *ult); +void WM_uilisttype_remove_ptr(struct Main *bmain, struct uiListType *ult); void WM_uilisttype_free(void); +void WM_uilisttype_to_full_list_id(const struct uiListType *ult, + const char *list_id, + char r_full_list_id[]); +const char *WM_uilisttype_list_id_get(const struct uiListType *ult, struct uiList *list); + /* wm_menu_type.c */ void WM_menutype_init(void); struct MenuType *WM_menutype_find(const char *idname, bool quiet); diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 0e3754ae73b..2b48a5f6648 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -439,6 +439,13 @@ typedef struct wmNotifier { #define ND_SPACE_FILE_PREVIEW (21 << 16) #define ND_SPACE_SPREADSHEET (22 << 16) +/* NC_ASSET */ +/* Denotes that the AssetList is done reading some previews. NOT that the preview generation of + * assets is done. */ +#define ND_ASSET_LIST (1 << 16) +#define ND_ASSET_LIST_PREVIEW (2 << 16) +#define ND_ASSET_LIST_READING (3 << 16) + /* subtype, 256 entries too */ #define NOTE_SUBTYPE 0x0000FF00 diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 6e4e66d0daf..6a328679c2e 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -264,7 +264,7 @@ bool WM_gizmomap_minmax(const wmGizmoMap *gzmap, } bool ok = false; - BLI_assert(!"TODO"); + BLI_assert_msg(0, "TODO"); return ok; } diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c index 93b8495b309..50d3a856cbe 100644 --- a/source/blender/windowmanager/intern/wm_cursors.c +++ b/source/blender/windowmanager/intern/wm_cursors.c @@ -163,7 +163,7 @@ void WM_cursor_set(wmWindow *win, int curs) win->cursor = curs; if (curs < 0 || curs >= WM_CURSOR_NUM) { - BLI_assert(!"Invalid cursor number"); + BLI_assert_msg(0, "Invalid cursor number"); return; } diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 435b3538e5f..da40040ce56 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -321,7 +321,7 @@ void WM_drag_add_local_ID(wmDrag *drag, ID *id, ID *from_parent) return; } if (GS(drag_id->id->name) != GS(id->name)) { - BLI_assert(!"All dragged IDs must have the same type"); + BLI_assert_msg(0, "All dragged IDs must have the same type"); return; } } diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 0922aaaee53..f01e28f8822 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -457,6 +457,7 @@ static void wm_draw_region_buffer_create(ARegion *region, bool stereo, bool use_ GPUOffScreen *offscreen = GPU_offscreen_create( region->winx, region->winy, false, false, NULL); if (!offscreen) { + WM_report(RPT_ERROR, "Region could not be drawn!"); return; } diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 6789a52f890..5e29a22304c 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -61,6 +61,7 @@ #include "BLT_translation.h" +#include "ED_asset.h" #include "ED_fileselect.h" #include "ED_info.h" #include "ED_screen.h" @@ -326,6 +327,7 @@ void WM_main_remap_editor_id_reference(ID *old_id, ID *new_id) } } } + ED_assetlist_storage_id_remap(old_id, new_id); wmWindowManager *wm = bmain->wm.first; if (wm && wm->message_bus) { diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index c85727175b2..3633d3c07d3 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -929,7 +929,7 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) } else { BKE_reportf(reports, RPT_ERROR, "Unknown error loading '%s'", filepath); - BLI_assert(!"invalid 'retval'"); + BLI_assert_msg(0, "invalid 'retval'"); } if (success == false) { diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index e225b9f8aca..d7ea47fc625 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -109,6 +109,7 @@ #include "ED_anim_api.h" #include "ED_armature.h" +#include "ED_asset.h" #include "ED_gpencil.h" #include "ED_keyframes_edit.h" #include "ED_keyframing.h" @@ -552,7 +553,6 @@ void WM_exit_ex(bContext *C, const bool do_python) wm_surfaces_free(); wm_dropbox_free(); WM_menutype_free(); - WM_uilisttype_free(); /* all non-screen and non-space stuff editors did, like editmode */ if (C) { @@ -571,6 +571,7 @@ void WM_exit_ex(bContext *C, const bool do_python) RE_engines_exit(); ED_preview_free_dbase(); /* frees a Main dbase, before BKE_blender_free! */ + ED_assetlist_storage_exit(); if (wm) { /* Before BKE_blender_free! - since the ListBases get freed there. */ @@ -607,6 +608,8 @@ void WM_exit_ex(bContext *C, const bool do_python) wm_gizmomaptypes_free(); wm_gizmogrouptype_free(); wm_gizmotype_free(); + /* Same for UI-list types. */ + WM_uilisttype_free(); BLF_exit(); diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index e9039f3b03f..25bcf1967ea 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -971,7 +971,7 @@ static const wmKeyMapItem *wm_modalkeymap_find_propvalue_iter(const wmKeyMap *km } } else { - BLI_assert(!"called with non modal keymap"); + BLI_assert_msg(0, "called with non modal keymap"); } return NULL; diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 485e65cf3dc..576f15731a0 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -604,6 +604,12 @@ void WM_operator_properties_create(PointerRNA *ptr, const char *opstring) * used for keymaps and macros */ void WM_operator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *opstring) { + IDProperty *tmp_properties = NULL; + /* Allow passing NULL for properties, just create the properties here then. */ + if (properties == NULL) { + properties = &tmp_properties; + } + if (*properties == NULL) { IDPropertyTemplate val = {0}; *properties = IDP_New(IDP_GROUP, &val, "wmOpItemProp"); diff --git a/source/blender/windowmanager/intern/wm_uilist_type.c b/source/blender/windowmanager/intern/wm_uilist_type.c index 45c14c0bbe9..82ba4aa6e6f 100644 --- a/source/blender/windowmanager/intern/wm_uilist_type.c +++ b/source/blender/windowmanager/intern/wm_uilist_type.c @@ -21,16 +21,24 @@ */ #include <stdio.h> +#include <string.h> +#include "BLI_listbase.h" #include "BLI_sys_types.h" +#include "DNA_space_types.h" #include "DNA_windowmanager_types.h" #include "MEM_guardedalloc.h" +#include "UI_interface.h" + #include "BLI_ghash.h" +#include "BLI_listbase.h" +#include "BLI_string.h" #include "BLI_utildefines.h" +#include "BKE_main.h" #include "BKE_screen.h" #include "WM_api.h" @@ -60,8 +68,62 @@ bool WM_uilisttype_add(uiListType *ult) return 1; } -void WM_uilisttype_freelink(uiListType *ult) +static void wm_uilisttype_unlink_from_region(const uiListType *ult, ARegion *region) { + LISTBASE_FOREACH (uiList *, list, ®ion->ui_lists) { + if (list->type == ult) { + /* Don't delete the list, it's not just runtime data but stored in files. Freeing would make + * that data get lost. */ + list->type = NULL; + } + } +} + +static void wm_uilisttype_unlink_from_area(const uiListType *ult, ScrArea *area) +{ + LISTBASE_FOREACH (SpaceLink *, space_link, &area->spacedata) { + ListBase *regionbase = (space_link == area->spacedata.first) ? &area->regionbase : + &space_link->regionbase; + LISTBASE_FOREACH (ARegion *, region, regionbase) { + wm_uilisttype_unlink_from_region(ult, region); + } + } +} + +/** + * For all lists representing \a ult, clear their `uiListType` pointer. Use when a list-type is + * deleted, so that the UI doesn't keep references to it. + * + * This is a common pattern for unregistering (usually .py defined) types at runtime, e.g. see + * #WM_gizmomaptype_group_unlink(). + * Note that unlike in some other cases using this pattern, we don't actually free the lists with + * type \a ult, we just clear the reference to the type. That's because UI-Lists are written to + * files and we don't want them to get lost together with their (user visible) settings. + */ +static void wm_uilisttype_unlink(Main *bmain, const uiListType *ult) +{ + for (wmWindowManager *wm = bmain->wm.first; wm != NULL; wm = wm->id.next) { + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { + LISTBASE_FOREACH (ScrArea *, global_area, &win->global_areas.areabase) { + wm_uilisttype_unlink_from_area(ult, global_area); + } + } + } + + for (bScreen *screen = bmain->screens.first; screen != NULL; screen = screen->id.next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + wm_uilisttype_unlink_from_area(ult, area); + } + + LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) { + wm_uilisttype_unlink_from_region(ult, region); + } + } +} + +void WM_uilisttype_remove_ptr(Main *bmain, uiListType *ult) +{ + wm_uilisttype_unlink(bmain, ult); bool ok = BLI_ghash_remove(uilisttypes_hash, ult->idname, NULL, MEM_freeN); @@ -88,3 +150,34 @@ void WM_uilisttype_free(void) BLI_ghash_free(uilisttypes_hash, NULL, MEM_freeN); uilisttypes_hash = NULL; } + +/** + * The "full" list-ID is an internal name used for storing and identifying a list. It is built like + * this: + * "{uiListType.idname}_{list_id}", whereby "list_id" is an optional parameter passed to + * `UILayout.template_list()`. If it is not set, the full list-ID is just "{uiListType.idname}_". + * + * Note that whenever the Python API refers to the list-ID, it's the short, "non-full" one it + * passed to `UILayout.template_list()`. C code can query that through + * #WM_uilisttype_list_id_get(). + */ +void WM_uilisttype_to_full_list_id(const uiListType *ult, + const char *list_id, + char r_full_list_id[/*UI_MAX_NAME_STR*/]) +{ + /* We tag the list id with the list type... */ + BLI_snprintf(r_full_list_id, UI_MAX_NAME_STR, "%s_%s", ult->idname, list_id ? list_id : ""); +} + +/** + * Get the "non-full" list-ID, see #WM_uilisttype_to_full_list_id() for details. + * + * \note Assumes `uiList.list_id` was set using #WM_uilisttype_to_full_list_id()! + */ +const char *WM_uilisttype_list_id_get(const uiListType *ult, uiList *list) +{ + /* Some sanity check for the assumed behavior of #WM_uilisttype_to_full_list_id(). */ + BLI_assert((list->list_id + strlen(ult->idname))[0] == '_'); + /* +1 to skip the '_' */ + return list->list_id + strlen(ult->idname) + 1; +} diff --git a/tests/performance/api/graph.py b/tests/performance/api/graph.py index eb411915ad9..b3c8329ff27 100644 --- a/tests/performance/api/graph.py +++ b/tests/performance/api/graph.py @@ -39,15 +39,24 @@ class TestGraph: else: categories[category] = [entry] - # Generate one graph for every device x category combination. + # Generate one graph for every device x category x result key combination. for category, category_entries in categories.items(): entries = sorted(category_entries, key=lambda entry: (entry.revision, entry.test)) + + outputs = set() + for entry in entries: + for output in entry.output.keys(): + outputs.add(output) + chart_type = 'line' if entries[0].benchmark_type == 'time_series' else 'comparison' - data.append(self.chart(device_name, category, entries, chart_type)) + + for output in outputs: + chart_name = f"{category} ({output})" + data.append(self.chart(device_name, chart_name, entries, chart_type, output)) self.json = json.dumps(data, indent=2) - def chart(self, device_name: str, category: str, entries: List, chart_type: str) -> Dict: + def chart(self, device_name: str, chart_name: str, entries: List, chart_type: str, output: str) -> Dict: # Gather used tests. tests = {} for entry in entries: @@ -71,7 +80,7 @@ class TestGraph: if chart_type == 'line': cols.append({'id': '', 'label': 'Date', 'type': 'date'}) else: - cols.append({'id': '', 'label': 'Revision', 'type': 'string'}) + cols.append({'id': '', 'label': ' ', 'type': 'string'}) for test, test_index in tests.items(): cols.append({'id': '', 'label': test, 'type': 'number'}) @@ -88,11 +97,11 @@ class TestGraph: for entry in entries: test_index = tests[entry.test] revision_index = revisions[entry.revision] - time = entry.output['time'] + time = entry.output[output] if output in entry.output else -1.0 rows[revision_index]['c'][test_index + 1] = {'f': None, 'v': time} data = {'cols': cols, 'rows': rows} - return {'device': device_name, 'category': category, 'data': data, 'chart_type': chart_type} + return {'device': device_name, 'name': chart_name, 'data': data, 'chart_type': chart_type} def write(self, filepath: pathlib.Path) -> None: # Write HTML page with JSON graph data embedded. diff --git a/tests/performance/api/graph.template.html b/tests/performance/api/graph.template.html index 8929c2bdd1b..147f1628c23 100644 --- a/tests/performance/api/graph.template.html +++ b/tests/performance/api/graph.template.html @@ -52,7 +52,7 @@ /* Chart drawing options. */ var options = { - chart: {title: device["category"], subtitle: device['device']}, + chart: {title: device["name"], subtitle: device['device']}, pointsVisible: true, pointSize: 2.5, height: 500, diff --git a/tests/performance/tests/cycles.py b/tests/performance/tests/cycles.py index f79e7333458..bac6b8a7ceb 100644 --- a/tests/performance/tests/cycles.py +++ b/tests/performance/tests/cycles.py @@ -65,16 +65,26 @@ class CyclesTest(api.Test): _, lines = env.run_in_blender(_run, args, ['--debug-cycles', '--verbose', '1', self.filepath]) # Parse render time from output - prefix = "Render time (without synchronization): " - time = 0.0 + prefix_time = "Render time (without synchronization): " + prefix_memory = "Peak: " + time = None + memory = None for line in lines: line = line.strip() - offset = line.find(prefix) + offset = line.find(prefix_time) if offset != -1: - time = line[offset + len(prefix):] - return {'time': float(time)} + time = line[offset + len(prefix_time):] + time = float(time) + offset = line.find(prefix_memory) + if offset != -1: + memory = line[offset + len(prefix_memory):] + memory = memory.split()[0].replace(',', '') + memory = float(memory) + + if not (time and memory): + raise Exception("Error parsing render time output") - raise Exception("Error parsing render time output") + return {'time': time, 'peak_memory': memory} def generate(env): diff --git a/tests/python/bl_run_operators_event_simulate.py b/tests/python/bl_run_operators_event_simulate.py index 92315d3e853..1cc621b9684 100644 --- a/tests/python/bl_run_operators_event_simulate.py +++ b/tests/python/bl_run_operators_event_simulate.py @@ -165,6 +165,16 @@ def gen_events_type_text(text): yield dict(type=type, value='RELEASE', **kw_extra) +def repr_action(name, args, kwargs): + return "%s(%s)" % ( + name, + ", ".join( + [repr(value) for value in args] + + [("%s=%r" % (key, value)) for key, value in kwargs.items()] + ) + ) + + # ----------------------------------------------------------------------------- # Simulate Events @@ -505,6 +515,18 @@ def argparse_create(): required=False, ) + parser.add_argument( + "--time-actions", + dest="time_actions", + default=False, + action="store_true", + help=( + "Display the time each action takes\n" + "(useful for measuring delay between key-presses)." + ), + required=False, + ) + # Collect doc-strings from static methods in `actions`. actions_docstring = [] for action_key in ACTION_DIR: @@ -554,7 +576,7 @@ def setup_default_preferences(prefs): # Main Function -def main_event_iter(*, action_list): +def main_event_iter(*, action_list, time_actions): """ Yield all events from action handlers. """ @@ -565,9 +587,18 @@ def main_event_iter(*, action_list): yield dict(type='MOUSEMOVE', value='NOTHING', x=x_init, y=y_init) + if time_actions: + import time + t_prev = time.time() + for (op, args, kwargs) in action_list: yield from handle_action(op, args, kwargs) + if time_actions: + t = time.time() + print("%.4f: %s" % ((t - t_prev), repr_action(op, args, kwargs))) + t_prev = t + def main(): from sys import argv @@ -588,7 +619,7 @@ def main(): bpy.app.use_event_simulate = False run_event_simulate( - event_iter=main_event_iter(action_list=args.actions), + event_iter=main_event_iter(action_list=args.actions, time_actions=args.time_actions), exit_fn=exit_fn, ) |