diff options
564 files changed, 14353 insertions, 4329 deletions
diff --git a/.clang-tidy b/.clang-tidy index df5fc05bfd1..ad8a89cab87 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -14,7 +14,6 @@ Checks: > -readability-else-after-return, -readability-inconsistent-declaration-parameter-name, - -readability-non-const-parameter, -readability-redundant-preprocessor, -readability-function-size, -readability-function-size, diff --git a/CMakeLists.txt b/CMakeLists.txt index e03ebb578fa..149b7d93aed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -416,7 +416,7 @@ option(WITH_ASSERT_ABORT "Call abort() when raising an assertion through BLI_ass mark_as_advanced(WITH_ASSERT_ABORT) if(UNIX AND NOT APPLE) - option(WITH_CLANG_TIDY "Use Clang Tidy to analyze the source code (only enable for development on Limux using Clang)" OFF) + option(WITH_CLANG_TIDY "Use Clang Tidy to analyze the source code (only enable for development on Linux using Clang)" OFF) mark_as_advanced(WITH_CLANG_TIDY) endif() diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt index 3c228fd9f7d..7e7c5d58d71 100644 --- a/build_files/build_environment/CMakeLists.txt +++ b/build_files/build_environment/CMakeLists.txt @@ -30,7 +30,7 @@ # build_deps 2015 x64 / build_deps 2015 x86 # # MAC OS X USAGE: -# Install with homebrew: brew install cmake autoconf automake libtool yasm nasm +# Install with homebrew: brew install cmake autoconf automake libtool yasm nasm bison # Run "make deps" from main Blender directory # # LINUX USAGE: @@ -76,6 +76,7 @@ include(cmake/llvm.cmake) include(cmake/clang.cmake) if(APPLE) include(cmake/openmp.cmake) + include(cmake/nasm.cmake) endif() include(cmake/openimageio.cmake) include(cmake/tiff.cmake) diff --git a/build_files/build_environment/cmake/boost.cmake b/build_files/build_environment/cmake/boost.cmake index 94c649e9109..6e7ee8c66b1 100644 --- a/build_files/build_environment/cmake/boost.cmake +++ b/build_files/build_environment/cmake/boost.cmake @@ -44,7 +44,7 @@ if(WIN32) elseif(APPLE) set(BOOST_CONFIGURE_COMMAND ./bootstrap.sh) set(BOOST_BUILD_COMMAND ./b2) - set(BOOST_BUILD_OPTIONS toolset=darwin cxxflags=${PLATFORM_CXXFLAGS} linkflags=${PLATFORM_LDFLAGS} visibility=global --disable-icu boost.locale.icu=off) + set(BOOST_BUILD_OPTIONS toolset=clang-darwin cxxflags=${PLATFORM_CXXFLAGS} linkflags=${PLATFORM_LDFLAGS} visibility=global --disable-icu boost.locale.icu=off) set(BOOST_HARVEST_CMD echo .) set(BOOST_PATCH_COMMAND echo .) else() diff --git a/build_files/build_environment/cmake/check_software.cmake b/build_files/build_environment/cmake/check_software.cmake index f5774551879..384915aba84 100644 --- a/build_files/build_environment/cmake/check_software.cmake +++ b/build_files/build_environment/cmake/check_software.cmake @@ -30,6 +30,7 @@ if(UNIX) nasm yasm tclsh + bison ) foreach(_software ${_required_software}) @@ -40,6 +41,12 @@ if(UNIX) unset(_software_find CACHE) endforeach() + if(APPLE) + if(NOT EXISTS "/usr/local/opt/bison/bin/bison") + set(_software_missing "${_software_missing} bison") + endif() + endif() + if(_software_missing) message( "\n" @@ -50,7 +57,7 @@ if(UNIX) " apt install autoconf automake libtool yasm nasm tcl\n" "\n" "On macOS (with homebrew):\n" - " brew install cmake autoconf automake libtool yasm nasm\n" + " brew install cmake autoconf automake libtool yasm nasm bison\n" "\n" "Other platforms:\n" " Install equivalent packages.\n") diff --git a/build_files/build_environment/cmake/ffmpeg.cmake b/build_files/build_environment/cmake/ffmpeg.cmake index 02e78c605af..164997b9aa5 100644 --- a/build_files/build_environment/cmake/ffmpeg.cmake +++ b/build_files/build_environment/cmake/ffmpeg.cmake @@ -50,7 +50,8 @@ if(APPLE) set(FFMPEG_EXTRA_FLAGS ${FFMPEG_EXTRA_FLAGS} --target-os=darwin - ) + --x86asmexe=${LIBDIR}/nasm/bin/nasm + ) endif() ExternalProject_Add(external_ffmpeg @@ -143,6 +144,12 @@ if(WIN32) external_zlib_mingw ) endif() +if(APPLE) + add_dependencies( + external_ffmpeg + external_nasm + ) +endif() if(BUILD_MODE STREQUAL Release AND WIN32) ExternalProject_Add_Step(external_ffmpeg after_install diff --git a/build_files/build_environment/cmake/freetype.cmake b/build_files/build_environment/cmake/freetype.cmake index 30dd2eed676..fefe2c900bc 100644 --- a/build_files/build_environment/cmake/freetype.cmake +++ b/build_files/build_environment/cmake/freetype.cmake @@ -24,7 +24,8 @@ set(FREETYPE_EXTRA_ARGS -DFT_WITH_HARFBUZZ=OFF -DFT_WITH_BZIP2=OFF -DCMAKE_DISABLE_FIND_PACKAGE_HarfBuzz=TRUE - -DCMAKE_DISABLE_FIND_PACKAGE_BZip2=TRUE) + -DCMAKE_DISABLE_FIND_PACKAGE_BZip2=TRUE + -DCMAKE_DISABLE_FIND_PACKAGE_BrotliDec=TRUE) ExternalProject_Add(external_freetype URL ${FREETYPE_URI} diff --git a/build_files/build_environment/cmake/ispc.cmake b/build_files/build_environment/cmake/ispc.cmake index 0bb5db82aea..b67351dcf9f 100644 --- a/build_files/build_environment/cmake/ispc.cmake +++ b/build_files/build_environment/cmake/ispc.cmake @@ -22,6 +22,17 @@ if(WIN32) -DBISON_EXECUTABLE=${LIBDIR}/flexbison/win_bison.exe -DM4_EXECUTABLE=${DOWNLOAD_DIR}/mingw/mingw64/msys/1.0/bin/m4.exe ) +elseif(APPLE) + # Use bison installed via Homebrew. + # The one which comes which Xcode toolset is too old. + set(ISPC_EXTRA_ARGS_APPLE + -DBISON_EXECUTABLE=/usr/local/opt/bison/bin/bison + ) +elseif(UNIX) + set(ISPC_EXTRA_ARGS_UNIX + -DCMAKE_C_COMPILER=${LIBDIR}/clang/bin/clang + -DCMAKE_CXX_COMPILER=${LIBDIR}/clang/bin/clang++ + ) endif() set(ISPC_EXTRA_ARGS @@ -36,6 +47,8 @@ set(ISPC_EXTRA_ARGS -DCLANG_LIBRARY_DIR=${LIBDIR}/clang/lib -DCLANG_INCLUDE_DIRS=${LIBDIR}/clang/include ${ISPC_EXTRA_ARGS_WIN} + ${ISPC_EXTRA_ARGS_APPLE} + ${ISPC_EXTRA_ARGS_UNIX} ) ExternalProject_Add(external_ispc diff --git a/build_files/build_environment/cmake/nasm.cmake b/build_files/build_environment/cmake/nasm.cmake new file mode 100644 index 00000000000..51d7ebd8830 --- /dev/null +++ b/build_files/build_environment/cmake/nasm.cmake @@ -0,0 +1,29 @@ +# ***** 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 ***** + +ExternalProject_Add(external_nasm + URL ${NASM_URI} + DOWNLOAD_DIR ${DOWNLOAD_DIR} + URL_HASH SHA256=${NASM_HASH} + PREFIX ${BUILD_DIR}/nasm + PATCH_COMMAND ${PATCH_CMD} --verbose -p 1 -N -d ${BUILD_DIR}/nasm/src/external_nasm < ${PATCH_DIR}/nasm.diff + CONFIGURE_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/nasm/src/external_nasm/ && ${CONFIGURE_COMMAND} --prefix=${LIBDIR}/nasm + BUILD_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/nasm/src/external_nasm/ && make -j${MAKE_THREADS} + INSTALL_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/nasm/src/external_nasm/ && make install + INSTALL_DIR ${LIBDIR}/nasm +) diff --git a/build_files/build_environment/cmake/ogg.cmake b/build_files/build_environment/cmake/ogg.cmake index e2d0f0905b8..808a35c6e4d 100644 --- a/build_files/build_environment/cmake/ogg.cmake +++ b/build_files/build_environment/cmake/ogg.cmake @@ -21,6 +21,7 @@ ExternalProject_Add(external_ogg DOWNLOAD_DIR ${DOWNLOAD_DIR} URL_HASH SHA256=${OGG_HASH} PREFIX ${BUILD_DIR}/ogg + PATCH_COMMAND ${PATCH_CMD} --verbose -p 1 -N -d ${BUILD_DIR}/ogg/src/external_ogg < ${PATCH_DIR}/ogg.diff CONFIGURE_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/ogg/src/external_ogg/ && ${CONFIGURE_COMMAND} --prefix=${LIBDIR}/ogg --disable-shared --enable-static BUILD_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/ogg/src/external_ogg/ && make -j${MAKE_THREADS} INSTALL_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/ogg/src/external_ogg/ && make install diff --git a/build_files/build_environment/cmake/options.cmake b/build_files/build_environment/cmake/options.cmake index 4b973067020..39334af0bcf 100644 --- a/build_files/build_environment/cmake/options.cmake +++ b/build_files/build_environment/cmake/options.cmake @@ -113,14 +113,18 @@ else() COMMAND xcode-select --print-path OUTPUT_VARIABLE XCODE_DEV_PATH OUTPUT_STRIP_TRAILING_WHITESPACE ) + execute_process( + COMMAND xcodebuild -version -sdk macosx SDKVersion + OUTPUT_VARIABLE MACOSX_SDK_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) + set(OSX_ARCHITECTURES x86_64) - set(OSX_DEPLOYMENT_TARGET 10.11) + set(OSX_DEPLOYMENT_TARGET 10.13) set(OSX_SYSROOT ${XCODE_DEV_PATH}/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk) set(PLATFORM_CFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET}") set(PLATFORM_CXXFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET} -std=c++11 -stdlib=libc++") set(PLATFORM_LDFLAGS "-isysroot ${OSX_SYSROOT} -mmacosx-version-min=${OSX_DEPLOYMENT_TARGET}") - set(PLATFORM_BUILD_TARGET --build=x86_64-apple-darwin15.0.0) # OS X 10.11 + set(PLATFORM_BUILD_TARGET --build=x86_64-apple-darwin17.0.0) # OS X 10.13 set(PLATFORM_CMAKE_FLAGS -DCMAKE_OSX_ARCHITECTURES:STRING=${OSX_ARCHITECTURES} -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=${OSX_DEPLOYMENT_TARGET} @@ -155,6 +159,7 @@ else() set(CONFIGURE_ENV export MACOSX_DEPLOYMENT_TARGET=${OSX_DEPLOYMENT_TARGET} && + export MACOSX_SDK_VERSION=${OSX_DEPLOYMENT_TARGET} && export CFLAGS=${PLATFORM_CFLAGS} && export CXXFLAGS=${PLATFORM_CXXFLAGS} && export LDFLAGS=${PLATFORM_LDFLAGS} diff --git a/build_files/build_environment/cmake/python.cmake b/build_files/build_environment/cmake/python.cmake index 2d64feb9858..681b20577d6 100644 --- a/build_files/build_environment/cmake/python.cmake +++ b/build_files/build_environment/cmake/python.cmake @@ -48,7 +48,12 @@ if(WIN32) else() if(APPLE) - # disable functions that can be in 10.13 sdk but aren't available on 10.9 target + # Disable functions that can be in 10.13 sdk but aren't available on 10.9 target. + # + # Disable libintl (gettext library) as it might come from Homebrew, which makes + # it so test program compiles, but the Python does not. This is because for Python + # we use isysroot, which seems to forbid using libintl.h. + # The gettext functionality seems to come from CoreFoundation, so should be all fine. set(PYTHON_FUNC_CONFIGS export ac_cv_func_futimens=no && export ac_cv_func_utimensat=no && @@ -60,7 +65,10 @@ else() export ac_cv_func_getentropy=no && export ac_cv_func_mkostemp=no && export ac_cv_func_mkostemps=no && - export ac_cv_func_timingsafe_bcmp=no) + export ac_cv_func_timingsafe_bcmp=no && + export ac_cv_header_libintl_h=no && + export ac_cv_lib_intl_textdomain=no + ) set(PYTHON_CONFIGURE_ENV ${CONFIGURE_ENV} && ${PYTHON_FUNC_CONFIGS}) set(PYTHON_BINARY ${BUILD_DIR}/python/src/external_python/python.exe) else() diff --git a/build_files/build_environment/cmake/tiff.cmake b/build_files/build_environment/cmake/tiff.cmake index fa5a1423603..fe2c82a6eaa 100644 --- a/build_files/build_environment/cmake/tiff.cmake +++ b/build_files/build_environment/cmake/tiff.cmake @@ -16,6 +16,12 @@ # # ***** END GPL LICENSE BLOCK ***** +if(WITH_WEBP) + set(WITH_TIFF_WEBP ON) +else() + set(WITH_TIFF_WEBP OFF) +endif() + set(TIFF_EXTRA_ARGS -DZLIB_LIBRARY=${LIBDIR}/zlib/lib/${ZLIB_LIBRARY} -DZLIB_INCLUDE_DIR=${LIBDIR}/zlib/include @@ -23,6 +29,8 @@ set(TIFF_EXTRA_ARGS -DBUILD_SHARED_LIBS=OFF -Dlzma=OFF -Djbig=OFF + -Dzstd=OFF + -Dwebp=${WITH_TIFF_WEBP} ) ExternalProject_Add(external_tiff diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index 5ec5553079c..ce2a1191f17 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -305,6 +305,10 @@ set(MESA_VERSION 18.3.1) set(MESA_URI ftp://ftp.freedesktop.org/pub/mesa//mesa-${MESA_VERSION}.tar.xz) set(MESA_HASH d60828056d77bfdbae0970f9b15fb1be) +set(NASM_VERSION 2.15.02) +set(NASM_URI https://www.nasm.us/pub/nasm/releasebuilds/${NASM_VERSION}/nasm-${NASM_VERSION}.tar.xz) +set(NASM_HASH f4fd1329b1713e1ccd34b2fc121c4bcd278c9f91cc4cb205ae8fcd2e4728dd14) + set(XR_OPENXR_SDK_VERSION 1.0.8) set(XR_OPENXR_SDK_URI https://github.com/KhronosGroup/OpenXR-SDK/archive/release-${XR_OPENXR_SDK_VERSION}.tar.gz) set(XR_OPENXR_SDK_HASH c6de63d2e0f9029aa58dfa97cad8ce07) diff --git a/build_files/build_environment/cmake/x264.cmake b/build_files/build_environment/cmake/x264.cmake index 8bcb5a2938f..96bf031ce83 100644 --- a/build_files/build_environment/cmake/x264.cmake +++ b/build_files/build_environment/cmake/x264.cmake @@ -21,12 +21,21 @@ if(WIN32) endif() +if(APPLE) + set(X264_CONFIGURE_ENV + export AS=${LIBDIR}/nasm/bin/nasm + ) +else() + set(X264_CONFIGURE_ENV echo .) +endif() + ExternalProject_Add(external_x264 URL ${X264_URI} DOWNLOAD_DIR ${DOWNLOAD_DIR} URL_HASH SHA256=${X264_HASH} PREFIX ${BUILD_DIR}/x264 - CONFIGURE_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/x264/src/external_x264/ && ${CONFIGURE_COMMAND} --prefix=${LIBDIR}/x264 + CONFIGURE_COMMAND ${CONFIGURE_ENV} && ${X264_CONFIGURE_ENV} && cd ${BUILD_DIR}/x264/src/external_x264/ && + ${CONFIGURE_COMMAND} --prefix=${LIBDIR}/x264 --enable-static --enable-pic --disable-lavf @@ -39,3 +48,10 @@ ExternalProject_Add(external_x264 if(MSVC) set_target_properties(external_x264 PROPERTIES FOLDER Mingw) endif() + +if(APPLE) + add_dependencies( + external_x264 + external_nasm + ) +endif() diff --git a/build_files/build_environment/patches/ffmpeg.diff b/build_files/build_environment/patches/ffmpeg.diff index 960728ae980..e195ca272de 100644 --- a/build_files/build_environment/patches/ffmpeg.diff +++ b/build_files/build_environment/patches/ffmpeg.diff @@ -9,3 +9,62 @@ enabled libopenmpt && require_pkg_config libopenmpt "libopenmpt >= 0.2.6557" libopenmpt/libopenmpt.h openmpt_module_create -lstdc++ && append libopenmpt_extralibs "-lstdc++" enabled libopus && { enabled libopus_decoder && { +--- a/libavcodec/cfhddata.c ++++ b/libavcodec/cfhddata.c +@@ -276,10 +276,10 @@ + av_cold int ff_cfhd_init_vlcs(CFHDContext *s) + { + int i, j, ret = 0; +- uint32_t new_cfhd_vlc_bits[NB_VLC_TABLE_18 * 2]; +- uint8_t new_cfhd_vlc_len[NB_VLC_TABLE_18 * 2]; +- uint16_t new_cfhd_vlc_run[NB_VLC_TABLE_18 * 2]; +- int16_t new_cfhd_vlc_level[NB_VLC_TABLE_18 * 2]; ++ uint32_t *new_cfhd_vlc_bits = av_calloc(sizeof(uint32_t), NB_VLC_TABLE_18 * 2); ++ uint8_t *new_cfhd_vlc_len = av_calloc(sizeof(uint8_t), NB_VLC_TABLE_18 * 2); ++ uint16_t *new_cfhd_vlc_run = av_calloc(sizeof(uint16_t), NB_VLC_TABLE_18 * 2); ++ int16_t *new_cfhd_vlc_level = av_calloc(sizeof(int16_t), NB_VLC_TABLE_18 * 2); + + /** Similar to dv.c, generate signed VLC tables **/ + +@@ -305,8 +305,13 @@ + + ret = init_vlc(&s->vlc_9, VLC_BITS, j, new_cfhd_vlc_len, + 1, 1, new_cfhd_vlc_bits, 4, 4, 0); +- if (ret < 0) ++ if (ret < 0) { ++ av_free(new_cfhd_vlc_bits); ++ av_free(new_cfhd_vlc_len); ++ av_free(new_cfhd_vlc_run); ++ av_free(new_cfhd_vlc_level); + return ret; ++ } + for (i = 0; i < s->vlc_9.table_size; i++) { + int code = s->vlc_9.table[i][0]; + int len = s->vlc_9.table[i][1]; +@@ -346,8 +351,14 @@ + + ret = init_vlc(&s->vlc_18, VLC_BITS, j, new_cfhd_vlc_len, + 1, 1, new_cfhd_vlc_bits, 4, 4, 0); +- if (ret < 0) ++ if (ret < 0) { ++ av_free(new_cfhd_vlc_bits); ++ av_free(new_cfhd_vlc_len); ++ av_free(new_cfhd_vlc_run); ++ av_free(new_cfhd_vlc_level); + return ret; ++ } ++ + av_assert0(s->vlc_18.table_size == 4572); + + for (i = 0; i < s->vlc_18.table_size; i++) { +@@ -367,5 +378,10 @@ + s->table_18_rl_vlc[i].run = run; + } + ++ av_free(new_cfhd_vlc_bits); ++ av_free(new_cfhd_vlc_len); ++ av_free(new_cfhd_vlc_run); ++ av_free(new_cfhd_vlc_level); ++ + return ret; + } diff --git a/build_files/build_environment/patches/ispc.diff b/build_files/build_environment/patches/ispc.diff index 710bfc7a448..689dd0abdc5 100644 --- a/build_files/build_environment/patches/ispc.diff +++ b/build_files/build_environment/patches/ispc.diff @@ -34,3 +34,52 @@ diff -Naur orig/cmake/GenerateBuiltins.cmake.txt external_ispc/cmake/GenerateBui elseif ("${bit}" STREQUAL "64" AND ${arch} STREQUAL "x86") set(target_arch "x86_64") elseif ("${bit}" STREQUAL "32" AND ${arch} STREQUAL "arm") +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 46a8db8..f53beef 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -36,8 +36,12 @@ + cmake_minimum_required(VERSION 3.13) + + if (UNIX) +- set(CMAKE_C_COMPILER "clang") +- set(CMAKE_CXX_COMPILER "clang++") ++ if (NOT CMAKE_C_COMPILER) ++ set(CMAKE_C_COMPILER "clang") ++ endif() ++ if (NOT CMAKE_CXX_COMPILER) ++ set(CMAKE_CXX_COMPILER "clang++") ++ endif() + endif() + + set(PROJECT_NAME ispc) +@@ -412,6 +416,29 @@ else() + endif() + endif() + ++# Link against libstdc++.a which must be provided to the linker after ++# LLVM and CLang libraries. ++# This is needed because some of LLVM/CLang dependencies are using ++# std::make_shared, which is defined in one of those: ++# - libclang-cpp.so ++# - libstdc++.a ++# Using the former one is tricky because then generated binary depends ++# on a library which is outside of the LD_LIBRARY_PATH. ++# ++# Hence, using C++ implementation from G++ which seems to work just fine. ++# In fact, from investigation seems that libclang-cpp.so itself is pulling ++# std::_Sp_make_shared_tag from G++'s libstdc++.a. ++if(UNIX AND NOT APPLE) ++ execute_process( ++ COMMAND g++ --print-file-name libstdc++.a ++ OUTPUT_VARIABLE GCC_LIBSTDCXX_A ++ OUTPUT_STRIP_TRAILING_WHITESPACE ++ ) ++ if(GCC_LIBSTDCXX_A AND EXISTS ${GCC_LIBSTDCXX_A}) ++ target_link_libraries(${PROJECT_NAME} ${GCC_LIBSTDCXX_A}) ++ endif() ++endif() ++ + # Build target for utility checking host ISA + if (ISPC_INCLUDE_UTILS) + add_executable(check_isa "") diff --git a/build_files/build_environment/patches/nasm.diff b/build_files/build_environment/patches/nasm.diff new file mode 100644 index 00000000000..821e1a1d905 --- /dev/null +++ b/build_files/build_environment/patches/nasm.diff @@ -0,0 +1,129 @@ +diff --git a/output/macho.h b/output/macho.h +index 538c531e..fd5e8849 100644 +--- a/output/macho.h ++++ b/output/macho.h +@@ -60,6 +60,8 @@ + #define LC_SEGMENT 0x1 + #define LC_SEGMENT_64 0x19 + #define LC_SYMTAB 0x2 ++#define LC_VERSION_MIN_MACOSX 0x24 ++#define LC_BUILD_VERSION 0x32 + + /* Symbol type bits */ + #define N_STAB 0xe0 +diff --git a/output/outmacho.c b/output/outmacho.c +index 08147883..de6ec902 100644 +--- a/output/outmacho.c ++++ b/output/outmacho.c +@@ -38,6 +38,8 @@ + + #include "compiler.h" + ++#include <stdlib.h> ++ + #include "nctype.h" + + #include "nasm.h" +@@ -64,6 +66,8 @@ + #define MACHO_SYMCMD_SIZE 24 + #define MACHO_NLIST_SIZE 12 + #define MACHO_RELINFO_SIZE 8 ++#define MACHO_BUILD_VERSION_SIZE 24 ++#define MACHO_VERSION_MIN_MACOSX_SIZE 16 + + #define MACHO_HEADER64_SIZE 32 + #define MACHO_SEGCMD64_SIZE 72 +@@ -1224,6 +1228,46 @@ static void macho_layout_symbols (uint32_t *numsyms, + } + } + ++static bool get_full_version_from_env (const char *variable_name, ++ int *r_major, ++ int *r_minor, ++ int *r_patch) { ++ *r_major = 0; ++ *r_minor = 0; ++ *r_patch = 0; ++ ++ const char *value = getenv(variable_name); ++ if (value == NULL || value[0] == '\0') { ++ return false; ++ } ++ ++ const char *current_value = value; ++ const char *end_value = value + strlen(value); ++ ++ char *endptr; ++ ++ *r_major = strtol(current_value, &endptr, 10); ++ if (endptr >= end_value) { ++ return true; ++ } ++ current_value = endptr + 1; ++ ++ *r_minor = strtol(current_value, &endptr, 10); ++ if (endptr >= end_value) { ++ return true; ++ } ++ current_value = endptr + 1; ++ ++ *r_patch = strtol(current_value, &endptr, 10); ++ ++ return true; ++} ++ ++static bool need_version_min_macosx_command (void) { ++ return getenv("MACOSX_DEPLOYMENT_TARGET") && ++ getenv("MACOSX_SDK_VERSION"); ++} ++ + /* Calculate some values we'll need for writing later. */ + + static void macho_calculate_sizes (void) +@@ -1270,6 +1314,12 @@ static void macho_calculate_sizes (void) + head_sizeofcmds += fmt.segcmd_size + seg_nsects * fmt.sectcmd_size; + } + ++ /* LC_VERSION_MIN_MACOSX */ ++ if (need_version_min_macosx_command()) { ++ ++head_ncmds; ++ head_sizeofcmds += MACHO_VERSION_MIN_MACOSX_SIZE; ++ } ++ + if (nsyms > 0) { + ++head_ncmds; + head_sizeofcmds += MACHO_SYMCMD_SIZE; +@@ -1653,6 +1703,33 @@ static void macho_write (void) + else + nasm_warn(WARN_OTHER, "no sections?"); + ++#define ENCODE_BUILD_VERSION(major, minor, patch) \ ++ (((major) << 16) | ((minor) << 8) | (patch)) ++ ++ if (0) { ++ fwriteint32_t(LC_BUILD_VERSION, ofile); /* cmd == LC_BUILD_VERSION */ ++ fwriteint32_t(MACHO_BUILD_VERSION_SIZE, ofile); /* size of load command */ ++ fwriteint32_t(1, ofile); /* platform */ ++ fwriteint32_t(ENCODE_BUILD_VERSION(10, 13, 0), ofile); /* minos, X.Y.Z is encoded in nibbles xxxx.yy.zz */ ++ fwriteint32_t(ENCODE_BUILD_VERSION(10, 15, 4), ofile); /* sdk, X.Y.Z is encoded in nibbles xxxx.yy.zz */ ++ fwriteint32_t(0, ofile); /* number of tool entries following this */ ++ } ++ ++ if (need_version_min_macosx_command()) { ++ int sdk_major, sdk_minor, sdk_patch; ++ get_full_version_from_env("MACOSX_SDK_VERSION", &sdk_major, &sdk_minor, &sdk_patch); ++ ++ int version_major, version_minor, version_patch; ++ get_full_version_from_env("MACOSX_DEPLOYMENT_TARGET", &version_major, &version_minor, &version_patch); ++ ++ fwriteint32_t(LC_VERSION_MIN_MACOSX, ofile); /* cmd == LC_VERSION_MIN_MACOSX */ ++ fwriteint32_t(MACHO_VERSION_MIN_MACOSX_SIZE, ofile); /* size of load command */ ++ fwriteint32_t(ENCODE_BUILD_VERSION(version_major, version_minor, version_patch), ofile); /* minos, X.Y.Z is encoded in nibbles xxxx.yy.zz */ ++ fwriteint32_t(ENCODE_BUILD_VERSION(sdk_major, sdk_minor, sdk_patch), ofile); /* sdk, X.Y.Z is encoded in nibbles xxxx.yy.zz */ ++ } ++ ++#undef ENCODE_BUILD_VERSION ++ + if (nsyms > 0) { + /* write out symbol command */ + fwriteint32_t(LC_SYMTAB, ofile); /* cmd == LC_SYMTAB */ diff --git a/build_files/build_environment/patches/ogg.diff b/build_files/build_environment/patches/ogg.diff new file mode 100644 index 00000000000..fca426e1d35 --- /dev/null +++ b/build_files/build_environment/patches/ogg.diff @@ -0,0 +1,12 @@ +diff --git a/include/ogg/os_types.h b/include/ogg/os_types.h +index eb8a322..6f73b72 100644 +--- a/include/ogg/os_types.h ++++ b/include/ogg/os_types.h +@@ -71,6 +71,7 @@ + #elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */ + + # include <sys/types.h> ++# include <stdint.h> + typedef int16_t ogg_int16_t; + typedef uint16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; diff --git a/build_files/cmake/Modules/FindEmbree.cmake b/build_files/cmake/Modules/FindEmbree.cmake index d9d525d4586..fa613f62308 100644 --- a/build_files/cmake/Modules/FindEmbree.cmake +++ b/build_files/cmake/Modules/FindEmbree.cmake @@ -82,7 +82,7 @@ FIND_LIBRARY(EMBREE_LIBRARY # handle the QUIETLY and REQUIRED arguments and set EMBREE_FOUND to TRUE if # all listed variables are TRUE INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(EMBREE DEFAULT_MSG +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Embree DEFAULT_MSG _embree_LIBRARIES EMBREE_INCLUDE_DIR) IF(EMBREE_FOUND) diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake index d8ee82d4c10..b24d7734423 100644 --- a/build_files/cmake/platform/platform_apple.cmake +++ b/build_files/cmake/platform/platform_apple.cmake @@ -20,7 +20,7 @@ # Libraries configuration for Apple. -set(MACOSX_DEPLOYMENT_TARGET "10.11") +set(MACOSX_DEPLOYMENT_TARGET "10.13") macro(find_package_wrapper) # do nothing, just satisfy the macro diff --git a/build_files/cmake/platform/platform_apple_xcode.cmake b/build_files/cmake/platform/platform_apple_xcode.cmake index f1f02c151ee..434c2ee31b9 100644 --- a/build_files/cmake/platform/platform_apple_xcode.cmake +++ b/build_files/cmake/platform/platform_apple_xcode.cmake @@ -65,13 +65,9 @@ endif() message(STATUS "Detected OS X ${OSX_SYSTEM} and Xcode ${XCODE_VERSION} at ${XCODE_BUNDLE}") -# Older Xcode versions had different approach to the directory hiearchy. -# Require newer Xcode which is also have better chances of being able to compile with the -# required deployment target. -# -# NOTE: Xcode version 8.2 is the latest one which runs on macOS 10.11. -if(${XCODE_VERSION} VERSION_LESS 8.2) - message(FATAL_ERROR "Only Xcode version 8.2 and newer is supported") +# Require a relatively recent Xcode version. +if(${XCODE_VERSION} VERSION_LESS 10.0) + message(FATAL_ERROR "Only Xcode version 10.0 and newer is supported") endif() # note: xcode-select path could be ambiguous, @@ -133,14 +129,14 @@ if(${CMAKE_GENERATOR} MATCHES "Xcode") endif() unset(OSX_SDKROOT) -# 10.11 is our min. target, if you use higher sdk, weak linking happens +# 10.13 is our min. target, if you use higher sdk, weak linking happens if(CMAKE_OSX_DEPLOYMENT_TARGET) - if(${CMAKE_OSX_DEPLOYMENT_TARGET} VERSION_LESS 10.11) - message(STATUS "Setting deployment target to 10.11, lower versions are not supported") - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.11" CACHE STRING "" FORCE) + if(${CMAKE_OSX_DEPLOYMENT_TARGET} VERSION_LESS 10.13) + message(STATUS "Setting deployment target to 10.13, lower versions are not supported") + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "" FORCE) endif() else() - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.11" CACHE STRING "" FORCE) + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "" FORCE) endif() if(NOT ${CMAKE_GENERATOR} MATCHES "Xcode") diff --git a/build_files/utils/make_test.py b/build_files/utils/make_test.py index 309ab36ecdd..15bd6dde352 100755 --- a/build_files/utils/make_test.py +++ b/build_files/utils/make_test.py @@ -40,7 +40,8 @@ if make_utils.command_missing(git_command): # Test if we are building a specific release version. branch = make_utils.git_branch(git_command) -release_version = make_utils.git_branch_release_version(branch) +tag = make_utils.git_tag(git_command) +release_version = make_utils.git_branch_release_version(branch, tag) lib_tests_dirpath = os.path.join('..', 'lib', "tests") if not os.path.exists(lib_tests_dirpath): diff --git a/build_files/utils/make_update.py b/build_files/utils/make_update.py index b6157107c23..324bb6944bf 100755 --- a/build_files/utils/make_update.py +++ b/build_files/utils/make_update.py @@ -197,7 +197,8 @@ if __name__ == "__main__": # Test if we are building a specific release version. branch = make_utils.git_branch(args.git_command) - release_version = make_utils.git_branch_release_version(branch) + tag = make_utils.git_tag(args.git_command) + release_version = make_utils.git_branch_release_version(branch, tag) if not args.no_libraries: svn_update(args, release_version) diff --git a/build_files/utils/make_utils.py b/build_files/utils/make_utils.py index 7b21bc607a4..9a3d25f4952 100755 --- a/build_files/utils/make_utils.py +++ b/build_files/utils/make_utils.py @@ -36,7 +36,7 @@ def check_output(cmd, exit_on_error=True): return output.strip() def git_branch(git_command): - # Test if we are building a specific release version. + # Get current branch name. try: branch = subprocess.check_output([git_command, "rev-parse", "--abbrev-ref", "HEAD"]) except subprocess.CalledProcessError as e: @@ -45,10 +45,23 @@ def git_branch(git_command): return branch.strip().decode('utf8') -def git_branch_release_version(branch): +def git_tag(git_command): + # Get current tag name. + try: + tag = subprocess.check_output([git_command, "describe", "--exact-match"]) + except subprocess.CalledProcessError as e: + return None + + return tag.strip().decode('utf8') + +def git_branch_release_version(branch, tag): release_version = re.search("^blender-v(.*)-release$", branch) if release_version: release_version = release_version.group(1) + elif tag: + release_version = re.search("^v([0-9]*\.[0-9]*).*", tag) + if release_version: + release_version = release_version.group(1) return release_version def svn_libraries_base_url(release_version): diff --git a/doc/python_api/rst/info_overview.rst b/doc/python_api/rst/info_overview.rst index 9676489950e..50928963f60 100644 --- a/doc/python_api/rst/info_overview.rst +++ b/doc/python_api/rst/info_overview.rst @@ -248,7 +248,7 @@ using the ``bl_idname`` rather than the classes original name. .. note:: There are some exceptions to this for class names which aren't guarantee to be unique. - In this case use: :func:`bpy.types.Struct.bl_rna_get_subclass`. + In this case use: :func:`bpy.types.Struct.bl_rna_get_subclass_py`. When loading a class, Blender performs sanity checks making sure all required properties and functions are found, diff --git a/extern/mantaflow/helper/util/vectorbase.h b/extern/mantaflow/helper/util/vectorbase.h index 41584663a0f..9ccf445f42c 100644 --- a/extern/mantaflow/helper/util/vectorbase.h +++ b/extern/mantaflow/helper/util/vectorbase.h @@ -248,12 +248,14 @@ template<class S> class Vector3D { protected: }; -//! helper to check whether float/double value is non-zero -inline bool notZero(Real f) +//! helper to check whether value is non-zero +template<class S> inline bool notZero(S v) { - if (std::abs(f) > VECTOR_EPSILON) - return true; - return false; + return (std::abs(v) > VECTOR_EPSILON); +} +template<class S> inline bool notZero(Vector3D<S> v) +{ + return (std::abs(norm(v)) > VECTOR_EPSILON); } //************************************************************************ diff --git a/extern/mantaflow/preprocessed/fileio/iomeshes.cpp b/extern/mantaflow/preprocessed/fileio/iomeshes.cpp index 1c50376de77..b5e51625077 100644 --- a/extern/mantaflow/preprocessed/fileio/iomeshes.cpp +++ b/extern/mantaflow/preprocessed/fileio/iomeshes.cpp @@ -315,10 +315,14 @@ int readObjFile(const std::string &name, Mesh *mesh, bool append) return 0; } + const Real dx = mesh->getParent()->getDx(); + const Vec3 gs = toVec3(mesh->getParent()->getGridSize()); + if (!append) mesh->clear(); int nodebase = mesh->numNodes(); - int cnt = nodebase; + int cntNodes = nodebase, cntNormals = nodebase; + while (ifs.good() && !ifs.eof()) { string id; ifs >> id; @@ -333,19 +337,23 @@ int readObjFile(const std::string &name, Mesh *mesh, bool append) } else if (id == "vn") { // normals - if (!mesh->numNodes()) { + if (mesh->numNodes() != cntNodes) { errMsg("invalid amount of nodes"); return 0; } - Node n = mesh->nodes(cnt); - ifs >> n.normal.x >> n.normal.y >> n.normal.z; - cnt++; + Node *n = &mesh->nodes(cntNormals); + ifs >> n->normal.x >> n->normal.y >> n->normal.z; + cntNormals++; } else if (id == "v") { // vertex Node n; ifs >> n.pos.x >> n.pos.y >> n.pos.z; + // convert to grid space + n.pos /= dx; + n.pos += gs * 0.5; mesh->addNode(n); + cntNodes++; } else if (id == "g") { // group @@ -408,7 +416,6 @@ int writeObjFile(const string &name, Mesh *mesh) // write normals for (int i = 0; i < numVerts; i++) { Vector3D<float> n = toVec3f(mesh->nodes(i).normal); - // normalize to unit cube around 0 ofs << "vn " << n.value[0] << " " << n.value[1] << " " << n.value[2] << " " << "\n"; } diff --git a/extern/mantaflow/preprocessed/gitinfo.h b/extern/mantaflow/preprocessed/gitinfo.h index 73ff70b10a0..03dcbb3d9c5 100644 --- a/extern/mantaflow/preprocessed/gitinfo.h +++ b/extern/mantaflow/preprocessed/gitinfo.h @@ -1,3 +1,3 @@ -#define MANTA_GIT_VERSION "commit d80d3c821de74315ab26b5efd153d41477b976c4" +#define MANTA_GIT_VERSION "commit 7395d36e3f504edbdabe34b30edc855b422c7baa" diff --git a/intern/CMakeLists.txt b/intern/CMakeLists.txt index fa18f4d793a..0758567bb78 100644 --- a/intern/CMakeLists.txt +++ b/intern/CMakeLists.txt @@ -30,6 +30,7 @@ add_subdirectory(opensubdiv) add_subdirectory(mikktspace) add_subdirectory(glew-mx) add_subdirectory(eigen) +add_subdirectory(sky) if(WITH_AUDASPACE) add_subdirectory(audaspace) diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt index ef374f91a65..a2b0ed03925 100644 --- a/intern/cycles/app/CMakeLists.txt +++ b/intern/cycles/app/CMakeLists.txt @@ -35,7 +35,7 @@ if(WITH_CYCLES_OSL) endif() if(NOT CYCLES_STANDALONE_REPOSITORY) - list(APPEND LIBRARIES bf_intern_glew_mx bf_intern_guardedalloc bf_intern_numaapi) + list(APPEND LIBRARIES bf_intern_glew_mx bf_intern_guardedalloc bf_intern_numaapi bf_intern_sky) endif() if(WITH_CYCLES_LOGGING) diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py index 7566ca28dd7..67e448db859 100644 --- a/intern/cycles/blender/addon/engine.py +++ b/intern/cycles/blender/addon/engine.py @@ -224,7 +224,7 @@ def system_info(): import _cycles return _cycles.system_info() -def list_render_passes(srl): +def list_render_passes(scene, srl): # Builtin Blender passes. yield ("Combined", "RGBA", 'COLOR') @@ -279,14 +279,17 @@ def list_render_passes(srl): yield ("Denoising Normal", "XYZ", 'VECTOR') yield ("Denoising Albedo", "RGB", 'COLOR') yield ("Denoising Depth", "Z", 'VALUE') - yield ("Denoising Shadowing", "X", 'VALUE') - yield ("Denoising Variance", "RGB", 'COLOR') - yield ("Denoising Intensity", "X", 'VALUE') - clean_options = ("denoising_diffuse_direct", "denoising_diffuse_indirect", - "denoising_glossy_direct", "denoising_glossy_indirect", - "denoising_transmission_direct", "denoising_transmission_indirect") - if any(getattr(crl, option) for option in clean_options): - yield ("Denoising Clean", "RGB", 'COLOR') + + if scene.cycles.denoiser == 'NLM': + yield ("Denoising Shadowing", "X", 'VALUE') + yield ("Denoising Variance", "RGB", 'COLOR') + yield ("Denoising Intensity", "X", 'VALUE') + + clean_options = ("denoising_diffuse_direct", "denoising_diffuse_indirect", + "denoising_glossy_direct", "denoising_glossy_indirect", + "denoising_transmission_direct", "denoising_transmission_indirect") + if any(getattr(crl, option) for option in clean_options): + yield ("Denoising Clean", "RGB", 'COLOR') # Custom AOV passes. for aov in crl.aovs: @@ -298,15 +301,15 @@ def list_render_passes(srl): def register_passes(engine, scene, view_layer): # Detect duplicate render pass names, first one wins. listed = set() - for name, channelids, channeltype in list_render_passes(view_layer): + for name, channelids, channeltype in list_render_passes(scene, view_layer): if name not in listed: engine.register_pass(scene, view_layer, name, len(channelids), channelids, channeltype) listed.add(name) -def detect_conflicting_passes(view_layer): +def detect_conflicting_passes(scene, view_layer): # Detect conflicting render pass names for UI. counter = {} - for name, _, _ in list_render_passes(view_layer): + for name, _, _ in list_render_passes(scene, view_layer): counter[name] = counter.get(name, 0) + 1 for aov in view_layer.cycles.aovs: diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index da706451f88..45d25720aff 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -182,6 +182,7 @@ enum_aov_types = ( ('COLOR', "Color", "Write a Color pass", 1), ) + def enum_openimagedenoise_denoiser(self, context): if _cycles.with_openimagedenoise: return [('OPENIMAGEDENOISE', "OpenImageDenoise", "Use Intel OpenImageDenoise AI denoiser running on the CPU", 4)] @@ -208,14 +209,23 @@ def enum_preview_denoiser(self, context): def enum_denoiser(self, context): items = [('NLM', "NLM", "Cycles native non-local means denoiser, running on any compute device", 1)] items += enum_optix_denoiser(self, context) + items += enum_openimagedenoise_denoiser(self, context) return items -enum_denoising_optix_input_passes = ( +enum_denoising_input_passes = ( ('RGB', "Color", "Use only color as input", 1), ('RGB_ALBEDO', "Color + Albedo", "Use color and albedo data as input", 2), ('RGB_ALBEDO_NORMAL', "Color + Albedo + Normal", "Use color, albedo and normal data as input", 3), ) + +def update_render_passes(self, context): + scene = context.scene + view_layer = context.view_layer + view_layer.update_render_passes() + engine.detect_conflicting_passes(scene, view_layer) + + class CyclesRenderSettings(bpy.types.PropertyGroup): device: EnumProperty( @@ -261,9 +271,12 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): denoiser: EnumProperty( name="Denoiser", - description="Denoise the image with the selected denoiser", + description="Denoise the image with the selected denoiser. " + "For denoising the image after rendering, denoising data render passes " + "also adapt to the selected denoiser", items=enum_denoiser, default=1, + update=update_render_passes, ) preview_denoiser: EnumProperty( name="Viewport Denoiser", @@ -818,6 +831,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): debug_use_cuda_split_kernel: BoolProperty(name="Split Kernel", default=False) debug_optix_cuda_streams: IntProperty(name="CUDA Streams", default=1, min=1) + debug_optix_curves_api: BoolProperty(name="Native OptiX Curve Primitive", default=False) debug_opencl_kernel_type: EnumProperty( name="OpenCL Kernel Type", @@ -1291,12 +1305,6 @@ class CyclesCurveRenderSettings(bpy.types.PropertyGroup): del bpy.types.Scene.cycles_curves -def update_render_passes(self, context): - view_layer = context.view_layer - view_layer.update_render_passes() - engine.detect_conflicting_passes(view_layer) - - class CyclesAOVPass(bpy.types.PropertyGroup): name: StringProperty( name="Name", @@ -1430,7 +1438,7 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup): ) denoising_store_passes: BoolProperty( name="Store Denoising Passes", - description="Store the denoising feature passes and the noisy image", + description="Store the denoising feature passes and the noisy image. The passes adapt to the denoiser selected for rendering", default=False, update=update_render_passes, ) @@ -1443,11 +1451,18 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup): denoising_optix_input_passes: EnumProperty( name="Input Passes", - description="Passes handed over to the OptiX denoiser (this can have different effects on the denoised image)", - items=enum_denoising_optix_input_passes, + description="Passes used by the denoiser to distinguish noise from shader and geometry detail", + items=enum_denoising_input_passes, default='RGB_ALBEDO', ) + denoising_openimagedenoise_input_passes: EnumProperty( + name="Input Passes", + description="Passes used by the denoiser to distinguish noise from shader and geometry detail", + items=enum_denoising_input_passes, + default='RGB_ALBEDO_NORMAL', + ) + use_pass_crypto_object: BoolProperty( name="Cryptomatte Object", description="Render cryptomatte object pass, for isolating objects in compositing", diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 129f16b0357..03b1675c309 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -265,7 +265,12 @@ class CYCLES_RENDER_PT_sampling_denoising(CyclesButtonsPanel, Panel): row = heading.row(align=True) row.prop(cscene, "use_denoising", text="") sub = row.row() + sub.active = cscene.use_denoising + for view_layer in scene.view_layers: + if view_layer.cycles.denoising_store_passes: + sub.active = True + sub.prop(cscene, "denoiser", text="") heading = layout.column(align=False, heading="Viewport") @@ -777,10 +782,6 @@ class CYCLES_RENDER_PT_filter(CyclesButtonsPanel, Panel): col.prop(view_layer, "use_solid", text="Surfaces") col.prop(view_layer, "use_strand", text="Hair") col.prop(view_layer, "use_volumes", text="Volumes") - if with_freestyle: - sub = col.row(align=True) - sub.prop(view_layer, "use_freestyle", text="Freestyle") - sub.active = rd.use_freestyle class CYCLES_RENDER_PT_override(CyclesButtonsPanel, Panel): @@ -1007,6 +1008,7 @@ class CYCLES_RENDER_PT_denoising(CyclesButtonsPanel, Panel): col.prop(cycles_view_layer, "denoising_optix_input_passes") return elif denoiser == 'OPENIMAGEDENOISE': + col.prop(cycles_view_layer, "denoising_openimagedenoise_input_passes") return col.prop(cycles_view_layer, "denoising_radius", text="Radius") @@ -2026,6 +2028,7 @@ class CYCLES_RENDER_PT_debug(CyclesButtonsPanel, Panel): col = layout.column() col.label(text="OptiX Flags:") col.prop(cscene, "debug_optix_cuda_streams") + col.prop(cscene, "debug_optix_curves_api") col.separator() diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp index 011678a7a65..592a69585de 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/blender_camera.cpp @@ -709,6 +709,10 @@ static void blender_camera_from_view(BlenderCamera *bcam, /* 3d view transform */ bcam->matrix = transform_inverse(get_transform(b_rv3d.view_matrix())); + + /* dimensions */ + bcam->full_width = width; + bcam->full_height = height; } static void blender_camera_view_subset(BL::RenderEngine &b_engine, diff --git a/intern/cycles/blender/blender_id_map.h b/intern/cycles/blender/blender_id_map.h index 3bc42e349ae..b5f6aaa67a8 100644 --- a/intern/cycles/blender/blender_id_map.h +++ b/intern/cycles/blender/blender_id_map.h @@ -200,7 +200,7 @@ template<typename K, typename T> class id_map { * To uniquely identify instances, we use the parent, object and persistent instance ID. * We also export separate object for a mesh and its particle hair. */ -enum { OBJECT_PERSISTENT_ID_SIZE = 16 }; +enum { OBJECT_PERSISTENT_ID_SIZE = 8 /* MAX_DUPLI_RECUR in Blender. */ }; struct ObjectKey { void *parent; diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index d3a37563ef4..3ea6892a349 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -59,7 +59,7 @@ bool BlenderSync::BKE_object_is_modified(BL::Object &b_ob) return false; } -bool BlenderSync::object_is_mesh(BL::Object &b_ob) +bool BlenderSync::object_is_geometry(BL::Object &b_ob) { BL::ID b_ob_data = b_ob.data(); @@ -143,7 +143,7 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph, } /* only interested in object that we can create meshes from */ - if (!object_is_mesh(b_ob)) { + if (!object_is_geometry(b_ob)) { return NULL; } diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp index 3e595c3ee52..25c77b74ce3 100644 --- a/intern/cycles/blender/blender_python.cpp +++ b/intern/cycles/blender/blender_python.cpp @@ -92,6 +92,7 @@ bool debug_flags_sync_from_scene(BL::Scene b_scene) flags.cuda.split_kernel = get_boolean(cscene, "debug_use_cuda_split_kernel"); /* Synchronize OptiX flags. */ flags.optix.cuda_streams = get_int(cscene, "debug_optix_cuda_streams"); + flags.optix.curves_api = get_boolean(cscene, "debug_optix_curves_api"); /* Synchronize OpenCL device type. */ switch (get_enum(cscene, "debug_opencl_device_type")) { case 0: diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 19d2730dc93..33e73b5a4b9 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -815,9 +815,10 @@ static ShaderNode *add_node(Scene *scene, sky->ground_albedo = b_sky_node.ground_albedo(); sky->sun_disc = b_sky_node.sun_disc(); sky->sun_size = b_sky_node.sun_size(); + sky->sun_intensity = b_sky_node.sun_intensity(); sky->sun_elevation = b_sky_node.sun_elevation(); sky->sun_rotation = b_sky_node.sun_rotation(); - sky->altitude = b_sky_node.altitude(); + sky->altitude = 1000.0f * b_sky_node.altitude(); sky->air_density = b_sky_node.air_density(); sky->dust_density = b_sky_node.dust_density(); sky->ozone_density = b_sky_node.ozone_density(); diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index bf065cc5492..d509f2fc786 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -147,30 +147,43 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d /* Object */ else if (b_id.is_a(&RNA_Object)) { BL::Object b_ob(b_id); - const bool updated_geometry = b_update->is_updated_geometry(); + const bool is_geometry = object_is_geometry(b_ob); + const bool is_light = !is_geometry && object_is_light(b_ob); - if (b_update->is_updated_transform() || b_update->is_updated_shading()) { - object_map.set_recalc(b_ob); - light_map.set_recalc(b_ob); - } + if (is_geometry || is_light) { + const bool updated_geometry = b_update->is_updated_geometry(); - if (object_is_mesh(b_ob)) { - if (updated_geometry || - (object_subdivision_type(b_ob, preview, experimental) != Mesh::SUBDIVISION_NONE)) { - BL::ID key = BKE_object_is_modified(b_ob) ? b_ob : b_ob.data(); - geometry_map.set_recalc(key); - } - } - else if (object_is_light(b_ob)) { - if (updated_geometry) { - light_map.set_recalc(b_ob); + /* Geometry (mesh, hair, volume). */ + if (is_geometry) { + if (b_update->is_updated_transform() || b_update->is_updated_shading()) { + object_map.set_recalc(b_ob); + } + + if (updated_geometry || + (object_subdivision_type(b_ob, preview, experimental) != Mesh::SUBDIVISION_NONE)) { + BL::ID key = BKE_object_is_modified(b_ob) ? b_ob : b_ob.data(); + geometry_map.set_recalc(key); + } + + if (updated_geometry) { + BL::Object::particle_systems_iterator b_psys; + for (b_ob.particle_systems.begin(b_psys); b_psys != b_ob.particle_systems.end(); + ++b_psys) { + particle_system_map.set_recalc(b_ob); + } + } } - } + /* Light */ + else if (is_light) { + if (b_update->is_updated_transform() || b_update->is_updated_shading()) { + object_map.set_recalc(b_ob); + light_map.set_recalc(b_ob); + } - if (updated_geometry) { - BL::Object::particle_systems_iterator b_psys; - for (b_ob.particle_systems.begin(b_psys); b_psys != b_ob.particle_systems.end(); ++b_psys) - particle_system_map.set_recalc(b_ob); + if (updated_geometry) { + light_map.set_recalc(b_ob); + } + } } } /* Mesh */ @@ -941,7 +954,13 @@ DenoiseParams BlenderSync::get_denoise_params(BL::Scene &b_scene, denoising.strength = get_float(clayer, "denoising_strength"); denoising.feature_strength = get_float(clayer, "denoising_feature_strength"); denoising.relative_pca = get_boolean(clayer, "denoising_relative_pca"); - denoising.optix_input_passes = get_enum(clayer, "denoising_optix_input_passes"); + + denoising.input_passes = (DenoiserInput)get_enum( + clayer, + (denoising.type == DENOISER_OPTIX) ? "denoising_optix_input_passes" : + "denoising_openimagedenoise_input_passes", + DENOISER_INPUT_NUM, + DENOISER_INPUT_RGB_ALBEDO_NORMAL); denoising.store_passes = get_boolean(clayer, "denoising_store_passes"); } diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index 0214d9eb3b8..a551ec31e04 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -208,7 +208,7 @@ class BlenderSync { /* util */ void find_shader(BL::ID &id, vector<Shader *> &used_shaders, Shader *default_shader); bool BKE_object_is_modified(BL::Object &b_ob); - bool object_is_mesh(BL::Object &b_ob); + bool object_is_geometry(BL::Object &b_ob); bool object_is_light(BL::Object &b_ob); /* variables */ diff --git a/intern/cycles/cmake/external_libs.cmake b/intern/cycles/cmake/external_libs.cmake index 0b082b11cf7..b09f442bd16 100644 --- a/intern/cycles/cmake/external_libs.cmake +++ b/intern/cycles/cmake/external_libs.cmake @@ -133,9 +133,9 @@ if(CYCLES_STANDALONE_REPOSITORY) set(BOOST_DEFINITIONS "-DBOOST_ALL_NO_LIB") #### - # embree + # Embree if(WITH_CYCLES_EMBREE) - find_package(embree 3.8.0 REQUIRED) + find_package(Embree 3.8.0 REQUIRED) endif() #### diff --git a/intern/cycles/device/cuda/device_cuda_impl.cpp b/intern/cycles/device/cuda/device_cuda_impl.cpp index b9bbeb9a25b..0be2c322dfa 100644 --- a/intern/cycles/device/cuda/device_cuda_impl.cpp +++ b/intern/cycles/device/cuda/device_cuda_impl.cpp @@ -1760,7 +1760,7 @@ void CUDADevice::denoise(RenderTile &rtile, DenoisingTask &denoising) denoising.render_buffer.samples = rtile.sample; denoising.buffer.gpu_temporary_mem = true; - denoising.run_denoising(&rtile); + denoising.run_denoising(rtile); } void CUDADevice::adaptive_sampling_filter(uint filter_sample, diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp index 9dbb33980b4..407f73e8451 100644 --- a/intern/cycles/device/device.cpp +++ b/intern/cycles/device/device.cpp @@ -209,13 +209,13 @@ bool Device::bind_fallback_display_space_shader(const float width, const float h glUseProgram(fallback_shader_program); image_texture_location = glGetUniformLocation(fallback_shader_program, "image_texture"); if (image_texture_location < 0) { - LOG(ERROR) << "Shader doesn't containt the 'image_texture' uniform."; + LOG(ERROR) << "Shader doesn't contain the 'image_texture' uniform."; return false; } fullscreen_location = glGetUniformLocation(fallback_shader_program, "fullscreen"); if (fullscreen_location < 0) { - LOG(ERROR) << "Shader doesn't containt the 'fullscreen' uniform."; + LOG(ERROR) << "Shader doesn't contain the 'fullscreen' uniform."; return false; } diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h index a5833369a17..115b05e3911 100644 --- a/intern/cycles/device/device.h +++ b/intern/cycles/device/device.h @@ -439,10 +439,10 @@ class Device { { return 0; } - virtual void map_neighbor_tiles(Device * /*sub_device*/, RenderTile * /*tiles*/) + virtual void map_neighbor_tiles(Device * /*sub_device*/, RenderTileNeighbors & /*neighbors*/) { } - virtual void unmap_neighbor_tiles(Device * /*sub_device*/, RenderTile * /*tiles*/) + virtual void unmap_neighbor_tiles(Device * /*sub_device*/, RenderTileNeighbors & /*neighbors*/) { } diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp index 8f68e66a1b4..ee3a3ddea64 100644 --- a/intern/cycles/device/device_cpu.cpp +++ b/intern/cycles/device/device_cpu.cpp @@ -182,6 +182,7 @@ class CPUDevice : public Device { oidn::DeviceRef oidn_device; oidn::FilterRef oidn_filter; #endif + thread_spin_lock oidn_task_lock; bool use_split_kernel; @@ -948,12 +949,25 @@ class CPUDevice : public Device { } } - void denoise_openimagedenoise(DeviceTask &task, RenderTile &rtile) + void denoise_openimagedenoise_buffer(DeviceTask &task, + float *buffer, + const size_t offset, + const size_t stride, + const size_t x, + const size_t y, + const size_t w, + const size_t h, + const float scale) { #ifdef WITH_OPENIMAGEDENOISE assert(openimagedenoise_supported()); - /* Only one at a time, since OpenImageDenoise itself is multithreaded. */ + /* Only one at a time, since OpenImageDenoise itself is multithreaded for full + * buffers, and for tiled rendering because creating multiple devices and filters + * is slow and memory hungry as well. + * + * TODO: optimize tiled rendering case, by batching together denoising of many + * tiles somehow? */ static thread_mutex mutex; thread_scoped_lock lock(mutex); @@ -964,54 +978,192 @@ class CPUDevice : public Device { } if (!oidn_filter) { oidn_filter = oidn_device.newFilter("RT"); + oidn_filter.set("hdr", true); + oidn_filter.set("srgb", false); } - /* Copy pixels from compute device to CPU (no-op for CPU device). */ - rtile.buffers->buffer.copy_from_device(); - /* Set images with appropriate stride for our interleaved pass storage. */ - const struct { + struct { const char *name; - int offset; - } passes[] = {{"color", task.pass_denoising_data + DENOISING_PASS_COLOR}, - {"normal", task.pass_denoising_data + DENOISING_PASS_NORMAL}, - {"albedo", task.pass_denoising_data + DENOISING_PASS_ALBEDO}, - {"output", 0}, + const int offset; + const bool scale; + const bool use; + array<float> scaled_buffer; + } passes[] = {{"color", task.pass_denoising_data + DENOISING_PASS_COLOR, false, true}, + {"albedo", + task.pass_denoising_data + DENOISING_PASS_ALBEDO, + true, + task.denoising.input_passes >= DENOISER_INPUT_RGB_ALBEDO}, + {"normal", + task.pass_denoising_data + DENOISING_PASS_NORMAL, + true, + task.denoising.input_passes >= DENOISER_INPUT_RGB_ALBEDO_NORMAL}, + {"output", 0, false, true}, { NULL, 0 }}; for (int i = 0; passes[i].name; i++) { - const int64_t offset = rtile.offset + rtile.x + rtile.y * rtile.stride; - const int64_t buffer_offset = (offset * task.pass_stride + passes[i].offset) * sizeof(float); - const int64_t pixel_stride = task.pass_stride * sizeof(float); - const int64_t row_stride = rtile.stride * pixel_stride; + if (!passes[i].use) { + continue; + } - oidn_filter.setImage(passes[i].name, - (char *)rtile.buffer + buffer_offset, - oidn::Format::Float3, - rtile.w, - rtile.h, - 0, - pixel_stride, - row_stride); + const int64_t pixel_offset = offset + x + y * stride; + const int64_t buffer_offset = (pixel_offset * task.pass_stride + passes[i].offset); + const int64_t pixel_stride = task.pass_stride; + const int64_t row_stride = stride * pixel_stride; + + if (passes[i].scale && scale != 1.0f) { + /* Normalize albedo and normal passes as they are scaled by the number of samples. + * For the color passes OIDN will perform auto-exposure making it unnecessary. */ + array<float> &scaled_buffer = passes[i].scaled_buffer; + scaled_buffer.resize(w * h * 3); + + for (int y = 0; y < h; y++) { + const float *pass_row = buffer + buffer_offset + y * row_stride; + float *scaled_row = scaled_buffer.data() + y * w * 3; + + for (int x = 0; x < w; x++) { + scaled_row[x * 3 + 0] = pass_row[x * pixel_stride + 0] * scale; + scaled_row[x * 3 + 1] = pass_row[x * pixel_stride + 1] * scale; + scaled_row[x * 3 + 2] = pass_row[x * pixel_stride + 2] * scale; + } + } + + oidn_filter.setImage( + passes[i].name, scaled_buffer.data(), oidn::Format::Float3, w, h, 0, 0, 0); + } + else { + oidn_filter.setImage(passes[i].name, + buffer + buffer_offset, + oidn::Format::Float3, + w, + h, + 0, + pixel_stride * sizeof(float), + row_stride * sizeof(float)); + } } /* Execute filter. */ - oidn_filter.set("hdr", true); - oidn_filter.set("srgb", false); oidn_filter.commit(); oidn_filter.execute(); - - /* todo: it may be possible to avoid this copy, but we have to ensure that - * when other code copies data from the device it doesn't overwrite the - * denoiser buffers. */ - rtile.buffers->buffer.copy_to_device(); #else (void)task; - (void)rtile; + (void)buffer; + (void)offset; + (void)stride; + (void)x; + (void)y; + (void)w; + (void)h; + (void)scale; #endif } + void denoise_openimagedenoise(DeviceTask &task, RenderTile &rtile) + { + if (task.type == DeviceTask::DENOISE_BUFFER) { + /* Copy pixels from compute device to CPU (no-op for CPU device). */ + rtile.buffers->buffer.copy_from_device(); + + denoise_openimagedenoise_buffer(task, + (float *)rtile.buffer, + rtile.offset, + rtile.stride, + rtile.x, + rtile.y, + rtile.w, + rtile.h, + 1.0f / rtile.sample); + + /* todo: it may be possible to avoid this copy, but we have to ensure that + * when other code copies data from the device it doesn't overwrite the + * denoiser buffers. */ + rtile.buffers->buffer.copy_to_device(); + } + else { + /* Per-tile denoising. */ + rtile.sample = rtile.start_sample + rtile.num_samples; + const float scale = 1.0f / rtile.sample; + const float invscale = rtile.sample; + const size_t pass_stride = task.pass_stride; + + /* Map neighboring tiles into one buffer for denoising. */ + RenderTileNeighbors neighbors(rtile); + task.map_neighbor_tiles(neighbors, this); + RenderTile ¢er_tile = neighbors.tiles[RenderTileNeighbors::CENTER]; + rtile = center_tile; + + /* Calculate size of the tile to denoise (including overlap). The overlap + * size was chosen empirically. OpenImageDenoise specifies an overlap size + * of 128 but this is significantly bigger than typical tile size. */ + const int4 rect = rect_clip(rect_expand(center_tile.bounds(), 64), neighbors.bounds()); + const int2 rect_size = make_int2(rect.z - rect.x, rect.w - rect.y); + + /* Adjacent tiles are in separate memory regions, copy into single buffer. */ + array<float> merged(rect_size.x * rect_size.y * task.pass_stride); + + for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { + RenderTile &ntile = neighbors.tiles[i]; + if (!ntile.buffer) { + continue; + } + + const int xmin = max(ntile.x, rect.x); + const int ymin = max(ntile.y, rect.y); + const int xmax = min(ntile.x + ntile.w, rect.z); + const int ymax = min(ntile.y + ntile.h, rect.w); + + const size_t tile_offset = ntile.offset + xmin + ymin * ntile.stride; + const float *tile_buffer = (float *)ntile.buffer + tile_offset * pass_stride; + + const size_t merged_stride = rect_size.x; + const size_t merged_offset = (xmin - rect.x) + (ymin - rect.y) * merged_stride; + float *merged_buffer = merged.data() + merged_offset * pass_stride; + + for (int y = ymin; y < ymax; y++) { + for (int x = 0; x < pass_stride * (xmax - xmin); x++) { + merged_buffer[x] = tile_buffer[x] * scale; + } + tile_buffer += ntile.stride * pass_stride; + merged_buffer += merged_stride * pass_stride; + } + } + + /* Denoise */ + denoise_openimagedenoise_buffer( + task, merged.data(), 0, rect_size.x, 0, 0, rect_size.x, rect_size.y, 1.0f); + + /* Copy back result from merged buffer. */ + RenderTile &ntile = neighbors.target; + if (ntile.buffer) { + const int xmin = max(ntile.x, rect.x); + const int ymin = max(ntile.y, rect.y); + const int xmax = min(ntile.x + ntile.w, rect.z); + const int ymax = min(ntile.y + ntile.h, rect.w); + + const size_t tile_offset = ntile.offset + xmin + ymin * ntile.stride; + float *tile_buffer = (float *)ntile.buffer + tile_offset * pass_stride; + + const size_t merged_stride = rect_size.x; + const size_t merged_offset = (xmin - rect.x) + (ymin - rect.y) * merged_stride; + const float *merged_buffer = merged.data() + merged_offset * pass_stride; + + for (int y = ymin; y < ymax; y++) { + for (int x = 0; x < pass_stride * (xmax - xmin); x += pass_stride) { + tile_buffer[x + 0] = merged_buffer[x + 0] * invscale; + tile_buffer[x + 1] = merged_buffer[x + 1] * invscale; + tile_buffer[x + 2] = merged_buffer[x + 2] * invscale; + } + tile_buffer += ntile.stride * pass_stride; + merged_buffer += merged_stride * pass_stride; + } + } + + task.unmap_neighbor_tiles(neighbors, this); + } + } + void denoise_nlm(DenoisingTask &denoising, RenderTile &tile) { ProfilingHelper profiling(denoising.profiler, PROFILING_DENOISING); @@ -1040,7 +1192,7 @@ class CPUDevice : public Device { denoising.render_buffer.samples = tile.sample; denoising.buffer.gpu_temporary_mem = false; - denoising.run_denoising(&tile); + denoising.run_denoising(tile); } void thread_render(DeviceTask &task) @@ -1070,10 +1222,23 @@ class CPUDevice : public Device { } } + /* NLM denoiser. */ DenoisingTask *denoising = NULL; + /* OpenImageDenoise: we can only denoise with one thread at a time, so to + * avoid waiting with mutex locks in the denoiser, we let only a single + * thread acquire denoising tiles. */ + uint tile_types = task.tile_types; + bool hold_denoise_lock = false; + if ((tile_types & RenderTile::DENOISE) && task.denoising.type == DENOISER_OPENIMAGEDENOISE) { + if (!oidn_task_lock.try_lock()) { + tile_types &= ~RenderTile::DENOISE; + hold_denoise_lock = true; + } + } + RenderTile tile; - while (task.acquire_tile(this, tile, task.tile_types)) { + while (task.acquire_tile(this, tile, tile_types)) { if (tile.task == RenderTile::PATH_TRACE) { if (use_split_kernel) { device_only_memory<uchar> void_buffer(this, "void_buffer"); @@ -1108,6 +1273,10 @@ class CPUDevice : public Device { } } + if (hold_denoise_lock) { + oidn_task_lock.unlock(); + } + profiler.remove_state(&kg->profiler); thread_kernel_globals_free((KernelGlobals *)kgbuffer.device_pointer); diff --git a/intern/cycles/device/device_denoising.cpp b/intern/cycles/device/device_denoising.cpp index 89de80a5bcd..38c42d15cab 100644 --- a/intern/cycles/device/device_denoising.cpp +++ b/intern/cycles/device/device_denoising.cpp @@ -71,29 +71,30 @@ DenoisingTask::~DenoisingTask() tile_info_mem.free(); } -void DenoisingTask::set_render_buffer(RenderTile *rtiles) +void DenoisingTask::set_render_buffer(RenderTileNeighbors &neighbors) { - for (int i = 0; i < 9; i++) { - tile_info->offsets[i] = rtiles[i].offset; - tile_info->strides[i] = rtiles[i].stride; - tile_info->buffers[i] = rtiles[i].buffer; + for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { + RenderTile &rtile = neighbors.tiles[i]; + tile_info->offsets[i] = rtile.offset; + tile_info->strides[i] = rtile.stride; + tile_info->buffers[i] = rtile.buffer; } - tile_info->x[0] = rtiles[3].x; - tile_info->x[1] = rtiles[4].x; - tile_info->x[2] = rtiles[5].x; - tile_info->x[3] = rtiles[5].x + rtiles[5].w; - tile_info->y[0] = rtiles[1].y; - tile_info->y[1] = rtiles[4].y; - tile_info->y[2] = rtiles[7].y; - tile_info->y[3] = rtiles[7].y + rtiles[7].h; - - target_buffer.offset = rtiles[9].offset; - target_buffer.stride = rtiles[9].stride; - target_buffer.ptr = rtiles[9].buffer; - - if (do_prefilter && rtiles[9].buffers) { + tile_info->x[0] = neighbors.tiles[3].x; + tile_info->x[1] = neighbors.tiles[4].x; + tile_info->x[2] = neighbors.tiles[5].x; + tile_info->x[3] = neighbors.tiles[5].x + neighbors.tiles[5].w; + tile_info->y[0] = neighbors.tiles[1].y; + tile_info->y[1] = neighbors.tiles[4].y; + tile_info->y[2] = neighbors.tiles[7].y; + tile_info->y[3] = neighbors.tiles[7].y + neighbors.tiles[7].h; + + target_buffer.offset = neighbors.target.offset; + target_buffer.stride = neighbors.target.stride; + target_buffer.ptr = neighbors.target.buffer; + + if (do_prefilter && neighbors.target.buffers) { target_buffer.denoising_output_offset = - rtiles[9].buffers->params.get_denoising_prefiltered_offset(); + neighbors.target.buffers->params.get_denoising_prefiltered_offset(); } else { target_buffer.denoising_output_offset = 0; @@ -320,12 +321,11 @@ void DenoisingTask::reconstruct() functions.solve(target_buffer.ptr); } -void DenoisingTask::run_denoising(RenderTile *tile) +void DenoisingTask::run_denoising(RenderTile &tile) { - RenderTile rtiles[10]; - rtiles[4] = *tile; - functions.map_neighbor_tiles(rtiles); - set_render_buffer(rtiles); + RenderTileNeighbors neighbors(tile); + functions.map_neighbor_tiles(neighbors); + set_render_buffer(neighbors); setup_denoising_buffer(); @@ -347,7 +347,7 @@ void DenoisingTask::run_denoising(RenderTile *tile) write_buffer(); } - functions.unmap_neighbor_tiles(rtiles); + functions.unmap_neighbor_tiles(neighbors); } CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_denoising.h b/intern/cycles/device/device_denoising.h index 4c122e981eb..2c0dc23b44a 100644 --- a/intern/cycles/device/device_denoising.h +++ b/intern/cycles/device/device_denoising.h @@ -102,8 +102,8 @@ class DenoisingTask { device_ptr output_ptr)> detect_outliers; function<bool(int out_offset, device_ptr frop_ptr, device_ptr buffer_ptr)> write_feature; - function<void(RenderTile *rtiles)> map_neighbor_tiles; - function<void(RenderTile *rtiles)> unmap_neighbor_tiles; + function<void(RenderTileNeighbors &neighbors)> map_neighbor_tiles; + function<void(RenderTileNeighbors &neighbors)> unmap_neighbor_tiles; } functions; /* Stores state of the current Reconstruction operation, @@ -154,7 +154,7 @@ class DenoisingTask { DenoisingTask(Device *device, const DeviceTask &task); ~DenoisingTask(); - void run_denoising(RenderTile *tile); + void run_denoising(RenderTile &tile); struct DenoiseBuffers { int pass_stride; @@ -179,7 +179,7 @@ class DenoisingTask { protected: Device *device; - void set_render_buffer(RenderTile *rtiles); + void set_render_buffer(RenderTileNeighbors &neighbors); void setup_denoising_buffer(); void prefilter_shadowing(); void prefilter_features(); diff --git a/intern/cycles/device/device_multi.cpp b/intern/cycles/device/device_multi.cpp index fd14bbdccc5..9ea8782d0f0 100644 --- a/intern/cycles/device/device_multi.cpp +++ b/intern/cycles/device/device_multi.cpp @@ -177,8 +177,11 @@ class MultiDevice : public Device { return false; if (requested_features.use_denoising) { + /* Only need denoising feature, everything else is unused. */ + DeviceRequestedFeatures denoising_features; + denoising_features.use_denoising = true; foreach (SubDevice &sub, denoising_devices) - if (!sub.device->load_kernels(requested_features)) + if (!sub.device->load_kernels(denoising_features)) return false; } @@ -581,20 +584,22 @@ class MultiDevice : public Device { return -1; } - void map_neighbor_tiles(Device *sub_device, RenderTile *tiles) + void map_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors) { - for (int i = 0; i < 9; i++) { - if (!tiles[i].buffers) { + for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { + RenderTile &tile = neighbors.tiles[i]; + + if (!tile.buffers) { continue; } - device_vector<float> &mem = tiles[i].buffers->buffer; - tiles[i].buffer = mem.device_pointer; + device_vector<float> &mem = tile.buffers->buffer; + tile.buffer = mem.device_pointer; if (mem.device == this && matching_rendering_and_denoising_devices) { /* Skip unnecessary copies in viewport mode (buffer covers the * whole image), but still need to fix up the tile device pointer. */ - map_tile(sub_device, tiles[i]); + map_tile(sub_device, tile); continue; } @@ -607,15 +612,15 @@ class MultiDevice : public Device { * also required for the case where a CPU thread is denoising * a tile rendered on the GPU. In that case we have to avoid * overwriting the buffer being de-noised by the CPU thread. */ - if (!tiles[i].buffers->map_neighbor_copied) { - tiles[i].buffers->map_neighbor_copied = true; + if (!tile.buffers->map_neighbor_copied) { + tile.buffers->map_neighbor_copied = true; mem.copy_from_device(); } if (mem.device == this) { /* Can re-use memory if tile is already allocated on the sub device. */ - map_tile(sub_device, tiles[i]); - mem.swap_device(sub_device, mem.device_size, tiles[i].buffer); + map_tile(sub_device, tile); + mem.swap_device(sub_device, mem.device_size, tile.buffer); } else { mem.swap_device(sub_device, 0, 0); @@ -623,40 +628,42 @@ class MultiDevice : public Device { mem.copy_to_device(); - tiles[i].buffer = mem.device_pointer; - tiles[i].device_size = mem.device_size; + tile.buffer = mem.device_pointer; + tile.device_size = mem.device_size; mem.restore_device(); } } } - void unmap_neighbor_tiles(Device *sub_device, RenderTile *tiles) + void unmap_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors) { - device_vector<float> &mem = tiles[9].buffers->buffer; + RenderTile &target_tile = neighbors.target; + device_vector<float> &mem = target_tile.buffers->buffer; if (mem.device == this && matching_rendering_and_denoising_devices) { return; } /* Copy denoised result back to the host. */ - mem.swap_device(sub_device, tiles[9].device_size, tiles[9].buffer); + mem.swap_device(sub_device, target_tile.device_size, target_tile.buffer); mem.copy_from_device(); mem.restore_device(); /* Copy denoised result to the original device. */ mem.copy_to_device(); - for (int i = 0; i < 9; i++) { - if (!tiles[i].buffers) { + for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { + RenderTile &tile = neighbors.tiles[i]; + if (!tile.buffers) { continue; } - device_vector<float> &mem = tiles[i].buffers->buffer; + device_vector<float> &mem = tile.buffers->buffer; if (mem.device != sub_device && mem.device != this) { /* Free up memory again if it was allocated for the copy above. */ - mem.swap_device(sub_device, tiles[i].device_size, tiles[i].buffer); + mem.swap_device(sub_device, tile.device_size, tile.buffer); sub_device->mem_free(mem); mem.restore_device(); } diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp index ececca3df53..1cc45983565 100644 --- a/intern/cycles/device/device_optix.cpp +++ b/intern/cycles/device/device_optix.cpp @@ -131,8 +131,12 @@ class OptiXDevice : public CUDADevice { PG_RGEN, PG_MISS, PG_HITD, // Default hit group - PG_HITL, // __BVH_LOCAL__ hit group PG_HITS, // __SHADOW_RECORD_ALL__ hit group + PG_HITL, // __BVH_LOCAL__ hit group (only used for triangles) +# if OPTIX_ABI_VERSION >= 36 + PG_HITD_MOTION, + PG_HITS_MOTION, +# endif # ifdef WITH_CYCLES_DEBUG PG_EXCP, # endif @@ -177,6 +181,7 @@ class OptiXDevice : public CUDADevice { OptixDeviceContext context = NULL; OptixModule optix_module = NULL; // All necessary OptiX kernels are in one module + OptixModule builtin_modules[2] = {}; OptixPipeline pipelines[NUM_PIPELINES] = {}; bool motion_blur = false; @@ -264,6 +269,9 @@ class OptiXDevice : public CUDADevice { // Unload modules if (optix_module != NULL) optixModuleDestroy(optix_module); + for (unsigned int i = 0; i < 2; ++i) + if (builtin_modules[i] != NULL) + optixModuleDestroy(builtin_modules[i]); for (unsigned int i = 0; i < NUM_PIPELINES; ++i) if (pipelines[i] != NULL) optixPipelineDestroy(pipelines[i]); @@ -338,6 +346,12 @@ class OptiXDevice : public CUDADevice { optixModuleDestroy(optix_module); optix_module = NULL; } + for (unsigned int i = 0; i < 2; ++i) { + if (builtin_modules[i] != NULL) { + optixModuleDestroy(builtin_modules[i]); + builtin_modules[i] = NULL; + } + } for (unsigned int i = 0; i < NUM_PIPELINES; ++i) { if (pipelines[i] != NULL) { optixPipelineDestroy(pipelines[i]); @@ -369,6 +383,18 @@ class OptiXDevice : public CUDADevice { # endif pipeline_options.pipelineLaunchParamsVariableName = "__params"; // See kernel_globals.h +# if OPTIX_ABI_VERSION >= 36 + pipeline_options.usesPrimitiveTypeFlags = OPTIX_PRIMITIVE_TYPE_FLAGS_TRIANGLE; + if (requested_features.use_hair) { + if (DebugFlags().optix.curves_api && requested_features.use_hair_thick) { + pipeline_options.usesPrimitiveTypeFlags |= OPTIX_PRIMITIVE_TYPE_FLAGS_ROUND_CUBIC_BSPLINE; + } + else { + pipeline_options.usesPrimitiveTypeFlags |= OPTIX_PRIMITIVE_TYPE_FLAGS_CUSTOM; + } + } +# endif + // Keep track of whether motion blur is enabled, so to enable/disable motion in BVH builds // This is necessary since objects may be reported to have motion if the Vector pass is // active, but may still need to be rendered without motion blur if that isn't active as well @@ -442,6 +468,34 @@ class OptiXDevice : public CUDADevice { group_descs[PG_HITD].hitgroup.entryFunctionNameIS = "__intersection__curve_ribbon"; group_descs[PG_HITS].hitgroup.entryFunctionNameIS = "__intersection__curve_ribbon"; } + +# if OPTIX_ABI_VERSION >= 36 + if (DebugFlags().optix.curves_api && requested_features.use_hair_thick) { + OptixBuiltinISOptions builtin_options; + builtin_options.builtinISModuleType = OPTIX_PRIMITIVE_TYPE_ROUND_CUBIC_BSPLINE; + builtin_options.usesMotionBlur = false; + + check_result_optix_ret(optixBuiltinISModuleGet( + context, &module_options, &pipeline_options, &builtin_options, &builtin_modules[0])); + + group_descs[PG_HITD].hitgroup.moduleIS = builtin_modules[0]; + group_descs[PG_HITD].hitgroup.entryFunctionNameIS = nullptr; + group_descs[PG_HITS].hitgroup.moduleIS = builtin_modules[0]; + group_descs[PG_HITS].hitgroup.entryFunctionNameIS = nullptr; + + if (motion_blur) { + builtin_options.usesMotionBlur = true; + + check_result_optix_ret(optixBuiltinISModuleGet( + context, &module_options, &pipeline_options, &builtin_options, &builtin_modules[1])); + + group_descs[PG_HITD_MOTION] = group_descs[PG_HITD]; + group_descs[PG_HITD_MOTION].hitgroup.moduleIS = builtin_modules[1]; + group_descs[PG_HITS_MOTION] = group_descs[PG_HITS]; + group_descs[PG_HITS_MOTION].hitgroup.moduleIS = builtin_modules[1]; + } + } +# endif } if (requested_features.use_subsurface || requested_features.use_shader_raytrace) { @@ -493,8 +547,14 @@ class OptiXDevice : public CUDADevice { unsigned int trace_css = stack_size[PG_HITD].cssCH; // This is based on the maximum of closest-hit and any-hit/intersection programs trace_css = std::max(trace_css, stack_size[PG_HITD].cssIS + stack_size[PG_HITD].cssAH); - trace_css = std::max(trace_css, stack_size[PG_HITL].cssIS + stack_size[PG_HITL].cssAH); trace_css = std::max(trace_css, stack_size[PG_HITS].cssIS + stack_size[PG_HITS].cssAH); + trace_css = std::max(trace_css, stack_size[PG_HITL].cssIS + stack_size[PG_HITL].cssAH); +# if OPTIX_ABI_VERSION >= 36 + trace_css = std::max(trace_css, + stack_size[PG_HITD_MOTION].cssIS + stack_size[PG_HITD_MOTION].cssAH); + trace_css = std::max(trace_css, + stack_size[PG_HITS_MOTION].cssIS + stack_size[PG_HITS_MOTION].cssAH); +# endif OptixPipelineLinkOptions link_options; link_options.maxTraceDepth = 1; @@ -503,17 +563,23 @@ class OptiXDevice : public CUDADevice { # else link_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_LINEINFO; # endif - link_options.overrideUsesMotionBlur = pipeline_options.usesMotionBlur; +# if OPTIX_ABI_VERSION < 24 + link_options.overrideUsesMotionBlur = motion_blur; +# endif { // Create path tracing pipeline OptixProgramGroup pipeline_groups[] = { - groups[PG_RGEN], - groups[PG_MISS], - groups[PG_HITD], - groups[PG_HITS], - groups[PG_HITL], + groups[PG_RGEN], + groups[PG_MISS], + groups[PG_HITD], + groups[PG_HITS], + groups[PG_HITL], +# if OPTIX_ABI_VERSION >= 36 + groups[PG_HITD_MOTION], + groups[PG_HITS_MOTION], +# endif # ifdef WITH_CYCLES_DEBUG - groups[PG_EXCP], + groups[PG_EXCP], # endif }; check_result_optix_ret( @@ -530,8 +596,8 @@ class OptiXDevice : public CUDADevice { const unsigned int css = stack_size[PG_RGEN].cssRG + link_options.maxTraceDepth * trace_css; // Set stack size depending on pipeline options - check_result_optix_ret(optixPipelineSetStackSize( - pipelines[PIP_PATH_TRACE], 0, 0, css, (pipeline_options.usesMotionBlur ? 3 : 2))); + check_result_optix_ret( + optixPipelineSetStackSize(pipelines[PIP_PATH_TRACE], 0, 0, css, (motion_blur ? 3 : 2))); } // Only need to create shader evaluation pipeline if one of these features is used: @@ -541,15 +607,19 @@ class OptiXDevice : public CUDADevice { if (use_shader_eval_pipeline) { // Create shader evaluation pipeline OptixProgramGroup pipeline_groups[] = { - groups[PG_BAKE], - groups[PG_DISP], - groups[PG_BACK], - groups[PG_MISS], - groups[PG_HITD], - groups[PG_HITS], - groups[PG_HITL], + groups[PG_BAKE], + groups[PG_DISP], + groups[PG_BACK], + groups[PG_MISS], + groups[PG_HITD], + groups[PG_HITS], + groups[PG_HITL], +# if OPTIX_ABI_VERSION >= 36 + groups[PG_HITD_MOTION], + groups[PG_HITS_MOTION], +# endif # ifdef WITH_CYCLES_DEBUG - groups[PG_EXCP], + groups[PG_EXCP], # endif }; check_result_optix_ret( @@ -672,7 +742,11 @@ class OptiXDevice : public CUDADevice { sbt_params.missRecordCount = 1; sbt_params.hitgroupRecordBase = sbt_data.device_pointer + PG_HITD * sizeof(SbtRecord); sbt_params.hitgroupRecordStrideInBytes = sizeof(SbtRecord); - sbt_params.hitgroupRecordCount = 3; // PG_HITD, PG_HITL, PG_HITS +# if OPTIX_ABI_VERSION >= 36 + sbt_params.hitgroupRecordCount = 5; // PG_HITD(_MOTION), PG_HITS(_MOTION), PG_HITL +# else + sbt_params.hitgroupRecordCount = 3; // PG_HITD, PG_HITS, PG_HITL +# endif // Launch the ray generation program check_result_optix(optixLaunch(pipelines[PIP_PATH_TRACE], @@ -727,19 +801,18 @@ class OptiXDevice : public CUDADevice { // 0 1 2 // 3 4 5 // 6 7 8 9 - RenderTile rtiles[10]; - rtiles[4] = rtile; - task.map_neighbor_tiles(rtiles, this); - rtile = rtiles[4]; // Tile may have been modified by mapping code + RenderTileNeighbors neighbors(rtile); + task.map_neighbor_tiles(neighbors, this); + RenderTile ¢er_tile = neighbors.tiles[RenderTileNeighbors::CENTER]; + RenderTile &target_tile = neighbors.target; + rtile = center_tile; // Tile may have been modified by mapping code // Calculate size of the tile to denoise (including overlap) - int4 rect = make_int4( - rtiles[4].x, rtiles[4].y, rtiles[4].x + rtiles[4].w, rtiles[4].y + rtiles[4].h); + int4 rect = center_tile.bounds(); // Overlap between tiles has to be at least 64 pixels // TODO(pmours): Query this value from OptiX rect = rect_expand(rect, 64); - int4 clip_rect = make_int4( - rtiles[3].x, rtiles[1].y, rtiles[5].x + rtiles[5].w, rtiles[7].y + rtiles[7].h); + int4 clip_rect = neighbors.bounds(); rect = rect_clip(rect, clip_rect); int2 rect_size = make_int2(rect.z - rect.x, rect.w - rect.y); int2 overlap_offset = make_int2(rtile.x - rect.x, rtile.y - rect.y); @@ -760,14 +833,14 @@ class OptiXDevice : public CUDADevice { device_only_memory<float> input(this, "denoiser input"); device_vector<TileInfo> tile_info_mem(this, "denoiser tile info", MEM_READ_WRITE); - if ((!rtiles[0].buffer || rtiles[0].buffer == rtile.buffer) && - (!rtiles[1].buffer || rtiles[1].buffer == rtile.buffer) && - (!rtiles[2].buffer || rtiles[2].buffer == rtile.buffer) && - (!rtiles[3].buffer || rtiles[3].buffer == rtile.buffer) && - (!rtiles[5].buffer || rtiles[5].buffer == rtile.buffer) && - (!rtiles[6].buffer || rtiles[6].buffer == rtile.buffer) && - (!rtiles[7].buffer || rtiles[7].buffer == rtile.buffer) && - (!rtiles[8].buffer || rtiles[8].buffer == rtile.buffer)) { + bool contiguous_memory = true; + for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { + if (neighbors.tiles[i].buffer && neighbors.tiles[i].buffer != rtile.buffer) { + contiguous_memory = false; + } + } + + if (contiguous_memory) { // Tiles are in continous memory, so can just subtract overlap offset input_ptr -= (overlap_offset.x + overlap_offset.y * rtile.stride) * pixel_stride; // Stride covers the whole width of the image and not just a single tile @@ -782,19 +855,19 @@ class OptiXDevice : public CUDADevice { input_stride *= rect_size.x; TileInfo *tile_info = tile_info_mem.alloc(1); - for (int i = 0; i < 9; i++) { - tile_info->offsets[i] = rtiles[i].offset; - tile_info->strides[i] = rtiles[i].stride; - tile_info->buffers[i] = rtiles[i].buffer; + for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { + tile_info->offsets[i] = neighbors.tiles[i].offset; + tile_info->strides[i] = neighbors.tiles[i].stride; + tile_info->buffers[i] = neighbors.tiles[i].buffer; } - tile_info->x[0] = rtiles[3].x; - tile_info->x[1] = rtiles[4].x; - tile_info->x[2] = rtiles[5].x; - tile_info->x[3] = rtiles[5].x + rtiles[5].w; - tile_info->y[0] = rtiles[1].y; - tile_info->y[1] = rtiles[4].y; - tile_info->y[2] = rtiles[7].y; - tile_info->y[3] = rtiles[7].y + rtiles[7].h; + tile_info->x[0] = neighbors.tiles[3].x; + tile_info->x[1] = neighbors.tiles[4].x; + tile_info->x[2] = neighbors.tiles[5].x; + tile_info->x[3] = neighbors.tiles[5].x + neighbors.tiles[5].w; + tile_info->y[0] = neighbors.tiles[1].y; + tile_info->y[1] = neighbors.tiles[4].y; + tile_info->y[2] = neighbors.tiles[7].y; + tile_info->y[3] = neighbors.tiles[7].y + neighbors.tiles[7].h; tile_info_mem.copy_to_device(); void *args[] = { @@ -804,7 +877,7 @@ class OptiXDevice : public CUDADevice { # if OPTIX_DENOISER_NO_PIXEL_STRIDE device_only_memory<float> input_rgb(this, "denoiser input rgb"); - input_rgb.alloc_to_device(rect_size.x * rect_size.y * 3 * task.denoising.optix_input_passes); + input_rgb.alloc_to_device(rect_size.x * rect_size.y * 3 * task.denoising.input_passes); void *input_args[] = {&input_rgb.device_pointer, &input_ptr, @@ -813,7 +886,7 @@ class OptiXDevice : public CUDADevice { &input_stride, &task.pass_stride, const_cast<int *>(pass_offset), - &task.denoising.optix_input_passes, + &task.denoising.input_passes, &rtile.sample}; launch_filter_kernel( "kernel_cuda_filter_convert_to_rgb", rect_size.x, rect_size.y, input_args); @@ -824,7 +897,7 @@ class OptiXDevice : public CUDADevice { # endif const bool recreate_denoiser = (denoiser == NULL) || - (task.denoising.optix_input_passes != denoiser_input_passes); + (task.denoising.input_passes != denoiser_input_passes); if (recreate_denoiser) { // Destroy existing handle before creating new one if (denoiser != NULL) { @@ -833,23 +906,29 @@ class OptiXDevice : public CUDADevice { // Create OptiX denoiser handle on demand when it is first used OptixDenoiserOptions denoiser_options; - assert(task.denoising.optix_input_passes >= 1 && task.denoising.optix_input_passes <= 3); + assert(task.denoising.input_passes >= 1 && task.denoising.input_passes <= 3); denoiser_options.inputKind = static_cast<OptixDenoiserInputKind>( - OPTIX_DENOISER_INPUT_RGB + (task.denoising.optix_input_passes - 1)); + OPTIX_DENOISER_INPUT_RGB + (task.denoising.input_passes - 1)); +# if OPTIX_ABI_VERSION < 28 denoiser_options.pixelFormat = OPTIX_PIXEL_FORMAT_FLOAT3; +# endif check_result_optix_ret(optixDenoiserCreate(context, &denoiser_options, &denoiser)); check_result_optix_ret( optixDenoiserSetModel(denoiser, OPTIX_DENOISER_MODEL_KIND_HDR, NULL, 0)); // OptiX denoiser handle was created with the requested number of input passes - denoiser_input_passes = task.denoising.optix_input_passes; + denoiser_input_passes = task.denoising.input_passes; } OptixDenoiserSizes sizes = {}; check_result_optix_ret( optixDenoiserComputeMemoryResources(denoiser, rect_size.x, rect_size.y, &sizes)); +# if OPTIX_ABI_VERSION < 28 const size_t scratch_size = sizes.recommendedScratchSizeInBytes; +# else + const size_t scratch_size = sizes.withOverlapScratchSizeInBytes; +# endif const size_t scratch_offset = sizes.stateSizeInBytes; // Allocate denoiser state if tile size has changed since last setup @@ -897,10 +976,10 @@ class OptiXDevice : public CUDADevice { int2 output_offset = overlap_offset; overlap_offset = make_int2(0, 0); // Not supported by denoiser API, so apply manually # else - output_layers[0].data = rtiles[9].buffer + pixel_offset; - output_layers[0].width = rtiles[9].w; - output_layers[0].height = rtiles[9].h; - output_layers[0].rowStrideInBytes = rtiles[9].stride * pixel_stride; + output_layers[0].data = target_tile.buffer + pixel_offset; + output_layers[0].width = target_tile.w; + output_layers[0].height = target_tile.h; + output_layers[0].rowStrideInBytes = target_tile.stride * pixel_stride; output_layers[0].pixelStrideInBytes = pixel_stride; # endif output_layers[0].format = OPTIX_PIXEL_FORMAT_FLOAT3; @@ -913,7 +992,7 @@ class OptiXDevice : public CUDADevice { denoiser_state.device_pointer, scratch_offset, input_layers, - task.denoising.optix_input_passes, + task.denoising.input_passes, overlap_offset.x, overlap_offset.y, output_layers, @@ -922,26 +1001,26 @@ class OptiXDevice : public CUDADevice { # if OPTIX_DENOISER_NO_PIXEL_STRIDE void *output_args[] = {&input_ptr, - &rtiles[9].buffer, + &target_tile.buffer, &output_offset.x, &output_offset.y, &rect_size.x, &rect_size.y, - &rtiles[9].x, - &rtiles[9].y, - &rtiles[9].w, - &rtiles[9].h, - &rtiles[9].offset, - &rtiles[9].stride, + &target_tile.x, + &target_tile.y, + &target_tile.w, + &target_tile.h, + &target_tile.offset, + &target_tile.stride, &task.pass_stride, &rtile.sample}; launch_filter_kernel( - "kernel_cuda_filter_convert_from_rgb", rtiles[9].w, rtiles[9].h, output_args); + "kernel_cuda_filter_convert_from_rgb", target_tile.w, target_tile.h, output_args); # endif check_result_cuda_ret(cuStreamSynchronize(0)); - task.unmap_neighbor_tiles(rtiles, this); + task.unmap_neighbor_tiles(neighbors, this); } else { // Run CUDA denoising kernels @@ -993,7 +1072,11 @@ class OptiXDevice : public CUDADevice { sbt_params.missRecordCount = 1; sbt_params.hitgroupRecordBase = sbt_data.device_pointer + PG_HITD * sizeof(SbtRecord); sbt_params.hitgroupRecordStrideInBytes = sizeof(SbtRecord); - sbt_params.hitgroupRecordCount = 3; // PG_HITD, PG_HITL, PG_HITS +# if OPTIX_ABI_VERSION >= 36 + sbt_params.hitgroupRecordCount = 5; // PG_HITD(_MOTION), PG_HITS(_MOTION), PG_HITL +# else + sbt_params.hitgroupRecordCount = 3; // PG_HITD, PG_HITS, PG_HITL +# endif check_result_optix(optixLaunch(pipelines[PIP_SHADER_EVAL], cuda_stream[thread_index], @@ -1070,7 +1153,7 @@ class OptiXDevice : public CUDADevice { &build_input, 1, temp_mem.device_pointer, - temp_mem.device_size, + sizes.tempSizeInBytes, out_data, sizes.outputSizeInBytes, &out_handle, @@ -1142,7 +1225,6 @@ class OptiXDevice : public CUDADevice { continue; } - const size_t num_curves = hair->num_curves(); const size_t num_segments = hair->num_segments(); size_t num_motion_steps = 1; @@ -1152,7 +1234,18 @@ class OptiXDevice : public CUDADevice { } device_vector<OptixAabb> aabb_data(this, "temp_aabb_data", MEM_READ_ONLY); - aabb_data.alloc(num_segments * num_motion_steps); +# if OPTIX_ABI_VERSION >= 36 + device_vector<int> index_data(this, "temp_index_data", MEM_READ_ONLY); + device_vector<float4> vertex_data(this, "temp_vertex_data", MEM_READ_ONLY); + // Four control points for each curve segment + const size_t num_vertices = num_segments * 4; + if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) { + index_data.alloc(num_segments); + vertex_data.alloc(num_vertices * num_motion_steps); + } + else +# endif + aabb_data.alloc(num_segments * num_motion_steps); // Get AABBs for each motion step for (size_t step = 0; step < num_motion_steps; ++step) { @@ -1165,44 +1258,127 @@ class OptiXDevice : public CUDADevice { keys = motion_keys->data_float3() + attr_offset * hair->curve_keys.size(); } - size_t i = step * num_segments; - for (size_t j = 0; j < num_curves; ++j) { - const Hair::Curve c = hair->get_curve(j); - - for (size_t k = 0; k < c.num_segments(); ++i, ++k) { - BoundBox bounds = BoundBox::empty; - c.bounds_grow(k, keys, hair->curve_radius.data(), bounds); - - aabb_data[i].minX = bounds.min.x; - aabb_data[i].minY = bounds.min.y; - aabb_data[i].minZ = bounds.min.z; - aabb_data[i].maxX = bounds.max.x; - aabb_data[i].maxY = bounds.max.y; - aabb_data[i].maxZ = bounds.max.z; + for (size_t j = 0, i = 0; j < hair->num_curves(); ++j) { + const Hair::Curve curve = hair->get_curve(j); + + for (int segment = 0; segment < curve.num_segments(); ++segment, ++i) { +# if OPTIX_ABI_VERSION >= 36 + if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) { + int k0 = curve.first_key + segment; + int k1 = k0 + 1; + int ka = max(k0 - 1, curve.first_key); + int kb = min(k1 + 1, curve.first_key + curve.num_keys - 1); + + const float4 px = make_float4(keys[ka].x, keys[k0].x, keys[k1].x, keys[kb].x); + const float4 py = make_float4(keys[ka].y, keys[k0].y, keys[k1].y, keys[kb].y); + const float4 pz = make_float4(keys[ka].z, keys[k0].z, keys[k1].z, keys[kb].z); + const float4 pw = make_float4(hair->curve_radius[ka], + hair->curve_radius[k0], + hair->curve_radius[k1], + hair->curve_radius[kb]); + + // Convert Catmull-Rom data to Bezier spline + static const float4 cr2bsp0 = make_float4(+7, -4, +5, -2) / 6.f; + static const float4 cr2bsp1 = make_float4(-2, 11, -4, +1) / 6.f; + static const float4 cr2bsp2 = make_float4(+1, -4, 11, -2) / 6.f; + static const float4 cr2bsp3 = make_float4(-2, +5, -4, +7) / 6.f; + + index_data[i] = i * 4; + float4 *const v = vertex_data.data() + step * num_vertices + index_data[i]; + v[0] = make_float4( + dot(cr2bsp0, px), dot(cr2bsp0, py), dot(cr2bsp0, pz), dot(cr2bsp0, pw)); + v[1] = make_float4( + dot(cr2bsp1, px), dot(cr2bsp1, py), dot(cr2bsp1, pz), dot(cr2bsp1, pw)); + v[2] = make_float4( + dot(cr2bsp2, px), dot(cr2bsp2, py), dot(cr2bsp2, pz), dot(cr2bsp2, pw)); + v[3] = make_float4( + dot(cr2bsp3, px), dot(cr2bsp3, py), dot(cr2bsp3, pz), dot(cr2bsp3, pw)); + } + else +# endif + { + BoundBox bounds = BoundBox::empty; + curve.bounds_grow(segment, keys, hair->curve_radius.data(), bounds); + + const size_t index = step * num_segments + i; + aabb_data[index].minX = bounds.min.x; + aabb_data[index].minY = bounds.min.y; + aabb_data[index].minZ = bounds.min.z; + aabb_data[index].maxX = bounds.max.x; + aabb_data[index].maxY = bounds.max.y; + aabb_data[index].maxZ = bounds.max.z; + } } } } // Upload AABB data to GPU aabb_data.copy_to_device(); +# if OPTIX_ABI_VERSION >= 36 + index_data.copy_to_device(); + vertex_data.copy_to_device(); +# endif vector<device_ptr> aabb_ptrs; aabb_ptrs.reserve(num_motion_steps); +# if OPTIX_ABI_VERSION >= 36 + vector<device_ptr> width_ptrs; + vector<device_ptr> vertex_ptrs; + width_ptrs.reserve(num_motion_steps); + vertex_ptrs.reserve(num_motion_steps); +# endif for (size_t step = 0; step < num_motion_steps; ++step) { aabb_ptrs.push_back(aabb_data.device_pointer + step * num_segments * sizeof(OptixAabb)); +# if OPTIX_ABI_VERSION >= 36 + const device_ptr base_ptr = vertex_data.device_pointer + + step * num_vertices * sizeof(float4); + width_ptrs.push_back(base_ptr + 3 * sizeof(float)); // Offset by vertex size + vertex_ptrs.push_back(base_ptr); +# endif } - // Disable visibility test anyhit program, since it is already checked during intersection - // Those trace calls that require anyhit can force it with OPTIX_RAY_FLAG_ENFORCE_ANYHIT - unsigned int build_flags = OPTIX_GEOMETRY_FLAG_DISABLE_ANYHIT; + // Force a single any-hit call, so shadow record-all behavior works correctly + unsigned int build_flags = OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL; OptixBuildInput build_input = {}; - build_input.type = OPTIX_BUILD_INPUT_TYPE_CUSTOM_PRIMITIVES; - build_input.aabbArray.aabbBuffers = (CUdeviceptr *)aabb_ptrs.data(); - build_input.aabbArray.numPrimitives = num_segments; - build_input.aabbArray.strideInBytes = sizeof(OptixAabb); - build_input.aabbArray.flags = &build_flags; - build_input.aabbArray.numSbtRecords = 1; - build_input.aabbArray.primitiveIndexOffset = hair->optix_prim_offset; +# if OPTIX_ABI_VERSION >= 36 + if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) { + build_input.type = OPTIX_BUILD_INPUT_TYPE_CURVES; + build_input.curveArray.curveType = OPTIX_PRIMITIVE_TYPE_ROUND_CUBIC_BSPLINE; + build_input.curveArray.numPrimitives = num_segments; + build_input.curveArray.vertexBuffers = (CUdeviceptr *)vertex_ptrs.data(); + build_input.curveArray.numVertices = num_vertices; + build_input.curveArray.vertexStrideInBytes = sizeof(float4); + build_input.curveArray.widthBuffers = (CUdeviceptr *)width_ptrs.data(); + build_input.curveArray.widthStrideInBytes = sizeof(float4); + build_input.curveArray.indexBuffer = (CUdeviceptr)index_data.device_pointer; + build_input.curveArray.indexStrideInBytes = sizeof(int); + build_input.curveArray.flag = build_flags; + build_input.curveArray.primitiveIndexOffset = hair->optix_prim_offset; + } + else +# endif + { + // Disable visibility test any-hit program, since it is already checked during + // intersection. Those trace calls that require anyhit can force it with a ray flag. + build_flags |= OPTIX_GEOMETRY_FLAG_DISABLE_ANYHIT; + + build_input.type = OPTIX_BUILD_INPUT_TYPE_CUSTOM_PRIMITIVES; +# if OPTIX_ABI_VERSION < 23 + build_input.aabbArray.aabbBuffers = (CUdeviceptr *)aabb_ptrs.data(); + build_input.aabbArray.numPrimitives = num_segments; + build_input.aabbArray.strideInBytes = sizeof(OptixAabb); + build_input.aabbArray.flags = &build_flags; + build_input.aabbArray.numSbtRecords = 1; + build_input.aabbArray.primitiveIndexOffset = hair->optix_prim_offset; +# else + build_input.customPrimitiveArray.aabbBuffers = (CUdeviceptr *)aabb_ptrs.data(); + build_input.customPrimitiveArray.numPrimitives = num_segments; + build_input.customPrimitiveArray.strideInBytes = sizeof(OptixAabb); + build_input.customPrimitiveArray.flags = &build_flags; + build_input.customPrimitiveArray.numSbtRecords = 1; + build_input.customPrimitiveArray.primitiveIndexOffset = hair->optix_prim_offset; +# endif + } // Allocate memory for new BLAS and build it OptixTraversableHandle handle; @@ -1257,8 +1433,8 @@ class OptiXDevice : public CUDADevice { vertex_ptrs.push_back(vertex_data.device_pointer + num_verts * step * sizeof(float3)); } - // No special build flags for triangle primitives - unsigned int build_flags = OPTIX_GEOMETRY_FLAG_NONE; + // Force a single any-hit call, so shadow record-all behavior works correctly + unsigned int build_flags = OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL; OptixBuildInput build_input = {}; build_input.type = OPTIX_BUILD_INPUT_TYPE_TRIANGLES; build_input.triangleArray.vertexBuffers = (CUdeviceptr *)vertex_ptrs.data(); @@ -1324,9 +1500,26 @@ class OptiXDevice : public CUDADevice { // Set user instance ID to object index instance.instanceId = ob->get_device_index(); - // Volumes have a special bit set in the visibility mask so a trace can mask only volumes - // See 'scene_intersect_volume' in bvh.h - instance.visibilityMask = (ob->geometry->has_volume ? 3 : 1); + // Have to have at least one bit in the mask, or else instance would always be culled + instance.visibilityMask = 1; + + if (ob->geometry->has_volume) { + // Volumes have a special bit set in the visibility mask so a trace can mask only volumes + instance.visibilityMask |= 2; + } + + if (ob->geometry->type == Geometry::HAIR) { + // Same applies to curves (so they can be skipped in local trace calls) + instance.visibilityMask |= 4; + +# if OPTIX_ABI_VERSION >= 36 + if (motion_blur && ob->geometry->has_motion_blur() && DebugFlags().optix.curves_api && + static_cast<const Hair *>(ob->geometry)->curve_shape == CURVE_THICK) { + // Select between motion blur and non-motion blur built-in intersection module + instance.sbtOffset = PG_HITD_MOTION - PG_HITD; + } +# endif + } // Insert motion traversable if object has motion if (motion_blur && ob->use_motion()) { diff --git a/intern/cycles/device/device_task.h b/intern/cycles/device/device_task.h index 600973b8100..fd380788282 100644 --- a/intern/cycles/device/device_task.h +++ b/intern/cycles/device/device_task.h @@ -29,6 +29,7 @@ CCL_NAMESPACE_BEGIN class Device; class RenderBuffers; class RenderTile; +class RenderTileNeighbors; class Tile; enum DenoiserType { @@ -41,6 +42,14 @@ enum DenoiserType { DENOISER_ALL = ~0, }; +enum DenoiserInput { + DENOISER_INPUT_RGB = 1, + DENOISER_INPUT_RGB_ALBEDO = 2, + DENOISER_INPUT_RGB_ALBEDO_NORMAL = 3, + + DENOISER_INPUT_NUM, +}; + typedef int DenoiserTypeMask; class DenoiseParams { @@ -72,10 +81,10 @@ class DenoiseParams { /* Clamp the input to the range of +-1e8. Should be enough for any legitimate data. */ bool clamp_input; - /** Optix Denoiser **/ + /** OIDN/Optix Denoiser **/ - /* Passes handed over to the OptiX denoiser (default to color + albedo). */ - int optix_input_passes; + /* Passes handed over to the OIDN/OptiX denoiser (default to color + albedo). */ + DenoiserInput input_passes; DenoiseParams() { @@ -91,7 +100,7 @@ class DenoiseParams { neighbor_frames = 2; clamp_input = true; - optix_input_passes = 2; + input_passes = DENOISER_INPUT_RGB_ALBEDO_NORMAL; start_sample = 0; } @@ -150,8 +159,8 @@ class DeviceTask { function<void(RenderTile &)> update_tile_sample; function<void(RenderTile &)> release_tile; function<bool()> get_cancel; - function<void(RenderTile *, Device *)> map_neighbor_tiles; - function<void(RenderTile *, Device *)> unmap_neighbor_tiles; + function<void(RenderTileNeighbors &, Device *)> map_neighbor_tiles; + function<void(RenderTileNeighbors &, Device *)> unmap_neighbor_tiles; uint tile_types; DenoiseParams denoising; diff --git a/intern/cycles/device/opencl/device_opencl_impl.cpp b/intern/cycles/device/opencl/device_opencl_impl.cpp index 8c94815b193..e851749949d 100644 --- a/intern/cycles/device/opencl/device_opencl_impl.cpp +++ b/intern/cycles/device/opencl/device_opencl_impl.cpp @@ -1850,7 +1850,7 @@ void OpenCLDevice::denoise(RenderTile &rtile, DenoisingTask &denoising) denoising.render_buffer.samples = rtile.sample; denoising.buffer.gpu_temporary_mem = true; - denoising.run_denoising(&rtile); + denoising.run_denoising(rtile); } void OpenCLDevice::shader(DeviceTask &task) diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h index 80b58f46329..3049f243ae9 100644 --- a/intern/cycles/kernel/bvh/bvh.h +++ b/intern/cycles/kernel/bvh/bvh.h @@ -172,11 +172,11 @@ ccl_device_intersect bool scene_intersect(KernelGlobals *kg, 0.0f, ray->t, ray->time, - 0xFF, + 0xF, OPTIX_RAY_FLAG_NONE, + 0, // SBT offset for PG_HITD 0, 0, - 0, // SBT offset for PG_HITD p0, p1, p2, @@ -264,12 +264,13 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals *kg, 0.0f, ray->t, ray->time, + // Skip curves + 0x3, // Need to always call into __anyhit__kernel_optix_local_hit - 0xFF, OPTIX_RAY_FLAG_ENFORCE_ANYHIT, - 1, + 2, // SBT offset for PG_HITL + 0, 0, - 0, // SBT offset for PG_HITL p0, p1, p2, @@ -374,12 +375,12 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals *kg, 0.0f, ray->t, ray->time, + 0xF, // Need to always call into __anyhit__kernel_optix_shadow_all_hit - 0xFF, OPTIX_RAY_FLAG_ENFORCE_ANYHIT, - 2, + 1, // SBT offset for PG_HITS + 0, 0, - 0, // SBT offset for PG_HITS p0, p1, *num_hits, @@ -458,12 +459,12 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals *kg, 0.0f, ray->t, ray->time, - // Visibility mask set to only intersect objects with volumes - 0x02, + // Skip everything but volumes + 0x2, OPTIX_RAY_FLAG_NONE, + 0, // SBT offset for PG_HITD 0, 0, - 0, // SBT offset for PG_HITD p0, p1, p2, diff --git a/intern/cycles/kernel/geom/geom_curve_intersect.h b/intern/cycles/kernel/geom/geom_curve_intersect.h index c04dbee52cc..06d2c016f5b 100644 --- a/intern/cycles/kernel/geom/geom_curve_intersect.h +++ b/intern/cycles/kernel/geom/geom_curve_intersect.h @@ -734,7 +734,6 @@ ccl_device_inline void curve_shader_setup(KernelGlobals *kg, } sd->u = isect->u; - sd->v = isect->v; P = P + D * t; @@ -750,6 +749,7 @@ ccl_device_inline void curve_shader_setup(KernelGlobals *kg, sd->N = normalize(sine * bitangent - cosine * normalize(cross(tangent, bitangent))); sd->Ng = -D; + sd->v = isect->v; # if 0 /* This approximates the position and geometric normal of a thick curve too, @@ -764,8 +764,11 @@ ccl_device_inline void curve_shader_setup(KernelGlobals *kg, * This could be optimized by recording the normal in the intersection, * however for Optix this would go beyond the size of the payload. */ const float3 P_inside = float4_to_float3(catmull_rom_basis_eval(P_curve, isect->u)); - sd->Ng = normalize(P - P_inside); - sd->N = sd->Ng; + const float3 Ng = normalize(P - P_inside); + + sd->N = Ng; + sd->Ng = Ng; + sd->v = 0.0f; } # ifdef __DPDU__ diff --git a/intern/cycles/kernel/kernel_camera.h b/intern/cycles/kernel/kernel_camera.h index 445cf9eb44b..efe46d5b0dd 100644 --- a/intern/cycles/kernel/kernel_camera.h +++ b/intern/cycles/kernel/kernel_camera.h @@ -379,7 +379,7 @@ ccl_device_inline void camera_sample(KernelGlobals *kg, const int shutter_table_offset = kernel_data.cam.shutter_table_offset; ray->time = lookup_table_read(kg, time, shutter_table_offset, SHUTTER_TABLE_SIZE); /* TODO(sergey): Currently single rolling shutter effect type only - * where scan-lines are acquired from top to bottom and whole scanline + * where scan-lines are acquired from top to bottom and whole scan-line * is acquired at once (no delay in acquisition happens between pixels * of single scan-line). * diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h index ba46d84d158..eb6c94fe104 100644 --- a/intern/cycles/kernel/kernel_path.h +++ b/intern/cycles/kernel/kernel_path.h @@ -284,19 +284,11 @@ ccl_device_forceinline bool kernel_path_shader_apply(KernelGlobals *kg, #ifdef __HOLDOUT__ if (((sd->flag & SD_HOLDOUT) || (sd->object_flag & SD_OBJECT_HOLDOUT_MASK)) && (state->flag & PATH_RAY_TRANSPARENT_BACKGROUND)) { + const float3 holdout_weight = shader_holdout_apply(kg, sd); if (kernel_data.background.transparent) { - float3 holdout_weight; - if (sd->object_flag & SD_OBJECT_HOLDOUT_MASK) { - holdout_weight = make_float3(1.0f, 1.0f, 1.0f); - } - else { - holdout_weight = shader_holdout_eval(kg, sd); - } - /* any throughput is ok, should all be identical here */ L->transparent += average(holdout_weight * throughput); } - - if (sd->object_flag & SD_OBJECT_HOLDOUT_MASK) { + if (isequal_float3(holdout_weight, make_float3(1.0f, 1.0f, 1.0f))) { return false; } } diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index 3d9f787f267..e461e1642b6 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -1017,15 +1017,36 @@ ccl_device float3 shader_emissive_eval(ShaderData *sd) /* Holdout */ -ccl_device float3 shader_holdout_eval(KernelGlobals *kg, ShaderData *sd) +ccl_device float3 shader_holdout_apply(KernelGlobals *kg, ShaderData *sd) { float3 weight = make_float3(0.0f, 0.0f, 0.0f); - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; + /* For objects marked as holdout, preserve transparency and remove all other + * closures, replacing them with a holdout weight. */ + if (sd->object_flag & SD_OBJECT_HOLDOUT_MASK) { + if ((sd->flag & SD_TRANSPARENT) && !(sd->flag & SD_HAS_ONLY_VOLUME)) { + weight = make_float3(1.0f, 1.0f, 1.0f) - sd->closure_transparent_extinction; - if (CLOSURE_IS_HOLDOUT(sc->type)) - weight += sc->weight; + for (int i = 0; i < sd->num_closure; i++) { + ShaderClosure *sc = &sd->closure[i]; + if (!CLOSURE_IS_BSDF_TRANSPARENT(sc->type)) { + sc->type = NBUILTIN_CLOSURES; + } + } + + sd->flag &= ~(SD_CLOSURE_FLAGS - (SD_TRANSPARENT | SD_BSDF)); + } + else { + weight = make_float3(1.0f, 1.0f, 1.0f); + } + } + else { + for (int i = 0; i < sd->num_closure; i++) { + ShaderClosure *sc = &sd->closure[i]; + if (CLOSURE_IS_HOLDOUT(sc->type)) { + weight += sc->weight; + } + } } return weight; diff --git a/intern/cycles/kernel/kernels/optix/kernel_optix.cu b/intern/cycles/kernel/kernels/optix/kernel_optix.cu index c730d952ed4..3b166e59dfd 100644 --- a/intern/cycles/kernel/kernels/optix/kernel_optix.cu +++ b/intern/cycles/kernel/kernels/optix/kernel_optix.cu @@ -15,6 +15,7 @@ * limitations under the License. */ +// clang-format off #include "kernel/kernel_compat_optix.h" #include "util/util_atomic.h" #include "kernel/kernel_types.h" @@ -23,6 +24,7 @@ #include "kernel/kernel_path.h" #include "kernel/kernel_bake.h" +// clang-format on template<typename T> ccl_device_forceinline T *get_payload_ptr_0() { @@ -139,8 +141,8 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit() } else { if (local_isect->num_hits && optixGetRayTmax() > local_isect->hits[0].t) { - // Record closest intersection only (do not terminate ray here, since there is no guarantee - // about distance ordering in anyhit) + // Record closest intersection only + // Do not terminate ray here, since there is no guarantee about distance ordering in any-hit return optixIgnoreIntersection(); } @@ -153,15 +155,9 @@ extern "C" __global__ void __anyhit__kernel_optix_local_hit() isect->object = get_object_id(); isect->type = kernel_tex_fetch(__prim_type, isect->prim); - if (optixIsTriangleHit()) { - const float2 barycentrics = optixGetTriangleBarycentrics(); - isect->u = 1.0f - barycentrics.y - barycentrics.x; - isect->v = barycentrics.x; - } - else { - isect->u = __uint_as_float(optixGetAttribute_0()); - isect->v = __uint_as_float(optixGetAttribute_1()); - } + const float2 barycentrics = optixGetTriangleBarycentrics(); + isect->u = 1.0f - barycentrics.y - barycentrics.x; + isect->v = barycentrics.x; // Record geometric normal const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, isect->prim); @@ -198,10 +194,18 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() isect->u = 1.0f - barycentrics.y - barycentrics.x; isect->v = barycentrics.x; } +# ifdef __HAIR__ else { - isect->u = __uint_as_float(optixGetAttribute_0()); + const float u = __uint_as_float(optixGetAttribute_0()); + isect->u = u; isect->v = __uint_as_float(optixGetAttribute_1()); + + // Filter out curve endcaps + if (u == 0.0f || u == 1.0f) { + return optixIgnoreIntersection(); + } } +# endif # ifdef __TRANSPARENT_SHADOWS__ // Detect if this surface has a shader with transparent shadows @@ -213,7 +217,6 @@ extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() # ifdef __TRANSPARENT_SHADOWS__ } - // TODO(pmours): Do we need REQUIRE_UNIQUE_ANYHIT for this to work? optixSetPayload_2(optixGetPayload_2() + 1); // num_hits++ // Continue tracing @@ -227,13 +230,25 @@ extern "C" __global__ void __anyhit__kernel_optix_visibility_test() uint visibility = optixGetPayload_4(); #ifdef __VISIBILITY_FLAG__ const uint prim = optixGetPrimitiveIndex(); - if ((kernel_tex_fetch(__prim_visibility, prim) & visibility) == 0) + if ((kernel_tex_fetch(__prim_visibility, prim) & visibility) == 0) { return optixIgnoreIntersection(); + } +#endif + +#ifdef __HAIR__ + if (!optixIsTriangleHit()) { + // Filter out curve endcaps + const float u = __uint_as_float(optixGetAttribute_0()); + if (u == 0.0f || u == 1.0f) { + return optixIgnoreIntersection(); + } + } #endif // Shadow ray early termination - if (visibility & PATH_RAY_SHADOW_OPAQUE) + if (visibility & PATH_RAY_SHADOW_OPAQUE) { return optixTerminateRay(); + } } extern "C" __global__ void __closesthit__kernel_optix_hit() @@ -250,7 +265,7 @@ extern "C" __global__ void __closesthit__kernel_optix_hit() optixSetPayload_2(__float_as_uint(barycentrics.x)); } else { - optixSetPayload_1(optixGetAttribute_0()); + optixSetPayload_1(optixGetAttribute_0()); // Same as 'optixGetCurveParameter()' optixSetPayload_2(optixGetAttribute_1()); } } @@ -286,7 +301,6 @@ ccl_device_inline void optix_intersection_curve(const uint prim, const uint type __float_as_int(isect.u), // Attribute_0 __float_as_int(isect.v)); // Attribute_1 } - } extern "C" __global__ void __intersection__curve_ribbon() diff --git a/intern/cycles/kernel/shaders/node_sky_texture.osl b/intern/cycles/kernel/shaders/node_sky_texture.osl index 08bc8f85120..acb198a9852 100644 --- a/intern/cycles/kernel/shaders/node_sky_texture.osl +++ b/intern/cycles/kernel/shaders/node_sky_texture.osl @@ -122,12 +122,18 @@ vector geographical_to_direction(float lat, float lon) return vector(cos(lat) * cos(lon), cos(lat) * sin(lon), sin(lat)); } -color sky_radiance_nishita(vector dir, float nishita_data[9], string filename) +float precise_angle(vector a, vector b) +{ + return 2.0 * atan2(length(a - b), length(a + b)); +} + +color sky_radiance_nishita(vector dir, float nishita_data[10], string filename) { /* definitions */ float sun_elevation = nishita_data[6]; float sun_rotation = nishita_data[7]; float angular_diameter = nishita_data[8]; + float sun_intensity = nishita_data[9]; int sun_disc = angular_diameter > 0; float alpha = 1.0; color xyz; @@ -138,13 +144,13 @@ color sky_radiance_nishita(vector dir, float nishita_data[9], string filename) if (dir[2] >= 0.0) { /* definitions */ vector sun_dir = geographical_to_direction(sun_elevation, sun_rotation + M_PI_2); - float sun_dir_angle = acos(dot(dir, sun_dir)); + float sun_dir_angle = precise_angle(dir, sun_dir); float half_angular = angular_diameter / 2.0; float dir_elevation = M_PI_2 - direction[0]; /* if ray inside sun disc render it, otherwise render sky */ if (sun_dir_angle < half_angular && sun_disc == 1) { - /* get 3 pixels data */ + /* get 2 pixels data */ color pixel_bottom = color(nishita_data[0], nishita_data[1], nishita_data[2]); color pixel_top = color(nishita_data[3], nishita_data[4], nishita_data[5]); float y; @@ -153,13 +159,13 @@ color sky_radiance_nishita(vector dir, float nishita_data[9], string filename) if (sun_elevation - half_angular > 0.0) { if ((sun_elevation + half_angular) > 0.0) { y = ((dir_elevation - sun_elevation) / angular_diameter) + 0.5; - xyz = mix(pixel_bottom, pixel_top, y); + xyz = mix(pixel_bottom, pixel_top, y) * sun_intensity; } } else { if (sun_elevation + half_angular > 0.0) { y = dir_elevation / (sun_elevation + half_angular); - xyz = mix(pixel_bottom, pixel_top, y); + xyz = mix(pixel_bottom, pixel_top, y) * sun_intensity; } } /* limb darkening, coefficient is 0.6f */ @@ -171,7 +177,8 @@ color sky_radiance_nishita(vector dir, float nishita_data[9], string filename) else { /* sky interpolation */ float x = (direction[1] + M_PI + sun_rotation) / M_2PI; - float y = 1.0 - (dir_elevation / M_PI_2); + /* more pixels toward horizon compensation */ + float y = 1.0 - sqrt(dir_elevation / M_PI_2); if (x > 1.0) { x = x - 1.0; } @@ -201,19 +208,20 @@ color sky_radiance_nishita(vector dir, float nishita_data[9], string filename) return xyz_to_rgb(xyz[0], xyz[1], xyz[2]) * 120000.0; } -shader node_sky_texture(int use_mapping = 0, - matrix mapping = matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - vector Vector = P, - string type = "hosek_wilkie", - float theta = 0.0, - float phi = 0.0, - string filename = "", - color radiance = color(0.0, 0.0, 0.0), - float config_x[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - float config_y[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - float config_z[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - float nishita_data[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - output color Color = color(0.0, 0.0, 0.0)) +shader node_sky_texture( + int use_mapping = 0, + matrix mapping = matrix(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + vector Vector = P, + string type = "hosek_wilkie", + float theta = 0.0, + float phi = 0.0, + string filename = "", + color radiance = color(0.0, 0.0, 0.0), + float config_x[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + float config_y[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + float config_z[9] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + float nishita_data[10] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + output color Color = color(0.0, 0.0, 0.0)) { vector p = Vector; diff --git a/intern/cycles/kernel/svm/svm_sky.h b/intern/cycles/kernel/svm/svm_sky.h index e877bd9a5c8..be2c8ccdacf 100644 --- a/intern/cycles/kernel/svm/svm_sky.h +++ b/intern/cycles/kernel/svm/svm_sky.h @@ -136,6 +136,7 @@ ccl_device float3 sky_radiance_nishita(KernelGlobals *kg, float sun_elevation = nishita_data[6]; float sun_rotation = nishita_data[7]; float angular_diameter = nishita_data[8]; + float sun_intensity = nishita_data[9]; bool sun_disc = (angular_diameter > 0.0f); float3 xyz; /* convert dir to spherical coordinates */ @@ -145,13 +146,13 @@ ccl_device float3 sky_radiance_nishita(KernelGlobals *kg, if (dir.z >= 0.0f) { /* definitions */ float3 sun_dir = geographical_to_direction(sun_elevation, sun_rotation + M_PI_2_F); - float sun_dir_angle = acos(dot(dir, sun_dir)); + float sun_dir_angle = precise_angle(dir, sun_dir); float half_angular = angular_diameter / 2.0f; float dir_elevation = M_PI_2_F - direction.x; /* if ray inside sun disc render it, otherwise render sky */ if (sun_disc && sun_dir_angle < half_angular) { - /* get 3 pixels data */ + /* get 2 pixels data */ float3 pixel_bottom = make_float3(nishita_data[0], nishita_data[1], nishita_data[2]); float3 pixel_top = make_float3(nishita_data[3], nishita_data[4], nishita_data[5]); float y; @@ -160,13 +161,13 @@ ccl_device float3 sky_radiance_nishita(KernelGlobals *kg, if (sun_elevation - half_angular > 0.0f) { if (sun_elevation + half_angular > 0.0f) { y = ((dir_elevation - sun_elevation) / angular_diameter) + 0.5f; - xyz = interp(pixel_bottom, pixel_top, y); + xyz = interp(pixel_bottom, pixel_top, y) * sun_intensity; } } else { if (sun_elevation + half_angular > 0.0f) { y = dir_elevation / (sun_elevation + half_angular); - xyz = interp(pixel_bottom, pixel_top, y); + xyz = interp(pixel_bottom, pixel_top, y) * sun_intensity; } } /* limb darkening, coefficient is 0.6f */ @@ -178,7 +179,8 @@ ccl_device float3 sky_radiance_nishita(KernelGlobals *kg, else { /* sky interpolation */ float x = (direction.y + M_PI_F + sun_rotation) / M_2PI_F; - float y = dir_elevation / M_PI_2_F; + /* more pixels toward horizon compensation */ + float y = safe_sqrtf(dir_elevation / M_PI_2_F); if (x > 1.0f) { x -= 1.0f; } @@ -301,7 +303,7 @@ ccl_device void svm_node_tex_sky( /* Nishita */ else { /* Define variables */ - float nishita_data[9]; + float nishita_data[10]; float4 data = read_node_float(kg, offset); nishita_data[0] = data.x; @@ -317,7 +319,8 @@ ccl_device void svm_node_tex_sky( data = read_node_float(kg, offset); nishita_data[8] = data.x; - uint texture_id = __float_as_uint(data.y); + nishita_data[9] = data.y; + uint texture_id = __float_as_uint(data.z); /* Compute Sky */ f = sky_radiance_nishita(kg, dir, nishita_data, texture_id); diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt index e37a0407976..6a1335dc5dd 100644 --- a/intern/cycles/render/CMakeLists.txt +++ b/intern/cycles/render/CMakeLists.txt @@ -2,6 +2,7 @@ set(INC .. ../../glew-mx + ../../sky/include ) set(INC_SYS @@ -92,6 +93,7 @@ set(LIB cycles_device cycles_subd cycles_util + bf_intern_sky ) if(WITH_CYCLES_OSL) diff --git a/intern/cycles/render/buffers.h b/intern/cycles/render/buffers.h index 975bae2239c..06b6094e6c9 100644 --- a/intern/cycles/render/buffers.h +++ b/intern/cycles/render/buffers.h @@ -52,7 +52,7 @@ class BufferParams { /* passes */ vector<Pass> passes; bool denoising_data_pass; - /* If only some light path types should be denoised, an additional pass is needed. */ + /* If only some light path types should be target, an additional pass is needed. */ bool denoising_clean_pass; /* When we're prefiltering the passes during rendering, we need to keep both the * original and the prefiltered data around because neighboring tiles might still @@ -149,6 +149,50 @@ class RenderTile { RenderBuffers *buffers; RenderTile(); + + int4 bounds() const + { + return make_int4(x, /* xmin */ + y, /* ymin */ + x + w, /* xmax */ + y + h); /* ymax */ + } +}; + +/* Render Tile Neighbors + * Set of neighboring tiles used for denoising. Tile order: + * 0 1 2 + * 3 4 5 + * 6 7 8 */ + +class RenderTileNeighbors { + public: + static const int SIZE = 9; + static const int CENTER = 4; + + RenderTile tiles[SIZE]; + RenderTile target; + + RenderTileNeighbors(const RenderTile ¢er) + { + tiles[CENTER] = center; + } + + int4 bounds() const + { + return make_int4(tiles[3].x, /* xmin */ + tiles[1].y, /* ymin */ + tiles[5].x + tiles[5].w, /* xmax */ + tiles[7].y + tiles[7].h); /* ymax */ + } + + void set_bounds_from_center() + { + tiles[3].x = tiles[CENTER].x; + tiles[1].y = tiles[CENTER].y; + tiles[5].x = tiles[CENTER].x + tiles[CENTER].w; + tiles[7].y = tiles[CENTER].y + tiles[CENTER].h; + } }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/camera.cpp b/intern/cycles/render/camera.cpp index 74953afae9d..bbc111cb798 100644 --- a/intern/cycles/render/camera.cpp +++ b/intern/cycles/render/camera.cpp @@ -26,6 +26,7 @@ #include "util/util_function.h" #include "util/util_logging.h" #include "util/util_math_cdf.h" +#include "util/util_task.h" #include "util/util_vector.h" /* needed for calculating differentials */ @@ -496,20 +497,35 @@ void Camera::device_update_volume(Device * /*device*/, DeviceScene *dscene, Scen if (!need_device_update && !need_flags_update) { return; } - KernelCamera *kcam = &dscene->data.cam; - BoundBox viewplane_boundbox = viewplane_bounds_get(); - for (size_t i = 0; i < scene->objects.size(); ++i) { - Object *object = scene->objects[i]; - if (object->geometry->has_volume && viewplane_boundbox.intersects(object->bounds)) { - /* TODO(sergey): Consider adding more grained check. */ - VLOG(1) << "Detected camera inside volume."; - kcam->is_inside_volume = 1; - break; + + KernelIntegrator *kintegrator = &dscene->data.integrator; + if (kintegrator->use_volumes) { + KernelCamera *kcam = &dscene->data.cam; + BoundBox viewplane_boundbox = viewplane_bounds_get(); + + /* Parallel object update, with grain size to avoid too much threading overhead + * for individual objects. */ + static const int OBJECTS_PER_TASK = 32; + parallel_for(blocked_range<size_t>(0, scene->objects.size(), OBJECTS_PER_TASK), + [&](const blocked_range<size_t> &r) { + for (size_t i = r.begin(); i != r.end(); i++) { + Object *object = scene->objects[i]; + if (object->geometry->has_volume && + viewplane_boundbox.intersects(object->bounds)) { + /* TODO(sergey): Consider adding more grained check. */ + VLOG(1) << "Detected camera inside volume."; + kcam->is_inside_volume = 1; + parallel_for_cancel(); + break; + } + } + }); + + if (!kcam->is_inside_volume) { + VLOG(1) << "Camera is outside of the volume."; } } - if (!kcam->is_inside_volume) { - VLOG(1) << "Camera is outside of the volume."; - } + need_device_update = false; need_flags_update = false; } diff --git a/intern/cycles/render/denoising.cpp b/intern/cycles/render/denoising.cpp index 4055bc4773b..76408ca4849 100644 --- a/intern/cycles/render/denoising.cpp +++ b/intern/cycles/render/denoising.cpp @@ -271,42 +271,45 @@ bool DenoiseTask::acquire_tile(Device *device, Device *tile_device, RenderTile & * * However, since there is only one large memory, the denoised result has to be written to * a different buffer to avoid having to copy an entire horizontal slice of the image. */ -void DenoiseTask::map_neighboring_tiles(RenderTile *tiles, Device *tile_device) +void DenoiseTask::map_neighboring_tiles(RenderTileNeighbors &neighbors, Device *tile_device) { + RenderTile ¢er_tile = neighbors.tiles[RenderTileNeighbors::CENTER]; + RenderTile &target_tile = neighbors.target; + /* Fill tile information. */ - for (int i = 0; i < 9; i++) { - if (i == 4) { + for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { + if (i == RenderTileNeighbors::CENTER) { continue; } + RenderTile &tile = neighbors.tiles[i]; int dx = (i % 3) - 1; int dy = (i / 3) - 1; - tiles[i].x = clamp(tiles[4].x + dx * denoiser->tile_size.x, 0, image.width); - tiles[i].w = clamp(tiles[4].x + (dx + 1) * denoiser->tile_size.x, 0, image.width) - tiles[i].x; - tiles[i].y = clamp(tiles[4].y + dy * denoiser->tile_size.y, 0, image.height); - tiles[i].h = clamp(tiles[4].y + (dy + 1) * denoiser->tile_size.y, 0, image.height) - - tiles[i].y; + tile.x = clamp(center_tile.x + dx * denoiser->tile_size.x, 0, image.width); + tile.w = clamp(center_tile.x + (dx + 1) * denoiser->tile_size.x, 0, image.width) - tile.x; + tile.y = clamp(center_tile.y + dy * denoiser->tile_size.y, 0, image.height); + tile.h = clamp(center_tile.y + (dy + 1) * denoiser->tile_size.y, 0, image.height) - tile.y; - tiles[i].buffer = tiles[4].buffer; - tiles[i].offset = tiles[4].offset; - tiles[i].stride = image.width; + tile.buffer = center_tile.buffer; + tile.offset = center_tile.offset; + tile.stride = image.width; } /* Allocate output buffer. */ device_vector<float> *output_mem = new device_vector<float>( tile_device, "denoising_output", MEM_READ_WRITE); - output_mem->alloc(OUTPUT_NUM_CHANNELS * tiles[4].w * tiles[4].h); + output_mem->alloc(OUTPUT_NUM_CHANNELS * center_tile.w * center_tile.h); /* Fill output buffer with noisy image, assumed by kernel_filter_finalize * when skipping denoising of some pixels. */ float *result = output_mem->data(); - float *in = &image.pixels[image.num_channels * (tiles[4].y * image.width + tiles[4].x)]; + float *in = &image.pixels[image.num_channels * (center_tile.y * image.width + center_tile.x)]; const DenoiseImageLayer &layer = image.layers[current_layer]; const int *input_to_image_channel = layer.input_to_image_channel.data(); - for (int y = 0; y < tiles[4].h; y++) { - for (int x = 0; x < tiles[4].w; x++, result += OUTPUT_NUM_CHANNELS) { + for (int y = 0; y < center_tile.h; y++) { + for (int x = 0; x < center_tile.w; x++, result += OUTPUT_NUM_CHANNELS) { for (int i = 0; i < OUTPUT_NUM_CHANNELS; i++) { result[i] = in[image.num_channels * x + input_to_image_channel[INPUT_NOISY_IMAGE + i]]; } @@ -317,35 +320,38 @@ void DenoiseTask::map_neighboring_tiles(RenderTile *tiles, Device *tile_device) output_mem->copy_to_device(); /* Fill output tile info. */ - tiles[9] = tiles[4]; - tiles[9].buffer = output_mem->device_pointer; - tiles[9].stride = tiles[9].w; - tiles[9].offset -= tiles[9].x + tiles[9].y * tiles[9].stride; + target_tile = center_tile; + target_tile.buffer = output_mem->device_pointer; + target_tile.stride = target_tile.w; + target_tile.offset -= target_tile.x + target_tile.y * target_tile.stride; thread_scoped_lock output_lock(output_mutex); - assert(output_pixels.count(tiles[4].tile_index) == 0); - output_pixels[tiles[9].tile_index] = output_mem; + assert(output_pixels.count(center_tile.tile_index) == 0); + output_pixels[target_tile.tile_index] = output_mem; } -void DenoiseTask::unmap_neighboring_tiles(RenderTile *tiles) +void DenoiseTask::unmap_neighboring_tiles(RenderTileNeighbors &neighbors) { + RenderTile ¢er_tile = neighbors.tiles[RenderTileNeighbors::CENTER]; + RenderTile &target_tile = neighbors.target; + thread_scoped_lock output_lock(output_mutex); - assert(output_pixels.count(tiles[4].tile_index) == 1); - device_vector<float> *output_mem = output_pixels[tiles[9].tile_index]; - output_pixels.erase(tiles[4].tile_index); + assert(output_pixels.count(center_tile.tile_index) == 1); + device_vector<float> *output_mem = output_pixels[target_tile.tile_index]; + output_pixels.erase(center_tile.tile_index); output_lock.unlock(); /* Copy denoised pixels from device. */ - output_mem->copy_from_device(0, OUTPUT_NUM_CHANNELS * tiles[9].w, tiles[9].h); + output_mem->copy_from_device(0, OUTPUT_NUM_CHANNELS * target_tile.w, target_tile.h); float *result = output_mem->data(); - float *out = &image.pixels[image.num_channels * (tiles[9].y * image.width + tiles[9].x)]; + float *out = &image.pixels[image.num_channels * (target_tile.y * image.width + target_tile.x)]; const DenoiseImageLayer &layer = image.layers[current_layer]; const int *output_to_image_channel = layer.output_to_image_channel.data(); - for (int y = 0; y < tiles[9].h; y++) { - for (int x = 0; x < tiles[9].w; x++, result += OUTPUT_NUM_CHANNELS) { + for (int y = 0; y < target_tile.h; y++) { + for (int x = 0; x < target_tile.w; x++, result += OUTPUT_NUM_CHANNELS) { for (int i = 0; i < OUTPUT_NUM_CHANNELS; i++) { out[image.num_channels * x + output_to_image_channel[i]] = result[i]; } diff --git a/intern/cycles/render/denoising.h b/intern/cycles/render/denoising.h index 5c6f913cb38..c1b4d0a5596 100644 --- a/intern/cycles/render/denoising.h +++ b/intern/cycles/render/denoising.h @@ -196,8 +196,8 @@ class DenoiseTask { /* Device task callbacks */ bool acquire_tile(Device *device, Device *tile_device, RenderTile &tile); - void map_neighboring_tiles(RenderTile *tiles, Device *tile_device); - void unmap_neighboring_tiles(RenderTile *tiles); + void map_neighboring_tiles(RenderTileNeighbors &neighbors, Device *tile_device); + void unmap_neighboring_tiles(RenderTileNeighbors &neighbors); void release_tile(); bool get_cancel(); }; diff --git a/intern/cycles/render/image_sky.cpp b/intern/cycles/render/image_sky.cpp index 442e1d7941f..0560907c63e 100644 --- a/intern/cycles/render/image_sky.cpp +++ b/intern/cycles/render/image_sky.cpp @@ -16,16 +16,20 @@ #include "render/image_sky.h" +#include "sky_model.h" + #include "util/util_image.h" #include "util/util_logging.h" #include "util/util_path.h" -#include "util/util_sky_model.h" #include "util/util_task.h" CCL_NAMESPACE_BEGIN -SkyLoader::SkyLoader( - float sun_elevation, int altitude, float air_density, float dust_density, float ozone_density) +SkyLoader::SkyLoader(float sun_elevation, + float altitude, + float air_density, + float dust_density, + float ozone_density) : sun_elevation(sun_elevation), altitude(altitude), air_density(air_density), @@ -56,23 +60,22 @@ bool SkyLoader::load_pixels(const ImageMetaData &metadata, int width = metadata.width; int height = metadata.height; float *pixel_data = (float *)pixels; - float altitude_f = (float)altitude; /* precompute sky texture */ const int rows_per_task = divide_up(1024, width); parallel_for(blocked_range<size_t>(0, height, rows_per_task), [&](const blocked_range<size_t> &r) { - nishita_skymodel_precompute_texture(pixel_data, - metadata.channels, - r.begin(), - r.end(), - width, - height, - sun_elevation, - altitude_f, - air_density, - dust_density, - ozone_density); + SKY_nishita_skymodel_precompute_texture(pixel_data, + metadata.channels, + r.begin(), + r.end(), + width, + height, + sun_elevation, + altitude, + air_density, + dust_density, + ozone_density); }); return true; diff --git a/intern/cycles/render/image_sky.h b/intern/cycles/render/image_sky.h index cf4a3e8942c..686f4e5b885 100644 --- a/intern/cycles/render/image_sky.h +++ b/intern/cycles/render/image_sky.h @@ -21,14 +21,14 @@ CCL_NAMESPACE_BEGIN class SkyLoader : public ImageLoader { private: float sun_elevation; - int altitude; + float altitude; float air_density; float dust_density; float ozone_density; public: SkyLoader(float sun_elevation, - int altitude, + float altitude, float air_density, float dust_density, float ozone_density); diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index c0615c6217b..183c02cb6b9 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -625,13 +625,19 @@ void LightManager::device_update_background(Device *device, } } + /* Determine sun direction from lat/long and texture mapping. */ float latitude = sky->sun_elevation; float longitude = M_2PI_F - sky->sun_rotation + M_PI_2_F; + float3 sun_direction = make_float3( + cosf(latitude) * cosf(longitude), cosf(latitude) * sinf(longitude), sinf(latitude)); + Transform sky_transform = transform_inverse(sky->tex_mapping.compute_transform()); + sun_direction = transform_direction(&sky_transform, sun_direction); + + /* Pack sun direction and size. */ float half_angle = sky->sun_size * 0.5f; - kbackground->sun = make_float4(cosf(latitude) * cosf(longitude), - cosf(latitude) * sinf(longitude), - sinf(latitude), - half_angle); + kbackground->sun = make_float4( + sun_direction.x, sun_direction.y, sun_direction.z, half_angle); + kbackground->sun_weight = 4.0f; environment_res.x = max(environment_res.x, 512); environment_res.y = max(environment_res.y, 256); diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index ab392839e52..1a29663ec5e 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -27,9 +27,10 @@ #include "render/scene.h" #include "render/svm.h" +#include "sky_model.h" + #include "util/util_foreach.h" #include "util/util_logging.h" -#include "util/util_sky_model.h" #include "util/util_transform.h" #include "kernel/svm/svm_color_util.h" @@ -631,7 +632,7 @@ typedef struct SunSky { /* Parameter */ float radiance_x, radiance_y, radiance_z; - float config_x[9], config_y[9], config_z[9], nishita_data[9]; + float config_x[9], config_y[9], config_z[9], nishita_data[10]; } SunSky; /* Preetham model */ @@ -726,8 +727,8 @@ static void sky_texture_precompute_hosek(SunSky *sunsky, float solarElevation = M_PI_2_F - theta; /* Initialize Sky Model */ - ArHosekSkyModelState *sky_state; - sky_state = arhosek_xyz_skymodelstate_alloc_init( + SKY_ArHosekSkyModelState *sky_state; + sky_state = SKY_arhosek_xyz_skymodelstate_alloc_init( (double)turbidity, (double)ground_albedo, (double)solarElevation); /* Copy values from sky_state to SunSky */ @@ -741,25 +742,31 @@ static void sky_texture_precompute_hosek(SunSky *sunsky, sunsky->radiance_z = (float)sky_state->radiances[2]; /* Free sky_state */ - arhosekskymodelstate_free(sky_state); + SKY_arhosekskymodelstate_free(sky_state); } /* Nishita improved */ static void sky_texture_precompute_nishita(SunSky *sunsky, bool sun_disc, float sun_size, + float sun_intensity, float sun_elevation, float sun_rotation, - int altitude, + float altitude, float air_density, float dust_density) { /* sample 2 sun pixels */ float pixel_bottom[3]; float pixel_top[3]; - float altitude_f = (float)altitude; - nishita_skymodel_precompute_sun( - sun_elevation, sun_size, altitude_f, air_density, dust_density, pixel_bottom, pixel_top); + SKY_nishita_skymodel_precompute_sun( + sun_elevation, sun_size, altitude, air_density, dust_density, pixel_bottom, pixel_top); + /* limit sun rotation between 0 and 360 degrees */ + sun_rotation = fmodf(sun_rotation, M_2PI_F); + if (sun_rotation < 0.0f) { + sun_rotation += M_2PI_F; + } + sun_rotation = M_2PI_F - sun_rotation; /* send data to svm_sky */ sunsky->nishita_data[0] = pixel_bottom[0]; sunsky->nishita_data[1] = pixel_bottom[1]; @@ -768,8 +775,9 @@ static void sky_texture_precompute_nishita(SunSky *sunsky, sunsky->nishita_data[4] = pixel_top[1]; sunsky->nishita_data[5] = pixel_top[2]; sunsky->nishita_data[6] = sun_elevation; - sunsky->nishita_data[7] = M_2PI_F - sun_rotation; + sunsky->nishita_data[7] = sun_rotation; sunsky->nishita_data[8] = sun_disc ? sun_size : 0.0f; + sunsky->nishita_data[9] = sun_intensity; } NODE_DEFINE(SkyTextureNode) @@ -789,9 +797,10 @@ NODE_DEFINE(SkyTextureNode) SOCKET_FLOAT(ground_albedo, "Ground Albedo", 0.3f); SOCKET_BOOLEAN(sun_disc, "Sun Disc", true); SOCKET_FLOAT(sun_size, "Sun Size", 0.009512f); + SOCKET_FLOAT(sun_intensity, "Sun Intensity", 1.0f); SOCKET_FLOAT(sun_elevation, "Sun Elevation", M_PI_2_F); SOCKET_FLOAT(sun_rotation, "Sun Rotation", 0.0f); - SOCKET_INT(altitude, "Altitude", 0); + SOCKET_FLOAT(altitude, "Altitude", 1.0f); SOCKET_FLOAT(air_density, "Air", 1.0f); SOCKET_FLOAT(dust_density, "Dust", 1.0f); SOCKET_FLOAT(ozone_density, "Ozone", 1.0f); @@ -819,12 +828,17 @@ void SkyTextureNode::compile(SVMCompiler &compiler) else if (type == NODE_SKY_HOSEK) sky_texture_precompute_hosek(&sunsky, sun_direction, turbidity, ground_albedo); else if (type == NODE_SKY_NISHITA) { + /* Clamp altitude to reasonable values. + * Below 1m causes numerical issues and above 60km is space. */ + float clamped_altitude = clamp(altitude, 1.0f, 59999.0f); + sky_texture_precompute_nishita(&sunsky, sun_disc, sun_size, + sun_intensity, sun_elevation, sun_rotation, - altitude, + clamped_altitude, air_density, dust_density); /* precomputed texture image parameters */ @@ -836,7 +850,7 @@ void SkyTextureNode::compile(SVMCompiler &compiler) /* precompute sky texture */ if (handle.empty()) { SkyLoader *loader = new SkyLoader( - sun_elevation, altitude, air_density, dust_density, ozone_density); + sun_elevation, clamped_altitude, air_density, dust_density, ozone_density); handle = image_manager->add_image(loader, impar); } } @@ -891,7 +905,10 @@ void SkyTextureNode::compile(SVMCompiler &compiler) __float_as_uint(sunsky.nishita_data[5]), __float_as_uint(sunsky.nishita_data[6]), __float_as_uint(sunsky.nishita_data[7])); - compiler.add_node(__float_as_uint(sunsky.nishita_data[8]), handle.svm_slot(), 0, 0); + compiler.add_node(__float_as_uint(sunsky.nishita_data[8]), + __float_as_uint(sunsky.nishita_data[9]), + handle.svm_slot(), + 0); } tex_mapping.compile_end(compiler, vector_in, vector_offset); @@ -907,12 +924,17 @@ void SkyTextureNode::compile(OSLCompiler &compiler) else if (type == NODE_SKY_HOSEK) sky_texture_precompute_hosek(&sunsky, sun_direction, turbidity, ground_albedo); else if (type == NODE_SKY_NISHITA) { + /* Clamp altitude to reasonable values. + * Below 1m causes numerical issues and above 60km is space. */ + float clamped_altitude = clamp(altitude, 1.0f, 59999.0f); + sky_texture_precompute_nishita(&sunsky, sun_disc, sun_size, + sun_intensity, sun_elevation, sun_rotation, - altitude, + clamped_altitude, air_density, dust_density); /* precomputed texture image parameters */ @@ -924,7 +946,7 @@ void SkyTextureNode::compile(OSLCompiler &compiler) /* precompute sky texture */ if (handle.empty()) { SkyLoader *loader = new SkyLoader( - sun_elevation, altitude, air_density, dust_density, ozone_density); + sun_elevation, clamped_altitude, air_density, dust_density, ozone_density); handle = image_manager->add_image(loader, impar); } } @@ -939,7 +961,7 @@ void SkyTextureNode::compile(OSLCompiler &compiler) compiler.parameter_array("config_x", sunsky.config_x, 9); compiler.parameter_array("config_y", sunsky.config_y, 9); compiler.parameter_array("config_z", sunsky.config_z, 9); - compiler.parameter_array("nishita_data", sunsky.nishita_data, 9); + compiler.parameter_array("nishita_data", sunsky.nishita_data, 10); /* nishita texture */ if (type == NODE_SKY_NISHITA) { compiler.parameter_texture("filename", handle.svm_slot()); diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 846ba7423e5..326f1d14168 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -170,9 +170,10 @@ class SkyTextureNode : public TextureNode { float ground_albedo; bool sun_disc; float sun_size; + float sun_intensity; float sun_elevation; float sun_rotation; - int altitude; + float altitude; float air_density; float dust_density; float ozone_density; diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index c45ae5553a8..f200e409b9e 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -823,6 +823,12 @@ void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, P Mesh *mesh = static_cast<Mesh *>(geom); apply = apply && mesh->subdivision_type == Mesh::SUBDIVISION_NONE; } + else if (geom->type == Geometry::HAIR) { + /* Can't apply non-uniform scale to curves, this can't be represented by + * control points and radius alone. */ + float scale; + apply = apply && transform_uniform_scale(object->tfm, scale); + } if (apply) { if (!(motion_blur && object->use_motion())) { diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 1a94d3e9db7..c5033359c6b 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -536,7 +536,7 @@ void Session::release_tile(RenderTile &rtile, const bool need_denoise) denoising_cond.notify_all(); } -void Session::map_neighbor_tiles(RenderTile *tiles, Device *tile_device) +void Session::map_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device) { thread_scoped_lock tile_lock(tile_mutex); @@ -546,75 +546,77 @@ void Session::map_neighbor_tiles(RenderTile *tiles, Device *tile_device) tile_manager.state.buffer.full_x + tile_manager.state.buffer.width, tile_manager.state.buffer.full_y + tile_manager.state.buffer.height); + RenderTile ¢er_tile = neighbors.tiles[RenderTileNeighbors::CENTER]; + if (!tile_manager.schedule_denoising) { /* Fix up tile slices with overlap. */ if (tile_manager.slice_overlap != 0) { - int y = max(tiles[4].y - tile_manager.slice_overlap, image_region.y); - tiles[4].h = min(tiles[4].y + tiles[4].h + tile_manager.slice_overlap, image_region.w) - y; - tiles[4].y = y; + int y = max(center_tile.y - tile_manager.slice_overlap, image_region.y); + center_tile.h = min(center_tile.y + center_tile.h + tile_manager.slice_overlap, + image_region.w) - + y; + center_tile.y = y; } /* Tiles are not being denoised individually, which means the entire image is processed. */ - tiles[3].x = tiles[4].x; - tiles[1].y = tiles[4].y; - tiles[5].x = tiles[4].x + tiles[4].w; - tiles[7].y = tiles[4].y + tiles[4].h; + neighbors.set_bounds_from_center(); } else { - int center_idx = tiles[4].tile_index; + int center_idx = center_tile.tile_index; assert(tile_manager.state.tiles[center_idx].state == Tile::DENOISE); for (int dy = -1, i = 0; dy <= 1; dy++) { for (int dx = -1; dx <= 1; dx++, i++) { + RenderTile &rtile = neighbors.tiles[i]; int nindex = tile_manager.get_neighbor_index(center_idx, i); if (nindex >= 0) { Tile *tile = &tile_manager.state.tiles[nindex]; - tiles[i].x = image_region.x + tile->x; - tiles[i].y = image_region.y + tile->y; - tiles[i].w = tile->w; - tiles[i].h = tile->h; + rtile.x = image_region.x + tile->x; + rtile.y = image_region.y + tile->y; + rtile.w = tile->w; + rtile.h = tile->h; if (buffers) { - tile_manager.state.buffer.get_offset_stride(tiles[i].offset, tiles[i].stride); + tile_manager.state.buffer.get_offset_stride(rtile.offset, rtile.stride); - tiles[i].buffer = buffers->buffer.device_pointer; - tiles[i].buffers = buffers; + rtile.buffer = buffers->buffer.device_pointer; + rtile.buffers = buffers; } else { assert(tile->buffers); - tile->buffers->params.get_offset_stride(tiles[i].offset, tiles[i].stride); + tile->buffers->params.get_offset_stride(rtile.offset, rtile.stride); - tiles[i].buffer = tile->buffers->buffer.device_pointer; - tiles[i].buffers = tile->buffers; + rtile.buffer = tile->buffers->buffer.device_pointer; + rtile.buffers = tile->buffers; } } else { - int px = tiles[4].x + dx * params.tile_size.x; - int py = tiles[4].y + dy * params.tile_size.y; + int px = center_tile.x + dx * params.tile_size.x; + int py = center_tile.y + dy * params.tile_size.y; - tiles[i].x = clamp(px, image_region.x, image_region.z); - tiles[i].y = clamp(py, image_region.y, image_region.w); - tiles[i].w = tiles[i].h = 0; + rtile.x = clamp(px, image_region.x, image_region.z); + rtile.y = clamp(py, image_region.y, image_region.w); + rtile.w = rtile.h = 0; - tiles[i].buffer = (device_ptr)NULL; - tiles[i].buffers = NULL; + rtile.buffer = (device_ptr)NULL; + rtile.buffers = NULL; } } } } - assert(tiles[4].buffers); - device->map_neighbor_tiles(tile_device, tiles); + assert(center_tile.buffers); + device->map_neighbor_tiles(tile_device, neighbors); /* The denoised result is written back to the original tile. */ - tiles[9] = tiles[4]; + neighbors.target = center_tile; } -void Session::unmap_neighbor_tiles(RenderTile *tiles, Device *tile_device) +void Session::unmap_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device) { thread_scoped_lock tile_lock(tile_mutex); - device->unmap_neighbor_tiles(tile_device, tiles); + device->unmap_neighbor_tiles(tile_device, neighbors); } void Session::run_cpu() @@ -1003,7 +1005,7 @@ bool Session::update_scene() int height = tile_manager.state.buffer.full_height; int resolution = tile_manager.state.resolution_divider; - if (width != cam->width || height != cam->height) { + if (width != cam->width || height != cam->height || resolution != cam->resolution) { cam->width = width; cam->height = height; cam->resolution = resolution; @@ -1126,6 +1128,11 @@ bool Session::render_need_denoise(bool &delayed) { delayed = false; + /* Not supported yet for baking. */ + if (read_bake_tile_cb) { + return false; + } + /* Denoising enabled? */ if (!params.denoising.need_denoising_task()) { return false; diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h index 0141629762c..e3ac054ead3 100644 --- a/intern/cycles/render/session.h +++ b/intern/cycles/render/session.h @@ -198,8 +198,8 @@ class Session { void update_tile_sample(RenderTile &tile); void release_tile(RenderTile &tile, const bool need_denoise); - void map_neighbor_tiles(RenderTile *tiles, Device *tile_device); - void unmap_neighbor_tiles(RenderTile *tiles, Device *tile_device); + void map_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device); + void unmap_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device); bool device_use_gl; diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt index ad4ea9c86e0..23a47e064e2 100644 --- a/intern/cycles/util/CMakeLists.txt +++ b/intern/cycles/util/CMakeLists.txt @@ -98,10 +98,6 @@ set(SRC_HEADERS util_rect.h util_set.h util_simd.h - util_sky_model.cpp - util_sky_model.h - util_sky_model_data.h - util_sky_nishita.cpp util_avxf.h util_avxb.h util_semaphore.h diff --git a/intern/cycles/util/util_debug.cpp b/intern/cycles/util/util_debug.cpp index 6ad4f709ab5..74ecefa1917 100644 --- a/intern/cycles/util/util_debug.cpp +++ b/intern/cycles/util/util_debug.cpp @@ -83,6 +83,7 @@ DebugFlags::OptiX::OptiX() void DebugFlags::OptiX::reset() { cuda_streams = 1; + curves_api = false; } DebugFlags::OpenCL::OpenCL() : device_type(DebugFlags::OpenCL::DEVICE_ALL), debug(false) diff --git a/intern/cycles/util/util_debug.h b/intern/cycles/util/util_debug.h index da9f5408b59..6ac4beb55b8 100644 --- a/intern/cycles/util/util_debug.h +++ b/intern/cycles/util/util_debug.h @@ -108,6 +108,9 @@ class DebugFlags { /* Number of CUDA streams to launch kernels concurrently from. */ int cuda_streams; + + /* Use OptiX curves API for hair instead of custom implementation. */ + bool curves_api; }; /* Descriptor of OpenCL feature-set to be used. */ diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h index 737c834e073..8caabf6eac3 100644 --- a/intern/cycles/util/util_math.h +++ b/intern/cycles/util/util_math.h @@ -787,6 +787,16 @@ ccl_device_inline float compare_floats(float a, float b, float abs_diff, int ulp return (abs(__float_as_int(a) - __float_as_int(b)) < ulp_diff); } +/* Calculate the angle between the two vectors a and b. + * The usual approach acos(dot(a, b)) has severe precision issues for small angles, + * which are avoided by this method. + * Based on "Mangled Angles" from https://people.eecs.berkeley.edu/~wkahan/Mindless.pdf + */ +ccl_device_inline float precise_angle(float3 a, float3 b) +{ + return 2.0f * atan2f(len(a - b), len(a + b)); +} + CCL_NAMESPACE_END #endif /* __UTIL_MATH_H__ */ diff --git a/intern/cycles/util/util_tbb.h b/intern/cycles/util/util_tbb.h index 301cb80c5b0..206ba106ca6 100644 --- a/intern/cycles/util/util_tbb.h +++ b/intern/cycles/util/util_tbb.h @@ -34,6 +34,11 @@ using tbb::blocked_range; using tbb::enumerable_thread_specific; using tbb::parallel_for; +static inline void parallel_for_cancel() +{ + tbb::task::self().cancel_group_execution(); +} + CCL_NAMESPACE_END #endif /* __UTIL_TBB_H__ */ diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index c7737392e7b..92061f55128 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -362,8 +362,8 @@ extern GHOST_TSuccess GHOST_HasCursorShape(GHOST_WindowHandle windowhandle, * \param mask The mask data for the cursor. * \param sizex The width of the cursor * \param sizey The height of the cursor - * \param hotX The X coordinate of the cursor hotspot. - * \param hotY The Y coordinate of the cursor hotspot. + * \param hotX The X coordinate of the cursor hot-spot. + * \param hotY The Y coordinate of the cursor hot-spot. * \param canInvertColor Let macOS invert cursor color to match platform convention. * \return Indication of success. */ diff --git a/intern/ghost/GHOST_IWindow.h b/intern/ghost/GHOST_IWindow.h index 62290d20f1c..9c72b6f07f9 100644 --- a/intern/ghost/GHOST_IWindow.h +++ b/intern/ghost/GHOST_IWindow.h @@ -287,8 +287,8 @@ class GHOST_IWindow { * Set the shape of the cursor to a custom cursor. * \param bitmap The bitmap data for the cursor. * \param mask The mask data for the cursor. - * \param hotX The X coordinate of the cursor hotspot. - * \param hotY The Y coordinate of the cursor hotspot. + * \param hotX The X coordinate of the cursor hot-spot. + * \param hotY The Y coordinate of the cursor hot-spot. * \return Indication of success. */ virtual GHOST_TSuccess setCustomCursorShape(GHOST_TUns8 *bitmap, diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 5c1f34e3a63..96073c21e79 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -1324,7 +1324,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) int revert_to; /* as ICCCM say, we need reply this event - * with a SetInputFocus, the data[1] have + * with a #SetInputFocus, the data[1] have * the valid timestamp (send by the wm). * * Some WM send this event before the @@ -1345,7 +1345,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) else { #ifdef WITH_XDND /* try to handle drag event - * (if there's no such events, GHOST_HandleClientMessage will return zero) */ + * (if there's no such events, #GHOST_HandleClientMessage will return zero) */ if (window->getDropTarget()->GHOST_HandleClientMessage(xe) == false) { /* Unknown client message, ignore */ } @@ -1366,12 +1366,12 @@ void GHOST_SystemX11::processEvent(XEvent *xe) case EnterNotify: case LeaveNotify: { - /* XCrossingEvents pointer leave enter window. - * also do cursor move here, MotionNotify only + /* #XCrossingEvents pointer leave enter window. + * also do cursor move here, #MotionNotify only * happens when motion starts & ends inside window. * we only do moves when the crossing mode is 'normal' - * (really crossing between windows) since some windowmanagers - * also send grab/ungrab crossings for mousewheel events. + * (really crossing between windows) since some window-managers + * also send grab/un-grab crossings for mouse-wheel events. */ XCrossingEvent &xce = xe->xcrossing; if (xce.mode == NotifyNormal) { @@ -1396,11 +1396,11 @@ void GHOST_SystemX11::processEvent(XEvent *xe) case MapNotify: /* * From ICCCM: - * [ Clients can select for StructureNotify on their + * [ Clients can select for #StructureNotify on their * top-level windows to track transition between - * Normal and Iconic states. Receipt of a MapNotify + * Normal and Iconic states. Receipt of a #MapNotify * event will indicate a transition to the Normal - * state, and receipt of an UnmapNotify event will + * state, and receipt of an #UnmapNotify event will * indicate a transition to the Iconic state. ] */ if (window->m_post_init == True) { @@ -1441,7 +1441,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) nxe.xselection.target = xse->target; nxe.xselection.time = xse->time; - /* Check to see if the requestor is asking for String */ + /* Check to see if the requester is asking for String */ if (xse->target == utf8_string || xse->target == string || xse->target == compound_text || xse->target == c_string) { if (xse->selection == XInternAtom(m_display, "PRIMARY", False)) { @@ -1487,7 +1487,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) nxe.xselection.property = None; } - /* Send the event to the client 0 0 == False, SelectionNotify */ + /* Send the event to the client 0 0 == False, #SelectionNotify */ XSendEvent(m_display, xse->requestor, 0, 0, &nxe); XFlush(m_display); break; @@ -1513,7 +1513,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) /* Note: This event might be generated with incomplete data-set * (don't exactly know why, looks like in some cases, if the value does not change, - * it is not included in subsequent XDeviceMotionEvent events). + * it is not included in subsequent #XDeviceMotionEvent events). * So we have to check which values this event actually contains! */ @@ -1569,14 +1569,13 @@ void GHOST_SystemX11::processEvent(XEvent *xe) GHOST_TSuccess GHOST_SystemX11::getModifierKeys(GHOST_ModifierKeys &keys) const { - /* Analyze the masks returned from XQueryPointer. */ + /* Analyze the masks returned from #XQueryPointer. */ memset((void *)m_keyboard_vector, 0, sizeof(m_keyboard_vector)); XQueryKeymap(m_display, (char *)m_keyboard_vector); - /* now translate key symbols into keycodes and - * test with vector. */ + /* Now translate key symbols into key-codes and test with vector. */ const static KeyCode shift_l = XKeysymToKeycode(m_display, XK_Shift_L); const static KeyCode shift_r = XKeysymToKeycode(m_display, XK_Shift_R); @@ -1671,7 +1670,7 @@ GHOST_TSuccess GHOST_SystemX11::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y { /* This is a brute force move in screen coordinates - * XWarpPointer does relative moves so first determine the + * #XWarpPointer does relative moves so first determine the * current pointer position. */ int cx, cy; diff --git a/intern/ghost/intern/GHOST_Window.h b/intern/ghost/intern/GHOST_Window.h index 6738aa850ce..7cfea5110c5 100644 --- a/intern/ghost/intern/GHOST_Window.h +++ b/intern/ghost/intern/GHOST_Window.h @@ -124,8 +124,8 @@ class GHOST_Window : public GHOST_IWindow { * Set the shape of the cursor to a custom cursor. * \param bitmap The bitmap data for the cursor. * \param mask The mask data for the cursor. - * \param hotX The X coordinate of the cursor hotspot. - * \param hotY The Y coordinate of the cursor hotspot. + * \param hotX The X coordinate of the cursor hot-spot. + * \param hotY The Y coordinate of the cursor hot-spot. * \return Indication of success. */ GHOST_TSuccess setCustomCursorShape(GHOST_TUns8 *bitmap, diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h index 55ea1d0bb70..1318aa10697 100644 --- a/intern/guardedalloc/MEM_guardedalloc.h +++ b/intern/guardedalloc/MEM_guardedalloc.h @@ -243,6 +243,11 @@ void MEM_use_guarded_allocator(void); void *operator new(size_t /*count*/, void *ptr) \ { \ return ptr; \ + } \ + /* This is the matching delete operator to the placement-new operator above. Both parameters \ + * will have the same value. Without this, we get the warning C4291 on windows. */ \ + void operator delete(void * /*ptr_to_free*/, void * /*ptr*/) \ + { \ } /* Needed when type includes a namespace, then the namespace should not be diff --git a/intern/guardedalloc/test/simpletest/memtest.c b/intern/guardedalloc/test/simpletest/memtest.c index 5c37ceb5b24..f7a9a785f5a 100644 --- a/intern/guardedalloc/test/simpletest/memtest.c +++ b/intern/guardedalloc/test/simpletest/memtest.c @@ -148,7 +148,7 @@ int main(int argc, char *argv[]) fprintf(stderr, "|\n|--* Errors were detected\n"); } else { - fprintf(stderr, "|\n|--* Test exited succesfully\n"); + fprintf(stderr, "|\n|--* Test exited successfully\n"); } fprintf(stderr, "|\n*** Finished test\n\n"); diff --git a/intern/libc_compat/libc_compat.c b/intern/libc_compat/libc_compat.c index 8da3ca218af..78e387e3117 100644 --- a/intern/libc_compat/libc_compat.c +++ b/intern/libc_compat/libc_compat.c @@ -28,6 +28,7 @@ # if defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 31) double __exp_finite(double x); +double __exp2_finite(double x); double __acos_finite(double x); double __asin_finite(double x); double __log2_finite(double x); @@ -35,6 +36,7 @@ double __log10_finite(double x); double __log_finite(double x); double __pow_finite(double x, double y); float __expf_finite(float x); +float __exp2f_finite(float x); float __acosf_finite(float x); float __asinf_finite(float x); float __log2f_finite(float x); @@ -47,6 +49,11 @@ double __exp_finite(double x) return exp(x); } +double __exp2_finite(double x) +{ + return exp2(x); +} + double __acos_finite(double x) { return acos(x); @@ -82,6 +89,11 @@ float __expf_finite(float x) return expf(x); } +float __exp2f_finite(float x) +{ + return exp2f(x); +} + float __acosf_finite(float x) { return acosf(x); diff --git a/intern/libmv/libmv/simple_pipeline/pipeline.cc b/intern/libmv/libmv/simple_pipeline/pipeline.cc index 6c8592baa00..728601f3732 100644 --- a/intern/libmv/libmv/simple_pipeline/pipeline.cc +++ b/intern/libmv/libmv/simple_pipeline/pipeline.cc @@ -316,8 +316,8 @@ double InternalReprojectionError( } LG << "Skipped " << num_skipped << " markers."; LG << "Reprojected " << num_reprojected << " markers."; - LG << "Total error: " << total_error; - LG << "Average error: " << (total_error / num_reprojected) << " [pixels]."; + LG << "Total error: " << total_error << " px"; + LG << "Average error: " << (total_error / num_reprojected) << " px"; return total_error / num_reprojected; } diff --git a/intern/mantaflow/intern/MANTA_main.cpp b/intern/mantaflow/intern/MANTA_main.cpp index 43121f08f2d..676a2fd785e 100644 --- a/intern/mantaflow/intern/MANTA_main.cpp +++ b/intern/mantaflow/intern/MANTA_main.cpp @@ -870,9 +870,9 @@ void MANTA::initializeRNAMap(FluidModifierData *fmd) mRNAMap["GUIDING_ALPHA"] = to_string(fds->guide_alpha); mRNAMap["GUIDING_BETA"] = to_string(fds->guide_beta); mRNAMap["GUIDING_FACTOR"] = to_string(fds->guide_vel_factor); - mRNAMap["GRAVITY_X"] = to_string(fds->gravity[0]); - mRNAMap["GRAVITY_Y"] = to_string(fds->gravity[1]); - mRNAMap["GRAVITY_Z"] = to_string(fds->gravity[2]); + mRNAMap["GRAVITY_X"] = to_string(fds->gravity_final[0]); + mRNAMap["GRAVITY_Y"] = to_string(fds->gravity_final[1]); + mRNAMap["GRAVITY_Z"] = to_string(fds->gravity_final[2]); mRNAMap["CACHE_DIR"] = cacheDirectory; mRNAMap["COMPRESSION_OPENVDB"] = vdbCompressionMethod; mRNAMap["PRECISION_OPENVDB"] = vdbPrecisionHalf; @@ -1256,6 +1256,7 @@ bool MANTA::readData(FluidModifierData *fmd, int framenr, bool resumable) << ", '" << volume_format << "', " << resumable_cache << ")"; pythonCommands.push_back(ss.str()); result &= runPythonString(pythonCommands); + return (mSmokeFromFile = result); } if (mUsingLiquid) { ss.str(""); @@ -1263,6 +1264,7 @@ bool MANTA::readData(FluidModifierData *fmd, int framenr, bool resumable) << ", '" << volume_format << "', " << resumable_cache << ")"; pythonCommands.push_back(ss.str()); result &= runPythonString(pythonCommands); + return (mFlipFromFile = result); } return result; } @@ -1296,7 +1298,7 @@ bool MANTA::readNoise(FluidModifierData *fmd, int framenr, bool resumable) << ", '" << volume_format << "', " << resumable_cache << ")"; pythonCommands.push_back(ss.str()); - return runPythonString(pythonCommands); + return (mNoiseFromFile = runPythonString(pythonCommands)); } bool MANTA::readMesh(FluidModifierData *fmd, int framenr) @@ -1331,7 +1333,7 @@ bool MANTA::readMesh(FluidModifierData *fmd, int framenr) pythonCommands.push_back(ss.str()); } - return runPythonString(pythonCommands); + return (mMeshFromFile = runPythonString(pythonCommands)); } bool MANTA::readParticles(FluidModifierData *fmd, int framenr, bool resumable) @@ -1365,7 +1367,7 @@ bool MANTA::readParticles(FluidModifierData *fmd, int framenr, bool resumable) << framenr << ", '" << volume_format << "', " << resumable_cache << ")"; pythonCommands.push_back(ss.str()); - return runPythonString(pythonCommands); + return (mParticlesFromFile = runPythonString(pythonCommands)); } bool MANTA::readGuiding(FluidModifierData *fmd, int framenr, bool sourceDomain) diff --git a/intern/mantaflow/intern/strings/fluid_script.h b/intern/mantaflow/intern/strings/fluid_script.h index 977b99e7759..4ee3ae59957 100644 --- a/intern/mantaflow/intern/strings/fluid_script.h +++ b/intern/mantaflow/intern/strings/fluid_script.h @@ -146,19 +146,19 @@ mantaMsg('1 Mantaflow cell is ' + str(ratioMetersToRes_s$ID$) + ' Blender length ratioResToBLength_s$ID$ = float(res_s$ID$) / float(domainSize_s$ID$) # [cells / blength] (blength: cm, m, or km, ... )\n\ mantaMsg('1 Blender length unit is ' + str(ratioResToBLength_s$ID$) + ' Mantaflow cells long.')\n\ \n\ -ratioBTimeToTimstep_s$ID$ = float(1) / float(frameLengthRaw_s$ID$) # the time within 1 blender time unit, see also fluid.c\n\ -mantaMsg('1 Blender time unit is ' + str(ratioBTimeToTimstep_s$ID$) + ' Mantaflow time units long.')\n\ +ratioBTimeToTimestep_s$ID$ = float(1) / float(frameLengthRaw_s$ID$) # the time within 1 blender time unit, see also fluid.c\n\ +mantaMsg('1 Blender time unit is ' + str(ratioBTimeToTimestep_s$ID$) + ' Mantaflow time units long.')\n\ \n\ ratioFrameToFramelength_s$ID$ = float(1) / float(frameLengthUnscaled_s$ID$ ) # the time within 1 frame\n\ mantaMsg('frame / frameLength is ' + str(ratioFrameToFramelength_s$ID$) + ' Mantaflow time units long.')\n\ \n\ -scaleAcceleration_s$ID$ = ratioResToBLength_s$ID$ * (ratioBTimeToTimstep_s$ID$**2)# [meters/btime^2] to [cells/timestep^2] (btime: sec, min, or h, ...)\n\ +scaleAcceleration_s$ID$ = ratioResToBLength_s$ID$ * (ratioBTimeToTimestep_s$ID$**2)# [meters/btime^2] to [cells/timestep^2] (btime: sec, min, or h, ...)\n\ mantaMsg('scaleAcceleration is ' + str(scaleAcceleration_s$ID$))\n\ \n\ scaleSpeedFrames_s$ID$ = ratioResToBLength_s$ID$ * ratioFrameToFramelength_s$ID$ # [blength/frame] to [cells/frameLength]\n\ mantaMsg('scaleSpeed is ' + str(scaleSpeedFrames_s$ID$))\n\ \n\ -scaleSpeedTime_s$ID$ = ratioResToBLength_s$ID$ * ratioBTimeToTimstep_s$ID$ # [blength/btime] to [cells/frameLength]\n\ +scaleSpeedTime_s$ID$ = ratioResToBLength_s$ID$ * ratioBTimeToTimestep_s$ID$ # [blength/btime] to [cells/frameLength]\n\ mantaMsg('scaleSpeedTime is ' + str(scaleSpeedTime_s$ID$))\n\ \n\ gravity_s$ID$ *= scaleAcceleration_s$ID$ # scale from world acceleration to cell based acceleration\n\ diff --git a/intern/sky/CMakeLists.txt b/intern/sky/CMakeLists.txt new file mode 100644 index 00000000000..d46880367dc --- /dev/null +++ b/intern/sky/CMakeLists.txt @@ -0,0 +1,35 @@ +# ***** 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(INC + include +) + +set(INC_SYS + +) + +set(SRC + source/sky_model.cpp + source/sky_nishita.cpp +) + +set(LIB +) + +blender_add_lib(bf_intern_sky "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/intern/cycles/util/util_sky_model.h b/intern/sky/include/sky_model.h index 36f1079a16d..021086e1e02 100644 --- a/intern/cycles/util/util_sky_model.h +++ b/intern/sky/include/sky_model.h @@ -298,14 +298,14 @@ HINT #1: if you want to model the sky of an earth-like planet that orbits previous paragraph. */ -#include "util/util_types.h" +#ifndef __SKY_MODEL_H__ +#define __SKY_MODEL_H__ -CCL_NAMESPACE_BEGIN +#ifdef __cplusplus +extern "C" { +#endif -#ifndef _SKY_MODEL_H_ -# define _SKY_MODEL_H_ - -typedef double ArHosekSkyModelConfiguration[9]; +typedef double SKY_ArHosekSkyModelConfiguration[9]; // Spectral version of the model @@ -335,8 +335,8 @@ typedef double ArHosekSkyModelConfiguration[9]; ---------------------------------------------------------------------------- */ -typedef struct ArHosekSkyModelState { - ArHosekSkyModelConfiguration configs[11]; +typedef struct SKY_ArHosekSkyModelState { + SKY_ArHosekSkyModelConfiguration configs[11]; double radiances[11]; double turbidity; double solar_radius; @@ -344,7 +344,7 @@ typedef struct ArHosekSkyModelState { double emission_correction_factor_sun[11]; double albedo; double elevation; -} ArHosekSkyModelState; +} SKY_ArHosekSkyModelState; /* ---------------------------------------------------------------------------- @@ -355,9 +355,9 @@ typedef struct ArHosekSkyModelState { ---------------------------------------------------------------------------- */ -ArHosekSkyModelState *arhosekskymodelstate_alloc_init(const double solar_elevation, - const double atmospheric_turbidity, - const double ground_albedo); +SKY_ArHosekSkyModelState *SKY_arhosekskymodelstate_alloc_init(const double solar_elevation, + const double atmospheric_turbidity, + const double ground_albedo); /* ---------------------------------------------------------------------------- @@ -388,66 +388,68 @@ ArHosekSkyModelState *arhosekskymodelstate_alloc_init(const double solar_elevati ---------------------------------------------------------------------------- */ -ArHosekSkyModelState *arhosekskymodelstate_alienworld_alloc_init( +SKY_ArHosekSkyModelState *SKY_arhosekskymodelstate_alienworld_alloc_init( const double solar_elevation, const double solar_intensity, const double solar_surface_temperature_kelvin, const double atmospheric_turbidity, const double ground_albedo); -void arhosekskymodelstate_free(ArHosekSkyModelState *state); +void SKY_arhosekskymodelstate_free(SKY_ArHosekSkyModelState *state); -double arhosekskymodel_radiance(ArHosekSkyModelState *state, - double theta, - double gamma, - double wavelength); +double SKY_arhosekskymodel_radiance(SKY_ArHosekSkyModelState *state, + double theta, + double gamma, + double wavelength); // CIE XYZ and RGB versions -ArHosekSkyModelState *arhosek_xyz_skymodelstate_alloc_init(const double turbidity, - const double albedo, - const double elevation); +SKY_ArHosekSkyModelState *SKY_arhosek_xyz_skymodelstate_alloc_init(const double turbidity, + const double albedo, + const double elevation); -ArHosekSkyModelState *arhosek_rgb_skymodelstate_alloc_init(const double turbidity, - const double albedo, - const double elevation); +SKY_ArHosekSkyModelState *SKY_arhosek_rgb_skymodelstate_alloc_init(const double turbidity, + const double albedo, + const double elevation); -double arhosek_tristim_skymodel_radiance(ArHosekSkyModelState *state, - double theta, - double gamma, - int channel); +double SKY_arhosek_tristim_skymodel_radiance(SKY_ArHosekSkyModelState *state, + double theta, + double gamma, + int channel); // Delivers the complete function: sky + sun, including limb darkening. // Please read the above description before using this - there are several // caveats! -double arhosekskymodel_solar_radiance(ArHosekSkyModelState *state, - double theta, - double gamma, - double wavelength); - -#endif // _SKY_MODEL_H_ +double SKY_arhosekskymodel_solar_radiance(SKY_ArHosekSkyModelState *state, + double theta, + double gamma, + double wavelength); /* Nishita improved sky model */ -void nishita_skymodel_precompute_texture(float *pixels, - int stride, - int start_y, - int end_y, - int width, - int height, - float sun_elevation, +void SKY_nishita_skymodel_precompute_texture(float *pixels, + int stride, + int start_y, + int end_y, + int width, + int height, + float sun_elevation, + float altitude, + float air_density, + float dust_density, + float ozone_density); + +void SKY_nishita_skymodel_precompute_sun(float sun_elevation, + float angular_diameter, float altitude, float air_density, float dust_density, - float ozone_density); + float *r_pixel_bottom, + float *r_pixel_top); -void nishita_skymodel_precompute_sun(float sun_elevation, - float angular_diameter, - float altitude, - float air_density, - float dust_density, - float *pixel_bottom, - float *pixel_top); +#ifdef __cplusplus +} +#endif -CCL_NAMESPACE_END +#endif // __SKY_MODEL_H__ diff --git a/intern/sky/source/sky_float3.h b/intern/sky/source/sky_float3.h new file mode 100644 index 00000000000..2a9b9c89623 --- /dev/null +++ b/intern/sky/source/sky_float3.h @@ -0,0 +1,157 @@ +/* + * 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. + */ + +#ifndef __SKY_FLOAT3_H__ +#define __SKY_FLOAT3_H__ + +// minimal float3 + util_math.h implementation for nishita sky model + +#include <math.h> + +#ifndef M_PI_F +# define M_PI_F (3.1415926535897932f) /* pi */ +#endif +#ifndef M_PI_2_F +# define M_PI_2_F (1.5707963267948966f) /* pi/2 */ +#endif +#ifndef M_2PI_F +# define M_2PI_F (6.2831853071795864f) /* 2*pi */ +#endif + +struct float3 { + float x, y, z; + + float3() = default; + + float3(const float *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]} + { + } + + float3(const float (*ptr)[3]) : float3((const float *)ptr) + { + } + + explicit float3(float value) : x(value), y(value), z(value) + { + } + + explicit float3(int value) : x(value), y(value), z(value) + { + } + + float3(float x, float y, float z) : x{x}, y{y}, z{z} + { + } + + operator const float *() const + { + return &x; + } + + operator float *() + { + return &x; + } + + friend float3 operator*(const float3 &a, float b) + { + return {a.x * b, a.y * b, a.z * b}; + } + + friend float3 operator*(float b, const float3 &a) + { + return {a.x * b, a.y * b, a.z * b}; + } + + friend float3 operator-(const float3 &a, const float3 &b) + { + return {a.x - b.x, a.y - b.y, a.z - b.z}; + } + + friend float3 operator-(const float3 &a) + { + return {-a.x, -a.y, -a.z}; + } + + float length_squared() const + { + return x * x + y * y + z * z; + } + + float length() const + { + return sqrt(length_squared()); + } + + static float distance(const float3 &a, const float3 &b) + { + return (a - b).length(); + } + + friend float3 operator+(const float3 &a, const float3 &b) + { + return {a.x + b.x, a.y + b.y, a.z + b.z}; + } + + void operator+=(const float3 &b) + { + this->x += b.x; + this->y += b.y; + this->z += b.z; + } + + friend float3 operator*(const float3 &a, const float3 &b) + { + return {a.x * b.x, a.y * b.y, a.z * b.z}; + } +}; + +inline float sqr(float a) +{ + return a * a; +} + +inline float3 make_float3(float x, float y, float z) +{ + return float3(x, y, z); +} + +inline float dot(const float3 &a, const float3 &b) +{ + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +inline float distance(const float3 &a, const float3 &b) +{ + return float3::distance(a, b); +} + +inline float len_squared(float3 f) +{ + return f.length_squared(); +} + +inline float len(float3 f) +{ + return f.length(); +} + +inline float reduce_add(float3 f) +{ + return f.x + f.y + f.z; +} + +#endif /* __SKY_FLOAT3_H__ */ diff --git a/intern/cycles/util/util_sky_model.cpp b/intern/sky/source/sky_model.cpp index 8cdad8a90a4..64cf14ec030 100644 --- a/intern/cycles/util/util_sky_model.cpp +++ b/intern/sky/source/sky_model.cpp @@ -97,16 +97,14 @@ All instructions on how to use this code are in the accompanying header file. */ -#include "util/util_sky_model.h" -#include "util/util_sky_model_data.h" +#include "sky_model.h" +#include "sky_model_data.h" #include <assert.h> #include <math.h> #include <stdio.h> #include <stdlib.h> -CCL_NAMESPACE_BEGIN - // Some macro definitions that occur elsewhere in ART, and that have to be // replicated to make this a stand-alone module. @@ -138,7 +136,7 @@ typedef const double *ArHosekSkyModel_Radiance_Dataset; // internal functions static void ArHosekSkyModel_CookConfiguration(ArHosekSkyModel_Dataset dataset, - ArHosekSkyModelConfiguration config, + SKY_ArHosekSkyModelConfiguration config, double turbidity, double albedo, double solar_elevation) @@ -272,7 +270,7 @@ static double ArHosekSkyModel_CookRadianceConfiguration(ArHosekSkyModel_Radiance return res; } -static double ArHosekSkyModel_GetRadianceInternal(ArHosekSkyModelConfiguration configuration, +static double ArHosekSkyModel_GetRadianceInternal(SKY_ArHosekSkyModelConfiguration configuration, double theta, double gamma) { @@ -288,15 +286,15 @@ static double ArHosekSkyModel_GetRadianceInternal(ArHosekSkyModelConfiguration c configuration[6] * mieM + configuration[7] * zenith); } -void arhosekskymodelstate_free(ArHosekSkyModelState *state) +void SKY_arhosekskymodelstate_free(SKY_ArHosekSkyModelState *state) { free(state); } -double arhosekskymodel_radiance(ArHosekSkyModelState *state, - double theta, - double gamma, - double wavelength) +double SKY_arhosekskymodel_radiance(SKY_ArHosekSkyModelState *state, + double theta, + double gamma, + double wavelength) { int low_wl = (int)((wavelength - 320.0) / 40.0); @@ -324,11 +322,11 @@ double arhosekskymodel_radiance(ArHosekSkyModelState *state, // xyz and rgb versions -ArHosekSkyModelState *arhosek_xyz_skymodelstate_alloc_init(const double turbidity, - const double albedo, - const double elevation) +SKY_ArHosekSkyModelState *SKY_arhosek_xyz_skymodelstate_alloc_init(const double turbidity, + const double albedo, + const double elevation) { - ArHosekSkyModelState *state = ALLOC(ArHosekSkyModelState); + SKY_ArHosekSkyModelState *state = ALLOC(SKY_ArHosekSkyModelState); state->solar_radius = TERRESTRIAL_SOLAR_RADIUS; state->turbidity = turbidity; @@ -345,5 +343,3 @@ ArHosekSkyModelState *arhosek_xyz_skymodelstate_alloc_init(const double turbidit return state; } - -CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_sky_model_data.h b/intern/sky/source/sky_model_data.h index a2a3935eb84..8d98f84cdae 100644 --- a/intern/cycles/util/util_sky_model_data.h +++ b/intern/sky/source/sky_model_data.h @@ -91,8 +91,6 @@ an updated version of this code has been published! ============================================================================ */ -CCL_NAMESPACE_BEGIN - /* This file contains the coefficient data for the XYZ colour space version of @@ -3843,5 +3841,3 @@ static const double datasetXYZRad3[] = { static const double *datasetsXYZ[] = {datasetXYZ1, datasetXYZ2, datasetXYZ3}; static const double *datasetsXYZRad[] = {datasetXYZRad1, datasetXYZRad2, datasetXYZRad3}; - -CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_sky_nishita.cpp b/intern/sky/source/sky_nishita.cpp index 92397804d43..eae95dc73fe 100644 --- a/intern/cycles/util/util_sky_nishita.cpp +++ b/intern/sky/source/sky_nishita.cpp @@ -14,21 +14,23 @@ * limitations under the License. */ -#include "util/util_math.h" -#include "util/util_sky_model.h" - -CCL_NAMESPACE_BEGIN +#include "sky_float3.h" +#include "sky_model.h" /* Constants */ static const float rayleigh_scale = 8000.0f; // Rayleigh scale height (m) static const float mie_scale = 1200.0f; // Mie scale height (m) static const float mie_coeff = 2e-5f; // Mie scattering coefficient static const float mie_G = 0.76f; // aerosols anisotropy +static const float sqr_G = mie_G * mie_G; // squared aerosols anisotropy static const float earth_radius = 6360000.0f; // radius of Earth (m) static const float atmosphere_radius = 6420000.0f; // radius of atmosphere (m) static const int steps = 32; // segments per primary ray static const int steps_light = 16; // segments per sun connection ray static const int num_wavelengths = 21; // number of wavelengths +static const int max_luminous_efficacy = 683; // maximum luminous efficacy +static const float step_lambda = (num_wavelengths - 1) * + 1e-9f; // step between each sampled wavelength /* irradiance at top of atmosphere */ static const float irradiance[] = { 1.45756829855592995315f, 1.56596305559738380175f, 1.65148449067670455293f, @@ -92,7 +94,7 @@ static float3 spec_to_xyz(float *spectrum) xyz.y += cmf_xyz[i][1] * spectrum[i]; xyz.z += cmf_xyz[i][2] * spectrum[i]; } - return xyz * (20 * 683 * 1e-9f); + return xyz * step_lambda * max_luminous_efficacy; } /* Atmosphere volume models */ @@ -124,8 +126,6 @@ static float phase_rayleigh(float mu) static float phase_mie(float mu) { - static const float sqr_G = mie_G * mie_G; - return (3.0f * (1.0f - sqr_G) * (1.0f + sqr(mu))) / (8.0f * M_PI_F * (2.0f + sqr_G) * powf((1.0f + sqr_G - 2.0f * mie_G * mu), 1.5)); } @@ -169,6 +169,7 @@ static float3 ray_optical_depth(float3 ray_origin, float3 ray_dir) /* The density of each segment is evaluated at its middle. */ float3 P = ray_origin + 0.5f * segment; + for (int i = 0; i < steps_light; i++) { /* Compute height above sea level. */ float height = len(P) - earth_radius; @@ -176,13 +177,13 @@ static float3 ray_optical_depth(float3 ray_origin, float3 ray_dir) /* Accumulate optical depth of this segment (density is assumed to be constant along it). */ float3 density = make_float3( density_rayleigh(height), density_mie(height), density_ozone(height)); - optical_depth += segment_length * density; + optical_depth += density; /* Advance along ray. */ P += segment; } - return optical_depth; + return optical_depth * segment_length; } /* Single Scattering implementation */ @@ -221,6 +222,7 @@ static void single_scattering(float3 ray_dir, /* The density and in-scattering of each segment is evaluated at its middle. */ float3 P = ray_origin + 0.5f * segment; + for (int i = 0; i < steps; i++) { /* Compute height above sea level. */ float height = len(P) - earth_radius; @@ -269,17 +271,17 @@ static void single_scattering(float3 ray_dir, } /* calculate texture array */ -void nishita_skymodel_precompute_texture(float *pixels, - int stride, - int start_y, - int end_y, - int width, - int height, - float sun_elevation, - float altitude, - float air_density, - float dust_density, - float ozone_density) +void SKY_nishita_skymodel_precompute_texture(float *pixels, + int stride, + int start_y, + int end_y, + int width, + int height, + float sun_elevation, + float altitude, + float air_density, + float dust_density, + float ozone_density) { /* calculate texture pixels */ float spectrum[num_wavelengths]; @@ -289,11 +291,13 @@ void nishita_skymodel_precompute_texture(float *pixels, float latitude_step = M_PI_2_F / height; float longitude_step = M_2PI_F / width; + float half_lat_step = latitude_step / 2.0f; for (int y = start_y; y < end_y; y++) { - float latitude = latitude_step * y; + /* sample more pixels toward the horizon */ + float latitude = (M_PI_2_F + half_lat_step) * sqr((float)y / height); - float *pixel_row = pixels + (y * width) * stride; + float *pixel_row = pixels + (y * width * stride); for (int x = 0; x < half_width; x++) { float longitude = longitude_step * x - M_PI_F; @@ -301,13 +305,15 @@ void nishita_skymodel_precompute_texture(float *pixels, single_scattering(dir, sun_dir, cam_pos, air_density, dust_density, ozone_density, spectrum); float3 xyz = spec_to_xyz(spectrum); - pixel_row[x * stride + 0] = xyz.x; - pixel_row[x * stride + 1] = xyz.y; - pixel_row[x * stride + 2] = xyz.z; - int mirror_x = width - x - 1; - pixel_row[mirror_x * stride + 0] = xyz.x; - pixel_row[mirror_x * stride + 1] = xyz.y; - pixel_row[mirror_x * stride + 2] = xyz.z; + int pos_x = x * stride; + pixel_row[pos_x] = xyz.x; + pixel_row[pos_x + 1] = xyz.y; + pixel_row[pos_x + 2] = xyz.z; + /* mirror sky */ + int mirror_x = (width - x - 1) * stride; + pixel_row[mirror_x] = xyz.x; + pixel_row[mirror_x + 1] = xyz.y; + pixel_row[mirror_x + 2] = xyz.z; } } } @@ -328,17 +334,17 @@ static void sun_radiation(float3 cam_dir, /* Combine spectra and the optical depth into transmittance. */ float transmittance = rayleigh_coeff[i] * optical_depth.x * air_density + 1.11f * mie_coeff * optical_depth.y * dust_density; - r_spectrum[i] = (irradiance[i] / solid_angle) * expf(-transmittance); + r_spectrum[i] = irradiance[i] * expf(-transmittance) / solid_angle; } } -void nishita_skymodel_precompute_sun(float sun_elevation, - float angular_diameter, - float altitude, - float air_density, - float dust_density, - float *pixel_bottom, - float *pixel_top) +void SKY_nishita_skymodel_precompute_sun(float sun_elevation, + float angular_diameter, + float altitude, + float air_density, + float dust_density, + float *r_pixel_bottom, + float *r_pixel_top) { /* definitions */ float half_angular = angular_diameter / 2.0f; @@ -360,12 +366,10 @@ void nishita_skymodel_precompute_sun(float sun_elevation, pix_top = spec_to_xyz(spectrum); /* store pixels */ - pixel_bottom[0] = pix_bottom.x; - pixel_bottom[1] = pix_bottom.y; - pixel_bottom[2] = pix_bottom.z; - pixel_top[0] = pix_top.x; - pixel_top[1] = pix_top.y; - pixel_top[2] = pix_top.z; + r_pixel_bottom[0] = pix_bottom.x; + r_pixel_bottom[1] = pix_bottom.y; + r_pixel_bottom[2] = pix_bottom.z; + r_pixel_top[0] = pix_top.x; + r_pixel_top[1] = pix_top.y; + r_pixel_top[2] = pix_top.z; } - -CCL_NAMESPACE_END diff --git a/release/scripts/modules/bl_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py index e522ec3fcf9..a6101474aa9 100644 --- a/release/scripts/modules/bl_i18n_utils/settings.py +++ b/release/scripts/modules/bl_i18n_utils/settings.py @@ -601,8 +601,11 @@ class I18nSettings: return json.dumps(export_dict) def load(self, fname, reset=False): + reset = reset or fname is None if reset: self.__dict__ = {uid: data for uid, data in globals().items() if not uid.startswith("_")} + if fname is None: + return if isinstance(fname, str): if not os.path.isfile(fname): # Assume it is already real JSon string... diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index b76f57c4545..832f9c1f7a4 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -43,6 +43,7 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.sndparticle_sampling_trappedair*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-trappedair"), ("bpy.types.fluiddomainsettings.sndparticle_sampling_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-wavecrest"), ("bpy.types.fluiddomainsettings.sndparticle_potential_radius*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-radius"), + ("bpy.types.fluiddomainsettings.openvdb_cache_compress_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-openvdb-cache-compress-type"), ("bpy.types.fluiddomainsettings.sndparticle_bubble_buoyancy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-bubble-buoyancy"), ("bpy.types.fluiddomainsettings.sndparticle_combined_export*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-combined-export"), ("bpy.types.fluiddomainsettings.use_collision_border_bottom*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-bottom"), @@ -68,7 +69,6 @@ url_manual_mapping = ( ("bpy.types.linestylegeometrymodifier_polygonalization*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/polygonization.html#bpy-types-linestylegeometrymodifier-polygonalization"), ("bpy.ops.view3d.edit_mesh_extrude_move_shrink_fatten*", "modeling/meshes/editing/face/extrude_faces_normal.html#bpy-ops-view3d-edit-mesh-extrude-move-shrink-fatten"), ("bpy.types.cyclesrendersettings.distance_cull_margin*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-distance-cull-margin"), - ("bpy.types.fluiddomainsettings.cache_particle_format*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-particle-format"), ("bpy.types.fluiddomainsettings.display_interpolation*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-display-interpolation"), ("bpy.types.materialgpencilstyle.use_fill_texture_mix*", "grease_pencil/materials/grease_pencil_shader.html#bpy-types-materialgpencilstyle-use-fill-texture-mix"), ("bpy.types.rendersettings_simplify_gpencil_shader_fx*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-shader-fx"), @@ -91,15 +91,18 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.particle_band_width*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-particle-band-width"), ("bpy.types.fluiddomainsettings.particle_randomness*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-particle-randomness"), ("bpy.types.fluiddomainsettings.use_adaptive_domain*", "physics/fluid/type/domain/gas/adaptive_domain.html#bpy-types-fluiddomainsettings-use-adaptive-domain"), + ("bpy.types.fluiddomainsettings.use_resumable_cache*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-use-resumable-cache"), ("bpy.types.fluiddomainsettings.use_spray_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-use-spray-particles"), ("bpy.types.fluiddomainsettings.vector_display_type*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-vector-display-type"), ("bpy.types.linestylegeometrymodifier_perlinnoise1d*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/perlin_noise_1d.html#bpy-types-linestylegeometrymodifier-perlinnoise1d"), ("bpy.types.linestylegeometrymodifier_perlinnoise2d*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/perlin_noise_2d.html#bpy-types-linestylegeometrymodifier-perlinnoise2d"), ("bpy.types.rendersettings.use_high_quality_normals*", "render/eevee/render_settings/performance.html#bpy-types-rendersettings-use-high-quality-normals"), ("bpy.types.cyclesrendersettings.use_distance_cull*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-use-distance-cull"), + ("bpy.types.fluiddomainsettings.cache_frame_offset*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-frame-offset"), ("bpy.types.fluiddomainsettings.delete_in_obstacle*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-delete-in-obstacle"), ("bpy.types.fluiddomainsettings.mesh_concave_lower*", "physics/fluid/type/domain/liquid/mesh.html#bpy-types-fluiddomainsettings-mesh-concave-lower"), ("bpy.types.fluiddomainsettings.mesh_concave_upper*", "physics/fluid/type/domain/liquid/mesh.html#bpy-types-fluiddomainsettings-mesh-concave-upper"), + ("bpy.types.fluiddomainsettings.openvdb_data_depth*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-openvdb-data-depth"), ("bpy.types.fluiddomainsettings.use_dissolve_smoke*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-dissolve-smoke"), ("bpy.types.fluiddomainsettings.use_flip_particles*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-flip-particles"), ("bpy.types.fluiddomainsettings.use_foam_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-use-foam-particles"), @@ -132,6 +135,7 @@ url_manual_mapping = ( ("bpy.types.linestyle*modifier_distancefromobject*", "render/freestyle/parameter_editor/line_style/modifiers/color/distance_from_object.html#bpy-types-linestyle-modifier-distancefromobject"), ("bpy.types.linestylegeometrymodifier_2dtransform*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/2d_transform.html#bpy-types-linestylegeometrymodifier-2dtransform"), ("bpy.types.linestylegeometrymodifier_beziercurve*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/bezier_curve.html#bpy-types-linestylegeometrymodifier-beziercurve"), + ("bpy.types.particlesettings.use_parent_particles*", "physics/particles/emitter/render.html#bpy-types-particlesettings-use-parent-particles"), ("bpy.types.rendersettings_simplify_gpencil_blend*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-blend"), ("bpy.types.toolsettings.gpencil_stroke_placement*", "grease_pencil/modes/draw/stroke_placement.html#bpy-types-toolsettings-gpencil-stroke-placement"), ("bpy.types.cyclesrendersettings.use_camera_cull*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-use-camera-cull"), @@ -140,6 +144,7 @@ url_manual_mapping = ( ("bpy.types.linestylegeometrymodifier_tipremover*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/tip_remover.html#bpy-types-linestylegeometrymodifier-tipremover"), ("bpy.types.rendersettings_simplify_gpencil_tint*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-tint"), ("bpy.types.toolsettings.use_gpencil_draw_onback*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-draw-onback"), + ("bpy.ops.sequencer.deinterlace_selected_movies*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-deinterlace-selected-movies"), ("bpy.types.brush.surface_smooth_current_vertex*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-surface-smooth-current-vertex"), ("bpy.types.brush.use_multiplane_scrape_dynamic*", "sculpt_paint/sculpting/tools/multiplane_scrape.html#bpy-types-brush-use-multiplane-scrape-dynamic"), ("bpy.types.clothsettings.vertex_group_pressure*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-vertex-group-pressure"), @@ -267,6 +272,7 @@ url_manual_mapping = ( ("bpy.types.rendersettings.use_placeholder*", "render/output/settings.html#bpy-types-rendersettings-use-placeholder"), ("bpy.types.shadernodesubsurfacescattering*", "render/shader_nodes/shader/sss.html#bpy-types-shadernodesubsurfacescattering"), ("bpy.types.spacedopesheeteditor.auto_snap*", "editors/dope_sheet/editing.html#bpy-types-spacedopesheeteditor-auto-snap"), + ("bpy.types.spacetexteditor.use_match_case*", "editors/text_editor.html#bpy-types-spacetexteditor-use-match-case"), ("bpy.types.volumedisplay.wireframe_detail*", "modeling/volumes/properties.html#bpy-types-volumedisplay-wireframe-detail"), ("bpy.ops.object.vertex_group_limit_total*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-limit-total"), ("bpy.ops.object.vertex_group_remove_from*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-remove-from"), @@ -276,7 +282,6 @@ url_manual_mapping = ( ("bpy.types.clothsettings.pressure_factor*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-pressure-factor"), ("bpy.types.compositornodecolorcorrection*", "compositing/types/color/color_correction.html#bpy-types-compositornodecolorcorrection"), ("bpy.types.compositornodemoviedistortion*", "compositing/types/distort/movie_distortion.html#bpy-types-compositornodemoviedistortion"), - ("bpy.types.ffmpegsettings.audio_channels*", "scene_layout/scene/properties.html#bpy-types-ffmpegsettings-audio-channels"), ("bpy.types.fluiddomainsettings.use_guide*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-use-guide"), ("bpy.types.fluiddomainsettings.use_noise*", "physics/fluid/type/domain/gas/noise.html#bpy-types-fluiddomainsettings-use-noise"), ("bpy.types.fluiddomainsettings.vorticity*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-vorticity"), @@ -288,6 +293,8 @@ url_manual_mapping = ( ("bpy.types.fmodifierenvelopecontrolpoint*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierenvelopecontrolpoint"), ("bpy.types.material.use_sss_translucency*", "render/eevee/materials/settings.html#bpy-types-material-use-sss-translucency"), ("bpy.types.sceneeevee.taa_render_samples*", "render/eevee/render_settings/sampling.html#bpy-types-sceneeevee-taa-render-samples"), + ("bpy.types.spacetexteditor.margin_column*", "editors/text_editor.html#bpy-types-spacetexteditor-margin-column"), + ("bpy.types.spacetexteditor.use_find_wrap*", "editors/text_editor.html#bpy-types-spacetexteditor-use-find-wrap"), ("bpy.types.spaceuveditor.pixel_snap_mode*", "modeling/meshes/uv/editing.html#bpy-types-spaceuveditor-pixel-snap-mode"), ("bpy.types.spaceuveditor.use_live_unwrap*", "modeling/meshes/uv/editing.html#bpy-types-spaceuveditor-use-live-unwrap"), ("bpy.types.vertexweightproximitymodifier*", "modeling/modifiers/modify/weight_proximity.html#bpy-types-vertexweightproximitymodifier"), @@ -296,7 +303,6 @@ url_manual_mapping = ( ("bpy.types.brush.use_grab_active_vertex*", "sculpt_paint/sculpting/tools/grab.html#bpy-types-brush-use-grab-active-vertex"), ("bpy.types.compositornodebrightcontrast*", "compositing/types/color/bright_contrast.html#bpy-types-compositornodebrightcontrast"), ("bpy.types.compositornodedoubleedgemask*", "compositing/types/matte/double_edge_mask.html#bpy-types-compositornodedoubleedgemask"), - ("bpy.types.ffmpegsettings.audio_mixrate*", "scene_layout/scene/properties.html#bpy-types-ffmpegsettings-audio-mixrate"), ("bpy.types.fluiddomainsettings.clipping*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-clipping"), ("bpy.types.fluiddomainsettings.use_mesh*", "physics/fluid/type/domain/liquid/mesh.html#bpy-types-fluiddomainsettings-use-mesh"), ("bpy.types.material.preview_render_type*", "render/materials/preview.html#bpy-types-material-preview-render-type"), @@ -309,12 +315,15 @@ url_manual_mapping = ( ("bpy.types.shadernodevectordisplacement*", "render/shader_nodes/vector/vector_displacement.html#bpy-types-shadernodevectordisplacement"), ("bpy.types.spacegrapheditor.show_cursor*", "editors/graph_editor/introduction.html#bpy-types-spacegrapheditor-show-cursor"), ("bpy.types.spaceimageeditor.show_repeat*", "editors/image/view_tab.html#bpy-types-spaceimageeditor-show-repeat"), + ("bpy.types.spacetexteditor.replace_text*", "editors/text_editor.html#bpy-types-spacetexteditor-replace-text"), + ("bpy.types.spacetexteditor.use_find_all*", "editors/text_editor.html#bpy-types-spacetexteditor-use-find-all"), ("bpy.types.volumedisplay.wireframe_type*", "modeling/volumes/properties.html#bpy-types-volumedisplay-wireframe-type"), ("bpy.ops.curve.normals_make_consistent*", "modeling/curves/editing/control_points.html#bpy-ops-curve-normals-make-consistent"), ("bpy.ops.gpencil.stroke_simplify_fixed*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-stroke-simplify-fixed"), ("bpy.ops.object.gpencil_modifier_apply*", "grease_pencil/modifiers/introduction.html#bpy-ops-object-gpencil-modifier-apply"), ("bpy.ops.object.vertex_group_normalize*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-normalize"), ("bpy.ops.object.visual_transform_apply*", "scene_layout/object/editing/apply.html#bpy-ops-object-visual-transform-apply"), + ("bpy.ops.sequencer.change_effect_input*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-change-effect-input"), ("bpy.types.brush.texture_overlay_alpha*", "sculpt_paint/brush/cursor.html#bpy-types-brush-texture-overlay-alpha"), ("bpy.types.brushgpencilsettings.random*", "grease_pencil/modes/draw/tool_settings/brushes/draw_brush.html#bpy-types-brushgpencilsettings-random"), ("bpy.types.clothsettings.target_volume*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-target-volume"), @@ -327,16 +336,20 @@ url_manual_mapping = ( ("bpy.types.linestyle*modifier_material*", "render/freestyle/parameter_editor/line_style/modifiers/color/material.html#bpy-types-linestyle-modifier-material"), ("bpy.types.particlesettingstextureslot*", "physics/particles/texture_influence.html#bpy-types-particlesettingstextureslot"), ("bpy.types.posebone.ik_rotation_weight*", "animation/armatures/posing/bone_constraints/inverse_kinematics/introduction.html#bpy-types-posebone-ik-rotation-weight"), + ("bpy.types.regionview3d.show_sync_view*", "editors/3dview/navigate/views.html#bpy-types-regionview3d-show-sync-view"), ("bpy.types.sceneeevee.volumetric_light*", "render/eevee/render_settings/volumetrics.html#bpy-types-sceneeevee-volumetric-light"), ("bpy.types.sculpt.symmetrize_direction*", "sculpt_paint/sculpting/tool_settings/symmetry.html#bpy-types-sculpt-symmetrize-direction"), ("bpy.types.sequenceeditor.show_overlay*", "video_editing/preview/properties.html#bpy-types-sequenceeditor-show-overlay"), + ("bpy.types.spacetexteditor.show_margin*", "editors/text_editor.html#bpy-types-spacetexteditor-show-margin"), ("bpy.types.spline.radius_interpolation*", "modeling/curves/properties/active_spline.html#bpy-types-spline-radius-interpolation"), ("bpy.types.viewlayer.material_override*", "render/layers/layers.html#bpy-types-viewlayer-material-override"), ("bpy.ops.gpencil.interpolate_sequence*", "grease_pencil/animation/tools.html#bpy-ops-gpencil-interpolate-sequence"), ("bpy.ops.mesh.normals_make_consistent*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-normals-make-consistent"), + ("bpy.ops.mesh.offset_edge_loops_slide*", "modeling/meshes/editing/edge/offset_edge_slide.html#bpy-ops-mesh-offset-edge-loops-slide"), ("bpy.ops.object.duplicate_move_linked*", "scene_layout/object/editing/duplicate_linked.html#bpy-ops-object-duplicate-move-linked"), ("bpy.ops.object.vertex_group_quantize*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-quantize"), - ("bpy.ops.view3d.localview_remove_from*", "editors/3dview/navigate/views.html#bpy-ops-view3d-localview-remove-from"), + ("bpy.ops.sequencer.change_effect_type*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-change-effect-type"), + ("bpy.ops.view3d.localview_remove_from*", "editors/3dview/navigate/local_view.html#bpy-ops-view3d-localview-remove-from"), ("bpy.types.animdata.action_blend_type*", "editors/nla/properties_modifiers.html#bpy-types-animdata-action-blend-type"), ("bpy.types.brush.cursor_overlay_alpha*", "sculpt_paint/brush/cursor.html#bpy-types-brush-cursor-overlay-alpha"), ("bpy.types.brush.normal_radius_factor*", "sculpt_paint/sculpting/tool_settings/brush_settings.html#bpy-types-brush-normal-radius-factor"), @@ -354,6 +367,9 @@ url_manual_mapping = ( ("bpy.types.materialgpencilstyle.color*", "grease_pencil/materials/grease_pencil_shader.html#bpy-types-materialgpencilstyle-color"), ("bpy.types.movietrackingstabilization*", "movie_clip/tracking/clip/properties/stabilization/index.html#bpy-types-movietrackingstabilization"), ("bpy.types.object.display_bounds_type*", "scene_layout/object/properties/display.html#bpy-types-object-display-bounds-type"), + ("bpy.types.regionview3d.lock_rotation*", "editors/3dview/navigate/views.html#bpy-types-regionview3d-lock-rotation"), + ("bpy.types.scene.audio_distance_model*", "scene_layout/scene/properties.html#bpy-types-scene-audio-distance-model"), + ("bpy.types.scene.audio_doppler_factor*", "scene_layout/scene/properties.html#bpy-types-scene-audio-doppler-factor"), ("bpy.types.shadernodeambientocclusion*", "render/shader_nodes/input/ao.html#bpy-types-shadernodeambientocclusion"), ("bpy.types.shadernodevolumeabsorption*", "render/shader_nodes/shader/volume_absorption.html#bpy-types-shadernodevolumeabsorption"), ("bpy.types.shadernodevolumeprincipled*", "render/shader_nodes/shader/volume_principled.html#bpy-types-shadernodevolumeprincipled"), @@ -373,6 +389,7 @@ url_manual_mapping = ( ("bpy.types.brush.crease_pinch_factor*", "sculpt_paint/sculpting/tools/snake_hook.html#bpy-types-brush-crease-pinch-factor"), ("bpy.types.brush.elastic_deform_type*", "sculpt_paint/sculpting/tools/elastic_deform.html#bpy-types-brush-elastic-deform-type"), ("bpy.types.brush.use_primary_overlay*", "sculpt_paint/brush/cursor.html#bpy-types-brush-use-primary-overlay"), + ("bpy.types.camera.passepartout_alpha*", "render/cameras.html#bpy-types-camera-passepartout-alpha"), ("bpy.types.compositornodechromamatte*", "compositing/types/matte/chroma_key.html#bpy-types-compositornodechromamatte"), ("bpy.types.compositornodedilateerode*", "compositing/types/filter/dilate_erode.html#bpy-types-compositornodedilateerode"), ("bpy.types.compositornodeellipsemask*", "compositing/types/matte/ellipse_mask.html#bpy-types-compositornodeellipsemask"), @@ -385,12 +402,17 @@ url_manual_mapping = ( ("bpy.types.materialgpencilstyle.mode*", "grease_pencil/materials/grease_pencil_shader.html#bpy-types-materialgpencilstyle-mode"), ("bpy.types.object.empty_display_size*", "modeling/empties.html#bpy-types-object-empty-display-size"), ("bpy.types.object.empty_display_type*", "modeling/empties.html#bpy-types-object-empty-display-type"), + ("bpy.types.regionview3d.use_box_clip*", "editors/3dview/navigate/views.html#bpy-types-regionview3d-use-box-clip"), ("bpy.types.rendersettings.use_border*", "render/output/settings.html#bpy-types-rendersettings-use-border"), + ("bpy.types.scene.audio_doppler_speed*", "scene_layout/scene/properties.html#bpy-types-scene-audio-doppler-speed"), ("bpy.types.sceneeevee.bokeh_max_size*", "render/eevee/render_settings/depth_of_field.html#bpy-types-sceneeevee-bokeh-max-size"), ("bpy.types.shadernodebsdfanisotropic*", "render/shader_nodes/shader/anisotropic.html#bpy-types-shadernodebsdfanisotropic"), ("bpy.types.shadernodebsdftranslucent*", "render/shader_nodes/shader/translucent.html#bpy-types-shadernodebsdftranslucent"), ("bpy.types.shadernodebsdftransparent*", "render/shader_nodes/shader/transparent.html#bpy-types-shadernodebsdftransparent"), ("bpy.types.shadernodevectortransform*", "render/shader_nodes/vector/transform.html#bpy-types-shadernodevectortransform"), + ("bpy.types.spacetexteditor.find_text*", "editors/text_editor.html#bpy-types-spacetexteditor-find-text"), + ("bpy.types.spacetexteditor.font_size*", "editors/text_editor.html#bpy-types-spacetexteditor-font-size"), + ("bpy.types.spacetexteditor.tab_width*", "editors/text_editor.html#bpy-types-spacetexteditor-tab-width"), ("bpy.types.spaceuveditor.lock_bounds*", "modeling/meshes/uv/editing.html#bpy-types-spaceuveditor-lock-bounds"), ("bpy.types.spline.tilt_interpolation*", "modeling/curves/properties/active_spline.html#bpy-types-spline-tilt-interpolation"), ("bpy.ops.mesh.customdata_mask_clear*", "sculpt_paint/sculpting/hide_mask.html#bpy-ops-mesh-customdata-mask-clear"), @@ -414,6 +436,7 @@ url_manual_mapping = ( ("bpy.types.brush.auto_smooth_factor*", "sculpt_paint/sculpting/tool_settings/brush_settings.html#bpy-types-brush-auto-smooth-factor"), ("bpy.types.brush.smooth_deform_type*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-smooth-deform-type"), ("bpy.types.brush.use_cursor_overlay*", "sculpt_paint/brush/cursor.html#bpy-types-brush-use-cursor-overlay"), + ("bpy.types.camera.show_passepartout*", "render/cameras.html#bpy-types-camera-show-passepartout"), ("bpy.types.compositornodebokehimage*", "compositing/types/input/bokeh_image.html#bpy-types-compositornodebokehimage"), ("bpy.types.compositornodecolormatte*", "compositing/types/matte/color_key.html#bpy-types-compositornodecolormatte"), ("bpy.types.compositornodecolorspill*", "compositing/types/matte/color_spill.html#bpy-types-compositornodecolorspill"), @@ -430,7 +453,7 @@ url_manual_mapping = ( ("bpy.types.linestyle*modifier_noise*", "render/freestyle/parameter_editor/line_style/modifiers/color/noise.html#bpy-types-linestyle-modifier-noise"), ("bpy.types.maintainvolumeconstraint*", "animation/constraints/transform/maintain_volume.html#bpy-types-maintainvolumeconstraint"), ("bpy.types.mesh.use_mirror_topology*", "modeling/meshes/tools/tool_settings.html#bpy-types-mesh-use-mirror-topology"), - ("bpy.types.particleinstancemodifier*", "modeling/modifiers/simulate/particle_instance.html#bpy-types-particleinstancemodifier"), + ("bpy.types.particleinstancemodifier*", "modeling/modifiers/physics/particle_instance.html#bpy-types-particleinstancemodifier"), ("bpy.types.shadernodebrightcontrast*", "render/shader_nodes/color/bright_contrast.html#bpy-types-shadernodebrightcontrast"), ("bpy.types.shadernodebsdfprincipled*", "render/shader_nodes/shader/principled.html#bpy-types-shadernodebsdfprincipled"), ("bpy.types.shadernodebsdfrefraction*", "render/shader_nodes/shader/refraction.html#bpy-types-shadernodebsdfrefraction"), @@ -448,6 +471,8 @@ url_manual_mapping = ( ("bpy.ops.object.vertex_group_clean*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-clean"), ("bpy.ops.render.play-rendered-anim*", "render/output/animation_player.html#bpy-ops-render-play-rendered-anim"), ("bpy.ops.sculpt.set_pivot_position*", "sculpt_paint/sculpting/editing.html#bpy-ops-sculpt-set-pivot-position"), + ("bpy.ops.sequencer.reassign_inputs*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-reassign-inputs"), + ("bpy.ops.view3d.view_center_camera*", "editors/3dview/navigate/camera_view.html#bpy-ops-view3d-view-center-camera"), ("bpy.types.armaturegpencilmodifier*", "grease_pencil/modifiers/deform/armature.html#bpy-types-armaturegpencilmodifier"), ("bpy.types.camera.show_composition*", "render/cameras.html#bpy-types-camera-show-composition"), ("bpy.types.compositornodealphaover*", "compositing/types/color/alpha_over.html#bpy-types-compositornodealphaover"), @@ -462,6 +487,7 @@ url_manual_mapping = ( ("bpy.types.compositornodestabilize*", "compositing/types/distort/stabilize_2d.html#bpy-types-compositornodestabilize"), ("bpy.types.compositornodetransform*", "compositing/types/distort/transform.html#bpy-types-compositornodetransform"), ("bpy.types.compositornodetranslate*", "compositing/types/distort/translate.html#bpy-types-compositornodetranslate"), + ("bpy.types.constraint.target_space*", "animation/constraints/interface/common.html#bpy-types-constraint-target-space"), ("bpy.types.freestylemodulesettings*", "render/freestyle/python.html#bpy-types-freestylemodulesettings"), ("bpy.types.gpencillayer.blend_mode*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-blend-mode"), ("bpy.types.gpencillayer.mask_layer*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-mask-layer"), @@ -498,6 +524,7 @@ url_manual_mapping = ( ("bpy.ops.object.vertex_parent_set*", "modeling/meshes/editing/vertex/make_vertex_parent.html#bpy-ops-object-vertex-parent-set"), ("bpy.ops.paint.mask_lasso_gesture*", "sculpt_paint/sculpting/hide_mask.html#bpy-ops-paint-mask-lasso-gesture"), ("bpy.ops.screen.spacedata_cleanup*", "advanced/operators.html#bpy-ops-screen-spacedata-cleanup"), + ("bpy.ops.sequencer.duplicate_move*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-duplicate-move"), ("bpy.ops.uv.average_islands_scale*", "modeling/meshes/uv/editing.html#bpy-ops-uv-average-islands-scale"), ("bpy.types.brightcontrastmodifier*", "video_editing/sequencer/properties/modifiers.html#bpy-types-brightcontrastmodifier"), ("bpy.types.brush.cursor_color_add*", "sculpt_paint/brush/cursor.html#bpy-types-brush-cursor-color-add"), @@ -516,6 +543,7 @@ url_manual_mapping = ( ("bpy.types.compositornodesunbeams*", "compositing/types/filter/sun_beams.html#bpy-types-compositornodesunbeams"), ("bpy.types.compositornodetrackpos*", "compositing/types/input/track_position.html#bpy-types-compositornodetrackpos"), ("bpy.types.compositornodezcombine*", "compositing/types/color/z_combine.html#bpy-types-compositornodezcombine"), + ("bpy.types.constraint.owner_space*", "animation/constraints/interface/common.html#bpy-types-constraint-owner-space"), ("bpy.types.copylocationconstraint*", "animation/constraints/transform/copy_location.html#bpy-types-copylocationconstraint"), ("bpy.types.copyrotationconstraint*", "animation/constraints/transform/copy_rotation.html#bpy-types-copyrotationconstraint"), ("bpy.types.cyclesmaterialsettings*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings"), @@ -585,6 +613,7 @@ url_manual_mapping = ( ("bpy.types.particlefluidsettings*", "physics/particles/emitter/physics/fluid.html#bpy-types-particlefluidsettings"), ("bpy.types.posebone.custom_shape*", "animation/armatures/bones/properties/display.html#bpy-types-posebone-custom-shape"), ("bpy.types.sceneeevee.volumetric*", "render/eevee/render_settings/volumetrics.html#bpy-types-sceneeevee-volumetric"), + ("bpy.types.sculpt.gravity_object*", "sculpt_paint/sculpting/tool_settings/options.html#bpy-types-sculpt-gravity-object"), ("bpy.types.shadernodebsdfdiffuse*", "render/shader_nodes/shader/diffuse.html#bpy-types-shadernodebsdfdiffuse"), ("bpy.types.shadernodelayerweight*", "render/shader_nodes/input/layer_weight.html#bpy-types-shadernodelayerweight"), ("bpy.types.shadernodeoutputlight*", "render/shader_nodes/output/light.html#bpy-types-shadernodeoutputlight"), @@ -611,6 +640,7 @@ url_manual_mapping = ( ("bpy.ops.outliner.lib_operation*", "files/linked_libraries/introduction.html#bpy-ops-outliner-lib-operation"), ("bpy.ops.outliner.orphans_purge*", "editors/outliner.html#bpy-ops-outliner-orphans-purge"), ("bpy.ops.screen.region_quadview*", "editors/3dview/navigate/views.html#bpy-ops-screen-region-quadview"), + ("bpy.ops.sequencer.offset_clear*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-offset-clear"), ("bpy.ops.uv.follow_active_quads*", "modeling/meshes/editing/uv.html#bpy-ops-uv-follow-active-quads"), ("bpy.types.arraygpencilmodifier*", "grease_pencil/modifiers/generate/array.html#bpy-types-arraygpencilmodifier"), ("bpy.types.brush.use_persistent*", "sculpt_paint/sculpting/tools/layer.html#bpy-types-brush-use-persistent"), @@ -677,8 +707,12 @@ url_manual_mapping = ( ("bpy.ops.object.select_pattern*", "scene_layout/object/selecting.html#bpy-ops-object-select-pattern"), ("bpy.ops.paint.mask_flood_fill*", "sculpt_paint/sculpting/hide_mask.html#bpy-ops-paint-mask-flood-fill"), ("bpy.ops.screen.repeat_history*", "interface/undo_redo.html#bpy-ops-screen-repeat-history"), + ("bpy.ops.sculpt.face_sets_init*", "sculpt_paint/sculpting/editing.html#bpy-ops-sculpt-face-sets-init"), + ("bpy.ops.sequencer.change_path*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-change-path"), ("bpy.ops.sequencer.refresh_all*", "video_editing/sequencer/navigating.html#bpy-ops-sequencer-refresh-all"), + ("bpy.ops.sequencer.swap_inputs*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-swap-inputs"), ("bpy.ops.surface.primitive*add*", "modeling/surfaces/primitives.html#bpy-ops-surface-primitive-add"), + ("bpy.ops.text.resolve_conflict*", "editors/text_editor.html#bpy-ops-text-resolve-conflict"), ("bpy.ops.transform.edge_crease*", "modeling/meshes/editing/edge/edge_data.html#bpy-ops-transform-edge-crease"), ("bpy.ops.transform.skin_resize*", "modeling/meshes/editing/mesh/transform/skin_resize.html#bpy-ops-transform-skin-resize"), ("bpy.ops.uv.seams_from_islands*", "modeling/meshes/uv/unwrapping/seams.html#bpy-ops-uv-seams-from-islands"), @@ -731,6 +765,7 @@ url_manual_mapping = ( ("bpy.types.windowmanager.addon*", "editors/preferences/addons.html#bpy-types-windowmanager-addon"), ("bpy.ops.anim.keyframe_delete*", "animation/keyframes/editing.html#bpy-ops-anim-keyframe-delete"), ("bpy.ops.anim.keyframe_insert*", "animation/keyframes/editing.html#bpy-ops-anim-keyframe-insert"), + ("bpy.ops.clip.detect_features*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-detect-features"), ("bpy.ops.console.autocomplete*", "editors/python_console.html#bpy-ops-console-autocomplete"), ("bpy.ops.curve.dissolve_verts*", "modeling/curves/editing/curve.html#bpy-ops-curve-dissolve-verts"), ("bpy.ops.curve.duplicate_move*", "modeling/curves/editing/curve.html#bpy-ops-curve-duplicate-move"), @@ -752,6 +787,9 @@ url_manual_mapping = ( ("bpy.ops.object.select_random*", "scene_layout/object/selecting.html#bpy-ops-object-select-random"), ("bpy.ops.paint.add_simple_uvs*", "sculpt_paint/texture_paint/tool_settings/texture_slots.html#bpy-ops-paint-add-simple-uvs"), ("bpy.ops.scene.view_layer_add*", "render/layers/layers.html#bpy-ops-scene-view-layer-add"), + ("bpy.ops.sequencer.gap_insert*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-gap-insert"), + ("bpy.ops.sequencer.gap_remove*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-gap-remove"), + ("bpy.ops.sequencer.rendersize*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-rendersize"), ("bpy.ops.sound.bake_animation*", "scene_layout/scene/properties.html#bpy-ops-sound-bake-animation"), ("bpy.ops.transform.edge_slide*", "modeling/meshes/editing/edge/edge_slide.html#bpy-ops-transform-edge-slide"), ("bpy.ops.transform.vert_slide*", "modeling/meshes/editing/vertex/slide_vertices.html#bpy-ops-transform-vert-slide"), @@ -769,7 +807,7 @@ url_manual_mapping = ( ("bpy.types.compositornodetime*", "compositing/types/input/time.html#bpy-types-compositornodetime"), ("bpy.types.curve.resolution_u*", "modeling/curves/properties/shape.html#bpy-types-curve-resolution-u"), ("bpy.types.curve.resolution_v*", "modeling/surfaces/properties/shape.html#bpy-types-curve-resolution-v"), - ("bpy.types.curvepaintsettings*", "modeling/curves/editing/other.html#bpy-types-curvepaintsettings"), + ("bpy.types.curvepaintsettings*", "modeling/curves/tools/draw.html#bpy-types-curvepaintsettings"), ("bpy.types.fmodifiergenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiergenerator"), ("bpy.types.freestylelinestyle*", "render/freestyle/parameter_editor/line_style/index.html#bpy-types-freestylelinestyle"), ("bpy.types.gammacrosssequence*", "video_editing/sequencer/strips/transitions/cross.html#bpy-types-gammacrosssequence"), @@ -825,7 +863,9 @@ url_manual_mapping = ( ("bpy.ops.object.parent_clear*", "scene_layout/object/editing/parent.html#bpy-ops-object-parent-clear"), ("bpy.ops.object.shade_smooth*", "scene_layout/object/editing/shading.html#bpy-ops-object-shade-smooth"), ("bpy.ops.object.voxel_remesh*", "modeling/meshes/retopology.html#bpy-ops-object-voxel-remesh"), + ("bpy.ops.sequencer.swap_data*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-swap-data"), ("bpy.ops.transform.push_pull*", "modeling/meshes/editing/mesh/transform/push_pull.html#bpy-ops-transform-push-pull"), + ("bpy.ops.transform.seq_slide*", "video_editing/sequencer/editing.html#bpy-ops-transform-seq-slide"), ("bpy.ops.transform.trackball*", "scene_layout/object/editing/transform/basics.html#bpy-ops-transform-trackball"), ("bpy.ops.transform.transform*", "scene_layout/object/editing/transform/align_transform_orientation.html#bpy-ops-transform-transform"), ("bpy.ops.transform.translate*", "scene_layout/object/editing/transform/basics.html#bpy-ops-transform-translate"), @@ -888,7 +928,7 @@ url_manual_mapping = ( ("bpy.ops.mesh.edge_collapse*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-edge-collapse"), ("bpy.ops.mesh.edge_face_add*", "modeling/meshes/editing/vertex/make_face_edge.html#bpy-ops-mesh-edge-face-add"), ("bpy.ops.mesh.knife_project*", "modeling/meshes/editing/mesh/knife_project.html#bpy-ops-mesh-knife-project"), - ("bpy.ops.mesh.loopcut_slide*", "modeling/meshes/tools/loop.html#bpy-ops-mesh-loopcut-slide"), + ("bpy.ops.mesh.loopcut_slide*", "modeling/meshes/editing/edge/loopcut_slide.html#bpy-ops-mesh-loopcut-slide"), ("bpy.ops.mesh.merge_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-merge-normals"), ("bpy.ops.mesh.normals_tools*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-normals-tools"), ("bpy.ops.mesh.point_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-point-normals"), @@ -905,6 +945,7 @@ url_manual_mapping = ( ("bpy.ops.sculpt.mask_expand*", "sculpt_paint/sculpting/hide_mask.html#bpy-ops-sculpt-mask-expand"), ("bpy.ops.sculpt.mask_filter*", "sculpt_paint/sculpting/hide_mask.html#bpy-ops-sculpt-mask-filter"), ("bpy.ops.transform.tosphere*", "modeling/meshes/editing/mesh/transform/to_sphere.html#bpy-ops-transform-tosphere"), + ("bpy.ops.view3d.clip_border*", "editors/3dview/navigate/regions.html#bpy-ops-view3d-clip-border"), ("bpy.ops.wm.previews_ensure*", "files/blend/previews.html#bpy-ops-wm-previews-ensure"), ("bpy.types.actionconstraint*", "animation/constraints/relationship/action.html#bpy-types-actionconstraint"), ("bpy.types.addonpreferences*", "editors/preferences/addons.html#bpy-types-addonpreferences"), @@ -944,6 +985,7 @@ url_manual_mapping = ( ("bpy.types.spacepreferences*", "editors/preferences/index.html#bpy-types-spacepreferences"), ("bpy.types.spaceview3d.lock*", "editors/3dview/properties/sidebar.html#bpy-types-spaceview3d-lock"), ("bpy.types.subtractsequence*", "video_editing/sequencer/strips/effects/subtract.html#bpy-types-subtractsequence"), + ("bpy.types.text.indentation*", "editors/text_editor.html#bpy-types-text-indentation"), ("bpy.types.texturenodegroup*", "editors/texture_node/types/groups.html#bpy-types-texturenodegroup"), ("bpy.types.texturenodeimage*", "editors/texture_node/types/input/image.html#bpy-types-texturenodeimage"), ("bpy.types.viewlayer.use_ao*", "render/layers/layers.html#bpy-types-viewlayer-use-ao"), @@ -977,7 +1019,7 @@ url_manual_mapping = ( ("bpy.types.booleanmodifier*", "modeling/modifiers/generate/booleans.html#bpy-types-booleanmodifier"), ("bpy.types.brush.mask_tool*", "sculpt_paint/sculpting/tools/mask.html#bpy-types-brush-mask-tool"), ("bpy.types.constraint.mute*", "animation/constraints/interface/header.html#bpy-types-constraint-mute"), - ("bpy.types.explodemodifier*", "modeling/modifiers/simulate/explode.html#bpy-types-explodemodifier"), + ("bpy.types.explodemodifier*", "modeling/modifiers/physics/explode.html#bpy-types-explodemodifier"), ("bpy.types.fcurvemodifiers*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fcurvemodifiers"), ("bpy.types.floorconstraint*", "animation/constraints/relationship/floor.html#bpy-types-floorconstraint"), ("bpy.types.fmodifiercycles*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiercycles"), @@ -1021,12 +1063,17 @@ url_manual_mapping = ( ("bpy.ops.object.hide_view*", "scene_layout/object/editing/show_hide.html#bpy-ops-object-hide-view"), ("bpy.ops.object.track_set*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-track-set"), ("bpy.ops.scene.view_layer*", "render/layers/layers.html#bpy-ops-scene-view-layer"), + ("bpy.ops.sequencer.delete*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-delete"), + ("bpy.ops.sequencer.reload*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-reload"), + ("bpy.ops.sequencer.unlock*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-unlock"), + ("bpy.ops.sequencer.unmute*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-unmute"), ("bpy.ops.transform.mirror*", "scene_layout/object/editing/mirror.html#bpy-ops-transform-mirror"), ("bpy.ops.transform.resize*", "scene_layout/object/editing/transform/basics.html#bpy-ops-transform-resize"), ("bpy.ops.transform.rotate*", "scene_layout/object/editing/transform/basics.html#bpy-ops-transform-rotate"), ("bpy.ops.uv.lightmap_pack*", "modeling/meshes/editing/uv.html#bpy-ops-uv-lightmap-pack"), ("bpy.ops.uv.smart_project*", "modeling/meshes/editing/uv.html#bpy-ops-uv-smart-project"), - ("bpy.ops.view3d.localview*", "editors/3dview/navigate/views.html#bpy-ops-view3d-localview"), + ("bpy.ops.view3d.localview*", "editors/3dview/navigate/local_view.html#bpy-ops-view3d-localview"), + ("bpy.ops.view3d.view_axis*", "editors/3dview/navigate/viewpoint.html#bpy-ops-view3d-view-axis"), ("bpy.types.bone.show_wire*", "animation/armatures/bones/properties/display.html#bpy-types-bone-show-wire"), ("bpy.types.brush.hardness*", "sculpt_paint/sculpting/tool_settings/brush_settings.html#bpy-types-brush-hardness"), ("bpy.types.curvesmodifier*", "video_editing/sequencer/properties/modifiers.html#bpy-types-curvesmodifier"), @@ -1043,6 +1090,7 @@ url_manual_mapping = ( ("bpy.types.rigidbodyworld*", "physics/rigid_body/world.html#bpy-types-rigidbodyworld"), ("bpy.types.sceneeevee.ssr*", "render/eevee/render_settings/screen_space_reflections.html#bpy-types-sceneeevee-ssr"), ("bpy.types.sceneeevee.sss*", "render/eevee/render_settings/subsurface_scattering.html#bpy-types-sceneeevee-sss"), + ("bpy.types.sculpt.gravity*", "sculpt_paint/sculpting/tool_settings/options.html#bpy-types-sculpt-gravity"), ("bpy.types.shaderfxshadow*", "grease_pencil/visual_effects/shadow.html#bpy-types-shaderfxshadow"), ("bpy.types.shadernodebump*", "render/shader_nodes/vector/bump.html#bpy-types-shadernodebump"), ("bpy.types.shadernodemath*", "render/shader_nodes/converter/math.html#bpy-types-shadernodemath"), @@ -1076,6 +1124,7 @@ url_manual_mapping = ( ("bpy.ops.object.armature*", "animation/armatures/index.html#bpy-ops-object-armature"), ("bpy.ops.object.face_map*", "modeling/meshes/properties/object_data.html#bpy-ops-object-face-map"), ("bpy.ops.rigidbody.world*", "physics/rigid_body/world.html#bpy-ops-rigidbody-world"), + ("bpy.ops.sequencer.split*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-split"), ("bpy.ops.transform.shear*", "modeling/meshes/editing/mesh/transform/shear.html#bpy-ops-transform-shear"), ("bpy.ops.uv.cube_project*", "modeling/meshes/editing/uv.html#bpy-ops-uv-cube-project"), ("bpy.ops.uv.pack_islands*", "modeling/meshes/uv/editing.html#bpy-ops-uv-pack-islands"), @@ -1103,7 +1152,7 @@ url_manual_mapping = ( ("bpy.types.nlastrip.mute*", "editors/nla/properties_modifiers.html#bpy-types-nlastrip-mute"), ("bpy.types.nlastrip.name*", "editors/nla/properties_modifiers.html#bpy-types-nlastrip-name"), ("bpy.types.object.parent*", "scene_layout/object/editing/parent.html#bpy-types-object-parent"), - ("bpy.types.oceanmodifier*", "modeling/modifiers/simulate/ocean.html#bpy-types-oceanmodifier"), + ("bpy.types.oceanmodifier*", "modeling/modifiers/physics/ocean.html#bpy-types-oceanmodifier"), ("bpy.types.particlebrush*", "physics/particles/mode.html#bpy-types-particlebrush"), ("bpy.types.scene.gravity*", "physics/forces/gravity.html#bpy-types-scene-gravity"), ("bpy.types.sceneeevee.gi*", "render/eevee/render_settings/indirect_lighting.html#bpy-types-sceneeevee-gi"), @@ -1135,6 +1184,11 @@ url_manual_mapping = ( ("bpy.ops.object.convert*", "scene_layout/object/editing/convert.html#bpy-ops-object-convert"), ("bpy.ops.object.gpencil*", "grease_pencil/index.html#bpy-ops-object-gpencil"), ("bpy.ops.object.speaker*", "render/output/audio/speaker.html#bpy-ops-object-speaker"), + ("bpy.ops.sequencer.lock*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-lock"), + ("bpy.ops.sequencer.mute*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-mute"), + ("bpy.ops.sequencer.slip*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-slip"), + ("bpy.ops.sequencer.snap*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-snap"), + ("bpy.ops.sequencer.swap*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-swap"), ("bpy.ops.transform.bend*", "modeling/meshes/editing/mesh/transform/bend.html#bpy-ops-transform-bend"), ("bpy.ops.transform.tilt*", "modeling/curves/editing/control_points.html#bpy-ops-transform-tilt"), ("bpy.ops.wm.search_menu*", "interface/controls/templates/operator_search.html#bpy-ops-wm-search-menu"), @@ -1219,6 +1273,7 @@ url_manual_mapping = ( ("bpy.ops.object.align*", "scene_layout/object/editing/transform/align_objects.html#bpy-ops-object-align"), ("bpy.ops.object.empty*", "modeling/empties.html#bpy-ops-object-empty"), ("bpy.ops.object.quick*", "physics/introduction.html#bpy-ops-object-quick"), + ("bpy.ops.text.replace*", "editors/text_editor.html#bpy-ops-text-replace"), ("bpy.ops.uv.mark_seam*", "modeling/meshes/uv/unwrapping/seams.html#bpy-ops-uv-mark-seam"), ("bpy.ops.view3d.ruler*", "editors/3dview/toolbar/measure.html#bpy-ops-view3d-ruler"), ("bpy.types.areaspaces*", "interface/window_system/areas.html#bpy-types-areaspaces"), @@ -1264,7 +1319,7 @@ url_manual_mapping = ( ("bpy.types.viewlayer*", "render/layers/layers.html#bpy-types-viewlayer"), ("bpy.ops.collection*", "scene_layout/collections/collections.html#bpy-ops-collection"), ("bpy.ops.constraint*", "animation/constraints/index.html#bpy-ops-constraint"), - ("bpy.ops.curve.draw*", "modeling/curves/editing/other.html#bpy-ops-curve-draw"), + ("bpy.ops.curve.draw*", "modeling/curves/tools/draw.html#bpy-ops-curve-draw"), ("bpy.ops.curve.hide*", "modeling/curves/editing/curve.html#bpy-ops-curve-hide"), ("bpy.ops.curve.spin*", "modeling/surfaces/editing/surface.html#bpy-ops-curve-spin"), ("bpy.ops.graph.bake*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-bake"), @@ -1303,6 +1358,7 @@ url_manual_mapping = ( ("bpy.ops.nla.split*", "editors/nla/editing.html#bpy-ops-nla-split"), ("bpy.ops.rigidbody*", "physics/rigid_body/index.html#bpy-ops-rigidbody"), ("bpy.ops.sequencer*", "video_editing/index.html#bpy-ops-sequencer"), + ("bpy.ops.text.find*", "editors/text_editor.html#bpy-ops-text-find"), ("bpy.ops.transform*", "scene_layout/object/editing/transform/index.html#bpy-ops-transform"), ("bpy.ops.uv.select*", "editors/uv/selecting.html#bpy-ops-uv-select"), ("bpy.ops.uv.stitch*", "modeling/meshes/uv/editing.html#bpy-ops-uv-stitch"), diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 4b037f209bb..3a885837444 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -738,6 +738,9 @@ def km_property_editor(_params): ("object.gpencil_modifier_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}), ("object.gpencil_modifier_copy", {"type": 'D', "value": 'PRESS', "shift": True}, None), ("object.gpencil_modifier_apply", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("report", True)]}), + # ShaderFX panels + ("object.shaderfx_remove", {"type": 'X', "value": 'PRESS'}, {"properties": [("report", True)]}), + ("object.shaderfx_remove", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("report", True)]}), ]) return keymap @@ -847,6 +850,7 @@ def km_uv_editor(params): {"properties": [("extend", False)]}), ("uv.select_loop", {"type": params.select_mouse, "value": params.select_mouse_value, "shift": True, "alt": True}, {"properties": [("extend", True)]}), + ("uv.shortest_path_pick", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True}, None), ("uv.select_split", {"type": 'Y', "value": 'PRESS'}, None), ("uv.select_box", {"type": 'B', "value": 'PRESS'}, {"properties": [("pinned", False)]}), @@ -866,8 +870,11 @@ def km_uv_editor(params): ("uv.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True}, None), *_template_items_select_actions(params, "uv.select_all"), ("uv.select_pinned", {"type": 'P', "value": 'PRESS', "shift": True}, None), - op_menu("IMAGE_MT_uvs_weldalign", {"type": 'W', "value": 'PRESS', "shift": True}), - ("uv.stitch", {"type": 'V', "value": 'PRESS'}, None), + op_menu("IMAGE_MT_uvs_merge", {"type": 'M', "value": 'PRESS'}), + op_menu("IMAGE_MT_uvs_split", {"type": 'M', "value": 'PRESS', "alt": True}), + op_menu("IMAGE_MT_uvs_align", {"type": 'W', "value": 'PRESS', "shift": True}), + ("uv.stitch", {"type": 'V', "value": 'PRESS', "alt": True}, None), + ("uv.rip_move", {"type": 'V', "value": 'PRESS'}, None), ("uv.pin", {"type": 'P', "value": 'PRESS'}, {"properties": [("clear", False)]}), ("uv.pin", {"type": 'P', "value": 'PRESS', "alt": True}, @@ -5589,6 +5596,17 @@ def km_image_editor_tool_uv_select_lasso(params): ) +def km_image_editor_tool_uv_rip_region(params): + return ( + "Image Editor Tool: Uv, Rip Region", + {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, + {"items": [ + ("uv.rip_move", {"type": params.tool_tweak, "value": 'ANY'}, + {"properties": [("TRANSFORM_OT_translate", [("release_confirm", True)])]}), + ]}, + ) + + def km_image_editor_tool_uv_sculpt_stroke(params): return ( "Image Editor Tool: Uv, Sculpt Stroke", @@ -6791,6 +6809,7 @@ def generate_keymaps(params=None): km_image_editor_tool_uv_select_box(params), km_image_editor_tool_uv_select_circle(params), km_image_editor_tool_uv_select_lasso(params), + km_image_editor_tool_uv_rip_region(params), km_image_editor_tool_uv_sculpt_stroke(params), km_image_editor_tool_uv_move(params), km_image_editor_tool_uv_rotate(params), diff --git a/release/scripts/startup/bl_operators/view3d.py b/release/scripts/startup/bl_operators/view3d.py index a8e04eb2f24..02bfebbdc0c 100644 --- a/release/scripts/startup/bl_operators/view3d.py +++ b/release/scripts/startup/bl_operators/view3d.py @@ -159,6 +159,33 @@ class VIEW3D_OT_edit_mesh_extrude_shrink_fatten(Operator): return self.execute(context) +class VIEW3D_OT_edit_mesh_extrude_manifold_normal(Operator): + """Extrude manifold region along normals""" + bl_label = "Extrude Manifold Along Normals" + bl_idname = "view3d.edit_mesh_extrude_manifold_normal" + + @classmethod + def poll(cls, context): + obj = context.active_object + return (obj is not None and obj.mode == 'EDIT') + + def execute(self, context): + bpy.ops.mesh.extrude_manifold( + 'INVOKE_REGION_WIN', + MESH_OT_extrude_region={ + "use_dissolve_ortho_edges": True, + }, + TRANSFORM_OT_translate={ + "orient_type": 'NORMAL', + "constraint_axis": (False, False, True), + }, + ) + return {'FINISHED'} + + def invoke(self, context, _event): + return self.execute(context) + + class VIEW3D_OT_transform_gizmo_set(Operator): """Set the current transform gizmo""" bl_label = "Transform Gizmo Set" @@ -208,5 +235,6 @@ classes = ( VIEW3D_OT_edit_mesh_extrude_individual_move, VIEW3D_OT_edit_mesh_extrude_move, VIEW3D_OT_edit_mesh_extrude_shrink_fatten, + VIEW3D_OT_edit_mesh_extrude_manifold_normal, VIEW3D_OT_transform_gizmo_set, ) diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index a543ea6685c..fc11e4a0a87 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -1466,8 +1466,7 @@ class WM_OT_properties_edit(Operator): row = layout.row() row.prop(self, "use_soft_limits") - if bpy.app.use_override_library: - row.prop(self, "is_overridable_library") + row.prop(self, "is_overridable_library") row = layout.row(align=True) row.enabled = self.use_soft_limits diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py index 8bae32775a9..215c96a5975 100644 --- a/release/scripts/startup/bl_ui/properties_constraint.py +++ b/release/scripts/startup/bl_ui/properties_constraint.py @@ -971,7 +971,7 @@ class ConstraintButtonsSubPanel(Panel): self.layout.context_pointer_set("constraint", con) return con - def draw_transform_source(self, context): + def draw_transform_from(self, context): layout = self.layout con = self.get_constraint(context) @@ -980,36 +980,29 @@ class ConstraintButtonsSubPanel(Panel): layout.use_property_split = True layout.use_property_decorate = True + from_axes = [con.map_to_x_from, con.map_to_y_from, con.map_to_z_from] + if con.map_from == 'ROTATION': layout.prop(con, "from_rotation_mode", text="Mode") ext = "" if con.map_from == 'LOCATION' else "_rot" if con.map_from == 'ROTATION' else "_scale" col = layout.column(align=True) + col.active = "X" in from_axes col.prop(con, "from_min_x" + ext, text="X Min") col.prop(con, "from_max_x" + ext, text="Max") col = layout.column(align=True) + col.active = "Y" in from_axes col.prop(con, "from_min_y" + ext, text="Y Min") col.prop(con, "from_max_y" + ext, text="Max") col = layout.column(align=True) + col.active = "Z" in from_axes col.prop(con, "from_min_z" + ext, text="Z Min") col.prop(con, "from_max_z" + ext, text="Max") - def draw_transform_mapping(self, context): - layout = self.layout - con = self.get_constraint(context) - layout.use_property_split = True - layout.use_property_decorate = True - - layout.prop(con, "map_to_x_from", expand=False, text="Source Axis X") - - layout.prop(con, "map_to_y_from", expand=False, text="Y") - - layout.prop(con, "map_to_z_from", expand=False, text="Z") - - def draw_transform_destination(self, context): + def draw_transform_to(self, context): layout = self.layout con = self.get_constraint(context) @@ -1024,15 +1017,18 @@ class ConstraintButtonsSubPanel(Panel): ext = "" if con.map_to == 'LOCATION' else "_rot" if con.map_to == 'ROTATION' else "_scale" col = layout.column(align=True) - col.prop(con, "to_min_x" + ext, text="X Min") + col.prop(con, "map_to_x_from", expand=False, text="X Source Axis") + col.prop(con, "to_min_x" + ext, text="Min") col.prop(con, "to_max_x" + ext, text="Max") col = layout.column(align=True) - col.prop(con, "to_min_y" + ext, text="Y Min") + col.prop(con, "map_to_y_from", expand=False, text="Y Source Axis") + col.prop(con, "to_min_y" + ext, text="Min") col.prop(con, "to_max_y" + ext, text="Max") col = layout.column(align=True) - col.prop(con, "to_min_z" + ext, text="Z Min") + col.prop(con, "map_to_z_from", expand=False, text="Z Source Axis") + col.prop(con, "to_min_z" + ext, text="Min") col.prop(con, "to_max_z" + ext, text="Max") layout.prop(con, "mix_mode" + ext, text="Mix") @@ -1387,50 +1383,34 @@ class BONE_PT_bTransformConstraint(BoneConstraintPanel, ConstraintButtonsPanel): class OBJECT_PT_bTransformConstraint_source(ObjectConstraintPanel, ConstraintButtonsSubPanel): bl_parent_id = "OBJECT_PT_bTransformConstraint" - bl_label = "Source" + bl_label = "Map From" def draw(self, context): - self.draw_transform_source(context) + self.draw_transform_from(context) -class BONE_PT_bTransformConstraint_source(BoneConstraintPanel, ConstraintButtonsSubPanel): +class BONE_PT_bTransformConstraint_from(BoneConstraintPanel, ConstraintButtonsSubPanel): bl_parent_id = "BONE_PT_bTransformConstraint" - bl_label = "Source" + bl_label = "Map From" def draw(self, context): - self.draw_transform_source(context) - - -class OBJECT_PT_bTransformConstraint_mapping(ObjectConstraintPanel, ConstraintButtonsSubPanel): - bl_parent_id = "OBJECT_PT_bTransformConstraint" - bl_label = "Mapping" - - def draw(self, context): - self.draw_transform_mapping(context) - - -class BONE_PT_bTransformConstraint_mapping(BoneConstraintPanel, ConstraintButtonsSubPanel): - bl_parent_id = "BONE_PT_bTransformConstraint" - bl_label = "Mapping" - - def draw(self, context): - self.draw_transform_mapping(context) + self.draw_transform_from(context) class OBJECT_PT_bTransformConstraint_destination(ObjectConstraintPanel, ConstraintButtonsSubPanel): bl_parent_id = "OBJECT_PT_bTransformConstraint" - bl_label = "Destination" + bl_label = "Map To" def draw(self, context): - self.draw_transform_destination(context) + self.draw_transform_to(context) -class BONE_PT_bTransformConstraint_destination(BoneConstraintPanel, ConstraintButtonsSubPanel): +class BONE_PT_bTransformConstraint_to(BoneConstraintPanel, ConstraintButtonsSubPanel): bl_parent_id = "BONE_PT_bTransformConstraint" - bl_label = "Destination" + bl_label = "Map To" def draw(self, context): - self.draw_transform_destination(context) + self.draw_transform_to(context) # Shrinkwrap Constraint @@ -1619,7 +1599,6 @@ classes = ( OBJECT_PT_bClampToConstraint, OBJECT_PT_bTransformConstraint, OBJECT_PT_bTransformConstraint_source, - OBJECT_PT_bTransformConstraint_mapping, OBJECT_PT_bTransformConstraint_destination, OBJECT_PT_bShrinkwrapConstraint, OBJECT_PT_bDampTrackConstraint, @@ -1653,9 +1632,8 @@ classes = ( BONE_PT_bMinMaxConstraint, BONE_PT_bClampToConstraint, BONE_PT_bTransformConstraint, - BONE_PT_bTransformConstraint_source, - BONE_PT_bTransformConstraint_mapping, - BONE_PT_bTransformConstraint_destination, + BONE_PT_bTransformConstraint_from, + BONE_PT_bTransformConstraint_to, BONE_PT_bShrinkwrapConstraint, BONE_PT_bDampTrackConstraint, BONE_PT_bSplineIKConstraint, diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index fbd8e2d7cff..27df265d013 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -464,6 +464,10 @@ class DATA_PT_sculpt_vertex_colors(MeshButtonsPanel, Panel): bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + @classmethod + def poll(cls, context): + return context.preferences.experimental.use_sculpt_vertex_colors + def draw(self, context): layout = self.layout @@ -508,7 +512,8 @@ class DATA_PT_remesh(MeshButtonsPanel, Panel): col.prop(mesh, "use_remesh_preserve_volume", text="Volume") col.prop(mesh, "use_remesh_preserve_paint_mask", text="Paint Mask") col.prop(mesh, "use_remesh_preserve_sculpt_face_sets", text="Face Sets") - col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Vertex Colors") + if context.preferences.experimental.use_sculpt_vertex_colors: + col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Vertex Colors") col.operator("object.voxel_remesh", text="Voxel Remesh") else: diff --git a/release/scripts/startup/bl_ui/properties_freestyle.py b/release/scripts/startup/bl_ui/properties_freestyle.py index f70789ebeed..54b1ca3d910 100644 --- a/release/scripts/startup/bl_ui/properties_freestyle.py +++ b/release/scripts/startup/bl_ui/properties_freestyle.py @@ -115,6 +115,15 @@ class VIEWLAYER_PT_freestyle(ViewLayerFreestyleButtonsPanel, Panel): bl_label = "Freestyle" COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'} + def draw_header(self, context): + view_layer = context.view_layer + rd = context.scene.render + + layout = self.layout + + layout.active = rd.use_freestyle + layout.prop(view_layer, "use_freestyle", text="") + def draw(self, context): layout = self.layout diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 209231cacb0..004bcaf819b 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -192,6 +192,10 @@ class ColorPalettePanel(BrushPanel): elif context.vertex_paint_object: capabilities = brush.vertex_paint_capabilities return capabilities.has_color + + elif context.sculpt_object: + capabilities = brush.sculpt_capabilities + return capabilities.has_color return False def draw(self, context): @@ -678,6 +682,10 @@ def brush_settings(layout, context, brush, popover=False): col.prop(brush, "tip_roundness") col.prop(brush, "tip_scale_x") + if brush.sculpt_tool == 'SMEAR': + col = layout.column() + col.prop(brush, "smear_deform_type") + if brush.sculpt_tool == 'MULTIPLANE_SCRAPE': col = layout.column() col.prop(brush, "multiplane_scrape_angle") diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index 73bc5fe5c29..032a4a612c8 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -183,7 +183,7 @@ class CLIP_HT_header(Header): r = active_object.reconstruction if r.is_valid and sc.view == 'CLIP': - layout.label(text="Solve error: %.4f" % + layout.label(text="Solve error: %.2f px" % (r.average_error)) row = layout.row() @@ -741,7 +741,7 @@ class CLIP_PT_track(CLIP_PT_tracking_panel, Panel): layout.prop(act_track, "weight_stab") if act_track.has_bundle: - label_text = "Average Error: %.4f" % (act_track.average_error) + label_text = "Average Error: %.2f px" % (act_track.average_error) layout.label(text=label_text) layout.use_property_split = False diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 76b7fc7f156..651866cf316 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -176,7 +176,7 @@ class IMAGE_MT_select(Menu): layout.separator() layout.operator("uv.select_pinned") - layout.operator("uv.select_linked") + layout.menu("IMAGE_MT_select_linked") layout.separator() @@ -184,6 +184,16 @@ class IMAGE_MT_select(Menu): layout.operator("uv.select_overlap") +class IMAGE_MT_select_linked(Menu): + bl_label = "Select Linked" + + def draw(self, _context): + layout = self.layout + + layout.operator("uv.select_linked", text="Linked") + layout.operator("uv.shortest_path_select", text="Shortest Path") + + class IMAGE_MT_image(Menu): bl_label = "Image" @@ -321,15 +331,37 @@ class IMAGE_MT_uvs_mirror(Menu): layout.operator("transform.mirror", text="Y Axis").constraint_axis[1] = True -class IMAGE_MT_uvs_weldalign(Menu): - bl_label = "Weld/Align" +class IMAGE_MT_uvs_align(Menu): + bl_label = "Align" + + def draw(self, _context): + layout = self.layout + + layout.operator_enum("uv.align", "axis") + + +class IMAGE_MT_uvs_merge(Menu): + bl_label = "Merge" def draw(self, _context): layout = self.layout - layout.operator("uv.weld") # W, 1. - layout.operator("uv.remove_doubles") - layout.operator_enum("uv.align", "axis") # W, 2/3/4. + layout.operator("uv.weld", text="At Center") + # Mainly to match the mesh menu. + layout.operator("uv.snap_selected", text="At Cursor").target = 'CURSOR' + + layout.separator() + + layout.operator("uv.remove_doubles", text="By Distance") + + +class IMAGE_MT_uvs_split(Menu): + bl_label = "Split" + + def draw(self, _context): + layout = self.layout + + layout.operator("uv.select_split", text="Selection") class IMAGE_MT_uvs(Menu): @@ -350,6 +382,11 @@ class IMAGE_MT_uvs(Menu): layout.separator() + layout.menu("IMAGE_MT_uvs_merge") + layout.menu("IMAGE_MT_uvs_split") + + layout.separator() + layout.prop(uv, "use_live_unwrap") layout.operator("uv.unwrap") @@ -373,7 +410,7 @@ class IMAGE_MT_uvs(Menu): layout.operator("uv.minimize_stretch") layout.operator("uv.stitch") - layout.menu("IMAGE_MT_uvs_weldalign") + layout.menu("IMAGE_MT_uvs_align") layout.separator() @@ -462,7 +499,7 @@ class IMAGE_MT_uvs_context_menu(Menu): layout.separator() # Remove - layout.operator("uv.remove_doubles", text="Remove Double UVs") + layout.operator("uv.remove_doubles", text="Merge By Distance") layout.operator("uv.stitch") layout.operator("uv.weld") @@ -1452,6 +1489,7 @@ classes = ( IMAGE_MT_view, IMAGE_MT_view_zoom, IMAGE_MT_select, + IMAGE_MT_select_linked, IMAGE_MT_image, IMAGE_MT_image_invert, IMAGE_MT_uvs, @@ -1459,7 +1497,9 @@ classes = ( IMAGE_MT_uvs_transform, IMAGE_MT_uvs_snap, IMAGE_MT_uvs_mirror, - IMAGE_MT_uvs_weldalign, + IMAGE_MT_uvs_align, + IMAGE_MT_uvs_merge, + IMAGE_MT_uvs_split, IMAGE_MT_uvs_select_mode, IMAGE_MT_uvs_context_menu, IMAGE_MT_mask_context_menu, diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 87464451632..b7852eb92e0 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -50,11 +50,14 @@ def generate_from_enum_ex( attr, cursor='DEFAULT', tooldef_keywords={}, + exclude_filter = {} ): tool_defs = [] for enum in type.bl_rna.properties[attr].enum_items_static: name = enum.name idname = enum.identifier + if idname in exclude_filter: + continue tool_defs.append( ToolDef.from_dict( dict( @@ -1178,12 +1181,18 @@ class _defs_sculpt: @staticmethod def generate_from_brushes(context): + if bpy.context.preferences.experimental.use_sculpt_vertex_colors: + exclude_filter = {} + else: + exclude_filter = {'PAINT', 'SMEAR'} + return generate_from_enum_ex( context, idname_prefix="builtin_brush.", icon_prefix="brush.sculpt.", type=bpy.types.Brush, attr="sculpt_tool", + exclude_filter = exclude_filter, ) @ToolDef.from_fn @@ -1579,6 +1588,20 @@ class _defs_image_uv_select: ) +class _defs_image_uv_edit: + + @ToolDef.from_fn + def rip_region(): + return dict( + idname="builtin.rip_region", + label="Rip Region", + icon="ops.mesh.rip", + # TODO: generic operator (UV version of `VIEW3D_GGT_tool_generic_handle_free`). + widget=None, + keymap=(), + ) + + class _defs_image_uv_sculpt: @staticmethod @@ -2173,6 +2196,8 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel): None, *_tools_annotate, None, + _defs_image_uv_edit.rip_region, + None, lambda context: ( _defs_image_uv_sculpt.generate_from_brushes(context) if _defs_image_generic.poll_uvedit(context) @@ -2469,9 +2494,19 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): None, _defs_sculpt.mesh_filter, _defs_sculpt.cloth_filter, - _defs_sculpt.color_filter, + lambda context: ( + (_defs_sculpt.color_filter,) + if bpy.context.preferences.view.show_developer_ui and \ + bpy.context.preferences.experimental.use_sculpt_vertex_colors + else () + ), None, - _defs_sculpt.mask_by_color, + lambda context: ( + (_defs_sculpt.mask_by_color,) + if bpy.context.preferences.view.show_developer_ui and \ + bpy.context.preferences.experimental.use_sculpt_vertex_colors + else () + ), None, _defs_transform.translate, _defs_transform.rotate, diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index e5171df597a..5392ed9cc25 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -2143,6 +2143,7 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel): self._draw_items( context, ( ({"property": "use_new_particle_system"}, "T73324"), + ({"property": "use_sculpt_vertex_colors"}, "T71947"), ), ) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index df2b3fcdbeb..e4d0423cd76 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -2256,8 +2256,7 @@ class VIEW3D_MT_object_relations(Menu): layout.operator("object.proxy_make", text="Make Proxy...") - if bpy.app.use_override_library: - layout.operator("object.make_override_library", text="Make Library Override...") + layout.operator("object.make_override_library", text="Make Library Override...") layout.operator("object.make_dupli_face") @@ -3877,6 +3876,8 @@ class VIEW3D_MT_edit_mesh_extrude(Menu): layout.operator("view3d.edit_mesh_extrude_move_shrink_fatten", text="Extrude Faces Along Normals"), 'FACE': lambda layout: layout.operator("mesh.extrude_faces_move", text="Extrude Individual Faces"), + 'MANIFOLD': lambda layout: + layout.operator("view3d.edit_mesh_extrude_manifold_normal", text="Extrude Manifold"), } @staticmethod @@ -3887,7 +3888,7 @@ class VIEW3D_MT_edit_mesh_extrude(Menu): menu = [] if mesh.total_face_sel: - menu += ['REGION', 'REGION_VERT_NORMAL', 'FACE'] + menu += ['REGION', 'REGION_VERT_NORMAL', 'FACE', 'MANIFOLD'] if mesh.total_edge_sel and (select_mode[0] or select_mode[1]): menu += ['EDGE'] if mesh.total_vert_sel and select_mode[0]: diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 549b89938e0..39e4ee2beac 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -737,7 +737,8 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): @classmethod def poll(cls, context): - return (context.sculpt_object and context.tool_settings.sculpt) + paint_settings = cls.paint_settings(context) + return (context.sculpt_object and context.tool_settings.sculpt and paint_settings) def draw_header(self, context): is_popover = self.is_popover @@ -811,7 +812,8 @@ class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel): col.prop(mesh, "use_remesh_preserve_volume", text="Volume") col.prop(mesh, "use_remesh_preserve_paint_mask", text="Paint Mask") col.prop(mesh, "use_remesh_preserve_sculpt_face_sets", text="Face Sets") - col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Vertex Colors") + if context.preferences.experimental.use_sculpt_vertex_colors: + col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Vertex Colors") layout.operator("object.voxel_remesh", text="Remesh") diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 2dc6c6cd409..50c1a2e4e4e 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -484,6 +484,7 @@ simulation_node_categories = [ NodeItem("SimulationNodeTime"), NodeItem("SimulationNodeParticleAttribute"), NodeItem("FunctionNodeGroupInstanceID"), + NodeItem("ShaderNodeValue"), ]), SimulationNodeCategory("SIM_EMITTERS", "Emitters", items=[ NodeItem("SimulationNodeParticleMeshEmitter"), diff --git a/source/blender/blenkernel/BKE_anim_data.h b/source/blender/blenkernel/BKE_anim_data.h index 5aeaf4405f5..48e95740b9d 100644 --- a/source/blender/blenkernel/BKE_anim_data.h +++ b/source/blender/blenkernel/BKE_anim_data.h @@ -71,7 +71,11 @@ bool BKE_animdata_copy_id(struct Main *bmain, const int flag); /* Copy AnimData Actions */ -void BKE_animdata_copy_id_action(struct Main *bmain, struct ID *id, const bool set_newid); +void BKE_animdata_copy_id_action(struct Main *bmain, struct ID *id); + +void BKE_animdata_duplicate_id_action(struct Main *bmain, + struct ID *id, + const uint duplicate_flags); /* Merge copies of data from source AnimData block */ typedef enum eAnimData_MergeCopy_Modes { diff --git a/source/blender/blenkernel/BKE_cloth.h b/source/blender/blenkernel/BKE_cloth.h index f25482570b1..cd78746a3f0 100644 --- a/source/blender/blenkernel/BKE_cloth.h +++ b/source/blender/blenkernel/BKE_cloth.h @@ -93,10 +93,10 @@ typedef struct Cloth { struct Implicit_Data *implicit; /* our implicit solver connects to this pointer */ struct EdgeSet *edgeset; /* used for selfcollisions */ int last_frame; - float initial_mesh_volume; /* Initial volume of the mesh. Used for pressure */ - float average_acceleration[3]; /* Moving average of overall acceleration. */ - struct MEdge *edges; /* Used for hair collisions. */ - struct GHash *sew_edge_graph; /* Sewing edges represented using a GHash */ + float initial_mesh_volume; /* Initial volume of the mesh. Used for pressure */ + float average_acceleration[3]; /* Moving average of overall acceleration. */ + struct MEdge *edges; /* Used for hair collisions. */ + struct EdgeSet *sew_edge_graph; /* Sewing edges represented using a GHash */ } Cloth; /** diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h index d32ab474229..ca41e51984b 100644 --- a/source/blender/blenkernel/BKE_curve.h +++ b/source/blender/blenkernel/BKE_curve.h @@ -140,7 +140,7 @@ void BKE_curve_nurbs_vert_coords_apply(struct ListBase *lb, float (*BKE_curve_nurbs_key_vert_coords_alloc(struct ListBase *lb, float *key, int *r_vert_len))[3]; -void BKE_curve_nurbs_key_vert_tilts_apply(struct ListBase *lb, float *key); +void BKE_curve_nurbs_key_vert_tilts_apply(struct ListBase *lb, const float *key); void BKE_curve_editNurb_keyIndex_delCV(struct GHash *keyindex, const void *cv); void BKE_curve_editNurb_keyIndex_free(struct GHash **keyindex); diff --git a/source/blender/blenkernel/BKE_deform.h b/source/blender/blenkernel/BKE_deform.h index 04b85aebb39..f59e796c98c 100644 --- a/source/blender/blenkernel/BKE_deform.h +++ b/source/blender/blenkernel/BKE_deform.h @@ -111,7 +111,7 @@ void BKE_defvert_sync_mapped(struct MDeformVert *dvert_dst, const int *flip_map, const int flip_map_len, const bool use_ensure); -void BKE_defvert_remap(struct MDeformVert *dvert, int *map, const int map_len); +void BKE_defvert_remap(struct MDeformVert *dvert, const int *map, const int map_len); void BKE_defvert_flip(struct MDeformVert *dvert, const int *flip_map, const int flip_map_len); void BKE_defvert_flip_merged(struct MDeformVert *dvert, const int *flip_map, diff --git a/source/blender/blenkernel/BKE_derived_node_tree.hh b/source/blender/blenkernel/BKE_derived_node_tree.hh index 2ed96f0c60d..083057835a5 100644 --- a/source/blender/blenkernel/BKE_derived_node_tree.hh +++ b/source/blender/blenkernel/BKE_derived_node_tree.hh @@ -140,6 +140,9 @@ class DNode : NonCopyable, NonMovable { const DInputSocket &input(uint index) const; const DOutputSocket &output(uint index) const; + const DInputSocket &input(uint index, StringRef expected_name) const; + const DOutputSocket &output(uint index, StringRef expected_name) const; + uint id() const; PointerRNA *rna() const; @@ -313,12 +316,12 @@ inline const InputSocketRef &DInputSocket::socket_ref() const inline Span<const DOutputSocket *> DInputSocket::linked_sockets() const { - return linked_sockets_.as_span(); + return linked_sockets_; } inline Span<const DGroupInput *> DInputSocket::linked_group_inputs() const { - return linked_group_inputs_.as_span(); + return linked_group_inputs_; } inline bool DInputSocket::is_linked() const @@ -337,7 +340,7 @@ inline const OutputSocketRef &DOutputSocket::socket_ref() const inline Span<const DInputSocket *> DOutputSocket::linked_sockets() const { - return linked_sockets_.as_span(); + return linked_sockets_; } /* -------------------------------------------------------------------- @@ -361,7 +364,7 @@ inline const DParentNode *DGroupInput::parent() const inline Span<const DInputSocket *> DGroupInput::linked_sockets() const { - return linked_sockets_.as_span(); + return linked_sockets_; } inline uint DGroupInput::id() const @@ -408,6 +411,22 @@ inline const DOutputSocket &DNode::output(uint index) const return *outputs_[index]; } +inline const DInputSocket &DNode::input(uint index, StringRef expected_name) const +{ + const DInputSocket &socket = *inputs_[index]; + BLI_assert(socket.name() == expected_name); + UNUSED_VARS_NDEBUG(expected_name); + return socket; +} + +inline const DOutputSocket &DNode::output(uint index, StringRef expected_name) const +{ + const DOutputSocket &socket = *outputs_[index]; + BLI_assert(socket.name() == expected_name); + UNUSED_VARS_NDEBUG(expected_name); + return socket; +} + inline uint DNode::id() const { return id_; @@ -453,7 +472,7 @@ inline uint DParentNode::id() const inline Span<const DNode *> DerivedNodeTree::nodes() const { - return nodes_by_id_.as_span(); + return nodes_by_id_; } inline Span<const DNode *> DerivedNodeTree::nodes_by_type(StringRefNull idname) const @@ -469,28 +488,28 @@ inline Span<const DNode *> DerivedNodeTree::nodes_by_type(const bNodeType *nodet return {}; } else { - return nodes->as_span(); + return *nodes; } } inline Span<const DSocket *> DerivedNodeTree::sockets() const { - return sockets_by_id_.as_span(); + return sockets_by_id_; } inline Span<const DInputSocket *> DerivedNodeTree::input_sockets() const { - return input_sockets_.as_span(); + return input_sockets_; } inline Span<const DOutputSocket *> DerivedNodeTree::output_sockets() const { - return output_sockets_.as_span(); + return output_sockets_; } inline Span<const DGroupInput *> DerivedNodeTree::group_inputs() const { - return group_inputs_.as_span(); + return group_inputs_; } } // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_duplilist.h b/source/blender/blenkernel/BKE_duplilist.h index 71b6d06b450..13918dd4fb1 100644 --- a/source/blender/blenkernel/BKE_duplilist.h +++ b/source/blender/blenkernel/BKE_duplilist.h @@ -52,7 +52,7 @@ typedef struct DupliObject { /* Persistent identifier for a dupli object, for inter-frame matching of * objects with motion blur, or inter-update matching for syncing. */ - int persistent_id[16]; /* 2*MAX_DUPLI_RECUR */ + int persistent_id[8]; /* MAX_DUPLI_RECUR */ /* Particle this dupli was generated from. */ struct ParticleSystem *particle_system; diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index c3a597e29b9..ddd0cc286ab 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -271,7 +271,7 @@ void testhandles_fcurve(struct FCurve *fcu, eBezTriple_Flag sel_flag, const bool void sort_time_fcurve(struct FCurve *fcu); short test_time_fcurve(struct FCurve *fcu); -void correct_bezpart(float v1[2], float v2[2], float v3[2], float v4[2]); +void correct_bezpart(const float v1[2], float v2[2], float v3[2], const float v4[2]); /* -------- Evaluation -------- */ diff --git a/source/blender/blenkernel/BKE_fluid.h b/source/blender/blenkernel/BKE_fluid.h index c958afb212e..2392263181d 100644 --- a/source/blender/blenkernel/BKE_fluid.h +++ b/source/blender/blenkernel/BKE_fluid.h @@ -37,7 +37,7 @@ struct Main; struct Scene; typedef float (*BKE_Fluid_BresenhamFn)( - float *result, float *input, int res[3], int *pixel, float *tRay, float correct); + float *result, const float *input, int res[3], int *pixel, float *tRay, float correct); struct Mesh *BKE_fluid_modifier_do(struct FluidModifierData *fmd, struct Depsgraph *depsgraph, @@ -56,9 +56,9 @@ bool BKE_fluid_reallocate_fluid(struct FluidDomainSettings *fds, int res[3], int void BKE_fluid_reallocate_copy_fluid(struct FluidDomainSettings *fds, int o_res[3], int n_res[3], - int o_min[3], - int n_min[3], - int o_max[3], + const int o_min[3], + const int n_min[3], + const int o_max[3], int o_shift[3], int n_shift[3]); void BKE_fluid_cache_free_all(struct FluidDomainSettings *fds, struct Object *ob); diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index a823693e126..38322427374 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -47,9 +47,9 @@ enum { }; typedef struct IDCacheKey { - /* The session uuid of the ID owning the cached data. */ + /* The session UUID of the ID owning the cached data. */ unsigned int id_session_uuid; - /* Value uniquely indentifying the cache whithin its ID. + /* Value uniquely identifying the cache within its ID. * Typically the offset of its member in the data-block struct, but can be anything. */ size_t offset_in_ID; /* Actual address of the cached data to save and restore. */ @@ -59,7 +59,7 @@ typedef struct IDCacheKey { uint BKE_idtype_cache_key_hash(const void *key_v); bool BKE_idtype_cache_key_cmp(const void *key_a_v, const void *key_b_v); -/* ********** Prototypes for IDTypeInfo callbacks. ********** */ +/* ********** Prototypes for #IDTypeInfo callbacks. ********** */ typedef void (*IDTypeInitDataFunction)(struct ID *id); @@ -76,9 +76,15 @@ typedef void (*IDTypeMakeLocalFunction)(struct Main *bmain, struct ID *id, const typedef void (*IDTypeForeachIDFunction)(struct ID *id, struct LibraryForeachIDData *data); +typedef enum eIDTypeInfoCacheCallbackFlags { + /** Indicates to the callback that that cache may be stored in the .blend file, so its pointer + * should not be cleared at read-time. */ + IDTYPE_CACHE_CB_FLAGS_PERSISTENT = 1 << 0, +} eIDTypeInfoCacheCallbackFlags; typedef void (*IDTypeForeachCacheFunctionCallback)(struct ID *id, const struct IDCacheKey *cache_key, void **cache_p, + uint flags, void *user_data); typedef void (*IDTypeForeachCacheFunction)(struct ID *id, IDTypeForeachCacheFunctionCallback function_callback, @@ -229,6 +235,14 @@ short BKE_idtype_idcode_from_index(const int index); short BKE_idtype_idcode_iter_step(int *index); +/* Some helpers/wrappers around callbacks defined in #IDTypeInfo, dealing e.g. with embedded IDs. + * XXX Ideally those would rather belong to #BKE_lib_id, but using callback function pointers makes + * this hard to do properly if we want to avoid headers includes in headers. */ + +void BKE_idtype_id_foreach_cache(struct ID *id, + IDTypeForeachCacheFunctionCallback function_callback, + void *user_data); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index bc72afdd08d..76bae6fa909 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -229,7 +229,6 @@ bool BKE_id_copy(struct Main *bmain, const struct ID *id, struct ID **newid); bool BKE_id_copy_ex(struct Main *bmain, const struct ID *id, struct ID **r_newid, const int flag); struct ID *BKE_id_copy_for_duplicate(struct Main *bmain, struct ID *id, - const bool is_owner_id_liboverride, const uint duplicate_flags); void BKE_lib_id_swap(struct Main *bmain, struct ID *id_a, struct ID *id_b); diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 411df66fe36..8f6603e28ae 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -51,9 +51,6 @@ struct Main; struct PointerRNA; struct PropertyRNA; -void BKE_lib_override_library_enable(const bool do_enable); -bool BKE_lib_override_library_is_enabled(void); - struct IDOverrideLibrary *BKE_lib_override_library_init(struct ID *local_id, struct ID *reference_id); void BKE_lib_override_library_copy(struct ID *dst_id, @@ -66,6 +63,10 @@ struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain, struct ID *reference_id, const bool do_tagged_remap); bool BKE_lib_override_library_create_from_tag(struct Main *bmain); +void BKE_lib_override_library_dependencies_tag(struct Main *bmain, + struct ID *id_root, + const uint tag, + const bool do_create_main_relashionships); struct IDOverrideLibraryProperty *BKE_lib_override_library_property_find( struct IDOverrideLibrary *override, const char *rna_path); diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 7d989bfcf69..b2510be656e 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -513,13 +513,13 @@ void BKE_mesh_loops_to_mface_corners(struct CustomData *fdata, void BKE_mesh_loops_to_tessdata(struct CustomData *fdata, struct CustomData *ldata, struct MFace *mface, - int *polyindices, + const int *polyindices, unsigned int (*loopindices)[4], const int num_faces); void BKE_mesh_tangent_loops_to_tessdata(struct CustomData *fdata, struct CustomData *ldata, struct MFace *mface, - int *polyindices, + const int *polyindices, unsigned int (*loopindices)[4], const int num_faces, const char *layer_name); diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h index 9864cc4d28d..5ad5238bbb5 100644 --- a/source/blender/blenkernel/BKE_mesh_mapping.h +++ b/source/blender/blenkernel/BKE_mesh_mapping.h @@ -199,7 +199,7 @@ void BKE_mesh_loop_islands_clear(MeshIslandStore *island_store); void BKE_mesh_loop_islands_free(MeshIslandStore *island_store); void BKE_mesh_loop_islands_add(MeshIslandStore *islands, const int item_num, - int *item_indices, + const int *item_indices, const int num_island_items, int *island_item_indices, const int num_innercut_items, diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index bdcbe2129c8..acd4c75befd 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -101,6 +101,30 @@ typedef struct bNodeSocketTemplate { char identifier[64]; /* generated from name */ } bNodeSocketTemplate; +/* Use `void *` for callbacks that require C++. This is rather ugly, but works well for now. This + * would not be necessary if we would use bNodeSocketType and bNodeType only in C++ code. + * However, achieving this requires quite a few changes currently. */ +#ifdef __cplusplus +namespace blender { +namespace bke { +class SocketMFNetworkBuilder; +class NodeMFNetworkBuilder; +} // namespace bke +namespace fn { +class MFDataType; +} +} // namespace blender + +using NodeExpandInMFNetworkFunction = void (*)(blender::bke::NodeMFNetworkBuilder &builder); +using SocketGetMFDataTypeFunction = blender::fn::MFDataType (*)(); +using SocketExpandInMFNetworkFunction = void (*)(blender::bke::SocketMFNetworkBuilder &builder); + +#else +typedef void *NodeExpandInMFNetworkFunction; +typedef void *SocketGetMFDataTypeFunction; +typedef void *SocketExpandInMFNetworkFunction; +#endif + /** * \brief Defines a socket type. * @@ -153,6 +177,11 @@ typedef struct bNodeSocketType { /* Callback to free the socket type. */ void (*free_self)(struct bNodeSocketType *stype); + + /* Returns the multi-function data type of this socket type. */ + SocketGetMFDataTypeFunction get_mf_data_type; + /* Expands the socket into a multi-function node that outputs the socket value. */ + SocketExpandInMFNetworkFunction expand_in_mf_network; } bNodeSocketType; typedef void *(*NodeInitExecFunction)(struct bNodeExecContext *context, @@ -267,6 +296,9 @@ typedef struct bNodeType { /* gpu */ NodeGPUExecFunction gpufunc; + /* Expands the bNode into nodes in a multi-function network, which will be evaluated later on. */ + NodeExpandInMFNetworkFunction expand_in_mf_network; + /* RNA integration */ ExtensionRNA rna_ext; } bNodeType; diff --git a/source/blender/blenkernel/BKE_node_tree_multi_function.hh b/source/blender/blenkernel/BKE_node_tree_multi_function.hh new file mode 100644 index 00000000000..dcbd551591f --- /dev/null +++ b/source/blender/blenkernel/BKE_node_tree_multi_function.hh @@ -0,0 +1,374 @@ +/* + * 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. + */ + +#ifndef __BKE_NODE_TREE_FUNCTION_HH__ +#define __BKE_NODE_TREE_FUNCTION_HH__ + +/** \file + * \ingroup bke + * + * This file allows you to generate a multi-function network from a user-generated node tree. + */ + +#include "FN_multi_function_builder.hh" +#include "FN_multi_function_network.hh" + +#include "BKE_derived_node_tree.hh" + +#include "BLI_resource_collector.hh" + +namespace blender { +namespace bke { + +/* Maybe this should be moved to BKE_node.h. */ +inline bool is_multi_function_data_socket(const bNodeSocket *bsocket) +{ + if (bsocket->typeinfo->get_mf_data_type != nullptr) { + BLI_assert(bsocket->typeinfo->expand_in_mf_network != nullptr); + return true; + } + return false; +} + +/** + * A MFNetworkTreeMap maps various components of a bke::DerivedNodeTree to components of a + * fn::MFNetwork. This is necessary for further processing of a multi-function network that has + * been generated from a node tree. + */ +class MFNetworkTreeMap { + private: + /** + * Store by id instead of using a hash table to avoid unnecessary hash table lookups. + * + * Input sockets in a node tree can have multiple corresponding sockets in the generated + * MFNetwork. This is because nodes are allowed to expand into multiple multi-function nodes. + */ + const DerivedNodeTree &tree_; + fn::MFNetwork &network_; + Array<Vector<fn::MFSocket *, 1>> sockets_by_dsocket_id_; + Array<fn::MFOutputSocket *> socket_by_group_input_id_; + + public: + MFNetworkTreeMap(const DerivedNodeTree &tree, fn::MFNetwork &network) + : tree_(tree), + network_(network), + sockets_by_dsocket_id_(tree.sockets().size()), + socket_by_group_input_id_(tree.group_inputs().size(), nullptr) + { + } + + const DerivedNodeTree &tree() const + { + return tree_; + } + + const fn::MFNetwork &network() const + { + return network_; + } + + fn::MFNetwork &network() + { + return network_; + } + + void add(const DSocket &dsocket, fn::MFSocket &socket) + { + BLI_assert(dsocket.is_input() == socket.is_input()); + sockets_by_dsocket_id_[dsocket.id()].append(&socket); + } + + void add(const DInputSocket &dsocket, fn::MFInputSocket &socket) + { + sockets_by_dsocket_id_[dsocket.id()].append(&socket); + } + + void add(const DOutputSocket &dsocket, fn::MFOutputSocket &socket) + { + sockets_by_dsocket_id_[dsocket.id()].append(&socket); + } + + void add(Span<const DInputSocket *> dsockets, Span<fn::MFInputSocket *> sockets) + { + assert_same_size(dsockets, sockets); + for (uint i : dsockets.index_range()) { + this->add(*dsockets[i], *sockets[i]); + } + } + + void add(Span<const DOutputSocket *> dsockets, Span<fn::MFOutputSocket *> sockets) + { + assert_same_size(dsockets, sockets); + for (uint i : dsockets.index_range()) { + this->add(*dsockets[i], *sockets[i]); + } + } + + void add(const DGroupInput &group_input, fn::MFOutputSocket &socket) + { + BLI_assert(socket_by_group_input_id_[group_input.id()] == nullptr); + socket_by_group_input_id_[group_input.id()] = &socket; + } + + void add_try_match(const DNode &dnode, fn::MFNode &node) + { + this->add_try_match(dnode.inputs(), node.inputs()); + this->add_try_match(dnode.outputs(), node.outputs()); + } + + void add_try_match(Span<const DSocket *> dsockets, Span<fn::MFSocket *> sockets) + { + uint used_sockets = 0; + for (const DSocket *dsocket : dsockets) { + if (!dsocket->is_available()) { + continue; + } + if (!is_multi_function_data_socket(dsocket->bsocket())) { + continue; + } + fn::MFSocket *socket = sockets[used_sockets]; + this->add(*dsocket, *socket); + used_sockets++; + } + } + + fn::MFOutputSocket &lookup(const DGroupInput &group_input) + { + fn::MFOutputSocket *socket = socket_by_group_input_id_[group_input.id()]; + BLI_assert(socket != nullptr); + return *socket; + } + + fn::MFOutputSocket &lookup(const DOutputSocket &dsocket) + { + auto &sockets = sockets_by_dsocket_id_[dsocket.id()]; + BLI_assert(sockets.size() == 1); + return sockets[0]->as_output(); + } + + Span<fn::MFInputSocket *> lookup(const DInputSocket &dsocket) + { + return sockets_by_dsocket_id_[dsocket.id()].as_span().cast<fn::MFInputSocket *>(); + } + + fn::MFInputSocket &lookup_dummy(const DInputSocket &dsocket) + { + Span<fn::MFInputSocket *> sockets = this->lookup(dsocket); + BLI_assert(sockets.size() == 1); + fn::MFInputSocket &socket = *sockets[0]; + BLI_assert(socket.node().is_dummy()); + return socket; + } + + fn::MFOutputSocket &lookup_dummy(const DOutputSocket &dsocket) + { + fn::MFOutputSocket &socket = this->lookup(dsocket); + BLI_assert(socket.node().is_dummy()); + return socket; + } + + bool is_mapped(const DSocket &dsocket) const + { + return sockets_by_dsocket_id_[dsocket.id()].size() >= 1; + } +}; + +/** + * This data is necessary throughout the generation of a MFNetwork from a node tree. + */ +struct CommonMFNetworkBuilderData { + ResourceCollector &resources; + fn::MFNetwork &network; + MFNetworkTreeMap &network_map; + const DerivedNodeTree &tree; +}; + +class MFNetworkBuilderBase { + protected: + CommonMFNetworkBuilderData &common_; + + public: + MFNetworkBuilderBase(CommonMFNetworkBuilderData &common) : common_(common) + { + } + + /** + * Returns the network that is currently being built. + */ + fn::MFNetwork &network() + { + return common_.network; + } + + /** + * Returns the map between the node tree and the multi-function network that is being built. + */ + MFNetworkTreeMap &network_map() + { + return common_.network_map; + } + + /** + * Returns a resource collector that will only be destructed after the multi-function network is + * destructed. + */ + ResourceCollector &resources() + { + return common_.resources; + } + + /** + * Constructs a new function that will live at least as long as the MFNetwork. + */ + template<typename T, typename... Args> T &construct_fn(Args &&... args) + { + BLI_STATIC_ASSERT((std::is_base_of_v<fn::MultiFunction, T>), ""); + void *buffer = common_.resources.linear_allocator().allocate(sizeof(T), alignof(T)); + T *fn = new (buffer) T(std::forward<Args>(args)...); + common_.resources.add(destruct_ptr<T>(fn), fn->name().data()); + return *fn; + } +}; + +/** + * This class is used by socket implementations to define how an unlinked input socket is handled + * in a multi-function network. + */ +class SocketMFNetworkBuilder : public MFNetworkBuilderBase { + private: + const DSocket *dsocket_ = nullptr; + const DGroupInput *group_input_ = nullptr; + bNodeSocket *bsocket_; + fn::MFOutputSocket *built_socket_ = nullptr; + + public: + SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DSocket &dsocket) + : MFNetworkBuilderBase(common), dsocket_(&dsocket), bsocket_(dsocket.bsocket()) + { + } + + SocketMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DGroupInput &group_input) + : MFNetworkBuilderBase(common), group_input_(&group_input), bsocket_(group_input.bsocket()) + { + } + + /** + * Returns the socket that is currently being built. + */ + bNodeSocket &bsocket() + { + return *bsocket_; + } + + /** + * Utility method that returns bsocket->default_value for the current socket. + */ + template<typename T> T *socket_default_value() + { + return (T *)bsocket_->default_value; + } + + /** + * Builds a function node for that socket that outputs the given constant value. + */ + template<typename T> void set_constant_value(T value) + { + const fn::MultiFunction &fn = this->construct_fn<fn::CustomMF_Constant<T>>(std::move(value)); + this->set_generator_fn(fn); + } + + /** + * Uses the first output of the given multi-function as value of the socket. + */ + void set_generator_fn(const fn::MultiFunction &fn) + { + fn::MFFunctionNode &node = common_.network.add_function(fn); + this->set_socket(node.output(0)); + } + + /** + * Define a multi-function socket that outputs the value of the bsocket. + */ + void set_socket(fn::MFOutputSocket &socket) + { + built_socket_ = &socket; + } + + fn::MFOutputSocket *built_socket() + { + return built_socket_; + } +}; + +/** + * This class is used by node implementations to define how a user-level node expands into + * multi-function nodes internally. + */ +class NodeMFNetworkBuilder : public MFNetworkBuilderBase { + private: + const DNode &node_; + + public: + NodeMFNetworkBuilder(CommonMFNetworkBuilderData &common, const DNode &node) + : MFNetworkBuilderBase(common), node_(node) + { + } + + /** + * Tells the builder to build a function that corresponds to the node that is being built. It + * will try to match up sockets. + */ + template<typename T, typename... Args> void construct_and_set_matching_fn(Args &&... args) + { + const fn::MultiFunction &function = this->construct_fn<T>(std::forward<Args>(args)...); + this->set_matching_fn(function); + } + + /** + * Tells the builder that the given function corresponds to the node that is being built. It will + * try to match up sockets. For that it skips unavailable and non-data sockets. + */ + void set_matching_fn(const fn::MultiFunction &function) + { + fn::MFFunctionNode &node = common_.network.add_function(function); + common_.network_map.add_try_match(node_, node); + } + + /** + * Returns the node that is currently being built. + */ + bNode &bnode() + { + return *node_.node_ref().bnode(); + } + + /** + * Returns the node that is currently being built. + */ + const DNode &dnode() const + { + return node_; + } +}; + +MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network, + const DerivedNodeTree &tree, + ResourceCollector &resources); + +} // namespace bke +} // namespace blender + +#endif /* __BKE_NODE_TREE_FUNCTION_HH__ */ diff --git a/source/blender/blenkernel/BKE_node_tree_ref.hh b/source/blender/blenkernel/BKE_node_tree_ref.hh index e25849cb569..b1e8904d718 100644 --- a/source/blender/blenkernel/BKE_node_tree_ref.hh +++ b/source/blender/blenkernel/BKE_node_tree_ref.hh @@ -197,12 +197,12 @@ class NodeTreeRef : NonCopyable, NonMovable { inline Span<const SocketRef *> SocketRef::linked_sockets() const { - return linked_sockets_.as_span(); + return linked_sockets_; } inline Span<const SocketRef *> SocketRef::directly_linked_sockets() const { - return directly_linked_sockets_.as_span(); + return directly_linked_sockets_; } inline bool SocketRef::is_linked() const @@ -326,12 +326,12 @@ inline const NodeTreeRef &NodeRef::tree() const inline Span<const InputSocketRef *> NodeRef::inputs() const { - return inputs_.as_span(); + return inputs_; } inline Span<const OutputSocketRef *> NodeRef::outputs() const { - return outputs_.as_span(); + return outputs_; } inline const InputSocketRef &NodeRef::input(uint index) const @@ -400,7 +400,7 @@ inline bool NodeRef::is_group_output_node() const inline Span<const NodeRef *> NodeTreeRef::nodes() const { - return nodes_by_id_.as_span(); + return nodes_by_id_; } inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(StringRefNull idname) const @@ -416,23 +416,23 @@ inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(const bNodeType *nodetyp return {}; } else { - return nodes->as_span(); + return *nodes; } } inline Span<const SocketRef *> NodeTreeRef::sockets() const { - return sockets_by_id_.as_span(); + return sockets_by_id_; } inline Span<const InputSocketRef *> NodeTreeRef::input_sockets() const { - return input_sockets_.as_span(); + return input_sockets_; } inline Span<const OutputSocketRef *> NodeTreeRef::output_sockets() const { - return output_sockets_.as_span(); + return output_sockets_; } inline bNodeTree *NodeTreeRef::btree() const diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index d830a35dda0..f2a022c84a3 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -139,7 +139,7 @@ bool BKE_object_obdata_is_libdata(const struct Object *ob); struct Object *BKE_object_duplicate(struct Main *bmain, struct Object *ob, - const uint dupflag, + uint dupflag, const uint duplicate_options); void BKE_object_obdata_size_init(struct Object *ob, const float scale); diff --git a/source/blender/blenkernel/BKE_object_deform.h b/source/blender/blenkernel/BKE_object_deform.h index 410cb862aa7..e4813aa2288 100644 --- a/source/blender/blenkernel/BKE_object_deform.h +++ b/source/blender/blenkernel/BKE_object_deform.h @@ -33,7 +33,7 @@ struct Object; struct bDeformGroup; /* General vgroup operations */ -void BKE_object_defgroup_remap_update_users(struct Object *ob, int *map); +void BKE_object_defgroup_remap_update_users(struct Object *ob, const int *map); bool BKE_object_defgroup_array_get(struct ID *id, struct MDeformVert **dvert_arr, int *dvert_tot); diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index a22cb9ef126..7de588450d6 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -365,6 +365,10 @@ struct ModifierData *object_add_particle_system(struct Main *bmain, struct Scene *scene, struct Object *ob, const char *name); +struct ModifierData *object_copy_particle_system(struct Main *bmain, + struct Scene *scene, + struct Object *ob, + const struct ParticleSystem *psys_orig); void object_remove_particle_system(struct Main *bmain, struct Scene *scene, struct Object *ob); struct ParticleSettings *BKE_particlesettings_add(struct Main *bmain, const char *name); struct ParticleSettings *BKE_particlesettings_copy(struct Main *bmain, diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 87875314aec..da9280148fd 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -224,7 +224,7 @@ void BKE_pbvh_bounding_box(const PBVH *pbvh, float min[3], float max[3]); unsigned int **BKE_pbvh_grid_hidden(const PBVH *pbvh); int BKE_pbvh_count_grid_quads(BLI_bitmap **grid_hidden, - int *grid_indices, + const int *grid_indices, int totgrid, int gridsize); @@ -264,6 +264,7 @@ void BKE_pbvh_node_mark_redraw(PBVHNode *node); void BKE_pbvh_node_mark_normals_update(PBVHNode *node); void BKE_pbvh_node_mark_topology_update(PBVHNode *node); void BKE_pbvh_node_fully_hidden_set(PBVHNode *node, int fully_hidden); +bool BKE_pbvh_node_fully_hidden_get(PBVHNode *node); void BKE_pbvh_node_fully_masked_set(PBVHNode *node, int fully_masked); bool BKE_pbvh_node_fully_masked_get(PBVHNode *node); void BKE_pbvh_node_fully_unmasked_set(PBVHNode *node, int fully_masked); diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h index b0973ed458c..a9abe2d0f6d 100644 --- a/source/blender/blenkernel/BKE_pointcache.h +++ b/source/blender/blenkernel/BKE_pointcache.h @@ -147,7 +147,7 @@ typedef struct PTCacheID { /* copies point data to cache data */ int (*write_point)(int index, void *calldata, void **data, int cfra); /* copies cache cata to point data */ - void (*read_point)(int index, void *calldata, void **data, float cfra, float *old_data); + void (*read_point)(int index, void *calldata, void **data, float cfra, const float *old_data); /* interpolated between previously read point data and cache data */ void (*interpolate_point)(int index, void *calldata, @@ -155,7 +155,7 @@ typedef struct PTCacheID { float cfra, float cfra1, float cfra2, - float *old_data); + const float *old_data); /* copies point data to cache data */ int (*write_stream)(PTCacheFile *pf, void *calldata); @@ -296,7 +296,9 @@ void BKE_ptcache_id_from_dynamicpaint(PTCacheID *pid, struct Object *ob, struct DynamicPaintSurface *surface); void BKE_ptcache_id_from_rigidbody(PTCacheID *pid, struct Object *ob, struct RigidBodyWorld *rbw); -void BKE_ptcache_id_from_sim_particles(PTCacheID *pid, struct ParticleSimulationState *state); +void BKE_ptcache_id_from_sim_particles(PTCacheID *pid, + struct ParticleSimulationState *state_orig, + struct ParticleSimulationState *state_cow); PTCacheID BKE_ptcache_id_find(struct Object *ob, struct Scene *scene, struct PointCache *cache); void BKE_ptcache_ids_from_object(struct ListBase *lb, diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index a50f9b24c61..107a27b00ab 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -285,6 +285,11 @@ void BKE_sequence_reload_new_file(struct Main *bmain, struct Scene *scene, struct Sequence *seq, const bool lock_range); +void BKE_sequence_movie_reload_if_needed(struct Main *bmain, + struct Scene *scene, + struct Sequence *seq, + bool *r_was_reloaded, + bool *r_can_produce_frames); int BKE_sequencer_evaluate_frame(struct Scene *scene, int cfra); int BKE_sequencer_get_shown_sequences(struct ListBase *seqbasep, int cfra, diff --git a/source/blender/blenkernel/BKE_subdiv_ccg.h b/source/blender/blenkernel/BKE_subdiv_ccg.h index 78e91d3ad2f..5808f223f32 100644 --- a/source/blender/blenkernel/BKE_subdiv_ccg.h +++ b/source/blender/blenkernel/BKE_subdiv_ccg.h @@ -313,6 +313,22 @@ void BKE_subdiv_ccg_neighbor_coords_get(const SubdivCCG *subdiv_ccg, int BKE_subdiv_ccg_grid_to_face_index(const SubdivCCG *subdiv_ccg, const int grid_index); +typedef enum SubdivCCGAdjacencyType { + SUBDIV_CCG_ADJACENT_NONE, + SUBDIV_CCG_ADJACENT_VERTEX, + SUBDIV_CCG_ADJACENT_EDGE, +} SubdivCCGAdjacencyType; + +/* Returns if a grid coordinates is adjacent to a coarse mesh edge, vertex or nothing. If it is + * adjacent to an edge, r_v1 and r_v2 will be set to the two vertices of that edge. If it is + * adjacent to a vertex, r_v1 and r_v2 will be the index of that vertex. */ +SubdivCCGAdjacencyType BKE_subdiv_ccg_coarse_mesh_adjacency_info_get(const SubdivCCG *subdiv_ccg, + const SubdivCCGCoord *coord, + const MLoop *mloop, + const MPoly *mpoly, + int *r_v1, + int *r_v2); + /* Get array which is indexed by face index and contains index of a first grid of the face. * * The "ensure" version allocates the mapping if it's not know yet and stores it in the subdiv_ccg diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index b9054d29752..b70476b8590 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -27,6 +27,7 @@ set(INC ../bmesh ../depsgraph ../draw + ../functions ../gpencil_modifiers ../gpu ../ikplugin @@ -187,6 +188,7 @@ set(SRC intern/multires_unsubdivide.c intern/nla.c intern/node.c + intern/node_tree_multi_function.cc intern/node_tree_ref.cc intern/object.c intern/object_deform.c @@ -356,6 +358,7 @@ set(SRC BKE_multires.h BKE_nla.h BKE_node.h + BKE_node_tree_multi_function.hh BKE_node_tree_ref.hh BKE_object.h BKE_object_deform.h @@ -424,6 +427,7 @@ set(LIB bf_bmesh bf_depsgraph bf_draw + bf_functions bf_gpencil_modifiers bf_gpu bf_ikplugin diff --git a/source/blender/blenkernel/intern/CCGSubSurf_util.c b/source/blender/blenkernel/intern/CCGSubSurf_util.c index 58d5f2e0495..bc63d8b97f7 100644 --- a/source/blender/blenkernel/intern/CCGSubSurf_util.c +++ b/source/blender/blenkernel/intern/CCGSubSurf_util.c @@ -304,7 +304,7 @@ void ccgSubSurf__dumpCoords(CCGSubSurf *ss) } for (x = 0; x < gridSize; x++) { float *co = FACE_getIECo(f, subdivLevels, S, x); - printf("face index=%d. cornder=%d, ie_index=%d, coord=(%f, %f, %f)\n", + printf("face index=%d. corner=%d, ie_index=%d, coord=(%f, %f, %f)\n", index, S, x, diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 3e2c69fbcea..61181278c60 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -375,17 +375,19 @@ bool BKE_animdata_copy_id(Main *bmain, ID *id_to, ID *id_from, const int flag) return true; } -void BKE_animdata_copy_id_action(Main *bmain, ID *id, const bool set_newid) +static void animdata_copy_id_action(Main *bmain, + ID *id, + const bool set_newid, + const bool do_linked_id) { - const bool is_id_liboverride = ID_IS_OVERRIDE_LIBRARY(id); AnimData *adt = BKE_animdata_from_id(id); if (adt) { - if (adt->action && (!is_id_liboverride || !ID_IS_LINKED(adt->action))) { + if (adt->action && (do_linked_id || !ID_IS_LINKED(adt->action))) { id_us_min((ID *)adt->action); adt->action = set_newid ? ID_NEW_SET(adt->action, BKE_action_copy(bmain, adt->action)) : BKE_action_copy(bmain, adt->action); } - if (adt->tmpact && (!is_id_liboverride || !ID_IS_LINKED(adt->tmpact))) { + if (adt->tmpact && (do_linked_id || !ID_IS_LINKED(adt->tmpact))) { id_us_min((ID *)adt->tmpact); adt->tmpact = set_newid ? ID_NEW_SET(adt->tmpact, BKE_action_copy(bmain, adt->tmpact)) : BKE_action_copy(bmain, adt->tmpact); @@ -393,12 +395,27 @@ void BKE_animdata_copy_id_action(Main *bmain, ID *id, const bool set_newid) } bNodeTree *ntree = ntreeFromID(id); if (ntree) { - BKE_animdata_copy_id_action(bmain, &ntree->id, set_newid); + animdata_copy_id_action(bmain, &ntree->id, set_newid, do_linked_id); } /* Note that collections are not animatable currently, so no need to handle scenes' master * collection here. */ } +void BKE_animdata_copy_id_action(Main *bmain, ID *id) +{ + const bool is_id_liboverride = ID_IS_OVERRIDE_LIBRARY(id); + animdata_copy_id_action(bmain, id, false, !is_id_liboverride); +} + +void BKE_animdata_duplicate_id_action(struct Main *bmain, + struct ID *id, + const eDupli_ID_Flags duplicate_flags) +{ + if (duplicate_flags & USER_DUP_ACT) { + animdata_copy_id_action(bmain, id, true, (duplicate_flags & USER_DUP_LINKED_ID) != 0); + } +} + /* Merge copies of the data from the src AnimData into the destination AnimData */ void BKE_animdata_merge_copy( Main *bmain, ID *dst_id, ID *src_id, eAnimData_MergeCopy_Modes action_mode, bool fix_drivers) diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index 58337bcf488..2cc715464ad 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -56,7 +56,7 @@ # ifdef WITH_BINRELOC # include "binreloc.h" # endif -/* mkdtemp on OSX (and probably all *BSD?), not worth making specific check for this OS. */ +/* #mkdtemp on OSX (and probably all *BSD?), not worth making specific check for this OS. */ # include <unistd.h> #endif /* WIN32 */ @@ -120,7 +120,7 @@ static char *blender_version_decimal(const int ver) } /** - * Concatenates path_base, (optional) path_sep and (optional) folder_name into targetpath, + * Concatenates path_base, (optional) path_sep and (optional) folder_name into \a targetpath, * returning true if result points to a directory. */ static bool test_path(char *targetpath, @@ -138,14 +138,14 @@ static bool test_path(char *targetpath, BLI_strncpy(tmppath, path_base, sizeof(tmppath)); } - /* rare cases folder_name is omitted (when looking for ~/.config/blender/2.xx dir only) */ + /* Rare cases folder_name is omitted (when looking for `~/.config/blender/2.xx` dir only). */ if (folder_name) { BLI_join_dirfile(targetpath, targetpath_len, tmppath, folder_name); } else { BLI_strncpy(targetpath, tmppath, targetpath_len); } - /* FIXME: why is "//" on front of tmppath expanded to "/" (by BLI_join_dirfile) + /* FIXME: why is "//" on front of \a tmppath expanded to "/" (by BLI_join_dirfile) * if folder_name is specified but not otherwise? */ if (BLI_is_dir(targetpath)) { @@ -190,7 +190,7 @@ static bool test_env_path(char *path, const char *envvar) /** * Constructs in \a targetpath the name of a directory relative to a version-specific - * subdirectory in the parent directory of the Blender executable. + * sub-directory in the parent directory of the Blender executable. * * \param targetpath: String to return path * \param folder_name: Optional folder name within version-specific directory @@ -307,7 +307,7 @@ static bool get_path_environment_notest(char *targetpath, * \param targetpath: String to return path * \param folder_name: default name of folder within user area * \param subfolder_name: optional name of subfolder within folder - * \param ver: Blender version, used to construct a subdirectory name + * \param ver: Blender version, used to construct a sub-directory name * \return true if it was able to construct such a path. */ static bool get_path_user(char *targetpath, @@ -350,8 +350,8 @@ static bool get_path_user(char *targetpath, * * \param targetpath: String to return path * \param folder_name: default name of folder within installation area - * \param subfolder_name: optional name of subfolder within folder - * \param ver: Blender version, used to construct a subdirectory name + * \param subfolder_name: optional name of sub-folder within folder + * \param ver: Blender version, used to construct a sub-directory name * \return true if it was able to construct such a path. */ static bool get_path_system(char *targetpath, @@ -635,7 +635,7 @@ const char *BKE_appdir_folder_id_version(const int folder_id, const int ver, con * adds the correct extension (.com .exe etc) from * $PATHEXT if necessary. Also on Windows it translates * the name to its 8.3 version to prevent problems with - * spaces and stuff. Final result is returned in fullname. + * spaces and stuff. Final result is returned in \a fullname. * * \param fullname: The full path and full name of the executable * (must be FILE_MAX minimum) @@ -975,7 +975,7 @@ static void where_is_temp(char *fullname, char *basename, const size_t maxlen, c /** * Sets btempdir_base to userdir if specified and is a valid directory, otherwise * chooses a suitable OS-specific temporary directory. - * Sets btempdir_session to a mkdtemp-generated sub-dir of btempdir_base. + * Sets btempdir_session to a #mkdtemp generated sub-dir of btempdir_base. * * \note On Window userdir will be set to the temporary directory! */ @@ -1019,7 +1019,11 @@ void BKE_tempdir_session_purge(void) } /* Gets a good default directory for fonts */ -bool BKE_appdir_font_folder_default(char *dir) + +bool BKE_appdir_font_folder_default( + /* This parameter can only be `const` on non-windows platforms. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + char *dir) { bool success = false; #ifdef WIN32 diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index 6b25095a87a..f45cd5b679a 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -30,7 +30,6 @@ #include "DNA_scene_types.h" #include "BLI_edgehash.h" -#include "BLI_ghash.h" #include "BLI_linklist.h" #include "BLI_math.h" #include "BLI_rand.h" @@ -587,7 +586,7 @@ void cloth_free_modifier(ClothModifierData *clmd) } if (cloth->sew_edge_graph) { - BLI_ghash_free(cloth->sew_edge_graph, MEM_freeN, NULL); + BLI_edgeset_free(cloth->sew_edge_graph); cloth->sew_edge_graph = NULL; } @@ -669,7 +668,7 @@ void cloth_free_modifier_extern(ClothModifierData *clmd) } if (cloth->sew_edge_graph) { - BLI_ghash_free(cloth->sew_edge_graph, MEM_freeN, NULL); + BLI_edgeset_free(cloth->sew_edge_graph); cloth->sew_edge_graph = NULL; } @@ -1038,7 +1037,7 @@ static void cloth_free_errorsprings(Cloth *cloth, } BLI_INLINE void cloth_bend_poly_dir( - ClothVertex *verts, int i, int j, int *inds, int len, float r_dir[3]) + ClothVertex *verts, int i, int j, const int *inds, int len, float r_dir[3]) { float cent[3] = {0}; float fact = 1.0f / len; @@ -1557,9 +1556,8 @@ static bool find_internal_spring_target_vertex(BVHTreeFromMesh *treedata, *r_tar_v_idx = vert_idx; return true; } - - return false; - + + return false; } static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) @@ -1693,8 +1691,7 @@ static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW) { /* cloth->sew_edge_graph should not exist before this */ BLI_assert(cloth->sew_edge_graph == NULL); - cloth->sew_edge_graph = BLI_ghash_new( - BLI_ghashutil_inthash_v2_p, BLI_ghashutil_inthash_v2_cmp, "cloth_sewing_edges_graph"); + cloth->sew_edge_graph = BLI_edgeset_new("cloth_sewing_edges_graph"); } /* Structural springs. */ @@ -1709,18 +1706,7 @@ static int cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) spring->lin_stiffness = 1.0f; spring->type = CLOTH_SPRING_TYPE_SEWING; - /* set indices of verts of the sewing edge symmetrically in sew_edge_graph */ - unsigned int *vertex_index_pair = MEM_mallocN(sizeof(unsigned int) * 2, - "sewing_edge_index_pair_01"); - if (medge[i].v1 < medge[i].v2) { - vertex_index_pair[0] = medge[i].v1; - vertex_index_pair[1] = medge[i].v2; - } - else { - vertex_index_pair[0] = medge[i].v2; - vertex_index_pair[1] = medge[i].v1; - } - BLI_ghash_insert(cloth->sew_edge_graph, vertex_index_pair, NULL); + BLI_edgeset_insert(cloth->sew_edge_graph, medge[i].v1, medge[i].v2); } else { shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl); diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 080d61f1500..7e22048379b 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -333,7 +333,6 @@ static Collection *collection_duplicate_recursive(Main *bmain, Collection *collection_new; bool do_full_process = false; const bool is_collection_master = (collection_old->flag & COLLECTION_IS_MASTER) != 0; - const bool is_collection_liboverride = ID_IS_OVERRIDE_LIBRARY(collection_old); const bool do_objects = (duplicate_flags & USER_DUP_OBJECT) != 0; @@ -346,7 +345,12 @@ static Collection *collection_duplicate_recursive(Main *bmain, } else if (collection_old->id.newid == NULL) { collection_new = (Collection *)BKE_id_copy_for_duplicate( - bmain, (ID *)collection_old, is_collection_liboverride, duplicate_flags); + bmain, (ID *)collection_old, duplicate_flags); + + if (collection_new == collection_old) { + return collection_new; + } + do_full_process = true; } else { @@ -382,17 +386,15 @@ static Collection *collection_duplicate_recursive(Main *bmain, Object *ob_old = cob->ob; Object *ob_new = (Object *)ob_old->id.newid; - /* If collection is an override, we do not want to duplicate any linked data-block, as that - * would generate a purely local data. */ - if (is_collection_liboverride && ID_IS_LINKED(ob_old)) { - continue; - } - if (ob_new == NULL) { ob_new = BKE_object_duplicate( bmain, ob_old, duplicate_flags, duplicate_options | LIB_ID_DUPLICATE_IS_SUBPROCESS); } + if (ob_new == ob_old) { + continue; + } + collection_object_add(bmain, collection_new, ob_new, 0, true); collection_object_remove(bmain, collection_new, ob_old, false); } @@ -403,13 +405,11 @@ static Collection *collection_duplicate_recursive(Main *bmain, LISTBASE_FOREACH_MUTABLE (CollectionChild *, child, &collection_old->children) { Collection *child_collection_old = child->collection; - if (is_collection_liboverride && ID_IS_LINKED(child_collection_old)) { - continue; - } - - collection_duplicate_recursive( + Collection *child_collection_new = collection_duplicate_recursive( bmain, collection_new, child_collection_old, duplicate_flags, duplicate_options); - collection_child_remove(collection_new, child_collection_old); + if (child_collection_new != child_collection_old) { + collection_child_remove(collection_new, child_collection_old); + } } return collection_new; @@ -434,6 +434,11 @@ Collection *BKE_collection_duplicate(Main *bmain, if (!is_subprocess) { BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); BKE_main_id_clear_newpoins(bmain); + /* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate + * all expected linked data. */ + if (ID_IS_LINKED(collection)) { + duplicate_flags |= USER_DUP_LINKED_ID; + } } Collection *collection_new = collection_duplicate_recursive( diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c index daf1602319f..31d49dd4508 100644 --- a/source/blender/blenkernel/intern/collision.c +++ b/source/blender/blenkernel/intern/collision.c @@ -32,7 +32,7 @@ #include "DNA_scene_types.h" #include "BLI_blenlib.h" -#include "BLI_ghash.h" +#include "BLI_edgehash.h" #include "BLI_linklist.h" #include "BLI_math.h" #include "BLI_task.h" @@ -1153,17 +1153,7 @@ static bool cloth_bvh_selfcollision_is_active(const Cloth *cloth, } if (sewing_active) { - unsigned int vertex_index_pair[2]; - /* The indices pair are ordered, thus must ensure the same here as well */ - if (tri_a->tri[i] < tri_b->tri[j]) { - vertex_index_pair[0] = tri_a->tri[i]; - vertex_index_pair[1] = tri_b->tri[j]; - } - else { - vertex_index_pair[0] = tri_b->tri[j]; - vertex_index_pair[1] = tri_a->tri[i]; - } - if (BLI_ghash_haskey(cloth->sew_edge_graph, vertex_index_pair)) { + if (BLI_edgeset_haskey(cloth->sew_edge_graph, tri_a->tri[i], tri_b->tri[j])) { return false; } } diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index c894534c504..e9e44ac05e5 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -1157,7 +1157,7 @@ void BKE_nurb_knot_calc_v(Nurb *nu) } static void basisNurb( - float t, short order, int pnts, float *knots, float *basis, int *start, int *end) + float t, short order, int pnts, const float *knots, float *basis, int *start, int *end) { float d, e; int i, i1 = 0, i2 = 0, j, orderpluspnts, opp2, o2; @@ -3560,8 +3560,13 @@ static void free_arrays(void *buffer) } /* computes in which direction to change h[i] to satisfy conditions better */ -static float bezier_relax_direction( - float *a, float *b, float *c, float *d, float *h, int i, int count) +static float bezier_relax_direction(const float *a, + const float *b, + const float *c, + const float *d, + const float *h, + int i, + int count) { /* current deviation between sides of the equation */ float state = a[i] * h[(i + count - 1) % count] + b[i] * h[i] + c[i] * h[(i + 1) % count] - d[i]; @@ -3577,8 +3582,15 @@ static void bezier_lock_unknown(float *a, float *b, float *c, float *d, int i, f d[i] = value; } -static void bezier_restore_equation( - float *a, float *b, float *c, float *d, float *a0, float *b0, float *c0, float *d0, int i) +static void bezier_restore_equation(float *a, + float *b, + float *c, + float *d, + const float *a0, + const float *b0, + const float *c0, + const float *d0, + int i) { a[i] = a0[i]; b[i] = b0[i]; @@ -3586,8 +3598,14 @@ static void bezier_restore_equation( d[i] = d0[i]; } -static bool tridiagonal_solve_with_limits( - float *a, float *b, float *c, float *d, float *h, float *hmin, float *hmax, int solve_count) +static bool tridiagonal_solve_with_limits(float *a, + float *b, + float *c, + float *d, + float *h, + const float *hmin, + const float *hmax, + int solve_count) { float *a0, *b0, *c0, *d0; float **arrays[] = {&a0, &b0, &c0, &d0, NULL}; @@ -3726,7 +3744,7 @@ static bool tridiagonal_solve_with_limits( /* clang-format on */ static void bezier_eq_continuous( - float *a, float *b, float *c, float *d, float *dy, float *l, int i) + float *a, float *b, float *c, float *d, const float *dy, const float *l, int i) { a[i] = l[i] * l[i]; b[i] = 2.0f * (l[i] + 1); @@ -3735,7 +3753,7 @@ static void bezier_eq_continuous( } static void bezier_eq_noaccel_right( - float *a, float *b, float *c, float *d, float *dy, float *l, int i) + float *a, float *b, float *c, float *d, const float *dy, const float *l, int i) { a[i] = 0.0f; b[i] = 2.0f; @@ -3744,7 +3762,7 @@ static void bezier_eq_noaccel_right( } static void bezier_eq_noaccel_left( - float *a, float *b, float *c, float *d, float *dy, float *l, int i) + float *a, float *b, float *c, float *d, const float *dy, const float *l, int i) { a[i] = l[i] * l[i]; b[i] = 2.0f * l[i]; @@ -4823,7 +4841,7 @@ float (*BKE_curve_nurbs_key_vert_coords_alloc(ListBase *lb, float *key, int *r_v return cos; } -void BKE_curve_nurbs_key_vert_tilts_apply(ListBase *lb, float *key) +void BKE_curve_nurbs_key_vert_tilts_apply(ListBase *lb, const float *key) { Nurb *nu; int i; diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 2be61239ac6..c11fa69db76 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -300,7 +300,7 @@ static void layerInterp_mdeformvert(const void **sources, /* now we know how many unique deform weights there are, so realloc */ if (dvert->dw && (dvert->totweight == totweight)) { - /* pass (fastpath if we don't need to realloc) */ + /* pass (fast-path if we don't need to realloc). */ } else { if (dvert->dw) { @@ -1772,7 +1772,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { /* 42: CD_SCULPT_FACE_SETS */ {sizeof(int), "", 0, NULL, NULL, NULL, NULL, NULL, NULL}, /* 43: CD_LOCATION */ - {sizeof(float[3]), "vec3f", 1, NULL, NULL, NULL, NULL, NULL, NULL}, + {sizeof(float[3]), "vec3f", 1, "Location", NULL, NULL, NULL, NULL, NULL}, /* 44: CD_RADIUS */ {sizeof(float), "MFloatProperty", 1, NULL, NULL, NULL, NULL, NULL, NULL}, /* 45: CD_HAIRCURVE */ diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index 48e0ee50d43..9bdd80fd668 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -562,7 +562,7 @@ static bool data_transfer_layersmapping_cdlayers_multisrc_to_dst(ListBase *r_map CustomData *cd_dst, const bool use_dupref_dst, const int tolayers, - bool *use_layers_src, + const bool *use_layers_src, const int num_layers_src, cd_datatransfer_interp interp, void *interp_data) diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index b97935d57f2..98fc5f9a23a 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -249,7 +249,7 @@ void BKE_defvert_sync_mapped(MDeformVert *dvert_dst, /** * be sure all flip_map values are valid */ -void BKE_defvert_remap(MDeformVert *dvert, int *map, const int map_len) +void BKE_defvert_remap(MDeformVert *dvert, const int *map, const int map_len) { MDeformWeight *dw = dvert->dw; unsigned int i; @@ -1184,7 +1184,7 @@ static bool data_transfer_layersmapping_vgroups_multisrc_to_dst(ListBase *r_map, CustomData *cd_dst, const bool UNUSED(use_dupref_dst), const int tolayers, - bool *use_layers_src, + const bool *use_layers_src, const int num_layers_src) { int idx_src; diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 2e1fa519284..b9279ace39f 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -588,7 +588,7 @@ static bool boundsIntersectDist(Bounds3D *b1, Bounds3D *b2, const float dist) } /* check whether bounds intersects a point with given radius */ -static bool boundIntersectPoint(Bounds3D *b, float point[3], const float radius) +static bool boundIntersectPoint(Bounds3D *b, const float point[3], const float radius) { if (!b->valid) { return false; @@ -4780,13 +4780,16 @@ static void dynamic_paint_paint_single_point_cb_ex(void *__restrict userdata, } } -static int dynamicPaint_paintSinglePoint(Depsgraph *depsgraph, - DynamicPaintSurface *surface, - float *pointCoord, - DynamicPaintBrushSettings *brush, - Object *brushOb, - Scene *scene, - float timescale) +static int dynamicPaint_paintSinglePoint( + Depsgraph *depsgraph, + DynamicPaintSurface *surface, + /* Cannot be const, because it is assigned to non-const variable. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + float *pointCoord, + DynamicPaintBrushSettings *brush, + Object *brushOb, + Scene *scene, + float timescale) { PaintSurfaceData *sData = surface->data; float brush_radius = brush->paint_distance * surface->radius_scale; @@ -5456,11 +5459,14 @@ static void dynamic_paint_effect_drip_cb(void *__restrict userdata, } } -static void dynamicPaint_doEffectStep(DynamicPaintSurface *surface, - float *force, - PaintPoint *prevPoint, - float timescale, - float steps) +static void dynamicPaint_doEffectStep( + DynamicPaintSurface *surface, + /* Cannot be const, because it is assigned to non-const variable. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + float *force, + PaintPoint *prevPoint, + float timescale, + float steps) { PaintSurfaceData *sData = surface->data; diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 235c834fde9..a43553ee89f 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -1094,11 +1094,11 @@ void BKE_effectors_apply(ListBase *effectors, * Modifies the force on a particle according to its * relation with the effector object * Different kind of effectors include: - * Forcefields: Gravity-like attractor + * Force-fields: Gravity-like attractor * (force power is related to the inverse of distance to the power of a falloff value) * Vortex fields: swirling effectors * (particles rotate around Z-axis of the object. otherwise, same relation as) - * (Forcefields, but this is not done through a force/acceleration) + * (Force-fields, but this is not done through a force/acceleration) * Guide: particles on a path * (particles are guided along a curve bezier or old nurbs) * (is independent of other effectors) diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index bc14f525c2c..746aff1697c 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -1277,7 +1277,7 @@ short test_time_fcurve(FCurve *fcu) * than the horizontal distance between (v1-v4). * This is to prevent curve loops. */ -void correct_bezpart(float v1[2], float v2[2], float v3[2], float v4[2]) +void correct_bezpart(const float v1[2], float v2[2], float v3[2], const float v4[2]) { float h1[2], h2[2], len1, len2, len, fac; diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 1f748487841..52a3521189b 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -137,9 +137,9 @@ bool BKE_fluid_reallocate_fluid(FluidDomainSettings *fds, int res[3], int free_o void BKE_fluid_reallocate_copy_fluid(FluidDomainSettings *fds, int o_res[3], int n_res[3], - int o_min[3], - int n_min[3], - int o_max[3], + const int o_min[3], + const int n_min[3], + const int o_max[3], int o_shift[3], int n_shift[3]) { @@ -491,6 +491,17 @@ static void manta_set_domain_from_mesh(FluidDomainSettings *fds, fds->cell_size[2] /= (float)fds->base_res[2]; } +static void update_final_gravity(FluidDomainSettings *fds, Scene *scene) +{ + if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) { + copy_v3_v3(fds->gravity_final, scene->physics_settings.gravity); + } + else { + copy_v3_v3(fds->gravity_final, fds->gravity); + } + mul_v3_fl(fds->gravity_final, fds->effector_weights->global_gravity); +} + static bool BKE_fluid_modifier_init( FluidModifierData *fmd, Depsgraph *depsgraph, Object *ob, Scene *scene, Mesh *me) { @@ -502,10 +513,7 @@ static bool BKE_fluid_modifier_init( /* Set domain dimensions from mesh. */ manta_set_domain_from_mesh(fds, ob, me, true); /* Set domain gravity, use global gravity if enabled. */ - if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) { - copy_v3_v3(fds->gravity, scene->physics_settings.gravity); - } - mul_v3_fl(fds->gravity, fds->effector_weights->global_gravity); + update_final_gravity(fds, scene); /* Reset domain values. */ zero_v3_int(fds->shift); zero_v3(fds->shift_f); @@ -559,7 +567,7 @@ static bool BKE_fluid_modifier_init( // forward declaration static void manta_smoke_calc_transparency(FluidDomainSettings *fds, ViewLayer *view_layer); static float calc_voxel_transp( - float *result, float *input, int res[3], int *pixel, float *t_ray, float correct); + float *result, const float *input, int res[3], int *pixel, float *t_ray, float correct); static void update_distances(int index, float *fesh_distances, BVHTreeFromMesh *tree_data, @@ -594,8 +602,8 @@ static int get_light(ViewLayer *view_layer, float *light) static void clamp_bounds_in_domain(FluidDomainSettings *fds, int min[3], int max[3], - float *min_vel, - float *max_vel, + const float *min_vel, + const float *max_vel, int margin, float dt) { @@ -1830,7 +1838,7 @@ static void sample_mesh(FluidFlowSettings *ffs, float *velocity_map, int index, const int base_res[3], - float flow_center[3], + const float flow_center[3], BVHTreeFromMesh *tree_data, const float ray_start[3], const float *vert_vel, @@ -3328,17 +3336,13 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Obj mverts->co[1] = manta_liquid_get_vertex_y_at(fds->fluid, i); mverts->co[2] = manta_liquid_get_vertex_z_at(fds->fluid, i); - /* If reading raw data directly from manta, normalize now (e.g. during replay mode). - * If reading data from files from disk, omit this normalization. */ - if (!manta_liquid_mesh_from_file(fds->fluid)) { - // normalize to unit cube around 0 - mverts->co[0] -= ((float)fds->res[0] * fds->mesh_scale) * 0.5f; - mverts->co[1] -= ((float)fds->res[1] * fds->mesh_scale) * 0.5f; - mverts->co[2] -= ((float)fds->res[2] * fds->mesh_scale) * 0.5f; - mverts->co[0] *= fds->dx / fds->mesh_scale; - mverts->co[1] *= fds->dx / fds->mesh_scale; - mverts->co[2] *= fds->dx / fds->mesh_scale; - } + /* Adjust coordinates from Mantaflow to match viewport scaling. */ + float tmp[3] = {(float)fds->res[0], (float)fds->res[1], (float)fds->res[2]}; + /* Scale to unit cube around 0. */ + mul_v3_fl(tmp, fds->mesh_scale * 0.5f); + sub_v3_v3(mverts->co, tmp); + /* Apply scaling of domain object. */ + mul_v3_fl(mverts->co, fds->dx / fds->mesh_scale); mul_v3_v3(mverts->co, co_scale); add_v3_v3(mverts->co, co_offset); @@ -3808,10 +3812,7 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd, fds->time_per_frame = 0; /* Ensure that gravity is copied over every frame (could be keyframed). */ - if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) { - copy_v3_v3(fds->gravity, scene->physics_settings.gravity); - mul_v3_fl(fds->gravity, fds->effector_weights->global_gravity); - } + update_final_gravity(fds, scene); int next_frame = scene_framenr + 1; int prev_frame = scene_framenr - 1; @@ -4118,43 +4119,47 @@ static void BKE_fluid_modifier_process( struct Mesh *BKE_fluid_modifier_do( FluidModifierData *fmd, Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *me) { - /* Lock so preview render does not read smoke data while it gets modified. */ - if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) { - BLI_rw_mutex_lock(fmd->domain->fluid_mutex, THREAD_LOCK_WRITE); - } - - BKE_fluid_modifier_process(fmd, depsgraph, scene, ob, me); - - if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) { - BLI_rw_mutex_unlock(fmd->domain->fluid_mutex); - } - /* Optimization: Do not update viewport during bakes (except in replay mode) * Reason: UI is locked and updated liquid / smoke geometry is not visible anyways. */ bool needs_viewport_update = false; - if (fmd->domain) { - FluidDomainSettings *fds = fmd->domain; - /* Always update viewport in cache replay mode. */ - if (fds->cache_type == FLUID_DOMAIN_CACHE_REPLAY || - fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) { - needs_viewport_update = true; + /* Optimization: Only process modifier if object is not being altered. */ + if (!G.moving) { + /* Lock so preview render does not read smoke data while it gets modified. */ + if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) { + BLI_rw_mutex_lock(fmd->domain->fluid_mutex, THREAD_LOCK_WRITE); } - /* In other cache modes, only update the viewport when no bake is going on. */ - else { - bool with_mesh; - with_mesh = fds->flags & FLUID_DOMAIN_USE_MESH; - bool baking_data, baking_noise, baking_mesh, baking_particles, baking_guide; - baking_data = fds->cache_flag & FLUID_DOMAIN_BAKING_DATA; - baking_noise = fds->cache_flag & FLUID_DOMAIN_BAKING_NOISE; - baking_mesh = fds->cache_flag & FLUID_DOMAIN_BAKING_MESH; - baking_particles = fds->cache_flag & FLUID_DOMAIN_BAKING_PARTICLES; - baking_guide = fds->cache_flag & FLUID_DOMAIN_BAKING_GUIDE; - - if (with_mesh && !baking_data && !baking_noise && !baking_mesh && !baking_particles && - !baking_guide) { + + BKE_fluid_modifier_process(fmd, depsgraph, scene, ob, me); + + if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) { + BLI_rw_mutex_unlock(fmd->domain->fluid_mutex); + } + + if (fmd->domain) { + FluidDomainSettings *fds = fmd->domain; + + /* Always update viewport in cache replay mode. */ + if (fds->cache_type == FLUID_DOMAIN_CACHE_REPLAY || + fds->flags & FLUID_DOMAIN_USE_ADAPTIVE_DOMAIN) { needs_viewport_update = true; } + /* In other cache modes, only update the viewport when no bake is going on. */ + else { + bool with_mesh; + with_mesh = fds->flags & FLUID_DOMAIN_USE_MESH; + bool baking_data, baking_noise, baking_mesh, baking_particles, baking_guide; + baking_data = fds->cache_flag & FLUID_DOMAIN_BAKING_DATA; + baking_noise = fds->cache_flag & FLUID_DOMAIN_BAKING_NOISE; + baking_mesh = fds->cache_flag & FLUID_DOMAIN_BAKING_MESH; + baking_particles = fds->cache_flag & FLUID_DOMAIN_BAKING_PARTICLES; + baking_guide = fds->cache_flag & FLUID_DOMAIN_BAKING_GUIDE; + + if (with_mesh && !baking_data && !baking_noise && !baking_mesh && !baking_particles && + !baking_guide) { + needs_viewport_update = true; + } + } } } @@ -4196,7 +4201,7 @@ struct Mesh *BKE_fluid_modifier_do( } static float calc_voxel_transp( - float *result, float *input, int res[3], int *pixel, float *t_ray, float correct) + float *result, const float *input, int res[3], int *pixel, float *t_ray, float correct) { const size_t index = manta_get_index(pixel[0], res[0], pixel[1], res[1], pixel[2]); diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 6a9511d8275..eeb55c44d6e 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -681,10 +681,10 @@ void BKE_gpencil_stroke_weights_duplicate(bGPDstroke *gps_src, bGPDstroke *gps_d } /** - * Make a copy of a given gpencil stroke. - * \param gps_src: Source grease pencil strokeyes - * \param dup_points: Duplicate points data - * \return Pointer to new stroke + * Make a copy of a given grease-pencil stroke. + * \param gps_src: Source grease pencil strokes. + * \param dup_points: Duplicate points data. + * \return Pointer to new stroke. */ bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src, const bool dup_points) { @@ -1512,12 +1512,12 @@ int BKE_gpencil_object_material_ensure(Main *bmain, Object *ob, Material *materi } /** - * Creates a new gpencil material and assigns it to object. + * Creates a new grease-pencil material and assigns it to object. * \param bmain: Main pointer * \param ob: Grease pencil object * \param name: Material name * \param r_index: value is set to zero based index of the new material if \a r_index is not NULL. - * \return Materil pointer + * \return Material pointer. */ Material *BKE_gpencil_object_material_new(Main *bmain, Object *ob, const char *name, int *r_index) { @@ -1555,7 +1555,7 @@ Material *BKE_gpencil_object_material_from_brush_get(Object *ob, Brush *brush) * Returns the material index for a brush with respect to its pinned state. * \param ob: Grease pencil object * \param brush: Brush - * \return Materil index + * \return Material index. */ int BKE_gpencil_object_material_get_index_from_brush(Object *ob, Brush *brush) { @@ -1571,8 +1571,7 @@ int BKE_gpencil_object_material_get_index_from_brush(Object *ob, Brush *brush) * Guaranteed to return a material assigned to object. Returns never NULL. * \param bmain: Main pointer * \param ob: Grease pencil object - * \param ts: Toolsettings - * \return Material pointer + * \return Material pointer. */ Material *BKE_gpencil_object_material_ensure_from_active_input_toolsettings(Main *bmain, Object *ob, @@ -1590,7 +1589,7 @@ Material *BKE_gpencil_object_material_ensure_from_active_input_toolsettings(Main /** * Guaranteed to return a material assigned to object. Returns never NULL. * \param bmain: Main pointer - * \param ob: Grease pencil obejct + * \param ob: Grease pencil object. * \param brush: Brush * \return Material pointer */ @@ -1783,7 +1782,8 @@ float BKE_gpencil_multiframe_falloff_calc( value = BKE_curvemapping_evaluateF(cur_falloff, 0, fnum + 0.5f); } else { - value = 1.0f; + /* Center of the curve. */ + value = BKE_curvemapping_evaluateF(cur_falloff, 0, 0.5f); } return value; @@ -2330,8 +2330,8 @@ void BKE_gpencil_visible_stroke_iter(ViewLayer *view_layer, /** * Update original pointers in evaluated frame. - * \param gpf_orig: Original greas epencil frame - * \param gpf_eval: Evaluated grease pencil frame + * \param gpf_orig: Original grease-pencil frame. + * \param gpf_eval: Evaluated grease pencil frame. */ void BKE_gpencil_frame_original_pointers_update(const struct bGPDframe *gpf_orig, const struct bGPDframe *gpf_eval) diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c index 49940c2d466..b92f86d7aea 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.c +++ b/source/blender/blenkernel/intern/gpencil_geom.c @@ -989,7 +989,7 @@ bool BKE_gpencil_stroke_smooth_uv(bGPDstroke *gps, int point_index, float influe * \param points: Array of grease pencil points (3D) * \param totpoints: Total of points * \param points2d: Result array of 2D points - * \param r_direction: Return Concave (-1), Convex (1), or Autodetect (0) + * \param r_direction: Return Concave (-1), Convex (1), or Auto-detect (0) */ void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points, int totpoints, @@ -1043,7 +1043,7 @@ void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points, points2d[i][1] = dot_v3v3(loc, locy); } - /* Concave (-1), Convex (1), or Autodetect (0)? */ + /* Concave (-1), Convex (1), or Auto-detect (0)? */ *r_direction = (int)locy[2]; } @@ -1056,7 +1056,7 @@ void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points, * \param totpoints: Total points * \param points2d: Result array of 2D points * \param scale: Scale factor - * \param r_direction: Return Concave (-1), Convex (1), or Autodetect (0) + * \param r_direction: Return Concave (-1), Convex (1), or Auto-detect (0) */ void BKE_gpencil_stroke_2d_flat_ref(const bGPDspoint *ref_points, int ref_totpoints, @@ -1138,7 +1138,7 @@ void BKE_gpencil_stroke_2d_flat_ref(const bGPDspoint *ref_points, points2d[i][1] = dot_v3v3(loc, locy); } - /* Concave (-1), Convex (1), or Autodetect (0)? */ + /* Concave (-1), Convex (1), or Auto-detect (0)? */ *r_direction = (int)locy[2]; } @@ -1146,7 +1146,7 @@ void BKE_gpencil_stroke_2d_flat_ref(const bGPDspoint *ref_points, static void gpencil_calc_stroke_fill_uv(const float (*points2d)[2], bGPDstroke *gps, const float minv[2], - float maxv[2], + const float maxv[2], float (*r_uv)[2]) { const float s = sin(gps->uv_rotation); @@ -1289,9 +1289,9 @@ void BKE_gpencil_stroke_geometry_update(bGPDstroke *gps) /** * Calculate grease pencil stroke length. - * @param gps Grease pencil stroke - * @param use_3d Set to true to use 3D points - * @return Length of the stroke + * \param gps: Grease pencil stroke + * \param use_3d: Set to true to use 3D points + * \return Length of the stroke */ float BKE_gpencil_stroke_length(const bGPDstroke *gps, bool use_3d) { @@ -2395,8 +2395,8 @@ void BKE_gpencil_convert_mesh(Main *bmain, /** * Apply grease pencil Transforms. - * @param gpd Grease pencil data-block - * @param mat Transformation matrix + * \param gpd: Grease pencil data-block + * \param mat: Transformation matrix */ void BKE_gpencil_transform(bGPdata *gpd, float mat[4][4]) { diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index 24cfc65a8fe..e92bf5a4502 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -197,7 +197,7 @@ static int gpencil_time_modifier( /** * Set current grease pencil active frame. * \param depsgraph: Current depsgraph - * \param gpd: Grease pencil data-block + * \param gpd: Grease pencil data-block. */ void BKE_gpencil_frame_active_set(Depsgraph *depsgraph, bGPdata *gpd) { @@ -223,8 +223,7 @@ void BKE_gpencil_frame_active_set(Depsgraph *depsgraph, bGPdata *gpd) } /** - * Init grease pencil modifier. - * \param void + * Initialize grease pencil modifier. */ void BKE_gpencil_modifier_init(void) { @@ -460,7 +459,6 @@ GpencilModifierData *BKE_gpencil_modifiers_findby_type(Object *ob, GpencilModifi * Set grease pencil modifier error. * \param md: Modifier data * \param _format: Format - * \param */ void BKE_gpencil_modifier_set_error(GpencilModifierData *md, const char *_format, ...) { @@ -560,8 +558,8 @@ static int gpencil_remap_time_get(Depsgraph *depsgraph, Scene *scene, Object *ob return remap_cfra; } -/** Get the current frame retimed with time modifiers. - * \param depsgraph: Current depsgraph +/** Get the current frame re-timed with time modifiers. + * \param depsgraph: Current depsgraph. * \param scene: Current scene * \param ob: Grease pencil object * \param gpl: Grease pencil layer diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c index 2684e964eb1..1166ad9ad2f 100644 --- a/source/blender/blenkernel/intern/idtype.c +++ b/source/blender/blenkernel/intern/idtype.c @@ -36,8 +36,11 @@ #include "BLT_translation.h" #include "DNA_ID.h" +#include "DNA_node_types.h" +#include "DNA_scene_types.h" #include "BKE_main.h" +#include "BKE_node.h" #include "BKE_idtype.h" @@ -470,3 +473,33 @@ short BKE_idtype_idcode_iter_step(int *index) { return (*index < ARRAY_SIZE(id_types)) ? BKE_idtype_idcode_from_index((*index)++) : 0; } + +/** Wrapper around IDTypeInfo foreach_cache that also handles embedded IDs. */ +void BKE_idtype_id_foreach_cache(struct ID *id, + IDTypeForeachCacheFunctionCallback function_callback, + void *user_data) +{ + const IDTypeInfo *type_info = BKE_idtype_get_info_from_id(id); + if (type_info->foreach_cache != NULL) { + type_info->foreach_cache(id, function_callback, user_data); + } + + /* Handle 'private IDs'. */ + bNodeTree *nodetree = ntreeFromID(id); + if (nodetree != NULL) { + type_info = BKE_idtype_get_info_from_id(&nodetree->id); + if (type_info->foreach_cache != NULL) { + type_info->foreach_cache(&nodetree->id, function_callback, user_data); + } + } + + if (GS(id->name) == ID_SCE) { + Scene *scene = (Scene *)id; + if (scene->master_collection != NULL) { + type_info = BKE_idtype_get_info_from_id(&scene->master_collection->id); + if (type_info->foreach_cache != NULL) { + type_info->foreach_cache(&scene->master_collection->id, function_callback, user_data); + } + } + } +} diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 41ef5dc00ef..647349108e1 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -195,24 +195,24 @@ static void image_foreach_cache(ID *id, .offset_in_ID = offsetof(Image, cache), .cache_v = image->cache, }; - function_callback(id, &key, (void **)&image->cache, user_data); + function_callback(id, &key, (void **)&image->cache, 0, user_data); for (int eye = 0; eye < 2; eye++) { for (int a = 0; a < TEXTARGET_COUNT; a++) { key.offset_in_ID = offsetof(Image, gputexture[a][eye]); key.cache_v = image->gputexture[a][eye]; - function_callback(id, &key, (void **)&image->gputexture[a][eye], user_data); + function_callback(id, &key, (void **)&image->gputexture[a][eye], 0, user_data); } } key.offset_in_ID = offsetof(Image, rr); key.cache_v = image->rr; - function_callback(id, &key, (void **)&image->rr, user_data); + function_callback(id, &key, (void **)&image->rr, 0, user_data); LISTBASE_FOREACH (RenderSlot *, slot, &image->renderslots) { key.offset_in_ID = (size_t)BLI_ghashutil_strhash_p(slot->name); key.cache_v = slot->render; - function_callback(id, &key, (void **)&slot->render, user_data); + function_callback(id, &key, (void **)&slot->render, 0, user_data); } } @@ -4363,7 +4363,7 @@ static ImBuf *load_image_single(Image *ima, /* make packed file for autopack */ if ((has_packed == false) && (G.fileflags & G_FILE_AUTOPACK)) { - ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image Packefile"); + ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image Pack-file"); BLI_addtail(&ima->packedfiles, imapf); STRNCPY(imapf->filepath, filepath); diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index af8ab22e14b..46a41df0391 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -529,7 +529,13 @@ static int setkeys(float fac, ListBase *lb, KeyBlock *k[], float t[4], int cycl) return 0; } -static void flerp(int tot, float *in, float *f0, float *f1, float *f2, float *f3, float *t) +static void flerp(int tot, + float *in, + const float *f0, + const float *f1, + const float *f2, + const float *f3, + const float *t) { int a; @@ -538,7 +544,7 @@ static void flerp(int tot, float *in, float *f0, float *f1, float *f2, float *f3 } } -static void rel_flerp(int tot, float *in, float *ref, float *out, float fac) +static void rel_flerp(int tot, float *in, const float *ref, const float *out, float fac) { int a; diff --git a/source/blender/blenkernel/intern/lattice_deform.c b/source/blender/blenkernel/intern/lattice_deform.c index 2b3349d4d9a..674ee9ed2c5 100644 --- a/source/blender/blenkernel/intern/lattice_deform.c +++ b/source/blender/blenkernel/intern/lattice_deform.c @@ -418,8 +418,8 @@ static void lattice_deform_coords_impl(const Object *ob_lattice, void BKE_lattice_deform_coords(const Object *ob_lattice, const Object *ob_target, float (*vert_coords)[3], - int vert_coords_len, - short flag, + const int vert_coords_len, + const short flag, const char *defgrp_name, float fac) { diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 7c09ae51344..eb440de1a6f 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -611,41 +611,39 @@ bool BKE_id_copy(Main *bmain, const ID *id, ID **newid) * Invokes the appropriate copy method for the block and returns the result in * newid, unless test. Returns true if the block can be copied. */ -ID *BKE_id_copy_for_duplicate(Main *bmain, - ID *id, - const bool is_owner_id_liboverride, - const eDupli_ID_Flags duplicate_flags) +ID *BKE_id_copy_for_duplicate(Main *bmain, ID *id, const eDupli_ID_Flags duplicate_flags) { if (id == NULL) { - return NULL; + return id; } if (id->newid == NULL) { - if (!is_owner_id_liboverride || !ID_IS_LINKED(id)) { - ID *id_new; - BKE_id_copy(bmain, id, &id_new); - /* Copying add one user by default, need to get rid of that one. */ - id_us_min(id_new); - ID_NEW_SET(id, id_new); - - /* Shape keys are always copied with their owner ID, by default. */ - ID *key_new = (ID *)BKE_key_from_id(id_new); - ID *key = (ID *)BKE_key_from_id(id); - if (key != NULL) { - ID_NEW_SET(key, key_new); - } + const bool do_linked_id = (duplicate_flags & USER_DUP_LINKED_ID) != 0; + if (!(do_linked_id || !ID_IS_LINKED(id))) { + return id; + } - /* Note: embedded data (root nodetrees and master collections) should never be referenced by - * anything else, so we do not need to set their newid pointer and flag. */ + ID *id_new; + BKE_id_copy(bmain, id, &id_new); + /* Copying add one user by default, need to get rid of that one. */ + id_us_min(id_new); + ID_NEW_SET(id, id_new); - if (duplicate_flags & USER_DUP_ACT) { - BKE_animdata_copy_id_action(bmain, id_new, true); - if (key_new != NULL) { - BKE_animdata_copy_id_action(bmain, key_new, true); - } - /* Note that actions of embedded data (root nodetrees and master collections) are handled - * by `BKE_animdata_copy_id_action` as well. */ - } + /* Shape keys are always copied with their owner ID, by default. */ + ID *key_new = (ID *)BKE_key_from_id(id_new); + ID *key = (ID *)BKE_key_from_id(id); + if (key != NULL) { + ID_NEW_SET(key, key_new); + } + + /* Note: embedded data (root nodetrees and master collections) should never be referenced by + * anything else, so we do not need to set their newid pointer and flag. */ + + BKE_animdata_duplicate_id_action(bmain, id_new, duplicate_flags); + if (key_new != NULL) { + BKE_animdata_duplicate_id_action(bmain, id_new, duplicate_flags); } + /* Note that actions of embedded data (root nodetrees and master collections) are handled + * by `BKE_animdata_duplicate_id_action` as well. */ } return id->newid; } diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 457d096f983..58856729f24 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -38,6 +38,7 @@ #include "BKE_key.h" #include "BKE_lib_id.h" #include "BKE_lib_override.h" +#include "BKE_lib_query.h" #include "BKE_lib_remap.h" #include "BKE_main.h" @@ -66,20 +67,6 @@ static void lib_override_library_property_clear(IDOverrideLibraryProperty *op); static void lib_override_library_property_operation_clear( IDOverrideLibraryPropertyOperation *opop); -/* Temp, for until library override is ready and tested enough to go 'public', - * we hide it by default in UI and such. */ -static bool _lib_override_library_enabled = true; - -void BKE_lib_override_library_enable(const bool do_enable) -{ - _lib_override_library_enabled = do_enable; -} - -bool BKE_lib_override_library_is_enabled() -{ - return _lib_override_library_enabled; -} - /** Initialize empty overriding of \a reference_id by \a local_id. */ IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id) { @@ -368,6 +355,56 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain) return success; } +static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id) +{ + MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->id_user_to_used, id); + + /* This way we won't process again that ID should we encounter it again through another + * relationship hierarchy. + * Note that this does not free any memory from relations, so we can still use the entries. + */ + BKE_main_relations_ID_remove(bmain, id); + + for (; entry != NULL; entry = entry->next) { + /* We only consider IDs from the same library. */ + if (entry->id_pointer != NULL && (*entry->id_pointer)->lib == id->lib) { + if (lib_override_hierarchy_recursive_tag(bmain, *entry->id_pointer)) { + id->tag |= LIB_TAG_DOIT; + } + } + } + + return (id->tag & LIB_TAG_DOIT) != 0; +} + +/** + * Tag all IDs in given \a bmain that use (depends on) given \a id_root ID. + * + * This will include all local IDs, and all IDs from the same library as the \a id_root. + * + * \param id_root The root of the hierarchy of dependencies to be tagged. + * \param do_create_main_relashionships Whether main relations needs to be created or already exist + * (in any case, they will be freed by this function). + */ +void BKE_lib_override_library_dependencies_tag(struct Main *bmain, + struct ID *id_root, + const uint tag, + const bool do_create_main_relashionships) +{ + id_root->tag |= tag; + + if (do_create_main_relashionships) { + BKE_main_relations_create(bmain, 0); + } + + /* Then we tag all intermediary data-blocks in-between two overridden ones (e.g. if a shapekey + * has a driver using an armature object's bone, we need to override the shapekey/obdata, the + * objects using them, etc.) */ + lib_override_hierarchy_recursive_tag(bmain, id_root); + + BKE_main_relations_free(bmain); +} + /* We only build override GHash on request. */ BLI_INLINE IDOverrideLibraryRuntime *override_library_rna_path_mapping_ensure( IDOverrideLibrary *override) diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index 49c909850de..7e859799a4e 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -624,7 +624,7 @@ void BKE_mask_point_segment_co(MaskSpline *spline, MaskSplinePoint *point, float co, bezt->vec[1], bezt->vec[2], bezt_next->vec[0], bezt_next->vec[1], u); } -BLI_INLINE void orthogonal_direction_get(float vec[2], float result[2]) +BLI_INLINE void orthogonal_direction_get(const float vec[2], float result[2]) { result[0] = -vec[1]; result[1] = vec[0]; diff --git a/source/blender/blenkernel/intern/mask_rasterize.c b/source/blender/blenkernel/intern/mask_rasterize.c index 412ccd3ab39..7f98266becc 100644 --- a/source/blender/blenkernel/intern/mask_rasterize.c +++ b/source/blender/blenkernel/intern/mask_rasterize.c @@ -1256,7 +1256,7 @@ static float maskrasterize_layer_z_depth_quad( return w[2] + w[3]; /* we can make this assumption for small speedup */ } -static float maskrasterize_layer_isect(unsigned int *face, +static float maskrasterize_layer_isect(const unsigned int *face, float (*cos)[3], const float dist_orig, const float xy[2]) @@ -1489,6 +1489,8 @@ static void maskrasterize_buffer_cb(void *__restrict userdata, void BKE_maskrasterize_buffer(MaskRasterHandle *mr_handle, const unsigned int width, const unsigned int height, + /* Cannot be const, because it is assigned to non-const variable. + * NOLINTNEXTLINE: readability-non-const-parameter. */ float *buffer) { const float x_inv = 1.0f / (float)width; diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c index b298a6a2787..49957b584ad 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.c +++ b/source/blender/blenkernel/intern/mesh_evaluate.c @@ -2888,7 +2888,7 @@ void BKE_mesh_loops_to_mface_corners( void BKE_mesh_loops_to_tessdata(CustomData *fdata, CustomData *ldata, MFace *mface, - int *polyindices, + const int *polyindices, unsigned int (*loopindices)[4], const int num_faces) { @@ -2981,7 +2981,7 @@ void BKE_mesh_loops_to_tessdata(CustomData *fdata, void BKE_mesh_tangent_loops_to_tessdata(CustomData *fdata, CustomData *ldata, MFace *mface, - int *polyindices, + const int *polyindices, unsigned int (*loopindices)[4], const int num_faces, const char *layer_name) diff --git a/source/blender/blenkernel/intern/mesh_mapping.c b/source/blender/blenkernel/intern/mesh_mapping.c index 4ed9b31dbb5..686f58a0ceb 100644 --- a/source/blender/blenkernel/intern/mesh_mapping.c +++ b/source/blender/blenkernel/intern/mesh_mapping.c @@ -953,7 +953,7 @@ void BKE_mesh_loop_islands_free(MeshIslandStore *island_store) void BKE_mesh_loop_islands_add(MeshIslandStore *island_store, const int item_num, - int *items_indices, + const int *items_indices, const int num_island_items, int *island_item_indices, const int num_innercut_items, diff --git a/source/blender/blenkernel/intern/mesh_remap.c b/source/blender/blenkernel/intern/mesh_remap.c index 404d6a581ae..a4991675d2d 100644 --- a/source/blender/blenkernel/intern/mesh_remap.c +++ b/source/blender/blenkernel/intern/mesh_remap.c @@ -1071,7 +1071,7 @@ static void mesh_island_to_astar_graph_edge_process(MeshIslandStore *islands, BLI_bitmap *done_edges, MeshElemMap *edge_to_poly_map, const bool is_edge_innercut, - int *poly_island_index_map, + const int *poly_island_index_map, float (*poly_centers)[3], unsigned char *poly_status) { diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index d36f5ed0329..4a65c6ff5e7 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -139,11 +139,11 @@ static void movie_clip_foreach_cache(ID *id, .offset_in_ID = offsetof(MovieClip, cache), .cache_v = movie_clip->cache, }; - function_callback(id, &key, (void **)&movie_clip->cache, user_data); + function_callback(id, &key, (void **)&movie_clip->cache, 0, user_data); key.offset_in_ID = offsetof(MovieClip, tracking.camera.intrinsics); key.cache_v = movie_clip->tracking.camera.intrinsics; - function_callback(id, &key, (void **)&movie_clip->tracking.camera.intrinsics, user_data); + function_callback(id, &key, (void **)&movie_clip->tracking.camera.intrinsics, 0, user_data); } IDTypeInfo IDType_ID_MC = { diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index 7e78be6d66e..71d49dd1c19 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -175,7 +175,7 @@ static BLI_bitmap *multires_mdisps_upsample_hidden(BLI_bitmap *lo_hidden, return subd; } -static BLI_bitmap *multires_mdisps_downsample_hidden(BLI_bitmap *old_hidden, +static BLI_bitmap *multires_mdisps_downsample_hidden(const BLI_bitmap *old_hidden, int old_level, int new_level) { diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.c b/source/blender/blenkernel/intern/multires_reshape_smooth.c index 3564ae80d24..e12e692ea23 100644 --- a/source/blender/blenkernel/intern/multires_reshape_smooth.c +++ b/source/blender/blenkernel/intern/multires_reshape_smooth.c @@ -271,7 +271,7 @@ static void base_surface_grids_allocate(MultiresReshapeSmoothContext *reshape_sm for (int grid_index = 0; grid_index < num_grids; ++grid_index) { surface_grid[grid_index].points = MEM_calloc_arrayN( - sizeof(SurfacePoint), grid_area, "delta grid dispalcement"); + sizeof(SurfacePoint), grid_area, "delta grid displacement"); } reshape_smooth_context->base_surface_grids = surface_grid; diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.c b/source/blender/blenkernel/intern/multires_unsubdivide.c index 6bd7b6b6a98..fa1a53f946e 100644 --- a/source/blender/blenkernel/intern/multires_unsubdivide.c +++ b/source/blender/blenkernel/intern/multires_unsubdivide.c @@ -80,7 +80,7 @@ /** * Used to check if a vertex is in a disconnected element ID. */ -static bool is_vertex_in_id(BMVert *v, int *elem_id, int elem) +static bool is_vertex_in_id(BMVert *v, const int *elem_id, int elem) { const int v_index = BM_elem_index_get(v); return elem_id[v_index] == elem; diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index b73f957535c..52f0d259058 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -315,6 +315,33 @@ static void node_foreach_id(ID *id, LibraryForeachIDData *data) } } +static void node_foreach_cache(ID *id, + IDTypeForeachCacheFunctionCallback function_callback, + void *user_data) +{ + bNodeTree *nodetree = (bNodeTree *)id; + IDCacheKey key = { + .id_session_uuid = id->session_uuid, + .offset_in_ID = offsetof(bNodeTree, previews), + .cache_v = nodetree->previews, + }; + + /* TODO, see also `direct_link_nodetree()` in readfile.c. */ +#if 0 + function_callback(id, &key, (void **)&nodetree->previews, 0, user_data); +#endif + + if (nodetree->type == NTREE_COMPOSIT) { + for (bNode *node = nodetree->nodes.first; node; node = node->next) { + if (node->type == CMP_NODE_MOVIEDISTORTION) { + key.offset_in_ID = (size_t)BLI_ghashutil_strhash_p(node->name); + key.cache_v = node->storage; + function_callback(id, &key, (void **)&node->storage, 0, user_data); + } + } + } +} + IDTypeInfo IDType_ID_NT = { .id_code = ID_NT, .id_filter = FILTER_ID_NT, @@ -330,6 +357,7 @@ IDTypeInfo IDType_ID_NT = { .free_data = ntree_free_data, .make_local = NULL, .foreach_id = node_foreach_id, + .foreach_cache = node_foreach_cache, }; static void node_add_sockets_from_type(bNodeTree *ntree, bNode *node, bNodeType *ntype) diff --git a/source/blender/blenkernel/intern/node_tree_multi_function.cc b/source/blender/blenkernel/intern/node_tree_multi_function.cc new file mode 100644 index 00000000000..4e505db9b9d --- /dev/null +++ b/source/blender/blenkernel/intern/node_tree_multi_function.cc @@ -0,0 +1,306 @@ +/* + * 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_node_tree_multi_function.hh" + +#include "BLI_float3.hh" + +namespace blender { +namespace bke { + +/* Maybe this should be moved to BKE_node.h. */ +static std::optional<fn::MFDataType> try_get_multi_function_data_type_of_socket( + const bNodeSocket *bsocket) +{ + if (bsocket->typeinfo->get_mf_data_type == nullptr) { + return {}; + } + return bsocket->typeinfo->get_mf_data_type(); +} + +static void insert_dummy_node(CommonMFNetworkBuilderData &common, const DNode &dnode) +{ + constexpr uint stack_capacity = 10; + + Vector<fn::MFDataType, stack_capacity> input_types; + Vector<StringRef, stack_capacity> input_names; + Vector<const DInputSocket *, stack_capacity> input_dsockets; + + for (const DInputSocket *dsocket : dnode.inputs()) { + if (dsocket->is_available()) { + std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket( + dsocket->bsocket()); + if (data_type.has_value()) { + input_types.append(*data_type); + input_names.append(dsocket->name()); + input_dsockets.append(dsocket); + } + } + } + + Vector<fn::MFDataType, stack_capacity> output_types; + Vector<StringRef, stack_capacity> output_names; + Vector<const DOutputSocket *, stack_capacity> output_dsockets; + + for (const DOutputSocket *dsocket : dnode.outputs()) { + if (dsocket->is_available()) { + std::optional<fn::MFDataType> data_type = try_get_multi_function_data_type_of_socket( + dsocket->bsocket()); + if (data_type.has_value()) { + output_types.append(*data_type); + output_names.append(dsocket->name()); + output_dsockets.append(dsocket); + } + } + } + + fn::MFDummyNode &dummy_node = common.network.add_dummy( + dnode.name(), input_types, output_types, input_names, output_names); + + common.network_map.add(input_dsockets, dummy_node.inputs()); + common.network_map.add(output_dsockets, dummy_node.outputs()); +} + +static bool has_data_sockets(const DNode &dnode) +{ + for (const DInputSocket *socket : dnode.inputs()) { + if (is_multi_function_data_socket(socket->bsocket())) { + return true; + } + } + for (const DOutputSocket *socket : dnode.outputs()) { + if (is_multi_function_data_socket(socket->bsocket())) { + return true; + } + } + return false; +} + +/** + * Expands all function nodes in the multi-function network. Nodes that don't have an expand + * function, but do have data sockets, will get corresponding dummy nodes. + */ +static void insert_nodes(CommonMFNetworkBuilderData &common) +{ + for (const DNode *dnode : common.tree.nodes()) { + const bNodeType *node_type = dnode->node_ref().bnode()->typeinfo; + if (node_type->expand_in_mf_network != nullptr) { + NodeMFNetworkBuilder builder{common, *dnode}; + node_type->expand_in_mf_network(builder); + } + else if (has_data_sockets(*dnode)) { + insert_dummy_node(common, *dnode); + } + } +} + +static void insert_group_inputs(CommonMFNetworkBuilderData &common) +{ + for (const DGroupInput *group_input : common.tree.group_inputs()) { + bNodeSocket *bsocket = group_input->bsocket(); + if (is_multi_function_data_socket(bsocket)) { + bNodeSocketType *socktype = bsocket->typeinfo; + BLI_assert(socktype->expand_in_mf_network != nullptr); + + SocketMFNetworkBuilder builder{common, *group_input}; + socktype->expand_in_mf_network(builder); + + fn::MFOutputSocket *from_socket = builder.built_socket(); + BLI_assert(from_socket != nullptr); + common.network_map.add(*group_input, *from_socket); + } + } +} + +static fn::MFOutputSocket *try_find_origin(CommonMFNetworkBuilderData &common, + const DInputSocket &to_dsocket) +{ + Span<const DOutputSocket *> from_dsockets = to_dsocket.linked_sockets(); + Span<const DGroupInput *> from_group_inputs = to_dsocket.linked_group_inputs(); + uint total_linked_amount = from_dsockets.size() + from_group_inputs.size(); + BLI_assert(total_linked_amount <= 1); + + if (total_linked_amount == 0) { + return nullptr; + } + + if (from_dsockets.size() == 1) { + if (is_multi_function_data_socket(from_dsockets[0]->bsocket())) { + return &common.network_map.lookup(*from_dsockets[0]); + } + else { + return nullptr; + } + } + else { + if (is_multi_function_data_socket(from_group_inputs[0]->bsocket())) { + return &common.network_map.lookup(*from_group_inputs[0]); + } + else { + return nullptr; + } + } +} + +static const fn::MultiFunction *try_get_conversion_function(fn::MFDataType from, fn::MFDataType to) +{ + if (from == fn::MFDataType::ForSingle<float>()) { + if (to == fn::MFDataType::ForSingle<int32_t>()) { + static fn::CustomMF_Convert<float, int32_t> function; + return &function; + } + if (to == fn::MFDataType::ForSingle<float3>()) { + static fn::CustomMF_Convert<float, float3> function; + return &function; + } + } + if (from == fn::MFDataType::ForSingle<float3>()) { + if (to == fn::MFDataType::ForSingle<float>()) { + static fn::CustomMF_SI_SO<float3, float> function{"Vector Length", + [](float3 a) { return a.length(); }}; + return &function; + } + } + if (from == fn::MFDataType::ForSingle<int32_t>()) { + if (to == fn::MFDataType::ForSingle<float>()) { + static fn::CustomMF_Convert<int32_t, float> function; + return &function; + } + if (to == fn::MFDataType::ForSingle<float3>()) { + static fn::CustomMF_SI_SO<int32_t, float3> function{ + "int32 to float3", [](int32_t a) { return float3((float)a); }}; + return &function; + } + } + + return nullptr; +} + +static fn::MFOutputSocket &insert_default_value_for_type(CommonMFNetworkBuilderData &common, + fn::MFDataType type) +{ + const fn::MultiFunction *default_fn; + if (type.is_single()) { + default_fn = &common.resources.construct<fn::CustomMF_GenericConstant>( + AT, type.single_type(), type.single_type().default_value()); + } + else { + default_fn = &common.resources.construct<fn::CustomMF_GenericConstantArray>( + AT, fn::GSpan(type.vector_base_type())); + } + + fn::MFNode &node = common.network.add_function(*default_fn); + return node.output(0); +} + +static void insert_links(CommonMFNetworkBuilderData &common) +{ + for (const DInputSocket *to_dsocket : common.tree.input_sockets()) { + if (!to_dsocket->is_available()) { + continue; + } + if (!to_dsocket->is_linked()) { + continue; + } + if (!is_multi_function_data_socket(to_dsocket->bsocket())) { + continue; + } + + Span<fn::MFInputSocket *> to_sockets = common.network_map.lookup(*to_dsocket); + BLI_assert(to_sockets.size() >= 1); + fn::MFDataType to_type = to_sockets[0]->data_type(); + + fn::MFOutputSocket *from_socket = try_find_origin(common, *to_dsocket); + if (from_socket == nullptr) { + from_socket = &insert_default_value_for_type(common, to_type); + } + + fn::MFDataType from_type = from_socket->data_type(); + + if (from_type != to_type) { + const fn::MultiFunction *conversion_fn = try_get_conversion_function(from_type, to_type); + if (conversion_fn != nullptr) { + fn::MFNode &node = common.network.add_function(*conversion_fn); + common.network.add_link(*from_socket, node.input(0)); + from_socket = &node.output(0); + } + else { + from_socket = &insert_default_value_for_type(common, to_type); + } + } + + for (fn::MFInputSocket *to_socket : to_sockets) { + common.network.add_link(*from_socket, *to_socket); + } + } +} + +static void insert_unlinked_input(CommonMFNetworkBuilderData &common, const DInputSocket &dsocket) +{ + bNodeSocket *bsocket = dsocket.bsocket(); + bNodeSocketType *socktype = bsocket->typeinfo; + BLI_assert(socktype->expand_in_mf_network != nullptr); + + SocketMFNetworkBuilder builder{common, dsocket}; + socktype->expand_in_mf_network(builder); + + fn::MFOutputSocket *from_socket = builder.built_socket(); + BLI_assert(from_socket != nullptr); + + for (fn::MFInputSocket *to_socket : common.network_map.lookup(dsocket)) { + common.network.add_link(*from_socket, *to_socket); + } +} + +static void insert_unlinked_inputs(CommonMFNetworkBuilderData &common) +{ + Vector<const DInputSocket *> unlinked_data_inputs; + for (const DInputSocket *dsocket : common.tree.input_sockets()) { + if (dsocket->is_available()) { + if (is_multi_function_data_socket(dsocket->bsocket())) { + if (!dsocket->is_linked()) { + insert_unlinked_input(common, *dsocket); + } + } + } + } +} + +/** + * Expands all function nodes contained in the given node tree within the given multi-function + * network. + * + * Returns a mapping between the original node tree and the generated nodes/sockets for further + * processing. + */ +MFNetworkTreeMap insert_node_tree_into_mf_network(fn::MFNetwork &network, + const DerivedNodeTree &tree, + ResourceCollector &resources) +{ + MFNetworkTreeMap network_map{tree, network}; + + CommonMFNetworkBuilderData common{resources, network, network_map, tree}; + + insert_nodes(common); + insert_group_inputs(common); + insert_links(common); + insert_unlinked_inputs(common); + + return network_map; +} + +} // namespace bke +} // namespace blender diff --git a/source/blender/blenkernel/intern/node_tree_ref.cc b/source/blender/blenkernel/intern/node_tree_ref.cc index 54ea2d338db..5c998a06cb5 100644 --- a/source/blender/blenkernel/intern/node_tree_ref.cc +++ b/source/blender/blenkernel/intern/node_tree_ref.cc @@ -52,8 +52,8 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree) RNA_pointer_create(&btree->id, &RNA_NodeSocket, bsocket, &socket.rna_); } - input_sockets_.extend(node.inputs_); - output_sockets_.extend(node.outputs_); + input_sockets_.extend(node.inputs_.as_span()); + output_sockets_.extend(node.outputs_.as_span()); node_mapping.add_new(bnode, &node); } diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 6331f87f09f..d48ea33cc65 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1760,7 +1760,7 @@ Object *BKE_object_copy(Main *bmain, const Object *ob) */ Object *BKE_object_duplicate(Main *bmain, Object *ob, - const eDupli_ID_Flags dupflag, + eDupli_ID_Flags dupflag, const eLibIDDuplicateFlags duplicate_options) { const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0; @@ -1768,10 +1768,14 @@ Object *BKE_object_duplicate(Main *bmain, if (!is_subprocess) { BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); BKE_main_id_clear_newpoins(bmain); + /* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate + * all expected linked data. */ + if (ID_IS_LINKED(ob)) { + dupflag |= USER_DUP_LINKED_ID; + } } Material ***matarar; - const bool is_object_liboverride = ID_IS_OVERRIDE_LIBRARY(ob); Object *obn; BKE_id_copy(bmain, &ob->id, (ID **)&obn); @@ -1785,112 +1789,109 @@ Object *BKE_object_duplicate(Main *bmain, return obn; } - /* duplicates using userflags */ - if (dupflag & USER_DUP_ACT) { - BKE_animdata_copy_id_action(bmain, &obn->id, true); - } + BKE_animdata_duplicate_id_action(bmain, &obn->id, dupflag); if (dupflag & USER_DUP_MAT) { for (int i = 0; i < obn->totcol; i++) { - BKE_id_copy_for_duplicate(bmain, (ID *)obn->mat[i], is_object_liboverride, dupflag); + BKE_id_copy_for_duplicate(bmain, (ID *)obn->mat[i], dupflag); } } if (dupflag & USER_DUP_PSYS) { ParticleSystem *psys; for (psys = obn->particlesystem.first; psys; psys = psys->next) { - BKE_id_copy_for_duplicate(bmain, (ID *)psys->part, is_object_liboverride, dupflag); + BKE_id_copy_for_duplicate(bmain, (ID *)psys->part, dupflag); } } - ID *id = obn->data; + ID *id_old = obn->data; ID *id_new = NULL; - const bool need_to_duplicate_obdata = (id != NULL) && (id->newid == NULL); + const bool need_to_duplicate_obdata = (id_old != NULL) && (id_old->newid == NULL); switch (obn->type) { case OB_MESH: if (dupflag & USER_DUP_MESH) { - id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_CURVE: if (dupflag & USER_DUP_CURVE) { - id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_SURF: if (dupflag & USER_DUP_SURF) { - id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_FONT: if (dupflag & USER_DUP_FONT) { - id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_MBALL: if (dupflag & USER_DUP_MBALL) { - id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_LAMP: if (dupflag & USER_DUP_LAMP) { - id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_ARMATURE: if (dupflag & USER_DUP_ARM) { - id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_LATTICE: if (dupflag != 0) { - id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_CAMERA: if (dupflag != 0) { - id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_LIGHTPROBE: if (dupflag & USER_DUP_LIGHTPROBE) { - id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_SPEAKER: if (dupflag != 0) { - id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_GPENCIL: if (dupflag & USER_DUP_GPENCIL) { - id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_HAIR: if (dupflag & USER_DUP_HAIR) { - id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_POINTCLOUD: if (dupflag & USER_DUP_POINTCLOUD) { - id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; case OB_VOLUME: if (dupflag & USER_DUP_VOLUME) { - id_new = BKE_id_copy_for_duplicate(bmain, id, is_object_liboverride, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); } break; } /* If obdata has been copied, we may also have to duplicate the materials assigned to it. */ - if (need_to_duplicate_obdata && id_new != NULL) { + if (need_to_duplicate_obdata && !ELEM(id_new, NULL, id_old)) { if (dupflag & USER_DUP_MAT) { matarar = BKE_object_material_array_p(obn); if (matarar) { for (int i = 0; i < obn->totcol; i++) { - BKE_id_copy_for_duplicate(bmain, (ID *)(*matarar)[i], is_object_liboverride, dupflag); + BKE_id_copy_for_duplicate(bmain, (ID *)(*matarar)[i], dupflag); } } } diff --git a/source/blender/blenkernel/intern/object_deform.c b/source/blender/blenkernel/intern/object_deform.c index 6ca1442497a..51ec89cf77d 100644 --- a/source/blender/blenkernel/intern/object_deform.c +++ b/source/blender/blenkernel/intern/object_deform.c @@ -68,7 +68,7 @@ static Lattice *object_defgroup_lattice_get(ID *id) * * \param map: an array mapping old indices to new indices. */ -void BKE_object_defgroup_remap_update_users(Object *ob, int *map) +void BKE_object_defgroup_remap_update_users(Object *ob, const int *map) { ModifierData *md; ParticleSystem *psys; diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c index 4c6354f12a1..f498e147110 100644 --- a/source/blender/blenkernel/intern/object_dupli.c +++ b/source/blender/blenkernel/intern/object_dupli.c @@ -186,7 +186,7 @@ static DupliObject *make_dupli(const DupliContext *ctx, Object *ob, float mat[4] dob->random_id = BLI_hash_string(dob->ob->id.name + 2); if (dob->persistent_id[0] != INT_MAX) { - for (i = 0; i < MAX_DUPLI_RECUR * 2; i++) { + for (i = 0; i < MAX_DUPLI_RECUR; i++) { dob->random_id = BLI_hash_int_2d(dob->random_id, (unsigned int)dob->persistent_id[i]); } } diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 609c5c1e580..f94ef946851 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -147,19 +147,19 @@ static void init_complex(fftw_complex cmpl, float real, float image) cmpl[1] = image; } -static void add_comlex_c(fftw_complex res, fftw_complex cmpl1, fftw_complex cmpl2) +static void add_comlex_c(fftw_complex res, const fftw_complex cmpl1, const fftw_complex cmpl2) { res[0] = cmpl1[0] + cmpl2[0]; res[1] = cmpl1[1] + cmpl2[1]; } -static void mul_complex_f(fftw_complex res, fftw_complex cmpl, float f) +static void mul_complex_f(fftw_complex res, const fftw_complex cmpl, float f) { res[0] = cmpl[0] * (double)f; res[1] = cmpl[1] * (double)f; } -static void mul_complex_c(fftw_complex res, fftw_complex cmpl1, fftw_complex cmpl2) +static void mul_complex_c(fftw_complex res, const fftw_complex cmpl1, const fftw_complex cmpl2) { fftwf_complex temp; temp[0] = cmpl1[0] * cmpl2[0] - cmpl1[1] * cmpl2[1]; @@ -178,7 +178,7 @@ static float image_c(fftw_complex cmpl) return cmpl[1]; } -static void conj_complex(fftw_complex res, fftw_complex cmpl1) +static void conj_complex(fftw_complex res, const fftw_complex cmpl1) { res[0] = cmpl1[0]; res[1] = -cmpl1[1]; diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index b3ab856468c..dca2022382a 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -1514,7 +1514,7 @@ static void sculpt_update_object(Depsgraph *depsgraph, /* Add a color layer if a color tool is used. */ Mesh *orig_me = BKE_object_get_original_mesh(ob); - if (need_colors) { + if (need_colors && U.experimental.use_sculpt_vertex_colors) { if (!CustomData_has_layer(&orig_me->vdata, CD_PROP_COLOR)) { CustomData_add_layer(&orig_me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, orig_me->totvert); BKE_mesh_update_customdata_pointers(orig_me, true); diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 942f3e0ca2b..a003daf1042 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -283,8 +283,8 @@ int count_particles_mod(ParticleSystem *psys, int totgr, int cur) } return tot; } -/* we allocate path cache memory in chunks instead of a big contiguous - * chunk, windows' memory allocater fails to find big blocks of memory often */ +/* We allocate path cache memory in chunks instead of a big contiguous + * chunk, windows' memory allocator fails to find big blocks of memory often. */ #define PATH_CACHE_BUF_SIZE 1024 @@ -1297,7 +1297,7 @@ static void do_particle_interpolation(ParticleSystem *psys, dfra = keys[2].time - keys[1].time; keytime = (real_t - keys[1].time) / dfra; - /* convert velocity to timestep size */ + /* Convert velocity to time-step size. */ if (pind->keyed || pind->cache || point_vel) { invdt = dfra * 0.04f * (psys ? psys->part->timetweak : 1.f); mul_v3_fl(keys[1].vel, invdt); @@ -1305,8 +1305,8 @@ static void do_particle_interpolation(ParticleSystem *psys, interp_qt_qtqt(result->rot, keys[1].rot, keys[2].rot, keytime); } - /* Now we should have in chronologiacl order k1<=k2<=t<=k3<=k4 with keytime between - * [0, 1]->[k2, k3] (k1 & k4 used for cardinal & bspline interpolation). */ + /* Now we should have in chronological order k1<=k2<=t<=k3<=k4 with key-time between + * [0, 1]->[k2, k3] (k1 & k4 used for cardinal & b-spline interpolation). */ psys_interpolate_particle((pind->keyed || pind->cache || point_vel) ? -1 /* signal for cubic interpolation */ : @@ -3611,7 +3611,8 @@ void psys_mat_hair_to_global( /************************************************/ /* ParticleSettings handling */ /************************************************/ -ModifierData *object_add_particle_system(Main *bmain, Scene *scene, Object *ob, const char *name) +static ModifierData *object_add_or_copy_particle_system( + Main *bmain, Scene *scene, Object *ob, const char *name, const ParticleSystem *psys_orig) { ParticleSystem *psys; ModifierData *md; @@ -3622,7 +3623,7 @@ ModifierData *object_add_particle_system(Main *bmain, Scene *scene, Object *ob, } if (name == NULL) { - name = DATA_("ParticleSettings"); + name = (psys_orig != NULL) ? psys_orig->name : DATA_("ParticleSettings"); } psys = ob->particlesystem.first; @@ -3635,8 +3636,13 @@ ModifierData *object_add_particle_system(Main *bmain, Scene *scene, Object *ob, BLI_addtail(&ob->particlesystem, psys); psys_unique_name(ob, psys, name); - psys->part = BKE_particlesettings_add(bmain, psys->name); - + if (psys_orig != NULL) { + psys->part = psys_orig->part; + id_us_plus(&psys->part->id); + } + else { + psys->part = BKE_particlesettings_add(bmain, psys->name); + } md = BKE_modifier_new(eModifierType_ParticleSystem); BLI_strncpy(md->name, psys->name, sizeof(md->name)); BKE_modifier_unique_name(&ob->modifiers, md); @@ -3656,6 +3662,20 @@ ModifierData *object_add_particle_system(Main *bmain, Scene *scene, Object *ob, return md; } + +ModifierData *object_add_particle_system(Main *bmain, Scene *scene, Object *ob, const char *name) +{ + return object_add_or_copy_particle_system(bmain, scene, ob, name, NULL); +} + +ModifierData *object_copy_particle_system(Main *bmain, + Scene *scene, + Object *ob, + const ParticleSystem *psys_orig) +{ + return object_add_or_copy_particle_system(bmain, scene, ob, NULL, psys_orig); +} + void object_remove_particle_system(Main *bmain, Scene *UNUSED(scene), Object *ob) { ParticleSystem *psys = psys_get_current(ob); diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c index 7b9b2484dbe..e0dccd4d14a 100644 --- a/source/blender/blenkernel/intern/particle_distribute.c +++ b/source/blender/blenkernel/intern/particle_distribute.c @@ -433,7 +433,7 @@ static void psys_uv_to_w(float u, float v, int quad, float *w) } /* Find the index in "sum" array before "value" is crossed. */ -static int distribute_binary_search(float *sum, int n, float value) +static int distribute_binary_search(const float *sum, int n, float value) { int mid, low = 0, high = n - 1; diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 4dc4aea04a7..bf9aea81181 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -1939,7 +1939,7 @@ static void sphclassical_density_accum_cb(void *userdata, return; } - /* Smoothing factor. Utilise the Wendland kernel. gnuplot: + /* Smoothing factor. Utilize the Wendland kernel. gnuplot: * q1(x) = (2.0 - x)**4 * ( 1.0 + 2.0 * x) * plot [0:2] q1(x) */ q = qfac / pow3f(pfr->h) * pow4f(2.0f - rij_h) * (1.0f + 2.0f * rij_h); @@ -2054,7 +2054,7 @@ static void sphclassical_force_cb(void *sphdata_v, npressure = stiffness * (pow7f(npa->sphdensity / rest_density) - 1.0f); - /* First derivative of smoothing factor. Utilise the Wendland kernel. + /* First derivative of smoothing factor. Utilize the Wendland kernel. * gnuplot: * q2(x) = 2.0 * (2.0 - x)**4 - 4.0 * (2.0 - x)**3 * (1.0 + 2.0 * x) * plot [0:2] q2(x) @@ -2947,7 +2947,7 @@ static int collision_response(ParticleSimulationData *sim, /* get exact velocity right before collision */ madd_v3_v3v3fl(v0, col->ve1, col->acc, dt1); - /* Convert collider velocity from 1/framestep to 1/s TODO: + /* Convert collider velocity from `1/frame_step` to `1/s` TODO: * here we assume 1 frame step for collision modifier. */ mul_v3_fl(pce->vel, col->inv_timestep); @@ -4966,6 +4966,7 @@ void particle_system_update(struct Depsgraph *depsgraph, psys_orig->flag = (psys->flag & ~PSYS_SHARED_CACHES); psys_orig->cfra = psys->cfra; psys_orig->recalc = psys->recalc; + psys_orig->part->totpart = part->totpart; } } diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 8d7dabf9859..67988427bd2 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -358,7 +358,7 @@ static void update_vb(PBVH *pbvh, PBVHNode *node, BBC *prim_bbc, int offset, int /* Returns the number of visible quads in the nodes' grids. */ int BKE_pbvh_count_grid_quads(BLI_bitmap **grid_hidden, - int *grid_indices, + const int *grid_indices, int totgrid, int gridsize) { @@ -1542,7 +1542,7 @@ static void pbvh_update_visibility_task_cb(void *__restrict userdata, PBVHUpdateData *data = userdata; PBVH *pbvh = data->pbvh; PBVHNode *node = data->nodes[n]; - if (node->flag & PBVH_UpdateMask) { + if (node->flag & PBVH_UpdateVisibility) { switch (BKE_pbvh_type(pbvh)) { case PBVH_FACES: pbvh_faces_node_visibility_update(pbvh, node); @@ -1554,7 +1554,7 @@ static void pbvh_update_visibility_task_cb(void *__restrict userdata, pbvh_bmesh_node_visibility_update(node); break; } - node->flag &= ~PBVH_UpdateMask; + node->flag &= ~PBVH_UpdateVisibility; } } @@ -1772,6 +1772,11 @@ void BKE_pbvh_node_fully_hidden_set(PBVHNode *node, int fully_hidden) } } +bool BKE_pbvh_node_fully_hidden_get(PBVHNode *node) +{ + return (node->flag & PBVH_Leaf) && (node->flag & PBVH_FullyHidden); +} + void BKE_pbvh_node_fully_masked_set(PBVHNode *node, int fully_masked) { BLI_assert(node->flag & PBVH_Leaf); diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 61308810191..9a9d8dc215b 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -206,7 +206,7 @@ static int ptcache_softbody_write(int index, void *soft_v, void **data, int UNUS return 1; } static void ptcache_softbody_read( - int index, void *soft_v, void **data, float UNUSED(cfra), float *old_data) + int index, void *soft_v, void **data, float UNUSED(cfra), const float *old_data) { SoftBody *soft = soft_v; BodyPoint *bp = soft->bpoint + index; @@ -220,8 +220,13 @@ static void ptcache_softbody_read( PTCACHE_DATA_TO(data, BPHYS_DATA_VELOCITY, 0, bp->vec); } } -static void ptcache_softbody_interpolate( - int index, void *soft_v, void **data, float cfra, float cfra1, float cfra2, float *old_data) +static void ptcache_softbody_interpolate(int index, + void *soft_v, + void **data, + float cfra, + float cfra1, + float cfra2, + const float *old_data) { SoftBody *soft = soft_v; BodyPoint *bp = soft->bpoint + index; @@ -316,7 +321,7 @@ static int ptcache_particle_write(int index, void *psys_v, void **data, int cfra return 1 + (pa->state.time >= pa->time && pa->prev_state.time <= pa->time); } static void ptcache_particle_read( - int index, void *psys_v, void **data, float cfra, float *old_data) + int index, void *psys_v, void **data, float cfra, const float *old_data) { ParticleSystem *psys = psys_v; ParticleData *pa; @@ -383,8 +388,13 @@ static void ptcache_particle_read( unit_qt(pa->state.rot); } } -static void ptcache_particle_interpolate( - int index, void *psys_v, void **data, float cfra, float cfra1, float cfra2, float *old_data) +static void ptcache_particle_interpolate(int index, + void *psys_v, + void **data, + float cfra, + float cfra1, + float cfra2, + const float *old_data) { ParticleSystem *psys = psys_v; ParticleData *pa; @@ -528,7 +538,7 @@ static int ptcache_cloth_write(int index, void *cloth_v, void **data, int UNUSED return 1; } static void ptcache_cloth_read( - int index, void *cloth_v, void **data, float UNUSED(cfra), float *old_data) + int index, void *cloth_v, void **data, float UNUSED(cfra), const float *old_data) { ClothModifierData *clmd = cloth_v; Cloth *cloth = clmd->clothObject; @@ -545,8 +555,13 @@ static void ptcache_cloth_read( PTCACHE_DATA_TO(data, BPHYS_DATA_XCONST, 0, vert->xconst); } } -static void ptcache_cloth_interpolate( - int index, void *cloth_v, void **data, float cfra, float cfra1, float cfra2, float *old_data) +static void ptcache_cloth_interpolate(int index, + void *cloth_v, + void **data, + float cfra, + float cfra1, + float cfra2, + const float *old_data) { ClothModifierData *clmd = cloth_v; Cloth *cloth = clmd->clothObject; @@ -1509,7 +1524,7 @@ static int ptcache_rigidbody_write(int index, void *rb_v, void **data, int UNUSE return 1; } static void ptcache_rigidbody_read( - int index, void *rb_v, void **data, float UNUSED(cfra), float *old_data) + int index, void *rb_v, void **data, float UNUSED(cfra), const float *old_data) { RigidBodyWorld *rbw = rb_v; Object *ob = NULL; @@ -1534,8 +1549,13 @@ static void ptcache_rigidbody_read( } } } -static void ptcache_rigidbody_interpolate( - int index, void *rb_v, void **data, float cfra, float cfra1, float cfra2, float *old_data) +static void ptcache_rigidbody_interpolate(int index, + void *rb_v, + void **data, + float cfra, + float cfra1, + float cfra2, + const float *old_data) { RigidBodyWorld *rbw = rb_v; Object *ob = NULL; @@ -1887,7 +1907,7 @@ static int ptcache_sim_particle_write(int index, void *state_v, void **data, int return 1; } static void ptcache_sim_particle_read( - int index, void *state_v, void **data, float UNUSED(cfra), float *UNUSED(old_data)) + int index, void *state_v, void **data, float UNUSED(cfra), const float *UNUSED(old_data)) { ParticleSimulationState *state = (ParticleSimulationState *)state_v; @@ -1898,19 +1918,13 @@ static void ptcache_sim_particle_read( PTCACHE_DATA_TO(data, BPHYS_DATA_LOCATION, 0, positions + (index * 3)); } -void BKE_ptcache_id_from_sim_particles(PTCacheID *pid, ParticleSimulationState *state) +void BKE_ptcache_id_from_sim_particles(PTCacheID *pid, + ParticleSimulationState *state_orig, + ParticleSimulationState *state_cow) { memset(pid, 0, sizeof(PTCacheID)); - ParticleSimulationState *state_orig; - if (state->head.orig_state != NULL) { - state_orig = (ParticleSimulationState *)state->head.orig_state; - } - else { - state_orig = state; - } - - pid->calldata = state; + pid->calldata = state_cow; pid->type = PTCACHE_TYPE_SIM_PARTICLES; pid->cache = state_orig->point_cache; pid->cache_ptr = &state_orig->point_cache; @@ -2050,11 +2064,7 @@ static bool foreach_object_modifier_ptcache(Object *object, LISTBASE_FOREACH (SimulationState *, state, &smd->simulation->states) { switch ((eSimulationStateType)state->type) { case SIM_STATE_TYPE_PARTICLES: { - ParticleSimulationState *particle_state = (ParticleSimulationState *)state; - BKE_ptcache_id_from_sim_particles(&pid, particle_state); - if (!callback(&pid, callback_user_data)) { - return false; - } + /* TODO(jacques) */ break; } } @@ -2289,7 +2299,9 @@ static int ptcache_filename(PTCacheID *pid, char *filename, int cfra, short do_p return len; /* make sure the above string is always 16 chars */ } -/* youll need to close yourself after! */ +/** + * Caller must close after! + */ static PTCacheFile *ptcache_file_open(PTCacheID *pid, int mode, int cfra) { PTCacheFile *pf; diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 7f3d6eb0372..5ae2f4b9005 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -581,7 +581,11 @@ static void scene_foreach_cache(ID *id, .cache_v = scene->eevee.light_cache_data, }; - function_callback(id, &key, (void **)&scene->eevee.light_cache_data, user_data); + function_callback(id, + &key, + (void **)&scene->eevee.light_cache_data, + IDTYPE_CACHE_CB_FLAGS_PERSISTENT, + user_data); } IDTypeInfo IDType_ID_SCE = { @@ -766,7 +770,6 @@ void BKE_scene_copy_data_eevee(Scene *sce_dst, const Scene *sce_src) Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) { - const bool is_scene_liboverride = ID_IS_OVERRIDE_LIBRARY(sce); Scene *sce_copy; /* TODO this should/could most likely be replaced by call to more generic code at some point... @@ -837,15 +840,13 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) return sce_copy; } else { - const eDupli_ID_Flags duplicate_flags = U.dupflag | USER_DUP_OBJECT; + eDupli_ID_Flags duplicate_flags = U.dupflag | USER_DUP_OBJECT; BKE_id_copy(bmain, (ID *)sce, (ID **)&sce_copy); id_us_min(&sce_copy->id); id_us_ensure_real(&sce_copy->id); - if (duplicate_flags & USER_DUP_ACT) { - BKE_animdata_copy_id_action(bmain, &sce_copy->id, true); - } + BKE_animdata_duplicate_id_action(bmain, &sce_copy->id, duplicate_flags); /* Extra actions, most notably SCE_FULL_COPY also duplicates several 'children' datablocks. */ @@ -856,22 +857,26 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) if (!is_subprocess) { BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false); BKE_main_id_clear_newpoins(bmain); + /* In case root duplicated ID is linked, assume we want to get a local copy of it and + * duplicate all expected linked data. */ + if (ID_IS_LINKED(sce)) { + duplicate_flags |= USER_DUP_LINKED_ID; + } } /* Copy Freestyle LineStyle datablocks. */ LISTBASE_FOREACH (ViewLayer *, view_layer_dst, &sce_copy->view_layers) { LISTBASE_FOREACH ( FreestyleLineSet *, lineset, &view_layer_dst->freestyle_config.linesets) { - BKE_id_copy_for_duplicate( - bmain, &lineset->linestyle->id, is_scene_liboverride, duplicate_flags); + BKE_id_copy_for_duplicate(bmain, (ID *)lineset->linestyle, duplicate_flags); } } /* Full copy of world (included animations) */ - BKE_id_copy_for_duplicate(bmain, &sce->world->id, is_scene_liboverride, duplicate_flags); + BKE_id_copy_for_duplicate(bmain, (ID *)sce->world, duplicate_flags); /* Full copy of GreasePencil. */ - BKE_id_copy_for_duplicate(bmain, &sce->gpd->id, is_scene_liboverride, duplicate_flags); + BKE_id_copy_for_duplicate(bmain, (ID *)sce->gpd, duplicate_flags); /* Deep-duplicate collections and objects (using preferences' settings for which sub-data to * duplicate along the object itself). */ diff --git a/source/blender/blenkernel/intern/seqeffects.c b/source/blender/blenkernel/intern/seqeffects.c index de233a8d473..4a2ad88bb28 100644 --- a/source/blender/blenkernel/intern/seqeffects.c +++ b/source/blender/blenkernel/intern/seqeffects.c @@ -2668,7 +2668,7 @@ static void RVAddBitmaps_float(float *a, float *b, float *c, int width, int heig } static void RVIsolateHighlights_float( - float *in, float *out, int width, int height, float threshold, float boost, float clamp) + const float *in, float *out, int width, int height, float threshold, float boost, float clamp) { int x, y, index; float intensity; @@ -3423,7 +3423,7 @@ static void do_gaussian_blur_effect_byte_x(Sequence *seq, int y, int frame_width, int UNUSED(frame_height), - unsigned char *rect, + const unsigned char *rect, unsigned char *out) { #define INDEX(_x, _y) (((_y) * (x) + (_x)) * 4) @@ -3473,7 +3473,7 @@ static void do_gaussian_blur_effect_byte_y(Sequence *seq, int y, int UNUSED(frame_width), int frame_height, - unsigned char *rect, + const unsigned char *rect, unsigned char *out) { #define INDEX(_x, _y) (((_y) * (x) + (_x)) * 4) diff --git a/source/blender/blenkernel/intern/seqmodifier.c b/source/blender/blenkernel/intern/seqmodifier.c index 604cbf476a8..a630170d6d5 100644 --- a/source/blender/blenkernel/intern/seqmodifier.c +++ b/source/blender/blenkernel/intern/seqmodifier.c @@ -57,7 +57,7 @@ typedef void (*modifier_apply_threaded_cb)(int width, unsigned char *rect, float *rect_float, unsigned char *mask_rect, - float *mask_rect_float, + const float *mask_rect_float, void *data_v); typedef struct ModifierInitData { @@ -223,7 +223,7 @@ static void whiteBalance_apply_threaded(int width, unsigned char *rect, float *rect_float, unsigned char *mask_rect, - float *mask_rect_float, + const float *mask_rect_float, void *data_v) { int x, y; @@ -331,7 +331,7 @@ static void curves_apply_threaded(int width, unsigned char *rect, float *rect_float, unsigned char *mask_rect, - float *mask_rect_float, + const float *mask_rect_float, void *data_v) { CurveMapping *curve_mapping = (CurveMapping *)data_v; @@ -461,7 +461,7 @@ static void hue_correct_apply_threaded(int width, unsigned char *rect, float *rect_float, unsigned char *mask_rect, - float *mask_rect_float, + const float *mask_rect_float, void *data_v) { CurveMapping *curve_mapping = (CurveMapping *)data_v; @@ -556,7 +556,7 @@ static void brightcontrast_apply_threaded(int width, unsigned char *rect, float *rect_float, unsigned char *mask_rect, - float *mask_rect_float, + const float *mask_rect_float, void *data_v) { BrightContrastThreadData *data = (BrightContrastThreadData *)data_v; @@ -658,7 +658,7 @@ static void maskmodifier_apply_threaded(int width, unsigned char *rect, float *rect_float, unsigned char *mask_rect, - float *mask_rect_float, + const float *mask_rect_float, void *UNUSED(data_v)) { int x, y; @@ -755,7 +755,7 @@ static void tonemapmodifier_apply_threaded_simple(int width, unsigned char *rect, float *rect_float, unsigned char *mask_rect, - float *mask_rect_float, + const float *mask_rect_float, void *data_v) { AvgLogLum *avg = (AvgLogLum *)data_v; @@ -814,7 +814,7 @@ static void tonemapmodifier_apply_threaded_photoreceptor(int width, unsigned char *rect, float *rect_float, unsigned char *mask_rect, - float *mask_rect_float, + const float *mask_rect_float, void *data_v) { AvgLogLum *avg = (AvgLogLum *)data_v; diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 297d60e5976..7339c887151 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -114,8 +114,7 @@ static ImBuf *seq_render_preprocess_ibuf(const SeqRenderData *context, float cfra, clock_t begin, bool use_preprocess, - const bool is_proxy_image, - const bool is_preprocessed); + const bool is_proxy_image); static ImBuf *seq_render_strip(const SeqRenderData *context, SeqRenderState *state, Sequence *seq, @@ -1091,6 +1090,64 @@ void BKE_sequence_reload_new_file(Main *bmain, Scene *scene, Sequence *seq, cons BKE_sequence_calc(scene, seq); } +void BKE_sequence_movie_reload_if_needed(struct Main *bmain, + struct Scene *scene, + struct Sequence *seq, + bool *r_was_reloaded, + bool *r_can_produce_frames) +{ + BLI_assert(seq->type == SEQ_TYPE_MOVIE || + !"This function is only implemented for movie strips."); + + bool must_reload = false; + + /* The Sequence struct allows for multiple anim structs to be associated with one strip. This + * function will return true only if there is at least one 'anim' AND all anims can produce + * frames. */ + + if (BLI_listbase_is_empty(&seq->anims)) { + /* No anim present, so reloading is always necessary. */ + must_reload = true; + } + else { + LISTBASE_FOREACH (StripAnim *, sanim, &seq->anims) { + if (!IMB_anim_can_produce_frames(sanim->anim)) { + /* Anim cannot produce frames, try reloading. */ + must_reload = true; + break; + } + }; + } + + if (!must_reload) { + /* There are one or more anims, and all can produce frames. */ + *r_was_reloaded = false; + *r_can_produce_frames = true; + return; + } + + BKE_sequence_reload_new_file(bmain, scene, seq, true); + *r_was_reloaded = true; + + if (BLI_listbase_is_empty(&seq->anims)) { + /* No anims present after reloading => no frames can be produced. */ + *r_can_produce_frames = false; + return; + } + + /* Check if there are still anims that cannot produce frames. */ + LISTBASE_FOREACH (StripAnim *, sanim, &seq->anims) { + if (!IMB_anim_can_produce_frames(sanim->anim)) { + /* There still is an anim that cannot produce frames. */ + *r_can_produce_frames = false; + return; + } + }; + + /* There are one or more anims, and all can produce frames. */ + *r_can_produce_frames = true; +} + void BKE_sequencer_sort(Scene *scene) { /* all strips together per kind, and in order of y location ("machine") */ @@ -1332,30 +1389,6 @@ ListBase *BKE_sequence_seqbase_get(Sequence *seq, int *r_offset) /*********************** DO THE SEQUENCE *************************/ -static void make_black_ibuf(ImBuf *ibuf) -{ - unsigned int *rect; - float *rect_float; - int tot; - - if (ibuf == NULL || (ibuf->rect == NULL && ibuf->rect_float == NULL)) { - return; - } - - tot = ibuf->x * ibuf->y; - - rect = ibuf->rect; - rect_float = ibuf->rect_float; - - if (rect) { - memset(rect, 0, tot * sizeof(char) * 4); - } - - if (rect_float) { - memset(rect_float, 0, tot * sizeof(float) * 4); - } -} - static void multibuf(ImBuf *ibuf, const float fmul) { char *rt; @@ -2415,7 +2448,7 @@ static void color_balance_byte_float(StripColorBalance *cb_, static void color_balance_float_float(StripColorBalance *cb_, float *rect_float, - float *mask_rect_float, + const float *mask_rect_float, int width, int height, float mul) @@ -2657,8 +2690,7 @@ static ImBuf *input_preprocess(const SeqRenderData *context, Sequence *seq, float cfra, ImBuf *ibuf, - const bool is_proxy_image, - const bool is_preprocessed) + const bool is_proxy_image) { Scene *scene = context->scene; float mul; @@ -2672,15 +2704,6 @@ static ImBuf *input_preprocess(const SeqRenderData *context, if (seq->flag & (SEQ_USE_CROP | SEQ_USE_TRANSFORM)) { StripCrop c = {0}; StripTransform t = {0}; - int sx, sy, dx, dy; - - if (is_proxy_image) { - double f = BKE_sequencer_rendersize_to_scale_factor(context->preview_render_size); - - if (f != 1.0) { - IMB_scalefastImBuf(ibuf, ibuf->x * f, ibuf->y * f); - } - } if (seq->flag & SEQ_USE_CROP && seq->strip->crop) { c = *seq->strip->crop; @@ -2689,33 +2712,41 @@ static ImBuf *input_preprocess(const SeqRenderData *context, t = *seq->strip->transform; } - if (is_preprocessed) { - double xscale = scene->r.xsch ? ((double)context->rectx / (double)scene->r.xsch) : 1.0; - double yscale = scene->r.ysch ? ((double)context->recty / (double)scene->r.ysch) : 1.0; - if (seq->flag & SEQ_USE_TRANSFORM) { - t.xofs *= xscale; - t.yofs *= yscale; + /* Calculate scale factor for current image if needed. */ + double scale_factor, image_scale_factor = 1.0; + if (context->preview_render_size == SEQ_PROXY_RENDER_SIZE_SCENE) { + scale_factor = image_scale_factor = (double)scene->r.size / 100; + } + else { + scale_factor = BKE_sequencer_rendersize_to_scale_factor(context->preview_render_size); + if (!is_proxy_image) { + image_scale_factor = scale_factor; } - if (seq->flag & SEQ_USE_CROP) { - c.left *= xscale; - c.right *= xscale; - c.top *= yscale; - c.bottom *= yscale; + } + + if (image_scale_factor != 1.0) { + if (context->for_render) { + IMB_scaleImBuf(ibuf, ibuf->x * image_scale_factor, ibuf->y * image_scale_factor); + } + else { + IMB_scalefastImBuf(ibuf, ibuf->x * image_scale_factor, ibuf->y * image_scale_factor); } } + t.xofs *= scale_factor; + t.yofs *= scale_factor; + c.left *= scale_factor; + c.right *= scale_factor; + c.top *= scale_factor; + c.bottom *= scale_factor; + + int sx, sy, dx, dy; sx = ibuf->x - c.left - c.right; sy = ibuf->y - c.top - c.bottom; if (seq->flag & SEQ_USE_TRANSFORM) { - if (is_preprocessed) { - dx = context->rectx; - dy = context->recty; - } - else { - dx = scene->r.xsch; - dy = scene->r.ysch; - } + dx = context->rectx; + dy = context->recty; } else { dx = sx; @@ -2724,19 +2755,15 @@ static ImBuf *input_preprocess(const SeqRenderData *context, if (c.top + c.bottom >= ibuf->y || c.left + c.right >= ibuf->x || t.xofs >= dx || t.yofs >= dy) { - make_black_ibuf(ibuf); + return NULL; } - else { - ImBuf *i = IMB_allocImBuf(dx, dy, 32, ibuf->rect_float ? IB_rectfloat : IB_rect); - - IMB_rectcpy(i, ibuf, t.xofs, t.yofs, c.left, c.bottom, sx, sy); - sequencer_imbuf_assign_spaces(scene, i); - - IMB_metadata_copy(i, ibuf); - IMB_freeImBuf(ibuf); - ibuf = i; - } + ImBuf *i = IMB_allocImBuf(dx, dy, 32, ibuf->rect_float ? IB_rectfloat : IB_rect); + IMB_rectcpy(i, ibuf, t.xofs, t.yofs, c.left, c.bottom, sx, sy); + sequencer_imbuf_assign_spaces(scene, i); + IMB_metadata_copy(i, ibuf); + IMB_freeImBuf(ibuf); + ibuf = i; } if (seq->flag & SEQ_FLIPX) { @@ -3097,7 +3124,7 @@ static ImBuf *seq_render_image_strip(const SeqRenderData *context, if (view_id != context->view_id) { ibufs_arr[view_id] = seq_render_preprocess_ibuf( - &localcontext, seq, ibufs_arr[view_id], cfra, clock(), true, false, false); + &localcontext, seq, ibufs_arr[view_id], cfra, clock(), true, false); } } @@ -3214,7 +3241,7 @@ static ImBuf *seq_render_movie_strip( if (view_id != context->view_id) { ibuf_arr[view_id] = seq_render_preprocess_ibuf( - &localcontext, seq, ibuf_arr[view_id], cfra, clock(), true, false, false); + &localcontext, seq, ibuf_arr[view_id], cfra, clock(), true, false); } } @@ -3804,8 +3831,7 @@ static ImBuf *seq_render_preprocess_ibuf(const SeqRenderData *context, float cfra, clock_t begin, bool use_preprocess, - const bool is_proxy_image, - const bool is_preprocessed) + const bool is_proxy_image) { if (context->is_proxy_render == false && (ibuf->x != context->rectx || ibuf->y != context->recty)) { @@ -3814,11 +3840,17 @@ static ImBuf *seq_render_preprocess_ibuf(const SeqRenderData *context, if (use_preprocess) { float cost = seq_estimate_render_cost_end(context->scene, begin); - BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_RAW, ibuf, cost, false); + + /* TODO (Richard): It should be possible to store in cache if image is proxy, + * but it adds quite a bit of complexity. Since proxies are fast to read, I would + * rather simplify existing code a bit. */ + if (!is_proxy_image) { + BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_RAW, ibuf, cost, false); + } /* Reset timer so we can get partial render time. */ begin = seq_estimate_render_cost_begin(); - ibuf = input_preprocess(context, seq, cfra, ibuf, is_proxy_image, is_preprocessed); + ibuf = input_preprocess(context, seq, cfra, ibuf, is_proxy_image); } float cost = seq_estimate_render_cost_end(context->scene, begin); @@ -3834,11 +3866,6 @@ static ImBuf *seq_render_strip(const SeqRenderData *context, ImBuf *ibuf = NULL; bool use_preprocess = false; bool is_proxy_image = false; - /* all effects are handled similarly with the exception of speed effect */ - int type = (seq->type & SEQ_TYPE_EFFECT && seq->type != SEQ_TYPE_SPEED) ? SEQ_TYPE_EFFECT : - seq->type; - bool is_preprocessed = !ELEM( - type, SEQ_TYPE_IMAGE, SEQ_TYPE_MOVIE, SEQ_TYPE_SCENE, SEQ_TYPE_MOVIECLIP); clock_t begin = seq_estimate_render_cost_begin(); @@ -3855,7 +3882,7 @@ static ImBuf *seq_render_strip(const SeqRenderData *context, if (ibuf) { use_preprocess = BKE_sequencer_input_have_to_preprocess(context, seq, cfra); ibuf = seq_render_preprocess_ibuf( - context, seq, ibuf, cfra, begin, use_preprocess, is_proxy_image, is_preprocessed); + context, seq, ibuf, cfra, begin, use_preprocess, is_proxy_image); } if (ibuf == NULL) { diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index c4a35141b0d..e163bb8da8d 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -31,6 +31,7 @@ #include "BLI_float3.hh" #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_rand.h" #include "BLI_span.hh" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -44,6 +45,7 @@ #include "BKE_lib_remap.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_node_tree_multi_function.hh" #include "BKE_pointcache.h" #include "BKE_simulation.h" @@ -51,9 +53,18 @@ #include "BLT_translation.h" +#include "FN_attributes_ref.hh" +#include "FN_cpp_types.hh" +#include "FN_multi_function_network_evaluation.hh" +#include "FN_multi_function_network_optimization.hh" + #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +extern "C" { +void WM_clipboard_text_set(const char *buf, bool selection); +} + static void simulation_init_data(ID *id) { Simulation *simulation = (Simulation *)id; @@ -63,14 +74,6 @@ static void simulation_init_data(ID *id) bNodeTree *ntree = ntreeAddTree(nullptr, "Simulation Nodetree", ntreeType_Simulation->idname); simulation->nodetree = ntree; - - /* Add a default particle simulation state for now. */ - ParticleSimulationState *state = (ParticleSimulationState *)MEM_callocN( - sizeof(ParticleSimulationState), __func__); - CustomData_reset(&state->attributes); - - state->point_cache = BKE_ptcache_add(&state->ptcaches); - BLI_addtail(&simulation->states, state); } static void simulation_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int flag) @@ -89,19 +92,19 @@ static void simulation_copy_data(Main *bmain, ID *id_dst, const ID *id_src, cons } BLI_listbase_clear(&simulation_dst->states); +} - LISTBASE_FOREACH (const SimulationState *, state_src, &simulation_src->states) { - switch ((eSimulationStateType)state_src->type) { - case SIM_STATE_TYPE_PARTICLES: { - ParticleSimulationState *particle_state_dst = (ParticleSimulationState *)MEM_callocN( - sizeof(ParticleSimulationState), __func__); - CustomData_reset(&particle_state_dst->attributes); +static void free_simulation_state_head(SimulationState *state) +{ + MEM_freeN(state->name); +} - BLI_addtail(&simulation_dst->states, particle_state_dst); - break; - } - } - } +static void free_particle_simulation_state(ParticleSimulationState *state) +{ + free_simulation_state_head(&state->head); + CustomData_free(&state->attributes, state->tot_particles); + BKE_ptcache_free_list(&state->ptcaches); + MEM_freeN(state); } static void simulation_free_data(ID *id) @@ -119,13 +122,10 @@ static void simulation_free_data(ID *id) LISTBASE_FOREACH_MUTABLE (SimulationState *, state, &simulation->states) { switch ((eSimulationStateType)state->type) { case SIM_STATE_TYPE_PARTICLES: { - ParticleSimulationState *particle_state = (ParticleSimulationState *)state; - CustomData_free(&particle_state->attributes, particle_state->tot_particles); - BKE_ptcache_free_list(&particle_state->ptcaches); + free_particle_simulation_state((ParticleSimulationState *)state); break; } } - MEM_freeN(state); } } @@ -166,59 +166,556 @@ void *BKE_simulation_add(Main *bmain, const char *name) namespace blender::bke { -static MutableSpan<float3> get_particle_positions(ParticleSimulationState *state) -{ - return MutableSpan<float3>( - (float3 *)CustomData_get_layer_named(&state->attributes, CD_LOCATION, "Position"), - state->tot_particles); -} - static void ensure_attributes_exist(ParticleSimulationState *state) { if (CustomData_get_layer_named(&state->attributes, CD_LOCATION, "Position") == nullptr) { CustomData_add_layer_named( &state->attributes, CD_LOCATION, CD_CALLOC, nullptr, state->tot_particles, "Position"); } + if (CustomData_get_layer_named(&state->attributes, CD_LOCATION, "Velocity") == nullptr) { + CustomData_add_layer_named( + &state->attributes, CD_LOCATION, CD_CALLOC, nullptr, state->tot_particles, "Velocity"); + } + if (CustomData_get_layer_named(&state->attributes, CD_PROP_INT32, "ID") == nullptr) { + CustomData_add_layer_named( + &state->attributes, CD_PROP_INT32, CD_CALLOC, nullptr, state->tot_particles, "ID"); + } } -static void copy_particle_state_to_cow(ParticleSimulationState *state_orig, - ParticleSimulationState *state_cow) +static void copy_states_to_cow(Simulation *simulation_orig, Simulation *simulation_cow) { - ensure_attributes_exist(state_cow); - CustomData_free(&state_cow->attributes, state_cow->tot_particles); - CustomData_copy(&state_orig->attributes, - &state_cow->attributes, - CD_MASK_ALL, - CD_DUPLICATE, - state_orig->tot_particles); - state_cow->current_frame = state_orig->current_frame; - state_cow->tot_particles = state_orig->tot_particles; + LISTBASE_FOREACH_MUTABLE (SimulationState *, state_cow, &simulation_cow->states) { + switch ((eSimulationStateType)state_cow->type) { + case SIM_STATE_TYPE_PARTICLES: { + BLI_remlink(&simulation_cow->states, state_cow); + free_particle_simulation_state((ParticleSimulationState *)state_cow); + break; + } + } + } + simulation_cow->current_frame = simulation_orig->current_frame; + + LISTBASE_FOREACH (SimulationState *, state_orig, &simulation_orig->states) { + switch ((eSimulationStateType)state_orig->type) { + case SIM_STATE_TYPE_PARTICLES: { + ParticleSimulationState *particle_state_orig = (ParticleSimulationState *)state_orig; + ParticleSimulationState *particle_state_cow = (ParticleSimulationState *)MEM_callocN( + sizeof(*particle_state_cow), AT); + particle_state_cow->tot_particles = particle_state_orig->tot_particles; + particle_state_cow->head.name = BLI_strdup(state_orig->name); + CustomData_copy(&particle_state_orig->attributes, + &particle_state_cow->attributes, + CD_MASK_ALL, + CD_DUPLICATE, + particle_state_orig->tot_particles); + BLI_addtail(&simulation_cow->states, particle_state_cow); + break; + } + } + } } -static void simulation_data_update(Depsgraph *depsgraph, Scene *scene, Simulation *simulation) +static Map<const fn::MFOutputSocket *, std::string> deduplicate_attribute_nodes( + fn::MFNetwork &network, MFNetworkTreeMap &network_map, const DerivedNodeTree &tree) { - int current_frame = scene->r.cfra; + Span<const DNode *> attribute_dnodes = tree.nodes_by_type("SimulationNodeParticleAttribute"); + uint amount = attribute_dnodes.size(); + if (amount == 0) { + return {}; + } - ParticleSimulationState *state_cow = (ParticleSimulationState *)simulation->states.first; - ParticleSimulationState *state_orig = (ParticleSimulationState *)state_cow->head.orig_state; + Vector<fn::MFInputSocket *> name_sockets; + for (const DNode *dnode : attribute_dnodes) { + fn::MFInputSocket &name_socket = network_map.lookup_dummy(dnode->input(0)); + name_sockets.append(&name_socket); + } - if (current_frame == state_cow->current_frame) { - return; + fn::MFNetworkEvaluator network_fn{{}, name_sockets.as_span()}; + + fn::MFParamsBuilder params{network_fn, 1}; + + Array<std::string> attribute_names{amount, NoInitialization()}; + for (uint i : IndexRange(amount)) { + params.add_uninitialized_single_output( + fn::GMutableSpan(fn::CPPType_string, attribute_names.data() + i, 1)); + } + + fn::MFContextBuilder context; + /* Todo: Check that the names don't depend on dummy nodes. */ + network_fn.call({0}, params, context); + + Map<std::pair<std::string, fn::MFDataType>, Vector<fn::MFNode *>> + attribute_nodes_by_name_and_type; + for (uint i : IndexRange(amount)) { + attribute_nodes_by_name_and_type + .lookup_or_add_default({attribute_names[i], name_sockets[i]->node().output(0).data_type()}) + .append(&name_sockets[i]->node()); + } + + Map<const fn::MFOutputSocket *, std::string> attribute_inputs; + for (auto item : attribute_nodes_by_name_and_type.items()) { + StringRef attribute_name = item.key.first; + fn::MFDataType data_type = item.key.second; + Span<fn::MFNode *> nodes = item.value; + + fn::MFOutputSocket &new_attribute_socket = network.add_input( + "Attribute '" + attribute_name + "'", data_type); + for (fn::MFNode *node : nodes) { + network.relink(node->output(0), new_attribute_socket); + } + network.remove(nodes); + + attribute_inputs.add_new(&new_attribute_socket, attribute_name); + } + + return attribute_inputs; +} + +class CustomDataAttributesRef { + private: + Vector<void *> buffers_; + uint size_; + std::unique_ptr<fn::AttributesInfo> info_; + + public: + CustomDataAttributesRef(CustomData &custom_data, uint size) + { + fn::AttributesInfoBuilder builder; + for (const CustomDataLayer &layer : Span(custom_data.layers, custom_data.totlayer)) { + buffers_.append(layer.data); + switch (layer.type) { + case CD_PROP_INT32: { + builder.add<int32_t>(layer.name, 0); + break; + } + case CD_LOCATION: { + builder.add<float3>(layer.name, {0, 0, 0}); + break; + } + } + } + info_ = std::make_unique<fn::AttributesInfo>(builder); + size_ = size; + } + + operator fn::MutableAttributesRef() + { + return fn::MutableAttributesRef(*info_, buffers_, size_); + } + + operator fn::AttributesRef() const + { + return fn::AttributesRef(*info_, buffers_, size_); + } +}; + +static std::string dnode_to_path(const DNode &dnode) +{ + std::string path; + for (const DParentNode *parent = dnode.parent(); parent; parent = parent->parent()) { + path = parent->node_ref().name() + "/" + path; + } + path = path + dnode.name(); + return path; +} + +static void remove_unused_states(Simulation *simulation, const VectorSet<std::string> &state_names) +{ + LISTBASE_FOREACH_MUTABLE (SimulationState *, state, &simulation->states) { + if (!state_names.contains(state->name)) { + BLI_remlink(&simulation->states, state); + free_particle_simulation_state((ParticleSimulationState *)state); + } + } +} + +static void reset_states(Simulation *simulation) +{ + LISTBASE_FOREACH (SimulationState *, state, &simulation->states) { + switch ((eSimulationStateType)state->type) { + case SIM_STATE_TYPE_PARTICLES: { + ParticleSimulationState *particle_state = (ParticleSimulationState *)state; + CustomData_free(&particle_state->attributes, particle_state->tot_particles); + particle_state->tot_particles = 0; + break; + } + } + } +} + +static SimulationState *try_find_state_by_name(Simulation *simulation, StringRef name) +{ + LISTBASE_FOREACH (SimulationState *, state, &simulation->states) { + if (state->name == name) { + return state; + } + } + return nullptr; +} + +static void add_missing_particle_states(Simulation *simulation, Span<std::string> state_names) +{ + for (StringRefNull name : state_names) { + SimulationState *state = try_find_state_by_name(simulation, name); + if (state != nullptr) { + BLI_assert(state->type == SIM_STATE_TYPE_PARTICLES); + continue; + } + + ParticleSimulationState *particle_state = (ParticleSimulationState *)MEM_callocN( + sizeof(*particle_state), AT); + particle_state->head.type = SIM_STATE_TYPE_PARTICLES; + particle_state->head.name = BLI_strdup(name.data()); + CustomData_reset(&particle_state->attributes); + particle_state->point_cache = BKE_ptcache_add(&particle_state->ptcaches); + BLI_addtail(&simulation->states, particle_state); + } +} + +static void reinitialize_empty_simulation_states(Simulation *simulation, + const DerivedNodeTree &tree) +{ + VectorSet<std::string> state_names; + for (const DNode *dnode : tree.nodes_by_type("SimulationNodeParticleSimulation")) { + state_names.add(dnode_to_path(*dnode)); + } + + remove_unused_states(simulation, state_names); + reset_states(simulation); + add_missing_particle_states(simulation, state_names); +} + +static void update_simulation_state_list(Simulation *simulation, const DerivedNodeTree &tree) +{ + VectorSet<std::string> state_names; + for (const DNode *dnode : tree.nodes_by_type("SimulationNodeParticleSimulation")) { + state_names.add(dnode_to_path(*dnode)); + } + + remove_unused_states(simulation, state_names); + add_missing_particle_states(simulation, state_names); +} + +class ParticleFunctionInput { + public: + virtual ~ParticleFunctionInput() = default; + virtual void add_input(fn::AttributesRef attributes, + fn::MFParamsBuilder ¶ms, + ResourceCollector &resources) const = 0; +}; + +class ParticleFunction { + private: + const fn::MultiFunction *global_fn_; + const fn::MultiFunction *per_particle_fn_; + Array<const ParticleFunctionInput *> global_inputs_; + Array<const ParticleFunctionInput *> per_particle_inputs_; + Array<bool> output_is_global_; + Vector<uint> global_output_indices_; + Vector<uint> per_particle_output_indices_; + Vector<fn::MFDataType> output_types_; + Vector<StringRefNull> output_names_; + + friend class ParticleFunctionEvaluator; + + public: + ParticleFunction(const fn::MultiFunction *global_fn, + const fn::MultiFunction *per_particle_fn, + Span<const ParticleFunctionInput *> global_inputs, + Span<const ParticleFunctionInput *> per_particle_inputs, + Span<bool> output_is_global) + : global_fn_(global_fn), + per_particle_fn_(per_particle_fn), + global_inputs_(global_inputs), + per_particle_inputs_(per_particle_inputs), + output_is_global_(output_is_global) + { + for (uint i : output_is_global_.index_range()) { + if (output_is_global_[i]) { + uint param_index = global_inputs_.size() + global_output_indices_.size(); + fn::MFParamType param_type = global_fn_->param_type(param_index); + BLI_assert(param_type.is_output()); + output_types_.append(param_type.data_type()); + output_names_.append(global_fn_->param_name(param_index)); + global_output_indices_.append(i); + } + else { + uint param_index = per_particle_inputs_.size() + per_particle_output_indices_.size(); + fn::MFParamType param_type = per_particle_fn_->param_type(param_index); + BLI_assert(param_type.is_output()); + output_types_.append(param_type.data_type()); + output_names_.append(per_particle_fn_->param_name(param_index)); + per_particle_output_indices_.append(i); + } + } + } +}; + +class ParticleFunctionEvaluator { + private: + ResourceCollector resources_; + const ParticleFunction &particle_fn_; + IndexMask mask_; + fn::MFContextBuilder global_context_; + fn::MFContextBuilder per_particle_context_; + fn::AttributesRef particle_attributes_; + Vector<void *> outputs_; + bool is_computed_ = false; + + public: + ParticleFunctionEvaluator(const ParticleFunction &particle_fn, + IndexMask mask, + fn::AttributesRef particle_attributes) + : particle_fn_(particle_fn), + mask_(mask), + particle_attributes_(particle_attributes), + outputs_(particle_fn_.output_types_.size(), nullptr) + { + } + + ~ParticleFunctionEvaluator() + { + for (uint output_index : outputs_.index_range()) { + void *buffer = outputs_[output_index]; + fn::MFDataType data_type = particle_fn_.output_types_[output_index]; + BLI_assert(data_type.is_single()); /* For now. */ + const fn::CPPType &type = data_type.single_type(); + + if (particle_fn_.output_is_global_[output_index]) { + type.destruct(buffer); + } + else { + type.destruct_indices(outputs_[0], mask_); + } + } + } + + void compute() + { + BLI_assert(!is_computed_); + this->compute_globals(); + this->compute_per_particle(); + is_computed_ = true; + } + + template<typename T> fn::VSpan<T> get(uint output_index, StringRef expected_name) const + { + return this->get(output_index, expected_name).typed<T>(); + } + + fn::GVSpan get(uint output_index, StringRef expected_name) const + { +#ifdef DEBUG + StringRef real_name = particle_fn_.output_names_[output_index]; + BLI_assert(expected_name == real_name); + BLI_assert(is_computed_); +#endif + UNUSED_VARS_NDEBUG(expected_name); + const void *buffer = outputs_[output_index]; + const fn::CPPType &type = particle_fn_.output_types_[output_index].single_type(); + if (particle_fn_.output_is_global_[output_index]) { + return fn::GVSpan::FromSingleWithMaxSize(type, buffer); + } + else { + return fn::GVSpan(fn::GSpan(type, buffer, mask_.min_array_size())); + } + } + + private: + void compute_globals() + { + if (particle_fn_.global_fn_ == nullptr) { + return; + } + + fn::MFParamsBuilder params(*particle_fn_.global_fn_, mask_.min_array_size()); + + /* Add input parameters. */ + for (const ParticleFunctionInput *input : particle_fn_.global_inputs_) { + input->add_input(particle_attributes_, params, resources_); + } + + /* Add output parameters. */ + for (uint output_index : particle_fn_.global_output_indices_) { + fn::MFDataType data_type = particle_fn_.output_types_[output_index]; + BLI_assert(data_type.is_single()); /* For now. */ + + const fn::CPPType &type = data_type.single_type(); + void *buffer = resources_.linear_allocator().allocate(type.size(), type.alignment()); + params.add_uninitialized_single_output(fn::GMutableSpan(type, buffer, 1)); + outputs_[output_index] = buffer; + } + + particle_fn_.global_fn_->call({0}, params, global_context_); + } + + void compute_per_particle() + { + if (particle_fn_.per_particle_fn_ == nullptr) { + return; + } + + fn::MFParamsBuilder params(*particle_fn_.per_particle_fn_, mask_.min_array_size()); + + /* Add input parameters. */ + for (const ParticleFunctionInput *input : particle_fn_.per_particle_inputs_) { + input->add_input(particle_attributes_, params, resources_); + } + + /* Add output parameters. */ + for (uint output_index : particle_fn_.per_particle_output_indices_) { + fn::MFDataType data_type = particle_fn_.output_types_[output_index]; + BLI_assert(data_type.is_single()); /* For now. */ + + const fn::CPPType &type = data_type.single_type(); + void *buffer = resources_.linear_allocator().allocate(type.size() * mask_.min_array_size(), + type.alignment()); + params.add_uninitialized_single_output( + fn::GMutableSpan(type, buffer, mask_.min_array_size())); + outputs_[output_index] = buffer; + } + + particle_fn_.per_particle_fn_->call(mask_, params, global_context_); + } +}; + +class ParticleAttributeInput : public ParticleFunctionInput { + private: + std::string attribute_name_; + const fn::CPPType &attribute_type_; + + public: + ParticleAttributeInput(std::string attribute_name, const fn::CPPType &attribute_type) + : attribute_name_(std::move(attribute_name)), attribute_type_(attribute_type) + { + } + + void add_input(fn::AttributesRef attributes, + fn::MFParamsBuilder ¶ms, + ResourceCollector &UNUSED(resources)) const override + { + std::optional<fn::GSpan> span = attributes.try_get(attribute_name_, attribute_type_); + if (span.has_value()) { + params.add_readonly_single_input(*span); + } + else { + params.add_readonly_single_input(fn::GVSpan::FromDefault(attribute_type_)); + } } +}; + +static const ParticleFunction *create_particle_function_for_inputs( + Span<const fn::MFInputSocket *> sockets_to_compute, + ResourceCollector &resources, + const Map<const fn::MFOutputSocket *, std::string> &attribute_inputs) +{ + BLI_assert(sockets_to_compute.size() >= 1); + const fn::MFNetwork &network = sockets_to_compute[0]->node().network(); + + VectorSet<const fn::MFOutputSocket *> dummy_deps; + VectorSet<const fn::MFInputSocket *> unlinked_input_deps; + network.find_dependencies(sockets_to_compute, dummy_deps, unlinked_input_deps); + BLI_assert(unlinked_input_deps.size() == 0); + + Vector<const ParticleFunctionInput *> per_particle_inputs; + for (const fn::MFOutputSocket *socket : dummy_deps) { + StringRef attribute_name = attribute_inputs.lookup(socket); + per_particle_inputs.append(&resources.construct<ParticleAttributeInput>( + AT, attribute_name, socket->data_type().single_type())); + } + + const fn::MultiFunction &per_particle_fn = resources.construct<fn::MFNetworkEvaluator>( + AT, dummy_deps.as_span(), sockets_to_compute); + + Array<bool> output_is_global(sockets_to_compute.size(), false); + + const ParticleFunction &particle_fn = resources.construct<ParticleFunction>( + AT, + nullptr, + &per_particle_fn, + Span<const ParticleFunctionInput *>(), + per_particle_inputs.as_span(), + output_is_global.as_span()); + + return &particle_fn; +} + +class ParticleForce { + public: + virtual ~ParticleForce() = default; + virtual void add_force(fn::AttributesRef attributes, + MutableSpan<float3> r_combined_force) const = 0; +}; + +class ParticleFunctionForce : public ParticleForce { + private: + const ParticleFunction &particle_fn_; + + public: + ParticleFunctionForce(const ParticleFunction &particle_fn) : particle_fn_(particle_fn) + { + } + + void add_force(fn::AttributesRef attributes, MutableSpan<float3> r_combined_force) const override + { + IndexMask mask = IndexRange(attributes.size()); + ParticleFunctionEvaluator evaluator{particle_fn_, mask, attributes}; + evaluator.compute(); + fn::VSpan<float3> forces = evaluator.get<float3>(0, "Force"); + for (uint i : mask) { + r_combined_force[i] += forces[i]; + } + } +}; + +static Vector<const ParticleForce *> create_forces_for_particle_simulation( + const DNode &simulation_node, + MFNetworkTreeMap &network_map, + ResourceCollector &resources, + const Map<const fn::MFOutputSocket *, std::string> &attribute_inputs) +{ + Vector<const ParticleForce *> forces; + for (const DOutputSocket *origin_socket : simulation_node.input(2, "Forces").linked_sockets()) { + const DNode &origin_node = origin_socket->node(); + if (origin_node.idname() != "SimulationNodeForce") { + continue; + } + + const fn::MFInputSocket &force_socket = network_map.lookup_dummy( + origin_node.input(0, "Force")); + + const ParticleFunction *particle_fn = create_particle_function_for_inputs( + {&force_socket}, resources, attribute_inputs); - /* Number of particles should be stored in the cache, but for now assume it is constant. */ - state_cow->tot_particles = state_orig->tot_particles; - CustomData_realloc(&state_cow->attributes, state_orig->tot_particles); - ensure_attributes_exist(state_cow); + if (particle_fn == nullptr) { + continue; + } + + const ParticleForce &force = resources.construct<ParticleFunctionForce>(AT, *particle_fn); + forces.append(&force); + } + return forces; +} - PTCacheID pid_cow; - BKE_ptcache_id_from_sim_particles(&pid_cow, state_cow); - BKE_ptcache_id_time(&pid_cow, scene, current_frame, nullptr, nullptr, nullptr); +static Map<std::string, Vector<const ParticleForce *>> collect_forces( + MFNetworkTreeMap &network_map, + ResourceCollector &resources, + const Map<const fn::MFOutputSocket *, std::string> &attribute_inputs) +{ + Map<std::string, Vector<const ParticleForce *>> forces_by_simulation; + for (const DNode *dnode : network_map.tree().nodes_by_type("SimulationNodeParticleSimulation")) { + std::string name = dnode_to_path(*dnode); + Vector<const ParticleForce *> forces = create_forces_for_particle_simulation( + *dnode, network_map, resources, attribute_inputs); + forces_by_simulation.add_new(std::move(name), std::move(forces)); + } + return forces_by_simulation; +} - /* If successfull, this will read the state directly into the cow state. */ - int cache_result = BKE_ptcache_read(&pid_cow, current_frame, true); - if (cache_result == PTCACHE_READ_EXACT) { - state_cow->current_frame = current_frame; +static void simulation_data_update(Depsgraph *depsgraph, Scene *scene, Simulation *simulation_cow) +{ + int current_frame = scene->r.cfra; + if (simulation_cow->current_frame == current_frame) { return; } @@ -227,34 +724,82 @@ static void simulation_data_update(Depsgraph *depsgraph, Scene *scene, Simulatio return; } - PTCacheID pid_orig; - BKE_ptcache_id_from_sim_particles(&pid_orig, state_orig); - BKE_ptcache_id_time(&pid_orig, scene, current_frame, nullptr, nullptr, nullptr); + Simulation *simulation_orig = (Simulation *)DEG_get_original_id(&simulation_cow->id); + + NodeTreeRefMap tree_refs; + /* TODO: Use simulation_cow, but need to add depsgraph relations before that. */ + const DerivedNodeTree tree{simulation_orig->nodetree, tree_refs}; + fn::MFNetwork network; + ResourceCollector resources; + MFNetworkTreeMap network_map = insert_node_tree_into_mf_network(network, tree, resources); + // WM_clipboard_text_set(tree.to_dot().c_str(), false); + Map<const fn::MFOutputSocket *, std::string> attribute_inputs = deduplicate_attribute_nodes( + network, network_map, tree); + fn::mf_network_optimization::constant_folding(network, resources); + fn::mf_network_optimization::common_subnetwork_elimination(network); + fn::mf_network_optimization::dead_node_removal(network); + + Map<std::string, Vector<const ParticleForce *>> forces_by_simulation = collect_forces( + network_map, resources, attribute_inputs); if (current_frame == 1) { - state_orig->tot_particles = 100; - state_orig->current_frame = 1; - CustomData_realloc(&state_orig->attributes, state_orig->tot_particles); - ensure_attributes_exist(state_orig); - - MutableSpan<float3> positions = get_particle_positions(state_orig); - for (uint i : positions.index_range()) { - positions[i] = {i / 10.0f, 0, 0}; + reinitialize_empty_simulation_states(simulation_orig, tree); + + RNG *rng = BLI_rng_new(0); + + simulation_orig->current_frame = 1; + LISTBASE_FOREACH (ParticleSimulationState *, state, &simulation_orig->states) { + state->tot_particles = 1000; + CustomData_realloc(&state->attributes, state->tot_particles); + ensure_attributes_exist(state); + + CustomDataAttributesRef custom_data_attributes{state->attributes, + (uint)state->tot_particles}; + + fn::MutableAttributesRef attributes = custom_data_attributes; + MutableSpan<float3> positions = attributes.get<float3>("Position"); + MutableSpan<float3> velocities = attributes.get<float3>("Velocity"); + MutableSpan<int32_t> ids = attributes.get<int32_t>("ID"); + + for (uint i : positions.index_range()) { + positions[i] = {i / 100.0f, 0, 0}; + velocities[i] = {0, BLI_rng_get_float(rng), BLI_rng_get_float(rng) * 2 + 1}; + ids[i] = i; + } } - BKE_ptcache_write(&pid_orig, current_frame); - copy_particle_state_to_cow(state_orig, state_cow); + BLI_rng_free(rng); + + copy_states_to_cow(simulation_orig, simulation_cow); } - else if (current_frame == state_orig->current_frame + 1) { - state_orig->current_frame = current_frame; - ensure_attributes_exist(state_orig); - MutableSpan<float3> positions = get_particle_positions(state_orig); - for (float3 &position : positions) { - position.z += 0.1f; + else if (current_frame == simulation_orig->current_frame + 1) { + update_simulation_state_list(simulation_orig, tree); + float time_step = 1.0f / 24.0f; + simulation_orig->current_frame = current_frame; + + LISTBASE_FOREACH (ParticleSimulationState *, state, &simulation_orig->states) { + ensure_attributes_exist(state); + + CustomDataAttributesRef custom_data_attributes{state->attributes, + (uint)state->tot_particles}; + + fn::MutableAttributesRef attributes = custom_data_attributes; + MutableSpan<float3> positions = attributes.get<float3>("Position"); + MutableSpan<float3> velocities = attributes.get<float3>("Velocity"); + + Array<float3> force_vectors{(uint)state->tot_particles, {0, 0, 0}}; + Span<const ParticleForce *> forces = forces_by_simulation.lookup_as(state->head.name); + for (const ParticleForce *force : forces) { + force->add_force(attributes, force_vectors); + } + + for (uint i : positions.index_range()) { + velocities[i] += force_vectors[i] * time_step; + positions[i] += velocities[i] * time_step; + } } - BKE_ptcache_write(&pid_orig, current_frame); - copy_particle_state_to_cow(state_orig, state_cow); + copy_states_to_cow(simulation_orig, simulation_cow); } } diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index 75da92a26b8..b7b325644ca 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -165,24 +165,28 @@ static void free_softbody_intern(SoftBody *sb); /*physical unit of force is [kg * m / sec^2]*/ -static float sb_grav_force_scale(Object *UNUSED(ob)) -/* since unit of g is [m/sec^2] and F = mass * g we rescale unit mass of node to 1 gramm - * put it to a function here, so we can add user options later without touching simulation code +/** + * Since unit of g is [m/sec^2] and F = mass * g we re-scale unit mass of node to 1 gram + * put it to a function here, so we can add user options later without touching simulation code. */ +static float sb_grav_force_scale(Object *UNUSED(ob)) { return (0.001f); } -static float sb_fric_force_scale(Object *UNUSED(ob)) -/* rescaling unit of drag [1 / sec] to somehow reasonable - * put it to a function here, so we can add user options later without touching simulation code +/** + * Re-scaling unit of drag [1 / sec] to somehow reasonable + * put it to a function here, so we can add user options later without touching simulation code. */ +static float sb_fric_force_scale(Object *UNUSED(ob)) { return (0.01f); } +/** + * Defining the frames to *real* time relation. + */ static float sb_time_scale(Object *ob) -/* defining the frames to *real* time relation */ { SoftBody *sb = ob->soft; /* is supposed to be there */ if (sb) { diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index 18fd8a10cc1..1fcfc9b060f 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -123,7 +123,7 @@ static void sound_foreach_cache(ID *id, .cache_v = sound->waveform, }; - function_callback(id, &key, &sound->waveform, user_data); + function_callback(id, &key, &sound->waveform, 0, user_data); } IDTypeInfo IDType_ID_SO = { diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c index a1e218390c3..22649a2af07 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.c +++ b/source/blender/blenkernel/intern/subdiv_ccg.c @@ -1834,4 +1834,59 @@ const int *BKE_subdiv_ccg_start_face_grid_index_get(const SubdivCCG *subdiv_ccg) return subdiv_ccg->cache_.start_face_grid_index; } +static void adjacet_vertices_index_from_adjacent_edge(const SubdivCCG *subdiv_ccg, + const SubdivCCGCoord *coord, + const MLoop *mloop, + const MPoly *mpoly, + int *r_v1, + int *r_v2) +{ + const int grid_size_1 = subdiv_ccg->grid_size - 1; + const int poly_index = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, coord->grid_index); + const MPoly *p = &mpoly[poly_index]; + *r_v1 = mloop[coord->grid_index].v; + if (coord->x == grid_size_1) { + const MLoop *next = ME_POLY_LOOP_NEXT(mloop, p, coord->grid_index); + *r_v2 = next->v; + } + if (coord->y == grid_size_1) { + const MLoop *prev = ME_POLY_LOOP_PREV(mloop, p, coord->grid_index); + *r_v2 = prev->v; + } +} + +SubdivCCGAdjacencyType BKE_subdiv_ccg_coarse_mesh_adjacency_info_get(const SubdivCCG *subdiv_ccg, + const SubdivCCGCoord *coord, + const MLoop *mloop, + const MPoly *mpoly, + int *r_v1, + int *r_v2) +{ + + const int grid_size_1 = subdiv_ccg->grid_size - 1; + if (is_corner_grid_coord(subdiv_ccg, coord)) { + if (coord->x == 0 && coord->y == 0) { + /* Grid corner in the center of a poly. */ + return SUBDIV_CCG_ADJACENT_NONE; + } + if (coord->x == grid_size_1 && coord->y == grid_size_1) { + /* Grid corner adjacent to a coarse mesh vertex. */ + *r_v1 = *r_v2 = mloop[coord->grid_index].v; + return SUBDIV_CCG_ADJACENT_VERTEX; + } + /* Grid corner adjacent to the middle of a coarse mesh edge. */ + adjacet_vertices_index_from_adjacent_edge(subdiv_ccg, coord, mloop, mpoly, r_v1, r_v2); + return SUBDIV_CCG_ADJACENT_EDGE; + } + + if (is_boundary_grid_coord(subdiv_ccg, coord)) { + if (!is_inner_edge_grid_coordinate(subdiv_ccg, coord)) { + /* Grid boundary adjacent to a coarse mesh edge. */ + adjacet_vertices_index_from_adjacent_edge(subdiv_ccg, coord, mloop, mpoly, r_v1, r_v2); + return SUBDIV_CCG_ADJACENT_EDGE; + } + } + return SUBDIV_CCG_ADJACENT_NONE; +} + /** \} */ diff --git a/source/blender/blenkernel/intern/undo_system.c b/source/blender/blenkernel/intern/undo_system.c index e155dedeef0..0809e8dda6d 100644 --- a/source/blender/blenkernel/intern/undo_system.c +++ b/source/blender/blenkernel/intern/undo_system.c @@ -507,9 +507,7 @@ bool BKE_undosys_step_push_with_type(UndoStack *ustack, /* Might not be final place for this to be called - probably only want to call it from some * undo handlers, not all of them? */ - if (BKE_lib_override_library_is_enabled()) { - BKE_lib_override_library_main_operations_create(G_MAIN, false); - } + BKE_lib_override_library_main_operations_create(G_MAIN, false); /* Remove all undos after (also when 'ustack->step_active == NULL'). */ while (ustack->steps.last != ustack->step_active) { diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 18859869b4e..48b920c8a05 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -494,7 +494,7 @@ static void volume_foreach_cache(ID *id, /* cache_v */ volume->runtime.grids, }; - function_callback(id, &key, (void **)&volume->runtime.grids, user_data); + function_callback(id, &key, (void **)&volume->runtime.grids, 0, user_data); } IDTypeInfo IDType_ID_VO = { diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh index ee4e9702779..c411fc50f15 100644 --- a/source/blender/blenlib/BLI_array.hh +++ b/source/blender/blenlib/BLI_array.hh @@ -74,7 +74,7 @@ class Array { Allocator allocator_; /** A placeholder buffer that will remain uninitialized until it is used. */ - AlignedBuffer<sizeof(T) * InlineBufferCapacity, alignof(T)> inline_buffer_; + TypedBuffer<T, InlineBufferCapacity> inline_buffer_; public: /** @@ -82,23 +82,29 @@ class Array { */ Array() { - data_ = this->inline_buffer(); + data_ = inline_buffer_; size_ = 0; } /** * Create a new array that contains copies of all values. */ - Array(Span<T> values) + template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr> + Array(Span<U> values, Allocator allocator = {}) : allocator_(allocator) { size_ = values.size(); data_ = this->get_buffer_for_size(values.size()); - uninitialized_copy_n(values.data(), size_, data_); + uninitialized_convert_n<U, T>(values.data(), size_, data_); } /** * Create a new array that contains copies of all values. */ + template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr> + Array(const std::initializer_list<U> &values) : Array(Span<U>(values)) + { + } + Array(const std::initializer_list<T> &values) : Array(Span<T>(values)) { } @@ -147,12 +153,8 @@ class Array { data_ = this->get_buffer_for_size(size); } - Array(const Array &other) : allocator_(other.allocator_) + Array(const Array &other) : Array(other.as_span(), other.allocator_) { - size_ = other.size(); - - data_ = this->get_buffer_for_size(other.size()); - uninitialized_copy_n(other.data(), size_, data_); } Array(Array &&other) noexcept : allocator_(other.allocator_) @@ -167,7 +169,7 @@ class Array { uninitialized_relocate_n(other.data_, size_, data_); } - other.data_ = other.inline_buffer(); + other.data_ = other.inline_buffer_; other.size_ = 0; } @@ -223,6 +225,18 @@ class Array { return MutableSpan<T>(data_, size_); } + template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr> + operator Span<U>() const + { + return Span<U>(data_, size_); + } + + template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr> + operator MutableSpan<U>() + { + return MutableSpan<U>(data_, size_); + } + Span<T> as_span() const { return *this; @@ -335,18 +349,13 @@ class Array { T *get_buffer_for_size(uint size) { if (size <= InlineBufferCapacity) { - return this->inline_buffer(); + return inline_buffer_; } else { return this->allocate(size); } } - T *inline_buffer() const - { - return (T *)inline_buffer_.ptr(); - } - T *allocate(uint size) { return (T *)allocator_.allocate(size * sizeof(T), alignof(T), AT); @@ -354,7 +363,7 @@ class Array { bool uses_inline_buffer() const { - return data_ == this->inline_buffer(); + return data_ == inline_buffer_; } }; diff --git a/source/blender/blenlib/BLI_blenlib.h b/source/blender/blenlib/BLI_blenlib.h index 6dd1abacf78..4ebef814337 100644 --- a/source/blender/blenlib/BLI_blenlib.h +++ b/source/blender/blenlib/BLI_blenlib.h @@ -28,7 +28,7 @@ * a call to a BLI function that is not prototyped here, please add a * prototype here. The library offers mathematical operations (mainly * vector and matrix calculus), an abstraction layer for file i/o, - * functions for calculating Perlin noise, scanfilling services for + * functions for calculating Perlin noise, scan-filling services for * triangles, and a system for guarded memory * allocation/deallocation. There is also a patch to make MS Windows * behave more or less Posix-compliant. diff --git a/source/blender/blenlib/BLI_color.hh b/source/blender/blenlib/BLI_color.hh index 37f74edcf4c..265013c0013 100644 --- a/source/blender/blenlib/BLI_color.hh +++ b/source/blender/blenlib/BLI_color.hh @@ -51,6 +51,25 @@ struct Color4f { stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; return stream; } + + friend bool operator==(const Color4f &a, const Color4f &b) + { + return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; + } + + friend bool operator!=(const Color4f &a, const Color4f &b) + { + return !(a == b); + } + + uint32_t hash() const + { + uint32_t x1 = *(uint32_t *)&r; + uint32_t x2 = *(uint32_t *)&g; + uint32_t x3 = *(uint32_t *)&b; + uint32_t x4 = *(uint32_t *)&a; + return (x1 * 1283591) ^ (x2 * 850177) ^ (x3 * 735391) ^ (x4 * 442319); + } }; struct Color4b { @@ -89,6 +108,22 @@ struct Color4b { stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; return stream; } + + friend bool operator==(const Color4b &a, const Color4b &b) + { + return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; + } + + friend bool operator!=(const Color4b &a, const Color4b &b) + { + return !(a == b); + } + + uint32_t hash() const + { + return ((uint32_t)r * 1283591) ^ ((uint32_t)g * 850177) ^ ((uint32_t)b * 735391) ^ + ((uint32_t)a * 442319); + } }; } // namespace blender diff --git a/source/blender/blenlib/BLI_disjoint_set.hh b/source/blender/blenlib/BLI_disjoint_set.hh new file mode 100644 index 00000000000..3b8453669aa --- /dev/null +++ b/source/blender/blenlib/BLI_disjoint_set.hh @@ -0,0 +1,105 @@ +/* + * 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. + */ + +#ifndef __BLI_DISJOINT_SET_HH__ +#define __BLI_DISJOINT_SET_HH__ + +/** \file + * \ingroup bli + * + * This implements the disjoint set data structure with path compression and union by rank. + */ + +#include "BLI_array.hh" + +namespace blender { + +class DisjointSet { + private: + Array<uint> parents_; + Array<uint> ranks_; + + public: + /** + * Create a new disjoint set with the given size. Initially, every element is in a separate set. + */ + DisjointSet(uint size) : parents_(size), ranks_(size, 0) + { + for (uint i = 0; i < size; i++) { + parents_[i] = i; + } + } + + /** + * Join the sets containing elements x and y. Nothing happens when they have been in the same set + * before. + */ + void join(uint x, uint y) + { + uint root1 = this->find_root(x); + uint root2 = this->find_root(y); + + /* x and y are in the same set already. */ + if (root1 == root2) { + return; + } + + /* Implement union by rank heuristic. */ + if (ranks_[root1] < ranks_[root2]) { + std::swap(root1, root2); + } + parents_[root2] = root1; + + if (ranks_[root1] == ranks_[root2]) { + ranks_[root1]++; + } + } + + /** + * Return true when x and y are in the same set. + */ + bool in_same_set(uint x, uint y) + { + uint root1 = this->find_root(x); + uint root2 = this->find_root(y); + return root1 == root2; + } + + /** + * Find the element that represents the set containing x currently. + */ + uint find_root(uint x) + { + /* Find root by following parents. */ + uint root = x; + while (parents_[root] != root) { + root = parents_[root]; + } + + /* Compress path. */ + while (parents_[x] != root) { + uint parent = parents_[x]; + parents_[x] = root; + x = parent; + } + + return root; + } +}; + +} // namespace blender + +#endif /* __BLI_DISJOINT_SET_HH__ */ diff --git a/source/blender/blenlib/BLI_dot_export.hh b/source/blender/blenlib/BLI_dot_export.hh index 450cb2ef58c..a7c5f1436d1 100644 --- a/source/blender/blenlib/BLI_dot_export.hh +++ b/source/blender/blenlib/BLI_dot_export.hh @@ -269,6 +269,11 @@ class NodeWithSocketsRef { Span<std::string> input_names, Span<std::string> output_names); + Node &node() + { + return *node_; + } + NodePort input(uint index) const { std::string port = "\"in" + std::to_string(index) + "\""; diff --git a/source/blender/blenlib/BLI_float2.hh b/source/blender/blenlib/BLI_float2.hh index 94da5d18ad2..5fe9d1b8ca9 100644 --- a/source/blender/blenlib/BLI_float2.hh +++ b/source/blender/blenlib/BLI_float2.hh @@ -48,6 +48,34 @@ struct float2 { return &x; } + float2 &operator+=(const float2 &other) + { + x += other.x; + y += other.y; + return *this; + } + + float2 &operator-=(const float2 &other) + { + x -= other.x; + y -= other.y; + return *this; + } + + float2 &operator*=(float factor) + { + x *= factor; + y *= factor; + return *this; + } + + float2 &operator/=(float divisor) + { + x /= divisor; + y /= divisor; + return *this; + } + friend float2 operator+(const float2 &a, const float2 &b) { return {a.x + b.x, a.y + b.y}; @@ -79,6 +107,16 @@ struct float2 { stream << "(" << v.x << ", " << v.y << ")"; return stream; } + + friend bool operator==(const float2 &a, const float2 &b) + { + return a.x == b.x && a.y == b.y; + } + + friend bool operator!=(const float2 &a, const float2 &b) + { + return !(a == b); + } }; } // namespace blender diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh index 0edee600ef6..a36cedad41d 100644 --- a/source/blender/blenlib/BLI_float3.hh +++ b/source/blender/blenlib/BLI_float3.hh @@ -128,6 +128,16 @@ struct float3 { return stream; } + friend bool operator==(const float3 &a, const float3 &b) + { + return a.x == b.x && a.y == b.y && a.z == b.z; + } + + friend bool operator!=(const float3 &a, const float3 &b) + { + return !(a == b); + } + float normalize_and_get_length() { return normalize_v3(*this); @@ -178,6 +188,14 @@ struct float3 { z = -z; } + uint32_t hash() const + { + uint32_t x1 = *(uint32_t *)&x; + uint32_t x2 = *(uint32_t *)&y; + uint32_t x3 = *(uint32_t *)&z; + return (x1 * 435109) ^ (x2 * 380867) ^ (x3 * 1059217); + } + static float dot(const float3 &a, const float3 &b) { return a.x * b.x + a.y * b.y + a.z * b.z; diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh index 1e9bd12b12b..ef83f9ffc19 100644 --- a/source/blender/blenlib/BLI_float4x4.hh +++ b/source/blender/blenlib/BLI_float4x4.hh @@ -108,6 +108,16 @@ struct float4x4 { interp_m4_m4m4(result, a.values, b.values, t); return result; } + + uint32_t hash() const + { + uint32_t h = 435109; + for (uint i = 0; i < 16; i++) { + float value = ((const float *)this)[i]; + h = h * 33 + (*(uint32_t *)&value); + } + return h; + } }; } // namespace blender diff --git a/source/blender/blenlib/BLI_ghash.h b/source/blender/blenlib/BLI_ghash.h index 141c631381b..31a9658bd7e 100644 --- a/source/blender/blenlib/BLI_ghash.h +++ b/source/blender/blenlib/BLI_ghash.h @@ -371,20 +371,6 @@ unsigned int BLI_ghashutil_uinthash_v4_murmur(const unsigned int key[4]); bool BLI_ghashutil_uinthash_v4_cmp(const void *a, const void *b); #define BLI_ghashutil_inthash_v4_cmp BLI_ghashutil_uinthash_v4_cmp -unsigned int BLI_ghashutil_uinthash_v2(const unsigned int key[2]); -#define BLI_ghashutil_inthash_v2(key) \ - (CHECK_TYPE_ANY(key, int *, const int *), BLI_ghashutil_uinthash_v2((const unsigned int *)key)) -#define BLI_ghashutil_inthash_v2_p ((GSetHashFP)BLI_ghashutil_uinthash_v2) -#define BLI_ghashutil_uinthash_v2_p ((GSetHashFP)BLI_ghashutil_uinthash_v2) -unsigned int BLI_ghashutil_uinthash_v2_murmur(const unsigned int key[2]); -#define BLI_ghashutil_inthash_v2_murmur(key) \ - (CHECK_TYPE_ANY(key, int *, const int *), \ - BLI_ghashutil_uinthash_v2_murmur((const unsigned int *)key)) -#define BLI_ghashutil_inthash_v2_p_murmur ((GSetHashFP)BLI_ghashutil_uinthash_v2_murmur) -#define BLI_ghashutil_uinthash_v2_p_murmur ((GSetHashFP)BLI_ghashutil_uinthash_v2_murmur) -bool BLI_ghashutil_uinthash_v2_cmp(const void *a, const void *b); -#define BLI_ghashutil_inthash_v2_cmp BLI_ghashutil_uinthash_v2_cmp - typedef struct GHashPair { const void *first; const void *second; diff --git a/source/blender/blenlib/BLI_hash.hh b/source/blender/blenlib/BLI_hash.hh index 49e619ff1bc..5cd4ce3c1a9 100644 --- a/source/blender/blenlib/BLI_hash.hh +++ b/source/blender/blenlib/BLI_hash.hh @@ -154,6 +154,13 @@ template<> struct DefaultHash<float> { } }; +template<> struct DefaultHash<bool> { + uint32_t operator()(bool value) const + { + return (uint32_t)(value != false) * 1298191; + } +}; + inline uint32_t hash_string(StringRef str) { uint32_t hash = 5381; diff --git a/source/blender/blenlib/BLI_hash_tables.hh b/source/blender/blenlib/BLI_hash_tables.hh index 5f9e06c5a64..aaed772071d 100644 --- a/source/blender/blenlib/BLI_hash_tables.hh +++ b/source/blender/blenlib/BLI_hash_tables.hh @@ -65,13 +65,13 @@ inline constexpr uint32_t power_of_2_max_u_constexpr(const uint32_t x) template<typename IntT> inline constexpr IntT ceil_division(const IntT x, const IntT y) { - BLI_STATIC_ASSERT(!std::is_signed<IntT>::value, ""); + BLI_STATIC_ASSERT(!std::is_signed_v<IntT>, ""); return x / y + ((x % y) != 0); } template<typename IntT> inline constexpr IntT floor_division(const IntT x, const IntT y) { - BLI_STATIC_ASSERT(!std::is_signed<IntT>::value, ""); + BLI_STATIC_ASSERT(!std::is_signed_v<IntT>, ""); return x / y; } diff --git a/source/blender/blenlib/BLI_kdopbvh.h b/source/blender/blenlib/BLI_kdopbvh.h index 70fa633eeac..9e4e30181b9 100644 --- a/source/blender/blenlib/BLI_kdopbvh.h +++ b/source/blender/blenlib/BLI_kdopbvh.h @@ -174,6 +174,8 @@ BVHTreeOverlap *BLI_bvhtree_overlap(const BVHTree *tree1, BVHTree_OverlapCallback callback, void *userdata); +int *BLI_bvhtree_intersect_plane(BVHTree *tree, float plane[4], uint *r_intersect_tot); + int BLI_bvhtree_get_len(const BVHTree *tree); int BLI_bvhtree_get_tree_type(const BVHTree *tree); float BLI_bvhtree_get_epsilon(const BVHTree *tree); diff --git a/source/blender/blenlib/BLI_linklist.h b/source/blender/blenlib/BLI_linklist.h index 06796d6592a..324da859af1 100644 --- a/source/blender/blenlib/BLI_linklist.h +++ b/source/blender/blenlib/BLI_linklist.h @@ -55,6 +55,7 @@ int BLI_linklist_count(const LinkNode *list) ATTR_WARN_UNUSED_RESULT; int BLI_linklist_index(const LinkNode *list, void *ptr) ATTR_WARN_UNUSED_RESULT; LinkNode *BLI_linklist_find(LinkNode *list, int index) ATTR_WARN_UNUSED_RESULT; +LinkNode *BLI_linklist_find_last(LinkNode *list) ATTR_WARN_UNUSED_RESULT; void BLI_linklist_reverse(LinkNode **listp) ATTR_NONNULL(1); diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh index 0a044afe8af..6bbd4ee09db 100644 --- a/source/blender/blenlib/BLI_map.hh +++ b/source/blender/blenlib/BLI_map.hh @@ -271,10 +271,6 @@ class Map { { return this->add_as(std::move(key), std::move(value)); } - - /** - * Same as `add`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey, typename ForwardValue> bool add_as(ForwardKey &&key, ForwardValue &&value) { @@ -305,10 +301,6 @@ class Map { { return this->add_overwrite_as(std::move(key), std::move(value)); } - - /** - * Same as `add_overwrite`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey, typename ForwardValue> bool add_overwrite_as(ForwardKey &&key, ForwardValue &&value) { @@ -325,10 +317,6 @@ class Map { { return this->contains_as(key); } - - /** - * Same as `contains`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey> bool contains_as(const ForwardKey &key) const { return this->contains__impl(key, hash_(key)); @@ -344,10 +332,6 @@ class Map { { return this->remove_as(key); } - - /** - * Same as `remove`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey> bool remove_as(const ForwardKey &key) { return this->remove__impl(key, hash_(key)); @@ -361,11 +345,6 @@ class Map { { this->remove_contained_as(key); } - - /** - * Same as `remove_contained`, but accepts other key types that are supported by the hash - * function. - */ template<typename ForwardKey> void remove_contained_as(const ForwardKey &key) { this->remove_contained__impl(key, hash_(key)); @@ -379,10 +358,6 @@ class Map { { return this->pop_as(key); } - - /** - * Same as `pop`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey> Value pop_as(const ForwardKey &key) { return this->pop__impl(key, hash_(key)); @@ -396,10 +371,6 @@ class Map { { return this->pop_try_as(key); } - - /** - * Same as `pop_try`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey> std::optional<Value> pop_try_as(const ForwardKey &key) { return this->pop_try__impl(key, hash_(key)); @@ -417,10 +388,6 @@ class Map { { return this->pop_default_as(key, std::move(default_value)); } - - /** - * Same as `pop_default`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey, typename ForwardValue> Value pop_default_as(const ForwardKey &key, ForwardValue &&default_value) { @@ -460,10 +427,6 @@ class Map { { return this->add_or_modify_as(std::move(key), create_value, modify_value); } - - /** - * Same as `add_or_modify`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey, typename CreateValueF, typename ModifyValueF> auto add_or_modify_as(ForwardKey &&key, const CreateValueF &create_value, @@ -487,10 +450,6 @@ class Map { { return this->lookup_ptr_as(key); } - - /** - * Same as `lookup_ptr`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey> const Value *lookup_ptr_as(const ForwardKey &key) const { return this->lookup_ptr__impl(key, hash_(key)); @@ -512,10 +471,6 @@ class Map { { return this->lookup_as(key); } - - /** - * Same as `lookup`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey> const Value &lookup_as(const ForwardKey &key) const { const Value *ptr = this->lookup_ptr_as(key); @@ -537,10 +492,6 @@ class Map { { return this->lookup_default_as(key, default_value); } - - /** - * Same as `lookup_default`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey, typename ForwardValue> Value lookup_default_as(const ForwardKey &key, ForwardValue &&default_value) const { @@ -573,11 +524,6 @@ class Map { { return this->lookup_or_add_as(std::move(key), std::move(value)); } - - /** - * Same as `lookup_or_add`, but accepts other key types that are supported by the hash - * function. - */ template<typename ForwardKey, typename ForwardValue> Value &lookup_or_add_as(ForwardKey &&key, ForwardValue &&value) { @@ -602,11 +548,6 @@ class Map { { return this->lookup_or_add_cb_as(std::move(key), create_value); } - - /** - * Same as `lookup_or_add_cb`, but accepts other key types that are supported by the hash - * function. - */ template<typename ForwardKey, typename CreateValueF> Value &lookup_or_add_cb_as(ForwardKey &&key, const CreateValueF &create_value) { @@ -625,11 +566,6 @@ class Map { { return this->lookup_or_add_default_as(std::move(key)); } - - /** - * Same as `lookup_or_add_default`, but accepts other key types that are supported by the hash - * function. - */ template<typename ForwardKey> Value &lookup_or_add_default_as(ForwardKey &&key) { return this->lookup_or_add_cb_as(std::forward<ForwardKey>(key), []() { return Value(); }); @@ -1114,7 +1050,7 @@ class Map { { using CreateReturnT = decltype(create_value(nullptr)); using ModifyReturnT = decltype(modify_value(nullptr)); - BLI_STATIC_ASSERT((std::is_same<CreateReturnT, ModifyReturnT>::value), + BLI_STATIC_ASSERT((std::is_same_v<CreateReturnT, ModifyReturnT>), "Both callbacks should return the same type."); this->ensure_can_add(); diff --git a/source/blender/blenlib/BLI_map_slots.hh b/source/blender/blenlib/BLI_map_slots.hh index c3d88205e0a..ff3ed34eb9d 100644 --- a/source/blender/blenlib/BLI_map_slots.hh +++ b/source/blender/blenlib/BLI_map_slots.hh @@ -53,8 +53,8 @@ template<typename Key, typename Value> class SimpleMapSlot { }; State state_; - AlignedBuffer<sizeof(Key), alignof(Key)> key_buffer_; - AlignedBuffer<sizeof(Value), alignof(Value)> value_buffer_; + TypedBuffer<Key> key_buffer_; + TypedBuffer<Value> value_buffer_; public: /** @@ -71,8 +71,8 @@ template<typename Key, typename Value> class SimpleMapSlot { ~SimpleMapSlot() { if (state_ == Occupied) { - this->key()->~Key(); - this->value()->~Value(); + key_buffer_.ref().~Key(); + value_buffer_.ref().~Value(); } } @@ -84,8 +84,8 @@ template<typename Key, typename Value> class SimpleMapSlot { { state_ = other.state_; if (other.state_ == Occupied) { - new ((void *)this->key()) Key(*other.key()); - new ((void *)this->value()) Value(*other.value()); + new (&key_buffer_) Key(*other.key_buffer_); + new (&value_buffer_) Value(*other.value_buffer_); } } @@ -98,8 +98,8 @@ template<typename Key, typename Value> class SimpleMapSlot { { state_ = other.state_; if (other.state_ == Occupied) { - new ((void *)this->key()) Key(std::move(*other.key())); - new ((void *)this->value()) Value(std::move(*other.value())); + new (&key_buffer_) Key(std::move(*other.key_buffer_)); + new (&value_buffer_) Value(std::move(*other.value_buffer_)); } } @@ -108,7 +108,7 @@ template<typename Key, typename Value> class SimpleMapSlot { */ Key *key() { - return (Key *)key_buffer_.ptr(); + return key_buffer_; } /** @@ -116,7 +116,7 @@ template<typename Key, typename Value> class SimpleMapSlot { */ const Key *key() const { - return (const Key *)key_buffer_.ptr(); + return key_buffer_; } /** @@ -124,7 +124,7 @@ template<typename Key, typename Value> class SimpleMapSlot { */ Value *value() { - return (Value *)value_buffer_.ptr(); + return value_buffer_; } /** @@ -132,7 +132,7 @@ template<typename Key, typename Value> class SimpleMapSlot { */ const Value *value() const { - return (const Value *)value_buffer_.ptr(); + return value_buffer_; } /** @@ -158,7 +158,7 @@ template<typename Key, typename Value> class SimpleMapSlot { template<typename Hash> uint32_t get_hash(const Hash &hash) { BLI_assert(this->is_occupied()); - return hash(*this->key()); + return hash(*key_buffer_); } /** @@ -170,10 +170,10 @@ template<typename Key, typename Value> class SimpleMapSlot { BLI_assert(!this->is_occupied()); BLI_assert(other.is_occupied()); state_ = Occupied; - new ((void *)this->key()) Key(std::move(*other.key())); - new ((void *)this->value()) Value(std::move(*other.value())); - other.key()->~Key(); - other.value()->~Value(); + new (&key_buffer_) Key(std::move(*other.key_buffer_)); + new (&value_buffer_) Value(std::move(*other.value_buffer_)); + other.key_buffer_.ref().~Key(); + other.value_buffer_.ref().~Value(); } /** @@ -184,7 +184,7 @@ template<typename Key, typename Value> class SimpleMapSlot { bool contains(const ForwardKey &key, const IsEqual &is_equal, uint32_t UNUSED(hash)) const { if (state_ == Occupied) { - return is_equal(key, *this->key()); + return is_equal(key, *key_buffer_); } return false; } @@ -198,7 +198,7 @@ template<typename Key, typename Value> class SimpleMapSlot { { BLI_assert(!this->is_occupied()); this->occupy_without_value(std::forward<ForwardKey>(key), hash); - new ((void *)this->value()) Value(std::forward<ForwardValue>(value)); + new (&value_buffer_) Value(std::forward<ForwardValue>(value)); } /** @@ -209,7 +209,7 @@ template<typename Key, typename Value> class SimpleMapSlot { { BLI_assert(!this->is_occupied()); state_ = Occupied; - new ((void *)this->key()) Key(std::forward<ForwardKey>(key)); + new (&key_buffer_) Key(std::forward<ForwardKey>(key)); } /** @@ -220,8 +220,8 @@ template<typename Key, typename Value> class SimpleMapSlot { { BLI_assert(this->is_occupied()); state_ = Removed; - this->key()->~Key(); - this->value()->~Value(); + key_buffer_.ref().~Key(); + value_buffer_.ref().~Value(); } }; @@ -236,7 +236,7 @@ template<typename Key, typename Value> class SimpleMapSlot { template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot { private: Key key_ = KeyInfo::get_empty(); - AlignedBuffer<sizeof(Value), alignof(Value)> value_buffer_; + TypedBuffer<Value> value_buffer_; public: IntrusiveMapSlot() = default; @@ -244,21 +244,21 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot ~IntrusiveMapSlot() { if (KeyInfo::is_not_empty_or_removed(key_)) { - this->value()->~Value(); + value_buffer_.ref().~Value(); } } IntrusiveMapSlot(const IntrusiveMapSlot &other) : key_(other.key_) { if (KeyInfo::is_not_empty_or_removed(key_)) { - new ((void *)this->value()) Value(*other.value()); + new (&value_buffer_) Value(*other.value_buffer_); } } IntrusiveMapSlot(IntrusiveMapSlot &&other) noexcept : key_(other.key_) { if (KeyInfo::is_not_empty_or_removed(key_)) { - new ((void *)this->value()) Value(std::move(*other.value())); + new (&value_buffer_) Value(std::move(*other.value_buffer_)); } } @@ -274,12 +274,12 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot Value *value() { - return (Value *)value_buffer_.ptr(); + return value_buffer_; } const Value *value() const { - return (const Value *)value_buffer_.ptr(); + return value_buffer_; } bool is_occupied() const @@ -295,7 +295,7 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot template<typename Hash> uint32_t get_hash(const Hash &hash) { BLI_assert(this->is_occupied()); - return hash(*this->key()); + return hash(key_); } void relocate_occupied_here(IntrusiveMapSlot &other, uint32_t UNUSED(hash)) @@ -303,9 +303,9 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot BLI_assert(!this->is_occupied()); BLI_assert(other.is_occupied()); key_ = std::move(other.key_); - new ((void *)this->value()) Value(std::move(*other.value())); + new (&value_buffer_) Value(std::move(*other.value_buffer_)); other.key_.~Key(); - other.value()->~Value(); + other.value_buffer_.ref().~Value(); } template<typename ForwardKey, typename IsEqual> @@ -321,7 +321,7 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot BLI_assert(!this->is_occupied()); BLI_assert(KeyInfo::is_not_empty_or_removed(key)); this->occupy_without_value(std::forward<ForwardKey>(key), hash); - new ((void *)this->value()) Value(std::forward<ForwardValue>(value)); + new (&value_buffer_) Value(std::forward<ForwardValue>(value)); } template<typename ForwardKey> void occupy_without_value(ForwardKey &&key, uint32_t UNUSED(hash)) @@ -335,7 +335,7 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot { BLI_assert(this->is_occupied()); KeyInfo::remove(key_); - this->value()->~Value(); + value_buffer_.ref().~Value(); } }; diff --git a/source/blender/blenlib/BLI_math_color.h b/source/blender/blenlib/BLI_math_color.h index ba95da4092e..48f8e7d31d9 100644 --- a/source/blender/blenlib/BLI_math_color.h +++ b/source/blender/blenlib/BLI_math_color.h @@ -148,8 +148,12 @@ void blackbody_temperature_to_rgb_table(float *r_table, int width, float min, fl /********* lift/gamma/gain / ASC-CDL conversion ***********/ -void lift_gamma_gain_to_asc_cdl( - float *lift, float *gamma, float *gain, float *offset, float *slope, float *power); +void lift_gamma_gain_to_asc_cdl(const float *lift, + const float *gamma, + const float *gain, + float *offset, + float *slope, + float *power); #if BLI_MATH_DO_INLINE # include "intern/math_color_inline.c" diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index 2d11797bc34..33fcd750aee 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -64,7 +64,7 @@ void swap_m3m3(float A[3][3], float B[3][3]); void swap_m4m4(float A[4][4], float B[4][4]); /* Build index shuffle matrix */ -void shuffle_m4(float R[4][4], int index[4]); +void shuffle_m4(float R[4][4], const int index[4]); /******************************** Arithmetic *********************************/ diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh index 44d25340778..b73e0e95312 100644 --- a/source/blender/blenlib/BLI_memory_utils.hh +++ b/source/blender/blenlib/BLI_memory_utils.hh @@ -19,62 +19,82 @@ /** \file * \ingroup bli + * Some of the functions below have very similar alternatives in the standard library. However, it + * is rather annoying to use those when debugging. Therefore, some more specialized and easier to + * debug functions are provided here. */ #include <memory> #include <new> +#include <type_traits> #include "BLI_utildefines.h" namespace blender { /** - * Call the default constructor on n consecutive elements. For trivially constructible types, this - * does nothing. + * Call the destructor on n consecutive values. For trivially destructible types, this does + * nothing. + * + * Exception Safety: Destructors shouldn't throw exceptions. * * Before: - * ptr: uninitialized - * After: * ptr: initialized + * After: + * ptr: uninitialized */ -template<typename T> void default_construct_n(T *ptr, uint n) +template<typename T> void destruct_n(T *ptr, uint n) { + static_assert(std::is_nothrow_destructible_v<T>, + "This should be true for all types. Destructors are noexcept by default."); + /* This is not strictly necessary, because the loop below will be optimized away anyway. It is * nice to make behavior this explicitly, though. */ - if (std::is_trivially_constructible<T>::value) { + if (std::is_trivially_destructible_v<T>) { return; } for (uint i = 0; i < n; i++) { - new ((void *)(ptr + i)) T; + ptr[i].~T(); } } /** - * Call the destructor on n consecutive values. For trivially destructible types, this does - * nothing. + * Call the default constructor on n consecutive elements. For trivially constructible types, this + * does nothing. + * + * Exception Safety: Strong. * * Before: - * ptr: initialized - * After: * ptr: uninitialized + * After: + * ptr: initialized */ -template<typename T> void destruct_n(T *ptr, uint n) +template<typename T> void default_construct_n(T *ptr, uint n) { /* This is not strictly necessary, because the loop below will be optimized away anyway. It is * nice to make behavior this explicitly, though. */ - if (std::is_trivially_destructible<T>::value) { + if (std::is_trivially_constructible_v<T>) { return; } - for (uint i = 0; i < n; i++) { - ptr[i].~T(); + uint current = 0; + try { + for (; current < n; current++) { + new ((void *)(ptr + current)) T; + } + } + catch (...) { + destruct_n(ptr, current); + throw; } } /** * Copy n values from src to dst. * + * Exception Safety: Basic. + * * Before: * src: initialized * dst: initialized @@ -92,6 +112,8 @@ template<typename T> void initialized_copy_n(const T *src, uint n, T *dst) /** * Copy n values from src to dst. * + * Exception Safety: Strong. + * * Before: * src: initialized * dst: uninitialized @@ -101,14 +123,49 @@ template<typename T> void initialized_copy_n(const T *src, uint n, T *dst) */ template<typename T> void uninitialized_copy_n(const T *src, uint n, T *dst) { - for (uint i = 0; i < n; i++) { - new ((void *)(dst + i)) T(src[i]); + uint current = 0; + try { + for (; current < n; current++) { + new ((void *)(dst + current)) T(src[current]); + } + } + catch (...) { + destruct_n(dst, current); + throw; + } +} + +/** + * Convert n values from type `From` to type `To`. + * + * Exception Safety: Strong. + * + * Before: + * src: initialized + * dst: uninitialized + * After: + * src: initialized + * dst: initialized + */ +template<typename From, typename To> void uninitialized_convert_n(const From *src, uint n, To *dst) +{ + uint current = 0; + try { + for (; current < n; current++) { + new ((void *)(dst + current)) To((To)src[current]); + } + } + catch (...) { + destruct_n(dst, current); + throw; } } /** * Move n values from src to dst. * + * Exception Safety: Basic. + * * Before: * src: initialized * dst: initialized @@ -126,6 +183,8 @@ template<typename T> void initialized_move_n(T *src, uint n, T *dst) /** * Move n values from src to dst. * + * Exception Safety: Basic. + * * Before: * src: initialized * dst: uninitialized @@ -135,8 +194,15 @@ template<typename T> void initialized_move_n(T *src, uint n, T *dst) */ template<typename T> void uninitialized_move_n(T *src, uint n, T *dst) { - for (uint i = 0; i < n; i++) { - new ((void *)(dst + i)) T(std::move(src[i])); + uint current = 0; + try { + for (; current < n; current++) { + new ((void *)(dst + current)) T(std::move(src[current])); + } + } + catch (...) { + destruct_n(dst, current); + throw; } } @@ -144,6 +210,8 @@ template<typename T> void uninitialized_move_n(T *src, uint n, T *dst) * Relocate n values from src to dst. Relocation is a move followed by destruction of the src * value. * + * Exception Safety: Basic. + * * Before: * src: initialized * dst: initialized @@ -161,6 +229,8 @@ template<typename T> void initialized_relocate_n(T *src, uint n, T *dst) * Relocate n values from src to dst. Relocation is a move followed by destruction of the src * value. * + * Exception Safety: Basic. + * * Before: * src: initialized * dst: uninitialized @@ -177,6 +247,8 @@ template<typename T> void uninitialized_relocate_n(T *src, uint n, T *dst) /** * Copy the value to n consecutive elements. * + * Exception Safety: Basic. + * * Before: * dst: initialized * After: @@ -192,6 +264,8 @@ template<typename T> void initialized_fill_n(T *dst, uint n, const T &value) /** * Copy the value to n consecutive elements. * + * Exception Safety: Strong. + * * Before: * dst: uninitialized * After: @@ -199,8 +273,15 @@ template<typename T> void initialized_fill_n(T *dst, uint n, const T &value) */ template<typename T> void uninitialized_fill_n(T *dst, uint n, const T &value) { - for (uint i = 0; i < n; i++) { - new ((void *)(dst + i)) T(value); + uint current = 0; + try { + for (; current < n; current++) { + new ((void *)(dst + current)) T(value); + } + } + catch (...) { + destruct_n(dst, current); + throw; } } @@ -218,12 +299,8 @@ template<typename T> struct DestructValueAtAddress { template<typename T> using destruct_ptr = std::unique_ptr<T, DestructValueAtAddress<T>>; /** - * An `AlignedBuffer` is simply a byte array with the given size and alignment. The buffer will + * An `AlignedBuffer` is a byte array with at least the given size and alignment. The buffer will * not be initialized by the default constructor. - * - * This can be used to reserve memory for C++ objects whose lifetime is different from the - * lifetime of the object they are embedded in. It's used by containers with small buffer - * optimization and hash table implementations. */ template<size_t Size, size_t Alignment> class alignas(Alignment) AlignedBuffer { private: @@ -231,6 +308,16 @@ template<size_t Size, size_t Alignment> class alignas(Alignment) AlignedBuffer { char buffer_[(Size > 0) ? Size : 1]; public: + operator void *() + { + return (void *)buffer_; + } + + operator const void *() const + { + return (void *)buffer_; + } + void *ptr() { return (void *)buffer_; @@ -243,12 +330,72 @@ template<size_t Size, size_t Alignment> class alignas(Alignment) AlignedBuffer { }; /** + * This can be used to reserve memory for C++ objects whose lifetime is different from the + * lifetime of the object they are embedded in. It's used by containers with small buffer + * optimization and hash table implementations. + */ +template<typename T, size_t Size = 1> class TypedBuffer { + private: + AlignedBuffer<sizeof(T) * Size, alignof(T)> buffer_; + + public: + operator T *() + { + return (T *)&buffer_; + } + + operator const T *() const + { + return (const T *)&buffer_; + } + + T &operator*() + { + return *(T *)&buffer_; + } + + const T &operator*() const + { + return *(const T *)&buffer_; + } + + T *ptr() + { + return (T *)&buffer_; + } + + const T *ptr() const + { + return (const T *)&buffer_; + } + + T &ref() + { + return *(T *)&buffer_; + } + + const T &ref() const + { + return *(const T *)&buffer_; + } +}; + +/** * This can be used by container constructors. A parameter of this type should be used to indicate * that the constructor does not construct the elements. */ class NoInitialization { }; +/** + * Helper variable that checks if a pointer type can be converted into another pointer type without + * issues. Possible issues are casting away const and casting a pointer to a child class. + * Adding const or casting to a parent class is fine. + */ +template<typename From, typename To> +inline constexpr bool is_convertible_pointer_v = + std::is_convertible_v<From, To> &&std::is_pointer_v<From> &&std::is_pointer_v<To>; + } // namespace blender #endif /* __BLI_MEMORY_UTILS_HH__ */ diff --git a/source/blender/blenlib/BLI_rand.h b/source/blender/blenlib/BLI_rand.h index ae78ea3af16..c55bbd26db5 100644 --- a/source/blender/blenlib/BLI_rand.h +++ b/source/blender/blenlib/BLI_rand.h @@ -105,12 +105,12 @@ int BLI_rng_thread_rand(RNG_THREAD_ARRAY *rngarr, int thread) ATTR_WARN_UNUSED_R /** Return the _n_th number of the given low-discrepancy sequence. */ void BLI_halton_1d(unsigned int prime, double offset, int n, double *r); -void BLI_halton_2d(unsigned int prime[2], double offset[2], int n, double *r); -void BLI_halton_3d(unsigned int prime[3], double offset[3], int n, double *r); +void BLI_halton_2d(const unsigned int prime[2], double offset[2], int n, double *r); +void BLI_halton_3d(const unsigned int prime[3], double offset[3], int n, double *r); void BLI_hammersley_1d(unsigned int n, double *r); /** Return the whole low-discrepancy sequence up to _n_. */ -void BLI_halton_2d_sequence(unsigned int prime[2], double offset[2], int n, double *r); +void BLI_halton_2d_sequence(const unsigned int prime[2], double offset[2], int n, double *r); void BLI_hammersley_2d_sequence(unsigned int n, double *r); #ifdef __cplusplus diff --git a/source/blender/blenlib/BLI_rand.hh b/source/blender/blenlib/BLI_rand.hh new file mode 100644 index 00000000000..bfc4d276165 --- /dev/null +++ b/source/blender/blenlib/BLI_rand.hh @@ -0,0 +1,109 @@ +/* + * 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 bli + */ + +#ifndef __BLI_RAND_HH__ +#define __BLI_RAND_HH__ + +#include "BLI_float2.hh" +#include "BLI_float3.hh" +#include "BLI_math.h" +#include "BLI_span.hh" +#include "BLI_utildefines.h" + +namespace blender { + +class RandomNumberGenerator { + private: + uint64_t x_; + + public: + RandomNumberGenerator(uint32_t seed = 0) + { + this->seed(seed); + } + + /** + * Set the seed for future random numbers. + */ + void seed(uint32_t seed) + { + constexpr uint64_t lowseed = 0x330E; + x_ = (((uint64_t)seed) << 16) | lowseed; + } + + void seed_random(uint32_t seed); + + uint32_t get_uint32() + { + this->step(); + return (uint32_t)(x_ >> 17); + } + + int32_t get_int32() + { + this->step(); + return (int32_t)(x_ >> 17); + } + + /** + * \return Random value (0..1), but never 1.0. + */ + double get_double() + { + return (double)this->get_int32() / 0x80000000; + } + + /** + * \return Random value (0..1), but never 1.0. + */ + float get_float() + { + return (float)this->get_int32() / 0x80000000; + } + + float2 get_unit_float2(); + float3 get_unit_float3(); + float2 get_triangle_sample(float2 v1, float2 v2, float2 v3); + void get_bytes(MutableSpan<char> r_bytes); + + /** + * Simulate getting \a n random values. + */ + void skip(uint n) + { + while (n--) { + this->step(); + } + } + + private: + void step() + { + constexpr uint64_t multiplier = 0x5DEECE66Dll; + constexpr uint64_t addend = 0xB; + constexpr uint64_t mask = 0x0000FFFFFFFFFFFFll; + + x_ = (multiplier * x_ + addend) & mask; + } +}; + +} // namespace blender + +#endif /* __BLI_RAND_HH__ */ diff --git a/source/blender/blenlib/BLI_resource_collector.hh b/source/blender/blenlib/BLI_resource_collector.hh new file mode 100644 index 00000000000..672a1269962 --- /dev/null +++ b/source/blender/blenlib/BLI_resource_collector.hh @@ -0,0 +1,145 @@ +/* + * 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. + */ + +#ifndef __BLI_RESOURCE_COLLECTOR_HH__ +#define __BLI_RESOURCE_COLLECTOR_HH__ + +/** \file + * \ingroup bli + * + * A ResourceCollector holds an arbitrary set of resources, that will be destructed and/or freed + * when the ResourceCollector is destructed. This is useful when some object has to take ownership + * of other objects, but it does not know the type of those other objects. + * + * Resources owned by the ResourceCollector will be freed in reverse order. That allows resources + * that are added later to depend on resources that have been added before. + */ + +#include "BLI_linear_allocator.hh" +#include "BLI_utility_mixins.hh" +#include "BLI_vector.hh" + +namespace blender { + +class ResourceCollector : NonCopyable, NonMovable { + private: + struct ResourceData { + void *data; + void (*free)(void *data); + const char *debug_name; + }; + + LinearAllocator<> m_allocator; + Vector<ResourceData> m_resources; + + public: + ResourceCollector() = default; + + ~ResourceCollector() + { + /* Free in reversed order. */ + for (uint i = m_resources.size(); i--;) { + ResourceData &data = m_resources[i]; + data.free(data.data); + } + } + + /** + * Pass ownership of the resource to the ResourceCollector. It will be destructed and freed when + * the collector is destructed. + */ + template<typename T> void add(std::unique_ptr<T> resource, const char *name) + { + BLI_assert(resource.get() != nullptr); + this->add( + resource.release(), + [](void *data) { + T *typed_data = reinterpret_cast<T *>(data); + delete typed_data; + }, + name); + } + + /** + * Pass ownership of the resource to the ResourceCollector. It will be destructed when the + * collector is destructed. + */ + template<typename T> void add(destruct_ptr<T> resource, const char *name) + { + BLI_assert(resource.get() != nullptr); + this->add( + resource.release(), + [](void *data) { + T *typed_data = reinterpret_cast<T *>(data); + typed_data->~T(); + }, + name); + } + + /** + * Pass ownership of some resource to the ResourceCollector. The given free function will be + * called when the collector is destructed. + */ + void add(void *userdata, void (*free)(void *), const char *name) + { + ResourceData data; + data.debug_name = name; + data.data = userdata; + data.free = free; + m_resources.append(data); + } + + /** + * Returns a reference to a linear allocator that is owned by the ResourcesCollector. Memory + * allocated through this allocator will be freed when the collector is destructed. + */ + LinearAllocator<> &linear_allocator() + { + return m_allocator; + } + + /** + * Utility method to construct an instance of type T that will be owned by the ResourceCollector. + */ + template<typename T, typename... Args> T &construct(const char *name, Args &&... args) + { + T *value = m_allocator.construct<T>(std::forward<Args>(args)...); + this->add(destruct_ptr<T>(value), name); + return *value; + } + + /** + * Print the names of all the resources that are owned by this ResourceCollector. This can be + * useful for debugging. + */ + void print(StringRef name) const + { + if (m_resources.size() == 0) { + std::cout << "\"" << name << "\" has no resources.\n"; + return; + } + else { + std::cout << "Resources for \"" << name << "\":\n"; + for (const ResourceData &data : m_resources) { + std::cout << " " << data.data << ": " << data.debug_name << '\n'; + } + } + } +}; + +} // namespace blender + +#endif /* __BLI_RESOURCE_COLLECTOR_HH__ */ diff --git a/source/blender/blenlib/BLI_set.hh b/source/blender/blenlib/BLI_set.hh index 09b2d170eea..c5096f84c80 100644 --- a/source/blender/blenlib/BLI_set.hh +++ b/source/blender/blenlib/BLI_set.hh @@ -260,10 +260,6 @@ class Set { { return this->add_as(std::move(key)); } - - /** - * Same as `add`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey> bool add_as(ForwardKey &&key) { return this->add__impl(std::forward<ForwardKey>(key), hash_(key)); @@ -303,13 +299,53 @@ class Set { { return this->contains_as(key); } + template<typename ForwardKey> bool contains_as(const ForwardKey &key) const + { + return this->contains__impl(key, hash_(key)); + } /** - * Same as `contains`, but accepts other key types that are supported by the hash function. + * Returns the key that is stored in the set that compares equal to the given key. This invokes + * undefined behavior when the key is not in the set. */ - template<typename ForwardKey> bool contains_as(const ForwardKey &key) const + const Key &lookup_key(const Key &key) const { - return this->contains__impl(key, hash_(key)); + return this->lookup_key_as(key); + } + template<typename ForwardKey> const Key &lookup_key_as(const ForwardKey &key) const + { + return this->lookup_key__impl(key, hash_(key)); + } + + /** + * Returns the key that is stored in the set that compares equal to the given key. If the key is + * not in the set, the given default value is returned instead. + */ + const Key &lookup_key_default(const Key &key, const Key &default_value) const + { + return this->lookup_key_default_as(key, default_value); + } + template<typename ForwardKey> + const Key &lookup_key_default_as(const ForwardKey &key, const Key &default_key) const + { + const Key *ptr = this->lookup_key_ptr__impl(key, hash_(key)); + if (ptr == nullptr) { + return default_key; + } + return *ptr; + } + + /** + * Returns a pointer to the key that is stored in the set that compares equal to the given key. + * If the key is not in the set, nullptr is returned instead. + */ + const Key *lookup_key_ptr(const Key &key) const + { + return this->lookup_key_ptr_as(key); + } + template<typename ForwardKey> const Key *lookup_key_ptr_as(const ForwardKey &key) const + { + return this->lookup_key_ptr__impl(key, hash_(key)); } /** @@ -321,10 +357,6 @@ class Set { { return this->remove_as(key); } - - /** - * Same as `remove`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey> bool remove_as(const ForwardKey &key) { return this->remove__impl(key, hash_(key)); @@ -337,11 +369,6 @@ class Set { { this->remove_contained_as(key); } - - /** - * Same as `remove_contained`, but accepts other key types that are supported by the hash - * function. - */ template<typename ForwardKey> void remove_contained_as(const ForwardKey &key) { this->remove_contained__impl(key, hash_(key)); @@ -596,6 +623,33 @@ class Set { SET_SLOT_PROBING_END(); } + template<typename ForwardKey> + const Key &lookup_key__impl(const ForwardKey &key, const uint32_t hash) const + { + BLI_assert(this->contains_as(key)); + + SET_SLOT_PROBING_BEGIN (hash, slot) { + if (slot.contains(key, is_equal_, hash)) { + return *slot.key(); + } + } + SET_SLOT_PROBING_END(); + } + + template<typename ForwardKey> + const Key *lookup_key_ptr__impl(const ForwardKey &key, const uint32_t hash) const + { + SET_SLOT_PROBING_BEGIN (hash, slot) { + if (slot.contains(key, is_equal_, hash)) { + return slot.key(); + } + if (slot.is_empty()) { + return nullptr; + } + } + SET_SLOT_PROBING_END(); + } + template<typename ForwardKey> void add_new__impl(ForwardKey &&key, const uint32_t hash) { BLI_assert(!this->contains_as(key)); diff --git a/source/blender/blenlib/BLI_set_slots.hh b/source/blender/blenlib/BLI_set_slots.hh index 9719f322693..d3891e78b52 100644 --- a/source/blender/blenlib/BLI_set_slots.hh +++ b/source/blender/blenlib/BLI_set_slots.hh @@ -51,7 +51,7 @@ template<typename Key> class SimpleSetSlot { }; State state_; - AlignedBuffer<sizeof(Key), alignof(Key)> buffer_; + TypedBuffer<Key> key_buffer_; public: /** @@ -68,7 +68,7 @@ template<typename Key> class SimpleSetSlot { ~SimpleSetSlot() { if (state_ == Occupied) { - this->key()->~Key(); + key_buffer_.ref().~Key(); } } @@ -80,7 +80,7 @@ template<typename Key> class SimpleSetSlot { { state_ = other.state_; if (other.state_ == Occupied) { - new ((void *)this->key()) Key(*other.key()); + new (&key_buffer_) Key(*other.key_buffer_); } } @@ -93,7 +93,7 @@ template<typename Key> class SimpleSetSlot { { state_ = other.state_; if (other.state_ == Occupied) { - new ((void *)this->key()) Key(std::move(*other.key())); + new (&key_buffer_) Key(std::move(*other.key_buffer_)); } } @@ -102,7 +102,7 @@ template<typename Key> class SimpleSetSlot { */ Key *key() { - return (Key *)buffer_.ptr(); + return key_buffer_; } /** @@ -110,7 +110,7 @@ template<typename Key> class SimpleSetSlot { */ const Key *key() const { - return (const Key *)buffer_.ptr(); + return key_buffer_; } /** @@ -136,7 +136,7 @@ template<typename Key> class SimpleSetSlot { template<typename Hash> uint32_t get_hash(const Hash &hash) const { BLI_assert(this->is_occupied()); - return hash(*this->key()); + return hash(*key_buffer_); } /** @@ -148,8 +148,8 @@ template<typename Key> class SimpleSetSlot { BLI_assert(!this->is_occupied()); BLI_assert(other.is_occupied()); state_ = Occupied; - new ((void *)this->key()) Key(std::move(*other.key())); - other.key()->~Key(); + new (&key_buffer_) Key(std::move(*other.key_buffer_)); + other.key_buffer_.ref().~Key(); } /** @@ -160,7 +160,7 @@ template<typename Key> class SimpleSetSlot { bool contains(const ForwardKey &key, const IsEqual &is_equal, uint32_t UNUSED(hash)) const { if (state_ == Occupied) { - return is_equal(key, *this->key()); + return is_equal(key, *key_buffer_); } return false; } @@ -173,7 +173,7 @@ template<typename Key> class SimpleSetSlot { { BLI_assert(!this->is_occupied()); state_ = Occupied; - new ((void *)this->key()) Key(std::forward<ForwardKey>(key)); + new (&key_buffer_) Key(std::forward<ForwardKey>(key)); } /** @@ -183,7 +183,7 @@ template<typename Key> class SimpleSetSlot { { BLI_assert(this->is_occupied()); state_ = Removed; - this->key()->~Key(); + key_buffer_.ref().~Key(); } }; @@ -201,7 +201,7 @@ template<typename Key> class HashedSetSlot { uint32_t hash_; State state_; - AlignedBuffer<sizeof(Key), alignof(Key)> buffer_; + TypedBuffer<Key> key_buffer_; public: HashedSetSlot() @@ -212,7 +212,7 @@ template<typename Key> class HashedSetSlot { ~HashedSetSlot() { if (state_ == Occupied) { - this->key()->~Key(); + key_buffer_.ref().~Key(); } } @@ -221,7 +221,7 @@ template<typename Key> class HashedSetSlot { state_ = other.state_; if (other.state_ == Occupied) { hash_ = other.hash_; - new ((void *)this->key()) Key(*other.key()); + new (&key_buffer_) Key(*other.key_buffer_); } } @@ -230,18 +230,18 @@ template<typename Key> class HashedSetSlot { state_ = other.state_; if (other.state_ == Occupied) { hash_ = other.hash_; - new ((void *)this->key()) Key(std::move(*other.key())); + new (&key_buffer_) Key(std::move(*other.key_buffer_)); } } Key *key() { - return (Key *)buffer_.ptr(); + return key_buffer_; } const Key *key() const { - return (const Key *)buffer_.ptr(); + return key_buffer_; } bool is_occupied() const @@ -266,8 +266,8 @@ template<typename Key> class HashedSetSlot { BLI_assert(other.is_occupied()); state_ = Occupied; hash_ = hash; - new ((void *)this->key()) Key(std::move(*other.key())); - other.key()->~Key(); + new (&key_buffer_) Key(std::move(*other.key_buffer_)); + key_buffer_.ref().~Key(); } template<typename ForwardKey, typename IsEqual> @@ -276,7 +276,7 @@ template<typename Key> class HashedSetSlot { /* hash_ might be uninitialized here, but that is ok. */ if (hash_ == hash) { if (state_ == Occupied) { - return is_equal(key, *this->key()); + return is_equal(key, *key_buffer_); } } return false; @@ -287,14 +287,14 @@ template<typename Key> class HashedSetSlot { BLI_assert(!this->is_occupied()); state_ = Occupied; hash_ = hash; - new ((void *)this->key()) Key(std::forward<ForwardKey>(key)); + new (&key_buffer_) Key(std::forward<ForwardKey>(key)); } void remove() { BLI_assert(this->is_occupied()); state_ = Removed; - this->key()->~Key(); + key_buffer_.ref().~Key(); } }; diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index 92d3124e01d..57ef9ce9eb6 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -100,6 +100,11 @@ template<typename T> class Span { { } + template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr> + Span(const U *start, uint size) : start_((const T *)start), size_(size) + { + } + /** * Reference an initializer_list. Note that the data in the initializer_list is only valid until * the expression containing it is fully computed. @@ -128,9 +133,8 @@ template<typename T> class Span { * Span<T *> -> Span<const T *> * Span<Derived *> -> Span<Base *> */ - template<typename U, - typename std::enable_if<std::is_convertible<U *, T>::value>::type * = nullptr> - Span(Span<U *> array) : Span((T *)array.data(), array.size()) + template<typename U, typename std::enable_if_t<is_convertible_pointer_v<U, T>> * = nullptr> + Span(Span<U> array) : start_((T *)array.data()), size_(array.size()) { } @@ -436,21 +440,6 @@ template<typename T> class MutableSpan { { } - /** - * Reference an initializer_list. Note that the data in the initializer_list is only valid until - * the expression containing it is fully computed. - * - * Do: - * call_function_with_array({1, 2, 3, 4}); - * - * Don't: - * MutableSpan<int> span = {1, 2, 3, 4}; - * call_function_with_array(span); - */ - MutableSpan(std::initializer_list<T> &list) : MutableSpan(list.begin(), list.size()) - { - } - MutableSpan(std::vector<T> &vector) : MutableSpan(vector.data(), vector.size()) { } @@ -607,14 +596,6 @@ template<typename T> class MutableSpan { }; /** - * Shorthand to make use of automatic template parameter deduction. - */ -template<typename T> Span<T> ref_c_array(const T *array, uint size) -{ - return Span<T>(array, size); -} - -/** * Utilities to check that arrays have the same size in debug builds. */ template<typename T1, typename T2> void assert_same_size(const T1 &v1, const T2 &v2) diff --git a/source/blender/blenlib/BLI_stack.hh b/source/blender/blenlib/BLI_stack.hh index 50be9357a01..a5a95186e37 100644 --- a/source/blender/blenlib/BLI_stack.hh +++ b/source/blender/blenlib/BLI_stack.hh @@ -106,7 +106,7 @@ class Stack { uint size_; /** The buffer used to implement small object optimization. */ - AlignedBuffer<sizeof(T) * InlineBufferCapacity, alignof(T)> inline_buffer_; + TypedBuffer<T, InlineBufferCapacity> inline_buffer_; /** * A chunk referencing the inline buffer. This is always the bottom-most chunk. @@ -123,14 +123,12 @@ class Stack { */ Stack(Allocator allocator = {}) : allocator_(allocator) { - T *inline_buffer = this->inline_buffer(); - inline_chunk_.below = nullptr; inline_chunk_.above = nullptr; - inline_chunk_.begin = inline_buffer; - inline_chunk_.capacity_end = inline_buffer + InlineBufferCapacity; + inline_chunk_.begin = inline_buffer_; + inline_chunk_.capacity_end = inline_buffer_ + InlineBufferCapacity; - top_ = inline_buffer; + top_ = inline_buffer_; top_chunk_ = &inline_chunk_; size_ = 0; } @@ -168,15 +166,19 @@ class Stack { Stack(Stack &&other) noexcept : Stack(other.allocator_) { - uninitialized_relocate_n( - other.inline_buffer(), std::min(other.size_, InlineBufferCapacity), this->inline_buffer()); + uninitialized_relocate_n<T>( + other.inline_buffer_, std::min(other.size_, InlineBufferCapacity), inline_buffer_); inline_chunk_.above = other.inline_chunk_.above; size_ = other.size_; + if (inline_chunk_.above != nullptr) { + inline_chunk_.above->below = &inline_chunk_; + } + if (size_ <= InlineBufferCapacity) { top_chunk_ = &inline_chunk_; - top_ = this->inline_buffer() + size_; + top_ = inline_buffer_ + size_; } else { top_chunk_ = other.top_chunk_; @@ -335,11 +337,6 @@ class Stack { } private: - T *inline_buffer() const - { - return (T *)inline_buffer_.ptr(); - } - /** * Changes top_chunk_ to point to a new chunk that is above the current one. The new chunk might * be smaller than the given size_hint. This happens when a chunk that has been allocated before diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h index 6f810144a48..920a0a8f650 100644 --- a/source/blender/blenlib/BLI_threads.h +++ b/source/blender/blenlib/BLI_threads.h @@ -107,7 +107,7 @@ typedef uint32_t SpinLock; #elif defined(__APPLE__) typedef ThreadMutex SpinLock; #elif defined(_MSC_VER) -typedef volatile int SpinLock; +typedef volatile unsigned int SpinLock; #else typedef pthread_spinlock_t SpinLock; #endif diff --git a/source/blender/blenlib/BLI_timeit.hh b/source/blender/blenlib/BLI_timeit.hh index 34c2d63cbd7..f0968587597 100644 --- a/source/blender/blenlib/BLI_timeit.hh +++ b/source/blender/blenlib/BLI_timeit.hh @@ -23,8 +23,7 @@ #include "BLI_sys_types.h" -namespace blender { -namespace Timeit { +namespace blender::timeit { using Clock = std::chrono::steady_clock; using TimePoint = Clock::time_point; @@ -54,9 +53,8 @@ class ScopedTimer { } }; -} // namespace Timeit -} // namespace blender +} // namespace blender::timeit -#define SCOPED_TIMER(name) blender::Timeit::ScopedTimer scoped_timer(name) +#define SCOPED_TIMER(name) blender::timeit::ScopedTimer scoped_timer(name) #endif /* __BLI_TIMEIT_HH__ */ diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h index 1f28f7e80c5..bc35e969e6a 100644 --- a/source/blender/blenlib/BLI_utildefines.h +++ b/source/blender/blenlib/BLI_utildefines.h @@ -627,11 +627,11 @@ extern bool BLI_memory_is_zero(const void *arr, const size_t arr_size); /** \name String Macros * \{ */ -/* Macro to convert a value to string in the preprocessor - * STRINGIFY_ARG: gives the argument as a string - * STRINGIFY_APPEND: appends any argument 'b' onto the string argument 'a', - * used by STRINGIFY because some preprocessors warn about zero arguments - * STRINGIFY: gives the argument's value as a string */ +/* Macro to convert a value to string in the pre-processor: + * - `STRINGIFY_ARG`: gives the argument as a string + * - `STRINGIFY_APPEND`: appends any argument 'b' onto the string argument 'a', + * used by `STRINGIFY` because some preprocessors warn about zero arguments + * - `STRINGIFY`: gives the argument's value as a string. */ #define STRINGIFY_ARG(x) "" #x #define STRINGIFY_APPEND(a, b) "" a #b #define STRINGIFY(x) STRINGIFY_APPEND("", x) diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index a06be65685f..1fe38464ad0 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -92,7 +92,7 @@ class Vector { Allocator allocator_; /** A placeholder buffer that will remain uninitialized until it is used. */ - AlignedBuffer<(uint)sizeof(T) * InlineBufferCapacity, (uint)alignof(T)> inline_buffer_; + TypedBuffer<T, InlineBufferCapacity> inline_buffer_; /** * Store the size of the vector explicitly in debug builds. Otherwise you'd always have to call @@ -118,9 +118,9 @@ class Vector { * Create an empty vector. * This does not do any memory allocation. */ - Vector() + Vector(Allocator allocator = {}) : allocator_(allocator) { - begin_ = this->inline_buffer(); + begin_ = inline_buffer_; end_ = begin_; capacity_end_ = begin_ + InlineBufferCapacity; UPDATE_VECTOR_SIZE(this); @@ -141,9 +141,19 @@ class Vector { */ Vector(uint size, const T &value) : Vector() { + this->resize(size, value); + } + + /** + * Create a vector from an array ref. The values in the vector are copy constructed. + */ + template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr> + Vector(Span<U> values, Allocator allocator = {}) : Vector(allocator) + { + const uint size = values.size(); this->reserve(size); this->increase_size_by_unchecked(size); - blender::uninitialized_fill_n(begin_, size, value); + uninitialized_convert_n<U, T>(values.data(), size, begin_); } /** @@ -152,24 +162,25 @@ class Vector { * This allows you to write code like: * Vector<int> vec = {3, 4, 5}; */ + template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr> + Vector(const std::initializer_list<U> &values) : Vector(Span<U>(values)) + { + } + Vector(const std::initializer_list<T> &values) : Vector(Span<T>(values)) { } - /** - * Create a vector from an array ref. The values in the vector are copy constructed. - */ - Vector(Span<T> values) : Vector() + template<typename U, + size_t N, + typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr> + Vector(const std::array<U, N> &values) : Vector(Span(values)) { - const uint size = values.size(); - this->reserve(size); - this->increase_size_by_unchecked(size); - blender::uninitialized_copy_n(values.data(), size, begin_); } /** - * Create a vector from any container. It must be possible to use the container in a range-for - * loop. + * Create a vector from any container. It must be possible to use the container in a + * range-for loop. */ template<typename ContainerT> static Vector FromContainer(const ContainerT &container) { @@ -198,9 +209,8 @@ class Vector { * Create a copy of another vector. The other vector will not be changed. If the other vector has * less than InlineBufferCapacity elements, no allocation will be made. */ - Vector(const Vector &other) : allocator_(other.allocator_) + Vector(const Vector &other) : Vector(other.as_span(), other.allocator_) { - this->init_copy_from_other_vector(other); } /** @@ -209,9 +219,8 @@ class Vector { */ template<uint OtherInlineBufferCapacity> Vector(const Vector<T, OtherInlineBufferCapacity, Allocator> &other) - : allocator_(other.allocator_) + : Vector(other.as_span(), other.allocator_) { - this->init_copy_from_other_vector(other); } /** @@ -227,7 +236,7 @@ class Vector { if (other.is_inline()) { if (size <= InlineBufferCapacity) { /* Copy between inline buffers. */ - begin_ = this->inline_buffer(); + begin_ = inline_buffer_; end_ = begin_ + size; capacity_end_ = begin_ + InlineBufferCapacity; uninitialized_relocate_n(other.begin_, size, begin_); @@ -248,7 +257,7 @@ class Vector { capacity_end_ = other.capacity_end_; } - other.begin_ = other.inline_buffer(); + other.begin_ = other.inline_buffer_; other.end_ = other.begin_; other.capacity_end_ = other.begin_ + OtherInlineBufferCapacity; UPDATE_VECTOR_SIZE(this); @@ -315,6 +324,18 @@ class Vector { return MutableSpan<T>(begin_, this->size()); } + template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr> + operator Span<U>() const + { + return Span<U>(begin_, this->size()); + } + + template<typename U, typename std::enable_if_t<is_convertible_pointer_v<T, U>> * = nullptr> + operator MutableSpan<U>() + { + return MutableSpan<U>(begin_, this->size()); + } + Span<T> as_span() const { return *this; @@ -399,7 +420,7 @@ class Vector { allocator_.deallocate(begin_); } - begin_ = this->inline_buffer(); + begin_ = inline_buffer_; end_ = begin_; capacity_end_ = begin_ + InlineBufferCapacity; UPDATE_VECTOR_SIZE(this); @@ -763,14 +784,9 @@ class Vector { } private: - T *inline_buffer() const - { - return (T *)inline_buffer_.ptr(); - } - bool is_inline() const { - return begin_ == this->inline_buffer(); + return begin_ == inline_buffer_; } void ensure_space_for_one() @@ -804,44 +820,10 @@ class Vector { end_ = begin_ + size; capacity_end_ = begin_ + new_capacity; } - - /** - * Initialize all properties, except for allocator_, which has to be initialized beforehand. - */ - template<uint OtherInlineBufferCapacity> - void init_copy_from_other_vector(const Vector<T, OtherInlineBufferCapacity, Allocator> &other) - { - allocator_ = other.allocator_; - - const uint size = other.size(); - uint capacity; - - if (size <= InlineBufferCapacity) { - begin_ = this->inline_buffer(); - capacity = InlineBufferCapacity; - } - else { - begin_ = (T *)allocator_.allocate(sizeof(T) * size, alignof(T), AT); - capacity = size; - } - - end_ = begin_ + size; - capacity_end_ = begin_ + capacity; - - uninitialized_copy_n(other.data(), size, begin_); - UPDATE_VECTOR_SIZE(this); - } }; #undef UPDATE_VECTOR_SIZE -/** - * Use when the vector is used in the local scope of a function. It has a larger inline storage by - * default to make allocations less likely. - */ -template<typename T, uint InlineBufferCapacity = 20> -using ScopedVector = Vector<T, InlineBufferCapacity, GuardedAllocator>; - } /* namespace blender */ #endif /* __BLI_VECTOR_HH__ */ diff --git a/source/blender/blenlib/BLI_vector_set.hh b/source/blender/blenlib/BLI_vector_set.hh index a39f6a97f70..dd1b17653c0 100644 --- a/source/blender/blenlib/BLI_vector_set.hh +++ b/source/blender/blenlib/BLI_vector_set.hh @@ -288,10 +288,6 @@ class VectorSet { { return this->add_as(std::move(key)); } - - /** - * Same as `add`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey> bool add_as(ForwardKey &&key) { return this->add__impl(std::forward<ForwardKey>(key), hash_(key)); @@ -320,10 +316,6 @@ class VectorSet { { return this->contains_as(key); } - - /** - * Same as `contains`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey> bool contains_as(const ForwardKey &key) const { return this->contains__impl(key, hash_(key)); @@ -339,10 +331,6 @@ class VectorSet { { return this->remove_as(key); } - - /** - * Same as `remove`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey> bool remove_as(const ForwardKey &key) { return this->remove__impl(key, hash_(key)); @@ -356,11 +344,6 @@ class VectorSet { { this->remove_contained_as(key); } - - /** - * Same as `remove_contained`, but accepts other key types that are supported by the hash - * function. - */ template<typename ForwardKey> void remove_contained_as(const ForwardKey &key) { this->remove_contained__impl(key, hash_(key)); @@ -383,10 +366,6 @@ class VectorSet { { return this->index_of_as(key); } - - /** - * Same as `index_of`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey> uint32_t index_of_as(const ForwardKey &key) const { return this->index_of__impl(key, hash_(key)); @@ -400,10 +379,6 @@ class VectorSet { { return (int32_t)this->index_of_try_as(key); } - - /** - * Same as `index_of_try`, but accepts other key types that are supported by the hash function. - */ template<typename ForwardKey> int32_t index_of_try_as(const ForwardKey &key) const { return this->index_of_try__impl(key, hash_(key)); diff --git a/source/blender/blenlib/BLI_voxel.h b/source/blender/blenlib/BLI_voxel.h index 220ab9b3705..82854c57928 100644 --- a/source/blender/blenlib/BLI_voxel.h +++ b/source/blender/blenlib/BLI_voxel.h @@ -35,10 +35,13 @@ extern "C" { (int64_t)(z) * (int64_t)(res)[0] * (int64_t)(res)[1]) /* all input coordinates must be in bounding box 0.0 - 1.0 */ -float BLI_voxel_sample_nearest(float *data, const int res[3], const float co[3]); -float BLI_voxel_sample_trilinear(float *data, const int res[3], const float co[3]); -float BLI_voxel_sample_triquadratic(float *data, const int res[3], const float co[3]); -float BLI_voxel_sample_tricubic(float *data, const int res[3], const float co[3], int bspline); +float BLI_voxel_sample_nearest(const float *data, const int res[3], const float co[3]); +float BLI_voxel_sample_trilinear(const float *data, const int res[3], const float co[3]); +float BLI_voxel_sample_triquadratic(const float *data, const int res[3], const float co[3]); +float BLI_voxel_sample_tricubic(const float *data, + const int res[3], + const float co[3], + int bspline); #ifdef __cplusplus } diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index e599b00401a..c352a9ec1ae 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -105,7 +105,7 @@ set(SRC intern/polyfill_2d.c intern/polyfill_2d_beautify.c intern/quadric.c - intern/rand.c + intern/rand.cc intern/rct.c intern/scanfill.c intern/scanfill_utils.c @@ -163,6 +163,7 @@ set(SRC BLI_convexhull_2d.h BLI_delaunay_2d.h BLI_dial_2d.h + BLI_disjoint_set.hh BLI_dlrbTree.h BLI_dot_export.hh BLI_dot_export_attribute_enums.hh @@ -232,6 +233,7 @@ set(SRC BLI_quadric.h BLI_rand.h BLI_rect.h + BLI_resource_collector.hh BLI_scanfill.h BLI_set.hh BLI_set_slots.hh diff --git a/source/blender/blenlib/intern/BLI_ghash_utils.c b/source/blender/blenlib/intern/BLI_ghash_utils.c index 83bf0373ae7..d6a4b24682f 100644 --- a/source/blender/blenlib/intern/BLI_ghash_utils.c +++ b/source/blender/blenlib/intern/BLI_ghash_utils.c @@ -86,25 +86,6 @@ bool BLI_ghashutil_uinthash_v4_cmp(const void *a, const void *b) return (memcmp(a, b, sizeof(uint[4])) != 0); } -uint BLI_ghashutil_uinthash_v2(const uint key[2]) -{ - uint hash; - hash = key[0]; - hash *= 37; - hash += key[1]; - return hash; -} - -uint BLI_ghashutil_uinthash_v2_murmur(const uint key[2]) -{ - return BLI_hash_mm2((const unsigned char *)key, sizeof(int) * 2 /* sizeof(key) */, 0); -} - -bool BLI_ghashutil_uinthash_v2_cmp(const void *a, const void *b) -{ - return (memcmp(a, b, sizeof(uint[2])) != 0); -} - uint BLI_ghashutil_uinthash(uint key) { key += ~(key << 16); diff --git a/source/blender/blenlib/intern/BLI_kdopbvh.c b/source/blender/blenlib/intern/BLI_kdopbvh.c index 0707e4c1d2a..a3f93ccc753 100644 --- a/source/blender/blenlib/intern/BLI_kdopbvh.c +++ b/source/blender/blenlib/intern/BLI_kdopbvh.c @@ -169,6 +169,12 @@ typedef struct BVHNearestProjectedData { } BVHNearestProjectedData; +typedef struct BVHIntersectPlaneData { + const BVHTree *tree; + float plane[4]; + struct BLI_Stack *intersect; /* Store indexes. */ +} BVHIntersectPlaneData; + /** \} */ /** @@ -1393,6 +1399,71 @@ BVHTreeOverlap *BLI_bvhtree_overlap( /** \} */ /* -------------------------------------------------------------------- */ +/** \name BLI_bvhtree_intersect_plane + * \{ */ + +static bool tree_intersect_plane_test(const float *bv, const float plane[4]) +{ + /* TODO(germano): Support other kdop geometries. */ + const float bb_min[3] = {bv[0], bv[2], bv[4]}; + const float bb_max[3] = {bv[1], bv[3], bv[5]}; + float bb_near[3], bb_far[3]; + aabb_get_near_far_from_plane(plane, bb_min, bb_max, bb_near, bb_far); + if ((plane_point_side_v3(plane, bb_near) > 0.0f) != + (plane_point_side_v3(plane, bb_far) > 0.0f)) { + return true; + } + + return false; +} + +static void bvhtree_intersect_plane_dfs_recursive(BVHIntersectPlaneData *__restrict data, + const BVHNode *node) +{ + if (tree_intersect_plane_test(node->bv, data->plane)) { + /* check if node is a leaf */ + if (!node->totnode) { + int *intersect = BLI_stack_push_r(data->intersect); + *intersect = node->index; + } + else { + for (int j = 0; j < data->tree->tree_type; j++) { + if (node->children[j]) { + bvhtree_intersect_plane_dfs_recursive(data, node->children[j]); + } + } + } + } +} + +int *BLI_bvhtree_intersect_plane(BVHTree *tree, float plane[4], uint *r_intersect_tot) +{ + int *intersect = NULL; + size_t total = 0; + + if (tree->totleaf) { + BVHIntersectPlaneData data; + data.tree = tree; + copy_v4_v4(data.plane, plane); + data.intersect = BLI_stack_new(sizeof(int), __func__); + + BVHNode *root = tree->nodes[tree->totleaf]; + bvhtree_intersect_plane_dfs_recursive(&data, root); + + total = BLI_stack_count(data.intersect); + if (total) { + intersect = MEM_mallocN(sizeof(int) * total, __func__); + BLI_stack_pop_n(data.intersect, intersect, (uint)total); + } + BLI_stack_free(data.intersect); + } + *r_intersect_tot = (uint)total; + return intersect; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name BLI_bvhtree_find_nearest * \{ */ diff --git a/source/blender/blenlib/intern/BLI_linklist.c b/source/blender/blenlib/intern/BLI_linklist.c index 5020e06d0b3..dc5d20ece99 100644 --- a/source/blender/blenlib/intern/BLI_linklist.c +++ b/source/blender/blenlib/intern/BLI_linklist.c @@ -74,6 +74,16 @@ LinkNode *BLI_linklist_find(LinkNode *list, int index) return NULL; } +LinkNode *BLI_linklist_find_last(LinkNode *list) +{ + if (list) { + while (list->next) { + list = list->next; + } + } + return list; +} + void BLI_linklist_reverse(LinkNode **listp) { LinkNode *rhead = NULL, *cur = *listp; diff --git a/source/blender/blenlib/intern/delaunay_2d.c b/source/blender/blenlib/intern/delaunay_2d.c index 08ccff695c1..5f663dcb2e1 100644 --- a/source/blender/blenlib/intern/delaunay_2d.c +++ b/source/blender/blenlib/intern/delaunay_2d.c @@ -952,7 +952,7 @@ static void initial_triangulation(CDT_state *cdt) } #endif - /* Now dedup according to user-defined epsilon. + /* Now de-duplicate according to user-defined epsilon. * We will merge a vertex into an earlier-indexed vertex * that is within epsilon (Euclidean distance). * Merges may cascade. So we may end up merging two things @@ -4320,7 +4320,7 @@ static void exactinit(void) */ static int fast_expansion_sum_zeroelim( - int elen, double *e, int flen, double *f, double *h) /* h cannot be e or f. */ + int elen, const double *e, int flen, const double *f, double *h) /* h cannot be e or f. */ { double Q; INEXACT double Qnew; @@ -4405,7 +4405,7 @@ static int fast_expansion_sum_zeroelim( */ static int scale_expansion_zeroelim(int elen, - double *e, + const double *e, double b, double *h) /* e and h cannot be the same. */ { @@ -4451,7 +4451,7 @@ static int scale_expansion_zeroelim(int elen, * See either version of my paper for details. */ -static double estimate(int elen, double *e) +static double estimate(int elen, const double *e) { double Q; int eindex; diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index e87350ecbb6..e61cbd318fc 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -560,7 +560,7 @@ int BLI_move(const char *file, const char *to) /* windows doesn't support moving to a directory * it has to be 'mv filename filename' and not - * 'mv filename destdir' */ + * 'mv filename destination_directory' */ BLI_strncpy(str, to, sizeof(str)); /* points 'to' to a directory ? */ diff --git a/source/blender/blenlib/intern/math_color.c b/source/blender/blenlib/intern/math_color.c index 625849c01df..651a062e3d5 100644 --- a/source/blender/blenlib/intern/math_color.c +++ b/source/blender/blenlib/intern/math_color.c @@ -503,8 +503,12 @@ int constrain_rgb(float *r, float *g, float *b) /* ********************** lift/gamma/gain / ASC-CDL conversion ********************************* */ -void lift_gamma_gain_to_asc_cdl( - float *lift, float *gamma, float *gain, float *offset, float *slope, float *power) +void lift_gamma_gain_to_asc_cdl(const float *lift, + const float *gamma, + const float *gain, + float *offset, + float *slope, + float *power) { int c; for (c = 0; c < 3; c++) { diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index 92cfd09f191..fadd7d83444 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -248,7 +248,7 @@ void swap_m4m4(float m1[4][4], float m2[4][4]) } } -void shuffle_m4(float R[4][4], int index[4]) +void shuffle_m4(float R[4][4], const int index[4]) { zero_m4(R); for (int k = 0; k < 4; k++) { diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c index 6ec7c960d6b..7f1840228e2 100644 --- a/source/blender/blenlib/intern/math_vector.c +++ b/source/blender/blenlib/intern/math_vector.c @@ -307,7 +307,7 @@ void mid_v3_v3_array(float r[3], const float (*vec_arr)[3], const uint nbr) /** * Specialized function for calculating normals. - * fastpath for: + * Fast-path for: * * \code{.c} * add_v3_v3v3(r, a, b); diff --git a/source/blender/blenlib/intern/noise.c b/source/blender/blenlib/intern/noise.c index 42b5ba28f5a..1ae1c91a3bd 100644 --- a/source/blender/blenlib/intern/noise.c +++ b/source/blender/blenlib/intern/noise.c @@ -27,7 +27,7 @@ #include "BLI_noise.h" /* local */ -static float noise3_perlin(float vec[3]); +static float noise3_perlin(const float vec[3]); // static float turbulence_perlin(const float point[3], float lofreq, float hifreq); // static float turbulencep(float noisesize, float x, float y, float z, int nr); @@ -779,7 +779,7 @@ static const float g_perlin_data_v3[512 + 2][3] = { } \ (void)0 -static float noise3_perlin(float vec[3]) +static float noise3_perlin(const float vec[3]) { const char *p = g_perlin_data_ub; const float(*g)[3] = g_perlin_data_v3; diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 2f51b66725b..d912cb8d464 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -242,7 +242,7 @@ void BLI_path_normalize(const char *relabase, char *path) /* Note: previous version of following call used an offset of 3 instead of 4, * which meant that the "/../home/me" example actually became "home/me". - * Using offset of 3 gives behavior consistent with the abovementioned + * Using offset of 3 gives behavior consistent with the aforementioned * Python routine. */ memmove(path, path + 3, strlen(path + 3) + 1); } diff --git a/source/blender/blenlib/intern/rand.c b/source/blender/blenlib/intern/rand.cc index ab7a972e010..279682ea171 100644 --- a/source/blender/blenlib/intern/rand.c +++ b/source/blender/blenlib/intern/rand.cc @@ -30,6 +30,7 @@ #include "BLI_math.h" #include "BLI_rand.h" +#include "BLI_rand.hh" #include "BLI_threads.h" /* defines BLI_INLINE */ @@ -38,29 +39,22 @@ #include "BLI_strict_flags.h" #include "BLI_sys_types.h" -#define MULTIPLIER 0x5DEECE66Dll -#define MASK 0x0000FFFFFFFFFFFFll -#define MASK_BYTES 2 - -#define ADDEND 0xB -#define LOWSEED 0x330E - -extern unsigned char BLI_noise_hash_uchar_512[512]; /* noise.c */ +extern "C" unsigned char BLI_noise_hash_uchar_512[512]; /* noise.c */ #define hash BLI_noise_hash_uchar_512 /** * Random Number Generator. */ struct RNG { - uint64_t X; + blender::RandomNumberGenerator rng; + + MEM_CXX_CLASS_ALLOC_FUNCS("RNG") }; RNG *BLI_rng_new(unsigned int seed) { - RNG *rng = MEM_mallocN(sizeof(*rng), "rng"); - - BLI_rng_seed(rng, seed); - + RNG *rng = new RNG(); + rng->rng.seed(seed); return rng; } @@ -69,26 +63,24 @@ RNG *BLI_rng_new(unsigned int seed) */ RNG *BLI_rng_new_srandom(unsigned int seed) { - RNG *rng = MEM_mallocN(sizeof(*rng), "rng"); - - BLI_rng_srandom(rng, seed); - + RNG *rng = new RNG(); + rng->rng.seed_random(seed); return rng; } RNG *BLI_rng_copy(RNG *rng) { - return MEM_dupallocN(rng); + return new RNG(*rng); } void BLI_rng_free(RNG *rng) { - MEM_freeN(rng); + delete rng; } void BLI_rng_seed(RNG *rng, unsigned int seed) { - rng->X = (((uint64_t)seed) << 16) | LOWSEED; + rng->rng.seed(seed); } /** @@ -96,67 +88,23 @@ void BLI_rng_seed(RNG *rng, unsigned int seed) */ void BLI_rng_srandom(RNG *rng, unsigned int seed) { - BLI_rng_seed(rng, seed + hash[seed & 255]); - seed = BLI_rng_get_uint(rng); - BLI_rng_seed(rng, seed + hash[seed & 255]); - seed = BLI_rng_get_uint(rng); - BLI_rng_seed(rng, seed + hash[seed & 255]); -} - -BLI_INLINE void rng_step(RNG *rng) -{ - rng->X = (MULTIPLIER * rng->X + ADDEND) & MASK; + rng->rng.seed_random(seed); } void BLI_rng_get_char_n(RNG *rng, char *bytes, size_t bytes_len) { - size_t last_len = 0; - size_t trim_len = bytes_len; - -#define RAND_STRIDE (sizeof(rng->X) - MASK_BYTES) - - if (trim_len > RAND_STRIDE) { - last_len = trim_len % RAND_STRIDE; - trim_len = trim_len - last_len; - } - else { - trim_len = 0; - last_len = bytes_len; - } - - const char *data_src = (void *)&(rng->X); - size_t i = 0; - while (i != trim_len) { - BLI_assert(i < trim_len); -#ifdef __BIG_ENDIAN__ - for (size_t j = (RAND_STRIDE + MASK_BYTES) - 1; j != MASK_BYTES - 1; j--) -#else - for (size_t j = 0; j != RAND_STRIDE; j++) -#endif - { - bytes[i++] = data_src[j]; - } - rng_step(rng); - } - if (last_len) { - for (size_t j = 0; j != last_len; j++) { - bytes[i++] = data_src[j]; - } - } - -#undef RAND_STRIDE + BLI_assert(bytes_len > UINT32_MAX); + rng->rng.get_bytes(blender::MutableSpan(bytes, (uint32_t)bytes_len)); } int BLI_rng_get_int(RNG *rng) { - rng_step(rng); - return (int)(rng->X >> 17); + return rng->rng.get_int32(); } unsigned int BLI_rng_get_uint(RNG *rng) { - rng_step(rng); - return (unsigned int)(rng->X >> 17); + return rng->rng.get_uint32(); } /** @@ -164,7 +112,7 @@ unsigned int BLI_rng_get_uint(RNG *rng) */ double BLI_rng_get_double(RNG *rng) { - return (double)BLI_rng_get_int(rng) / 0x80000000; + return rng->rng.get_double(); } /** @@ -172,29 +120,17 @@ double BLI_rng_get_double(RNG *rng) */ float BLI_rng_get_float(RNG *rng) { - return (float)BLI_rng_get_int(rng) / 0x80000000; + return rng->rng.get_float(); } void BLI_rng_get_float_unit_v2(RNG *rng, float v[2]) { - float a = (float)(M_PI * 2.0) * BLI_rng_get_float(rng); - v[0] = cosf(a); - v[1] = sinf(a); + copy_v2_v2(v, rng->rng.get_unit_float2()); } void BLI_rng_get_float_unit_v3(RNG *rng, float v[3]) { - float r; - v[2] = (2.0f * BLI_rng_get_float(rng)) - 1.0f; - if ((r = 1.0f - (v[2] * v[2])) > 0.0f) { - float a = (float)(M_PI * 2.0) * BLI_rng_get_float(rng); - r = sqrtf(r); - v[0] = r * cosf(a); - v[1] = r * sinf(a); - } - else { - v[2] = 1.0f; - } + copy_v3_v3(v, rng->rng.get_unit_float3()); } /** @@ -203,27 +139,12 @@ void BLI_rng_get_float_unit_v3(RNG *rng, float v[3]) void BLI_rng_get_tri_sample_float_v2( RNG *rng, const float v1[2], const float v2[2], const float v3[2], float r_pt[2]) { - float u = BLI_rng_get_float(rng); - float v = BLI_rng_get_float(rng); - - float side_u[2], side_v[2]; - - if ((u + v) > 1.0f) { - u = 1.0f - u; - v = 1.0f - v; - } - - sub_v2_v2v2(side_u, v2, v1); - sub_v2_v2v2(side_v, v3, v1); - - copy_v2_v2(r_pt, v1); - madd_v2_v2fl(r_pt, side_u, u); - madd_v2_v2fl(r_pt, side_v, v); + copy_v2_v2(r_pt, rng->rng.get_triangle_sample(v1, v2, v3)); } void BLI_rng_shuffle_array(RNG *rng, void *data, unsigned int elem_size_i, unsigned int elem_tot) { - const size_t elem_size = (size_t)elem_size_i; + const uint elem_size = elem_size_i; unsigned int i = elem_tot; void *temp; @@ -254,9 +175,7 @@ void BLI_rng_shuffle_array(RNG *rng, void *data, unsigned int elem_size_i, unsig */ void BLI_rng_skip(RNG *rng, int n) { - while (n--) { - rng_step(rng); - } + rng->rng.skip((uint)n); } /***/ @@ -326,7 +245,8 @@ struct RNG_THREAD_ARRAY { RNG_THREAD_ARRAY *BLI_rng_threaded_new(void) { unsigned int i; - RNG_THREAD_ARRAY *rngarr = MEM_mallocN(sizeof(RNG_THREAD_ARRAY), "random_array"); + RNG_THREAD_ARRAY *rngarr = (RNG_THREAD_ARRAY *)MEM_mallocN(sizeof(RNG_THREAD_ARRAY), + "random_array"); for (i = 0; i < BLENDER_MAX_THREADS; i++) { BLI_rng_srandom(&rngarr->rng_tab[i], (unsigned int)clock()); @@ -382,7 +302,7 @@ void BLI_halton_1d(unsigned int prime, double offset, int n, double *r) } } -void BLI_halton_2d(unsigned int prime[2], double offset[2], int n, double *r) +void BLI_halton_2d(const unsigned int prime[2], double offset[2], int n, double *r) { const double invprimes[2] = {1.0 / (double)prime[0], 1.0 / (double)prime[1]}; @@ -395,7 +315,7 @@ void BLI_halton_2d(unsigned int prime[2], double offset[2], int n, double *r) } } -void BLI_halton_3d(unsigned int prime[3], double offset[3], int n, double *r) +void BLI_halton_3d(const unsigned int prime[3], double offset[3], int n, double *r) { const double invprimes[3] = { 1.0 / (double)prime[0], 1.0 / (double)prime[1], 1.0 / (double)prime[2]}; @@ -409,7 +329,7 @@ void BLI_halton_3d(unsigned int prime[3], double offset[3], int n, double *r) } } -void BLI_halton_2d_sequence(unsigned int prime[2], double offset[2], int n, double *r) +void BLI_halton_2d_sequence(const unsigned int prime[2], double offset[2], int n, double *r) { const double invprimes[2] = {1.0 / (double)prime[0], 1.0 / (double)prime[1]}; @@ -426,7 +346,7 @@ BLI_INLINE double radical_inverse(unsigned int n) { double u = 0; - /* This reverse the bitwise representation + /* This reverse the bit-wise representation * around the decimal point. */ for (double p = 0.5; n; p *= 0.5, n >>= 1) { if (n & 1) { @@ -449,3 +369,99 @@ void BLI_hammersley_2d_sequence(unsigned int n, double *r) r[s * 2 + 1] = radical_inverse(s); } } + +namespace blender { + +/** + * Set a randomized hash of the value as seed. + */ +void RandomNumberGenerator::seed_random(uint32_t seed) +{ + this->seed(seed + hash[seed & 255]); + seed = this->get_uint32(); + this->seed(seed + hash[seed & 255]); + seed = this->get_uint32(); + this->seed(seed + hash[seed & 255]); +} + +float2 RandomNumberGenerator::get_unit_float2() +{ + float a = (float)(M_PI * 2.0) * this->get_float(); + return {cosf(a), sinf(a)}; +} + +float3 RandomNumberGenerator::get_unit_float3() +{ + float z = (2.0f * this->get_float()) - 1.0f; + float r = 1.0f - z * z; + if (r > 0.0f) { + float a = (float)(M_PI * 2.0) * this->get_float(); + r = sqrtf(r); + float x = r * cosf(a); + float y = r * sinf(a); + return {x, y, z}; + } + return {0.0f, 0.0f, 1.0f}; +} + +/** + * Generate a random point inside the given triangle. + */ +float2 RandomNumberGenerator::get_triangle_sample(float2 v1, float2 v2, float2 v3) +{ + float u = this->get_float(); + float v = this->get_float(); + + if (u + v > 1.0f) { + u = 1.0f - u; + v = 1.0f - v; + } + + float2 side_u = v2 - v1; + float2 side_v = v3 - v1; + + float2 sample = v1; + sample += side_u * u; + sample += side_v * v; + return sample; +} + +void RandomNumberGenerator::get_bytes(MutableSpan<char> r_bytes) +{ + constexpr uint mask_bytes = 2; + constexpr uint rand_stride = (uint)sizeof(x_) - mask_bytes; + + uint last_len = 0; + uint trim_len = r_bytes.size(); + + if (trim_len > rand_stride) { + last_len = trim_len % rand_stride; + trim_len = trim_len - last_len; + } + else { + trim_len = 0; + last_len = r_bytes.size(); + } + + const char *data_src = (const char *)&x_; + uint i = 0; + while (i != trim_len) { + BLI_assert(i < trim_len); +#ifdef __BIG_ENDIAN__ + for (uint j = (rand_stride + mask_bytes) - 1; j != mask_bytes - 1; j--) +#else + for (uint j = 0; j != rand_stride; j++) +#endif + { + r_bytes[i++] = data_src[j]; + } + this->step(); + } + if (last_len) { + for (uint j = 0; j != last_len; j++) { + r_bytes[i++] = data_src[j]; + } + } +} + +} // namespace blender diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c index f2217b1cd1a..d3191148c90 100644 --- a/source/blender/blenlib/intern/storage.c +++ b/source/blender/blenlib/intern/storage.c @@ -53,9 +53,9 @@ # include "BLI_string_utf8.h" # include "BLI_winstuff.h" # include "utfconv.h" +# include <ShObjIdl.h> # include <direct.h> # include <io.h> -# include <shobjidl_core.h> # include <stdbool.h> #else # include <pwd.h> @@ -275,25 +275,26 @@ eFileAttributes BLI_file_attributes(const char *path) ret |= FILE_ATTR_REPARSE_POINT; } -# endif +# else -# ifdef __linux__ UNUSED_VARS(path); /* TODO: * If Immutable set FILE_ATTR_READONLY * If Archived set FILE_ATTR_ARCHIVE */ - # endif - return ret; } #endif /* Return alias/shortcut file target. Apple version is defined in storage_apple.mm */ #ifndef __APPLE__ -bool BLI_file_alias_target(char target[FILE_MAXDIR], const char *filepath) +bool BLI_file_alias_target( + /* This parameter can only be const on non-windows platforms. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + char target[FILE_MAXDIR], + const char *filepath) { # ifdef WIN32 if (!BLI_path_extension_check(filepath, ".lnk")) { @@ -330,9 +331,7 @@ bool BLI_file_alias_target(char target[FILE_MAXDIR], const char *filepath) } return (success && target[0]); -# endif - -# ifdef __linux__ +# else UNUSED_VARS(target, filepath); /* File-based redirection not supported. */ return false; diff --git a/source/blender/blenlib/intern/system.c b/source/blender/blenlib/intern/system.c index 53db49aa59c..20edbb97561 100644 --- a/source/blender/blenlib/intern/system.c +++ b/source/blender/blenlib/intern/system.c @@ -111,7 +111,11 @@ void BLI_system_backtrace(FILE *fp) /* NOTE: The code for CPU brand string is adopted from Cycles. */ #if !defined(_WIN32) || defined(FREE_WINDOWS) -static void __cpuid(int data[4], int selector) +static void __cpuid( + /* Cannot be const, because it is modified below. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + int data[4], + int selector) { # if defined(__x86_64__) asm("cpuid" : "=a"(data[0]), "=b"(data[1]), "=c"(data[2]), "=d"(data[3]) : "a"(selector)); diff --git a/source/blender/blenlib/intern/threads.cc b/source/blender/blenlib/intern/threads.cc index 7acd9b071e1..a8333d0c696 100644 --- a/source/blender/blenlib/intern/threads.cc +++ b/source/blender/blenlib/intern/threads.cc @@ -176,9 +176,9 @@ void BLI_threadpool_init(ListBase *threadbase, void *(*do_thread)(void *), int t unsigned int level = atomic_fetch_and_add_u(&thread_levels, 1); if (level == 0) { #ifdef USE_APPLE_OMP_FIX - /* workaround for Apple gcc 4.2.1 omp vs background thread bug, - * we copy gomp thread local storage pointer to setting it again - * inside the thread that we start */ + /* Workaround for Apple gcc 4.2.1 OMP vs background thread bug, + * we copy GOMP thread local storage pointer to setting it again + * inside the thread that we start. */ thread_tls_data = pthread_getspecific(gomp_tls_key); #endif } @@ -218,8 +218,8 @@ static void *tslot_thread_start(void *tslot_p) ThreadSlot *tslot = (ThreadSlot *)tslot_p; #ifdef USE_APPLE_OMP_FIX - /* workaround for Apple gcc 4.2.1 omp vs background thread bug, - * set gomp thread local storage pointer which was copied beforehand */ + /* Workaround for Apple gcc 4.2.1 OMP vs background thread bug, + * set GOMP thread local storage pointer which was copied beforehand */ pthread_setspecific(gomp_tls_key, thread_tls_data); #endif @@ -433,7 +433,7 @@ void BLI_mutex_free(ThreadMutex *mutex) /* Spin Locks */ -#if WITH_TBB +#ifdef WITH_TBB static tbb::spin_mutex *tbb_spin_mutex_cast(SpinLock *spin) { static_assert(sizeof(SpinLock) >= sizeof(tbb::spin_mutex), @@ -500,7 +500,7 @@ void BLI_spin_end(SpinLock *spin) #elif defined(__APPLE__) BLI_mutex_end(spin); #elif defined(_MSC_VER) - BLI_mutex_unlock(spin); + /* Nothing to do, spin is a simple integer type. */ #else pthread_spin_destroy(spin); #endif diff --git a/source/blender/blenlib/intern/timeit.cc b/source/blender/blenlib/intern/timeit.cc index 7938784da67..9e07e44ca12 100644 --- a/source/blender/blenlib/intern/timeit.cc +++ b/source/blender/blenlib/intern/timeit.cc @@ -16,8 +16,7 @@ #include "BLI_timeit.hh" -namespace blender { -namespace Timeit { +namespace blender::timeit { void print_duration(Nanoseconds duration) { @@ -32,5 +31,4 @@ void print_duration(Nanoseconds duration) } } -} // namespace Timeit -} // namespace blender +} // namespace blender::timeit diff --git a/source/blender/blenlib/intern/voronoi_2d.c b/source/blender/blenlib/intern/voronoi_2d.c index 59270c58341..bc11a2c7a1c 100644 --- a/source/blender/blenlib/intern/voronoi_2d.c +++ b/source/blender/blenlib/intern/voronoi_2d.c @@ -213,7 +213,7 @@ static void voronoiParabola_setRight(VoronoiParabola *parabola, VoronoiParabola right->parent = parabola; } -static float voronoi_getY(VoronoiProcess *process, float p[2], float x) +static float voronoi_getY(VoronoiProcess *process, const float p[2], float x) { float ly = process->current_y; diff --git a/source/blender/blenlib/intern/voxel.c b/source/blender/blenlib/intern/voxel.c index c7c794957c2..2c8eb9f5a13 100644 --- a/source/blender/blenlib/intern/voxel.c +++ b/source/blender/blenlib/intern/voxel.c @@ -26,7 +26,7 @@ #include "BLI_strict_flags.h" -BLI_INLINE float D(float *data, const int res[3], int x, int y, int z) +BLI_INLINE float D(const float *data, const int res[3], int x, int y, int z) { CLAMP(x, 0, res[0] - 1); CLAMP(y, 0, res[1] - 1); @@ -36,7 +36,7 @@ BLI_INLINE float D(float *data, const int res[3], int x, int y, int z) /* *** nearest neighbor *** */ /* input coordinates must be in bounding box 0.0 - 1.0 */ -float BLI_voxel_sample_nearest(float *data, const int res[3], const float co[3]) +float BLI_voxel_sample_nearest(const float *data, const int res[3], const float co[3]) { int xi, yi, zi; @@ -65,7 +65,7 @@ BLI_INLINE int64_t _clamp(int a, int b, int c) return (a < b) ? b : ((a > c) ? c : a); } -float BLI_voxel_sample_trilinear(float *data, const int res[3], const float co[3]) +float BLI_voxel_sample_trilinear(const float *data, const int res[3], const float co[3]) { if (data) { @@ -106,7 +106,7 @@ float BLI_voxel_sample_trilinear(float *data, const int res[3], const float co[3 return 0.f; } -float BLI_voxel_sample_triquadratic(float *data, const int res[3], const float co[3]) +float BLI_voxel_sample_triquadratic(const float *data, const int res[3], const float co[3]) { if (data) { @@ -161,7 +161,10 @@ float BLI_voxel_sample_triquadratic(float *data, const int res[3], const float c return 0.f; } -float BLI_voxel_sample_tricubic(float *data, const int res[3], const float co[3], int bspline) +float BLI_voxel_sample_tricubic(const float *data, + const int res[3], + const float co[3], + int bspline) { if (data) { diff --git a/source/blender/blenloader/BLO_read_write.h b/source/blender/blenloader/BLO_read_write.h index 59116b4eefc..7c9738b67c5 100644 --- a/source/blender/blenloader/BLO_read_write.h +++ b/source/blender/blenloader/BLO_read_write.h @@ -180,7 +180,7 @@ bool BLO_write_is_undo(BlendWriter *writer); void *BLO_read_get_new_data_address(BlendDataReader *reader, const void *old_address); #define BLO_read_data_address(reader, ptr_p) \ - *(ptr_p) = BLO_read_get_new_data_address((reader), *(ptr_p)) + *((void **)ptr_p) = BLO_read_get_new_data_address((reader), *(ptr_p)) typedef void (*BlendReadListFn)(BlendDataReader *reader, void *data); void BLO_read_list_cb(BlendDataReader *reader, struct ListBase *list, BlendReadListFn callback); diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index 09e2f4bf417..7eab0651d97 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -63,8 +63,8 @@ set(SRC BLO_blend_defs.h BLO_blend_validate.h - BLO_readfile.h BLO_read_write.h + BLO_readfile.h BLO_undofile.h BLO_writefile.h intern/readfile.h diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c index e4129282a68..cb2094d050f 100644 --- a/source/blender/blenloader/intern/readblenentry.c +++ b/source/blender/blenloader/intern/readblenentry.c @@ -390,12 +390,6 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain, blo_make_old_idmap_from_main(fd, old_mainlist.first); } - /* TODO: Move handling of nodetree caches to new system as well... */ - /* makes lookup of existing images in old main */ - blo_make_image_pointer_map(fd, oldmain); - /* makes lookup of existing video clips in old main */ - blo_make_movieclip_pointer_map(fd, oldmain); - /* removed packed data from this trick - it's internal data that needs saves */ /* Store all existing ID caches pointers into a mapping, to allow restoring them into newly @@ -407,12 +401,6 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain, /* Ensure relinked caches are not freed together with their old IDs. */ blo_cache_storage_old_bmain_clear(fd, oldmain); - /* TODO: Move handling of nodetree caches to new system as well... */ - /* ensures relinked images are not freed */ - blo_end_image_pointer_map(fd, oldmain); - /* ensures relinked movie clips are not freed */ - blo_end_movieclip_pointer_map(fd, oldmain); - /* Still in-use libraries have already been moved from oldmain to new mainlist, * but oldmain itself shall *never* be 'transferred' to new mainlist! */ BLI_assert(old_mainlist.first == oldmain); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 836260c46e5..44a1a6ec44a 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1613,21 +1613,6 @@ void blo_filedata_free(FileData *fd) if (fd->globmap) { oldnewmap_free(fd->globmap); } - if (fd->imamap) { - oldnewmap_free(fd->imamap); - } - if (fd->movieclipmap) { - oldnewmap_free(fd->movieclipmap); - } - if (fd->scenemap) { - oldnewmap_free(fd->scenemap); - } - if (fd->soundmap) { - oldnewmap_free(fd->soundmap); - } - if (fd->volumemap) { - oldnewmap_free(fd->volumemap); - } if (fd->packedmap) { oldnewmap_free(fd->packedmap); } @@ -1804,51 +1789,6 @@ static void *newglobadr(FileData *fd, const void *adr) return oldnewmap_lookup_and_inc(fd->globmap, adr, true); } -/* used to restore image data after undo */ -static void *newimaadr(FileData *fd, const void *adr) -{ - if (fd->imamap && adr) { - return oldnewmap_lookup_and_inc(fd->imamap, adr, true); - } - return NULL; -} - -/* used to restore scene data after undo */ -static void *newsceadr(FileData *fd, const void *adr) -{ - if (fd->scenemap && adr) { - return oldnewmap_lookup_and_inc(fd->scenemap, adr, true); - } - return NULL; -} - -/* used to restore movie clip data after undo */ -static void *newmclipadr(FileData *fd, const void *adr) -{ - if (fd->movieclipmap && adr) { - return oldnewmap_lookup_and_inc(fd->movieclipmap, adr, true); - } - return NULL; -} - -/* used to restore sound data after undo */ -static void *newsoundadr(FileData *fd, const void *adr) -{ - if (fd->soundmap && adr) { - return oldnewmap_lookup_and_inc(fd->soundmap, adr, true); - } - return NULL; -} - -/* used to restore volume data after undo */ -static void *newvolumeadr(FileData *fd, const void *adr) -{ - if (fd->volumemap && adr) { - return oldnewmap_lookup_and_inc(fd->volumemap, adr, true); - } - return NULL; -} - /* used to restore packed data after undo */ static void *newpackedadr(FileData *fd, const void *adr) { @@ -1926,201 +1866,6 @@ void blo_clear_proxy_pointers_from_lib(Main *oldmain) } } -void blo_make_scene_pointer_map(FileData *fd, Main *oldmain) -{ - Scene *sce = oldmain->scenes.first; - - fd->scenemap = oldnewmap_new(); - - for (; sce; sce = sce->id.next) { - if (sce->eevee.light_cache_data) { - struct LightCache *light_cache = sce->eevee.light_cache_data; - oldnewmap_insert(fd->scenemap, light_cache, light_cache, 0); - } - } -} - -void blo_end_scene_pointer_map(FileData *fd, Main *oldmain) -{ - OldNew *entry = fd->scenemap->entries; - Scene *sce = oldmain->scenes.first; - int i; - - /* used entries were restored, so we put them to zero */ - for (i = 0; i < fd->scenemap->nentries; i++, entry++) { - if (entry->nr > 0) { - entry->newp = NULL; - } - } - - for (; sce; sce = sce->id.next) { - sce->eevee.light_cache_data = newsceadr(fd, sce->eevee.light_cache_data); - } -} - -void blo_make_image_pointer_map(FileData *fd, Main *oldmain) -{ - Scene *sce = oldmain->scenes.first; - fd->imamap = oldnewmap_new(); - - for (; sce; sce = sce->id.next) { - if (sce->nodetree && sce->nodetree->previews) { - bNodeInstanceHashIterator iter; - NODE_INSTANCE_HASH_ITER (iter, sce->nodetree->previews) { - bNodePreview *preview = BKE_node_instance_hash_iterator_get_value(&iter); - oldnewmap_insert(fd->imamap, preview, preview, 0); - } - } - } -} - -/* set old main image ibufs to zero if it has been restored */ -/* this works because freeing old main only happens after this call */ -void blo_end_image_pointer_map(FileData *fd, Main *oldmain) -{ - OldNew *entry = fd->imamap->entries; - Scene *sce = oldmain->scenes.first; - int i; - - /* used entries were restored, so we put them to zero */ - for (i = 0; i < fd->imamap->nentries; i++, entry++) { - if (entry->nr > 0) { - entry->newp = NULL; - } - } - - for (; sce; sce = sce->id.next) { - if (sce->nodetree && sce->nodetree->previews) { - bNodeInstanceHash *new_previews = BKE_node_instance_hash_new("node previews"); - bNodeInstanceHashIterator iter; - - /* reconstruct the preview hash, only using remaining pointers */ - NODE_INSTANCE_HASH_ITER (iter, sce->nodetree->previews) { - bNodePreview *preview = BKE_node_instance_hash_iterator_get_value(&iter); - if (preview) { - bNodePreview *new_preview = newimaadr(fd, preview); - if (new_preview) { - bNodeInstanceKey key = BKE_node_instance_hash_iterator_get_key(&iter); - BKE_node_instance_hash_insert(new_previews, key, new_preview); - } - } - } - BKE_node_instance_hash_free(sce->nodetree->previews, NULL); - sce->nodetree->previews = new_previews; - } - } -} - -void blo_make_movieclip_pointer_map(FileData *fd, Main *oldmain) -{ - Scene *sce = oldmain->scenes.first; - - fd->movieclipmap = oldnewmap_new(); - - for (; sce; sce = sce->id.next) { - if (sce->nodetree) { - bNode *node; - for (node = sce->nodetree->nodes.first; node; node = node->next) { - if (node->type == CMP_NODE_MOVIEDISTORTION) { - oldnewmap_insert(fd->movieclipmap, node->storage, node->storage, 0); - } - } - } - } -} - -/* set old main movie clips caches to zero if it has been restored */ -/* this works because freeing old main only happens after this call */ -void blo_end_movieclip_pointer_map(FileData *fd, Main *oldmain) -{ - OldNew *entry = fd->movieclipmap->entries; - Scene *sce = oldmain->scenes.first; - int i; - - /* used entries were restored, so we put them to zero */ - for (i = 0; i < fd->movieclipmap->nentries; i++, entry++) { - if (entry->nr > 0) { - entry->newp = NULL; - } - } - - for (; sce; sce = sce->id.next) { - if (sce->nodetree) { - bNode *node; - for (node = sce->nodetree->nodes.first; node; node = node->next) { - if (node->type == CMP_NODE_MOVIEDISTORTION) { - node->storage = newmclipadr(fd, node->storage); - } - } - } - } -} - -void blo_make_sound_pointer_map(FileData *fd, Main *oldmain) -{ - bSound *sound = oldmain->sounds.first; - - fd->soundmap = oldnewmap_new(); - - for (; sound; sound = sound->id.next) { - if (sound->waveform) { - oldnewmap_insert(fd->soundmap, sound->waveform, sound->waveform, 0); - } - } -} - -/* set old main sound caches to zero if it has been restored */ -/* this works because freeing old main only happens after this call */ -void blo_end_sound_pointer_map(FileData *fd, Main *oldmain) -{ - OldNew *entry = fd->soundmap->entries; - bSound *sound = oldmain->sounds.first; - int i; - - /* used entries were restored, so we put them to zero */ - for (i = 0; i < fd->soundmap->nentries; i++, entry++) { - if (entry->nr > 0) { - entry->newp = NULL; - } - } - - for (; sound; sound = sound->id.next) { - sound->waveform = newsoundadr(fd, sound->waveform); - } -} - -void blo_make_volume_pointer_map(FileData *fd, Main *oldmain) -{ - fd->volumemap = oldnewmap_new(); - - Volume *volume = oldmain->volumes.first; - for (; volume; volume = volume->id.next) { - if (volume->runtime.grids) { - oldnewmap_insert(fd->volumemap, volume->runtime.grids, volume->runtime.grids, 0); - } - } -} - -/* set old main volume caches to zero if it has been restored */ -/* this works because freeing old main only happens after this call */ -void blo_end_volume_pointer_map(FileData *fd, Main *oldmain) -{ - OldNew *entry = fd->volumemap->entries; - Volume *volume = oldmain->volumes.first; - int i; - - /* used entries were restored, so we put them to zero */ - for (i = 0; i < fd->volumemap->nentries; i++, entry++) { - if (entry->nr > 0) { - entry->newp = NULL; - } - } - - for (; volume; volume = volume->id.next) { - volume->runtime.grids = newvolumeadr(fd, volume->runtime.grids); - } -} - /* XXX disabled this feature - packed files also belong in temp saves and quit.blend, * to make restore work. */ @@ -2263,6 +2008,7 @@ typedef struct BLOCacheStorage { static void blo_cache_storage_entry_register(ID *id, const IDCacheKey *key, void **UNUSED(cache_p), + eIDTypeInfoCacheCallbackFlags UNUSED(flags), void *cache_storage_v) { BLI_assert(key->id_session_uuid == id->session_uuid); @@ -2280,12 +2026,18 @@ static void blo_cache_storage_entry_register(ID *id, static void blo_cache_storage_entry_restore_in_new(ID *UNUSED(id), const IDCacheKey *key, void **cache_p, + eIDTypeInfoCacheCallbackFlags flags, void *cache_storage_v) { BLOCacheStorage *cache_storage = cache_storage_v; if (cache_storage == NULL) { - *cache_p = NULL; + /* In non-undo case, only clear the pointer if it is a purely runtime one. + * If it may be stored in a persistent way in the .blend file, direct_link code is responsible + * to properly deal with it. */ + if ((flags & IDTYPE_CACHE_CB_FLAGS_PERSISTENT) == 0) { + *cache_p = NULL; + } return; } @@ -2302,6 +2054,7 @@ static void blo_cache_storage_entry_restore_in_new(ID *UNUSED(id), static void blo_cache_storage_entry_clear_in_old(ID *UNUSED(id), const IDCacheKey *key, void **cache_p, + eIDTypeInfoCacheCallbackFlags UNUSED(flags), void *cache_storage_v) { BLOCacheStorage *cache_storage = cache_storage_v; @@ -2341,7 +2094,7 @@ void blo_cache_storage_init(FileData *fd, Main *bmain) if (ID_IS_LINKED(id)) { continue; } - type_info->foreach_cache(id, blo_cache_storage_entry_register, fd->cache_storage); + BKE_idtype_id_foreach_cache(id, blo_cache_storage_entry_register, fd->cache_storage); } FOREACH_MAIN_LISTBASE_ID_END; } @@ -2371,7 +2124,7 @@ void blo_cache_storage_old_bmain_clear(FileData *fd, Main *bmain_old) if (ID_IS_LINKED(id)) { continue; } - type_info->foreach_cache(id, blo_cache_storage_entry_clear_in_old, fd->cache_storage); + BKE_idtype_id_foreach_cache(id, blo_cache_storage_entry_clear_in_old, fd->cache_storage); } FOREACH_MAIN_LISTBASE_ID_END; } @@ -3744,7 +3497,8 @@ static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree) } if (node->type == CMP_NODE_MOVIEDISTORTION) { - node->storage = newmclipadr(reader->fd, node->storage); + /* Do nothing, this is runtime cache and hence handled by generic code using + * `IDTypeInfo.foreach_cache` callback. */ } else { BLO_read_data_address(reader, &node->storage); @@ -3843,28 +3597,8 @@ static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree) BLO_read_data_address(reader, &link->tosock); } -#if 0 - if (ntree->previews) { - bNodeInstanceHash* new_previews = BKE_node_instance_hash_new("node previews"); - bNodeInstanceHashIterator iter; - - NODE_INSTANCE_HASH_ITER(iter, ntree->previews) { - bNodePreview* preview = BKE_node_instance_hash_iterator_get_value(&iter); - if (preview) { - bNodePreview* new_preview = newimaadr(fd, preview); - if (new_preview) { - bNodeInstanceKey key = BKE_node_instance_hash_iterator_get_key(&iter); - BKE_node_instance_hash_insert(new_previews, key, new_preview); - } - } - } - BKE_node_instance_hash_free(ntree->previews, NULL); - ntree->previews = new_previews; - } -#else - /* XXX TODO */ + /* TODO, should be dealt by new generic cache handling of IDs... */ ntree->previews = NULL; -#endif /* type verification is in lib-link */ } @@ -8952,6 +8686,7 @@ static void direct_link_simulation(BlendDataReader *reader, Simulation *simulati BLO_read_list(reader, &simulation->states); LISTBASE_FOREACH (SimulationState *, state, &simulation->states) { + BLO_read_data_address(reader, &state->name); switch ((eSimulationStateType)state->type) { case SIM_STATE_TYPE_PARTICLES: { ParticleSimulationState *particle_state = (ParticleSimulationState *)state; @@ -9234,7 +8969,8 @@ static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID * /* try to restore (when undoing) or clear ID's cache pointers. */ if (id_type->foreach_cache != NULL) { - id_type->foreach_cache(id, blo_cache_storage_entry_restore_in_new, reader.fd->cache_storage); + BKE_idtype_id_foreach_cache( + id, blo_cache_storage_entry_restore_in_new, reader.fd->cache_storage); } return success; @@ -9249,8 +8985,8 @@ static BHead *read_data_into_datamap(FileData *fd, BHead *bhead, const char *all void *data; #if 0 /* XXX DUMB DEBUGGING OPTION TO GIVE NAMES for guarded malloc errors */ - short* sp = fd->filesdna->structs[bhead->SDNAnr]; - char* tmp = malloc(100); + short *sp = fd->filesdna->structs[bhead->SDNAnr]; + char *tmp = malloc(100); allocname = fd->filesdna->types[sp[0]]; strcpy(tmp, allocname); data = read_struct(fd, bhead, tmp); diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index 57c86f7128c..f8c91c77634 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -116,11 +116,6 @@ typedef struct FileData { struct OldNewMap *datamap; struct OldNewMap *globmap; struct OldNewMap *libmap; - struct OldNewMap *imamap; - struct OldNewMap *movieclipmap; - struct OldNewMap *scenemap; - struct OldNewMap *soundmap; - struct OldNewMap *volumemap; struct OldNewMap *packedmap; struct BLOCacheStorage *cache_storage; @@ -154,16 +149,6 @@ FileData *blo_filedata_from_memfile(struct MemFile *memfile, struct ReportList *reports); void blo_clear_proxy_pointers_from_lib(struct Main *oldmain); -void blo_make_image_pointer_map(FileData *fd, struct Main *oldmain); -void blo_end_image_pointer_map(FileData *fd, struct Main *oldmain); -void blo_make_scene_pointer_map(FileData *fd, struct Main *oldmain); -void blo_end_scene_pointer_map(FileData *fd, struct Main *oldmain); -void blo_make_movieclip_pointer_map(FileData *fd, struct Main *oldmain); -void blo_end_movieclip_pointer_map(FileData *fd, struct Main *oldmain); -void blo_make_sound_pointer_map(FileData *fd, struct Main *oldmain); -void blo_end_sound_pointer_map(FileData *fd, struct Main *oldmain); -void blo_make_volume_pointer_map(FileData *fd, struct Main *oldmain); -void blo_end_volume_pointer_map(FileData *fd, struct Main *oldmain); void blo_make_packed_pointer_map(FileData *fd, struct Main *oldmain); void blo_end_packed_pointer_map(FileData *fd, struct Main *oldmain); void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd); diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 0d16b58d28f..ba92e11cc2a 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -21,6 +21,7 @@ #define DNA_DEPRECATED_ALLOW #include "BLI_listbase.h" +#include "BLI_math.h" #include "BLI_utildefines.h" #include "DNA_brush_types.h" @@ -36,6 +37,7 @@ #include "BKE_colortools.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_node.h" #include "BLO_readfile.h" #include "readfile.h" @@ -255,6 +257,28 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Initialize parameters of the new Nishita sky model. */ + if (!DNA_struct_elem_find(fd->filesdna, "NodeTexSky", "float", "sun_size")) { + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_SHADER) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == SH_NODE_TEX_SKY && node->storage) { + NodeTexSky *tex = (NodeTexSky *)node->storage; + tex->sun_disc = true; + tex->sun_size = DEG2RADF(0.545); + tex->sun_elevation = M_PI_2; + tex->sun_rotation = 0.0f; + tex->altitude = 0.0f; + tex->air_density = 1.0f; + tex->dust_density = 1.0f; + tex->ozone_density = 1.0f; + } + } + } + } + FOREACH_NODETREE_END; + } } if (!MAIN_VERSION_ATLEAST(bmain, 290, 6)) { @@ -365,5 +389,21 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + /* Initialize additional parameter of the Nishita sky model and change altitude unit. */ + if (!DNA_struct_elem_find(fd->filesdna, "NodeTexSky", "float", "sun_intensity")) { + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_SHADER) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == SH_NODE_TEX_SKY && node->storage) { + NodeTexSky *tex = (NodeTexSky *)node->storage; + tex->sun_intensity = 1.0f; + tex->altitude *= 0.001f; + } + } + } + } + FOREACH_NODETREE_END; + } } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 1217b69f1b5..7f75c0100b8 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -315,7 +315,7 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene) copy_v2_fl2(scene->safe_areas.title, 0.1f, 0.05f); copy_v2_fl2(scene->safe_areas.action, 0.035f, 0.035f); - /* Change default cubemap quality. */ + /* Change default cube-map quality. */ scene->eevee.gi_filter_quality = 3.0f; /* Enable Soft Shadows by default. */ diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 2ce8abff3c5..4e2b4fef9a0 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -3832,6 +3832,7 @@ static void write_simulation(BlendWriter *writer, Simulation *simulation, const } LISTBASE_FOREACH (SimulationState *, state, &simulation->states) { + BLO_write_string(writer, state->name); switch ((eSimulationStateType)state->type) { case SIM_STATE_TYPE_PARTICLES: { ParticleSimulationState *particle_state = (ParticleSimulationState *)state; diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 7a389c63abd..0943b4a9c2a 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -118,6 +118,8 @@ set(SRC intern/bmesh_query.c intern/bmesh_query.h intern/bmesh_query_inline.h + intern/bmesh_query_uv.c + intern/bmesh_query_uv.h intern/bmesh_structure.c intern/bmesh_structure.h intern/bmesh_structure_inline.h @@ -151,6 +153,8 @@ set(SRC tools/bmesh_path.h tools/bmesh_path_region.c tools/bmesh_path_region.h + tools/bmesh_path_uv.c + tools/bmesh_path_uv.h tools/bmesh_region_match.c tools/bmesh_region_match.h tools/bmesh_separate.c diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h index c0791e6fdbc..c1f4b9daf27 100644 --- a/source/blender/bmesh/bmesh.h +++ b/source/blender/bmesh/bmesh.h @@ -223,6 +223,7 @@ extern "C" { #include "intern/bmesh_polygon.h" #include "intern/bmesh_polygon_edgenet.h" #include "intern/bmesh_query.h" +#include "intern/bmesh_query_uv.h" #include "intern/bmesh_walkers.h" #include "intern/bmesh_inline.h" diff --git a/source/blender/bmesh/bmesh_tools.h b/source/blender/bmesh/bmesh_tools.h index d0e91d033fb..a0dc6abf009 100644 --- a/source/blender/bmesh/bmesh_tools.h +++ b/source/blender/bmesh/bmesh_tools.h @@ -36,6 +36,7 @@ extern "C" { #include "tools/bmesh_edgesplit.h" #include "tools/bmesh_path.h" #include "tools/bmesh_path_region.h" +#include "tools/bmesh_path_uv.h" #include "tools/bmesh_region_match.h" #include "tools/bmesh_separate.h" #include "tools/bmesh_triangulate.h" diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index 35fb698eac1..4e9775bcfa7 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -101,6 +101,7 @@ BMVert *BM_vert_create(BMesh *bm, /* may add to middle of the pool */ bm->elem_index_dirty |= BM_VERT; bm->elem_table_dirty |= BM_VERT; + bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; bm->totvert++; @@ -190,6 +191,7 @@ BMEdge *BM_edge_create( /* may add to middle of the pool */ bm->elem_index_dirty |= BM_EDGE; bm->elem_table_dirty |= BM_EDGE; + bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; bm->totedge++; @@ -259,6 +261,7 @@ static BMLoop *bm_loop_create(BMesh *bm, /* may add to middle of the pool */ bm->elem_index_dirty |= BM_LOOP; + bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; bm->totloop++; @@ -402,6 +405,7 @@ BLI_INLINE BMFace *bm_face_create__internal(BMesh *bm) /* may add to middle of the pool */ bm->elem_index_dirty |= BM_FACE; bm->elem_table_dirty |= BM_FACE; + bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; bm->totface++; @@ -748,6 +752,7 @@ static void bm_kill_only_vert(BMesh *bm, BMVert *v) bm->totvert--; bm->elem_index_dirty |= BM_VERT; bm->elem_table_dirty |= BM_VERT; + bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; BM_select_history_remove(bm, v); @@ -770,6 +775,7 @@ static void bm_kill_only_edge(BMesh *bm, BMEdge *e) bm->totedge--; bm->elem_index_dirty |= BM_EDGE; bm->elem_table_dirty |= BM_EDGE; + bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; BM_select_history_remove(bm, (BMElem *)e); @@ -796,6 +802,7 @@ static void bm_kill_only_face(BMesh *bm, BMFace *f) bm->totface--; bm->elem_index_dirty |= BM_FACE; bm->elem_table_dirty |= BM_FACE; + bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; BM_select_history_remove(bm, (BMElem *)f); @@ -817,6 +824,8 @@ static void bm_kill_only_loop(BMesh *bm, BMLoop *l) { bm->totloop--; bm->elem_index_dirty |= BM_LOOP; + bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; + if (l->head.data) { CustomData_bmesh_free_block(&bm->ldata, &l->head.data); } diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c index ffe84f93679..8eed3141c7f 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -2377,6 +2377,27 @@ BMFace *BM_face_at_index_find(BMesh *bm, const int index) return BLI_mempool_findelem(bm->fpool, index); } +BMLoop *BM_loop_at_index_find(BMesh *bm, const int index) +{ + BMIter iter; + BMFace *f; + int i = index; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (i < f->len) { + BMLoop *l_first, *l_iter; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (i == 0) { + return l_iter; + } + i -= 1; + } while ((l_iter = l_iter->next) != l_first); + } + i -= f->len; + } + return NULL; +} + /** * Use lookup table when available, else use slower find functions. * diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index 4ba0d948499..0d665f1d391 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -118,6 +118,7 @@ BLI_INLINE BMFace *BM_face_at_index(BMesh *bm, const int index) BMVert *BM_vert_at_index_find(BMesh *bm, const int index); BMEdge *BM_edge_at_index_find(BMesh *bm, const int index); BMFace *BM_face_at_index_find(BMesh *bm, const int index); +BMLoop *BM_loop_at_index_find(BMesh *bm, const int index); BMVert *BM_vert_at_index_find_or_table(BMesh *bm, const int index); BMEdge *BM_edge_at_index_find_or_table(BMesh *bm, const int index); diff --git a/source/blender/bmesh/intern/bmesh_query.c b/source/blender/bmesh/intern/bmesh_query.c index fe67abf6aa5..80634618f6f 100644 --- a/source/blender/bmesh/intern/bmesh_query.c +++ b/source/blender/bmesh/intern/bmesh_query.c @@ -158,6 +158,37 @@ BMLoop *BM_loop_other_vert_loop(BMLoop *l, BMVert *v) } /** + * Return the other loop that uses this edge. + * + * In this case the loop defines the vertex, + * the edge passed in defines the direction to step. + * + * <pre> + * +----------+ <-- Return the face-loop of this vertex. + * | | + * | e | <-- This edge defines the direction. + * | | + * +----------+ <-- This loop defines the face and vertex.. + * l + * </pre> + * + */ +BMLoop *BM_loop_other_vert_loop_by_edge(BMLoop *l, BMEdge *e) +{ + BLI_assert(BM_vert_in_edge(e, l->v)); + if (l->e == e) { + return l->next; + } + else if (l->prev->e == e) { + return l->prev; + } + else { + BLI_assert(0); + return NULL; + } +} + +/** * Check if verts share a face. */ bool BM_vert_pair_share_face_check(BMVert *v_a, BMVert *v_b) diff --git a/source/blender/bmesh/intern/bmesh_query.h b/source/blender/bmesh/intern/bmesh_query.h index 7e07059d4d8..0d95efb778f 100644 --- a/source/blender/bmesh/intern/bmesh_query.h +++ b/source/blender/bmesh/intern/bmesh_query.h @@ -48,6 +48,8 @@ BMLoop *BM_face_other_edge_loop(BMFace *f, BMEdge *e, BMVert *v) ATTR_WARN_UNUSE BMLoop *BM_loop_other_edge_loop(BMLoop *l, BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BMLoop *BM_face_other_vert_loop(BMFace *f, BMVert *v_prev, BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +BMLoop *BM_loop_other_vert_loop_by_edge(BMLoop *l, BMEdge *e) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(); BMLoop *BM_loop_other_vert_loop(BMLoop *l, BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BMLoop *BM_vert_step_fan_loop(BMLoop *l, BMEdge **e_step) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); @@ -171,7 +173,6 @@ float BM_edge_calc_face_angle_with_imat3(const BMEdge *e, float BM_edge_calc_face_angle_signed(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); void BM_edge_calc_face_tangent(const BMEdge *e, const BMLoop *e_loop, float r_tangent[3]) ATTR_NONNULL(); - float BM_vert_calc_edge_angle(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); float BM_vert_calc_edge_angle_ex(const BMVert *v, const float fallback) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); diff --git a/source/blender/bmesh/intern/bmesh_query_uv.c b/source/blender/bmesh/intern/bmesh_query_uv.c new file mode 100644 index 00000000000..b9ea51f0c4d --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_query_uv.c @@ -0,0 +1,169 @@ +/* + * 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 bmesh + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_alloca.h" +#include "BLI_linklist.h" +#include "BLI_math.h" +#include "BLI_utildefines_stack.h" + +#include "BKE_customdata.h" + +#include "DNA_meshdata_types.h" + +#include "bmesh.h" +#include "intern/bmesh_private.h" + +static void uv_aspect(const BMLoop *l, + const float aspect[2], + const int cd_loop_uv_offset, + float r_uv[2]) +{ + const float *uv = ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset))->uv; + r_uv[0] = uv[0] * aspect[0]; + r_uv[1] = uv[1] * aspect[1]; +} + +/** + * Typically we avoid hiding arguments, + * make this an exception since it reads poorly with so many repeated arguments. + */ +#define UV_ASPECT(l, r_uv) uv_aspect(l, aspect, cd_loop_uv_offset, r_uv) + +/** + * Computes the UV center of a face, using the mean average weighted by edge length. + * + * See #BM_face_calc_center_median_weighted for matching spatial functionality. + * + * \param aspect: Calculate the center scaling by these values, and finally dividing. + * Since correct weighting depends on having the correct aspect. + */ +void BM_face_uv_calc_center_median_weighted(const BMFace *f, + const float aspect[2], + const int cd_loop_uv_offset, + float r_cent[2]) +{ + const BMLoop *l_iter; + const BMLoop *l_first; + float totw = 0.0f; + float w_prev; + + zero_v2(r_cent); + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + + float uv_prev[2], uv_curr[2]; + UV_ASPECT(l_iter->prev, uv_prev); + UV_ASPECT(l_iter, uv_curr); + w_prev = len_v2v2(uv_prev, uv_curr); + do { + float uv_next[2]; + UV_ASPECT(l_iter->next, uv_next); + const float w_curr = len_v2v2(uv_curr, uv_next); + const float w = (w_curr + w_prev); + madd_v2_v2fl(r_cent, uv_curr, w); + totw += w; + w_prev = w_curr; + copy_v2_v2(uv_curr, uv_next); + } while ((l_iter = l_iter->next) != l_first); + + if (totw != 0.0f) { + mul_v2_fl(r_cent, 1.0f / (float)totw); + } + /* Reverse aspect. */ + r_cent[0] /= aspect[0]; + r_cent[1] /= aspect[1]; +} + +#undef UV_ASPECT + +/** + * Calculate the UV cross product (use the sign to check the winding). + */ +float BM_face_uv_calc_cross(const BMFace *f, const int cd_loop_uv_offset) +{ + float(*uvs)[2] = BLI_array_alloca(uvs, f->len); + const BMLoop *l_iter; + const BMLoop *l_first; + int i = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); + copy_v2_v2(uvs[i++], luv->uv); + } while ((l_iter = l_iter->next) != l_first); + return cross_poly_v2(uvs, f->len); +} + +/** + * Check if two loops that share an edge also have the same UV coordinates. + */ +bool BM_loop_uv_share_edge_check(BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset) +{ + BLI_assert(l_a->e == l_b->e); + MLoopUV *luv_a_curr = BM_ELEM_CD_GET_VOID_P(l_a, cd_loop_uv_offset); + MLoopUV *luv_a_next = BM_ELEM_CD_GET_VOID_P(l_a->next, cd_loop_uv_offset); + MLoopUV *luv_b_curr = BM_ELEM_CD_GET_VOID_P(l_b, cd_loop_uv_offset); + MLoopUV *luv_b_next = BM_ELEM_CD_GET_VOID_P(l_b->next, cd_loop_uv_offset); + if (l_a->v != l_b->v) { + SWAP(MLoopUV *, luv_b_curr, luv_b_next); + } + return (equals_v2v2(luv_a_curr->uv, luv_b_curr->uv) && + equals_v2v2(luv_a_next->uv, luv_b_next->uv)); +} + +/** + * Check if two loops that share a vertex also have the same UV coordinates. + */ +bool BM_loop_uv_share_vert_check(BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset) +{ + BLI_assert(l_a->v == l_b->v); + const MLoopUV *luv_a = BM_ELEM_CD_GET_VOID_P(l_a, cd_loop_uv_offset); + const MLoopUV *luv_b = BM_ELEM_CD_GET_VOID_P(l_b, cd_loop_uv_offset); + if (!equals_v2v2(luv_a->uv, luv_b->uv)) { + return false; + } + return true; +} + +/** + * Check if two loops that share a vertex also have the same UV coordinates. + */ +bool BM_edge_uv_share_vert_check(BMEdge *e, BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset) +{ + BLI_assert(l_a->v == l_b->v); + if (!BM_loop_uv_share_vert_check(l_a, l_b, cd_loop_uv_offset)) { + return false; + } + + /* No need for NULL checks, these will always succeed. */ + const BMLoop *l_other_a = BM_loop_other_vert_loop_by_edge(l_a, e); + const BMLoop *l_other_b = BM_loop_other_vert_loop_by_edge(l_b, e); + + { + const MLoopUV *luv_other_a = BM_ELEM_CD_GET_VOID_P(l_other_a, cd_loop_uv_offset); + const MLoopUV *luv_other_b = BM_ELEM_CD_GET_VOID_P(l_other_b, cd_loop_uv_offset); + if (!equals_v2v2(luv_other_a->uv, luv_other_b->uv)) { + return false; + } + } + + return true; +} diff --git a/source/blender/bmesh/intern/bmesh_query_uv.h b/source/blender/bmesh/intern/bmesh_query_uv.h new file mode 100644 index 00000000000..293715a8c69 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_query_uv.h @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#ifndef __BMESH_QUERY_UV_H__ +#define __BMESH_QUERY_UV_H__ + +/** \file + * \ingroup bmesh + */ + +float BM_loop_uv_calc_edge_length_squared(const BMLoop *l, + const int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(); +float BM_loop_uv_calc_edge_length(const BMLoop *l, + const int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(); + +void BM_face_uv_calc_center_median_weighted(const BMFace *f, + const float aspect[2], + const int cd_loop_uv_offset, + float r_cent[2]) ATTR_NONNULL(); + +float BM_face_uv_calc_cross(const BMFace *f, const int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(); + +bool BM_loop_uv_share_edge_check(BMLoop *l_a, + BMLoop *l_b, + const int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(); + +bool BM_edge_uv_share_vert_check(BMEdge *e, BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset) + ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); + +bool BM_loop_uv_share_vert_check(BMLoop *l_a, + BMLoop *l_b, + const int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(); + +#endif /* __BMESH_QUERY_UV_H__ */ diff --git a/source/blender/bmesh/operators/bmo_create.c b/source/blender/bmesh/operators/bmo_create.c index c6813a864a8..0789000969e 100644 --- a/source/blender/bmesh/operators/bmo_create.c +++ b/source/blender/bmesh/operators/bmo_create.c @@ -280,7 +280,7 @@ void bmo_contextual_create_exec(BMesh *bm, BMOperator *op) * last resort when all else fails. */ if (totv > 2) { - /* TODO, some of these vertes may be connected by edges, + /* TODO, some of these vertices may be connected by edges, * this connectivity could be used rather than treating * them as a bunch of isolated verts. */ diff --git a/source/blender/bmesh/operators/bmo_extrude.c b/source/blender/bmesh/operators/bmo_extrude.c index 3c63f4a60d6..eee31969971 100644 --- a/source/blender/bmesh/operators/bmo_extrude.c +++ b/source/blender/bmesh/operators/bmo_extrude.c @@ -572,6 +572,7 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) f = BM_face_create_verts(bm, f_verts, 4, NULL, BM_CREATE_NOP, true); #endif + bm_extrude_copy_face_loop_attributes(bm, f); if (join_face) { BMVert *v1 = e->v1; BMVert *v2 = e->v2; @@ -583,11 +584,11 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) BMO_elem_flag_enable(bm, v2, EXT_TAG); dissolve_verts[dissolve_verts_len++] = v2; } + /* Tag the edges that can collapse. */ + BMO_elem_flag_enable(bm, f_edges[0], EXT_TAG); + BMO_elem_flag_enable(bm, f_edges[1], EXT_TAG); bmesh_kernel_join_face_kill_edge(bm, join_face, f, e); } - else { - bm_extrude_copy_face_loop_attributes(bm, f); - } } /* link isolated vert */ @@ -613,7 +614,7 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) BMEdge *e_other = BM_DISK_EDGE_NEXT(e, v); if ((e_other == e) || (BM_DISK_EDGE_NEXT(e_other, v) == e)) { /* Lose edge or BMVert is edge pair. */ - BM_edge_collapse(bm, e, v, true, false); + BM_edge_collapse(bm, BMO_elem_flag_test(bm, e, EXT_TAG) ? e : e_other, v, true, false); } else { BLI_assert(!BM_vert_is_edge_pair(v)); diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index b109dc3199a..626d58f75f8 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -1353,8 +1353,9 @@ static void offset_meet(EdgeHalf *e1, /** * Calculate the meeting point between e1 and e2 (one of which should have zero offsets), - * where e1 precedes e2 in CCW order around their common vertex v (viewed from normal side). - * If r_angle is provided, return the angle between e and emeet in *r_angle. + * where \a e1 precedes \a e2 in CCW order around their common vertex \a v + * (viewed from normal side). + * If \a r_angle is provided, return the angle between \a e and \a meetco in `*r_angle`. * If the angle is 0, or it is 180 degrees or larger, there will be no meeting point; * return false in that case, else true. */ diff --git a/source/blender/bmesh/tools/bmesh_intersect_edges.c b/source/blender/bmesh/tools/bmesh_intersect_edges.c index 99a5a9edb57..52231033fd3 100644 --- a/source/blender/bmesh/tools/bmesh_intersect_edges.c +++ b/source/blender/bmesh/tools/bmesh_intersect_edges.c @@ -833,15 +833,37 @@ bool BM_mesh_intersect_edges( } if (pair_array) { + BMVert *v_key, *v_val; pair_iter = &pair_array[0]; for (i = 0; i < pair_len; i++, pair_iter++) { BLI_assert((*pair_iter)[0].elem->head.htype == BM_VERT); BLI_assert((*pair_iter)[1].elem->head.htype == BM_VERT); BLI_assert((*pair_iter)[0].elem != (*pair_iter)[1].elem); - BMVert *v_key, *v_val; v_key = (*pair_iter)[0].vert; v_val = (*pair_iter)[1].vert; BLI_ghash_insert(r_targetmap, v_key, v_val); + } + + /** + * The weld_verts operator works best when all keys in the same group of + * collapsed vertices point to the same vertex. + * That is, if the pairs of vertices are: + * [1, 2], [2, 3] and [3, 4], + * They are better adjusted to: + * [1, 4], [2, 4] and [3, 4]. + */ + pair_iter = &pair_array[0]; + for (i = 0; i < pair_len; i++, pair_iter++) { + v_key = (*pair_iter)[0].vert; + v_val = (*pair_iter)[1].vert; + BMVert *v_target; + while ((v_target = BLI_ghash_lookup(r_targetmap, v_val))) { + v_val = v_target; + } + if (v_val != (*pair_iter)[1].vert) { + BMVert **v_val_p = (BMVert **)BLI_ghash_lookup_p(r_targetmap, v_key); + *v_val_p = (*pair_iter)[1].vert = v_val; + } if (split_faces) { /* The vertex index indicates its position in the pair_array flat. */ BM_elem_index_set(v_key, i * 2); diff --git a/source/blender/bmesh/tools/bmesh_path.c b/source/blender/bmesh/tools/bmesh_path.c index 713a68969e5..cb75f47acf3 100644 --- a/source/blender/bmesh/tools/bmesh_path.c +++ b/source/blender/bmesh/tools/bmesh_path.c @@ -18,6 +18,8 @@ * \ingroup bmesh * * Find a path between 2 elements. + * + * \note All 3 functions are similar, changes to one most likely apply to another. */ #include "MEM_guardedalloc.h" @@ -29,8 +31,11 @@ #include "bmesh.h" #include "bmesh_path.h" /* own include */ +#define COST_INIT_MAX FLT_MAX + /* -------------------------------------------------------------------- */ -/* Generic Helpers */ +/** \name Generic Helpers + * \{ */ /** * Use skip options when we want to start measuring from a boundary. @@ -40,16 +45,15 @@ static float step_cost_3_v3_ex( { float d1[3], d2[3]; - /* The cost is based on the simple sum of the length of the two edgees... */ + /* The cost is based on the simple sum of the length of the two edges. */ sub_v3_v3v3(d1, v2, v1); sub_v3_v3v3(d2, v3, v2); const float cost_12 = normalize_v3(d1); const float cost_23 = normalize_v3(d2); const float cost = ((skip_12 ? 0.0f : cost_12) + (skip_23 ? 0.0f : cost_23)); - /* but is biased to give higher values to sharp turns, so that it will take - * paths with fewer "turns" when selecting between equal-weighted paths between - * the two edges */ + /* But is biased to give higher values to sharp turns, so that it will take paths with + * fewer "turns" when selecting between equal-weighted paths between the two edges. */ return cost * (1.0f + 0.5f * (2.0f - sqrtf(fabsf(dot_v3v3(d1, d2))))); } @@ -58,8 +62,11 @@ static float step_cost_3_v3(const float v1[3], const float v2[3], const float v3 return step_cost_3_v3_ex(v1, v2, v3, false, false); } +/** \} */ + /* -------------------------------------------------------------------- */ -/* BM_mesh_calc_path_vert */ +/** \name BM_mesh_calc_path_vert + * \{ */ static void verttag_add_adjacent(HeapSimple *heap, BMVert *v_a, @@ -72,11 +79,11 @@ static void verttag_add_adjacent(HeapSimple *heap, { BMIter eiter; BMEdge *e; - /* loop over faces of face, but do so by first looping over loops */ + /* Loop over faces of face, but do so by first looping over loops. */ BM_ITER_ELEM (e, &eiter, v_a, BM_EDGES_OF_VERT) { BMVert *v_b = BM_edge_other_vert(e, v_a); if (!BM_elem_flag_test(v_b, BM_ELEM_TAG)) { - /* we know 'v_b' is not visited, check it out! */ + /* We know 'v_b' is not visited, check it out! */ const int v_b_index = BM_elem_index_get(v_b); const float cost_cut = params->use_topology_distance ? 1.0f : len_v3v3(v_a->co, v_b->co); const float cost_new = cost[v_a_index] + cost_cut; @@ -93,15 +100,15 @@ static void verttag_add_adjacent(HeapSimple *heap, if (params->use_step_face) { BMIter liter; BMLoop *l; - /* loop over faces of face, but do so by first looping over loops */ + /* Loop over faces of face, but do so by first looping over loops. */ BM_ITER_ELEM (l, &liter, v_a, BM_LOOPS_OF_VERT) { if (l->f->len > 3) { - /* skip loops on adjacent edges */ + /* Skip loops on adjacent edges. */ BMLoop *l_iter = l->next->next; do { BMVert *v_b = l_iter->v; if (!BM_elem_flag_test(v_b, BM_ELEM_TAG)) { - /* we know 'v_b' is not visited, check it out! */ + /* We know 'v_b' is not visited, check it out! */ const int v_b_index = BM_elem_index_get(v_b); const float cost_cut = params->use_topology_distance ? 1.0f : len_v3v3(v_a->co, v_b->co); @@ -127,7 +134,7 @@ LinkNode *BM_mesh_calc_path_vert(BMesh *bm, void *user_data) { LinkNode *path = NULL; - /* BM_ELEM_TAG flag is used to store visited edges */ + /* #BM_ELEM_TAG flag is used to store visited edges. */ BMVert *v; BMIter viter; HeapSimple *heap; @@ -135,7 +142,7 @@ LinkNode *BM_mesh_calc_path_vert(BMesh *bm, BMVert **verts_prev; int i, totvert; - /* note, would pass BM_EDGE except we are looping over all faces anyway */ + /* Note, would pass #BM_EDGE except we are looping over all faces anyway. */ // BM_mesh_elem_index_ensure(bm, BM_VERT /* | BM_EDGE */); // NOT NEEDED FOR FACETAG BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) { @@ -144,25 +151,25 @@ LinkNode *BM_mesh_calc_path_vert(BMesh *bm, } bm->elem_index_dirty &= ~BM_VERT; - /* alloc */ + /* Allocate. */ totvert = bm->totvert; verts_prev = MEM_callocN(sizeof(*verts_prev) * totvert, __func__); cost = MEM_mallocN(sizeof(*cost) * totvert, __func__); - copy_vn_fl(cost, totvert, 1e20f); + copy_vn_fl(cost, totvert, COST_INIT_MAX); /* * Arrays are now filled as follows: * - * As the search continues, verts_prev[n] will be the previous verts on the shortest - * path found so far to face n. BM_ELEM_TAG is used to tag elements we have visited, - * cost[n] will contain the length of the shortest + * As the search continues, `verts_prev[n]` will be the previous verts on the shortest + * path found so far to face `n`. #BM_ELEM_TAG is used to tag elements we have visited, + * `cost[n]` will contain the length of the shortest * path to face n found so far, Finally, heap is a priority heap which is built on the - * the same data as the cost array, but inverted: it is a worklist of faces prioritized + * the same data as the cost array, but inverted: it is a work-list of faces prioritized * by the shortest path found so far to the face. */ - /* regular dijkstra shortest path, but over faces instead of vertices */ + /* Regular dijkstra shortest path, but over faces instead of vertices. */ heap = BLI_heapsimple_new(); BLI_heapsimple_insert(heap, 0.0f, v_src); cost[BM_elem_index_get(v_src)] = 0.0f; @@ -193,8 +200,11 @@ LinkNode *BM_mesh_calc_path_vert(BMesh *bm, return path; } +/** \} */ + /* -------------------------------------------------------------------- */ -/* BM_mesh_calc_path_edge */ +/** \name BM_mesh_calc_path_edge + * \{ */ static float edgetag_cut_cost_vert(BMEdge *e_a, BMEdge *e_b, BMVert *v) { @@ -223,8 +233,8 @@ static void edgetag_add_adjacent(HeapSimple *heap, { const int e_a_index = BM_elem_index_get(e_a); - /* unlike vert/face, stepping faces disables scanning connected edges - * and only steps over faces (selecting a ring of edges instead of a loop) */ + /* Unlike vert/face, stepping faces disables scanning connected edges + * and only steps over faces (selecting a ring of edges instead of a loop). */ if (params->use_step_face == false || e_a->l == NULL) { BMIter viter; BMVert *v; @@ -234,14 +244,14 @@ static void edgetag_add_adjacent(HeapSimple *heap, BM_ITER_ELEM (v, &viter, e_a, BM_VERTS_OF_EDGE) { - /* don't walk over previous vertex */ + /* Don't walk over previous vertex. */ if ((edges_prev[e_a_index]) && (BM_vert_in_edge(edges_prev[e_a_index], v))) { continue; } BM_ITER_ELEM (e_b, &eiter, v, BM_EDGES_OF_VERT) { if (!BM_elem_flag_test(e_b, BM_ELEM_TAG)) { - /* we know 'e_b' is not visited, check it out! */ + /* We know 'e_b' is not visited, check it out! */ const int e_b_index = BM_elem_index_get(e_b); const float cost_cut = params->use_topology_distance ? 1.0f : @@ -267,7 +277,7 @@ static void edgetag_add_adjacent(HeapSimple *heap, l_cycle_iter = l_iter->next; l_cycle_end = l_iter; - /* good, but we need to allow this otherwise paths may fail to connect at all */ + /* Good, but we need to allow this otherwise paths may fail to connect at all. */ #if 0 if (l_iter->f->len > 3) { l_cycle_iter = l_cycle_iter->next; @@ -278,7 +288,7 @@ static void edgetag_add_adjacent(HeapSimple *heap, do { BMEdge *e_b = l_cycle_iter->e; if (!BM_elem_flag_test(e_b, BM_ELEM_TAG)) { - /* we know 'e_b' is not visited, check it out! */ + /* We know 'e_b' is not visited, check it out! */ const int e_b_index = BM_elem_index_get(e_b); const float cost_cut = params->use_topology_distance ? 1.0f : @@ -304,7 +314,7 @@ LinkNode *BM_mesh_calc_path_edge(BMesh *bm, void *user_data) { LinkNode *path = NULL; - /* BM_ELEM_TAG flag is used to store visited edges */ + /* #BM_ELEM_TAG flag is used to store visited edges. */ BMEdge *e; BMIter eiter; HeapSimple *heap; @@ -312,7 +322,7 @@ LinkNode *BM_mesh_calc_path_edge(BMesh *bm, BMEdge **edges_prev; int i, totedge; - /* note, would pass BM_EDGE except we are looping over all edges anyway */ + /* Note, would pass #BM_EDGE except we are looping over all edges anyway. */ BM_mesh_elem_index_ensure(bm, BM_VERT /* | BM_EDGE */); BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) { @@ -321,25 +331,25 @@ LinkNode *BM_mesh_calc_path_edge(BMesh *bm, } bm->elem_index_dirty &= ~BM_EDGE; - /* alloc */ + /* Allocate. */ totedge = bm->totedge; - edges_prev = MEM_callocN(sizeof(*edges_prev) * totedge, "SeamPathPrevious"); - cost = MEM_mallocN(sizeof(*cost) * totedge, "SeamPathCost"); + edges_prev = MEM_callocN(sizeof(*edges_prev) * totedge, __func__); + cost = MEM_mallocN(sizeof(*cost) * totedge, __func__); - copy_vn_fl(cost, totedge, 1e20f); + copy_vn_fl(cost, totedge, COST_INIT_MAX); /* * Arrays are now filled as follows: * - * As the search continues, prevedge[n] will be the previous edge on the shortest - * path found so far to edge n. BM_ELEM_TAG is used to tag elements we have visited, - * cost[n] will contain the length of the shortest + * As the search continues, `edges_prev[n]` will be the previous edge on the shortest + * path found so far to edge `n`. #BM_ELEM_TAG is used to tag elements we have visited, + * `cost[n]` will contain the length of the shortest * path to edge n found so far, Finally, heap is a priority heap which is built on the - * the same data as the cost array, but inverted: it is a worklist of edges prioritized + * the same data as the cost array, but inverted: it is a work-list of edges prioritized * by the shortest path found so far to the edge. */ - /* regular dijkstra shortest path, but over edges instead of vertices */ + /* Regular dijkstra shortest path, but over edges instead of vertices. */ heap = BLI_heapsimple_new(); BLI_heapsimple_insert(heap, 0.0f, e_src); cost[BM_elem_index_get(e_src)] = 0.0f; @@ -370,8 +380,11 @@ LinkNode *BM_mesh_calc_path_edge(BMesh *bm, return path; } +/** \} */ + /* -------------------------------------------------------------------- */ -/* BM_mesh_calc_path_face */ +/** \name BM_mesh_calc_path_face + * \{ */ static float facetag_cut_cost_edge(BMFace *f_a, BMFace *f_b, @@ -387,15 +400,15 @@ static float facetag_cut_cost_edge(BMFace *f_a, #if 0 mid_v3_v3v3(e_cent, e->v1->co, e->v2->co); #else - /* for triangle fans it gives better results to pick a point on the edge */ + /* For triangle fans it gives better results to pick a point on the edge. */ { - float ix_e[3], ix_f[3], f; + float ix_e[3], ix_f[3]; isect_line_line_v3(e->v1->co, e->v2->co, f_a_cent, f_b_cent, ix_e, ix_f); - f = line_point_factor_v3(ix_e, e->v1->co, e->v2->co); - if (f < 0.0f) { + const float factor = line_point_factor_v3(ix_e, e->v1->co, e->v2->co); + if (factor < 0.0f) { copy_v3_v3(e_cent, e->v1->co); } - else if (f > 1.0f) { + else if (factor > 1.0f) { copy_v3_v3(e_cent, e->v2->co); } else { @@ -432,7 +445,7 @@ static void facetag_add_adjacent(HeapSimple *heap, { const int f_a_index = BM_elem_index_get(f_a); - /* loop over faces of face, but do so by first looping over loops */ + /* Loop over faces of face, but do so by first looping over loops. */ { BMIter liter; BMLoop *l_a; @@ -444,7 +457,7 @@ static void facetag_add_adjacent(HeapSimple *heap, do { BMFace *f_b = l_iter->f; if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) { - /* we know 'f_b' is not visited, check it out! */ + /* We know 'f_b' is not visited, check it out! */ const int f_b_index = BM_elem_index_get(f_b); const float cost_cut = params->use_topology_distance ? 1.0f : @@ -472,7 +485,7 @@ static void facetag_add_adjacent(HeapSimple *heap, if ((l_a != l_b) && !BM_loop_share_edge_check(l_a, l_b)) { BMFace *f_b = l_b->f; if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) { - /* we know 'f_b' is not visited, check it out! */ + /* We know 'f_b' is not visited, check it out! */ const int f_b_index = BM_elem_index_get(f_b); const float cost_cut = params->use_topology_distance ? 1.0f : @@ -499,7 +512,7 @@ LinkNode *BM_mesh_calc_path_face(BMesh *bm, void *user_data) { LinkNode *path = NULL; - /* BM_ELEM_TAG flag is used to store visited edges */ + /* #BM_ELEM_TAG flag is used to store visited edges. */ BMFace *f; BMIter fiter; HeapSimple *heap; @@ -510,7 +523,7 @@ LinkNode *BM_mesh_calc_path_face(BMesh *bm, /* Start measuring face path at the face edges, ignoring their centers. */ const void *const f_endpoints[2] = {f_src, f_dst}; - /* note, would pass BM_EDGE except we are looping over all faces anyway */ + /* Note, would pass #BM_EDGE except we are looping over all faces anyway. */ // BM_mesh_elem_index_ensure(bm, BM_VERT /* | BM_EDGE */); // NOT NEEDED FOR FACETAG BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, i) { @@ -519,25 +532,25 @@ LinkNode *BM_mesh_calc_path_face(BMesh *bm, } bm->elem_index_dirty &= ~BM_FACE; - /* alloc */ + /* Allocate. */ totface = bm->totface; faces_prev = MEM_callocN(sizeof(*faces_prev) * totface, __func__); cost = MEM_mallocN(sizeof(*cost) * totface, __func__); - copy_vn_fl(cost, totface, 1e20f); + copy_vn_fl(cost, totface, COST_INIT_MAX); /* * Arrays are now filled as follows: * - * As the search continues, faces_prev[n] will be the previous face on the shortest - * path found so far to face n. BM_ELEM_TAG is used to tag elements we have visited, - * cost[n] will contain the length of the shortest + * As the search continues, `faces_prev[n]` will be the previous face on the shortest + * path found so far to face `n`. #BM_ELEM_TAG is used to tag elements we have visited, + * `cost[n]` will contain the length of the shortest * path to face n found so far, Finally, heap is a priority heap which is built on the - * the same data as the cost array, but inverted: it is a worklist of faces prioritized + * the same data as the cost array, but inverted: it is a work-list of faces prioritized * by the shortest path found so far to the face. */ - /* regular dijkstra shortest path, but over faces instead of vertices */ + /* Regular dijkstra shortest path, but over faces instead of vertices. */ heap = BLI_heapsimple_new(); BLI_heapsimple_insert(heap, 0.0f, f_src); cost[BM_elem_index_get(f_src)] = 0.0f; @@ -567,3 +580,4 @@ LinkNode *BM_mesh_calc_path_face(BMesh *bm, return path; } +/** \} */ diff --git a/source/blender/bmesh/tools/bmesh_path_uv.c b/source/blender/bmesh/tools/bmesh_path_uv.c new file mode 100644 index 00000000000..57a70645187 --- /dev/null +++ b/source/blender/bmesh/tools/bmesh_path_uv.c @@ -0,0 +1,433 @@ +/* + * 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 bmesh + * + * Find a path between 2 elements in UV space. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_heap_simple.h" +#include "BLI_linklist.h" +#include "BLI_math.h" + +#include "DNA_meshdata_types.h" + +#include "bmesh.h" +#include "bmesh_path_uv.h" /* own include */ +#include "intern/bmesh_query.h" +#include "intern/bmesh_query_uv.h" + +#define COST_INIT_MAX FLT_MAX + +/* -------------------------------------------------------------------- */ +/** \name Generic Helpers + * \{ */ + +/** + * Use skip options when we want to start measuring from a boundary. + * + * See #step_cost_3_v3_ex in bmesh_path.c which follows the same logic. + */ +static float step_cost_3_v2_ex( + const float v1[2], const float v2[2], const float v3[2], bool skip_12, bool skip_23) +{ + float d1[2], d2[2]; + + /* The cost is based on the simple sum of the length of the two edges. */ + sub_v2_v2v2(d1, v2, v1); + sub_v2_v2v2(d2, v3, v2); + const float cost_12 = normalize_v2(d1); + const float cost_23 = normalize_v2(d2); + const float cost = ((skip_12 ? 0.0f : cost_12) + (skip_23 ? 0.0f : cost_23)); + + /* But is biased to give higher values to sharp turns, so that it will take paths with + * fewer "turns" when selecting between equal-weighted paths between the two edges. */ + return cost * (1.0f + 0.5f * (2.0f - sqrtf(fabsf(dot_v2v2(d1, d2))))); +} + +static float UNUSED_FUNCTION(step_cost_3_v2)(const float v1[2], + const float v2[2], + const float v3[2]) +{ + return step_cost_3_v2_ex(v1, v2, v3, false, false); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name BM_mesh_calc_path_uv_vert + * \{ */ + +static void looptag_add_adjacent_uv(HeapSimple *heap, + BMLoop *l_a, + BMLoop **loops_prev, + float *cost, + const struct BMCalcPathUVParams *params) +{ + BLI_assert(params->aspect_y != 0.0f); + const uint cd_loop_uv_offset = params->cd_loop_uv_offset; + const int l_a_index = BM_elem_index_get(l_a); + const MLoopUV *luv_a = BM_ELEM_CD_GET_VOID_P(l_a, cd_loop_uv_offset); + const float uv_a[2] = {luv_a->uv[0], luv_a->uv[1] / params->aspect_y}; + + { + BMIter liter; + BMLoop *l; + /* Loop over faces of face, but do so by first looping over loops. */ + BM_ITER_ELEM (l, &liter, l_a->v, BM_LOOPS_OF_VERT) { + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (equals_v2v2(luv_a->uv, luv->uv)) { + /* 'l_a' is already tagged, tag all adjacent. */ + BM_elem_flag_enable(l, BM_ELEM_TAG); + BMLoop *l_b = l->next; + do { + if (!BM_elem_flag_test(l_b, BM_ELEM_TAG)) { + const MLoopUV *luv_b = BM_ELEM_CD_GET_VOID_P(l_b, cd_loop_uv_offset); + const float uv_b[2] = {luv_b->uv[0], luv_b->uv[1] / params->aspect_y}; + /* We know 'l_b' is not visited, check it out! */ + const int l_b_index = BM_elem_index_get(l_b); + const float cost_cut = params->use_topology_distance ? 1.0f : len_v2v2(uv_a, uv_b); + const float cost_new = cost[l_a_index] + cost_cut; + + if (cost[l_b_index] > cost_new) { + cost[l_b_index] = cost_new; + loops_prev[l_b_index] = l_a; + BLI_heapsimple_insert(heap, cost_new, l_b); + } + } + /* This means we only step onto `l->prev` & `l->next`. */ + if (params->use_step_face == false) { + if (l_b == l->next) { + l_b = l->prev->prev; + } + } + } while ((l_b = l_b->next) != l); + } + } + } +} + +struct LinkNode *BM_mesh_calc_path_uv_vert(BMesh *bm, + BMLoop *l_src, + BMLoop *l_dst, + const struct BMCalcPathUVParams *params, + bool (*filter_fn)(BMLoop *, void *), + void *user_data) +{ + LinkNode *path = NULL; + /* BM_ELEM_TAG flag is used to store visited edges */ + BMIter viter; + HeapSimple *heap; + float *cost; + BMLoop **loops_prev; + int i = 0, totloop; + BMFace *f; + + /* Note, would pass BM_EDGE except we are looping over all faces anyway. */ + // BM_mesh_elem_index_ensure(bm, BM_LOOP); // NOT NEEDED FOR FACETAG + + BM_ITER_MESH (f, &viter, bm, BM_FACES_OF_MESH) { + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter = l_first; + do { + BM_elem_flag_set(l_iter, BM_ELEM_TAG, !filter_fn(l_iter, user_data)); + BM_elem_index_set(l_iter, i); /* set_inline */ + i += 1; + } while ((l_iter = l_iter->next) != l_first); + } + bm->elem_index_dirty &= ~BM_LOOP; + + /* Allocate. */ + totloop = bm->totloop; + loops_prev = MEM_callocN(sizeof(*loops_prev) * totloop, __func__); + cost = MEM_mallocN(sizeof(*cost) * totloop, __func__); + + copy_vn_fl(cost, totloop, COST_INIT_MAX); + + /* Regular dijkstra shortest path, but over UV loops instead of vertices. */ + heap = BLI_heapsimple_new(); + BLI_heapsimple_insert(heap, 0.0f, l_src); + cost[BM_elem_index_get(l_src)] = 0.0f; + + BMLoop *l = NULL; + while (!BLI_heapsimple_is_empty(heap)) { + l = BLI_heapsimple_pop_min(heap); + + if ((l->v == l_dst->v) && BM_loop_uv_share_vert_check(l, l_dst, params->cd_loop_uv_offset)) { + break; + } + + if (!BM_elem_flag_test(l, BM_ELEM_TAG)) { + /* Adjacent loops are tagged while stepping to avoid 2x loops. */ + BM_elem_flag_enable(l, BM_ELEM_TAG); + looptag_add_adjacent_uv(heap, l, loops_prev, cost, params); + } + } + + if ((l->v == l_dst->v) && BM_loop_uv_share_vert_check(l, l_dst, params->cd_loop_uv_offset)) { + do { + BLI_linklist_prepend(&path, l); + } while ((l = loops_prev[BM_elem_index_get(l)])); + } + + MEM_freeN(loops_prev); + MEM_freeN(cost); + BLI_heapsimple_free(heap, NULL); + + return path; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name BM_mesh_calc_path_uv_edge + * \{ */ +/* TODO(campbell): not very urgent, since the operator fakes this using vertex path. */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name BM_mesh_calc_path_uv_face + * \{ */ + +static float facetag_cut_cost_edge_uv(BMFace *f_a, + BMFace *f_b, + BMLoop *l_edge, + const void *const f_endpoints[2], + const float aspect_v2[2], + const int cd_loop_uv_offset) +{ + float f_a_cent[2]; + float f_b_cent[2]; + float e_cent[2]; + + BM_face_uv_calc_center_median_weighted(f_a, aspect_v2, cd_loop_uv_offset, f_a_cent); + BM_face_uv_calc_center_median_weighted(f_b, aspect_v2, cd_loop_uv_offset, f_b_cent); + + const float *co_v1 = ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_edge, cd_loop_uv_offset))->uv; + const float *co_v2 = + ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_edge->next, cd_loop_uv_offset))->uv; + +#if 0 + mid_v2_v2v2(e_cent, co_v1, co_v2); +#else + /* For triangle fans it gives better results to pick a point on the edge. */ + { + float ix_e[2]; + isect_line_line_v2_point(co_v1, co_v2, f_a_cent, f_b_cent, ix_e); + const float factor = line_point_factor_v2(ix_e, co_v1, co_v2); + if (factor < 0.0f) { + copy_v2_v2(e_cent, co_v1); + } + else if (factor > 1.0f) { + copy_v2_v2(e_cent, co_v2); + } + else { + copy_v2_v2(e_cent, ix_e); + } + } +#endif + + /* Apply aspect before calculating cost. */ + mul_v2_v2(f_a_cent, aspect_v2); + mul_v2_v2(f_b_cent, aspect_v2); + mul_v2_v2(e_cent, aspect_v2); + + return step_cost_3_v2_ex( + f_a_cent, e_cent, f_b_cent, (f_a == f_endpoints[0]), (f_b == f_endpoints[1])); +} + +static float facetag_cut_cost_vert_uv(BMFace *f_a, + BMFace *f_b, + BMLoop *l_vert, + const void *const f_endpoints[2], + const float aspect_v2[2], + const int cd_loop_uv_offset) +{ + float f_a_cent[2]; + float f_b_cent[2]; + float v_cent[2]; + + BM_face_uv_calc_center_median_weighted(f_a, aspect_v2, cd_loop_uv_offset, f_a_cent); + BM_face_uv_calc_center_median_weighted(f_b, aspect_v2, cd_loop_uv_offset, f_b_cent); + + copy_v2_v2(v_cent, ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_vert, cd_loop_uv_offset))->uv); + + mul_v2_v2(f_a_cent, aspect_v2); + mul_v2_v2(f_b_cent, aspect_v2); + mul_v2_v2(v_cent, aspect_v2); + + return step_cost_3_v2_ex( + f_a_cent, v_cent, f_b_cent, (f_a == f_endpoints[0]), (f_b == f_endpoints[1])); +} + +static void facetag_add_adjacent_uv(HeapSimple *heap, + BMFace *f_a, + BMFace **faces_prev, + float *cost, + const void *const f_endpoints[2], + const float aspect_v2[2], + const struct BMCalcPathUVParams *params) +{ + const uint cd_loop_uv_offset = params->cd_loop_uv_offset; + const int f_a_index = BM_elem_index_get(f_a); + + /* Loop over faces of face, but do so by first looping over loops. */ + { + BMIter liter; + BMLoop *l_a; + + BM_ITER_ELEM (l_a, &liter, f_a, BM_LOOPS_OF_FACE) { + BMLoop *l_first, *l_iter; + + /* Check there is an adjacent face to loop over. */ + if (l_a != l_a->radial_next) { + l_iter = l_first = l_a->radial_next; + do { + BMFace *f_b = l_iter->f; + if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) { + if (BM_loop_uv_share_edge_check(l_a, l_iter, cd_loop_uv_offset)) { + /* We know 'f_b' is not visited, check it out! */ + const int f_b_index = BM_elem_index_get(f_b); + const float cost_cut = + params->use_topology_distance ? + 1.0f : + facetag_cut_cost_edge_uv( + f_a, f_b, l_iter, f_endpoints, aspect_v2, cd_loop_uv_offset); + const float cost_new = cost[f_a_index] + cost_cut; + + if (cost[f_b_index] > cost_new) { + cost[f_b_index] = cost_new; + faces_prev[f_b_index] = f_a; + BLI_heapsimple_insert(heap, cost_new, f_b); + } + } + } + } while ((l_iter = l_iter->radial_next) != l_first); + } + } + } + + if (params->use_step_face) { + BMIter liter; + BMLoop *l_a; + + BM_ITER_ELEM (l_a, &liter, f_a, BM_LOOPS_OF_FACE) { + BMIter litersub; + BMLoop *l_b; + BM_ITER_ELEM (l_b, &litersub, l_a->v, BM_LOOPS_OF_VERT) { + if ((l_a != l_b) && !BM_loop_share_edge_check(l_a, l_b)) { + BMFace *f_b = l_b->f; + if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) { + if (BM_loop_uv_share_vert_check(l_a, l_b, cd_loop_uv_offset)) { + /* We know 'f_b' is not visited, check it out! */ + const int f_b_index = BM_elem_index_get(f_b); + const float cost_cut = + params->use_topology_distance ? + 1.0f : + facetag_cut_cost_vert_uv( + f_a, f_b, l_a, f_endpoints, aspect_v2, cd_loop_uv_offset); + const float cost_new = cost[f_a_index] + cost_cut; + + if (cost[f_b_index] > cost_new) { + cost[f_b_index] = cost_new; + faces_prev[f_b_index] = f_a; + BLI_heapsimple_insert(heap, cost_new, f_b); + } + } + } + } + } + } + } +} + +struct LinkNode *BM_mesh_calc_path_uv_face(BMesh *bm, + BMFace *f_src, + BMFace *f_dst, + const struct BMCalcPathUVParams *params, + bool (*filter_fn)(BMFace *, void *), + void *user_data) +{ + const float aspect_v2[2] = {1.0f, 1.0f / params->aspect_y}; + LinkNode *path = NULL; + /* BM_ELEM_TAG flag is used to store visited edges */ + BMIter fiter; + HeapSimple *heap; + float *cost; + BMFace **faces_prev; + int i = 0, totface; + + /* Start measuring face path at the face edges, ignoring their centers. */ + const void *const f_endpoints[2] = {f_src, f_dst}; + + /* Note, would pass BM_EDGE except we are looping over all faces anyway. */ + // BM_mesh_elem_index_ensure(bm, BM_LOOP); // NOT NEEDED FOR FACETAG + + { + BMFace *f; + BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { + BM_elem_flag_set(f, BM_ELEM_TAG, !filter_fn(f, user_data)); + BM_elem_index_set(f, i); /* set_inline */ + i += 1; + } + bm->elem_index_dirty &= ~BM_FACE; + } + + /* Allocate. */ + totface = bm->totface; + faces_prev = MEM_callocN(sizeof(*faces_prev) * totface, __func__); + cost = MEM_mallocN(sizeof(*cost) * totface, __func__); + + copy_vn_fl(cost, totface, COST_INIT_MAX); + + /* Regular dijkstra shortest path, but over UV faces instead of vertices. */ + heap = BLI_heapsimple_new(); + BLI_heapsimple_insert(heap, 0.0f, f_src); + cost[BM_elem_index_get(f_src)] = 0.0f; + + BMFace *f = NULL; + while (!BLI_heapsimple_is_empty(heap)) { + f = BLI_heapsimple_pop_min(heap); + + if (f == f_dst) { + break; + } + + if (!BM_elem_flag_test(f, BM_ELEM_TAG)) { + /* Adjacent loops are tagged while stepping to avoid 2x loops. */ + BM_elem_flag_enable(f, BM_ELEM_TAG); + facetag_add_adjacent_uv(heap, f, faces_prev, cost, f_endpoints, aspect_v2, params); + } + } + + if (f == f_dst) { + do { + BLI_linklist_prepend(&path, f); + } while ((f = faces_prev[BM_elem_index_get(f)])); + } + + MEM_freeN(faces_prev); + MEM_freeN(cost); + BLI_heapsimple_free(heap, NULL); + + return path; +} + +/** \} */ diff --git a/source/blender/bmesh/tools/bmesh_path_uv.h b/source/blender/bmesh/tools/bmesh_path_uv.h new file mode 100644 index 00000000000..c7c5768f7d0 --- /dev/null +++ b/source/blender/bmesh/tools/bmesh_path_uv.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#ifndef __BMESH_PATH_UV_H__ +#define __BMESH_PATH_UV_H__ + +/** \file + * \ingroup bmesh + */ + +struct BMCalcPathUVParams { + uint use_topology_distance : 1; + uint use_step_face : 1; + uint cd_loop_uv_offset; + float aspect_y; +}; + +struct LinkNode *BM_mesh_calc_path_uv_vert(BMesh *bm, + BMLoop *l_src, + BMLoop *l_dst, + const struct BMCalcPathUVParams *params, + bool (*filter_fn)(BMLoop *, void *), + void *user_data) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(1, 2, 3, 5); + +struct LinkNode *BM_mesh_calc_path_uv_face(BMesh *bm, + BMFace *f_src, + BMFace *f_dst, + const struct BMCalcPathUVParams *params, + bool (*filter_fn)(BMFace *, void *), + void *user_data) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(1, 2, 3, 5); + +#endif /* __BMESH_PATH_UV_H__ */ diff --git a/source/blender/bmesh/tools/bmesh_region_match.c b/source/blender/bmesh/tools/bmesh_region_match.c index 8de23b696bf..d222ea214c4 100644 --- a/source/blender/bmesh/tools/bmesh_region_match.c +++ b/source/blender/bmesh/tools/bmesh_region_match.c @@ -1299,7 +1299,9 @@ static UUIDFashMatch *bm_vert_fasthash_create(BMesh *bm, const uint depth) return id_curr; } -static void bm_vert_fasthash_edge_order(UUIDFashMatch *fm, const BMEdge *e, UUIDFashMatch e_fm[2]) +static void bm_vert_fasthash_edge_order(const UUIDFashMatch *fm, + const BMEdge *e, + UUIDFashMatch e_fm[2]) { e_fm[0] = fm[BM_elem_index_get(e->v1)]; e_fm[1] = fm[BM_elem_index_get(e->v2)]; diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp index ae6f49bffcd..84f7fe2d225 100644 --- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp +++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp @@ -26,8 +26,8 @@ // this part has been copied from the double edge mask static void do_adjacentKeepBorders(unsigned int t, unsigned int rw, - unsigned int *limask, - unsigned int *lomask, + const unsigned int *limask, + const unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize) @@ -196,8 +196,8 @@ static void do_adjacentKeepBorders(unsigned int t, static void do_adjacentBleedBorders(unsigned int t, unsigned int rw, - unsigned int *limask, - unsigned int *lomask, + const unsigned int *limask, + const unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize) @@ -417,8 +417,8 @@ static void do_adjacentBleedBorders(unsigned int t, static void do_allKeepBorders(unsigned int t, unsigned int rw, - unsigned int *limask, - unsigned int *lomask, + const unsigned int *limask, + const unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize) @@ -579,8 +579,8 @@ static void do_allKeepBorders(unsigned int t, static void do_allBleedBorders(unsigned int t, unsigned int rw, - unsigned int *limask, - unsigned int *lomask, + const unsigned int *limask, + const unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize) @@ -793,8 +793,8 @@ static void do_allBleedBorders(unsigned int t, static void do_allEdgeDetection(unsigned int t, unsigned int rw, - unsigned int *limask, - unsigned int *lomask, + const unsigned int *limask, + const unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize, @@ -863,8 +863,8 @@ static void do_allEdgeDetection(unsigned int t, static void do_adjacentEdgeDetection(unsigned int t, unsigned int rw, - unsigned int *limask, - unsigned int *lomask, + const unsigned int *limask, + const unsigned int *lomask, unsigned int *lres, float *res, unsigned int *rsize, @@ -935,7 +935,7 @@ static void do_adjacentEdgeDetection(unsigned int t, static void do_createEdgeLocationBuffer(unsigned int t, unsigned int rw, - unsigned int *lres, + const unsigned int *lres, float *res, unsigned short *gbuf, unsigned int *innerEdgeOffset, @@ -1060,7 +1060,7 @@ static void do_createEdgeLocationBuffer(unsigned int t, static void do_fillGradientBuffer(unsigned int rw, float *res, - unsigned short *gbuf, + const unsigned short *gbuf, unsigned int isz, unsigned int osz, unsigned int gsz, diff --git a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cpp b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cpp index b43b94af06a..0087c720ac0 100644 --- a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cpp +++ b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cpp @@ -198,7 +198,7 @@ static void FHT2D( //------------------------------------------------------------------------------ /* 2D convolution calc, d1 *= d2, M/N - > log2 of width/height */ -static void fht_convolve(fREAL *d1, fREAL *d2, unsigned int M, unsigned int N) +static void fht_convolve(fREAL *d1, const fREAL *d2, unsigned int M, unsigned int N) { fREAL a, b; unsigned int i, j, k, L, mj, mL; diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.cpp b/source/blender/compositor/operations/COM_VectorBlurOperation.cpp index 56caa68fd35..56e0ab9b93f 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.cpp +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.cpp @@ -514,7 +514,7 @@ void antialias_tagbuf(int xsize, int ysize, char *rectmove) /* we make this into 3 points, center point is (0, 0) */ /* and offset the center point just enough to make curve go through midpoint */ -static void quad_bezier_2d(float *result, float *v1, float *v2, float *ipodata) +static void quad_bezier_2d(float *result, const float *v1, const float *v2, const float *ipodata) { float p1[2], p2[2], p3[2]; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 50c52a519b4..8054946777c 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -2949,7 +2949,7 @@ void DepsgraphRelationBuilder::build_driver_relations(IDNode *id_node) } add_operation_relation( - op_from->get_exit_operation(), op_to->get_entry_operation(), "Driver Serialisation"); + op_from->get_exit_operation(), op_to->get_entry_operation(), "Driver Serialization"); break; } } 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 38350d50da6..79d6c8d6a77 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 @@ -711,13 +711,6 @@ void update_modifiers_orig_pointers(const Object *object_orig, Object *object_co &object_orig->modifiers, &object_cow->modifiers, &ModifierData::orig_modifier_data); } -void update_simulation_states_orig_pointers(const Simulation *simulation_orig, - Simulation *simulation_cow) -{ - update_list_orig_pointers( - &simulation_orig->states, &simulation_cow->states, &SimulationState::orig_state); -} - void update_nla_strips_orig_pointers(const ListBase *strips_orig, ListBase *strips_cow) { NlaStrip *strip_orig = reinterpret_cast<NlaStrip *>(strips_orig->first); @@ -817,12 +810,6 @@ void update_id_after_copy(const Depsgraph *depsgraph, update_scene_orig_pointers(scene_orig, scene_cow); break; } - case ID_SIM: { - Simulation *simulation_cow = (Simulation *)id_cow; - const Simulation *simulation_orig = (const Simulation *)id_orig; - update_simulation_states_orig_pointers(simulation_orig, simulation_cow); - break; - } default: break; } diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c index 8c48ae65d9b..f6e74c6822c 100644 --- a/source/blender/draw/engines/eevee/eevee_effects.c +++ b/source/blender/draw/engines/eevee/eevee_effects.c @@ -486,7 +486,7 @@ void EEVEE_downsample_buffer(EEVEE_Data *vedata, GPUTexture *texture_src, int le } /** - * Simple down-sampling algorithm for cubemap. Reconstruct mip chain up to mip level. + * Simple down-sampling algorithm for cube-map. Reconstruct mip chain up to mip level. */ void EEVEE_downsample_cube_buffer(EEVEE_Data *vedata, GPUTexture *texture_src, int level) { diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c index 78d50a02fc7..93d60d7518f 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.c +++ b/source/blender/draw/engines/eevee/eevee_lightcache.c @@ -113,7 +113,7 @@ typedef struct EEVEE_LightBake { float samples_ct, invsamples_ct; /** Sampling bias during convolution step. */ float lod_factor; - /** Max cubemap LOD to sample when convolving. */ + /** Max cube-map LOD to sample when convolving. */ float lod_max; /** Number of probes to render + world probe. */ int cube_len, grid_len; @@ -121,7 +121,7 @@ typedef struct EEVEE_LightBake { /* Irradiance grid */ /** Current probe being rendered (UBO data). */ EEVEE_LightGrid *grid; - /** Target cubemap at MIP 0. */ + /** Target cube-map at MIP 0. */ int irr_cube_res; /** Size of the irradiance texture. */ int irr_size[3]; @@ -145,7 +145,7 @@ typedef struct EEVEE_LightBake { /* Reflection probe */ /** Current probe being rendered (UBO data). */ EEVEE_LightProbe *cube; - /** Target cubemap at MIP 0. */ + /** Target cube-map at MIP 0. */ int ref_cube_res; /** Index of the current cube. */ int cube_offset; diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c index 18365d69514..b89772441fa 100644 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ b/source/blender/draw/engines/eevee/eevee_lookdev.c @@ -107,9 +107,9 @@ void EEVEE_lookdev_cache_init(EEVEE_Data *vedata, EEVEE_EffectsInfo *effects = stl->effects; EEVEE_PrivateData *g_data = stl->g_data; const DRWContextState *draw_ctx = DRW_context_state_get(); - View3D *v3d = draw_ctx->v3d; - View3DShading *shading = &v3d->shading; - Scene *scene = draw_ctx->scene; + /* The view will be NULL when rendering previews. */ + const View3D *v3d = draw_ctx->v3d; + const Scene *scene = draw_ctx->scene; const bool probe_render = pinfo != NULL; @@ -150,6 +150,7 @@ void EEVEE_lookdev_cache_init(EEVEE_Data *vedata, } if (LOOK_DEV_STUDIO_LIGHT_ENABLED(v3d)) { + const View3DShading *shading = &v3d->shading; StudioLight *sl = BKE_studiolight_find(shading->lookdev_light, STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE); if (sl && (sl->flag & STUDIOLIGHT_TYPE_WORLD)) { diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 8c17ecd3905..143945b637a 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -114,8 +114,8 @@ void EEVEE_material_bind_resources(DRWShadingGroup *shgrp, GPUMaterial *gpumat, EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, - int *ssr_id, - float *refract_depth, + const int *ssr_id, + const float *refract_depth, bool use_ssrefraction, bool use_alpha_blend) { diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index a67593773ab..1b1355fdecd 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -589,7 +589,7 @@ typedef struct EEVEE_ObjectKey { /** Parent object for duplis */ struct Object *parent; /** Dupli objects recursive unique identifier */ - int id[16]; /* 2*MAX_DUPLI_RECUR */ + int id[8]; /* MAX_DUPLI_RECUR */ } EEVEE_ObjectKey; typedef struct EEVEE_ObjectMotionData { @@ -1012,8 +1012,8 @@ void EEVEE_material_bind_resources(DRWShadingGroup *shgrp, struct GPUMaterial *gpumat, EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, - int *ssr_id, - float *refract_depth, + const int *ssr_id, + const float *refract_depth, bool use_ssrefraction, bool use_alpha_blend); /* eevee_lights.c */ diff --git a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c index 714481c39f1..997f9a5be9d 100644 --- a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c +++ b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c @@ -96,7 +96,7 @@ static void invert_cdf(const float cdf[FILTER_CDF_TABLE_SIZE], } /* Evaluate a discrete function table with linear interpolation. */ -static float eval_table(float *table, float x) +static float eval_table(const float *table, float x) { CLAMP(x, 0.0f, 1.0f); x = x * (FILTER_CDF_TABLE_SIZE - 1); @@ -240,9 +240,9 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data view_is_valid = view_is_valid && (ED_screen_animation_no_scrub(wm) == NULL); } - effects->taa_total_sample = EEVEE_renderpasses_only_first_sample_pass_active(vedata) ? - 1 : - scene_eval->eevee.taa_samples; + const bool first_sample_only = EEVEE_renderpasses_only_first_sample_pass_active(vedata); + view_is_valid = view_is_valid && !first_sample_only; + effects->taa_total_sample = first_sample_only ? 1 : scene_eval->eevee.taa_samples; MAX2(effects->taa_total_sample, 0); DRW_view_persmat_get(NULL, persmat, false); diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c index 7ccb5d5a753..daf83e11e17 100644 --- a/source/blender/draw/engines/overlay/overlay_armature.c +++ b/source/blender/draw/engines/overlay/overlay_armature.c @@ -2118,7 +2118,7 @@ static void armature_context_setup(ArmatureDrawContext *ctx, const bool do_envelope_dist, const bool is_edit_mode, const bool is_pose_mode, - float *const_color) + const float *const_color) { const bool is_object_mode = !do_envelope_dist; const bool is_xray = (ob->dtx & OB_DRAWXRAY) != 0 || (pd->armature.do_pose_xray && is_pose_mode); diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index c0407345729..f096c9657c7 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -1356,7 +1356,7 @@ static void OVERLAY_volume_extra(OVERLAY_ExtraCallBuffers *cb, Object *ob, ModifierData *md, Scene *scene, - float *color) + const float *color) { FluidModifierData *fmd = (FluidModifierData *)md; FluidDomainSettings *fds = fmd->domain; diff --git a/source/blender/draw/engines/workbench/workbench_data.c b/source/blender/draw/engines/workbench/workbench_data.c index 7b08e97ac31..9e4d77429d5 100644 --- a/source/blender/draw/engines/workbench/workbench_data.c +++ b/source/blender/draw/engines/workbench/workbench_data.c @@ -256,6 +256,8 @@ void workbench_private_data_init(WORKBENCH_PrivateData *wpd) } else if (XRAY_ENABLED(v3d)) { wpd->shading.xray_alpha = XRAY_ALPHA(v3d); + /* Disable shading options that aren't supported in transparency mode. */ + wpd->shading.flag &= ~(V3D_SHADING_SHADOW | V3D_SHADING_CAVITY | V3D_SHADING_DEPTH_OF_FIELD); } else { wpd->shading.xray_alpha = 1.0f; diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index 5f453e765b1..5fff55e2f26 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -191,7 +191,12 @@ static void workbench_cache_common_populate(WORKBENCH_PrivateData *wpd, geom = DRW_cache_mesh_surface_vertpaint_get(ob); } else { - geom = DRW_cache_mesh_surface_sculptcolors_get(ob); + if (U.experimental.use_sculpt_vertex_colors) { + geom = DRW_cache_mesh_surface_sculptcolors_get(ob); + } + else { + geom = DRW_cache_mesh_surface_vertpaint_get(ob); + } } } else { @@ -272,8 +277,15 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd, } } else if (color_type == V3D_SHADING_VERTEX_COLOR) { - if ((me == NULL) || !CustomData_has_layer(&me->vdata, CD_PROP_COLOR)) { - color_type = V3D_SHADING_OBJECT_COLOR; + if (U.experimental.use_sculpt_vertex_colors) { + if ((me == NULL) || !CustomData_has_layer(&me->vdata, CD_PROP_COLOR)) { + color_type = V3D_SHADING_OBJECT_COLOR; + } + } + else { + if ((me == NULL) || !CustomData_has_layer(&me->ldata, CD_MLOOPCOL)) { + color_type = V3D_SHADING_OBJECT_COLOR; + } } } diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 555043ab408..1ab5ec18f65 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -623,7 +623,7 @@ void DRW_render_object_iter(void *vedata, struct RenderEngine *engine, struct Depsgraph *depsgraph)); void DRW_render_instance_buffer_finish(void); -void DRW_render_viewport_size_set(int size[2]); +void DRW_render_viewport_size_set(const int size[2]); void DRW_custom_pipeline(DrawEngineType *draw_engine_type, struct Depsgraph *depsgraph, diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 302f9a0d3a8..f05e8e2f9d6 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -60,6 +60,10 @@ typedef struct DRW_MeshCDMask { * modifiers could remove it. (see T68857) */ uint32_t edit_uv : 1; } DRW_MeshCDMask; +/* Keep `DRW_MeshCDMask` struct within an `uint64_t`. + * bit-wise and atomic operations are used to compare and update the struct. + * See `mesh_cd_layers_type_*` functions. */ +BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint64_t), "DRW_MeshCDMask exceeds 64 bits") typedef enum eMRIterType { MR_ITER_LOOPTRI = 1 << 0, diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index 98da668f78f..a27ee90b148 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -2534,26 +2534,28 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf) } /* Sculpt Vertex Colors */ - for (int i = 0; i < 8; i++) { - if (svcol_layers & (1 << i)) { - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i); - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - - BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - - if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) { - GPU_vertformat_alias_add(&format, "c"); - } - if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) { - GPU_vertformat_alias_add(&format, "ac"); - } - /* Gather number of auto layers. */ - /* We only do `vcols` that are not overridden by `uvs`. */ - if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { - BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); - GPU_vertformat_alias_add(&format, attr_name); + if (U.experimental.use_sculpt_vertex_colors) { + for (int i = 0; i < 8; i++) { + if (svcol_layers & (1 << i)) { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i); + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + + BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); + GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + + if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) { + GPU_vertformat_alias_add(&format, "c"); + } + if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) { + GPU_vertformat_alias_add(&format, "ac"); + } + /* Gather number of auto layers. */ + /* We only do `vcols` that are not overridden by `uvs`. */ + if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + GPU_vertformat_alias_add(&format, attr_name); + } } } } @@ -2599,7 +2601,7 @@ static void *extract_vcol_init(const MeshRenderData *mr, void *buf) } } - if (svcol_layers & (1 << i)) { + if (svcol_layers & (1 << i) && U.experimental.use_sculpt_vertex_colors) { if (mr->extract_type == MR_EXTRACT_BMESH) { int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i); BMIter f_iter; @@ -4384,10 +4386,6 @@ static void *extract_fdots_pos_init(const MeshRenderData *mr, void *buf) GPUVertBuf *vbo = buf; GPU_vertbuf_init_with_format(vbo, &format); GPU_vertbuf_data_alloc(vbo, mr->poly_len); - if (!mr->use_subsurf_fdots) { - /* Clear so we can accumulate on it. */ - memset(vbo->data, 0x0, mr->poly_len * vbo->format.stride); - } return vbo->data; } @@ -4396,12 +4394,20 @@ static void extract_fdots_pos_iter_poly_bm(const MeshRenderData *mr, void *data) { float(*center)[3] = data; - EXTRACT_POLY_AND_LOOP_FOREACH_BM_BEGIN(l, l_index, params, mr) + + EXTRACT_POLY_FOREACH_BM_BEGIN(f, f_index, params, mr) { - float w = 1.0f / (float)l->f->len; - madd_v3_v3fl(center[BM_elem_index_get(l->f)], bm_vert_co_get(mr, l->v), w); + float *co = center[f_index]; + zero_v3(co); + + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + add_v3_v3(co, bm_vert_co_get(mr, l_iter->v)); + } while ((l_iter = l_iter->next) != l_first); + mul_v3_fl(co, 1.0f / (float)f->len); } - EXTRACT_POLY_AND_LOOP_FOREACH_BM_END(l); + EXTRACT_POLY_FOREACH_BM_END; } static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr, @@ -4409,20 +4415,34 @@ static void extract_fdots_pos_iter_poly_mesh(const MeshRenderData *mr, void *data) { float(*center)[3] = (float(*)[3])data; - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) - { - const MVert *mv = &mr->mvert[ml->v]; - if (mr->use_subsurf_fdots) { + const MVert *mvert = mr->mvert; + const MLoop *mloop = mr->mloop; + + if (mr->use_subsurf_fdots) { + EXTRACT_POLY_AND_LOOP_FOREACH_MESH_BEGIN(mp, mp_index, ml, ml_index, params, mr) + { + const MVert *mv = &mr->mvert[ml->v]; if (mv->flag & ME_VERT_FACEDOT) { copy_v3_v3(center[mp_index], mv->co); } } - else { - float w = 1.0f / (float)mp->totloop; - madd_v3_v3fl(center[mp_index], mv->co, w); + EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; + } + else { + EXTRACT_POLY_FOREACH_MESH_BEGIN(mp, mp_index, params, mr) + { + float *co = center[mp_index]; + zero_v3(co); + + const MLoop *ml = &mloop[mp->loopstart]; + for (int i = 0; i < mp->totloop; i++, ml++) { + const MVert *mv = &mvert[ml->v]; + add_v3_v3(center[mp_index], mv->co); + } + mul_v3_fl(co, 1.0f / (float)mp->totloop); } + EXTRACT_POLY_FOREACH_MESH_END; } - EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static const MeshExtract extract_fdots_pos = { diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index ea1717f0684..40de0794b9e 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -74,22 +74,25 @@ static void mesh_batch_cache_clear(Mesh *me); /* Return true is all layers in _b_ are inside _a_. */ BLI_INLINE bool mesh_cd_layers_type_overlap(DRW_MeshCDMask a, DRW_MeshCDMask b) { - return (*((uint32_t *)&a) & *((uint32_t *)&b)) == *((uint32_t *)&b); + return (*((uint64_t *)&a) & *((uint64_t *)&b)) == *((uint64_t *)&b); } BLI_INLINE bool mesh_cd_layers_type_equal(DRW_MeshCDMask a, DRW_MeshCDMask b) { - return *((uint32_t *)&a) == *((uint32_t *)&b); + return *((uint64_t *)&a) == *((uint64_t *)&b); } BLI_INLINE void mesh_cd_layers_type_merge(DRW_MeshCDMask *a, DRW_MeshCDMask b) { - atomic_fetch_and_or_uint32((uint32_t *)a, *(uint32_t *)&b); + uint32_t *a_p = (uint32_t *)a; + uint32_t *b_p = (uint32_t *)&b; + atomic_fetch_and_or_uint32(a_p, *b_p); + atomic_fetch_and_or_uint32(a_p + 1, *(b_p + 1)); } BLI_INLINE void mesh_cd_layers_type_clear(DRW_MeshCDMask *a) { - *((uint32_t *)a) = 0; + *((uint64_t *)a) = 0; } static void mesh_cd_calc_edit_uv_layer(const Mesh *UNUSED(me), DRW_MeshCDMask *cd_used) @@ -205,8 +208,10 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, } if (layer == -1) { - layer = CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name); - type = CD_PROP_COLOR; + if (U.experimental.use_sculpt_vertex_colors) { + layer = CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name); + type = CD_PROP_COLOR; + } } #if 0 /* Tangents are always from UV's - this will never happen. */ if (layer == -1) { diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 3e42c4cdb23..5359f649d6b 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -369,7 +369,7 @@ void DRW_engine_viewport_data_size_get( } /* WARNING: only use for custom pipeline. 99% of the time, you don't want to use this. */ -void DRW_render_viewport_size_set(int size[2]) +void DRW_render_viewport_size_set(const int size[2]) { DST.size[0] = size[0]; DST.size[1] = size[1]; diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 31a2dd7f0fe..ab87760942c 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -583,7 +583,7 @@ void drw_state_set(DRWState state); void drw_debug_draw(void); void drw_debug_init(void); -eDRWCommandType command_type_get(uint64_t *command_type_bits, int index); +eDRWCommandType command_type_get(const uint64_t *command_type_bits, int index); void drw_batch_cache_validate(Object *ob); void drw_batch_cache_generate_requested(struct Object *ob); diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 14d77ab1b21..43fec503301 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -623,7 +623,7 @@ static void command_type_set(uint64_t *command_type_bits, int index, eDRWCommand command_type_bits[index / 16] |= ((uint64_t)type) << ((index % 16) * 4); } -eDRWCommandType command_type_get(uint64_t *command_type_bits, int index) +eDRWCommandType command_type_get(const uint64_t *command_type_bits, int index) { return ((command_type_bits[index / 16] >> ((index % 16) * 4)) & 0xF); } diff --git a/source/blender/draw/intern/draw_manager_shader.c b/source/blender/draw/intern/draw_manager_shader.c index 6304b707cb9..592b030c45e 100644 --- a/source/blender/draw/intern/draw_manager_shader.c +++ b/source/blender/draw/intern/draw_manager_shader.c @@ -91,10 +91,13 @@ static void drw_deferred_shader_queue_free(ListBase *queue) } } -static void drw_deferred_shader_compilation_exec(void *custom_data, - short *stop, - short *do_update, - float *progress) +static void drw_deferred_shader_compilation_exec( + void *custom_data, + /* Cannot be const, this function implements wm_jobs_start_callback. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + short *stop, + short *do_update, + float *progress) { DRWShaderCompiler *comp = (DRWShaderCompiler *)custom_data; void *gl_context = comp->gl_context; diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index d823b976a47..bcdbf4c74f0 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -540,7 +540,7 @@ static void draw_markers_background(rctf *rect) immUnbindProgram(); } -static bool marker_is_in_frame_range(TimeMarker *marker, int frame_range[2]) +static bool marker_is_in_frame_range(TimeMarker *marker, const int frame_range[2]) { if (marker->frame < frame_range[0]) { return false; diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c index 23fb6c0ab27..11066595e2e 100644 --- a/source/blender/editors/armature/meshlaplacian.c +++ b/source/blender/editors/armature/meshlaplacian.c @@ -655,7 +655,7 @@ void heat_bone_weighting(Object *ob, bDeformGroup **dgroupflip, float (*root)[3], float (*tip)[3], - int *selected, + const int *selected, const char **err_str) { LaplacianSystem *sys; @@ -1236,7 +1236,7 @@ static float meshdeform_boundary_phi(const MeshDeformBind *mdb, } static float meshdeform_interp_w(MeshDeformBind *mdb, - float *gridvec, + const float *gridvec, float *UNUSED(vec), int UNUSED(cagevert)) { diff --git a/source/blender/editors/armature/meshlaplacian.h b/source/blender/editors/armature/meshlaplacian.h index ef4759eab4a..0a9e6e878e4 100644 --- a/source/blender/editors/armature/meshlaplacian.h +++ b/source/blender/editors/armature/meshlaplacian.h @@ -56,7 +56,7 @@ void heat_bone_weighting(struct Object *ob, struct bDeformGroup **dgroupflip, float (*root)[3], float (*tip)[3], - int *selected, + const int *selected, const char **error); #ifdef RIGID_DEFORM diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index 9d70f8877c6..6ce9ed06f1a 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -87,12 +87,12 @@ static void action_set_activemarker(void *UNUSED(a), void *UNUSED(b), void *UNUS * It acts as a kind of "glorified clipboard for poses", allowing for naming of poses. * * Features: - * - PoseLibs are simply normal Actions. - * - Each "pose" is simply a set of keyframes that occur on a particular frame. - * - A set of TimeMarkers that belong to each Action, help 'label' where a 'pose' can be + * - Pose-libs are simply normal Actions. + * - Each "pose" is simply a set of key-frames that occur on a particular frame. + * - A set of #TimeMarker that belong to each Action, help 'label' where a 'pose' can be * found in the Action. - * - The Scrollwheel or PageUp/Down buttons when used in a special mode or after pressing/holding - * [a modifier] key, cycles through the poses available for the active pose's poselib, + * - The Scroll-wheel or PageUp/Down buttons when used in a special mode or after pressing/holding + * [a modifier] key, cycles through the poses available for the active pose's pose-lib, * allowing the animator to preview what action best suits that pose. */ /* ************************************************************* */ diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 38ab917ba72..3e428eaffc2 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -5498,6 +5498,7 @@ static int ed_editcurve_addvert(Curve *cu, if (nu) { nurb_new = BKE_nurb_copy(nu, 1, 1); + memcpy(nurb_new->bezt, nu->bezt, sizeof(BezTriple)); } else { nurb_new = MEM_callocN(sizeof(Nurb), "BLI_editcurve_addvert new_bezt_nurb 2"); diff --git a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c index e6333d7d3e0..f31e004264c 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c @@ -88,7 +88,8 @@ static void arrow_draw_geom(const ArrowGizmo3D *arrow, const bool select, const const int draw_style = RNA_enum_get(arrow->gizmo.ptr, "draw_style"); const int draw_options = RNA_enum_get(arrow->gizmo.ptr, "draw_options"); - immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); + immBindBuiltinProgram(select ? GPU_SHADER_3D_UNIFORM_COLOR : + GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); float viewport[4]; GPU_viewport_size_get_f(viewport); diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c index fd24149e9ea..85f84af5f14 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c @@ -637,7 +637,7 @@ static void gizmo_cage2d_draw_intern(wmGizmo *gz, } if (select) { - /* expand for hotspot */ + /* Expand for hot-spot. */ const float size[2] = {size_real[0] + margin[0] / 2, size_real[1] + margin[1] / 2}; if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE) { @@ -694,7 +694,7 @@ static void gizmo_cage2d_draw_intern(wmGizmo *gz, bool show = false; if (gz->highlight_part == ED_GIZMO_CAGE2D_PART_TRANSLATE) { /* Only show if we're drawing the center handle - * otherwise the entire rectangle is the hotspot. */ + * otherwise the entire rectangle is the hot-spot. */ if (draw_options & ED_GIZMO_CAGE2D_DRAW_FLAG_XFORM_CENTER_HANDLE) { show = true; } @@ -805,7 +805,7 @@ static int gizmo_cage2d_test_select(bContext *C, wmGizmo *gz, const int mval[2]) return -1; } - /* expand for hotspot */ + /* Expand for hots-pot. */ const float size[2] = {size_real[0] + margin[0] / 2, size_real[1] + margin[1] / 2}; const int transform_flag = RNA_enum_get(gz->ptr, "transform"); diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c index b0af8641767..8955a666e22 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c @@ -314,7 +314,7 @@ static void gizmo_cage3d_draw_intern( } if (select) { - /* expand for hotspot */ + /* Expand for hot-spot. */ #if 0 const float size[3] = { size_real[0] + margin[0] / 2, diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c index 763f5687edf..68ab5100bf4 100644 --- a/source/blender/editors/gpencil/gpencil_mesh.c +++ b/source/blender/editors/gpencil/gpencil_mesh.c @@ -82,7 +82,7 @@ static bool gpencil_bake_mesh_animation_poll(bContext *C) } typedef struct GpBakeOb { - struct GPBakelist *next, *prev; + struct GpBakeOb *next, *prev; Object *ob; } GpBakeOb; diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index dd5e16a9d9b..a695d2e4cf2 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -2245,7 +2245,7 @@ static void gpencil_copy_points( } static void gpencil_insert_point( - bGPDstroke *gps, bGPDspoint *a_pt, bGPDspoint *b_pt, const float co_a[3], float co_b[3]) + bGPDstroke *gps, bGPDspoint *a_pt, bGPDspoint *b_pt, const float co_a[3], const float co_b[3]) { bGPDspoint *temp_points; int totnewpoints, oldtotpoints; @@ -2764,7 +2764,7 @@ void ED_gpencil_init_random_settings(Brush *brush, } static void gpencil_sbuffer_vertex_color_random( - bGPdata *gpd, Brush *brush, tGPspoint *tpt, float random_color[3], float pen_pressure) + bGPdata *gpd, Brush *brush, tGPspoint *tpt, const float random_color[3], float pen_pressure) { BrushGpencilSettings *brush_settings = brush->gpencil_settings; if (brush_settings->flag & GP_BRUSH_GROUP_RANDOM) { @@ -2895,7 +2895,7 @@ void ED_gpencil_sbuffer_vertex_color_set(Depsgraph *depsgraph, /* Check if the stroke collides with brush. */ bool ED_gpencil_stroke_check_collision(GP_SpaceConversion *gsc, bGPDstroke *gps, - float mouse[2], + const float mouse[2], const int radius, const float diff_mat[4][4]) { diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c index 5f8ff228d4d..c36bc4388d7 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_paint.c +++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c @@ -348,7 +348,7 @@ static void gpencil_grid_cell_average_color_idx_get(tGP_BrushVertexpaintData *gs } } -static int gpencil_grid_cell_index_get(tGP_BrushVertexpaintData *gso, int pc[2]) +static int gpencil_grid_cell_index_get(tGP_BrushVertexpaintData *gso, const int pc[2]) { float bottom[2], top[2]; diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 3aecec0d6b6..bffa11a32f2 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -708,7 +708,7 @@ void getcolor_fcurve_rainbow(int cur, int tot, float out[3]); /* ----------------- NLA Drawing ----------------------- */ /* NOTE: Technically, this is not in the animation module (it's in space_nla) - * but these are sometimes needed by various animation apis. + * but these are sometimes needed by various animation API's. */ /* Get color to use for NLA Action channel's background */ diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index a523e924e54..8a239559627 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -106,7 +106,7 @@ struct FileSelectParams *ED_fileselect_get_params(struct SpaceFile *sfile); short ED_fileselect_set_params(struct SpaceFile *sfile); void ED_fileselect_set_params_from_userdef(struct SpaceFile *sfile); void ED_fileselect_params_to_userdef(struct SpaceFile *sfile, - int temp_win_size[], + const int temp_win_size[], const bool is_maximized); void ED_fileselect_reset_params(struct SpaceFile *sfile); diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index f961f835f12..64276706759 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -351,7 +351,7 @@ void ED_gpencil_init_random_settings(struct Brush *brush, bool ED_gpencil_stroke_check_collision(struct GP_SpaceConversion *gsc, struct bGPDstroke *gps, - float mouse[2], + const float mouse[2], const int radius, const float diff_mat[4][4]); bool ED_gpencil_stroke_point_is_inside(struct bGPDstroke *gps, diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index a8476e3d1ca..e8343fbbc19 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -125,8 +125,8 @@ void ED_image_draw_info(struct Scene *scene, const unsigned char cp[4], const float fp[4], const float linearcol[4], - int *zp, - float *zpf); + const int *zp, + const float *zpf); bool ED_space_image_show_cache(struct SpaceImage *sima); diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h index 47ccc0788c2..382902cd2de 100644 --- a/source/blender/editors/include/ED_node.h +++ b/source/blender/editors/include/ED_node.h @@ -121,7 +121,7 @@ void ED_operatormacros_node(void); bool ED_space_node_color_sample(struct Main *bmain, struct SpaceNode *snode, struct ARegion *region, - int mval[2], + const int mval[2], float r_col[3]); #ifdef __cplusplus diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index a851eb735b8..d8f55a0f60a 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -362,9 +362,10 @@ struct ModifierData *ED_object_modifier_add(struct ReportList *reports, int type); bool ED_object_modifier_remove(struct ReportList *reports, struct Main *bmain, + struct Scene *scene, struct Object *ob, struct ModifierData *md); -void ED_object_modifier_clear(struct Main *bmain, struct Object *ob); +void ED_object_modifier_clear(struct Main *bmain, struct Scene *scene, struct Object *ob); bool ED_object_modifier_move_down(struct ReportList *reports, struct Object *ob, struct ModifierData *md); @@ -389,8 +390,11 @@ bool ED_object_modifier_apply(struct Main *bmain, struct Scene *scene, struct Object *ob, struct ModifierData *md, - int mode); + int mode, + bool keep_modifier); int ED_object_modifier_copy(struct ReportList *reports, + struct Main *bmain, + struct Scene *scene, struct Object *ob, struct ModifierData *md); diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index bc6a4b23609..71b7d35908b 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -349,6 +349,7 @@ bool ED_operator_console_active(struct bContext *C); bool ED_operator_object_active(struct bContext *C); bool ED_operator_object_active_editable_ex(struct bContext *C, const Object *ob); bool ED_operator_object_active_editable(struct bContext *C); +bool ED_operator_object_active_local_editable(struct bContext *C); bool ED_operator_object_active_editable_mesh(struct bContext *C); bool ED_operator_object_active_editable_font(struct bContext *C); bool ED_operator_editable_mesh(struct bContext *C); diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index f656aaf9c07..ec41e785714 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -47,6 +47,7 @@ struct wmKeyConfig; /* uvedit_ops.c */ void ED_operatortypes_uvedit(void); +void ED_operatormacros_uvedit(void); void ED_keymap_uvedit(struct wmKeyConfig *keyconf); bool ED_uvedit_minmax(const struct Scene *scene, @@ -174,8 +175,26 @@ bool ED_uvedit_nearest_uv_multi(const struct Scene *scene, float *dist_sq, float r_uv[2]); -void ED_uvedit_get_aspect( - const struct Scene *scene, struct Object *ob, struct BMesh *em, float *r_aspx, float *r_aspy); +struct BMFace **ED_uvedit_selected_faces(struct Scene *scene, + struct BMesh *bm, + int len_max, + int *r_faces_len); +struct BMLoop **ED_uvedit_selected_edges(struct Scene *scene, + struct BMesh *bm, + int len_max, + int *r_edges_len); +struct BMLoop **ED_uvedit_selected_verts(struct Scene *scene, + struct BMesh *bm, + int len_max, + int *r_verts_len); + +void ED_uvedit_get_aspect(struct Object *obedit, float *r_aspx, float *r_aspy); + +void ED_uvedit_active_vert_loop_set(struct BMesh *bm, struct BMLoop *l); +struct BMLoop *ED_uvedit_active_vert_loop_get(struct BMesh *bm); + +void ED_uvedit_active_edge_loop_set(struct BMesh *bm, struct BMLoop *l); +struct BMLoop *ED_uvedit_active_edge_loop_get(struct BMesh *bm); /* uvedit_unwrap_ops.c */ void ED_uvedit_live_unwrap_begin(struct Scene *scene, struct Object *obedit); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 5e706856738..f8a4884c594 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -150,7 +150,7 @@ void ED_view3d_cursor3d_update(struct bContext *C, struct Camera *ED_view3d_camera_data_get(struct View3D *v3d, struct RegionView3D *rv3d); void ED_view3d_to_m4(float mat[4][4], const float ofs[3], const float quat[4], const float dist); -void ED_view3d_from_m4(const float mat[4][4], float ofs[3], float quat[4], float *dist); +void ED_view3d_from_m4(const float mat[4][4], float ofs[3], float quat[4], const float *dist); void ED_view3d_from_object( const struct Object *ob, float ofs[3], float quat[4], float *dist, float *lens); @@ -502,7 +502,7 @@ bool ED_view3d_autodist_simple(struct ARegion *region, const int mval[2], float mouse_worldloc[3], int margin, - float *force_depth); + const float *force_depth); bool ED_view3d_autodist_depth(struct ARegion *region, const int mval[2], int margin, float *depth); bool ED_view3d_autodist_depth_seg(struct ARegion *region, const int mval_sta[2], diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 7fd45e06fbf..7ce74a87ea0 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -293,12 +293,13 @@ enum { /* 16 to copy ICON_DEFAULT_HEIGHT */ #define UI_DPI_ICON_SIZE ((float)16 * UI_DPI_FAC) -/* Button types, bits stored in 1 value... and a short even! - * - bits 0-4: bitnr (0-31) +/** + * Button types, bits stored in 1 value... and a short even! + * - bits 0-4: #uiBut.bitnr (0-31) * - bits 5-7: pointer type * - bit 8: for 'bit' * - bit 9-15: button type (now 6 bits, 64 types) - * */ + */ typedef enum { UI_BUT_POIN_CHAR = 32, UI_BUT_POIN_SHORT = 64, @@ -1505,7 +1506,7 @@ uiBut *uiDefHotKeyevtButS(uiBlock *block, short width, short height, short *keypoin, - short *modkeypoin, + const short *modkeypoin, const char *tip); uiBut *uiDefSearchBut(uiBlock *block, diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 6c1f9d4f017..ca89c5f606f 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -737,8 +737,8 @@ static bool ui_but_update_from_old_block(const bContext *C, #else BLI_assert(*but_old_p == NULL || BLI_findindex(&oldblock->buttons, *but_old_p) != -1); - /* fastpath - avoid loop-in-loop, calling 'ui_but_find_old' - * as long as old/new buttons are aligned */ + /* Fast-path - avoid loop-in-loop, calling #ui_but_find_old + * as long as old/new buttons are aligned. */ if (LIKELY(*but_old_p && ui_but_equals_old(but, *but_old_p))) { oldbut = *but_old_p; } @@ -2308,7 +2308,7 @@ bool ui_but_is_rna_valid(uiBut *but) } /** - * Checks if the button supports ctrl+mousewheel cycling + * Checks if the button supports cycling next/previous menu items (ctrl+mouse-wheel). */ bool ui_but_supports_cycling(const uiBut *but) { @@ -6262,7 +6262,7 @@ uiBut *uiDefHotKeyevtButS(uiBlock *block, short width, short height, short *keypoin, - short *modkeypoin, + const short *modkeypoin, const char *tip) { uiBut *but = ui_def_but(block, diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index ccbbaf40992..bad833265d9 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -8935,11 +8935,11 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi my = event->y; ui_window_to_block(region, listbox->block, &mx, &my); - /* convert pan to scrollwheel */ + /* Convert pan to scroll-wheel. */ if (type == MOUSEPAN) { ui_pan_to_scroll(event, &type, &val); - /* if type still is mousepan, we call it handled, since delta-y accumulate */ + /* If type still is mouse-pan, we call it handled, since delta-y accumulate. */ /* also see wm_event_system.c do_wheel_ui hack */ if (type == MOUSEPAN) { retval = WM_UI_HANDLER_BREAK; @@ -9666,7 +9666,7 @@ static int ui_handle_menu_event(bContext *C, int type = event->type; int val = event->val; - /* convert pan to scrollwheel */ + /* Convert pan to scroll-wheel. */ if (type == MOUSEPAN) { ui_pan_to_scroll(event, &type, &val); } @@ -9691,7 +9691,7 @@ static int ui_handle_menu_event(bContext *C, case EVT_PAGEDOWNKEY: case EVT_HOMEKEY: case EVT_ENDKEY: - /* arrowkeys: only handle for block_loop blocks */ + /* Arrow-keys: only handle for block_loop blocks. */ if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { /* pass */ } @@ -9699,7 +9699,7 @@ static int ui_handle_menu_event(bContext *C, int type = event->type; int val = event->val; - /* convert pan to scrollwheel */ + /* Convert pan to scroll-wheel. */ if (type == MOUSEPAN) { ui_pan_to_scroll(event, &type, &val); } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 8f98f380854..3c0b5bd3027 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -362,12 +362,13 @@ static bool id_search_add(const bContext *C, */ char name_ui[MAX_ID_FULL_NAME_UI]; int iconid = ui_id_icon_get(C, id, template_ui->preview); - bool has_sep_char = (id->lib != NULL); + const bool use_lib_prefix = template_ui->preview || iconid; + const bool has_sep_char = (id->lib != NULL); /* When using previews, the library hint (linked, overridden, missing) is added with a * character prefix, otherwise we can use a icon. */ - BKE_id_full_name_ui_prefix_get(name_ui, id, template_ui->preview, UI_SEP_CHAR); - if (!template_ui->preview) { + BKE_id_full_name_ui_prefix_get(name_ui, id, use_lib_prefix, UI_SEP_CHAR); + if (!use_lib_prefix) { iconid = UI_library_icon_get(id); } @@ -521,7 +522,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) switch (event) { case UI_ID_BROWSE: case UI_ID_PIN: - RNA_warning("warning, id event %d shouldnt come here", event); + RNA_warning("warning, id event %d shouldn't come here", event); break; case UI_ID_OPEN: case UI_ID_ADD_NEW: @@ -558,7 +559,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) case UI_ID_LOCAL: if (id) { Main *bmain = CTX_data_main(C); - if (BKE_lib_override_library_is_enabled() && CTX_wm_window(C)->eventstate->shift) { + if (CTX_wm_window(C)->eventstate->shift) { if (ID_IS_OVERRIDABLE_LIBRARY(id)) { /* Only remap that specific ID usage to overriding local data-block. */ ID *override_id = BKE_lib_override_library_create_from_id(bmain, id, false); @@ -568,6 +569,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) /* Assign new pointer, takes care of updates/notifiers */ RNA_id_pointer_create(override_id, &idptr); } + undo_push_label = "Make Library Override"; } } else { @@ -576,11 +578,13 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) /* reassign to get get proper updates/notifiers */ idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); + undo_push_label = "Make Local"; } } - RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL); - RNA_property_update(C, &template_ui->ptr, template_ui->prop); - undo_push_label = "Make Local"; + if (undo_push_label != NULL) { + RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL); + RNA_property_update(C, &template_ui->ptr, template_ui->prop); + } } break; case UI_ID_OVERRIDE: @@ -930,10 +934,8 @@ static void template_ID(const bContext *C, 0, 0, 0, - BKE_lib_override_library_is_enabled() ? - TIP_("Direct linked library data-block, click to make local, " - "Shift + Click to create a library override") : - TIP_("Direct linked library data-block, click to make local")); + TIP_("Direct linked library data-block, click to make local, " + "Shift + Click to create a library override")); if (disabled) { UI_but_flag_enable(but, UI_BUT_DISABLED); } @@ -2186,8 +2188,13 @@ void uiTemplateShaderFx(uiLayout *UNUSED(layout), bContext *C) char panel_idname[MAX_NAME]; shaderfx_panel_id(fx, panel_idname); + /* Create custom data RNA pointer. */ + PointerRNA *fx_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata"); + RNA_pointer_create(&ob->id, &RNA_ShaderFx, fx, fx_ptr); + Panel *new_panel = UI_panel_add_instanced( - sa, region, ®ion->panels, panel_idname, i, NULL); + sa, region, ®ion->panels, panel_idname, i, fx_ptr); + if (new_panel != NULL) { UI_panel_set_expand_from_list_data(C, new_panel); } @@ -2200,6 +2207,27 @@ void uiTemplateShaderFx(uiLayout *UNUSED(layout), bContext *C) UI_panel_set_expand_from_list_data(C, panel); } } + + /* Assuming there's only one group of instanced panels, update the custom data pointers. */ + Panel *panel = region->panels.first; + LISTBASE_FOREACH (ShaderFxData *, fx, shaderfx) { + const ShaderFxTypeInfo *fxi = BKE_shaderfx_get_info(fx->type); + if (fxi->panelRegister == NULL) { + continue; + } + + /* Move to the next instanced panel corresponding to the next modifier. */ + while ((panel->type == NULL) || !(panel->type->flag & PNL_INSTANCED)) { + panel = panel->next; + BLI_assert(panel != NULL); /* There shouldn't be fewer panels than modifiers with UIs. */ + } + + PointerRNA *fx_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata"); + RNA_pointer_create(&ob->id, &RNA_ShaderFx, fx, fx_ptr); + UI_panel_custom_data_set(panel, fx_ptr); + + panel = panel->next; + } } } @@ -4821,6 +4849,7 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUp /* Preset selector */ /* There is probably potential to use simpler "uiItemR" functions here, but automatic updating * after a preset is selected would be more complicated. */ + row = uiLayoutRow(layout, true); bt = uiDefBlockBut( block, CurveProfile_buttons_presets, profile, "Preset", 0, 0, UI_UNIT_X, UI_UNIT_X, ""); UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 31f8c89c2bc..c8f2bec145b 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -4584,7 +4584,7 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu if ((but->drawflag & (UI_BUT_TEXT_LEFT | UI_BUT_TEXT_RIGHT)) == 0) { but->drawflag |= UI_BUT_TEXT_LEFT; } - /* widget_optionbut() carefully sets the text rectangle for fine tuned paddings. If the + /* #widget_optionbut() carefully sets the text rectangle for fine tuned paddings. If the * text drawing were to add its own padding, DPI and zoom factor would be applied twice * in the final padding, so it's difficult to control it. */ but->drawflag |= UI_BUT_NO_TEXT_PADDING; diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index a2c83c24e96..c37a7279773 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -68,7 +68,7 @@ static bool view2d_poll(bContext *C) /** * This group of operators come in several forms: * -# Modal 'dragging' with MMB - where movement of mouse dictates amount to pan view by - * -# Scrollwheel 'steps' - rolling mousewheel by one step moves view by predefined amount + * -# Scroll-wheel 'steps' - rolling mouse-wheel by one step moves view by predefined amount * * In order to make sure this works, each operator must define the following RNA-Operator Props: * - `deltax, deltay` - define how much to move view by (relative to zoom-correction factor) @@ -738,8 +738,8 @@ static void VIEW2D_OT_scroll_up(wmOperatorType *ot) /** * This group of operators come in several forms: - * -# Scrollwheel 'steps' - rolling mousewheel by one step zooms view by predefined amount. - * -# Scrollwheel 'steps' + alt + ctrl/shift - zooms view on one axis only (ctrl=x, shift=y). + * -# Scroll-wheel 'steps' - rolling mouse-wheel by one step zooms view by predefined amount. + * -# Scroll-wheel 'steps' + alt + ctrl/shift - zooms view on one axis only (ctrl=x, shift=y). * XXX this could be implemented... * -# Pad +/- Keys - pressing each key moves the zooms the view by a predefined amount. * diff --git a/source/blender/editors/lattice/editlattice_select.c b/source/blender/editors/lattice/editlattice_select.c index 0b62934d190..49cf4779496 100644 --- a/source/blender/editors/lattice/editlattice_select.c +++ b/source/blender/editors/lattice/editlattice_select.c @@ -268,7 +268,7 @@ void LATTICE_OT_select_mirror(wmOperatorType *ot) * \{ */ static bool lattice_test_bitmap_uvw( - Lattice *lt, BLI_bitmap *selpoints, int u, int v, int w, const bool selected) + Lattice *lt, const BLI_bitmap *selpoints, int u, int v, int w, const bool selected) { if ((u < 0 || u >= lt->pntsu) || (v < 0 || v >= lt->pntsv) || (w < 0 || w >= lt->pntsw)) { return false; diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c index b7fd661d8e6..c19a5b8ef68 100644 --- a/source/blender/editors/mask/mask_add.c +++ b/source/blender/editors/mask/mask_add.c @@ -439,7 +439,7 @@ static bool add_vertex_new(const bContext *C, Mask *mask, MaskLayer *mask_layer, /* Convert coordinate from normalized space to pixel one. * TODO(sergey): Make the function more generally available. */ static void mask_point_make_pixel_space(bContext *C, - float point_normalized[2], + const float point_normalized[2], float point_pixel[2]) { ScrArea *area = CTX_wm_area(C); diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index adc3be6b2ac..0394874e8c1 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -102,7 +102,7 @@ typedef struct KnifeVert { ListBase edges; ListBase faces; - float co[3], cageco[3], sco[2]; /* sco is screen coordinates for cageco */ + float co[3], cageco[3]; bool is_face, in_space; bool is_cut; /* along a cut created by user input (will draw too) */ } KnifeVert; @@ -429,8 +429,6 @@ static KnifeVert *new_knife_vert(KnifeTool_OpData *kcd, const float co[3], const copy_v3_v3(kfv->co, co); copy_v3_v3(kfv->cageco, cageco); - knife_project_v2(kcd, kfv->cageco, kfv->sco); - return kfv; } @@ -1548,8 +1546,8 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) { SmallHash faces, kfes, kfvs; float v1[3], v2[3], v3[3], v4[3], s1[2], s2[2]; - BVHTree *planetree, *tree; - BVHTreeOverlap *results, *result; + BVHTree *tree; + int *results, *result; BMLoop **ls; BMFace *f; KnifeEdge *kfe; @@ -1562,7 +1560,6 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) KnifeLineHit hit; void *val; void **val_p; - float plane_cos[12]; float s[2], se1[2], se2[2], sint[2]; float r1[3], r2[3]; float d, d1, d2, lambda; @@ -1623,22 +1620,22 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) clip_to_ortho_planes(v2, v4, kcd->ortho_extent_center, kcd->ortho_extent + 10.0f); } + float plane[4]; + { + float v1_v2[3], v1_v3[3]; + sub_v3_v3v3(v1_v2, v2, v1); + sub_v3_v3v3(v1_v3, v3, v1); + cross_v3_v3v3(plane, v1_v2, v1_v3); + plane_from_point_normal_v3(plane, v1, plane); + } + /* First use bvh tree to find faces, knife edges, and knife verts that might * intersect the cut plane with rays v1-v3 and v2-v4. * This deduplicates the candidates before doing more expensive intersection tests. */ tree = BKE_bmbvh_tree_get(kcd->bmbvh); - planetree = BLI_bvhtree_new(4, FLT_EPSILON * 4, 8, 8); - copy_v3_v3(plane_cos + 0, v1); - copy_v3_v3(plane_cos + 3, v2); - copy_v3_v3(plane_cos + 6, v3); - copy_v3_v3(plane_cos + 9, v4); - BLI_bvhtree_insert(planetree, 0, plane_cos, 4); - BLI_bvhtree_balance(planetree); - - results = BLI_bvhtree_overlap(tree, planetree, &tot, NULL, NULL); + results = BLI_bvhtree_intersect_plane(tree, plane, &tot); if (!results) { - BLI_bvhtree_free(planetree); return; } @@ -1647,9 +1644,9 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) BLI_smallhash_init(&kfvs); for (i = 0, result = results; i < tot; i++, result++) { - ls = (BMLoop **)kcd->em->looptris[result->indexA]; + ls = (BMLoop **)kcd->em->looptris[*result]; f = ls[0]->f; - set_lowest_face_tri(kcd, f, result->indexA); + set_lowest_face_tri(kcd, f, *result); /* occlude but never cut unselected faces (when only_select is used) */ if (kcd->only_select && !BM_elem_flag_test(f, BM_ELEM_SELECT)) { @@ -1834,7 +1831,6 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) BLI_smallhash_release(&faces); BLI_smallhash_release(&kfes); BLI_smallhash_release(&kfvs); - BLI_bvhtree_free(planetree); if (results) { MEM_freeN(results); } @@ -1928,10 +1924,11 @@ static int knife_sample_screen_density(KnifeTool_OpData *kcd, const float radius for (i = 0; i < 2; i++) { KnifeVert *kfv = i ? kfe->v2 : kfe->v1; + float kfv_sco[2]; - knife_project_v2(kcd, kfv->cageco, kfv->sco); + knife_project_v2(kcd, kfv->cageco, kfv_sco); - dis_sq = len_squared_v2v2(kfv->sco, sco); + dis_sq = len_squared_v2v2(kfv_sco, sco); if (dis_sq < radius_sq) { if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) { if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true) == 0) { @@ -1961,11 +1958,12 @@ static float knife_snap_size(KnifeTool_OpData *kcd, float maxsize) } /* p is closest point on edge to the mouse cursor */ -static KnifeEdge *knife_find_closest_edge( - KnifeTool_OpData *kcd, float p[3], float cagep[3], BMFace **fptr, bool *is_space) +static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd, + BMFace *f, + float p[3], + float cagep[3]) { - BMFace *f; - float co[3], cageco[3], sco[2]; + float sco[2]; float maxdist; if (kcd->is_interactive) { @@ -1979,127 +1977,105 @@ static KnifeEdge *knife_find_closest_edge( maxdist = KNIFE_FLT_EPS; } - f = knife_find_closest_face(kcd, co, cageco, NULL); - *is_space = !f; - - kcd->curr.bmface = f; - - if (f) { - const float maxdist_sq = maxdist * maxdist; - KnifeEdge *cure = NULL; - float cur_cagep[3]; - ListBase *lst; - Ref *ref; - float dis_sq, curdis_sq = FLT_MAX; - - /* set p to co, in case we don't find anything, means a face cut */ - copy_v3_v3(p, co); - copy_v3_v3(cagep, cageco); - - knife_project_v2(kcd, cageco, sco); - - /* look through all edges associated with this face */ - lst = knife_get_face_kedges(kcd, f); - for (ref = lst->first; ref; ref = ref->next) { - KnifeEdge *kfe = ref->ref; - float test_cagep[3]; - float lambda; - - /* project edge vertices into screen space */ - knife_project_v2(kcd, kfe->v1->cageco, kfe->v1->sco); - knife_project_v2(kcd, kfe->v2->cageco, kfe->v2->sco); - - /* check if we're close enough and calculate 'lambda' */ - if (kcd->is_angle_snapping) { - /* if snapping, check we're in bounds */ - float sco_snap[2]; - isect_line_line_v2_point( - kfe->v1->sco, kfe->v2->sco, kcd->prev.mval, kcd->curr.mval, sco_snap); - lambda = line_point_factor_v2(sco_snap, kfe->v1->sco, kfe->v2->sco); - - /* be strict about angle-snapping within edge */ - if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) { - continue; - } + const float maxdist_sq = maxdist * maxdist; + KnifeEdge *cure = NULL; + float cur_cagep[3]; + ListBase *lst; + Ref *ref; + float dis_sq, curdis_sq = FLT_MAX; + + knife_project_v2(kcd, cagep, sco); + + /* look through all edges associated with this face */ + lst = knife_get_face_kedges(kcd, f); + for (ref = lst->first; ref; ref = ref->next) { + KnifeEdge *kfe = ref->ref; + float kfv1_sco[2], kfv2_sco[2], test_cagep[3]; + float lambda; + + /* project edge vertices into screen space */ + knife_project_v2(kcd, kfe->v1->cageco, kfv1_sco); + knife_project_v2(kcd, kfe->v2->cageco, kfv2_sco); + + /* check if we're close enough and calculate 'lambda' */ + if (kcd->is_angle_snapping) { + /* if snapping, check we're in bounds */ + float sco_snap[2]; + isect_line_line_v2_point(kfv1_sco, kfv2_sco, kcd->prev.mval, kcd->curr.mval, sco_snap); + lambda = line_point_factor_v2(sco_snap, kfv1_sco, kfv2_sco); + + /* be strict about angle-snapping within edge */ + if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) { + continue; + } - dis_sq = len_squared_v2v2(sco, sco_snap); - if (dis_sq < curdis_sq && dis_sq < maxdist_sq) { - /* we already have 'lambda' */ - } - else { - continue; - } + dis_sq = len_squared_v2v2(sco, sco_snap); + if (dis_sq < curdis_sq && dis_sq < maxdist_sq) { + /* we already have 'lambda' */ } else { - dis_sq = dist_squared_to_line_segment_v2(sco, kfe->v1->sco, kfe->v2->sco); - if (dis_sq < curdis_sq && dis_sq < maxdist_sq) { - lambda = line_point_factor_v2(sco, kfe->v1->sco, kfe->v2->sco); - } - else { - continue; - } + continue; } - - /* now we have 'lambda' calculated (in screen-space) */ - knife_interp_v3_v3v3(kcd, test_cagep, kfe->v1->cageco, kfe->v2->cageco, lambda); - - if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) { - /* check we're in the view */ - if (ED_view3d_clipping_test(kcd->vc.rv3d, test_cagep, true)) { - continue; - } + } + else { + dis_sq = dist_squared_to_line_segment_v2(sco, kfv1_sco, kfv2_sco); + if (dis_sq < curdis_sq && dis_sq < maxdist_sq) { + lambda = line_point_factor_v2(sco, kfv1_sco, kfv2_sco); + } + else { + continue; } - - cure = kfe; - curdis_sq = dis_sq; - copy_v3_v3(cur_cagep, test_cagep); } - if (fptr) { - *fptr = f; + /* now we have 'lambda' calculated (in screen-space) */ + knife_interp_v3_v3v3(kcd, test_cagep, kfe->v1->cageco, kfe->v2->cageco, lambda); + + if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) { + /* check we're in the view */ + if (ED_view3d_clipping_test(kcd->vc.rv3d, test_cagep, true)) { + continue; + } } - if (cure) { - if (!kcd->ignore_edge_snapping || !(cure->e)) { - KnifeVert *edgesnap = NULL; + cure = kfe; + curdis_sq = dis_sq; + copy_v3_v3(cur_cagep, test_cagep); + } - if (kcd->snap_midpoints) { - mid_v3_v3v3(p, cure->v1->co, cure->v2->co); - mid_v3_v3v3(cagep, cure->v1->cageco, cure->v2->cageco); - } - else { - float lambda = line_point_factor_v3(cur_cagep, cure->v1->cageco, cure->v2->cageco); - copy_v3_v3(cagep, cur_cagep); - interp_v3_v3v3(p, cure->v1->co, cure->v2->co, lambda); - } + if (cure) { + if (!kcd->ignore_edge_snapping || !(cure->e)) { + KnifeVert *edgesnap = NULL; - /* update mouse coordinates to the snapped-to edge's screen coordinates - * this is important for angle snap, which uses the previous mouse position */ - edgesnap = new_knife_vert(kcd, p, cagep); - kcd->curr.mval[0] = edgesnap->sco[0]; - kcd->curr.mval[1] = edgesnap->sco[1]; + if (kcd->snap_midpoints) { + mid_v3_v3v3(p, cure->v1->co, cure->v2->co); + mid_v3_v3v3(cagep, cure->v1->cageco, cure->v2->cageco); } else { - return NULL; + float lambda = line_point_factor_v3(cur_cagep, cure->v1->cageco, cure->v2->cageco); + copy_v3_v3(cagep, cur_cagep); + interp_v3_v3v3(p, cure->v1->co, cure->v2->co, lambda); } - } - - return cure; - } - if (fptr) { - *fptr = NULL; + /* update mouse coordinates to the snapped-to edge's screen coordinates + * this is important for angle snap, which uses the previous mouse position */ + edgesnap = new_knife_vert(kcd, p, cagep); + knife_project_v2(kcd, edgesnap->cageco, kcd->curr.mval); + } + else { + return NULL; + } } - return NULL; + return cure; } /* find a vertex near the mouse cursor, if it exists */ -static KnifeVert *knife_find_closest_vert( - KnifeTool_OpData *kcd, float p[3], float cagep[3], BMFace **fptr, bool *is_space) +static KnifeVert *knife_find_closest_vert_of_face(KnifeTool_OpData *kcd, + BMFace *f, + float p[3], + float cagep[3]) { - BMFace *f; - float co[3], cageco[3], sco[2]; + float sco[2]; float maxdist; if (kcd->is_interactive) { @@ -2112,84 +2088,58 @@ static KnifeVert *knife_find_closest_vert( maxdist = KNIFE_FLT_EPS; } - f = knife_find_closest_face(kcd, co, cageco, is_space); - - kcd->curr.bmface = f; - - if (f) { - const float maxdist_sq = maxdist * maxdist; - ListBase *lst; - Ref *ref; - KnifeVert *curv = NULL; - float dis_sq, curdis_sq = FLT_MAX; - - /* set p to co, in case we don't find anything, means a face cut */ - copy_v3_v3(p, co); - copy_v3_v3(cagep, cageco); + const float maxdist_sq = maxdist * maxdist; + ListBase *lst; + Ref *ref; + KnifeVert *curv = NULL; + float cur_kfv_sco[2]; + float dis_sq, curdis_sq = FLT_MAX; - knife_project_v2(kcd, cageco, sco); + knife_project_v2(kcd, cagep, sco); - lst = knife_get_face_kedges(kcd, f); - for (ref = lst->first; ref; ref = ref->next) { - KnifeEdge *kfe = ref->ref; - int i; + lst = knife_get_face_kedges(kcd, f); + for (ref = lst->first; ref; ref = ref->next) { + KnifeEdge *kfe = ref->ref; + int i; - for (i = 0; i < 2; i++) { - KnifeVert *kfv = i ? kfe->v2 : kfe->v1; + for (i = 0; i < 2; i++) { + KnifeVert *kfv = i ? kfe->v2 : kfe->v1; + float kfv_sco[2]; - knife_project_v2(kcd, kfv->cageco, kfv->sco); + knife_project_v2(kcd, kfv->cageco, kfv_sco); - /* be strict about angle snapping, the vertex needs to be very close to the angle, - * or we ignore */ - if (kcd->is_angle_snapping) { - if (dist_squared_to_line_segment_v2(kfv->sco, kcd->prev.mval, kcd->curr.mval) > - KNIFE_FLT_EPSBIG) { - continue; - } + /* be strict about angle snapping, the vertex needs to be very close to the angle, + * or we ignore */ + if (kcd->is_angle_snapping) { + if (dist_squared_to_line_segment_v2(kfv_sco, kcd->prev.mval, kcd->curr.mval) > + KNIFE_FLT_EPSBIG) { + continue; } + } - dis_sq = len_squared_v2v2(kfv->sco, sco); - if (dis_sq < curdis_sq && dis_sq < maxdist_sq) { - if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) { - if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true) == 0) { - curv = kfv; - curdis_sq = dis_sq; - } - } - else { - curv = kfv; - curdis_sq = dis_sq; - } + dis_sq = len_squared_v2v2(kfv_sco, sco); + if (dis_sq < curdis_sq && dis_sq < maxdist_sq) { + if (!RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) || + !ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true)) { + curv = kfv; + curdis_sq = dis_sq; + copy_v2_v2(cur_kfv_sco, kfv_sco); } } } + } - if (!kcd->ignore_vert_snapping || !(curv && curv->v)) { - if (fptr) { - *fptr = f; - } - - if (curv) { - copy_v3_v3(p, curv->co); - copy_v3_v3(cagep, curv->cageco); - - /* update mouse coordinates to the snapped-to vertex's screen coordinates - * this is important for angle snap, which uses the previous mouse position */ - kcd->curr.mval[0] = curv->sco[0]; - kcd->curr.mval[1] = curv->sco[1]; - } - - return curv; - } + if (!kcd->ignore_vert_snapping || !(curv && curv->v)) { + if (curv) { + copy_v3_v3(p, curv->co); + copy_v3_v3(cagep, curv->cageco); - if (fptr) { - *fptr = f; + /* update mouse coordinates to the snapped-to vertex's screen coordinates + * this is important for angle snap, which uses the previous mouse position */ + copy_v2_v2(kcd->curr.mval, cur_kfv_sco); } - return NULL; - } - if (fptr) { - *fptr = NULL; + return curv; } return NULL; @@ -2236,11 +2186,10 @@ static bool knife_snap_angle(KnifeTool_OpData *kcd) return true; } -/* update active knife edge/vert pointers */ -static int knife_update_active(KnifeTool_OpData *kcd) +static void knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2]) { knife_pos_data_clear(&kcd->curr); - copy_v2_v2(kcd->curr.mval, kcd->mval); + copy_v2_v2(kcd->curr.mval, mval); /* view matrix may have changed, reproject */ knife_project_v2(kcd, kcd->prev.cage, kcd->prev.mval); @@ -2252,15 +2201,26 @@ static int knife_update_active(KnifeTool_OpData *kcd) kcd->is_angle_snapping = false; } - kcd->curr.vert = knife_find_closest_vert( - kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.bmface, &kcd->curr.is_space); + kcd->curr.bmface = knife_find_closest_face( + kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.is_space); + + if (kcd->curr.bmface) { + kcd->curr.vert = knife_find_closest_vert_of_face( + kcd, kcd->curr.bmface, kcd->curr.co, kcd->curr.cage); - if (!kcd->curr.vert && - /* no edge snapping while dragging (edges are too sticky when cuts are immediate) */ - !kcd->is_drag_hold) { - kcd->curr.edge = knife_find_closest_edge( - kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.bmface, &kcd->curr.is_space); + if (!kcd->curr.vert && + /* no edge snapping while dragging (edges are too sticky when cuts are immediate) */ + !kcd->is_drag_hold) { + kcd->curr.edge = knife_find_closest_edge_of_face( + kcd, kcd->curr.bmface, kcd->curr.co, kcd->curr.cage); + } } +} + +/* update active knife edge/vert pointers */ +static int knife_update_active(KnifeTool_OpData *kcd) +{ + knife_snap_update_from_mval(kcd, kcd->mval); /* if no hits are found this would normally default to (0, 0, 0) so instead * get a point at the mouse ray closest to the previous point. diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index b60e0074512..0cc4dcef442 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -778,6 +778,7 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, UvElement *newvlist = NULL, *vlist = element_map->vert[i]; UvElement *iterv, *v, *lastv, *next; float *uv, *uv2, uvdiff[2]; + bool uv_vert_sel, uv2_vert_sel; while (vlist) { v = vlist; @@ -788,6 +789,7 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, l = v->l; luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); uv = luv->uv; + uv_vert_sel = luv->flag & MLOOPUV_VERTSEL; lastv = NULL; iterv = vlist; @@ -798,12 +800,17 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, l = iterv->l; luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); uv2 = luv->uv; + uv2_vert_sel = luv->flag & MLOOPUV_VERTSEL; sub_v2_v2v2(uvdiff, uv2, uv); - if (fabsf(uvdiff[0]) < STD_UV_CONNECT_LIMIT && fabsf(uvdiff[1]) < STD_UV_CONNECT_LIMIT && - (!use_winding || - winding[BM_elem_index_get(iterv->l->f)] == winding[BM_elem_index_get(v->l->f)])) { + /* Check if the uv loops share the same selection state (if not, they are not connected as + * they have been ripped or other edit commands have seperated them). */ + bool connected = uv_vert_sel == uv2_vert_sel && fabsf(uvdiff[0]) < STD_UV_CONNECT_LIMIT && + fabsf(uvdiff[1]) < STD_UV_CONNECT_LIMIT; + + if (connected && (!use_winding || winding[BM_elem_index_get(iterv->l->f)] == + winding[BM_elem_index_get(v->l->f)])) { if (lastv) { lastv->next = next; } @@ -1029,7 +1036,7 @@ bool EDBM_vert_color_check(BMEditMesh *em) /** \name Mirror Cache API * \{ */ -static BMVert *cache_mirr_intptr_as_bmvert(intptr_t *index_lookup, int index) +static BMVert *cache_mirr_intptr_as_bmvert(const intptr_t *index_lookup, int index) { intptr_t eve_i = index_lookup[index]; return (eve_i == -1) ? NULL : (BMVert *)eve_i; diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index bebad312454..cec425d687d 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -76,6 +76,7 @@ struct BMElem *EDBM_elem_from_selectmode(struct BMEditMesh *em, struct BMVert *eve, struct BMEdge *eed, struct BMFace *efa); + int EDBM_elem_to_index_any(struct BMEditMesh *em, struct BMElem *ele); struct BMElem *EDBM_elem_from_index_any(struct BMEditMesh *em, int index); diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index b0730b32bed..4d84db9b35b 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -297,6 +297,37 @@ static void join_mesh_single(Depsgraph *depsgraph, *mpoly_pp += me->totpoly; } +/* Face Sets IDs are a sparse sequence, so this function offsets all the IDs by face_set_offset and + * updates face_set_offset with the maximum ID value. This way, when used in multiple meshes, all + * of them will have different IDs for their Face Sets. */ +static void mesh_join_offset_face_sets_ID(const Mesh *mesh, int *face_set_offset) +{ + if (!mesh->totpoly) { + return; + } + + int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + if (!face_sets) { + return; + } + + int max_face_set = 0; + for (int f = 0; f < mesh->totpoly; f++) { + /* As face sets encode the visibility in the integer sign, the offset needs to be added or + * subtracted depending on the initial sign of the integer to get the new ID. */ + if (abs(face_sets[f]) <= *face_set_offset) { + if (face_sets[f] > 0) { + face_sets[f] += *face_set_offset; + } + else { + face_sets[f] -= *face_set_offset; + } + } + max_face_set = max_ii(max_face_set, abs(face_sets[f])); + } + *face_set_offset = max_face_set; +} + int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -431,7 +462,13 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) key->type = KEY_RELATIVE; } - /* First pass over objects: Copying materials, vertex-groups & face-maps across. */ + /* Update face_set_id_offset with the face set data in the active object first. This way the Face + * Sets IDs in the active object are not the ones that are modified. */ + Mesh *mesh_active = BKE_mesh_from_object(ob); + int face_set_id_offset = 0; + mesh_join_offset_face_sets_ID(mesh_active, &face_set_id_offset); + + /* Copy materials, vertex-groups, face sets & face-maps across objects. */ CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) { /* only act if a mesh, and not the one we're joining to */ if ((ob != ob_iter) && (ob_iter->type == OB_MESH)) { @@ -463,6 +500,8 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) ob->actfmap = 1; } + mesh_join_offset_face_sets_ID(me, &face_set_id_offset); + if (me->totvert) { /* Add this object's materials to the base one's if they don't exist already * (but only if limits not exceeded yet) */ diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 3618eae7db9..90ea71ae5c5 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -670,12 +670,12 @@ static bool edit_constraint_poll_generic(bContext *C, StructRNA *rna_type) if (!ob) { CTX_wm_operator_poll_msg_set(C, "Context missing active object"); - return 0; + return false; } if (ID_IS_LINKED(ob) || (ptr.owner_id && ID_IS_LINKED(ptr.owner_id))) { CTX_wm_operator_poll_msg_set(C, "Cannot edit library data"); - return 0; + return false; } if (ID_IS_OVERRIDE_LIBRARY(ob) && ptr.data != NULL) { @@ -683,7 +683,7 @@ static bool edit_constraint_poll_generic(bContext *C, StructRNA *rna_type) return (((bConstraint *)ptr.data)->flag & CONSTRAINT_OVERRIDE_LIBRARY_LOCAL) != 0; } - return 1; + return true; } static bool edit_constraint_poll(bContext *C) @@ -702,7 +702,7 @@ static void edit_constraint_properties(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN); } -static int edit_constraint_invoke_properties(bContext *C, wmOperator *op) +static bool edit_constraint_invoke_properties(bContext *C, wmOperator *op) { PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint); Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); @@ -711,7 +711,7 @@ static int edit_constraint_invoke_properties(bContext *C, wmOperator *op) if (RNA_struct_property_is_set(op->ptr, "constraint") && RNA_struct_property_is_set(op->ptr, "owner")) { - return 1; + return true; } if (ptr.data) { @@ -727,10 +727,10 @@ static int edit_constraint_invoke_properties(bContext *C, wmOperator *op) RNA_enum_set(op->ptr, "owner", EDIT_CONSTRAINT_OWNER_BONE); } - return 1; + return true; } - return 0; + return false; } static bConstraint *edit_constraint_property_get(wmOperator *op, Object *ob, int type) @@ -1683,7 +1683,7 @@ void OBJECT_OT_constraints_clear(wmOperatorType *ot) /* callbacks */ ot->exec = object_constraints_clear_exec; - ot->poll = ED_operator_object_active_editable; + ot->poll = ED_operator_object_active_local_editable; } /** \} */ diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index a9eb454eb04..04113f70e52 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -895,7 +895,7 @@ void ED_object_check_force_modifiers(Main *bmain, Scene *scene, Object *object) else { if (!pd || (pd->shape != PFIELD_SHAPE_SURFACE) || ELEM(pd->forcefield, 0, PFIELD_GUIDE, PFIELD_TEXTURE)) { - ED_object_modifier_remove(NULL, bmain, object, md); + ED_object_modifier_remove(NULL, bmain, scene, object, md); } } } diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index afc87c0caba..9dc204b9083 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -167,6 +167,7 @@ void OBJECT_OT_modifier_move_up(struct wmOperatorType *ot); void OBJECT_OT_modifier_move_down(struct wmOperatorType *ot); void OBJECT_OT_modifier_move_to_index(struct wmOperatorType *ot); void OBJECT_OT_modifier_apply(struct wmOperatorType *ot); +void OBJECT_OT_modifier_apply_as_shapekey(wmOperatorType *ot); void OBJECT_OT_modifier_convert(struct wmOperatorType *ot); void OBJECT_OT_modifier_copy(struct wmOperatorType *ot); void OBJECT_OT_multires_subdivide(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 74109563929..6f254ea9400 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -330,10 +330,8 @@ static bool object_modifier_safe_to_delete(Main *bmain, !ED_object_iter_other(bmain, ob, false, object_has_modifier_cb, &type)); } -static bool object_modifier_remove(Main *bmain, - Object *ob, - ModifierData *md, - bool *r_sort_depsgraph) +static bool object_modifier_remove( + Main *bmain, Scene *scene, Object *ob, ModifierData *md, bool *r_sort_depsgraph) { /* It seems on rapid delete it is possible to * get called twice on same modifier, so make @@ -344,11 +342,8 @@ static bool object_modifier_remove(Main *bmain, /* special cases */ if (md->type == eModifierType_ParticleSystem) { - ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; - - BLI_remlink(&ob->particlesystem, psmd->psys); - psys_free(ob, psmd->psys); - psmd->psys = NULL; + object_remove_particle_system(bmain, scene, ob); + return true; } else if (md->type == eModifierType_Softbody) { if (ob->soft) { @@ -391,12 +386,13 @@ static bool object_modifier_remove(Main *bmain, return 1; } -bool ED_object_modifier_remove(ReportList *reports, Main *bmain, Object *ob, ModifierData *md) +bool ED_object_modifier_remove( + ReportList *reports, Main *bmain, Scene *scene, Object *ob, ModifierData *md) { bool sort_depsgraph = false; bool ok; - ok = object_modifier_remove(bmain, ob, md, &sort_depsgraph); + ok = object_modifier_remove(bmain, scene, ob, md, &sort_depsgraph); if (!ok) { BKE_reportf(reports, RPT_ERROR, "Modifier '%s' not in object '%s'", md->name, ob->id.name); @@ -409,7 +405,7 @@ bool ED_object_modifier_remove(ReportList *reports, Main *bmain, Object *ob, Mod return 1; } -void ED_object_modifier_clear(Main *bmain, Object *ob) +void ED_object_modifier_clear(Main *bmain, Scene *scene, Object *ob) { ModifierData *md = ob->modifiers.first; bool sort_depsgraph = false; @@ -423,7 +419,7 @@ void ED_object_modifier_clear(Main *bmain, Object *ob) next_md = md->next; - object_modifier_remove(bmain, ob, md, &sort_depsgraph); + object_modifier_remove(bmain, scene, ob, md, &sort_depsgraph); md = next_md; } @@ -822,7 +818,8 @@ bool ED_object_modifier_apply(Main *bmain, Scene *scene, Object *ob, ModifierData *md, - int mode) + int mode, + bool keep_modifier) { int prev_mode; @@ -830,7 +827,7 @@ bool ED_object_modifier_apply(Main *bmain, BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied in edit mode"); return false; } - if (ID_REAL_USERS(ob->data) > 1) { + if (mode != MODIFIER_APPLY_SHAPE && ID_REAL_USERS(ob->data) > 1) { BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data"); return false; } @@ -869,22 +866,34 @@ bool ED_object_modifier_apply(Main *bmain, } md_eval->mode = prev_mode; - BLI_remlink(&ob->modifiers, md); - BKE_modifier_free(md); + + if (!keep_modifier) { + BLI_remlink(&ob->modifiers, md); + BKE_modifier_free(md); + } BKE_object_free_derived_caches(ob); return true; } -int ED_object_modifier_copy(ReportList *UNUSED(reports), Object *ob, ModifierData *md) +int ED_object_modifier_copy( + ReportList *UNUSED(reports), Main *bmain, Scene *scene, Object *ob, ModifierData *md) { ModifierData *nmd; - nmd = BKE_modifier_new(md->type); - BKE_modifier_copydata(md, nmd); - BLI_insertlinkafter(&ob->modifiers, md, nmd); - BKE_modifier_unique_name(&ob->modifiers, nmd); + if (md->type == eModifierType_ParticleSystem) { + nmd = object_copy_particle_system(bmain, scene, ob, ((ParticleSystemModifierData *)md)->psys); + BLI_remlink(&ob->modifiers, nmd); + BLI_insertlinkafter(&ob->modifiers, md, nmd); + return true; + } + else { + nmd = BKE_modifier_new(md->type); + BKE_modifier_copydata(md, nmd); + BLI_insertlinkafter(&ob->modifiers, md, nmd); + BKE_modifier_unique_name(&ob->modifiers, nmd); + } return 1; } @@ -1116,6 +1125,7 @@ ModifierData *edit_modifier_property_get(wmOperator *op, Object *ob, int type) static int modifier_remove_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = ED_object_active_context(C); ModifierData *md = edit_modifier_property_get(op, ob, 0); @@ -1129,7 +1139,7 @@ static int modifier_remove_exec(bContext *C, wmOperator *op) char name[MAX_NAME]; strcpy(name, md->name); - if (!ED_object_modifier_remove(op->reports, bmain, ob, md)) { + if (!ED_object_modifier_remove(op->reports, bmain, scene, ob, md)) { return OPERATOR_CANCELLED; } @@ -1326,7 +1336,7 @@ void OBJECT_OT_modifier_move_to_index(wmOperatorType *ot) /** \name Apply Modifier Operator * \{ */ -static bool modifier_apply_poll(bContext *C) +static bool modifier_apply_poll_ex(bContext *C, bool allow_shared) { if (!edit_modifier_poll_generic(C, &RNA_Modifier, 0, false)) { return false; @@ -1341,7 +1351,7 @@ static bool modifier_apply_poll(bContext *C) CTX_wm_operator_poll_msg_set(C, "Modifiers cannot be applied on override data"); return false; } - if ((ob->data != NULL) && ID_REAL_USERS(ob->data) > 1) { + if (!allow_shared && (ob->data != NULL) && ID_REAL_USERS(ob->data) > 1) { CTX_wm_operator_poll_msg_set(C, "Modifiers cannot be applied to multi-user data"); return false; } @@ -1356,14 +1366,18 @@ static bool modifier_apply_poll(bContext *C) return true; } -static int modifier_apply_exec(bContext *C, wmOperator *op) +static bool modifier_apply_poll(bContext *C) +{ + return modifier_apply_poll_ex(C, false); +} + +static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, bool keep_modifier) { Main *bmain = CTX_data_main(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); Object *ob = ED_object_active_context(C); ModifierData *md = edit_modifier_property_get(op, ob, 0); - int apply_as = RNA_enum_get(op->ptr, "apply_as"); if (md == NULL) { return OPERATOR_CANCELLED; @@ -1373,7 +1387,8 @@ static int modifier_apply_exec(bContext *C, wmOperator *op) char name[MAX_NAME]; strcpy(name, md->name); - if (!ED_object_modifier_apply(bmain, op->reports, depsgraph, scene, ob, md, apply_as)) { + if (!ED_object_modifier_apply( + bmain, op->reports, depsgraph, scene, ob, md, apply_as, keep_modifier)) { return OPERATOR_CANCELLED; } @@ -1388,6 +1403,11 @@ static int modifier_apply_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static int modifier_apply_exec(bContext *C, wmOperator *op) +{ + return modifier_apply_exec_ex(C, op, MODIFIER_APPLY_DATA, false); +} + static int modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int retval; @@ -1397,16 +1417,6 @@ static int modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *eve return retval; } -static const EnumPropertyItem modifier_apply_as_items[] = { - {MODIFIER_APPLY_DATA, "DATA", 0, "Object Data", "Apply modifier to the object's data"}, - {MODIFIER_APPLY_SHAPE, - "SHAPE", - 0, - "New Shape", - "Apply deform-only modifier to a new shape on this object"}, - {0, NULL, 0, NULL, NULL}, -}; - void OBJECT_OT_modifier_apply(wmOperatorType *ot) { ot->name = "Apply Modifier"; @@ -1420,12 +1430,68 @@ void OBJECT_OT_modifier_apply(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - RNA_def_enum(ot->srna, - "apply_as", - modifier_apply_as_items, - MODIFIER_APPLY_DATA, - "Apply as", - "How to apply the modifier to the geometry"); + edit_modifier_properties(ot); + edit_modifier_report_property(ot); +} + +/** \} */ + +/* ------------------------------------------------------------------- */ +/** \name Apply Modifier As Shapekey Operator + * \{ */ + +static bool modifier_apply_as_shapekey_poll(bContext *C) +{ + return modifier_apply_poll_ex(C, true); +} + +static int modifier_apply_as_shapekey_exec(bContext *C, wmOperator *op) +{ + bool keep = RNA_boolean_get(op->ptr, "keep_modifier"); + + return modifier_apply_exec_ex(C, op, MODIFIER_APPLY_SHAPE, keep); +} + +static int modifier_apply_as_shapekey_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int retval; + if (edit_modifier_invoke_properties(C, op, event, &retval)) { + return modifier_apply_as_shapekey_exec(C, op); + } + else { + return retval; + } +} + +static char *modifier_apply_as_shapekey_get_description(struct bContext *UNUSED(C), + struct wmOperatorType *UNUSED(op), + struct PointerRNA *values) +{ + bool keep = RNA_boolean_get(values, "keep_modifier"); + + if (keep) { + return BLI_strdup("Apply modifier as a new shapekey and keep it in the stack"); + } + + return NULL; +} + +void OBJECT_OT_modifier_apply_as_shapekey(wmOperatorType *ot) +{ + ot->name = "Apply Modifier As Shapekey"; + ot->description = "Apply modifier as a new shapekey and remove from the stack"; + ot->idname = "OBJECT_OT_modifier_apply_as_shapekey"; + + ot->invoke = modifier_apply_as_shapekey_invoke; + ot->exec = modifier_apply_as_shapekey_exec; + ot->poll = modifier_apply_as_shapekey_poll; + ot->get_description = modifier_apply_as_shapekey_get_description; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + + RNA_def_boolean( + ot->srna, "keep_modifier", false, "Keep Modifier", "Do not remove the modifier from stack"); edit_modifier_properties(ot); edit_modifier_report_property(ot); } @@ -1487,10 +1553,12 @@ void OBJECT_OT_modifier_convert(wmOperatorType *ot) static int modifier_copy_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); Object *ob = ED_object_active_context(C); ModifierData *md = edit_modifier_property_get(op, ob, 0); - if (!md || !ED_object_modifier_copy(op->reports, ob, md)) { + if (!md || !ED_object_modifier_copy(op->reports, bmain, scene, ob, md)) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index e28bbb3fb1c..92880e5a114 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -130,6 +130,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_modifier_move_down); WM_operatortype_append(OBJECT_OT_modifier_move_to_index); WM_operatortype_append(OBJECT_OT_modifier_apply); + WM_operatortype_append(OBJECT_OT_modifier_apply_as_shapekey); WM_operatortype_append(OBJECT_OT_modifier_convert); WM_operatortype_append(OBJECT_OT_modifier_copy); WM_operatortype_append(OBJECT_OT_multires_subdivide); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index d37f03b7f55..945c7e87eb1 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1878,7 +1878,7 @@ static void single_obdata_users( /* Needed to remap texcomesh below. */ me = ob->data = ID_NEW_SET(ob->data, BKE_mesh_copy(bmain, ob->data)); if (me->key) { /* We do not need to set me->key->id.newid here... */ - BKE_animdata_copy_id_action(bmain, (ID *)me->key, false); + BKE_animdata_copy_id_action(bmain, (ID *)me->key); } break; case OB_MBALL: @@ -1891,13 +1891,13 @@ static void single_obdata_users( ID_NEW_REMAP(cu->bevobj); ID_NEW_REMAP(cu->taperobj); if (cu->key) { /* We do not need to set cu->key->id.newid here... */ - BKE_animdata_copy_id_action(bmain, (ID *)cu->key, false); + BKE_animdata_copy_id_action(bmain, (ID *)cu->key); } break; case OB_LATTICE: ob->data = lat = ID_NEW_SET(ob->data, BKE_lattice_copy(bmain, ob->data)); if (lat->key) { /* We do not need to set lat->key->id.newid here... */ - BKE_animdata_copy_id_action(bmain, (ID *)lat->key, false); + BKE_animdata_copy_id_action(bmain, (ID *)lat->key); } break; case OB_ARMATURE: @@ -1937,7 +1937,7 @@ static void single_obdata_users( * AnimData structure, which is not what we want. * (sergey) */ - BKE_animdata_copy_id_action(bmain, (ID *)ob->data, false); + BKE_animdata_copy_id_action(bmain, (ID *)ob->data); id_us_min(id); } @@ -1958,7 +1958,7 @@ static void single_object_action_users( FOREACH_OBJECT_FLAG_BEGIN (scene, view_layer, v3d, flag, ob) { if (!ID_IS_LINKED(ob)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - BKE_animdata_copy_id_action(bmain, &ob->id, false); + BKE_animdata_copy_id_action(bmain, &ob->id); } } FOREACH_OBJECT_FLAG_END; @@ -1980,7 +1980,7 @@ static void single_mat_users( if (ma->id.us > 1) { man = BKE_material_copy(bmain, ma); - BKE_animdata_copy_id_action(bmain, &man->id, false); + BKE_animdata_copy_id_action(bmain, &man->id); man->id.us = 0; BKE_object_material_assign(bmain, ob, man, a, BKE_MAT_ASSIGN_USERPREF); @@ -2242,28 +2242,6 @@ void OBJECT_OT_make_local(wmOperatorType *ot) /** \name Make Library Override Operator * \{ */ -static bool make_override_hierarchy_recursive_tag(Main *bmain, ID *id) -{ - MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->id_user_to_used, id); - - /* This way we won't process again that ID should we encounter it again through another - * relationship hierarchy. - * Note that this does not free any memory from relations, so we can still use the entries. - */ - BKE_main_relations_ID_remove(bmain, id); - - for (; entry != NULL; entry = entry->next) { - /* We only consider IDs from the same library. */ - if (entry->id_pointer != NULL && (*entry->id_pointer)->lib == id->lib) { - if (make_override_hierarchy_recursive_tag(bmain, *entry->id_pointer)) { - id->tag |= LIB_TAG_DOIT; - } - } - } - - return (id->tag & LIB_TAG_DOIT) != 0; -} - static int make_override_tag_ids_cb(LibraryIDLinkCallbackData *cb_data) { if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK)) { @@ -2315,14 +2293,9 @@ static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEve return OPERATOR_CANCELLED; } - /* Get object to work on - use a menu if we need to... */ - if (!ID_IS_LINKED(obact) && obact->instance_collection != NULL && - ID_IS_LINKED(obact->instance_collection)) { - /* Gives menu with list of objects in group. */ - WM_enum_search_invoke(C, op, event); - return OPERATOR_CANCELLED; - } - if (ID_IS_LINKED(obact)) { + if ((!ID_IS_LINKED(obact) && obact->instance_collection != NULL && + ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection)) || + ID_IS_OVERRIDABLE_LIBRARY(obact)) { uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("OK?"), ICON_QUESTION); uiLayout *layout = UI_popup_menu_layout(pup); @@ -2337,6 +2310,11 @@ static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEve /* This invoke just calls another instance of this operator... */ return OPERATOR_INTERFACE; } + else if (ID_IS_LINKED(obact)) { + /* Show menu with list of directly linked collections containing the active object. */ + WM_enum_search_invoke(C, op, event); + return OPERATOR_CANCELLED; + } /* Error.. cannot continue. */ BKE_report(op->reports, @@ -2366,11 +2344,28 @@ static int make_override_library_exec(bContext *C, wmOperator *op) id_root = &obact->instance_collection->id; } else if (!ID_IS_OVERRIDABLE_LIBRARY(obact)) { - BKE_reportf(op->reports, - RPT_ERROR_INVALID_INPUT, - "Active object '%s' is not overridable", - obact->id.name + 2); - return OPERATOR_CANCELLED; + const int i = RNA_property_enum_get(op->ptr, op->type->prop); + const uint collection_session_uuid = *((uint *)&i); + if (collection_session_uuid == MAIN_ID_SESSION_UUID_UNSET) { + BKE_reportf(op->reports, + RPT_ERROR_INVALID_INPUT, + "Active object '%s' is not overridable", + obact->id.name + 2); + return OPERATOR_CANCELLED; + } + + Collection *collection = BLI_listbase_bytes_find(&bmain->collections, + &collection_session_uuid, + sizeof(collection_session_uuid), + offsetof(ID, session_uuid)); + if (!ID_IS_OVERRIDABLE_LIBRARY(collection)) { + BKE_reportf(op->reports, + RPT_ERROR_INVALID_INPUT, + "Could not find an overridable collection containing object '%s'", + obact->id.name + 2); + return OPERATOR_CANCELLED; + } + id_root = &collection->id; } /* Else, poll func ensures us that ID_IS_LINKED(obact) is true. */ else { @@ -2399,12 +2394,8 @@ static int make_override_library_exec(bContext *C, wmOperator *op) } } - /* Then we tag all intermediary data-blocks in-between two overridden ones (e.g. if a shapekey - * has a driver using an armature object's bone, we need to override the shapekey/obdata, the - * objects using them, etc.) */ - make_override_hierarchy_recursive_tag(bmain, id_root); - - BKE_main_relations_free(bmain); + /* Note that this call will also free the main relations data we created above. */ + BKE_lib_override_library_dependencies_tag(bmain, id_root, LIB_TAG_DOIT, false); ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { @@ -2523,10 +2514,40 @@ static bool make_override_library_poll(bContext *C) Object *obact = CTX_data_active_object(C); /* Object must be directly linked to be overridable. */ - return (BKE_lib_override_library_is_enabled() && ED_operator_objectmode(C) && obact != NULL && - ((ID_IS_LINKED(obact) && obact->id.tag & LIB_TAG_EXTERN) || - (!ID_IS_LINKED(obact) && obact->instance_collection != NULL && - ID_IS_LINKED(obact->instance_collection)))); + return (ED_operator_objectmode(C) && obact != NULL && + (ID_IS_LINKED(obact) || (obact->instance_collection != NULL && + ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection)))); +} + +static const EnumPropertyItem *make_override_collections_of_linked_object_itemf( + bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +{ + EnumPropertyItem item_tmp = {0}, *item = NULL; + int totitem = 0; + + Object *object = ED_object_active_context(C); + Main *bmain = CTX_data_main(C); + + if (!object || !ID_IS_LINKED(object)) { + return DummyRNA_DEFAULT_items; + } + + LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { + /* Only check for directly linked collections. */ + if (!ID_IS_LINKED(&collection->id) || (collection->id.tag & LIB_TAG_INDIRECT) != 0) { + continue; + } + if (BKE_collection_has_object_recursive(collection, object)) { + item_tmp.identifier = item_tmp.name = collection->id.name + 2; + item_tmp.value = *((int *)&collection->id.session_uuid); + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; } void OBJECT_OT_make_override_library(wmOperatorType *ot) @@ -2547,12 +2568,13 @@ void OBJECT_OT_make_override_library(wmOperatorType *ot) /* properties */ PropertyRNA *prop; prop = RNA_def_enum(ot->srna, - "object", + "collection", DummyRNA_DEFAULT_items, - 0, - "Override Object", - "Name of lib-linked/collection object to make an override from"); - RNA_def_enum_funcs(prop, proxy_collection_object_itemf); + MAIN_ID_SESSION_UUID_UNSET, + "Override Collection", + "Name of directly linked collection containing the selected object, to make " + "an override from"); + RNA_def_enum_funcs(prop, make_override_collections_of_linked_object_itemf); RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); ot->prop = prop; } diff --git a/source/blender/editors/object/object_shader_fx.c b/source/blender/editors/object/object_shader_fx.c index 8df87a8a2f9..977d4abd4d4 100644 --- a/source/blender/editors/object/object_shader_fx.c +++ b/source/blender/editors/object/object_shader_fx.c @@ -24,6 +24,7 @@ #include <math.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include "MEM_guardedalloc.h" @@ -55,6 +56,8 @@ #include "ED_object.h" #include "ED_screen.h" +#include "UI_interface.h" + #include "WM_api.h" #include "WM_types.h" @@ -247,7 +250,7 @@ static int shaderfx_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob); return OPERATOR_FINISHED; } @@ -360,21 +363,57 @@ static void edit_shaderfx_properties(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN); } -static int edit_shaderfx_invoke_properties(bContext *C, wmOperator *op) +static void edit_shaderfx_report_property(wmOperatorType *ot) { - ShaderFxData *fx; + PropertyRNA *prop = RNA_def_boolean( + ot->srna, "report", false, "Report", "Create a notification after the operation"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} +/** + * \param event: If this isn't NULL, the operator will also look for panels underneath + * the cursor with customdata set to a modifier. + * \param r_retval: This should be used if #event is used in order to to return + * #OPERATOR_PASS_THROUGH to check other operators with the same key set. + */ +static bool edit_shaderfx_invoke_properties(bContext *C, + wmOperator *op, + const wmEvent *event, + int *r_retval) +{ if (RNA_struct_property_is_set(op->ptr, "shaderfx")) { return true; } - PointerRNA ptr = CTX_data_pointer_get_type(C, "shaderfx", &RNA_ShaderFx); - if (ptr.data) { - fx = ptr.data; + PointerRNA ctx_ptr = CTX_data_pointer_get_type(C, "shaderfx", &RNA_ShaderFx); + if (ctx_ptr.data != NULL) { + ShaderFxData *fx = ctx_ptr.data; RNA_string_set(op->ptr, "shaderfx", fx->name); return true; } + /* Check the custom data of panels under the mouse for an effect. */ + if (event != NULL) { + PointerRNA *panel_ptr = UI_region_panel_custom_data_under_cursor(C, event); + + if (!(panel_ptr == NULL || RNA_pointer_is_null(panel_ptr))) { + if (RNA_struct_is_a(panel_ptr->type, &RNA_ShaderFx)) { + ShaderFxData *fx = panel_ptr->data; + RNA_string_set(op->ptr, "shaderfx", fx->name); + return true; + } + + BLI_assert(r_retval != NULL); /* We need the return value in this case. */ + if (r_retval != NULL) { + *r_retval = (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED); + } + return false; + } + } + + if (r_retval != NULL) { + *r_retval = OPERATOR_CANCELLED; + } return false; } @@ -403,21 +442,30 @@ static int shaderfx_remove_exec(bContext *C, wmOperator *op) Object *ob = ED_object_active_context(C); ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0); + /* Store name temporarily for report. */ + char name[MAX_NAME]; + strcpy(name, fx->name); + if (!fx || !ED_object_shaderfx_remove(op->reports, bmain, ob, fx)) { return OPERATOR_CANCELLED; } - WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + if (RNA_boolean_get(op->ptr, "report")) { + BKE_reportf(op->reports, RPT_INFO, "Removed effect: %s", name); + } + + WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob); return OPERATOR_FINISHED; } -static int shaderfx_remove_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +static int shaderfx_remove_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - if (edit_shaderfx_invoke_properties(C, op)) { + int retval; + if (edit_shaderfx_invoke_properties(C, op, event, &retval)) { return shaderfx_remove_exec(C, op); } - return OPERATOR_CANCELLED; + return retval; } void OBJECT_OT_shaderfx_remove(wmOperatorType *ot) @@ -433,6 +481,7 @@ void OBJECT_OT_shaderfx_remove(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; edit_shaderfx_properties(ot); + edit_shaderfx_report_property(ot); } /************************ move up shaderfx operator *********************/ @@ -447,17 +496,18 @@ static int shaderfx_move_up_exec(bContext *C, wmOperator *op) } DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob); return OPERATOR_FINISHED; } -static int shaderfx_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +static int shaderfx_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - if (edit_shaderfx_invoke_properties(C, op)) { + int retval; + if (edit_shaderfx_invoke_properties(C, op, event, &retval)) { return shaderfx_move_up_exec(C, op); } - return OPERATOR_CANCELLED; + return retval; } void OBJECT_OT_shaderfx_move_up(wmOperatorType *ot) @@ -487,17 +537,18 @@ static int shaderfx_move_down_exec(bContext *C, wmOperator *op) } DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob); return OPERATOR_FINISHED; } -static int shaderfx_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +static int shaderfx_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - if (edit_shaderfx_invoke_properties(C, op)) { + int retval; + if (edit_shaderfx_invoke_properties(C, op, event, &retval)) { return shaderfx_move_down_exec(C, op); } - return OPERATOR_CANCELLED; + return retval; } void OBJECT_OT_shaderfx_move_down(wmOperatorType *ot) @@ -533,17 +584,18 @@ static int shaderfx_move_to_index_exec(bContext *C, wmOperator *op) } DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob); return OPERATOR_FINISHED; } -static int shaderfx_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +static int shaderfx_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - if (edit_shaderfx_invoke_properties(C, op)) { + int retval; + if (edit_shaderfx_invoke_properties(C, op, event, &retval)) { return shaderfx_move_to_index_exec(C, op); } - return OPERATOR_CANCELLED; + return retval; } void OBJECT_OT_shaderfx_move_to_index(wmOperatorType *ot) diff --git a/source/blender/editors/physics/dynamicpaint_ops.c b/source/blender/editors/physics/dynamicpaint_ops.c index 6922a03b12f..381bf317bee 100644 --- a/source/blender/editors/physics/dynamicpaint_ops.c +++ b/source/blender/editors/physics/dynamicpaint_ops.c @@ -102,7 +102,7 @@ void DPAINT_OT_surface_slot_add(wmOperatorType *ot) /* api callbacks */ ot->exec = surface_slot_add_exec; - ot->poll = ED_operator_object_active_editable; + ot->poll = ED_operator_object_active_local_editable; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -151,7 +151,7 @@ void DPAINT_OT_surface_slot_remove(wmOperatorType *ot) /* api callbacks */ ot->exec = surface_slot_remove_exec; - ot->poll = ED_operator_object_active_editable; + ot->poll = ED_operator_object_active_local_editable; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -203,7 +203,7 @@ void DPAINT_OT_type_toggle(wmOperatorType *ot) /* api callbacks */ ot->exec = type_toggle_exec; - ot->poll = ED_operator_object_active_editable; + ot->poll = ED_operator_object_active_local_editable; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -286,7 +286,7 @@ void DPAINT_OT_output_toggle(wmOperatorType *ot) /* api callbacks */ ot->exec = output_toggle_exec; - ot->poll = ED_operator_object_active_editable; + ot->poll = ED_operator_object_active_local_editable; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -538,5 +538,5 @@ void DPAINT_OT_bake(wmOperatorType *ot) /* api callbacks */ ot->exec = dynamicpaint_bake_exec; - ot->poll = ED_operator_object_active_editable; + ot->poll = ED_operator_object_active_local_editable; } diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c index e75169a476b..41e30adf724 100644 --- a/source/blender/editors/physics/particle_object.c +++ b/source/blender/editors/physics/particle_object.c @@ -104,7 +104,7 @@ void OBJECT_OT_particle_system_add(wmOperatorType *ot) ot->description = "Add a particle system"; /* api callbacks */ - ot->poll = ED_operator_object_active_editable; + ot->poll = ED_operator_object_active_local_editable; ot->exec = particle_system_add_exec; /* flags */ @@ -151,7 +151,7 @@ void OBJECT_OT_particle_system_remove(wmOperatorType *ot) ot->description = "Remove the selected particle system"; /* api callbacks */ - ot->poll = ED_operator_object_active_editable; + ot->poll = ED_operator_object_active_local_editable; ot->exec = particle_system_remove_exec; /* flags */ @@ -1210,7 +1210,7 @@ static bool copy_particle_systems_to_object(const bContext *C, static bool copy_particle_systems_poll(bContext *C) { Object *ob; - if (!ED_operator_object_active_editable(C)) { + if (!ED_operator_object_active_local_editable(C)) { return false; } @@ -1311,7 +1311,7 @@ void PARTICLE_OT_copy_particle_systems(wmOperatorType *ot) static bool duplicate_particle_systems_poll(bContext *C) { - if (!ED_operator_object_active_editable(C)) { + if (!ED_operator_object_active_local_editable(C)) { return false; } Object *ob = ED_object_active_context(C); diff --git a/source/blender/editors/physics/physics_fluid.c b/source/blender/editors/physics/physics_fluid.c index 583c68cb284..26b5f7fb2af 100644 --- a/source/blender/editors/physics/physics_fluid.c +++ b/source/blender/editors/physics/physics_fluid.c @@ -546,6 +546,7 @@ static int fluid_bake_exec(struct bContext *C, struct wmOperator *op) return OPERATOR_CANCELLED; } if (!fluid_validatepaths(job, op->reports)) { + fluid_bake_free(job); return OPERATOR_CANCELLED; } WM_report_banners_cancel(job->bmain); @@ -574,6 +575,7 @@ static int fluid_bake_invoke(struct bContext *C, } if (!fluid_validatepaths(job, op->reports)) { + fluid_bake_free(job); return OPERATOR_CANCELLED; } @@ -651,6 +653,7 @@ static int fluid_free_exec(struct bContext *C, struct wmOperator *op) job->name = op->type->name; if (!fluid_validatepaths(job, op->reports)) { + fluid_bake_free(job); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 754f8226684..149bae718a0 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -162,7 +162,7 @@ void OBJECT_OT_material_slot_add(wmOperatorType *ot) /* api callbacks */ ot->exec = material_slot_add_exec; - ot->poll = ED_operator_object_active_editable; + ot->poll = ED_operator_object_active_local_editable; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; @@ -207,7 +207,7 @@ void OBJECT_OT_material_slot_remove(wmOperatorType *ot) /* api callbacks */ ot->exec = material_slot_remove_exec; - ot->poll = ED_operator_object_active_editable; + ot->poll = ED_operator_object_active_local_editable; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; @@ -310,7 +310,7 @@ void OBJECT_OT_material_slot_assign(wmOperatorType *ot) /* api callbacks */ ot->exec = material_slot_assign_exec; - ot->poll = ED_operator_object_active_editable; + ot->poll = ED_operator_object_active_local_editable; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; @@ -564,6 +564,7 @@ void OBJECT_OT_material_slot_move(wmOperatorType *ot) ot->description = "Move the active material up/down in the list"; /* api callbacks */ + ot->poll = ED_operator_object_active_local_editable; ot->exec = material_slot_move_exec; /* flags */ @@ -638,7 +639,7 @@ void OBJECT_OT_material_slot_remove_unused(wmOperatorType *ot) /* api callbacks */ ot->exec = material_slot_remove_unused_exec; - ot->poll = ED_operator_object_active_editable; + ot->poll = ED_operator_object_active_local_editable; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -707,6 +708,7 @@ void MATERIAL_OT_new(wmOperatorType *ot) ot->description = "Add a new material"; /* api callbacks */ + ot->poll = ED_operator_object_active_local_editable; ot->exec = new_material_exec; /* flags */ diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index f32dd0c8703..b034fb186d2 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -360,6 +360,13 @@ bool ED_operator_object_active_editable(bContext *C) return ED_operator_object_active_editable_ex(C, ob); } +/** Object must be editable and fully local (i.e. not an override). */ +bool ED_operator_object_active_local_editable(bContext *C) +{ + Object *ob = ED_object_active_context(C); + return ED_operator_object_active_editable_ex(C, ob) && !ID_IS_OVERRIDE_LIBRARY(ob); +} + bool ED_operator_object_active_editable_mesh(bContext *C) { Object *ob = ED_object_active_context(C); diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 892aea6048f..be7b824fc3e 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -875,8 +875,12 @@ static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups, return alpha_overlay_active; } -BLI_INLINE void draw_tri_point( - uint pos, const float sel_col[4], float pivot_col[4], float *co, float width, bool selected) +BLI_INLINE void draw_tri_point(uint pos, + const float sel_col[4], + const float pivot_col[4], + float *co, + float width, + bool selected) { immUniformColor4fv(selected ? sel_col : pivot_col); @@ -905,8 +909,12 @@ BLI_INLINE void draw_tri_point( immEnd(); } -BLI_INLINE void draw_rect_point( - uint pos, const float sel_col[4], float handle_col[4], float *co, float width, bool selected) +BLI_INLINE void draw_rect_point(uint pos, + const float sel_col[4], + const float handle_col[4], + const float *co, + float width, + bool selected) { immUniformColor4fv(selected ? sel_col : handle_col); diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 16ccfaf8286..a7f09390a3d 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -257,7 +257,7 @@ static ushort *brush_painter_mask_ibuf_new(BrushPainter *painter, const int size /* update rectangular section of the brush image */ static void brush_painter_mask_imbuf_update(BrushPainter *painter, ImagePaintTile *tile, - ushort *tex_mask_old, + const ushort *tex_mask_old, int origx, int origy, int w, @@ -1052,7 +1052,7 @@ static void paint_2d_lift_soften(ImagePaintState *s, ImagePaintTile *tile, ImBuf *ibuf, ImBuf *ibufb, - int *pos, + const int *pos, const short paint_tile) { bool sharpen = (tile->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0)); @@ -1255,7 +1255,7 @@ static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos, short paint } } -static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos) +static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, const int *pos) { /* note: allocImbuf returns zero'd memory, so regions outside image will * have zero alpha, and hence not be blended onto the image */ diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 83620b4bc56..5af3a3f4241 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -1426,7 +1426,7 @@ static void insert_seam_vert_array(const ProjPaintState *ps, * Be tricky with flags, first 4 bits are #PROJ_FACE_SEAM0 to 4, * last 4 bits are #PROJ_FACE_NOSEAM0 to 4. `1 << i` - where i is `(0..3)`. * - * If we're multithreadng, make sure threads are locked when this is called. + * If we're multi-threading, make sure threads are locked when this is called. */ static void project_face_seams_init(const ProjPaintState *ps, MemArena *arena, @@ -3566,8 +3566,8 @@ static bool project_bucket_face_isect(ProjPaintState *ps, int bucket_y, const MLoopTri *lt) { - /* TODO - replace this with a tricker method that uses sideofline for all - * screenCoords's edges against the closest bucket corner */ + /* TODO - replace this with a trickier method that uses side-of-line for all + * #ProjPaintState.screenCoords edges against the closest bucket corner. */ const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)}; rctf bucket_bounds; float p1[2], p2[2], p3[2], p4[2]; diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index be545600e6e..d65f158174f 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -246,7 +246,11 @@ static int palette_color_add_exec(bContext *C, wmOperator *UNUSED(op)) color = BKE_palette_color_add(palette); palette->active_color = BLI_listbase_count(&palette->colors) - 1; - if (ELEM(mode, PAINT_MODE_TEXTURE_3D, PAINT_MODE_TEXTURE_2D, PAINT_MODE_VERTEX)) { + if (ELEM(mode, + PAINT_MODE_TEXTURE_3D, + PAINT_MODE_TEXTURE_2D, + PAINT_MODE_VERTEX, + PAINT_MODE_SCULPT)) { copy_v3_v3(color->rgb, BKE_brush_color_get(scene, brush)); color->value = 0.0; } 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 048f11c92d7..d05b1673c9a 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c @@ -206,7 +206,7 @@ void PAINT_OT_vertex_color_from_weight(wmOperatorType *ot) /** \name Smooth Vertex Colors Operator * \{ */ -static void vertex_color_smooth_looptag(Mesh *me, bool *mlooptag) +static void vertex_color_smooth_looptag(Mesh *me, const bool *mlooptag) { const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; const MPoly *mp; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 6d83e88b556..ffdf2c1e7c0 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -547,28 +547,81 @@ void SCULPT_visibility_sync_all_vertex_to_face_sets(SculptSession *ss) } } -bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index) +static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int index) { - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[index]; - int face_set = -1; - for (int i = 0; i < ss->pmap[index].count; i++) { - if (face_set == -1) { - face_set = abs(ss->face_sets[vert_map->indices[i]]); + MeshElemMap *vert_map = &ss->pmap[index]; + int face_set = -1; + for (int i = 0; i < ss->pmap[index].count; i++) { + if (face_set == -1) { + face_set = abs(ss->face_sets[vert_map->indices[i]]); + } + else { + if (abs(ss->face_sets[vert_map->indices[i]]) != face_set) { + return false; + } + } + } + return true; +} + +/** + * Checks if the face sets of the adjacent faces to the edge between \a v1 and \a v2 + * in the base mesh are equal. + */ +static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss, int v1, int v2) +{ + MeshElemMap *vert_map = &ss->pmap[v1]; + int p1 = -1, p2 = -1; + for (int i = 0; i < ss->pmap[v1].count; i++) { + MPoly *p = &ss->mpoly[vert_map->indices[i]]; + for (int l = 0; l < p->totloop; l++) { + MLoop *loop = &ss->mloop[p->loopstart + l]; + if (loop->v == v2) { + if (p1 == -1) { + p1 = vert_map->indices[i]; + break; } - else { - if (abs(ss->face_sets[vert_map->indices[i]]) != face_set) { - return false; - } + else if (p2 == -1) { + p2 = vert_map->indices[i]; + break; } } - return true; + } + } + + if (p1 != -1 && p2 != -1) { + return abs(ss->face_sets[p1]) == (ss->face_sets[p2]); + } + return true; +} + +bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: { + return sculpt_check_unique_face_set_in_base_mesh(ss, index); } case PBVH_BMESH: return false; - case PBVH_GRIDS: - return true; + case PBVH_GRIDS: { + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = index / key->grid_area; + const int vertex_index = index - grid_index * key->grid_area; + const SubdivCCGCoord coord = {.grid_index = grid_index, + .x = vertex_index % key->grid_size, + .y = vertex_index / key->grid_size}; + int v1, v2; + const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( + ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2); + switch (adjacency) { + case SUBDIV_CCG_ADJACENT_VERTEX: + return sculpt_check_unique_face_set_in_base_mesh(ss, v1); + case SUBDIV_CCG_ADJACENT_EDGE: + return sculpt_check_unique_face_set_for_edge_in_base_mesh(ss, v1, v2); + case SUBDIV_CCG_ADJACENT_NONE: + return true; + } + } } return false; } @@ -735,44 +788,64 @@ void SCULPT_vertex_neighbors_get(SculptSession *ss, } } +static bool sculpt_check_boundary_vertex_in_base_mesh(SculptSession *ss, const int index) +{ + const MeshElemMap *vert_map = &ss->pmap[index]; + if (vert_map->count <= 1) { + return true; + } + for (int i = 0; i < vert_map->count; i++) { + const MPoly *p = &ss->mpoly[vert_map->indices[i]]; + unsigned f_adj_v[2]; + if (poly_get_adj_loops_from_vert(p, ss->mloop, index, f_adj_v) != -1) { + int j; + for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { + if (!(vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2)) { + return true; + } + } + } + } + return false; +} + bool SCULPT_vertex_is_boundary(SculptSession *ss, const int index) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - const MeshElemMap *vert_map = &ss->pmap[index]; - - if (vert_map->count <= 1) { - return false; - } - if (!SCULPT_vertex_all_face_sets_visible_get(ss, index)) { - return false; - } - - for (int i = 0; i < vert_map->count; i++) { - const MPoly *p = &ss->mpoly[vert_map->indices[i]]; - unsigned f_adj_v[2]; - if (poly_get_adj_loops_from_vert(p, ss->mloop, index, f_adj_v) != -1) { - int j; - for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { - if (!(vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2)) { - return false; - } - } - } + return true; } - return true; + return sculpt_check_boundary_vertex_in_base_mesh(ss, index); } case PBVH_BMESH: { BMVert *v = BM_vert_at_index(ss->bm, index); return BM_vert_is_boundary(v); } - case PBVH_GRIDS: - return true; + case PBVH_GRIDS: { + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = index / key->grid_area; + const int vertex_index = index - grid_index * key->grid_area; + const SubdivCCGCoord coord = {.grid_index = grid_index, + .x = vertex_index % key->grid_size, + .y = vertex_index / key->grid_size}; + int v1, v2; + const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( + ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2); + switch (adjacency) { + case SUBDIV_CCG_ADJACENT_VERTEX: + return sculpt_check_boundary_vertex_in_base_mesh(ss, v1); + case SUBDIV_CCG_ADJACENT_EDGE: + return sculpt_check_boundary_vertex_in_base_mesh(ss, v1) && + sculpt_check_boundary_vertex_in_base_mesh(ss, v2); + case SUBDIV_CCG_ADJACENT_NONE: + return false; + } + } } - return true; + return false; } /* Utilities */ @@ -2400,7 +2473,10 @@ bool SCULPT_search_sphere_cb(PBVHNode *node, void *data_v) } float t[3], bb_min[3], bb_max[3]; - if (data->ignore_fully_masked) { + if (data->ignore_fully_ineffective) { + if (BKE_pbvh_node_fully_hidden_get(node)) { + return false; + } if (BKE_pbvh_node_fully_masked_get(node)) { return false; } @@ -2436,7 +2512,7 @@ bool SCULPT_search_circle_cb(PBVHNode *node, void *data_v) SculptSearchCircleData *data = data_v; float bb_min[3], bb_max[3]; - if (data->ignore_fully_masked) { + if (data->ignore_fully_ineffective) { if (BKE_pbvh_node_fully_masked_get(node)) { return false; } @@ -2489,7 +2565,7 @@ static PBVHNode **sculpt_pbvh_gather_cursor_update(Object *ob, .sd = sd, .radius_squared = ss->cursor_radius, .original = use_original, - .ignore_fully_masked = false, + .ignore_fully_ineffective = false, .center = NULL, }; BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode); @@ -2514,7 +2590,7 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, .sd = sd, .radius_squared = square_f(ss->cache->radius * radius_scale), .original = use_original, - .ignore_fully_masked = brush->sculpt_tool != SCULPT_TOOL_MASK, + .ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK, .center = NULL, }; BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode); @@ -2530,7 +2606,7 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, ss->cursor_radius, .original = use_original, .dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc, - .ignore_fully_masked = brush->sculpt_tool != SCULPT_TOOL_MASK, + .ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK, }; BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_circle_cb, &data, &nodes, r_totnode); } @@ -5387,11 +5463,15 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe /* Check for unsupported features. */ PBVHType type = BKE_pbvh_type(ss->pbvh); if (brush->sculpt_tool == SCULPT_TOOL_PAINT && type != PBVH_FACES) { - return; + if (!U.experimental.use_sculpt_vertex_colors) { + return; + } } if (brush->sculpt_tool == SCULPT_TOOL_SMEAR && type != PBVH_FACES) { - return; + if (!U.experimental.use_sculpt_vertex_colors) { + return; + } } /* Build a list of all nodes that are potentially within the brush's area of influence */ @@ -5411,7 +5491,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe .sd = sd, .radius_squared = square_f(ss->cache->radius * (1.0 + brush->cloth_sim_limit)), .original = false, - .ignore_fully_masked = false, + .ignore_fully_ineffective = false, .center = ss->cache->initial_location, }; BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); @@ -6046,6 +6126,14 @@ bool SCULPT_mode_poll(bContext *C) return ob && ob->mode & OB_MODE_SCULPT; } +bool SCULPT_vertex_colors_poll(bContext *C) +{ + if (!U.experimental.use_sculpt_vertex_colors) { + return false; + } + return SCULPT_mode_poll(C); +} + bool SCULPT_mode_poll_view3d(bContext *C) { return (SCULPT_mode_poll(C) && CTX_wm_region_view3d(C)); @@ -8096,7 +8184,7 @@ static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot) ot->idname = "SCULPT_OT_vertex_to_loop_colors"; /* api callbacks */ - ot->poll = SCULPT_mode_poll; + ot->poll = SCULPT_vertex_colors_poll; ot->exec = vertex_to_loop_colors_exec; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -8159,7 +8247,7 @@ static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot) ot->idname = "SCULPT_OT_loop_to_vertex_colors"; /* api callbacks */ - ot->poll = SCULPT_mode_poll; + ot->poll = SCULPT_vertex_colors_poll; ot->exec = loop_to_vertex_colors_exec; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -8184,7 +8272,6 @@ static int sculpt_sample_color_invoke(bContext *C, copy_v3_v3(color_srgb, active_vertex_color); IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb); BKE_brush_color_set(scene, brush, color_srgb); - BKE_brush_alpha_set(scene, brush, active_vertex_color[3]); WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); @@ -8200,7 +8287,7 @@ static void SCULPT_OT_sample_color(wmOperatorType *ot) /* api callbacks */ ot->invoke = sculpt_sample_color_invoke; - ot->poll = SCULPT_mode_poll; + ot->poll = SCULPT_vertex_colors_poll; ot->flag = OPTYPE_REGISTER; } @@ -8209,7 +8296,7 @@ static void SCULPT_OT_sample_color(wmOperatorType *ot) /* This allows the sculpt tools to work on meshes with multiple connected components as they had * only one connected component. When initialized and enabled, the sculpt API will return extra * connectivity neighbors that are not in the real mesh. These neighbors are calculated for each - * vertex using the minimun distance to a vertex that is in a different connected component. */ + * vertex using the minimum distance to a vertex that is in a different connected component. */ /* The fake neighbors first need to be ensured to be initialized. * After that tools which needs fake neighbors functionality need to @@ -8224,7 +8311,7 @@ static void SCULPT_OT_sample_color(wmOperatorType *ot) * } * * Such approach allows to keep all the connectivity information ready for reuse - * (withouy having lag prior to every stroke), but also makes it so the affect + * (without having lag prior to every stroke), but also makes it so the affect * is localized to a specific brushes and tools only. */ enum { @@ -8681,6 +8768,11 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + /* Color data is not available in Multires. */ + if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + return OPERATOR_CANCELLED; + } + if (!ss->vcol) { return OPERATOR_CANCELLED; } @@ -8726,12 +8818,12 @@ static void SCULPT_OT_mask_by_color(wmOperatorType *ot) /* api callbacks */ ot->invoke = sculpt_mask_by_color_invoke; - ot->poll = SCULPT_mode_poll; + ot->poll = SCULPT_vertex_colors_poll; ot->flag = OPTYPE_REGISTER; ot->prop = RNA_def_boolean( - ot->srna, "contiguous", false, "Contiguous", "Mask only contiguos color areas"); + ot->srna, "contiguous", false, "Contiguous", "Mask only contiguous color areas"); ot->prop = RNA_def_boolean(ot->srna, "invert", false, "Invert", "Invert the generated mask"); ot->prop = RNA_def_boolean( @@ -8774,6 +8866,7 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_face_sets_init); WM_operatortype_append(SCULPT_OT_cloth_filter); WM_operatortype_append(SCULPT_OT_face_sets_edit); + WM_operatortype_append(SCULPT_OT_sample_color); WM_operatortype_append(SCULPT_OT_loop_to_vertex_colors); WM_operatortype_append(SCULPT_OT_vertex_to_loop_colors); diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.c b/source/blender/editors/sculpt_paint/sculpt_automasking.c index 7c79c1d7a2f..48b278e52b6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.c +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.c @@ -209,7 +209,7 @@ float *SCULPT_boundary_automasking_init(Object *ob, { SculptSession *ss = ob->sculpt; - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { + if (!ss->pmap) { BLI_assert(!"Boundary Edges masking: pmap missing"); return NULL; } @@ -221,7 +221,7 @@ float *SCULPT_boundary_automasking_init(Object *ob, edge_distance[i] = EDGE_DISTANCE_INF; switch (mode) { case AUTOMASK_INIT_BOUNDARY_EDGES: - if (!SCULPT_vertex_is_boundary(ss, i)) { + if (SCULPT_vertex_is_boundary(ss, i)) { edge_distance[i] = 0; } break; diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index 6ca45ee0fb8..6a2137b6626 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -437,13 +437,16 @@ static void do_cloth_brush_solve_simulation_task_cb_ex( BKE_pbvh_vertex_iter_end; } -static void cloth_brush_build_nodes_constraints(Sculpt *sd, - Object *ob, - PBVHNode **nodes, - int totnode, - SculptClothSimulation *cloth_sim, - float initial_location[3], - const float radius) +static void cloth_brush_build_nodes_constraints( + Sculpt *sd, + Object *ob, + PBVHNode **nodes, + int totnode, + SculptClothSimulation *cloth_sim, + /* Cannot be const, because it is assigned to a non-const variable. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + float initial_location[3], + const float radius) { Brush *brush = BKE_paint_brush(&sd->paint); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 5f7805af347..556b73b0ea5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -308,7 +308,7 @@ void SCULPT_OT_color_filter(struct wmOperatorType *ot) /* api callbacks */ ot->invoke = sculpt_color_filter_invoke; ot->modal = sculpt_color_filter_modal; - ot->poll = SCULPT_mode_poll; + ot->poll = SCULPT_vertex_colors_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index 494588d0996..27dd0ab3e71 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -87,7 +87,7 @@ void SCULPT_filter_cache_init(Object *ob, Sculpt *sd, const int undo_type) .original = true, .center = center, .radius_squared = FLT_MAX, - .ignore_fully_masked = true, + .ignore_fully_ineffective = true, }; BKE_pbvh_search_gather(pbvh, diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index c7e07721eb7..e943bd280a3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -51,6 +51,8 @@ bool SCULPT_mode_poll_view3d(struct bContext *C); bool SCULPT_poll(struct bContext *C); bool SCULPT_poll_view3d(struct bContext *C); +bool SCULPT_vertex_colors_poll(struct bContext *C); + /* Updates */ typedef enum SculptUpdateType { @@ -676,7 +678,8 @@ typedef struct { float radius_squared; const float *center; bool original; - bool ignore_fully_masked; + /* This ignores fully masked and fully hidden nodes. */ + bool ignore_fully_ineffective; } SculptSearchSphereData; typedef struct { @@ -684,7 +687,7 @@ typedef struct { struct SculptSession *ss; float radius_squared; bool original; - bool ignore_fully_masked; + bool ignore_fully_ineffective; struct DistRayAABB_Precalc *dist_ray_to_aabb_precalc; } SculptSearchCircleData; diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index 5cf6a053382..608ba1b934e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -388,7 +388,17 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, float interp_color[4]; copy_v4_v4(interp_color, ss->cache->prev_colors[vd.index]); - sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location); + switch (brush->smear_deform_type) { + case BRUSH_SMEAR_DEFORM_DRAG: + sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location); + break; + case BRUSH_SMEAR_DEFORM_PINCH: + sub_v3_v3v3(current_disp, ss->cache->location, vd.co); + break; + case BRUSH_SMEAR_DEFORM_EXPAND: + sub_v3_v3v3(current_disp, vd.co, ss->cache->location); + break; + } normalize_v3_v3(current_disp_norm, current_disp); mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength); diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index e084d5fcdf2..49dbba5eba9 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -159,6 +159,7 @@ void ED_spacemacros_init(void) * We need to have them go after python operators too */ ED_operatormacros_armature(); ED_operatormacros_mesh(); + ED_operatormacros_uvedit(); ED_operatormacros_metaball(); ED_operatormacros_node(); ED_operatormacros_object(); diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 71b86996989..88c2c6e82b6 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -422,6 +422,9 @@ static void buttons_area_listener(wmWindow *UNUSED(win), buttons_area_redraw(area, BCONTEXT_CONSTRAINT); buttons_area_redraw(area, BCONTEXT_BONE_CONSTRAINT); break; + case ND_SHADERFX: + buttons_area_redraw(area, BCONTEXT_SHADERFX); + break; case ND_PARTICLE: if (wmn->action == NA_EDITED) { buttons_area_redraw(area, BCONTEXT_PARTICLE); @@ -435,13 +438,6 @@ static void buttons_area_listener(wmWindow *UNUSED(win), /* Needed to refresh context path when changing active particle system index. */ buttons_area_redraw(area, BCONTEXT_PARTICLE); break; - case ND_SHADING: - case ND_SHADING_DRAW: - case ND_SHADING_LINKS: - case ND_SHADING_PREVIEW: - /* currently works by redraws... if preview is set, it (re)starts job */ - sbuts->preview = 1; - break; default: /* Not all object RNA props have a ND_ notifier (yet) */ ED_area_tag_redraw(area); diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c index 35e0fdbfec3..68ebd6fed7a 100644 --- a/source/blender/editors/space_clip/clip_draw.c +++ b/source/blender/editors/space_clip/clip_draw.c @@ -1111,7 +1111,7 @@ static void draw_marker_texts(SpaceClip *sc, pos[1] -= fontsize; if (track->flag & TRACK_HAS_BUNDLE) { - BLI_snprintf(str, sizeof(str), "Average error: %.3f", track->error); + BLI_snprintf(str, sizeof(str), "Average error: %.2f px", track->error); BLF_position(fontid, pos[0], pos[1], 0.0f); BLF_draw(fontid, str, sizeof(str)); pos[1] -= fontsize; diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index a69601316ef..9da75ab7e3c 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -1403,6 +1403,8 @@ static void do_sequence_proxy(void *pjv, int build_count, int *build_undistort_sizes, int build_undistort_count, + /* Cannot be const, because it is assigned to a non-const variable. + * NOLINTNEXTLINE: readability-non-const-parameter. */ short *stop, short *do_update, float *progress) diff --git a/source/blender/editors/space_clip/tracking_ops_solve.c b/source/blender/editors/space_clip/tracking_ops_solve.c index c18207d7045..1ed965c30d2 100644 --- a/source/blender/editors/space_clip/tracking_ops_solve.c +++ b/source/blender/editors/space_clip/tracking_ops_solve.c @@ -144,7 +144,7 @@ static void solve_camera_freejob(void *scv) else { BKE_reportf(scj->reports, RPT_INFO, - "Average re-projection error: %.3f", + "Average re-projection error: %.2f px", tracking->reconstruction.error); } diff --git a/source/blender/editors/space_clip/tracking_ops_track.c b/source/blender/editors/space_clip/tracking_ops_track.c index cec7b371457..afbca48bd45 100644 --- a/source/blender/editors/space_clip/tracking_ops_track.c +++ b/source/blender/editors/space_clip/tracking_ops_track.c @@ -214,7 +214,13 @@ static bool track_markers_initjob(bContext *C, TrackMarkersJob *tmj, bool backwa return true; } -static void track_markers_startjob(void *tmv, short *stop, short *do_update, float *progress) +static void track_markers_startjob( + void *tmv, + /* Cannot be const, this function implements wm_jobs_start_callback. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + short *stop, + short *do_update, + float *progress) { TrackMarkersJob *tmj = (TrackMarkersJob *)tmv; int framenr = tmj->sfra; diff --git a/source/blender/editors/space_clip/tracking_select.c b/source/blender/editors/space_clip/tracking_select.c index 81cc858c69f..b6f9ca9589f 100644 --- a/source/blender/editors/space_clip/tracking_select.c +++ b/source/blender/editors/space_clip/tracking_select.c @@ -731,7 +731,9 @@ void CLIP_OT_select_lasso(wmOperatorType *ot) /********************** circle select operator *********************/ -static int point_inside_ellipse(float point[2], float offset[2], float ellipse[2]) +static int point_inside_ellipse(const float point[2], + const float offset[2], + const float ellipse[2]) { /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */ float x, y; diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index cb785495ad5..3ce80c11160 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -2660,9 +2660,9 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist) if (filelist->dir[0] == 0) { /* make directories */ # ifdef WITH_FREESTYLE - filelist->filelist.nbr_entries = 27; + filelist->filelist.nbr_entries = 27; # else - filelist->filelist.nbr_entries = 26; + filelist->filelist.nbr_entries = 26; # endif filelist_resize(filelist, filelist->filelist.nbr_entries); @@ -2693,11 +2693,11 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist) filelist->filelist.entries[20].entry->relpath = BLI_strdup("Action"); filelist->filelist.entries[21].entry->relpath = BLI_strdup("NodeTree"); filelist->filelist.entries[22].entry->relpath = BLI_strdup("Speaker"); - filelist->filelist.entries[23].entry->relpath = BLI_strdup("Hair"); - filelist->filelist.entries[24].entry->relpath = BLI_strdup("Point Cloud"); - filelist->filelist.entries[25].entry->relpath = BLI_strdup("Volume"); + filelist->filelist.entries[23].entry->relpath = BLI_strdup("Hair"); + filelist->filelist.entries[24].entry->relpath = BLI_strdup("Point Cloud"); + filelist->filelist.entries[25].entry->relpath = BLI_strdup("Volume"); # ifdef WITH_FREESTYLE - filelist->filelist.entries[26].entry->relpath = BLI_strdup("FreestyleLineStyle"); + filelist->filelist.entries[26].entry->relpath = BLI_strdup("FreestyleLineStyle"); # endif } else { @@ -2809,7 +2809,7 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist) static void filelist_readjob_do(const bool do_lib, FileList *filelist, const char *main_name, - short *stop, + const short *stop, short *do_update, float *progress, ThreadMutex *lock) diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index e6c40dce9c8..88c8c6b2939 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -374,7 +374,7 @@ void ED_fileselect_set_params_from_userdef(SpaceFile *sfile) * pass its size here so we can store that in the preferences. Otherwise NULL. */ void ED_fileselect_params_to_userdef(SpaceFile *sfile, - int temp_win_size[2], + const int temp_win_size[2], const bool is_maximized) { UserDef_FileSpaceData *sfile_udata_new = &U.file_space_data; diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index bff81201d60..4b329ab4524 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -754,8 +754,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) fsmenu, FS_CATEGORY_OTHER, &FOLDERID_SkyDrive, NULL, ICON_URL, FS_INSERT_LAST); } } -#else -# ifdef __APPLE__ +#elif defined(__APPLE__) { /* We store some known macOS system paths and corresponding icons * and names in the FS_CATEGORY_OTHER (not displayed directly) category. */ @@ -774,9 +773,9 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) const char *home = BLI_getenv("HOME"); -# define FS_MACOS_PATH(path, name, icon) \ - BLI_snprintf(line, sizeof(line), path, home); \ - fsmenu_insert_entry(fsmenu, FS_CATEGORY_OTHER, line, name, icon, FS_INSERT_LAST); +# define FS_MACOS_PATH(path, name, icon) \ + BLI_snprintf(line, sizeof(line), path, home); \ + fsmenu_insert_entry(fsmenu, FS_CATEGORY_OTHER, line, name, icon, FS_INSERT_LAST); FS_MACOS_PATH("%s/", NULL, ICON_HOME) FS_MACOS_PATH("%s/Desktop/", IFACE_("Desktop"), ICON_DESKTOP) @@ -787,7 +786,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) FS_MACOS_PATH("%s/Pictures/", IFACE_("Pictures"), ICON_FILE_IMAGE) FS_MACOS_PATH("%s/Library/Fonts/", IFACE_("Fonts"), ICON_FILE_FONT) -# undef FS_MACOS_PATH +# undef FS_MACOS_PATH /* Get mounted volumes better method OSX 10.6 and higher, see: * https://developer.apple.com/library/mac/#documentation/CoreFOundation/Reference/CFURLRef/Reference/reference.html @@ -849,8 +848,8 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) /* kLSSharedFileListFavoriteItems is deprecated, but available till macOS 10.15. * Will have to find a new method to sync the Finder Favorites with File Browser. */ -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" /* Finally get user favorite places */ if (read_bookmarks) { UInt32 seed; @@ -894,9 +893,9 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) CFRelease(pathesArray); CFRelease(list); } -# pragma GCC diagnostic pop +# pragma GCC diagnostic pop } -# else +#else /* unix */ { const char *home = BLI_getenv("HOME"); @@ -932,7 +931,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) { int found = 0; -# ifdef __linux__ +# ifdef __linux__ /* loop over mount points */ struct mntent *mnt; FILE *fp; @@ -994,7 +993,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) } BLI_filelist_free(dir, dir_len); } -# endif +# endif /* fallback */ if (!found) { @@ -1003,7 +1002,6 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) } } } -# endif #endif #if defined(WIN32) || defined(__APPLE__) @@ -1131,10 +1129,13 @@ int fsmenu_get_active_indices(struct FSMenu *fsmenu, enum FSMenuCategory categor * before being defined as unreachable by the OS, we need to validate the bookmarks in an async * job... */ -static void fsmenu_bookmark_validate_job_startjob(void *fsmenuv, - short *stop, - short *do_update, - float *UNUSED(progress)) +static void fsmenu_bookmark_validate_job_startjob( + void *fsmenuv, + /* Cannot be const, this function implements wm_jobs_start_callback. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + short *stop, + short *do_update, + float *UNUSED(progress)) { FSMenu *fsmenu = fsmenuv; diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index 179d73a38ba..0158e12c79c 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -1420,7 +1420,7 @@ void graph_buttons_register(ARegionType *art) pt->poll = graph_panel_drivers_poll; BLI_addtail(&art->paneltypes, pt); - pt = MEM_callocN(sizeof(PanelType), "spacetype graph panel drivers pover"); + pt = MEM_callocN(sizeof(PanelType), "spacetype graph panel drivers popover"); strcpy(pt->idname, "GRAPH_PT_drivers_popover"); strcpy(pt->label, N_("Add/Edit Driver")); strcpy(pt->category, "Drivers"); diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 24ec2393b69..1f8dd7cfe44 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -572,8 +572,12 @@ static void image_multiview_cb(bContext *C, void *rnd_pt, void *UNUSED(arg_v)) WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); } -static void uiblock_layer_pass_buttons( - uiLayout *layout, Image *image, RenderResult *rr, ImageUser *iuser, int w, short *render_slot) +static void uiblock_layer_pass_buttons(uiLayout *layout, + Image *image, + RenderResult *rr, + ImageUser *iuser, + int w, + const short *render_slot) { struct ImageUI_Data rnd_pt_local, *rnd_pt = NULL; uiBlock *block = uiLayoutGetBlock(layout); diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index 9040ca5e79c..a7fa7709c51 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -150,8 +150,8 @@ void ED_image_draw_info(Scene *scene, const uchar cp[4], const float fp[4], const float linearcol[4], - int *zp, - float *zpf) + const int *zp, + const float *zpf) { rcti color_rect; char str[256]; @@ -463,7 +463,7 @@ void ED_image_draw_info(Scene *scene, /* image drawing */ static void sima_draw_zbuf_pixels( - float x1, float y1, int rectx, int recty, int *rect, float zoomx, float zoomy) + float x1, float y1, int rectx, int recty, const int *rect, float zoomx, float zoomy) { float red[4] = {1.0f, 0.0f, 0.0f, 0.0f}; @@ -489,7 +489,7 @@ static void sima_draw_zbuffloat_pixels(Scene *scene, float y1, int rectx, int recty, - float *rect_float, + const float *rect_float, float zoomx, float zoomy) { diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index d0d9f2f57bb..130167f1bd0 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -392,7 +392,7 @@ static void nla_panel_properties(const bContext *C, Panel *panel) uiItemR(column, &strip_ptr, "blend_type", 0, NULL, ICON_NONE); /* Blend in/out + auto-blending: - * - blend in/out can only be set when autoblending is off + * - blend in/out can only be set when auto-blending is off. */ uiItemS(layout); diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index a56b7f8422e..96599fd92a7 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -645,8 +645,9 @@ static void nla_draw_strip_text(AnimData *adt, UI_view2d_text_cache_add_rectf(v2d, &rect, str, str_len, col); } -/* add frame extents to cache of text-strings to draw in pixelspace - * for now, only used when transforming strips +/** + * Add frame extents to cache of text-strings to draw in pixel-space + * for now, only used when transforming strips. */ static void nla_draw_strip_frames_text( NlaTrack *UNUSED(nlt), NlaStrip *strip, View2D *v2d, float UNUSED(yminc), float ymaxc) diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index f3207f2dc1e..780c3d6a217 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -860,11 +860,13 @@ static void node_shader_buts_tex_sky(uiLayout *layout, bContext *UNUSED(C), Poin if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_NISHITA) { uiItemR(layout, ptr, "sun_disc", DEFAULT_FLAGS, NULL, 0); + uiLayout *col; if (RNA_boolean_get(ptr, "sun_disc")) { - uiItemR(layout, ptr, "sun_size", DEFAULT_FLAGS, NULL, ICON_NONE); + col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "sun_size", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "sun_intensity", DEFAULT_FLAGS, NULL, ICON_NONE); } - uiLayout *col; col = uiLayoutColumn(layout, true); uiItemR(col, ptr, "sun_elevation", DEFAULT_FLAGS, NULL, ICON_NONE); uiItemR(col, ptr, "sun_rotation", DEFAULT_FLAGS, NULL, ICON_NONE); diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 11d87148713..7af64e75656 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -238,7 +238,12 @@ static void compo_progressjob(void *cjv, float progress) } /* only this runs inside thread */ -static void compo_startjob(void *cjv, short *stop, short *do_update, float *progress) +static void compo_startjob(void *cjv, + /* Cannot be const, this function implements wm_jobs_start_callback. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + short *stop, + short *do_update, + float *progress) { CompoJob *cj = cjv; bNodeTree *ntree = cj->localtree; diff --git a/source/blender/editors/space_node/node_view.c b/source/blender/editors/space_node/node_view.c index b1dbe3bc506..b376c6b29cf 100644 --- a/source/blender/editors/space_node/node_view.c +++ b/source/blender/editors/space_node/node_view.c @@ -419,7 +419,7 @@ static void sample_draw(const bContext *C, ARegion *region, void *arg_info) * And here we've got recursion in the comments tips... */ bool ED_space_node_color_sample( - Main *bmain, SpaceNode *snode, ARegion *region, int mval[2], float r_col[3]) + Main *bmain, SpaceNode *snode, ARegion *region, const int mval[2], float r_col[3]) { void *lock; Image *ima; diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index a77b5424eb6..f0287984268 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -54,6 +54,7 @@ #include "BKE_context.h" #include "BKE_fcurve.h" #include "BKE_global.h" +#include "BKE_idtype.h" #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_lib_override.h" @@ -739,22 +740,63 @@ static void id_local_cb(bContext *C, } } +typedef struct OutlinerLibOverrideData { + bool do_hierarchy; +} OutlinerLibOverrideData; + static void id_override_library_cb(bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), - TreeElement *UNUSED(te), + TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, - void *UNUSED(user_data)) + void *user_data) { - if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id)) { + BLI_assert(TSE_IS_REAL_ID(tselem)); + ID *id_root = tselem->id; + + if (ID_IS_LINKED(id_root) && + (BKE_idtype_get_info_from_id(id_root)->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0) { Main *bmain = CTX_data_main(C); + OutlinerLibOverrideData *data = user_data; + const bool do_hierarchy = data->do_hierarchy; + + id_root->tag |= LIB_TAG_DOIT; + + printf("%s: Tagging root id %s\n", __func__, id_root->name); + /* For now, remapp all local usages of linked ID to local override one here. */ - BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, true); - ID *override_id = BKE_lib_override_library_create_from_id(bmain, tselem->id, true); - if (override_id != NULL) { - BKE_main_id_clear_newpoins(bmain); + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { + if (ID_IS_LINKED(id_iter)) { + id_iter->tag &= ~LIB_TAG_DOIT; + } + else { + id_iter->tag |= LIB_TAG_DOIT; + } } + FOREACH_MAIN_ID_END; + + if (do_hierarchy) { + /* Tag all linked parents in tree hierarchy to be also overridden. */ + while ((te = te->parent) != NULL) { + if (!TSE_IS_REAL_ID(te->store_elem)) { + continue; + } + if (!ID_IS_LINKED(te->store_elem->id)) { + break; + } + te->store_elem->id->tag |= LIB_TAG_DOIT; + printf("%s: Tagging parent id %s\n", __func__, te->store_elem->id->name); + } + BKE_lib_override_library_dependencies_tag(bmain, id_root, LIB_TAG_DOIT, true); + BKE_lib_override_library_create_from_tag(bmain); + } + else if (ID_IS_OVERRIDABLE_LIBRARY(id_root)) { + BKE_lib_override_library_create_from_id(bmain, id_root, true); + } + + BKE_main_id_clear_newpoins(bmain); BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); } } @@ -1127,6 +1169,7 @@ static void modifier_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem { bContext *C = (bContext *)Carg; Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); ModifierData *md = (ModifierData *)te->directdata; Object *ob = (Object *)outliner_search_back(te, ID_OB); @@ -1141,7 +1184,7 @@ static void modifier_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); } else if (event == OL_MODIFIER_OP_DELETE) { - ED_object_modifier_remove(NULL, bmain, ob, md); + ED_object_modifier_remove(NULL, bmain, scene, ob, md); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER | NA_REMOVED, ob); te->store_elem->flag &= ~TSE_SELECTED; } @@ -1325,8 +1368,8 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) } else if (event == OL_OP_REMAP) { outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_remap_cb, NULL); - /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth trick - * does not work here). */ + /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth + * trick does not work here). */ } else if (event == OL_OP_LOCALIZED) { /* disabled, see above enum (ton) */ outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, id_local_cb); @@ -1507,6 +1550,7 @@ typedef enum eOutlinerIdOpTypes { OUTLINER_IDOP_UNLINK, OUTLINER_IDOP_LOCAL, OUTLINER_IDOP_OVERRIDE_LIBRARY, + OUTLINER_IDOP_OVERRIDE_LIBRARY_HIERARCHY, OUTLINER_IDOP_SINGLE, OUTLINER_IDOP_DELETE, OUTLINER_IDOP_REMAP, @@ -1530,6 +1574,11 @@ static const EnumPropertyItem prop_id_op_types[] = { 0, "Add Library Override", "Add a local override of this linked data-block"}, + {OUTLINER_IDOP_OVERRIDE_LIBRARY_HIERARCHY, + "OVERRIDE_LIBRARY_HIERARCHY", + 0, + "Add Library Override Hierarchy", + "Add a local override of this linked data-block, and its hierarchy of dependencies"}, {OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""}, {OUTLINER_IDOP_DELETE, "DELETE", ICON_X, "Delete", ""}, {OUTLINER_IDOP_REMAP, @@ -1562,7 +1611,7 @@ static bool outliner_id_operation_item_poll(bContext *C, switch (enum_value) { case OUTLINER_IDOP_OVERRIDE_LIBRARY: - return BKE_lib_override_library_is_enabled(); + return true; case OUTLINER_IDOP_SINGLE: if (!soops || ELEM(soops->outlinevis, SO_SCENES, SO_VIEW_LAYER)) { return true; @@ -1676,12 +1725,27 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY: { - if (BKE_lib_override_library_is_enabled()) { - /* make local */ - outliner_do_libdata_operation( - C, op->reports, scene, soops, &soops->tree, id_override_library_cb, NULL); - ED_undo_push(C, "Overridden Data"); - } + /* make local */ + outliner_do_libdata_operation(C, + op->reports, + scene, + soops, + &soops->tree, + id_override_library_cb, + &(OutlinerLibOverrideData){.do_hierarchy = false}); + ED_undo_push(C, "Overridden Data"); + break; + } + case OUTLINER_IDOP_OVERRIDE_LIBRARY_HIERARCHY: { + /* make local */ + outliner_do_libdata_operation(C, + op->reports, + scene, + soops, + &soops->tree, + id_override_library_cb, + &(OutlinerLibOverrideData){.do_hierarchy = true}); + ED_undo_push(C, "Overridden Data"); break; } case OUTLINER_IDOP_SINGLE: { diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 7178f32f182..0d8e0a87694 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -2607,6 +2607,8 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op)) MetaStack *ms; bool nothing_selected = true; + BKE_sequencer_prefetch_stop(scene); + seq = BKE_sequencer_active_get(scene); if (seq && seq->flag & SELECT) { /* Avoid a loop since this is likely to be selected. */ nothing_selected = false; diff --git a/source/blender/editors/space_sequencer/sequencer_scopes.c b/source/blender/editors/space_sequencer/sequencer_scopes.c index a20d9a2942c..80b1c7a5194 100644 --- a/source/blender/editors/space_sequencer/sequencer_scopes.c +++ b/source/blender/editors/space_sequencer/sequencer_scopes.c @@ -50,14 +50,14 @@ static void rgb_to_yuv_normalized(const float rgb[3], float yuv[3]) yuv[2] += 0.5f; } -static void scope_put_pixel(uchar *table, uchar *pos) +static void scope_put_pixel(const uchar *table, uchar *pos) { uchar newval = table[*pos]; pos[0] = pos[1] = pos[2] = newval; pos[3] = 255; } -static void scope_put_pixel_single(uchar *table, uchar *pos, int col) +static void scope_put_pixel_single(const uchar *table, uchar *pos, int col) { char newval = table[pos[col]]; pos[col] = newval; diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index ed81d87f053..c88303daa16 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -825,6 +825,7 @@ static void view3d_main_region_listener( case ND_POSE: case ND_DRAW: case ND_MODIFIER: + case ND_SHADERFX: case ND_CONSTRAINT: case ND_KEYS: case ND_PARTICLE: @@ -1381,6 +1382,7 @@ static void view3d_buttons_region_listener(wmWindow *UNUSED(win), case ND_DRAW: case ND_KEYS: case ND_MODIFIER: + case ND_SHADERFX: ED_region_tag_redraw(region); break; } diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 33625a8b775..fab98857c99 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -509,8 +509,8 @@ static void viewops_data_create(bContext *C, negate_v3_v3(my_origin, rv3d->ofs); /* ofs is flipped */ - /* Set the dist value to be the distance from this 3d point this means youll - * always be able to zoom into it and panning wont go bad when dist was zero */ + /* Set the dist value to be the distance from this 3d point this means you'll + * always be able to zoom into it and panning wont go bad when dist was zero. */ /* remove dist value */ upvec[0] = upvec[1] = 0; diff --git a/source/blender/editors/space_view3d/view3d_fly.c b/source/blender/editors/space_view3d/view3d_fly.c index c13990a4391..cc19ecf35a8 100644 --- a/source/blender/editors/space_view3d/view3d_fly.c +++ b/source/blender/editors/space_view3d/view3d_fly.c @@ -200,7 +200,7 @@ typedef struct FlyInfo { float grid; /* compare between last state */ - /** Used to accelerate when using the mousewheel a lot. */ + /** Used to accelerate when using the mouse-wheel a lot. */ double time_lastwheel; /** Time between draws. */ double time_lastdraw; @@ -614,8 +614,8 @@ static void flyEvent(FlyInfo *fly, const wmEvent *event) fly->axis = -1; } else { - /* flip speed rather than stopping, game like motion, - * else increase like mousewheel if we're already moving in that direction */ + /* Flip speed rather than stopping, game like motion, + * else increase like mouse-wheel if we're already moving in that direction. */ if (fly->speed < 0.0f) { fly->speed = -fly->speed; } diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index b7d857fb172..fc763d91e3d 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -1572,7 +1572,7 @@ void VIEW3D_OT_select_menu(wmOperatorType *ot) static Base *object_mouse_select_menu(bContext *C, ViewContext *vc, - uint *buffer, + const uint *buffer, int hits, const int mval[2], bool extend, diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index d3e509fe96b..0d121b23c71 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -1028,8 +1028,11 @@ void ED_view3d_autodist_init(Depsgraph *depsgraph, ARegion *region, View3D *v3d, } /* no 4x4 sampling, run #ED_view3d_autodist_init first */ -bool ED_view3d_autodist_simple( - ARegion *region, const int mval[2], float mouse_worldloc[3], int margin, float *force_depth) +bool ED_view3d_autodist_simple(ARegion *region, + const int mval[2], + float mouse_worldloc[3], + int margin, + const float *force_depth) { float depth; @@ -1458,7 +1461,7 @@ bool ED_view3d_lock(RegionView3D *rv3d) * \param quat: The view rotation, quaternion normally from RegionView3D.viewquat. * \param dist: The view distance from ofs, normally from RegionView3D.dist. */ -void ED_view3d_from_m4(const float mat[4][4], float ofs[3], float quat[4], float *dist) +void ED_view3d_from_m4(const float mat[4][4], float ofs[3], float quat[4], const float *dist) { float nmat[3][3]; diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index d68489759fe..70004d27dfc 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -27,6 +27,7 @@ #include "MEM_guardedalloc.h" #include "BLI_kdtree.h" +#include "BLI_linklist_stack.h" #include "BLI_listbase.h" #include "BLI_math.h" @@ -409,59 +410,66 @@ void transform_autoik_update(TransInfo *t, short mode) /** \name Curve Surface * \{ */ -void calc_distanceCurveVerts(TransData *head, TransData *tail) +void calc_distanceCurveVerts(TransData *head, TransData *tail, bool cyclic) { - TransData *td, *td_near = NULL; + TransData *td; + BLI_LINKSTACK_DECLARE(queue, TransData *); + BLI_LINKSTACK_INIT(queue); for (td = head; td <= tail; td++) { if (td->flag & TD_SELECTED) { - td_near = td; td->dist = 0.0f; + BLI_LINKSTACK_PUSH(queue, td); } - else if (td_near) { - float dist; - float vec[3]; + else { + td->dist = FLT_MAX; + } + } + + while ((td = BLI_LINKSTACK_POP(queue))) { + float dist; + float vec[3]; + + TransData *next_td = NULL; - sub_v3_v3v3(vec, td_near->center, td->center); + if (td + 1 <= tail) { + next_td = td + 1; + } + else if (cyclic) { + next_td = head; + } + + if (next_td != NULL && !(next_td->flag & TD_NOTCONNECTED)) { + sub_v3_v3v3(vec, next_td->center, td->center); mul_m3_v3(head->mtx, vec); - dist = len_v3(vec); + dist = len_v3(vec) + td->dist; - if (dist < (td - 1)->dist) { - td->dist = (td - 1)->dist; - } - else { - td->dist = dist; + if (dist < next_td->dist) { + next_td->dist = dist; + BLI_LINKSTACK_PUSH(queue, next_td); } } - else { - td->dist = FLT_MAX; - td->flag |= TD_NOTCONNECTED; + + next_td = NULL; + + if (td - 1 >= head) { + next_td = td - 1; } - } - td_near = NULL; - for (td = tail; td >= head; td--) { - if (td->flag & TD_SELECTED) { - td_near = td; - td->dist = 0.0f; + else if (cyclic) { + next_td = tail; } - else if (td_near) { - float dist; - float vec[3]; - sub_v3_v3v3(vec, td_near->center, td->center); + if (next_td != NULL && !(next_td->flag & TD_NOTCONNECTED)) { + sub_v3_v3v3(vec, next_td->center, td->center); mul_m3_v3(head->mtx, vec); - dist = len_v3(vec); + dist = len_v3(vec) + td->dist; - if (td->flag & TD_NOTCONNECTED || dist < td->dist || (td + 1)->dist < td->dist) { - td->flag &= ~TD_NOTCONNECTED; - if (dist < (td + 1)->dist) { - td->dist = (td + 1)->dist; - } - else { - td->dist = dist; - } + if (dist < next_td->dist) { + next_td->dist = dist; + BLI_LINKSTACK_PUSH(queue, next_td); } } } + BLI_LINKSTACK_FREE(queue); } /* Utility function for getting the handle data from bezier's */ @@ -1270,6 +1278,9 @@ void createTransData(bContext *C, TransInfo *t) set_prop_dist(t, false); } } + else if (convert_type == TC_MESH_UV && t->flag & T_PROP_CONNECTED) { + /* Already calculated by uv_set_connectivity_distance. */ + } else if (convert_type == TC_CURVE_VERTS && t->obedit_type == OB_CURVE) { set_prop_dist(t, false); } diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index f7eea286983..ee478ad8567 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -87,7 +87,7 @@ void transform_around_single_fallback_ex(TransInfo *t, int data_len_all); void transform_around_single_fallback(TransInfo *t); void posttrans_fcurve_clean(struct FCurve *fcu, const int sel_flag, const bool use_handle); bool constraints_list_needinv(TransInfo *t, ListBase *list); -void calc_distanceCurveVerts(TransData *head, TransData *tail); +void calc_distanceCurveVerts(TransData *head, TransData *tail, bool cyclic); struct TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTriple *bezt); char transform_convert_frame_side_dir_get(TransInfo *t, float cframe); bool FrameOnMouseSide(char side, float frame, float cframe); diff --git a/source/blender/editors/transform/transform_convert_curve.c b/source/blender/editors/transform/transform_convert_curve.c index 37e37072ed7..65b2c9f9382 100644 --- a/source/blender/editors/transform/transform_convert_curve.c +++ b/source/blender/editors/transform/transform_convert_curve.c @@ -350,12 +350,14 @@ void createTransCurveVerts(TransInfo *t) (void)hdata; /* quiet warning */ } else if (is_prop_edit && head != tail) { - calc_distanceCurveVerts(head, tail - 1); - head = tail; + tail->flag |= TD_NOTCONNECTED; + td++; + tail++; } } if (is_prop_edit && head != tail) { - calc_distanceCurveVerts(head, tail - 1); + bool cyclic = (nu->flagu & CU_NURB_CYCLIC) != 0; + calc_distanceCurveVerts(head, tail - 1, cyclic); } /* TODO - in the case of tilt and radius we can also avoid allocating the @@ -425,12 +427,14 @@ void createTransCurveVerts(TransInfo *t) } } else if (is_prop_edit && head != tail) { - calc_distanceCurveVerts(head, tail - 1); - head = tail; + tail->flag |= TD_NOTCONNECTED; + td++; + tail++; } } if (is_prop_edit && head != tail) { - calc_distanceCurveVerts(head, tail - 1); + bool cyclic = (nu->flagu & CU_NURB_CYCLIC) != 0; + calc_distanceCurveVerts(head, tail - 1, cyclic); } } } diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c index 22261b9bbd8..0eb12aeabed 100644 --- a/source/blender/editors/transform/transform_convert_gpencil.c +++ b/source/blender/editors/transform/transform_convert_gpencil.c @@ -354,7 +354,7 @@ void createTransGPencil(bContext *C, TransInfo *t) /* March over these points, and calculate the proportional editing distances */ if (is_prop_edit && (head != tail)) { - calc_distanceCurveVerts(head, tail - 1); + calc_distanceCurveVerts(head, tail - 1, false); } } } diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index 90e45253afa..f067dd60c19 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -1067,7 +1067,7 @@ static void create_trans_vert_customdata_layer(BMVert *v, BLI_ghash_insert(tcld->origverts, v, r_tcld_vert); } -static void trans_mesh_customdata_correction_init_container(TransInfo *t, TransDataContainer *tc) +static void trans_mesh_customdata_correction_init_container(TransDataContainer *tc) { if (tc->custom.type.data) { /* Custom data correction has initiated before. */ @@ -1075,23 +1075,6 @@ static void trans_mesh_customdata_correction_init_container(TransInfo *t, TransD return; } - if (!ELEM(t->mode, - TFM_TRANSLATION, - TFM_ROTATION, - TFM_RESIZE, - TFM_TOSPHERE, - TFM_SHEAR, - TFM_BEND, - TFM_SHRINKFATTEN, - TFM_TRACKBALL, - TFM_PUSHPULL, - TFM_ALIGN, - TFM_EDGE_SLIDE, - TFM_VERT_SLIDE)) { - /* Currently only modes that change the position of vertices are supported. */ - return; - } - BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; @@ -1173,13 +1156,63 @@ static void trans_mesh_customdata_correction_init_container(TransInfo *t, TransD void trans_mesh_customdata_correction_init(TransInfo *t) { + if (!ELEM(t->mode, + TFM_TRANSLATION, + TFM_ROTATION, + TFM_RESIZE, + TFM_TOSPHERE, + TFM_SHEAR, + TFM_BEND, + TFM_SHRINKFATTEN, + TFM_TRACKBALL, + TFM_PUSHPULL, + TFM_ALIGN, + TFM_EDGE_SLIDE, + TFM_VERT_SLIDE)) { + /* Currently only modes that change the position of vertices are supported. */ + return; + } + const char uvcalc_correct_flag = ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE) ? UVCALC_TRANSFORM_CORRECT_SLIDE : UVCALC_TRANSFORM_CORRECT; if (t->settings->uvcalc_flag & uvcalc_correct_flag) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { - trans_mesh_customdata_correction_init_container(t, tc); + trans_mesh_customdata_correction_init_container(tc); + } + } +} + +static void trans_mesh_customdata_correction_restore(struct TransDataContainer *tc) +{ + struct TransCustomDataLayer *tcld = tc->custom.type.data; + if (!tcld) { + return; + } + + BMesh *bm = tcld->bm; + struct TransCustomDataLayerVert *tcld_vert_iter = &tcld->data[0]; + for (int i = tcld->data_len; i--; tcld_vert_iter++) { + BMLoop *l; + BMIter liter; + BMVert *v = tcld_vert_iter->v; + BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { + /* Pop the key to not restore the face again. */ + BMFace *f_copy = BLI_ghash_popkey(tcld->origfaces, l->f, NULL); + if (f_copy) { + BMLoop *l_iter_a, *l_first_a; + BMLoop *l_iter_b, *l_first_b; + l_iter_a = l_first_a = BM_FACE_FIRST_LOOP(f_copy); + l_iter_b = l_first_b = BM_FACE_FIRST_LOOP(l->f); + do { + BM_elem_attrs_copy(tcld->bm_origfaces, bm, l_iter_a, l_iter_b); + } while (((l_iter_a = l_iter_a->next) != l_first_a) && + ((l_iter_b = l_iter_b->next) != l_first_b)); + + BM_elem_attrs_copy_ex( + tcld->bm_origfaces, bm, f_copy, l->f, BM_ELEM_SELECT, CD_MASK_NORMAL); + } } } } @@ -1195,7 +1228,7 @@ static const float *trans_vert_orig_co_get(struct TransCustomDataLayer *tcld, BM static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLayer *tcld, struct TransCustomDataLayerVert *tcld_vert, - bool is_final) + bool do_loop_mdisps) { BMesh *bm = tcld->bm; BMVert *v = tcld_vert->v; @@ -1295,8 +1328,8 @@ static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLa * Interpolate from every other loop (not ideal) * However values will only be taken from loops which overlap other mdisps. * */ - const bool do_loop_mdisps = is_moved && is_final && (tcld->cd_loop_mdisp_offset != -1); - if (do_loop_mdisps) { + const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1); + if (update_loop_mdisps) { float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num); BMLoop *l; @@ -1332,12 +1365,12 @@ static void trans_mesh_customdata_correction_apply(struct TransDataContainer *tc return; } - const bool has_mdisps = (tcld->cd_loop_mdisp_offset != -1); + const bool do_loop_mdisps = is_final && (tcld->cd_loop_mdisp_offset != -1); struct TransCustomDataLayerVert *tcld_vert_iter = &tcld->data[0]; for (int i = tcld->data_len; i--; tcld_vert_iter++) { - if (tcld_vert_iter->cd_loop_groups || has_mdisps) { - trans_mesh_customdata_correction_apply_vert(tcld, tcld_vert_iter, is_final); + if (tcld_vert_iter->cd_loop_groups || do_loop_mdisps) { + trans_mesh_customdata_correction_apply_vert(tcld, tcld_vert_iter, do_loop_mdisps); } } } @@ -1388,8 +1421,9 @@ static void transform_apply_to_mirror(TransInfo *t) void recalcData_mesh(TransInfo *t) { + bool is_cancelling = t->state == TRANS_CANCEL; /* mirror modifier clipping? */ - if (t->state != TRANS_CANCEL) { + if (!is_cancelling) { /* apply clipping after so we never project past the clip plane [#25423] */ applyProject(t); clipMirrorModifier(t); @@ -1400,7 +1434,12 @@ void recalcData_mesh(TransInfo *t) } FOREACH_TRANS_DATA_CONTAINER (t, tc) { - trans_mesh_customdata_correction_apply(tc, false); + if (is_cancelling) { + trans_mesh_customdata_correction_restore(tc); + } + else { + trans_mesh_customdata_correction_apply(tc, false); + } DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); @@ -1416,15 +1455,15 @@ void recalcData_mesh(TransInfo *t) void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) { - const bool canceled = (t->state == TRANS_CANCEL); - const bool use_automerge = !canceled && (t->flag & (T_AUTOMERGE | T_AUTOSPLIT)) != 0; + const bool is_cancelling = (t->state == TRANS_CANCEL); + const bool use_automerge = !is_cancelling && (t->flag & (T_AUTOMERGE | T_AUTOSPLIT)) != 0; - if (TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.type.data != NULL) { + if (!is_cancelling && TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.type.data != NULL) { /* Handle multires re-projection, done * on transform completion since it's * really slow -joeedh. */ FOREACH_TRANS_DATA_CONTAINER (t, tc) { - trans_mesh_customdata_correction_apply(tc, !canceled); + trans_mesh_customdata_correction_apply(tc, true); } } diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c index 41c09cd8ea2..56fa2d90fb2 100644 --- a/source/blender/editors/transform/transform_convert_mesh_uv.c +++ b/source/blender/editors/transform/transform_convert_mesh_uv.c @@ -26,6 +26,7 @@ #include "MEM_guardedalloc.h" #include "BLI_bitmap.h" +#include "BLI_linklist_stack.h" #include "BLI_math.h" #include "BKE_context.h" @@ -51,12 +52,13 @@ static void UVsToTransData(const float aspect[2], TransData2D *td2d, float *uv, const float *center, + float calc_dist, bool selected) { - /* uv coords are scaled by aspects. this is needed for rotations and - * proportional editing to be consistent with the stretched uv coords - * that are displayed. this also means that for display and numinput, - * and when the uv coords are flushed, these are converted each time */ + /* UV coords are scaled by aspects. this is needed for rotations and + * proportional editing to be consistent with the stretched UV coords + * that are displayed. this also means that for display and number-input, + * and when the UV coords are flushed, these are converted each time. */ td2d->loc[0] = uv[0] * aspect[0]; td2d->loc[1] = uv[1] * aspect[1]; td2d->loc[2] = 0.0f; @@ -79,12 +81,182 @@ static void UVsToTransData(const float aspect[2], td->dist = 0.0; } else { - td->dist = FLT_MAX; + td->dist = calc_dist; } unit_m3(td->mtx); unit_m3(td->smtx); } +/** + * \param dists: Store the closest connected distance to selected vertices. + */ +static void uv_set_connectivity_distance(BMesh *bm, float *dists, const float aspect[2]) +{ + /* Mostly copied from editmesh_set_connectivity_distance. */ + BLI_LINKSTACK_DECLARE(queue, BMLoop *); + + /* Any BM_ELEM_TAG'd loop is added to 'queue_next', this makes sure that we don't add things + * twice. */ + BLI_LINKSTACK_DECLARE(queue_next, BMLoop *); + + BLI_LINKSTACK_INIT(queue); + BLI_LINKSTACK_INIT(queue_next); + + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + BMIter fiter, liter; + BMVert *f; + BMLoop *l; + + BM_mesh_elem_index_ensure(bm, BM_LOOP); + + BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { + /* Visable faces was tagged in createTransUVs. */ + if (!BM_elem_flag_test(f, BM_ELEM_TAG)) { + continue; + } + + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + float dist; + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + + bool uv_vert_sel = luv->flag & MLOOPUV_VERTSEL; + + if (uv_vert_sel) { + BLI_LINKSTACK_PUSH(queue, l); + dist = 0.0f; + } + else { + dist = FLT_MAX; + } + + /* Make sure all loops are in a clean tag state. */ + BLI_assert(BM_elem_flag_test(l, BM_ELEM_TAG) == 0); + + int loop_idx = BM_elem_index_get(l); + + dists[loop_idx] = dist; + } + } + + /* Need to be very careful of feedback loops here, store previous dist's to avoid feedback. */ + float *dists_prev = MEM_dupallocN(dists); + + do { + while ((l = BLI_LINKSTACK_POP(queue))) { + BLI_assert(dists[BM_elem_index_get(l)] != FLT_MAX); + + BMLoop *l_other, *l_connected; + BMIter l_connected_iter; + + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + float l_uv[2]; + + copy_v2_v2(l_uv, luv->uv); + mul_v2_v2(l_uv, aspect); + + BM_ITER_ELEM (l_other, &liter, l->f, BM_LOOPS_OF_FACE) { + if (l_other == l) { + continue; + } + float other_uv[2], edge_vec[2]; + MLoopUV *luv_other = BM_ELEM_CD_GET_VOID_P(l_other, cd_loop_uv_offset); + + copy_v2_v2(other_uv, luv_other->uv); + mul_v2_v2(other_uv, aspect); + + sub_v2_v2v2(edge_vec, l_uv, other_uv); + + const int i = BM_elem_index_get(l); + const int i_other = BM_elem_index_get(l_other); + float dist = len_v2(edge_vec) + dists_prev[i]; + + if (dist < dists[i_other]) { + dists[i_other] = dist; + } + else { + /* The face loop already has a shorter path to it. */ + continue; + } + + float connected_uv[2]; + float uvdiff[2]; + + bool other_vert_sel, connected_vert_sel; + + other_vert_sel = luv_other->flag & MLOOPUV_VERTSEL; + + BM_ITER_ELEM (l_connected, &l_connected_iter, l_other->v, BM_LOOPS_OF_VERT) { + if (l_connected == l_other) { + continue; + } + /* Visable faces was tagged in createTransUVs. */ + if (!BM_elem_flag_test(l_connected->f, BM_ELEM_TAG)) { + continue; + } + + MLoopUV *luv_connected = BM_ELEM_CD_GET_VOID_P(l_connected, cd_loop_uv_offset); + connected_vert_sel = luv_connected->flag & MLOOPUV_VERTSEL; + copy_v2_v2(connected_uv, luv_connected->uv); + mul_v2_v2(connected_uv, aspect); + + sub_v2_v2v2(uvdiff, connected_uv, other_uv); + /* Check if this loop is connected in UV space. + * If the uv loops share the same selection state (if not, they are not connected as + * they have been ripped or other edit commands have separated them). */ + bool connected = other_vert_sel == connected_vert_sel && + fabsf(uvdiff[0]) < STD_UV_CONNECT_LIMIT && + fabsf(uvdiff[1]) < STD_UV_CONNECT_LIMIT; + if (!connected) { + continue; + } + + /* The loop vert is occupying the same space, so it has the same distance. */ + const int i_connected = BM_elem_index_get(l_connected); + dists[i_connected] = dist; + + if (BM_elem_flag_test(l_connected, BM_ELEM_TAG) == 0) { + BM_elem_flag_enable(l_connected, BM_ELEM_TAG); + BLI_LINKSTACK_PUSH(queue_next, l_connected); + } + } + } + } + + /* Clear elem flags for the next loop. */ + for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) { + BMLoop *l_link = lnk->link; + const int i = BM_elem_index_get(l_link); + + BM_elem_flag_disable(l_link, BM_ELEM_TAG); + + /* Store all new dist values. */ + dists_prev[i] = dists[i]; + } + + BLI_LINKSTACK_SWAP(queue, queue_next); + + } while (BLI_LINKSTACK_SIZE(queue)); + +#ifndef NDEBUG + /* Check that we didn't leave any loops tagged */ + BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { + /* Visable faces was tagged in createTransUVs. */ + if (!BM_elem_flag_test(f, BM_ELEM_TAG)) { + continue; + } + + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + BLI_assert(BM_elem_flag_test(l, BM_ELEM_TAG) == 0); + } + } +#endif + + BLI_LINKSTACK_FREE(queue); + BLI_LINKSTACK_FREE(queue_next); + + MEM_freeN(dists_prev); +} + void createTransUVs(bContext *C, TransInfo *t) { SpaceImage *sima = CTX_wm_space_image(C); @@ -103,12 +275,11 @@ void createTransUVs(bContext *C, TransInfo *t) BMFace *efa; BMIter iter, liter; UvElementMap *elementmap = NULL; - BLI_bitmap *island_enabled = NULL; struct { float co[2]; int co_num; } *island_center = NULL; - int count = 0, countsel = 0, count_rejected = 0; + int count = 0, countsel = 0; const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); if (!ED_space_image_show_uvedit(sima, tc->obedit)) { @@ -116,22 +287,15 @@ void createTransUVs(bContext *C, TransInfo *t) } /* count */ - if (is_prop_connected || is_island_center) { + if (is_island_center) { /* create element map with island information */ const bool use_facesel = (ts->uv_flag & UV_SYNC_SELECTION) == 0; - const bool use_uvsel = !is_prop_connected; - elementmap = BM_uv_element_map_create(em->bm, scene, use_facesel, use_uvsel, false, true); + elementmap = BM_uv_element_map_create(em->bm, scene, use_facesel, true, false, true); if (elementmap == NULL) { continue; } - if (is_prop_connected) { - island_enabled = BLI_BITMAP_NEW(elementmap->totalIslands, "TransIslandData(UV Editing)"); - } - - if (is_island_center) { - island_center = MEM_callocN(sizeof(*island_center) * elementmap->totalIslands, __func__); - } + island_center = MEM_callocN(sizeof(*island_center) * elementmap->totalIslands, __func__); } BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { @@ -147,20 +311,14 @@ void createTransUVs(bContext *C, TransInfo *t) if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { countsel++; - if (is_prop_connected || island_center) { + if (island_center) { UvElement *element = BM_uv_element_get(elementmap, efa, l); - if (is_prop_connected) { - BLI_BITMAP_ENABLE(island_enabled, element->island); - } - - if (is_island_center) { - if (element->flag == false) { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - add_v2_v2(island_center[element->island].co, luv->uv); - island_center[element->island].co_num++; - element->flag = true; - } + if (element->flag == false) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + add_v2_v2(island_center[element->island].co, luv->uv); + island_center[element->island].co_num++; + element->flag = true; } } } @@ -198,6 +356,14 @@ void createTransUVs(bContext *C, TransInfo *t) td = tc->data; td2d = tc->data_2d; + float *prop_dists = NULL; + + if (is_prop_connected) { + prop_dists = MEM_callocN(em->bm->totloop * sizeof(float), "TransObPropDists(UV Editing)"); + + uv_set_connectivity_distance(em->bm, prop_dists, t->aspect); + } + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { BMLoop *l; @@ -209,52 +375,41 @@ void createTransUVs(bContext *C, TransInfo *t) const bool selected = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); MLoopUV *luv; const float *center = NULL; + float prop_distance = FLT_MAX; if (!is_prop_edit && !selected) { continue; } - if (is_prop_connected || is_island_center) { + if (is_prop_connected) { + const int idx = BM_elem_index_get(l); + prop_distance = prop_dists[idx]; + } + + if (is_island_center) { UvElement *element = BM_uv_element_get(elementmap, efa, l); if (element) { - if (is_prop_connected) { - if (!BLI_BITMAP_TEST(island_enabled, element->island)) { - count_rejected++; - continue; - } - } - - if (is_island_center) { - center = island_center[element->island].co; - } + center = island_center[element->island].co; } } - BM_elem_flag_enable(l, BM_ELEM_TAG); luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - UVsToTransData(t->aspect, td++, td2d++, luv->uv, center, selected); + UVsToTransData(t->aspect, td++, td2d++, luv->uv, center, prop_distance, selected); } } - if (is_prop_connected) { - tc->data_len -= count_rejected; - } - if (sima->flag & SI_LIVE_UNWRAP) { ED_uvedit_live_unwrap_begin(t->scene, tc->obedit); } finally: - if (is_prop_connected || is_island_center) { + if (is_prop_connected) { + MEM_freeN(prop_dists); + } + if (is_island_center) { BM_uv_element_map_free(elementmap); - if (is_prop_connected) { - MEM_freeN(island_enabled); - } - - if (island_center) { - MEM_freeN(island_center); - } + MEM_freeN(island_center); } } } diff --git a/source/blender/editors/transform/transform_convert_particle.c b/source/blender/editors/transform/transform_convert_particle.c index 3fa722d14cf..fbe9c07ebe9 100644 --- a/source/blender/editors/transform/transform_convert_particle.c +++ b/source/blender/editors/transform/transform_convert_particle.c @@ -183,7 +183,7 @@ void createTransParticleVerts(bContext *C, TransInfo *t) tail++; } if (is_prop_edit && head != tail) { - calc_distanceCurveVerts(head, tail - 1); + calc_distanceCurveVerts(head, tail - 1, false); } } } diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index aee05197f10..cde06a9eaac 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -67,15 +67,27 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_ dist = len_v3(t->num.val); } else { + int i = 0; float dvec[3]; - - copy_v3_v3(dvec, vec); - applyAspectRatio(t, dvec); + if (!(t->flag & T_2D_EDIT) && t->con.mode & CON_APPLY) { + zero_v3(dvec); + if (t->con.mode & CON_AXIS0) { + dvec[i++] = vec[0]; + } + if (t->con.mode & CON_AXIS1) { + dvec[i++] = vec[1]; + } + if (t->con.mode & CON_AXIS2) { + dvec[i++] = vec[2]; + } + } + else { + copy_v3_v3(dvec, vec); + applyAspectRatio(t, dvec); + } dist = len_v3(vec); if (!(t->flag & T_2D_EDIT) && t->scene->unit.system) { - int i; - for (i = 0; i < 3; i++) { bUnit_AsString2(&tvec[NUM_STR_REP_LEN * i], NUM_STR_REP_LEN, diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c index 9e810b9c629..4bcc4cc6383 100644 --- a/source/blender/editors/transform/transform_mode_vert_slide.c +++ b/source/blender/editors/transform/transform_mode_vert_slide.c @@ -500,6 +500,10 @@ static void doVertSlide(TransInfo *t, float perc) FOREACH_TRANS_DATA_CONTAINER (t, tc) { VertSlideData *sld = tc->custom.mode.data; + if (sld == NULL) { + continue; + } + TransDataVertSlideVert *svlist = sld->sv, *sv; int i; diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index 3af176f5aef..6dbf80ed4b9 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -1106,11 +1106,13 @@ static bool raycastObjects(SnapObjectContext *sctx, const float ray_start[3], const float ray_dir[3], /* read/write args */ - float *ray_depth, + /* Parameters below cannot be const, because they are assigned to a + * non-const variable (readability-non-const-parameter). */ + float *ray_depth /* NOLINT */, /* return args */ - float r_loc[3], - float r_no[3], - int *r_index, + float r_loc[3] /* NOLINT */, + float r_no[3] /* NOLINT */, + int *r_index /* NOLINT */, Object **r_ob, float r_obmat[4][4], ListBase *r_hit_list) @@ -2789,11 +2791,13 @@ static short snapObjectsRay(SnapObjectContext *sctx, SnapData *snapdata, const struct SnapObjectParams *params, /* read/write args */ - float *dist_px, + /* Parameters below cannot be const, because they are assigned to a + * non-const variable (readability-non-const-parameter). */ + float *dist_px /* NOLINT */, /* return args */ - float r_loc[3], - float r_no[3], - int *r_index, + float r_loc[3] /* NOLINT */, + float r_no[3] /* NOLINT */, + int *r_index /* NOLINT */, Object **r_ob, float r_obmat[4][4]) { diff --git a/source/blender/editors/uvedit/CMakeLists.txt b/source/blender/editors/uvedit/CMakeLists.txt index b40b82c50fb..a39234561c2 100644 --- a/source/blender/editors/uvedit/CMakeLists.txt +++ b/source/blender/editors/uvedit/CMakeLists.txt @@ -40,6 +40,8 @@ set(SRC uvedit_draw.c uvedit_ops.c uvedit_parametrizer.c + uvedit_path.c + uvedit_rip.c uvedit_select.c uvedit_smart_stitch.c uvedit_unwrap_ops.c @@ -49,6 +51,7 @@ set(SRC ) set(LIB + bf_bmesh ) if(WITH_INTERNATIONAL) diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c index c6b8b0cd02d..9f716739b1c 100644 --- a/source/blender/editors/uvedit/uvedit_draw.c +++ b/source/blender/editors/uvedit/uvedit_draw.c @@ -413,7 +413,7 @@ static void draw_uvs(SpaceImage *sima, UI_GetThemeColor3fv(TH_EDGE_SELECT, col2); col2[3] = overlay_alpha; - float dash_width = (sima->dt_uv & SI_UVDT_DASH) ? (4.0f * UI_DPI_FAC) : 9999.0f; + const float dash_width = (sima->dt_uv == SI_UVDT_DASH) ? (4.0f * UI_DPI_FAC) : 9999.0f; eGPUBuiltinShader shader = (interpedges) ? GPU_SHADER_2D_UV_EDGES_SMOOTH : GPU_SHADER_2D_UV_EDGES; diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index 31384d6df17..6a5f5162dff 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -106,8 +106,13 @@ void UV_OT_pack_islands(struct wmOperatorType *ot); void UV_OT_reset(struct wmOperatorType *ot); void UV_OT_sphere_project(struct wmOperatorType *ot); void UV_OT_unwrap(struct wmOperatorType *ot); +void UV_OT_rip(struct wmOperatorType *ot); void UV_OT_stitch(struct wmOperatorType *ot); +/* uvedit_path.c */ +void UV_OT_shortest_path_pick(struct wmOperatorType *ot); +void UV_OT_shortest_path_select(struct wmOperatorType *ot); + /* uvedit_select.c */ bool uvedit_select_is_any_selected(struct Scene *scene, struct Object *obedit); diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 8d85de3b141..6003d4d5d95 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -2087,7 +2087,10 @@ void ED_operatortypes_uvedit(void) WM_operatortype_append(UV_OT_align); + WM_operatortype_append(UV_OT_rip); WM_operatortype_append(UV_OT_stitch); + WM_operatortype_append(UV_OT_shortest_path_pick); + WM_operatortype_append(UV_OT_shortest_path_select); WM_operatortype_append(UV_OT_seams_from_islands); WM_operatortype_append(UV_OT_mark_seam); @@ -2111,6 +2114,21 @@ void ED_operatortypes_uvedit(void) WM_operatortype_append(UV_OT_cursor_set); } +void ED_operatormacros_uvedit(void) +{ + wmOperatorType *ot; + wmOperatorTypeMacro *otmacro; + + ot = WM_operatortype_append_macro("UV_OT_rip_move", + "UV Rip Move", + "unstitch UV's and move the result", + OPTYPE_UNDO | OPTYPE_REGISTER); + WM_operatortype_macro_define(ot, "UV_OT_rip"); + otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false); + RNA_boolean_set(otmacro->ptr, "mirror", false); +} + void ED_keymap_uvedit(wmKeyConfig *keyconf) { wmKeyMap *keymap; diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c index 921b96f6023..a4ee9a294fe 100644 --- a/source/blender/editors/uvedit/uvedit_parametrizer.c +++ b/source/blender/editors/uvedit/uvedit_parametrizer.c @@ -487,7 +487,7 @@ static void p_chart_uv_scale_xy(PChart *chart, float x, float y) } } -static void p_chart_uv_translate(PChart *chart, float trans[2]) +static void p_chart_uv_translate(PChart *chart, const float trans[2]) { PVert *v; @@ -805,7 +805,7 @@ static PVert *p_vert_copy(PChart *chart, PVert *v) return nv; } -static PEdge *p_edge_lookup(PHandle *handle, PHashKey *vkeys) +static PEdge *p_edge_lookup(PHandle *handle, const PHashKey *vkeys) { PHashKey key = PHASH_edge(vkeys[0], vkeys[1]); PEdge *e = (PEdge *)phash_lookup(handle->hash_edges, key); @@ -1146,14 +1146,14 @@ static PFace *p_face_add(PHandle *handle) static PFace *p_face_add_construct(PHandle *handle, ParamKey key, - ParamKey *vkeys, + const ParamKey *vkeys, float *co[4], float *uv[4], int i1, int i2, int i3, - ParamBool *pin, - ParamBool *select) + const ParamBool *pin, + const ParamBool *select) { PFace *f = p_face_add(handle); PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; diff --git a/source/blender/editors/uvedit/uvedit_path.c b/source/blender/editors/uvedit/uvedit_path.c new file mode 100644 index 00000000000..e9615b62523 --- /dev/null +++ b/source/blender/editors/uvedit/uvedit_path.c @@ -0,0 +1,771 @@ +/* + * 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 eduv + * + * \note The logic in this file closely follows editmesh_path.c + */ + +#include <math.h> +#include <stdlib.h> +#include <string.h> + +#include "BLI_linklist.h" +#include "DNA_windowmanager_types.h" +#include "MEM_guardedalloc.h" + +#include "BLI_ghash.h" +#include "BLI_linklist_stack.h" +#include "BLI_math.h" +#include "BLI_math_vector.h" +#include "BLI_utildefines.h" + +#include "DNA_image_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" + +#include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_editmesh.h" +#include "BKE_layer.h" +#include "BKE_mesh.h" +#include "BKE_report.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "ED_screen.h" +#include "ED_transform.h" +#include "ED_uvedit.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_view2d.h" + +#include "intern/bmesh_marking.h" +#include "uvedit_intern.h" + +#include "bmesh_tools.h" + +/* TODO(campbell): region filling, matching mesh selection. */ +// #define USE_FILL + +/* -------------------------------------------------------------------- */ +/** \name Local Utilities + * \{ */ + +/** + * Support edge-path using vert-path calculation code. + * + * Cheat! Pick 2 closest loops and do vertex path, + * in practices only obscure/contrived cases will make give noticeably worse behavior. + * + * While the code below is a bit awkward, it's significantly less overhead than + * adding full edge selection which is nearly the same as vertex path in the case of UV's. + */ +static void bm_loop_calc_vert_pair_from_edge_pair(const int cd_loop_uv_offset, + const float aspect_y, + BMElem **ele_src_p, + BMElem **ele_dst_p, + BMElem **r_ele_dst_final) +{ + BMLoop *l_src = (BMLoop *)*ele_src_p; + BMLoop *l_dst = (BMLoop *)*ele_dst_p; + + const MLoopUV *luv_src_v1 = BM_ELEM_CD_GET_VOID_P(l_src, cd_loop_uv_offset); + const MLoopUV *luv_src_v2 = BM_ELEM_CD_GET_VOID_P(l_src->next, cd_loop_uv_offset); + const MLoopUV *luv_dst_v1 = BM_ELEM_CD_GET_VOID_P(l_dst, cd_loop_uv_offset); + const MLoopUV *luv_dst_v2 = BM_ELEM_CD_GET_VOID_P(l_dst->next, cd_loop_uv_offset); + + const float uv_src_v1[2] = {luv_src_v1->uv[0], luv_src_v1->uv[1] / aspect_y}; + const float uv_src_v2[2] = {luv_src_v2->uv[0], luv_src_v2->uv[1] / aspect_y}; + const float uv_dst_v1[2] = {luv_dst_v1->uv[0], luv_dst_v1->uv[1] / aspect_y}; + const float uv_dst_v2[2] = {luv_dst_v2->uv[0], luv_dst_v2->uv[1] / aspect_y}; + + struct { + int src_index; + int dst_index; + float len_sq; + } tests[4] = { + {0, 0, len_squared_v2v2(uv_src_v1, uv_dst_v1)}, + {0, 1, len_squared_v2v2(uv_src_v1, uv_dst_v2)}, + {1, 0, len_squared_v2v2(uv_src_v2, uv_dst_v1)}, + {1, 1, len_squared_v2v2(uv_src_v2, uv_dst_v2)}, + }; + int i_best = 0; + for (int i = 1; i < ARRAY_SIZE(tests); i++) { + if (tests[i].len_sq < tests[i_best].len_sq) { + i_best = i; + } + } + + *ele_src_p = (BMElem *)(tests[i_best].src_index ? l_src->next : l_src); + *ele_dst_p = (BMElem *)(tests[i_best].dst_index ? l_dst->next : l_dst); + + /* Ensure the edge is selected, not just the vertices up until we hit it. */ + *r_ele_dst_final = (BMElem *)(tests[i_best].dst_index ? l_dst : l_dst->next); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Path Select Struct & Properties + * \{ */ + +struct PathSelectParams { + /** ensure the active element is the last selected item (handy for picking) */ + bool track_active; + bool use_topology_distance; + bool use_face_step; +#ifdef USE_FILL + bool use_fill; +#endif + struct CheckerIntervalParams interval_params; +}; + +struct UserData_UV { + Scene *scene; + uint cd_loop_uv_offset; +}; + +static void path_select_properties(wmOperatorType *ot) +{ + RNA_def_boolean(ot->srna, + "use_face_step", + false, + "Face Stepping", + "Traverse connected faces (includes diagonals and edge-rings)"); + RNA_def_boolean(ot->srna, + "use_topology_distance", + false, + "Topology Distance", + "Find the minimum number of steps, ignoring spatial distance"); +#ifdef USE_FILL + RNA_def_boolean(ot->srna, + "use_fill", + false, + "Fill Region", + "Select all paths between the source/destination elements"); +#endif + + WM_operator_properties_checker_interval(ot, true); +} + +static void path_select_params_from_op(wmOperator *op, struct PathSelectParams *op_params) +{ + op_params->track_active = false; + op_params->use_face_step = RNA_boolean_get(op->ptr, "use_face_step"); +#ifdef USE_FILL + op_params->use_fill = RNA_boolean_get(op->ptr, "use_fill"); +#endif + op_params->use_topology_distance = RNA_boolean_get(op->ptr, "use_topology_distance"); + WM_operator_properties_checker_interval_from_op(op, &op_params->interval_params); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name UV Vert Path + * \{ */ + +/* callbacks */ +static bool looptag_filter_cb(BMLoop *l, void *user_data_v) +{ + struct UserData_UV *user_data = user_data_v; + return uvedit_face_visible_test(user_data->scene, l->f); +} +static bool looptag_test_cb(BMLoop *l, void *user_data_v) +{ + /* All connected loops are selected or we return false. */ + struct UserData_UV *user_data = user_data_v; + const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset; + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + BMIter iter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) { + if (looptag_filter_cb(l_iter, user_data)) { + const MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); + if (equals_v2v2(luv->uv, luv_iter->uv)) { + if ((luv_iter->flag & MLOOPUV_VERTSEL) == 0) { + return false; + } + } + } + } + return true; +} +static void looptag_set_cb(BMLoop *l, bool val, void *user_data_v) +{ + struct UserData_UV *user_data = user_data_v; + const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset; + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + BMIter iter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) { + if (looptag_filter_cb(l_iter, user_data)) { + MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); + if (equals_v2v2(luv->uv, luv_iter->uv)) { + SET_FLAG_FROM_TEST(luv_iter->flag, val, MLOOPUV_VERTSEL); + } + } + } +} + +static void mouse_mesh_uv_shortest_path_vert(Scene *scene, + Object *obedit, + const struct PathSelectParams *op_params, + BMLoop *l_src, + BMLoop *l_dst, + BMLoop *l_dst_add_to_path, + const float aspect_y, + const int cd_loop_uv_offset) +{ + const ToolSettings *ts = scene->toolsettings; + const bool use_fake_edge_select = (ts->uv_selectmode & UV_SELECT_EDGE); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + + struct UserData_UV user_data = { + .scene = scene, + .cd_loop_uv_offset = cd_loop_uv_offset, + }; + + const struct BMCalcPathUVParams params = { + .use_topology_distance = op_params->use_topology_distance, + .use_step_face = op_params->use_face_step, + .aspect_y = aspect_y, + .cd_loop_uv_offset = cd_loop_uv_offset, + }; + LinkNode *path = BM_mesh_calc_path_uv_vert( + bm, l_src, l_dst, ¶ms, looptag_filter_cb, &user_data); + /* TODO: false when we support region selection. */ + bool is_path_ordered = true; + + BMLoop *l_dst_last = l_dst; + + if (path) { + if ((l_dst_add_to_path != NULL) && (BLI_linklist_index(path, l_dst_add_to_path) == -1)) { + /* Append, this isn't optimal compared to #BLI_linklist_append, it's a one-off lookup. */ + LinkNode *path_last = BLI_linklist_find_last(path); + BLI_linklist_insert_after(&path_last, l_dst_add_to_path); + BLI_assert(BLI_linklist_find_last(path)->link == l_dst_add_to_path); + } + + /* toggle the flag */ + bool all_set = true; + LinkNode *node = path; + do { + if (!looptag_test_cb((BMLoop *)node->link, &user_data)) { + all_set = false; + break; + } + } while ((node = node->next)); + + int depth = -1; + node = path; + do { + if ((is_path_ordered == false) || + WM_operator_properties_checker_interval_test(&op_params->interval_params, depth)) { + looptag_set_cb((BMLoop *)node->link, !all_set, &user_data); + if (is_path_ordered) { + l_dst_last = node->link; + } + } + } while ((void)depth++, (node = node->next)); + + BLI_linklist_free(path, NULL); + } + else { + const bool is_act = !looptag_test_cb(l_dst, &user_data); + looptag_set_cb(l_dst, is_act, &user_data); /* switch the face option */ + } + + if (op_params->track_active) { + /* Fake edge selection. */ + if (use_fake_edge_select) { + ED_uvedit_active_edge_loop_set(bm, l_dst_last); + } + else { + ED_uvedit_active_vert_loop_set(bm, l_dst_last); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name UV Face Path + * \{ */ + +/* callbacks */ +static bool facetag_filter_cb(BMFace *f, void *user_data_v) +{ + struct UserData_UV *user_data = user_data_v; + return uvedit_face_visible_test(user_data->scene, f); +} +static bool facetag_test_cb(BMFace *f, void *user_data_v) +{ + /* All connected loops are selected or we return false. */ + struct UserData_UV *user_data = user_data_v; + const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset; + BMIter iter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &iter, f, BM_LOOPS_OF_FACE) { + const MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); + if ((luv_iter->flag & MLOOPUV_VERTSEL) == 0) { + return false; + } + } + return true; +} +static void facetag_set_cb(BMFace *f, bool val, void *user_data_v) +{ + struct UserData_UV *user_data = user_data_v; + const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset; + BMIter iter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &iter, f, BM_LOOPS_OF_FACE) { + MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); + SET_FLAG_FROM_TEST(luv_iter->flag, val, MLOOPUV_VERTSEL); + } +} + +static void mouse_mesh_uv_shortest_path_face(Scene *scene, + Object *obedit, + const struct PathSelectParams *op_params, + BMFace *f_src, + BMFace *f_dst, + const float aspect_y, + const int cd_loop_uv_offset) +{ + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + + struct UserData_UV user_data = { + .scene = scene, + .cd_loop_uv_offset = cd_loop_uv_offset, + }; + + const struct BMCalcPathUVParams params = { + .use_topology_distance = op_params->use_topology_distance, + .use_step_face = op_params->use_face_step, + .aspect_y = aspect_y, + .cd_loop_uv_offset = cd_loop_uv_offset, + }; + LinkNode *path = BM_mesh_calc_path_uv_face( + bm, f_src, f_dst, ¶ms, facetag_filter_cb, &user_data); + /* TODO: false when we support region selection. */ + bool is_path_ordered = true; + + BMFace *f_dst_last = f_dst; + + if (path) { + /* toggle the flag */ + bool all_set = true; + LinkNode *node = path; + do { + if (!facetag_test_cb((BMFace *)node->link, &user_data)) { + all_set = false; + break; + } + } while ((node = node->next)); + + int depth = -1; + node = path; + do { + if ((is_path_ordered == false) || + WM_operator_properties_checker_interval_test(&op_params->interval_params, depth)) { + facetag_set_cb((BMFace *)node->link, !all_set, &user_data); + if (is_path_ordered) { + f_dst_last = node->link; + } + } + } while ((void)depth++, (node = node->next)); + + BLI_linklist_free(path, NULL); + } + else { + const bool is_act = !facetag_test_cb(f_dst, &user_data); + facetag_set_cb(f_dst, is_act, &user_data); /* switch the face option */ + } + + if (op_params->track_active) { + /* Unlike other types, we can track active without it being selected. */ + BM_mesh_active_face_set(bm, f_dst_last); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Main Operator for vert/edge/face tag + * \{ */ + +static int uv_shortest_path_pick_exec(bContext *C, wmOperator *op); + +static bool uv_shortest_path_pick_ex(Scene *scene, + Depsgraph *depsgraph, + Object *obedit, + const struct PathSelectParams *op_params, + BMElem *ele_src, + BMElem *ele_dst, + const float aspect_y, + const int cd_loop_uv_offset) +{ + bool ok = false; + + if (ELEM(NULL, ele_src, ele_dst) || (ele_src->head.htype != ele_dst->head.htype)) { + /* pass */ + } + else if (ele_src->head.htype == BM_FACE) { + mouse_mesh_uv_shortest_path_face(scene, + obedit, + op_params, + (BMFace *)ele_src, + (BMFace *)ele_dst, + aspect_y, + cd_loop_uv_offset); + ok = true; + } + else if (ele_src->head.htype == BM_LOOP) { + const ToolSettings *ts = scene->toolsettings; + BMElem *ele_dst_final = NULL; + if (ts->uv_selectmode & UV_SELECT_EDGE) { + bm_loop_calc_vert_pair_from_edge_pair( + cd_loop_uv_offset, aspect_y, &ele_src, &ele_dst, &ele_dst_final); + } + mouse_mesh_uv_shortest_path_vert(scene, + obedit, + op_params, + (BMLoop *)ele_src, + (BMLoop *)ele_dst, + (BMLoop *)ele_dst_final, + aspect_y, + cd_loop_uv_offset); + ok = true; + } + + if (ok) { + Object *obedit_eval = DEG_get_evaluated_object(depsgraph, obedit); + BKE_mesh_batch_cache_dirty_tag(obedit_eval->data, BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT); + /* Only for region redraw. */ + WM_main_add_notifier(NC_GEOM | ND_SELECT, obedit->data); + } + + return ok; +} + +static int uv_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Scene *scene = CTX_data_scene(C); + const ToolSettings *ts = scene->toolsettings; + + /* We could support this, it needs further testing. */ + if (ts->uv_flag & UV_SYNC_SELECTION) { + BKE_report(op->reports, RPT_ERROR, "Sync selection doesn't support path select"); + return OPERATOR_CANCELLED; + } + + if (RNA_struct_property_is_set(op->ptr, "index")) { + return uv_shortest_path_pick_exec(C, op); + } + + struct PathSelectParams op_params; + path_select_params_from_op(op, &op_params); + + /* Set false if we support edge tagging. */ + op_params.track_active = true; + + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + + float co[2]; + + const ARegion *region = CTX_wm_region(C); + + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + + float aspect_y; + { + float aspx, aspy; + ED_uvedit_get_aspect(obedit, &aspx, &aspy); + aspect_y = aspx / aspy; + } + + UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); + + BMElem *ele_src = NULL, *ele_dst = NULL; + + if (ts->uv_selectmode & UV_SELECT_FACE) { + UvNearestHit hit = UV_NEAREST_HIT_INIT; + if (!uv_find_nearest_face(scene, obedit, co, &hit)) { + return OPERATOR_CANCELLED; + } + + BMFace *f_src = BM_mesh_active_face_get(bm, false, false); + /* Check selection? */ + + ele_src = (BMElem *)f_src; + ele_dst = (BMElem *)hit.efa; + } + else if (ts->uv_selectmode & UV_SELECT_EDGE) { + UvNearestHit hit = UV_NEAREST_HIT_INIT; + if (!uv_find_nearest_edge(scene, obedit, co, &hit)) { + return OPERATOR_CANCELLED; + } + + BMLoop *l_src = ED_uvedit_active_edge_loop_get(bm); + const MLoopUV *luv_src_v1 = BM_ELEM_CD_GET_VOID_P(l_src, cd_loop_uv_offset); + const MLoopUV *luv_src_v2 = BM_ELEM_CD_GET_VOID_P(l_src->next, cd_loop_uv_offset); + if ((luv_src_v1->flag & MLOOPUV_VERTSEL) == 0 && (luv_src_v2->flag & MLOOPUV_VERTSEL) == 0) { + l_src = NULL; + } + + ele_src = (BMElem *)l_src; + ele_dst = (BMElem *)hit.l; + } + else { + UvNearestHit hit = UV_NEAREST_HIT_INIT; + if (!uv_find_nearest_vert(scene, obedit, co, 0.0f, &hit)) { + return OPERATOR_CANCELLED; + } + + BMLoop *l_src = ED_uvedit_active_vert_loop_get(bm); + const MLoopUV *luv_src = BM_ELEM_CD_GET_VOID_P(l_src, cd_loop_uv_offset); + if ((luv_src->flag & MLOOPUV_VERTSEL) == 0) { + l_src = NULL; + } + + ele_src = (BMElem *)l_src; + ele_dst = (BMElem *)hit.l; + } + + if (ele_src == NULL || ele_dst == NULL) { + return OPERATOR_CANCELLED; + } + + uv_shortest_path_pick_ex( + scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, cd_loop_uv_offset); + + /* To support redo. */ + int index; + if (ts->uv_selectmode & UV_SELECT_FACE) { + BM_mesh_elem_index_ensure(bm, BM_FACE); + index = BM_elem_index_get(ele_dst); + } + else if (ts->uv_selectmode & UV_SELECT_EDGE) { + BM_mesh_elem_index_ensure(bm, BM_LOOP); + index = BM_elem_index_get(ele_dst); + } + else { + BM_mesh_elem_index_ensure(bm, BM_LOOP); + index = BM_elem_index_get(ele_dst); + } + RNA_int_set(op->ptr, "index", index); + + return OPERATOR_FINISHED; +} + +static int uv_shortest_path_pick_exec(bContext *C, wmOperator *op) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + const ToolSettings *ts = scene->toolsettings; + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + + float aspect_y; + { + float aspx, aspy; + ED_uvedit_get_aspect(obedit, &aspx, &aspy); + aspect_y = aspx / aspy; + } + + const int index = RNA_int_get(op->ptr, "index"); + + BMElem *ele_src, *ele_dst; + + if (ts->uv_selectmode & UV_SELECT_FACE) { + if (index < 0 || index >= bm->totface) { + return OPERATOR_CANCELLED; + } + if (!(ele_src = (BMElem *)BM_mesh_active_face_get(bm, false, false)) || + !(ele_dst = (BMElem *)BM_face_at_index_find_or_table(bm, index))) { + return OPERATOR_CANCELLED; + } + } + else if (ts->uv_selectmode & UV_SELECT_EDGE) { + if (index < 0 || index >= bm->totloop) { + return OPERATOR_CANCELLED; + } + if (!(ele_src = (BMElem *)ED_uvedit_active_edge_loop_get(bm)) || + !(ele_dst = (BMElem *)BM_loop_at_index_find(bm, index))) { + return OPERATOR_CANCELLED; + } + } + else { + if (index < 0 || index >= bm->totloop) { + return OPERATOR_CANCELLED; + } + if (!(ele_src = (BMElem *)ED_uvedit_active_vert_loop_get(bm)) || + !(ele_dst = (BMElem *)BM_loop_at_index_find(bm, index))) { + return OPERATOR_CANCELLED; + } + } + + struct PathSelectParams op_params; + path_select_params_from_op(op, &op_params); + op_params.track_active = true; + + if (!uv_shortest_path_pick_ex( + scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, cd_loop_uv_offset)) { + return OPERATOR_CANCELLED; + } + + return OPERATOR_FINISHED; +} + +void UV_OT_shortest_path_pick(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Pick Shortest Path"; + ot->idname = "UV_OT_shortest_path_pick"; + ot->description = "Select shortest path between two selections"; + + /* api callbacks */ + ot->invoke = uv_shortest_path_pick_invoke; + ot->exec = uv_shortest_path_pick_exec; + ot->poll = ED_operator_uvedit; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + path_select_properties(ot); + + /* use for redo */ + prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Path Between Existing Selection + * \{ */ + +static int uv_shortest_path_select_exec(bContext *C, wmOperator *op) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + const ToolSettings *ts = scene->toolsettings; + bool found_valid_elements = false; + + float aspect_y; + { + Object *obedit = CTX_data_edit_object(C); + float aspx, aspy; + ED_uvedit_get_aspect(obedit, &aspx, &aspy); + aspect_y = aspx / aspy; + } + + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( + view_layer, CTX_wm_view3d(C), &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + BMElem *ele_src = NULL, *ele_dst = NULL; + + /* Find 2x elements. */ + { + BMElem **ele_array = NULL; + int ele_array_len = 0; + if (ts->uv_selectmode & UV_SELECT_FACE) { + ele_array = (BMElem **)ED_uvedit_selected_faces(scene, bm, 3, &ele_array_len); + } + else if (ts->uv_selectmode & UV_SELECT_EDGE) { + ele_array = (BMElem **)ED_uvedit_selected_edges(scene, bm, 3, &ele_array_len); + } + else { + ele_array = (BMElem **)ED_uvedit_selected_verts(scene, bm, 3, &ele_array_len); + } + + if (ele_array_len == 2) { + ele_src = ele_array[0]; + ele_dst = ele_array[1]; + } + MEM_freeN(ele_array); + } + + if (ele_src && ele_dst) { + struct PathSelectParams op_params; + path_select_params_from_op(op, &op_params); + + uv_shortest_path_pick_ex( + scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, cd_loop_uv_offset); + + found_valid_elements = true; + } + } + MEM_freeN(objects); + + if (!found_valid_elements) { + BKE_report( + op->reports, RPT_WARNING, "Path selection requires two matching elements to be selected"); + return OPERATOR_CANCELLED; + } + + return OPERATOR_FINISHED; +} + +void UV_OT_shortest_path_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Shortest Path"; + ot->idname = "UV_OT_shortest_path_select"; + ot->description = "Selected shortest path between two vertices/edges/faces"; + + /* api callbacks */ + ot->exec = uv_shortest_path_select_exec; + ot->poll = ED_operator_editmesh; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + path_select_properties(ot); +} + +/** \} */ diff --git a/source/blender/editors/uvedit/uvedit_rip.c b/source/blender/editors/uvedit/uvedit_rip.c new file mode 100644 index 00000000000..562f0ce84c1 --- /dev/null +++ b/source/blender/editors/uvedit/uvedit_rip.c @@ -0,0 +1,981 @@ +/* + * 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 eduv + */ + +#include <math.h> +#include <stdlib.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_ghash.h" +#include "BLI_linklist_stack.h" +#include "BLI_math.h" +#include "BLI_math_vector.h" +#include "BLI_utildefines.h" + +#include "DNA_image_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" + +#include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_editmesh.h" +#include "BKE_layer.h" +#include "BKE_report.h" + +#include "DEG_depsgraph.h" + +#include "ED_screen.h" +#include "ED_transform.h" +#include "ED_uvedit.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_view2d.h" + +#include "uvedit_intern.h" + +/* -------------------------------------------------------------------- */ +/** \name UV Loop Rip Data Struct + * \{ */ + +/** Unordered loop data, stored in #BMLoop.head.index. */ +typedef struct ULData { + /** When this UV is selected as well as the next UV. */ + uint is_select_edge : 1; + /** + * When only this UV is selected and none of the other UV's + * around the connected fan are attached to an edge. + * + * In this case there is no need to detect contiguous loops, + * each isolated case is handled on it's own, no need to walk over selected edges. + * + * \note This flag isn't flushed to other loops which could also have this enabled. + * Currently it's not necessary since we can start off on any one of these loops, + * then walk onto the other loops around the uv-fan, without having the flag to be + * set on all loops. + */ + uint is_select_vert_single : 1; + /** This could be a face-tag. */ + uint is_select_all : 1; + /** Use when building the rip-pairs stack. */ + uint in_stack : 1; + /** Set once this has been added into a #UVRipPairs. */ + uint in_rip_pairs : 1; + /** The side this loop is part of. */ + uint side : 1; + /** + * Paranoid check to ensure we don't enter eternal loop swapping sides, + * this could happen with float precision error, making a swap to measure as slightly better + * depending on the order of addition. + */ + uint side_was_swapped : 1; +} ULData; + +/** Ensure this fits in an int (loop index). */ +BLI_STATIC_ASSERT(sizeof(ULData) <= sizeof(int), ""); + +BLI_INLINE ULData *UL(BMLoop *l) +{ + return (ULData *)&l->head.index; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name UV Utilities + * \{ */ + +static BMLoop *bm_loop_find_other_radial_loop_with_visible_face(BMLoop *l_src, + const int cd_loop_uv_offset) +{ + BMLoop *l_other = NULL; + BMLoop *l_iter = l_src->radial_next; + if (l_iter != l_src) { + do { + if (BM_elem_flag_test(l_iter->f, BM_ELEM_TAG) && UL(l_iter)->is_select_edge && + BM_loop_uv_share_edge_check(l_src, l_iter, cd_loop_uv_offset)) { + /* Check UV's are contiguous. */ + if (l_other == NULL) { + l_other = l_iter; + } + else { + /* Only use when there is a single alternative. */ + l_other = NULL; + break; + } + } + } while ((l_iter = l_iter->radial_next) != l_src); + } + return l_other; +} + +static BMLoop *bm_loop_find_other_fan_loop_with_visible_face(BMLoop *l_src, + BMVert *v_src, + const int cd_loop_uv_offset) +{ + BLI_assert(BM_vert_in_edge(l_src->e, v_src)); + BMLoop *l_other = NULL; + BMLoop *l_iter = l_src->radial_next; + if (l_iter != l_src) { + do { + if (BM_elem_flag_test(l_iter->f, BM_ELEM_TAG) && + BM_loop_uv_share_edge_check(l_src, l_iter, cd_loop_uv_offset)) { + /* Check UV's are contiguous. */ + if (l_other == NULL) { + l_other = l_iter; + } + else { + /* Only use when there is a single alternative. */ + l_other = NULL; + break; + } + } + } while ((l_iter = l_iter->radial_next) != l_src); + } + if (l_other != NULL) { + if (l_other->v == v_src) { + /* do nothing. */ + } + else if (l_other->next->v == v_src) { + l_other = l_other->next; + } + else if (l_other->prev->v == v_src) { + l_other = l_other->prev; + } + else { + BLI_assert(0); + } + } + return l_other; +} + +/** + * A version of #BM_vert_step_fan_loop that checks UV's. + */ +static BMLoop *bm_vert_step_fan_loop_uv(BMLoop *l, BMEdge **e_step, const int cd_loop_uv_offset) +{ + BMEdge *e_prev = *e_step; + BMLoop *l_next; + if (l->e == e_prev) { + l_next = l->prev; + } + else if (l->prev->e == e_prev) { + l_next = l; + } + else { + BLI_assert(0); + return NULL; + } + + *e_step = l_next->e; + + return bm_loop_find_other_fan_loop_with_visible_face(l_next, l->v, cd_loop_uv_offset); +} + +static void bm_loop_uv_select_single_vert_validate(BMLoop *l_init, const int cd_loop_uv_offset) +{ + const MLoopUV *luv_init = BM_ELEM_CD_GET_VOID_P(l_init, cd_loop_uv_offset); + BMIter liter; + BMLoop *l; + bool is_single_vert = true; + BM_ITER_ELEM (l, &liter, l_init->v, BM_LOOPS_OF_VERT) { + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (equals_v2v2(luv_init->uv, luv->uv)) { + if (UL(l->prev)->is_select_edge || UL(l)->is_select_edge) { + is_single_vert = false; + break; + } + } + } + if (is_single_vert == false) { + BM_ITER_ELEM (l, &liter, l_init->v, BM_LOOPS_OF_VERT) { + if (UL(l)->is_select_vert_single) { + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (equals_v2v2(luv_init->uv, luv->uv)) { + UL(l)->is_select_vert_single = false; + } + } + } + } +} + +/** + * The corner return values calculate the angle between both loops, + * the edge values pick the closest to the either edge (ignoring the center). + * + * \param dir: Direction to calculate the angle to (normalized and aspect corrected). + */ +static void bm_loop_calc_uv_angle_from_dir(BMLoop *l, + const float dir[2], + const float aspect_y, + const int cd_loop_uv_offset, + float *r_corner_angle, + float *r_edge_angle, + int *r_edge_index) +{ + /* Calculate 3 directions, return the shortest angle. */ + float dir_test[3][2]; + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + const MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset); + const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); + + sub_v2_v2v2(dir_test[0], luv->uv, luv_prev->uv); + sub_v2_v2v2(dir_test[2], luv->uv, luv_next->uv); + dir_test[0][1] /= aspect_y; + dir_test[2][1] /= aspect_y; + + normalize_v2(dir_test[0]); + normalize_v2(dir_test[2]); + + /* Calculate the orthogonal line (same as negating one, then adding). */ + sub_v2_v2v2(dir_test[1], dir_test[0], dir_test[2]); + normalize_v2(dir_test[1]); + + /* Rotate 90 degrees. */ + SWAP(float, dir_test[1][0], dir_test[1][1]); + dir_test[1][1] *= -1.0f; + + if (BM_face_uv_calc_cross(l->f, cd_loop_uv_offset) > 0.0f) { + negate_v2(dir_test[1]); + } + + const float angles[3] = { + angle_v2v2(dir, dir_test[0]), + angle_v2v2(dir, dir_test[1]), + angle_v2v2(dir, dir_test[2]), + }; + + /* Set the corner values. */ + *r_corner_angle = angles[1]; + + /* Set the edge values. */ + if (angles[0] < angles[2]) { + *r_edge_angle = angles[0]; + *r_edge_index = -1; + } + else { + *r_edge_angle = angles[2]; + *r_edge_index = 1; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name UV Rip Single + * \{ */ + +typedef struct UVRipSingle { + /** Walk around the selected UV point, store #BMLoop. */ + GSet *loops; +} UVRipSingle; + +/** + * Handle single loop, the following cases: + * + * - An isolated fan, without a shared UV edge to other fans which share the same coordinate, + * in this case we just need to pick the closest fan to \a co. + * + * - In the case of contiguous loops (part of the same fan). + * Rip away the loops connected to the closest edge. + * + * - In the case of 2 contiguous loops. + * Rip the closest loop away. + * + * \note This matches the behavior of edit-mesh rip tool. + */ +static UVRipSingle *uv_rip_single_from_loop(BMLoop *l_init_orig, + const float co[2], + const float aspect_y, + const int cd_loop_uv_offset) +{ + UVRipSingle *rip = MEM_callocN(sizeof(*rip), __func__); + const float *co_center = + (((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_init_orig, cd_loop_uv_offset))->uv); + rip->loops = BLI_gset_ptr_new(__func__); + + /* Track the closest loop, start walking from this so in the event we have multiple + * disconnected fans, we can rip away loops connected to this one. */ + BMLoop *l_init = NULL; + BMLoop *l_init_edge = NULL; + float corner_angle_best = FLT_MAX; + float edge_angle_best = FLT_MAX; + int edge_index_best = 0; /* -1 or +1 (never center). */ + + /* Calculate the direction from the cursor with aspect correction. */ + float dir_co[2]; + sub_v2_v2v2(dir_co, co_center, co); + dir_co[1] /= aspect_y; + if (UNLIKELY(normalize_v2(dir_co) == 0.0)) { + dir_co[1] = 1.0f; + } + + int uv_fan_count_all = 0; + { + BMIter liter; + BMLoop *l; + BM_ITER_ELEM (l, &liter, l_init_orig->v, BM_LOOPS_OF_VERT) { + if (BM_elem_flag_test(l->f, BM_ELEM_TAG)) { + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (equals_v2v2(co_center, luv->uv)) { + uv_fan_count_all += 1; + /* Clear at the same time. */ + UL(l)->is_select_vert_single = true; + UL(l)->side = 0; + BLI_gset_add(rip->loops, l); + + /* Update `l_init_close` */ + float corner_angle_test; + float edge_angle_test; + int edge_index_test; + bm_loop_calc_uv_angle_from_dir(l, + dir_co, + aspect_y, + cd_loop_uv_offset, + &corner_angle_test, + &edge_angle_test, + &edge_index_test); + if ((corner_angle_best == FLT_MAX) || (corner_angle_test < corner_angle_best)) { + corner_angle_best = corner_angle_test; + l_init = l; + } + + /* Trick so we don't consider concave corners further away than they should be. */ + edge_angle_test = min_ff(corner_angle_test, edge_angle_test); + + if ((edge_angle_best == FLT_MAX) || (edge_angle_test < edge_angle_best)) { + edge_angle_best = edge_angle_test; + edge_index_best = edge_index_test; + l_init_edge = l; + } + } + } + } + } + + /* Walk around the `l_init` in both directions of the UV fan. */ + int uv_fan_count_contiguous = 1; + UL(l_init)->side = 1; + for (int i = 0; i < 2; i += 1) { + BMEdge *e_prev = i ? l_init->e : l_init->prev->e; + BMLoop *l_iter = l_init; + while (((l_iter = bm_vert_step_fan_loop_uv(l_iter, &e_prev, cd_loop_uv_offset)) != l_init) && + (l_iter != NULL) && (UL(l_iter)->side == 0)) { + uv_fan_count_contiguous += 1; + /* Keep. */ + UL(l_iter)->side = 1; + } + /* May be useful to know if the fan is closed, currently it's not needed. */ +#if 0 + if (l_iter == l_init) { + is_closed = true; + } +#endif + } + + if (uv_fan_count_contiguous != uv_fan_count_all) { + /* Simply rip off the current fan, all tagging is done. */ + } + else { + GSetIterator gs_iter; + GSET_ITER (gs_iter, rip->loops) { + BMLoop *l = BLI_gsetIterator_getKey(&gs_iter); + UL(l)->side = 0; + } + + if (uv_fan_count_contiguous <= 2) { + /* Simple case, rip away the closest loop. */ + UL(l_init)->side = 1; + } + else { + /* Rip away from the closest edge. */ + BMLoop *l_radial_init = (edge_index_best == -1) ? l_init_edge->prev : l_init_edge; + BMLoop *l_radial_iter = l_radial_init; + do { + if (BM_loop_uv_share_edge_check(l_radial_init, l_radial_iter, cd_loop_uv_offset)) { + BMLoop *l = (l_radial_iter->v == l_init->v) ? l_radial_iter : l_radial_iter->next; + BLI_assert(l->v == l_init->v); + /* Keep. */ + UL(l)->side = 1; + } + } while ((l_radial_iter = l_radial_iter->radial_next) != l_radial_init); + } + } + + return rip; +} + +static void uv_rip_single_free(UVRipSingle *rip) +{ + BLI_gset_free(rip->loops, NULL); + MEM_freeN(rip); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name UV Rip Loop Pairs + * \{ */ + +typedef struct UVRipPairs { + /** Walk along the UV selection, store #BMLoop. */ + GSet *loops; +} UVRipPairs; + +static void uv_rip_pairs_add(UVRipPairs *rip, BMLoop *l) +{ + ULData *ul = UL(l); + BLI_assert(!BLI_gset_haskey(rip->loops, l)); + BLI_assert(ul->in_rip_pairs == false); + ul->in_rip_pairs = true; + BLI_gset_add(rip->loops, l); +} + +static void uv_rip_pairs_remove(UVRipPairs *rip, BMLoop *l) +{ + ULData *ul = UL(l); + BLI_assert(BLI_gset_haskey(rip->loops, l)); + BLI_assert(ul->in_rip_pairs == true); + ul->in_rip_pairs = false; + BLI_gset_remove(rip->loops, l, NULL); +} + +/** + * \note While this isn't especially efficient, + * this is only needed for rip-pairs end-points (only two per contiguous selection loop). + */ +static float uv_rip_pairs_calc_uv_angle(BMLoop *l_init, + uint side, + const float aspect_y, + const int cd_loop_uv_offset) +{ + BMIter liter; + const MLoopUV *luv_init = BM_ELEM_CD_GET_VOID_P(l_init, cd_loop_uv_offset); + float angle_of_side = 0.0f; + BMLoop *l; + BM_ITER_ELEM (l, &liter, l_init->v, BM_LOOPS_OF_VERT) { + if (UL(l)->in_rip_pairs) { + if (UL(l)->side == side) { + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (equals_v2v2(luv_init->uv, luv->uv)) { + const MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset); + const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); + float dir_prev[2], dir_next[2]; + sub_v2_v2v2(dir_prev, luv_prev->uv, luv->uv); + sub_v2_v2v2(dir_next, luv_next->uv, luv->uv); + dir_prev[1] /= aspect_y; + dir_next[1] /= aspect_y; + const float luv_angle = angle_v2v2(dir_prev, dir_next); + if (LIKELY(isfinite(luv_angle))) { + angle_of_side += luv_angle; + } + } + } + } + } + return angle_of_side; +} + +static int uv_rip_pairs_loop_count_on_side(BMLoop *l_init, uint side, const int cd_loop_uv_offset) +{ + const MLoopUV *luv_init = BM_ELEM_CD_GET_VOID_P(l_init, cd_loop_uv_offset); + int count = 0; + BMIter liter; + BMLoop *l; + BM_ITER_ELEM (l, &liter, l_init->v, BM_LOOPS_OF_VERT) { + if (UL(l)->in_rip_pairs) { + if (UL(l)->side == side) { + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (equals_v2v2(luv_init->uv, luv->uv)) { + count += 1; + } + } + } + } + return count; +} + +static bool uv_rip_pairs_loop_change_sides_test(BMLoop *l_switch, + BMLoop *l_target, + const float aspect_y, + const int cd_loop_uv_offset) +{ + const int side_a = UL(l_switch)->side; + const int side_b = UL(l_target)->side; + + BLI_assert(UL(l_switch)->side != UL(l_target)->side); + + /* First, check if this is a simple grid topology, + * in that case always choose the adjacent edge. */ + const int count_a = uv_rip_pairs_loop_count_on_side(l_switch, side_a, cd_loop_uv_offset); + const int count_b = uv_rip_pairs_loop_count_on_side(l_target, side_b, cd_loop_uv_offset); + if (count_a + count_b == 4) { + return count_a > count_b; + } + else { + const float angle_a_before = uv_rip_pairs_calc_uv_angle( + l_switch, side_a, aspect_y, cd_loop_uv_offset); + const float angle_b_before = uv_rip_pairs_calc_uv_angle( + l_target, side_b, aspect_y, cd_loop_uv_offset); + + UL(l_switch)->side = side_b; + + const float angle_a_after = uv_rip_pairs_calc_uv_angle( + l_switch, side_a, aspect_y, cd_loop_uv_offset); + const float angle_b_after = uv_rip_pairs_calc_uv_angle( + l_target, side_b, aspect_y, cd_loop_uv_offset); + + UL(l_switch)->side = side_a; + + return fabsf(angle_a_before - angle_b_before) > fabsf(angle_a_after - angle_b_after); + } +} + +/** + * Create 2x sides of a UV rip-pairs, the result is unordered, supporting non-contiguous rails. + * + * \param l_init: A loop on a boundary which can be used to initialize flood-filling. + * This will always be added to the first side. Other loops will be added to the second side. + * + * \note We could have more than two sides, however in practice this almost never happens. + */ +static UVRipPairs *uv_rip_pairs_from_loop(BMLoop *l_init, + const float aspect_y, + const int cd_loop_uv_offset) +{ + UVRipPairs *rip = MEM_callocN(sizeof(*rip), __func__); + rip->loops = BLI_gset_ptr_new(__func__); + + /* We can rely on this stack being small, as we're walking down two sides of an edge loop, + * so the stack wont be much larger than the total number of fans at any one vertex. */ + BLI_SMALLSTACK_DECLARE(stack, BMLoop *); + + /* Needed for cases when we walk onto loops which already have a side assigned, + * in this case we need to pick a better side (see #uv_rip_pairs_loop_change_sides_test) + * and put the loop back in the stack, + * which is needed in the case adjacent loops should also switch sides. */ +#define UV_SET_SIDE_AND_REMOVE_FROM_RAIL(loop, side_value) \ + { \ + BLI_assert(UL(loop)->side_was_swapped == false); \ + BLI_assert(UL(loop)->side != side_value); \ + if (!UL(loop)->in_stack) { \ + BLI_SMALLSTACK_PUSH(stack, loop); \ + UL(loop)->in_stack = true; \ + } \ + if (UL(loop)->in_rip_pairs) { \ + uv_rip_pairs_remove(rip, loop); \ + } \ + UL(loop)->side = side_value; \ + UL(loop)->side_was_swapped = true; \ + } + + /* Initialize the stack. */ + BLI_SMALLSTACK_PUSH(stack, l_init); + UL(l_init)->in_stack = true; + + BMLoop *l_step; + while ((l_step = BLI_SMALLSTACK_POP(stack))) { + int side = UL(l_step)->side; + UL(l_step)->in_stack = false; + + /* Note that we could add all loops into the rip-pairs when adding into the stack, + * however this complicates removal, so add into the rip-pairs when popping from the stack. */ + uv_rip_pairs_add(rip, l_step); + + /* Add to the other side if it exists. */ + if (UL(l_step)->is_select_edge) { + BMLoop *l_other = bm_loop_find_other_radial_loop_with_visible_face(l_step, + cd_loop_uv_offset); + if (l_other != NULL) { + if (!UL(l_other)->in_rip_pairs && !UL(l_other)->in_stack) { + BLI_SMALLSTACK_PUSH(stack, l_other); + UL(l_other)->in_stack = true; + UL(l_other)->side = !side; + } + else { + if (UL(l_other)->side == side) { + if (UL(l_other)->side_was_swapped == false) { + UV_SET_SIDE_AND_REMOVE_FROM_RAIL(l_other, !side); + } + } + } + } + + /* Add the next loop along the edge on the same side. */ + l_other = l_step->next; + if (!UL(l_other)->in_rip_pairs && !UL(l_other)->in_stack) { + BLI_SMALLSTACK_PUSH(stack, l_other); + UL(l_other)->in_stack = true; + UL(l_other)->side = side; + } + else { + if (UL(l_other)->side != side) { + if ((UL(l_other)->side_was_swapped == false) && + uv_rip_pairs_loop_change_sides_test(l_other, l_step, aspect_y, cd_loop_uv_offset)) { + UV_SET_SIDE_AND_REMOVE_FROM_RAIL(l_other, side); + } + } + } + } + + /* Walk over the fan of loops, starting from `l_step` in both directions. */ + for (int i = 0; i < 2; i++) { + BMLoop *l_radial_first = i ? l_step : l_step->prev; + if (l_radial_first != l_radial_first->radial_next) { + BMEdge *e_radial = l_radial_first->e; + BMLoop *l_radial_iter = l_radial_first->radial_next; + do { + /* Not a boundary and visible. */ + if (!UL(l_radial_iter)->is_select_edge && + BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) { + BMLoop *l_other = (l_radial_iter->v == l_step->v) ? l_radial_iter : + l_radial_iter->next; + BLI_assert(l_other->v == l_step->v); + if (BM_edge_uv_share_vert_check(e_radial, l_other, l_step, cd_loop_uv_offset)) { + if (!UL(l_other)->in_rip_pairs && !UL(l_other)->in_stack) { + BLI_SMALLSTACK_PUSH(stack, l_other); + UL(l_other)->in_stack = true; + UL(l_other)->side = side; + } + else { + if (UL(l_other)->side != side) { + if ((UL(l_other)->side_was_swapped == false) && + uv_rip_pairs_loop_change_sides_test( + l_other, l_step, aspect_y, cd_loop_uv_offset)) { + UV_SET_SIDE_AND_REMOVE_FROM_RAIL(l_other, side); + } + } + } + } + } + } while ((l_radial_iter = l_radial_iter->radial_next) != l_radial_first); + } + } + } + +#undef UV_SET_SIDE_AND_REMOVE_FROM_RAIL + + return rip; +} + +static void uv_rip_pairs_free(UVRipPairs *rip) +{ + BLI_gset_free(rip->loops, NULL); + MEM_freeN(rip); +} + +/** + * This is an approximation, it's easily good enough for our purpose. + */ +static bool uv_rip_pairs_calc_center_and_direction(UVRipPairs *rip, + const int cd_loop_uv_offset, + float r_center[2], + float r_dir_side[2][2]) +{ + zero_v2(r_center); + int center_total = 0; + int side_total[2] = {0, 0}; + + for (int i = 0; i < 2; i++) { + zero_v2(r_dir_side[i]); + } + GSetIterator gs_iter; + GSET_ITER (gs_iter, rip->loops) { + BMLoop *l = BLI_gsetIterator_getKey(&gs_iter); + int side = UL(l)->side; + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + add_v2_v2(r_center, luv->uv); + + float dir[2]; + if (!UL(l)->is_select_edge) { + const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); + sub_v2_v2v2(dir, luv_next->uv, luv->uv); + add_v2_v2(r_dir_side[side], dir); + } + if (!UL(l->prev)->is_select_edge) { + const MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset); + sub_v2_v2v2(dir, luv_prev->uv, luv->uv); + add_v2_v2(r_dir_side[side], dir); + } + side_total[side] += 1; + } + center_total += BLI_gset_len(rip->loops); + + for (int i = 0; i < 2; i++) { + normalize_v2(r_dir_side[i]); + } + mul_v2_fl(r_center, 1.0f / center_total); + + /* If only a single side is selected, don't handle this rip-pairs. */ + return side_total[0] && side_total[1]; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name UV Rip Main Function + * \{ */ + +/** + * \return true when a change was made. + */ +static bool uv_rip_object(Scene *scene, Object *obedit, const float co[2], const float aspect_y) +{ + Mesh *me = (Mesh *)obedit->data; + BMEditMesh *em = me->edit_mesh; + BMesh *bm = em->bm; + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + BMFace *efa; + BMIter iter, liter; + BMLoop *l; + + const ULData ul_clear = {0}; + + bool changed = false; + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BM_elem_flag_set(efa, BM_ELEM_TAG, uvedit_face_visible_test(scene, efa)); + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + ULData *ul = UL(l); + *ul = ul_clear; + } + } + bm->elem_index_dirty |= BM_LOOP; + + bool is_select_all_any = false; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { + bool is_all = true; + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (luv->flag & MLOOPUV_VERTSEL) { + const MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset); + const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); + if (luv_next->flag & MLOOPUV_VERTSEL) { + UL(l)->is_select_edge = true; + } + else { + if ((luv_prev->flag & MLOOPUV_VERTSEL) == 0) { + /* #bm_loop_uv_select_single_vert_validate validates below. */ + UL(l)->is_select_vert_single = true; + } + } + } + else { + is_all = false; + } + } + if (is_all) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + UL(l)->is_select_all = true; + } + is_select_all_any = true; + } + } + } + + /* Remove #ULData.is_select_vert_single when connected to selected edges. */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (UL(l)->is_select_vert_single) { + bm_loop_uv_select_single_vert_validate(l, cd_loop_uv_offset); + } + } + } + } + + /* Special case: if we have selected faces, isolated them. + * This isn't a rip, however it's useful for users as a quick way + * to detach the selection. + * + * We could also extract an edge loop from the boundary + * however in practice it's not that useful, see T78751. */ + if (is_select_all_any) { + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (!UL(l)->is_select_all) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (luv->flag & MLOOPUV_VERTSEL) { + luv->flag &= ~MLOOPUV_VERTSEL; + changed = true; + } + } + } + } + return changed; + } + + /* Extract loop pairs or single loops. */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (UL(l)->is_select_edge) { + if (!UL(l)->in_rip_pairs) { + UVRipPairs *rip = uv_rip_pairs_from_loop(l, aspect_y, cd_loop_uv_offset); + float center[2]; + float dir_cursor[2]; + float dir_side[2][2]; + int side_from_cursor = -1; + if (uv_rip_pairs_calc_center_and_direction(rip, cd_loop_uv_offset, center, dir_side)) { + for (int i = 0; i < 2; i++) { + sub_v2_v2v2(dir_cursor, center, co); + normalize_v2(dir_cursor); + } + side_from_cursor = (dot_v2v2(dir_side[0], dir_cursor) - + dot_v2v2(dir_side[1], dir_cursor)) < 0.0f; + } + GSetIterator gs_iter; + GSET_ITER (gs_iter, rip->loops) { + BMLoop *l_iter = BLI_gsetIterator_getKey(&gs_iter); + ULData *ul = UL(l_iter); + if (ul->side == side_from_cursor) { + uvedit_uv_select_disable(em, scene, l_iter, cd_loop_uv_offset); + changed = true; + } + /* Ensure we don't operate on these again. */ + *ul = ul_clear; + } + uv_rip_pairs_free(rip); + } + } + else if (UL(l)->is_select_vert_single) { + UVRipSingle *rip = uv_rip_single_from_loop(l, co, aspect_y, cd_loop_uv_offset); + /* We only ever use one side. */ + const int side_from_cursor = 0; + GSetIterator gs_iter; + GSET_ITER (gs_iter, rip->loops) { + BMLoop *l_iter = BLI_gsetIterator_getKey(&gs_iter); + ULData *ul = UL(l_iter); + if (ul->side == side_from_cursor) { + uvedit_uv_select_disable(em, scene, l_iter, cd_loop_uv_offset); + changed = true; + } + /* Ensure we don't operate on these again. */ + *ul = ul_clear; + } + uv_rip_single_free(rip); + } + } + } + } + return changed; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name UV Rip Operator + * \{ */ + +static int uv_rip_exec(bContext *C, wmOperator *op) +{ + SpaceImage *sima = CTX_wm_space_image(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + bool changed_multi = false; + + float co[2]; + RNA_float_get_array(op->ptr, "location", co); + + float aspx, aspy; + { + /* Note that we only want to run this on the */ + Object *obedit = CTX_data_edit_object(C); + ED_uvedit_get_aspect(obedit, &aspx, &aspy); + } + const float aspect_y = aspx / aspy; + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, ((View3D *)NULL), &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + + if (uv_rip_object(scene, obedit, co, aspect_y)) { + changed_multi = true; + uvedit_live_unwrap_update(sima, scene, obedit); + DEG_id_tag_update(obedit->data, 0); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + } + } + MEM_freeN(objects); + + if (!changed_multi) { + BKE_report(op->reports, RPT_ERROR, "Rip failed"); + return OPERATOR_CANCELLED; + } + return OPERATOR_FINISHED; +} + +static int uv_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + float co[2]; + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); + RNA_float_set_array(op->ptr, "location", co); + + return uv_rip_exec(C, op); +} + +void UV_OT_rip(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "UV Rip"; + ot->description = "Rip selected vertices or a selected region"; + ot->idname = "UV_OT_rip"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = uv_rip_exec; + ot->invoke = uv_rip_invoke; + ot->poll = ED_operator_uvedit; + + /* translation data */ + Transform_Properties(ot, P_MIRROR_DUMMY); + + /* properties */ + RNA_def_float_vector( + ot->srna, + "location", + 2, + NULL, + -FLT_MAX, + FLT_MAX, + "Location", + "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", + -100.0f, + 100.0f); +} + +/** \} */ diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index b701e94cd77..ddb276a663a 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -88,6 +88,59 @@ static void uv_select_tag_update_for_object(Depsgraph *depsgraph, Object *obedit); /* -------------------------------------------------------------------- */ +/** \name Active Selection Tracking + * + * Currently we don't store loops in the selection history, + * store face/edge/vert combinations (needed for UV path selection). + * \{ */ + +void ED_uvedit_active_vert_loop_set(BMesh *bm, BMLoop *l) +{ + BM_select_history_clear(bm); + BM_select_history_remove(bm, (BMElem *)l->f); + BM_select_history_remove(bm, (BMElem *)l->v); + BM_select_history_store_notest(bm, (BMElem *)l->f); + BM_select_history_store_notest(bm, (BMElem *)l->v); +} + +BMLoop *ED_uvedit_active_vert_loop_get(BMesh *bm) +{ + BMEditSelection *ese = bm->selected.last; + if (ese && ese->prev) { + BMEditSelection *ese_prev = ese->prev; + if ((ese->htype == BM_VERT) && (ese_prev->htype == BM_FACE)) { + /* May be NULL. */ + return BM_face_vert_share_loop((BMFace *)ese_prev->ele, (BMVert *)ese->ele); + } + } + return NULL; +} + +void ED_uvedit_active_edge_loop_set(BMesh *bm, BMLoop *l) +{ + BM_select_history_clear(bm); + BM_select_history_remove(bm, (BMElem *)l->f); + BM_select_history_remove(bm, (BMElem *)l->e); + BM_select_history_store_notest(bm, (BMElem *)l->f); + BM_select_history_store_notest(bm, (BMElem *)l->e); +} + +BMLoop *ED_uvedit_active_edge_loop_get(BMesh *bm) +{ + BMEditSelection *ese = bm->selected.last; + if (ese && ese->prev) { + BMEditSelection *ese_prev = ese->prev; + if ((ese->htype == BM_EDGE) && (ese_prev->htype == BM_FACE)) { + /* May be NULL. */ + return BM_face_edge_share_loop((BMFace *)ese_prev->ele, (BMEdge *)ese->ele); + } + } + return NULL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Visibility and Selection Utilities * \{ */ @@ -1424,7 +1477,7 @@ void UV_OT_select_all(wmOperatorType *ot) * \{ */ static bool uv_sticky_select( - float *limit, int hitv[], int v, float *hituv[], float *uv, int sticky, int hitlen) + const float *limit, const int hitv[], int v, float *hituv[], float *uv, int sticky, int hitlen) { int i; @@ -1531,6 +1584,11 @@ static int uv_mouse_select_multi(bContext *C, hituv[hit.lindex] = hit.luv->uv; hitlen = hit.efa->len; + + if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) { + BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm; + ED_uvedit_active_vert_loop_set(bm, hit.l); + } } } else if (selectmode == UV_SELECT_EDGE) { @@ -1550,6 +1608,11 @@ static int uv_mouse_select_multi(bContext *C, hituv[(hit.lindex + 1) % hit.efa->len] = hit.luv_next->uv; hitlen = hit.efa->len; + + if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) { + BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm; + ED_uvedit_active_edge_loop_set(bm, hit.l); + } } } else if (selectmode == UV_SELECT_FACE) { @@ -2061,6 +2124,7 @@ void UV_OT_select_linked_pick(wmOperatorType *ot) */ static int uv_select_split_exec(bContext *C, wmOperator *op) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); const ToolSettings *ts = scene->toolsettings; @@ -2127,6 +2191,7 @@ static int uv_select_split_exec(bContext *C, wmOperator *op) if (changed) { changed_multi = true; WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL); + uv_select_tag_update_for_object(depsgraph, ts, obedit); } } MEM_freeN(objects); @@ -3354,3 +3419,154 @@ void UV_OT_select_overlap(wmOperatorType *ot) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Selected Elements as Arrays (Vertex, Edge & Faces) + * + * These functions return single elements per connected vertex/edge. + * So an edge that has two connected edge loops only assigns one loop in the array. + * \{ */ + +BMFace **ED_uvedit_selected_faces(Scene *scene, BMesh *bm, int len_max, int *r_faces_len) +{ + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + CLAMP_MAX(len_max, bm->totface); + int faces_len = 0; + BMFace **faces = MEM_mallocN(sizeof(*faces) * len_max, __func__); + + BMIter iter; + BMFace *f; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (uvedit_face_visible_test(scene, f)) { + if (uvedit_face_select_test(scene, f, cd_loop_uv_offset)) { + faces[faces_len++] = f; + if (faces_len == len_max) { + goto finally; + } + } + } + } + +finally: + *r_faces_len = faces_len; + if (faces_len != len_max) { + faces = MEM_reallocN(faces, sizeof(*faces) * faces_len); + } + return faces; +} + +BMLoop **ED_uvedit_selected_edges(Scene *scene, BMesh *bm, int len_max, int *r_edges_len) +{ + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + CLAMP_MAX(len_max, bm->totloop); + int edges_len = 0; + BMLoop **edges = MEM_mallocN(sizeof(*edges) * len_max, __func__); + + BMIter iter; + BMFace *f; + + /* Clear tag. */ + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMIter liter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) { + BM_elem_flag_disable(l_iter, BM_ELEM_TAG); + } + } + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (uvedit_face_visible_test(scene, f)) { + BMIter liter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) { + if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) { + const MLoopUV *luv_curr = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); + const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l_iter->next, cd_loop_uv_offset); + if ((luv_curr->flag & MLOOPUV_VERTSEL) && (luv_next->flag & MLOOPUV_VERTSEL)) { + BM_elem_flag_enable(l_iter, BM_ELEM_TAG); + + edges[edges_len++] = l_iter; + if (edges_len == len_max) { + goto finally; + } + + /* Tag other connected loops so we don't consider them separate edges. */ + if (l_iter != l_iter->radial_next) { + BMLoop *l_radial_iter = l_iter->radial_next; + do { + if (BM_loop_uv_share_edge_check(l_iter, l_radial_iter, cd_loop_uv_offset)) { + BM_elem_flag_enable(l_radial_iter, BM_ELEM_TAG); + } + } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter); + } + } + } + } + } + } + +finally: + *r_edges_len = edges_len; + if (edges_len != len_max) { + edges = MEM_reallocN(edges, sizeof(*edges) * edges_len); + } + return edges; +} + +BMLoop **ED_uvedit_selected_verts(Scene *scene, BMesh *bm, int len_max, int *r_verts_len) +{ + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + CLAMP_MAX(len_max, bm->totloop); + int verts_len = 0; + BMLoop **verts = MEM_mallocN(sizeof(*verts) * len_max, __func__); + + BMIter iter; + BMFace *f; + + /* Clear tag. */ + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMIter liter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) { + BM_elem_flag_disable(l_iter, BM_ELEM_TAG); + } + } + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (uvedit_face_visible_test(scene, f)) { + BMIter liter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) { + if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) { + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); + if ((luv->flag & MLOOPUV_VERTSEL)) { + BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG); + + verts[verts_len++] = l_iter; + if (verts_len == len_max) { + goto finally; + } + + /* Tag other connected loops so we don't consider them separate vertices. */ + BMIter liter_disk; + BMLoop *l_disk_iter; + BM_ITER_ELEM (l_disk_iter, &liter_disk, l_iter->v, BM_LOOPS_OF_VERT) { + if (BM_loop_uv_share_vert_check(l_iter, l_disk_iter, cd_loop_uv_offset)) { + BM_elem_flag_enable(l_disk_iter, BM_ELEM_TAG); + } + } + } + } + } + } + } + +finally: + *r_verts_len = verts_len; + if (verts_len != len_max) { + verts = MEM_reallocN(verts, sizeof(*verts) * verts_len); + } + return verts; +} + +/** \} */ diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 38bd928e7b1..29e10f03e3c 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -534,7 +534,7 @@ static void stitch_island_calculate_edge_rotation(UvEdge *edge, StitchStateContainer *ssc, StitchState *state, UVVertAverage *uv_average, - uint *uvfinal_map, + const uint *uvfinal_map, IslandStitchData *island_stitch_data) { BMesh *bm = state->em->bm; @@ -1935,7 +1935,7 @@ static StitchState *stitch_init(bContext *C, return NULL; } - ED_uvedit_get_aspect(scene, obedit, em->bm, &aspx, &aspy); + ED_uvedit_get_aspect(obedit, &aspx, &aspy); state->aspect = aspx / aspy; /* Count 'unique' uvs */ diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index aff73308fb5..6fcfb0e0bfc 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -210,15 +210,16 @@ static bool uvedit_have_selection_multi(const Scene *scene, return have_select; } -void ED_uvedit_get_aspect( - const Scene *UNUSED(scene), Object *ob, BMesh *bm, float *r_aspx, float *r_aspy) +void ED_uvedit_get_aspect(Object *ob, float *r_aspx, float *r_aspy) { + BMEditMesh *em = BKE_editmesh_from_object(ob); + BLI_assert(em != NULL); bool sloppy = true; bool selected = false; BMFace *efa; Image *ima; - efa = BM_mesh_active_face_get(bm, sloppy, selected); + efa = BM_mesh_active_face_get(em->bm, sloppy, selected); if (efa) { ED_object_get_active_image(ob, efa->mat_nr + 1, &ima, NULL, NULL, NULL); @@ -285,7 +286,7 @@ static ParamHandle *construct_param_handle(const Scene *scene, if (options->correct_aspect) { float aspx, aspy; - ED_uvedit_get_aspect(scene, ob, bm, &aspx, &aspy); + ED_uvedit_get_aspect(ob, &aspx, &aspy); if (aspx != aspy) { param_aspect_ratio(handle, aspx, aspy); @@ -354,11 +355,9 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene, if (options->correct_aspect) { Object *ob = objects[0]; - BMEditMesh *em = BKE_editmesh_from_object(ob); - BMesh *bm = em->bm; float aspx, aspy; - ED_uvedit_get_aspect(scene, ob, bm, &aspx, &aspy); + ED_uvedit_get_aspect(ob, &aspx, &aspy); if (aspx != aspy) { param_aspect_ratio(handle, aspx, aspy); } @@ -500,7 +499,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene, if (options->correct_aspect) { float aspx, aspy; - ED_uvedit_get_aspect(scene, ob, em->bm, &aspx, &aspy); + ED_uvedit_get_aspect(ob, &aspx, &aspy); if (aspx != aspy) { param_aspect_ratio(handle, aspx, aspy); @@ -1413,7 +1412,7 @@ static void uv_transform_properties(wmOperatorType *ot, int radius) } } -static void correct_uv_aspect(const Scene *scene, Object *ob, BMEditMesh *em) +static void correct_uv_aspect(Object *ob, BMEditMesh *em) { BMLoop *l; BMIter iter, liter; @@ -1423,7 +1422,7 @@ static void correct_uv_aspect(const Scene *scene, Object *ob, BMEditMesh *em) const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - ED_uvedit_get_aspect(scene, ob, em->bm, &aspx, &aspy); + ED_uvedit_get_aspect(ob, &aspx, &aspy); if (aspx == aspy) { return; @@ -1491,10 +1490,7 @@ static void uv_map_clip_correct_properties(wmOperatorType *ot) "Scale UV coordinates to bounds after unwrapping"); } -static void uv_map_clip_correct_multi(const Scene *scene, - Object **objects, - uint objects_len, - wmOperator *op) +static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOperator *op) { BMFace *efa; BMLoop *l; @@ -1515,7 +1511,7 @@ static void uv_map_clip_correct_multi(const Scene *scene, /* correct for image aspect ratio */ if (correct_aspect) { - correct_uv_aspect(scene, ob, em); + correct_uv_aspect(ob, em); } if (scale_to_bounds) { @@ -1580,9 +1576,9 @@ static void uv_map_clip_correct_multi(const Scene *scene, } } -static void uv_map_clip_correct(const Scene *scene, Object *ob, wmOperator *op) +static void uv_map_clip_correct(Object *ob, wmOperator *op) { - uv_map_clip_correct_multi(scene, &ob, 1, op); + uv_map_clip_correct_multi(&ob, 1, op); } /** \} */ @@ -1973,7 +1969,7 @@ static int uv_from_view_exec(bContext *C, wmOperator *op) } if (changed_multi) { - uv_map_clip_correct_multi(scene, objects, objects_len, op); + uv_map_clip_correct_multi(objects, objects_len, op); } MEM_freeN(objects); @@ -2174,7 +2170,7 @@ static int sphere_project_exec(bContext *C, wmOperator *op) uv_map_mirror(em, efa); } - uv_map_clip_correct(scene, obedit, op); + uv_map_clip_correct(obedit, op); DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); @@ -2272,7 +2268,7 @@ static int cylinder_project_exec(bContext *C, wmOperator *op) uv_map_mirror(em, efa); } - uv_map_clip_correct(scene, obedit, op); + uv_map_clip_correct(obedit, op); DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); @@ -2395,7 +2391,7 @@ static int cube_project_exec(bContext *C, wmOperator *op) uvedit_unwrap_cube_project(em->bm, cube_size, true, center); - uv_map_clip_correct(scene, obedit, op); + uv_map_clip_correct(obedit, op); DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); diff --git a/source/blender/freestyle/intern/application/AppConfig.cpp b/source/blender/freestyle/intern/application/AppConfig.cpp index b97843e045d..b26c9a58f70 100644 --- a/source/blender/freestyle/intern/application/AppConfig.cpp +++ b/source/blender/freestyle/intern/application/AppConfig.cpp @@ -48,15 +48,13 @@ void Path::setRootDir(const string &iRootDir) { _ProjectDir = iRootDir + string(DIR_SEP) + "freestyle"; _ModelsPath = ""; - _PatternsPath = _ProjectDir + string(DIR_SEP) + "data" + string(DIR_SEP) + - "textures" + string(DIR_SEP) + "variation_patterns" + - string(DIR_SEP); - _BrushesPath = _ProjectDir + string(DIR_SEP) + "data" + string(DIR_SEP) + - "textures" + string(DIR_SEP) + "brushes" + string(DIR_SEP); - _EnvMapDir = _ProjectDir + string(DIR_SEP) + "data" + string(DIR_SEP) + - "env_map" + string(DIR_SEP); - _MapsDir = _ProjectDir + string(DIR_SEP) + "data" + string(DIR_SEP) + "maps" + - string(DIR_SEP); + _PatternsPath = _ProjectDir + string(DIR_SEP) + "data" + string(DIR_SEP) + "textures" + + string(DIR_SEP) + "variation_patterns" + string(DIR_SEP); + _BrushesPath = _ProjectDir + string(DIR_SEP) + "data" + string(DIR_SEP) + "textures" + + string(DIR_SEP) + "brushes" + string(DIR_SEP); + _EnvMapDir = _ProjectDir + string(DIR_SEP) + "data" + string(DIR_SEP) + "env_map" + + string(DIR_SEP); + _MapsDir = _ProjectDir + string(DIR_SEP) + "data" + string(DIR_SEP) + "maps" + string(DIR_SEP); } void Path::setHomeDir(const string &iHomeDir) diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp index 0af57e5d728..e1763514e08 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp @@ -222,7 +222,7 @@ void BlenderFileLoader::clipTriangle(int numTris, bool em1, bool em2, bool em3, - int clip[3]) + const int clip[3]) { float *v[3], *n[3]; bool em[3]; diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.h b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.h index ad6379d5f52..d9387b221f8 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.h +++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.h @@ -128,7 +128,7 @@ class BlenderFileLoader { bool em1, bool em2, bool em3, - int clip[3]); + const int clip[3]); void addTriangle(struct LoaderState *ls, float v1[3], float v2[3], diff --git a/source/blender/freestyle/intern/scene_graph/SceneHash.cpp b/source/blender/freestyle/intern/scene_graph/SceneHash.cpp index b5d954664e4..e5f029d28b7 100644 --- a/source/blender/freestyle/intern/scene_graph/SceneHash.cpp +++ b/source/blender/freestyle/intern/scene_graph/SceneHash.cpp @@ -65,7 +65,7 @@ void SceneHash::visitIndexedFaceSet(IndexedFaceSet &ifs) static const int MOD_ADLER = 65521; -void SceneHash::adler32(unsigned char *data, int size) +void SceneHash::adler32(const unsigned char *data, int size) { uint32_t sum1 = _sum & 0xffff; uint32_t sum2 = (_sum >> 16) & 0xffff; diff --git a/source/blender/freestyle/intern/scene_graph/SceneHash.h b/source/blender/freestyle/intern/scene_graph/SceneHash.h index 53d1381da60..05c0880f806 100644 --- a/source/blender/freestyle/intern/scene_graph/SceneHash.h +++ b/source/blender/freestyle/intern/scene_graph/SceneHash.h @@ -67,7 +67,7 @@ class SceneHash : public SceneVisitor { } private: - void adler32(unsigned char *data, int size); + void adler32(const unsigned char *data, int size); uint32_t _sum; uint32_t _prevSum; diff --git a/source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp b/source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp index 2df5ecd0867..cb3a297076a 100644 --- a/source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp +++ b/source/blender/freestyle/intern/view_map/CulledOccluderSource.cpp @@ -94,7 +94,7 @@ static inline bool crossesProscenium(real proscenium[4], FEdge *fe) return GeomUtils::intersect2dSeg2dArea(min, max, A, B); } -static inline bool insideProscenium(real proscenium[4], const Vec3r &point) +static inline bool insideProscenium(const real proscenium[4], const Vec3r &point) { return !(point[0] < proscenium[0] || point[0] > proscenium[1] || point[1] < proscenium[2] || point[1] > proscenium[3]); diff --git a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp index b7de3a5b319..8ac272e92b5 100644 --- a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp +++ b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp @@ -1084,7 +1084,7 @@ static inline bool crossesProscenium(real proscenium[4], FEdge *fe) return GeomUtils::intersect2dSeg2dArea(min, max, A, B); } -static inline bool insideProscenium(real proscenium[4], const Vec3r &point) +static inline bool insideProscenium(const real proscenium[4], const Vec3r &point) { return !(point[0] < proscenium[0] || point[0] > proscenium[1] || point[1] < proscenium[2] || point[1] > proscenium[3]); diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index 703d3c393e8..fefd86f6c86 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -30,8 +30,10 @@ set(SRC intern/attributes_ref.cc intern/cpp_types.cc intern/multi_function.cc + intern/multi_function_builder.cc intern/multi_function_network.cc intern/multi_function_network_evaluation.cc + intern/multi_function_network_optimization.cc FN_array_spans.hh FN_attributes_ref.hh @@ -43,6 +45,7 @@ set(SRC FN_multi_function_data_type.hh FN_multi_function_network.hh FN_multi_function_network_evaluation.hh + FN_multi_function_network_optimization.hh FN_multi_function_param_type.hh FN_multi_function_params.hh FN_multi_function_signature.hh diff --git a/source/blender/functions/FN_attributes_ref.hh b/source/blender/functions/FN_attributes_ref.hh index 3c31665e0b5..cf5ef2af8a6 100644 --- a/source/blender/functions/FN_attributes_ref.hh +++ b/source/blender/functions/FN_attributes_ref.hh @@ -161,6 +161,8 @@ class MutableAttributesRef { Span<void *> buffers_; IndexRange range_; + friend class AttributesRef; + public: MutableAttributesRef(const AttributesInfo &info, Span<void *> buffers, uint size) : MutableAttributesRef(info, buffers, IndexRange(size)) @@ -241,6 +243,97 @@ class MutableAttributesRef { } }; +class AttributesRef { + private: + const AttributesInfo *info_; + Span<const void *> buffers_; + IndexRange range_; + + public: + AttributesRef(const AttributesInfo &info, Span<const void *> buffers, uint size) + : AttributesRef(info, buffers, IndexRange(size)) + { + } + + AttributesRef(const AttributesInfo &info, Span<const void *> buffers, IndexRange range) + : info_(&info), buffers_(buffers), range_(range) + { + } + + AttributesRef(MutableAttributesRef attributes) + : info_(attributes.info_), buffers_(attributes.buffers_), range_(attributes.range_) + { + } + + uint size() const + { + return range_.size(); + } + + const AttributesInfo &info() const + { + return *info_; + } + + GSpan get(uint index) const + { + const CPPType &type = info_->type_of(index); + const void *ptr = POINTER_OFFSET(buffers_[index], type.size() * range_.start()); + return GSpan(type, ptr, range_.size()); + } + + GSpan get(StringRef name) const + { + return this->get(info_->index_of(name)); + } + + template<typename T> Span<T> get(uint index) const + { + BLI_assert(info_->type_of(index).is<T>()); + return Span<T>((T *)buffers_[index] + range_.start(), range_.size()); + } + + template<typename T> Span<T> get(StringRef name) const + { + return this->get<T>(info_->index_of(name)); + } + + std::optional<GSpan> try_get(StringRef name, const CPPType &type) const + { + int index = info_->try_index_of(name, type); + if (index == -1) { + return {}; + } + else { + return this->get((uint)index); + } + } + + template<typename T> std::optional<Span<T>> try_get(StringRef name) const + { + int index = info_->try_index_of(name); + if (index == -1) { + return {}; + } + else if (info_->type_of((uint)index).is<T>()) { + return this->get<T>((uint)index); + } + else { + return {}; + } + } + + AttributesRef slice(IndexRange range) const + { + return this->slice(range.start(), range.size()); + } + + AttributesRef slice(uint start, uint size) const + { + return AttributesRef(*info_, buffers_, range_.slice(start, size)); + } +}; + } // namespace blender::fn #endif /* __FN_ATTRIBUTES_REF_HH__ */ diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh index 1681ff9fe8c..7ec60809194 100644 --- a/source/blender/functions/FN_cpp_type.hh +++ b/source/blender/functions/FN_cpp_type.hh @@ -18,12 +18,12 @@ #define __FN_CPP_TYPE_HH__ /** \file - * \ingroup functions + * \ingroup fn * - * The CPPType class is the core of the runtime-type-system used by the functions system. An - * instance of this class can represent any C++ type, that is default-constructible, destructible, - * movable and copyable. Therefore it also works for all C types. This restrictions might need to - * be removed in the future, but for now every required type has these properties. + * The CPPType class is the core of the runtime-type-system used by the functions system. It can + * represent C++ types that are default-constructible, destructible, movable, copyable, + * equality comparable and hashable. In the future we might want to make some of these properties + * optional. * * Every type has a size and an alignment. Every function dealing with C++ types in a generic way, * has to make sure that alignment rules are followed. The methods provided by a CPPType instance @@ -40,9 +40,9 @@ * Constructs a single instance of that type at the given pointer. * - construct_default_n(void *ptr, uint n): * Constructs n instances of that type in an array that starts at the given pointer. - * - construct_default_indices(void *ptr, IndexMask index_mask): + * - construct_default_indices(void *ptr, IndexMask mask): * Constructs multiple instances of that type in an array that starts at the given pointer. - * Only the indices referenced by `index_mask` will by constructed. + * Only the indices referenced by `mask` will by constructed. * * In some cases default-construction does nothing (e.g. for trivial types like int). The * `default_value` method provides some default value anyway that can be copied instead. What the @@ -66,44 +66,94 @@ * pointers to virtual member functions. */ +#include "BLI_hash.hh" #include "BLI_index_mask.hh" #include "BLI_math_base.h" #include "BLI_string_ref.hh" +#include "BLI_utility_mixins.hh" namespace blender::fn { -class CPPType { +class CPPType : NonCopyable, NonMovable { public: using ConstructDefaultF = void (*)(void *ptr); using ConstructDefaultNF = void (*)(void *ptr, uint n); - using ConstructDefaultIndicesF = void (*)(void *ptr, IndexMask index_mask); + using ConstructDefaultIndicesF = void (*)(void *ptr, IndexMask mask); using DestructF = void (*)(void *ptr); using DestructNF = void (*)(void *ptr, uint n); - using DestructIndicesF = void (*)(void *ptr, IndexMask index_mask); + using DestructIndicesF = void (*)(void *ptr, IndexMask mask); using CopyToInitializedF = void (*)(const void *src, void *dst); using CopyToInitializedNF = void (*)(const void *src, void *dst, uint n); - using CopyToInitializedIndicesF = void (*)(const void *src, void *dst, IndexMask index_mask); + using CopyToInitializedIndicesF = void (*)(const void *src, void *dst, IndexMask mask); using CopyToUninitializedF = void (*)(const void *src, void *dst); using CopyToUninitializedNF = void (*)(const void *src, void *dst, uint n); - using CopyToUninitializedIndicesF = void (*)(const void *src, void *dst, IndexMask index_mask); + using CopyToUninitializedIndicesF = void (*)(const void *src, void *dst, IndexMask mask); using RelocateToInitializedF = void (*)(void *src, void *dst); using RelocateToInitializedNF = void (*)(void *src, void *dst, uint n); - using RelocateToInitializedIndicesF = void (*)(void *src, void *dst, IndexMask index_mask); + using RelocateToInitializedIndicesF = void (*)(void *src, void *dst, IndexMask mask); using RelocateToUninitializedF = void (*)(void *src, void *dst); using RelocateToUninitializedNF = void (*)(void *src, void *dst, uint n); - using RelocateToUninitializedIndicesF = void (*)(void *src, void *dst, IndexMask index_mask); + using RelocateToUninitializedIndicesF = void (*)(void *src, void *dst, IndexMask mask); using FillInitializedF = void (*)(const void *value, void *dst, uint n); - using FillInitializedIndicesF = void (*)(const void *value, void *dst, IndexMask index_mask); + using FillInitializedIndicesF = void (*)(const void *value, void *dst, IndexMask mask); using FillUninitializedF = void (*)(const void *value, void *dst, uint n); - using FillUninitializedIndicesF = void (*)(const void *value, void *dst, IndexMask index_mask); + using FillUninitializedIndicesF = void (*)(const void *value, void *dst, IndexMask mask); + using DebugPrintF = void (*)(const void *value, std::stringstream &ss); + using IsEqualF = bool (*)(const void *a, const void *b); + using HashF = uint32_t (*)(const void *value); + + private: + uint size_; + uint alignment_; + uintptr_t alignment_mask_; + bool is_trivially_destructible_; + + ConstructDefaultF construct_default_; + ConstructDefaultNF construct_default_n_; + ConstructDefaultIndicesF construct_default_indices_; + + DestructF destruct_; + DestructNF destruct_n_; + DestructIndicesF destruct_indices_; + + CopyToInitializedF copy_to_initialized_; + CopyToInitializedNF copy_to_initialized_n_; + CopyToInitializedIndicesF copy_to_initialized_indices_; + + CopyToUninitializedF copy_to_uninitialized_; + CopyToUninitializedNF copy_to_uninitialized_n_; + CopyToUninitializedIndicesF copy_to_uninitialized_indices_; + + RelocateToInitializedF relocate_to_initialized_; + RelocateToInitializedNF relocate_to_initialized_n_; + RelocateToInitializedIndicesF relocate_to_initialized_indices_; + + RelocateToUninitializedF relocate_to_uninitialized_; + RelocateToUninitializedNF relocate_to_uninitialized_n_; + RelocateToUninitializedIndicesF relocate_to_uninitialized_indices_; + + FillInitializedF fill_initialized_; + FillInitializedIndicesF fill_initialized_indices_; + + FillUninitializedF fill_uninitialized_; + FillUninitializedIndicesF fill_uninitialized_indices_; + + DebugPrintF debug_print_; + IsEqualF is_equal_; + HashF hash_; + + const void *default_value_; + std::string name_; + + public: CPPType(std::string name, uint size, uint alignment, @@ -130,6 +180,9 @@ class CPPType { FillInitializedIndicesF fill_initialized_indices, FillUninitializedF fill_uninitialized, FillUninitializedIndicesF fill_uninitialized_indices, + DebugPrintF debug_print, + IsEqualF is_equal, + HashF hash, const void *default_value) : size_(size), alignment_(alignment), @@ -156,6 +209,9 @@ class CPPType { fill_initialized_indices_(fill_initialized_indices), fill_uninitialized_(fill_uninitialized), fill_uninitialized_indices_(fill_uninitialized_indices), + debug_print_(debug_print), + is_equal_(is_equal), + hash_(hash), default_value_(default_value), name_(name) { @@ -164,6 +220,22 @@ class CPPType { } /** + * Two types only compare equal when their pointer is equal. No two instances of CPPType for the + * same C++ type should be created. + */ + friend bool operator==(const CPPType &a, const CPPType &b) + { + return &a == &b; + } + + friend bool operator!=(const CPPType &a, const CPPType &b) + { + return !(&a == &b); + } + + template<typename T> static const CPPType &get(); + + /** * Returns the name of the type for debugging purposes. This name should not be used as * identifier. */ @@ -199,7 +271,7 @@ class CPPType { * for optimization purposes. * * C++ equivalent: - * std::is_trivially_destructible<T>::value; + * std::is_trivially_destructible_v<T>; */ bool is_trivially_destructible() const { @@ -236,16 +308,16 @@ class CPPType { void construct_default_n(void *ptr, uint n) const { - BLI_assert(this->pointer_has_valid_alignment(ptr)); + BLI_assert(n == 0 || this->pointer_can_point_to_instance(ptr)); construct_default_n_(ptr, n); } - void construct_default_indices(void *ptr, IndexMask index_mask) const + void construct_default_indices(void *ptr, IndexMask mask) const { - BLI_assert(this->pointer_has_valid_alignment(ptr)); + BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(ptr)); - construct_default_indices_(ptr, index_mask); + construct_default_indices_(ptr, mask); } /** @@ -265,16 +337,21 @@ class CPPType { void destruct_n(void *ptr, uint n) const { - BLI_assert(this->pointer_has_valid_alignment(ptr)); + BLI_assert(n == 0 || this->pointer_can_point_to_instance(ptr)); destruct_n_(ptr, n); } - void destruct_indices(void *ptr, IndexMask index_mask) const + void destruct_indices(void *ptr, IndexMask mask) const { - BLI_assert(this->pointer_has_valid_alignment(ptr)); + BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(ptr)); - destruct_indices_(ptr, index_mask); + destruct_indices_(ptr, mask); + } + + DestructF destruct_cb() const + { + return destruct_; } /** @@ -295,19 +372,19 @@ class CPPType { void copy_to_initialized_n(const void *src, void *dst, uint n) const { BLI_assert(src != dst); - BLI_assert(this->pointer_has_valid_alignment(src)); - BLI_assert(this->pointer_has_valid_alignment(dst)); + BLI_assert(n == 0 || this->pointer_can_point_to_instance(src)); + BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); copy_to_initialized_n_(src, dst, n); } - void copy_to_initialized_indices(const void *src, void *dst, IndexMask index_mask) const + void copy_to_initialized_indices(const void *src, void *dst, IndexMask mask) const { BLI_assert(src != dst); - BLI_assert(this->pointer_has_valid_alignment(src)); - BLI_assert(this->pointer_has_valid_alignment(dst)); + BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); + BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - copy_to_initialized_indices_(src, dst, index_mask); + copy_to_initialized_indices_(src, dst, mask); } /** @@ -330,19 +407,19 @@ class CPPType { void copy_to_uninitialized_n(const void *src, void *dst, uint n) const { BLI_assert(src != dst); - BLI_assert(this->pointer_has_valid_alignment(src)); - BLI_assert(this->pointer_has_valid_alignment(dst)); + BLI_assert(n == 0 || this->pointer_can_point_to_instance(src)); + BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); copy_to_uninitialized_n_(src, dst, n); } - void copy_to_uninitialized_indices(const void *src, void *dst, IndexMask index_mask) const + void copy_to_uninitialized_indices(const void *src, void *dst, IndexMask mask) const { BLI_assert(src != dst); - BLI_assert(this->pointer_has_valid_alignment(src)); - BLI_assert(this->pointer_has_valid_alignment(dst)); + BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); + BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - copy_to_uninitialized_indices_(src, dst, index_mask); + copy_to_uninitialized_indices_(src, dst, mask); } /** @@ -365,19 +442,19 @@ class CPPType { void relocate_to_initialized_n(void *src, void *dst, uint n) const { BLI_assert(src != dst); - BLI_assert(this->pointer_has_valid_alignment(src)); - BLI_assert(this->pointer_has_valid_alignment(dst)); + BLI_assert(n == 0 || this->pointer_can_point_to_instance(src)); + BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); relocate_to_initialized_n_(src, dst, n); } - void relocate_to_initialized_indices(void *src, void *dst, IndexMask index_mask) const + void relocate_to_initialized_indices(void *src, void *dst, IndexMask mask) const { BLI_assert(src != dst); - BLI_assert(this->pointer_has_valid_alignment(src)); - BLI_assert(this->pointer_has_valid_alignment(dst)); + BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); + BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - relocate_to_initialized_indices_(src, dst, index_mask); + relocate_to_initialized_indices_(src, dst, mask); } /** @@ -400,19 +477,19 @@ class CPPType { void relocate_to_uninitialized_n(void *src, void *dst, uint n) const { BLI_assert(src != dst); - BLI_assert(this->pointer_has_valid_alignment(src)); - BLI_assert(this->pointer_has_valid_alignment(dst)); + BLI_assert(n == 0 || this->pointer_can_point_to_instance(src)); + BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); relocate_to_uninitialized_n_(src, dst, n); } - void relocate_to_uninitialized_indices(void *src, void *dst, IndexMask index_mask) const + void relocate_to_uninitialized_indices(void *src, void *dst, IndexMask mask) const { BLI_assert(src != dst); - BLI_assert(this->pointer_has_valid_alignment(src)); - BLI_assert(this->pointer_has_valid_alignment(dst)); + BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(src)); + BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - relocate_to_uninitialized_indices_(src, dst, index_mask); + relocate_to_uninitialized_indices_(src, dst, mask); } /** @@ -422,18 +499,18 @@ class CPPType { */ void fill_initialized(const void *value, void *dst, uint n) const { - BLI_assert(this->pointer_can_point_to_instance(value)); - BLI_assert(this->pointer_can_point_to_instance(dst)); + BLI_assert(n == 0 || this->pointer_can_point_to_instance(value)); + BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); fill_initialized_(value, dst, n); } - void fill_initialized_indices(const void *value, void *dst, IndexMask index_mask) const + void fill_initialized_indices(const void *value, void *dst, IndexMask mask) const { - BLI_assert(this->pointer_has_valid_alignment(value)); - BLI_assert(this->pointer_has_valid_alignment(dst)); + BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(value)); + BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); - fill_initialized_indices_(value, dst, index_mask); + fill_initialized_indices_(value, dst, mask); } /** @@ -443,18 +520,37 @@ class CPPType { */ void fill_uninitialized(const void *value, void *dst, uint n) const { - BLI_assert(this->pointer_can_point_to_instance(value)); - BLI_assert(this->pointer_can_point_to_instance(dst)); + BLI_assert(n == 0 || this->pointer_can_point_to_instance(value)); + BLI_assert(n == 0 || this->pointer_can_point_to_instance(dst)); fill_uninitialized_(value, dst, n); } - void fill_uninitialized_indices(const void *value, void *dst, IndexMask index_mask) const + void fill_uninitialized_indices(const void *value, void *dst, IndexMask mask) const { - BLI_assert(this->pointer_has_valid_alignment(value)); - BLI_assert(this->pointer_has_valid_alignment(dst)); + BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(value)); + BLI_assert(mask.size() == 0 || this->pointer_can_point_to_instance(dst)); + + fill_uninitialized_indices_(value, dst, mask); + } - fill_uninitialized_indices_(value, dst, index_mask); + void debug_print(const void *value, std::stringstream &ss) const + { + BLI_assert(this->pointer_can_point_to_instance(value)); + debug_print_(value, ss); + } + + bool is_equal(const void *a, const void *b) const + { + BLI_assert(this->pointer_can_point_to_instance(a)); + BLI_assert(this->pointer_can_point_to_instance(b)); + return is_equal_(a, b); + } + + uint32_t hash(const void *value) const + { + BLI_assert(this->pointer_can_point_to_instance(value)); + return hash_(value); } /** @@ -466,72 +562,22 @@ class CPPType { return default_value_; } - /** - * Two types only compare equal when their pointer is equal. No two instances of CPPType for the - * same C++ type should be created. - */ - friend bool operator==(const CPPType &a, const CPPType &b) - { - return &a == &b; - } - - friend bool operator!=(const CPPType &a, const CPPType &b) + uint32_t hash() const { - return !(&a == &b); + return DefaultHash<const CPPType *>{}(this); } - template<typename T> static const CPPType &get(); - template<typename T> bool is() const { return this == &CPPType::get<T>(); } - - private: - uint size_; - uint alignment_; - uintptr_t alignment_mask_; - bool is_trivially_destructible_; - - ConstructDefaultF construct_default_; - ConstructDefaultNF construct_default_n_; - ConstructDefaultIndicesF construct_default_indices_; - - DestructF destruct_; - DestructNF destruct_n_; - DestructIndicesF destruct_indices_; - - CopyToInitializedF copy_to_initialized_; - CopyToInitializedNF copy_to_initialized_n_; - CopyToInitializedIndicesF copy_to_initialized_indices_; - - CopyToUninitializedF copy_to_uninitialized_; - CopyToUninitializedNF copy_to_uninitialized_n_; - CopyToUninitializedIndicesF copy_to_uninitialized_indices_; - - RelocateToInitializedF relocate_to_initialized_; - RelocateToInitializedNF relocate_to_initialized_n_; - RelocateToInitializedIndicesF relocate_to_initialized_indices_; - - RelocateToUninitializedF relocate_to_uninitialized_; - RelocateToUninitializedNF relocate_to_uninitialized_n_; - RelocateToUninitializedIndicesF relocate_to_uninitialized_indices_; - - FillInitializedF fill_initialized_; - FillInitializedIndicesF fill_initialized_indices_; - - FillUninitializedF fill_uninitialized_; - FillUninitializedIndicesF fill_uninitialized_indices_; - - const void *default_value_; - std::string name_; }; /* -------------------------------------------------------------------- * Utility for creating CPPType instances for C++ types. */ -namespace CPPTypeUtil { +namespace cpp_type_util { template<typename T> void construct_default_cb(void *ptr) { @@ -541,9 +587,9 @@ template<typename T> void construct_default_n_cb(void *ptr, uint n) { blender::default_construct_n((T *)ptr, n); } -template<typename T> void construct_default_indices_cb(void *ptr, IndexMask index_mask) +template<typename T> void construct_default_indices_cb(void *ptr, IndexMask mask) { - index_mask.foreach_index([&](uint i) { new ((T *)ptr + i) T; }); + mask.foreach_index([&](uint i) { new ((T *)ptr + i) T; }); } template<typename T> void destruct_cb(void *ptr) @@ -554,10 +600,10 @@ template<typename T> void destruct_n_cb(void *ptr, uint n) { blender::destruct_n((T *)ptr, n); } -template<typename T> void destruct_indices_cb(void *ptr, IndexMask index_mask) +template<typename T> void destruct_indices_cb(void *ptr, IndexMask mask) { T *ptr_ = (T *)ptr; - index_mask.foreach_index([&](uint i) { ptr_[i].~T(); }); + mask.foreach_index([&](uint i) { ptr_[i].~T(); }); } template<typename T> void copy_to_initialized_cb(const void *src, void *dst) @@ -574,12 +620,12 @@ template<typename T> void copy_to_initialized_n_cb(const void *src, void *dst, u } } template<typename T> -void copy_to_initialized_indices_cb(const void *src, void *dst, IndexMask index_mask) +void copy_to_initialized_indices_cb(const void *src, void *dst, IndexMask mask) { const T *src_ = (const T *)src; T *dst_ = (T *)dst; - index_mask.foreach_index([&](uint i) { dst_[i] = src_[i]; }); + mask.foreach_index([&](uint i) { dst_[i] = src_[i]; }); } template<typename T> void copy_to_uninitialized_cb(const void *src, void *dst) @@ -591,12 +637,12 @@ template<typename T> void copy_to_uninitialized_n_cb(const void *src, void *dst, blender::uninitialized_copy_n((T *)src, n, (T *)dst); } template<typename T> -void copy_to_uninitialized_indices_cb(const void *src, void *dst, IndexMask index_mask) +void copy_to_uninitialized_indices_cb(const void *src, void *dst, IndexMask mask) { const T *src_ = (const T *)src; T *dst_ = (T *)dst; - index_mask.foreach_index([&](uint i) { new (dst_ + i) T(src_[i]); }); + mask.foreach_index([&](uint i) { new (dst_ + i) T(src_[i]); }); } template<typename T> void relocate_to_initialized_cb(void *src, void *dst) @@ -611,13 +657,12 @@ template<typename T> void relocate_to_initialized_n_cb(void *src, void *dst, uin { blender::initialized_relocate_n((T *)src, n, (T *)dst); } -template<typename T> -void relocate_to_initialized_indices_cb(void *src, void *dst, IndexMask index_mask) +template<typename T> void relocate_to_initialized_indices_cb(void *src, void *dst, IndexMask mask) { T *src_ = (T *)src; T *dst_ = (T *)dst; - index_mask.foreach_index([&](uint i) { + mask.foreach_index([&](uint i) { dst_[i] = std::move(src_[i]); src_[i].~T(); }); @@ -636,12 +681,12 @@ template<typename T> void relocate_to_uninitialized_n_cb(void *src, void *dst, u blender::uninitialized_relocate_n((T *)src, n, (T *)dst); } template<typename T> -void relocate_to_uninitialized_indices_cb(void *src, void *dst, IndexMask index_mask) +void relocate_to_uninitialized_indices_cb(void *src, void *dst, IndexMask mask) { T *src_ = (T *)src; T *dst_ = (T *)dst; - index_mask.foreach_index([&](uint i) { + mask.foreach_index([&](uint i) { new (dst_ + i) T(std::move(src_[i])); src_[i].~T(); }); @@ -656,13 +701,12 @@ template<typename T> void fill_initialized_cb(const void *value, void *dst, uint dst_[i] = value_; } } -template<typename T> -void fill_initialized_indices_cb(const void *value, void *dst, IndexMask index_mask) +template<typename T> void fill_initialized_indices_cb(const void *value, void *dst, IndexMask mask) { const T &value_ = *(const T *)value; T *dst_ = (T *)dst; - index_mask.foreach_index([&](uint i) { dst_[i] = value_; }); + mask.foreach_index([&](uint i) { dst_[i] = value_; }); } template<typename T> void fill_uninitialized_cb(const void *value, void *dst, uint n) @@ -675,24 +719,43 @@ template<typename T> void fill_uninitialized_cb(const void *value, void *dst, ui } } template<typename T> -void fill_uninitialized_indices_cb(const void *value, void *dst, IndexMask index_mask) +void fill_uninitialized_indices_cb(const void *value, void *dst, IndexMask mask) { const T &value_ = *(const T *)value; T *dst_ = (T *)dst; - index_mask.foreach_index([&](uint i) { new (dst_ + i) T(value_); }); + mask.foreach_index([&](uint i) { new (dst_ + i) T(value_); }); +} + +template<typename T> void debug_print_cb(const void *value, std::stringstream &ss) +{ + const T &value_ = *(const T *)value; + ss << value_; +} + +template<typename T> bool is_equal_cb(const void *a, const void *b) +{ + const T &a_ = *(T *)a; + const T &b_ = *(T *)b; + return a_ == b_; +} + +template<typename T> uint32_t hash_cb(const void *value) +{ + const T &value_ = *(const T *)value; + return DefaultHash<T>{}(value_); } -} // namespace CPPTypeUtil +} // namespace cpp_type_util template<typename T> -static std::unique_ptr<const CPPType> create_cpp_type(StringRef name, const T &default_value) +inline std::unique_ptr<const CPPType> create_cpp_type(StringRef name, const T &default_value) { - using namespace CPPTypeUtil; + using namespace cpp_type_util; const CPPType *type = new CPPType(name, sizeof(T), alignof(T), - std::is_trivially_destructible<T>::value, + std::is_trivially_destructible_v<T>, construct_default_cb<T>, construct_default_n_cb<T>, construct_default_indices_cb<T>, @@ -715,6 +778,9 @@ static std::unique_ptr<const CPPType> create_cpp_type(StringRef name, const T &d fill_initialized_indices_cb<T>, fill_uninitialized_cb<T>, fill_uninitialized_indices_cb<T>, + debug_print_cb<T>, + is_equal_cb<T>, + hash_cb<T>, (const void *)&default_value); return std::unique_ptr<const CPPType>(type); } diff --git a/source/blender/functions/FN_cpp_types.hh b/source/blender/functions/FN_cpp_types.hh index 704a1363935..63f6b49885f 100644 --- a/source/blender/functions/FN_cpp_types.hh +++ b/source/blender/functions/FN_cpp_types.hh @@ -18,7 +18,7 @@ #define __FN_CPP_TYPES_HH__ /** \file - * \ingroup functions + * \ingroup fn * * This header provides convenient access to CPPType instances for some core types like integer * types. diff --git a/source/blender/functions/FN_generic_vector_array.hh b/source/blender/functions/FN_generic_vector_array.hh index f28e94b34ee..2672484c184 100644 --- a/source/blender/functions/FN_generic_vector_array.hh +++ b/source/blender/functions/FN_generic_vector_array.hh @@ -78,7 +78,7 @@ class GVectorArray : NonCopyable, NonMovable { operator GVArraySpan() const { - return GVArraySpan(type_, starts_.as_span(), lengths_); + return GVArraySpan(type_, starts_, lengths_); } bool is_empty() const @@ -98,7 +98,7 @@ class GVectorArray : NonCopyable, NonMovable { Span<const void *> starts() const { - return starts_.as_span(); + return starts_; } Span<uint> lengths() const diff --git a/source/blender/functions/FN_multi_function.hh b/source/blender/functions/FN_multi_function.hh index 452fd5472ce..35f144368ac 100644 --- a/source/blender/functions/FN_multi_function.hh +++ b/source/blender/functions/FN_multi_function.hh @@ -45,6 +45,8 @@ * 3. Override the `call` function. */ +#include "BLI_hash.hh" + #include "FN_multi_function_context.hh" #include "FN_multi_function_params.hh" @@ -61,6 +63,21 @@ class MultiFunction { virtual void call(IndexMask mask, MFParams params, MFContext context) const = 0; + virtual uint32_t hash() const + { + return DefaultHash<const MultiFunction *>{}(this); + } + + virtual bool equals(const MultiFunction &UNUSED(other)) const + { + return false; + } + + uint param_amount() const + { + return signature_.param_types.size(); + } + IndexRange param_indices() const { return signature_.param_types.index_range(); diff --git a/source/blender/functions/FN_multi_function_builder.hh b/source/blender/functions/FN_multi_function_builder.hh index abc1e5d0723..6e7efb21850 100644 --- a/source/blender/functions/FN_multi_function_builder.hh +++ b/source/blender/functions/FN_multi_function_builder.hh @@ -203,6 +203,61 @@ template<typename Mut1> class CustomMF_SM : public MultiFunction { }; /** + * Generates a multi-function that converts between two types. + */ +template<typename From, typename To> class CustomMF_Convert : public MultiFunction { + public: + CustomMF_Convert() + { + std::string name = CPPType::get<From>().name() + " to " + CPPType::get<To>().name(); + MFSignatureBuilder signature = this->get_builder(std::move(name)); + signature.single_input<From>("Input"); + signature.single_output<To>("Output"); + } + + void call(IndexMask mask, MFParams params, MFContext UNUSED(context)) const override + { + VSpan<From> inputs = params.readonly_single_input<From>(0); + MutableSpan<To> outputs = params.uninitialized_single_output<To>(1); + + for (uint i : mask) { + new ((void *)&outputs[i]) To(inputs[i]); + } + } +}; + +/** + * A multi-function that outputs the same value every time. The value is not owned by an instance + * of this function. The caller is responsible for destructing and freeing the value. + */ +class CustomMF_GenericConstant : public MultiFunction { + private: + const CPPType &type_; + const void *value_; + + template<typename T> friend class CustomMF_Constant; + + public: + CustomMF_GenericConstant(const CPPType &type, const void *value); + void call(IndexMask mask, MFParams params, MFContext context) const override; + uint32_t hash() const override; + bool equals(const MultiFunction &other) const override; +}; + +/** + * A multi-function that outputs the same array every time. The array is not owned by in instance + * of this function. The caller is responsible for destructing and freeing the values. + */ +class CustomMF_GenericConstantArray : public MultiFunction { + private: + GSpan array_; + + public: + CustomMF_GenericConstantArray(GSpan array); + void call(IndexMask mask, MFParams params, MFContext context) const override; +}; + +/** * Generates a multi-function that outputs a constant value. */ template<typename T> class CustomMF_Constant : public MultiFunction { @@ -223,6 +278,28 @@ template<typename T> class CustomMF_Constant : public MultiFunction { MutableSpan<T> output = params.uninitialized_single_output<T>(0); mask.foreach_index([&](uint i) { new (&output[i]) T(value_); }); } + + uint32_t hash() const override + { + return DefaultHash<T>{}(value_); + } + + bool equals(const MultiFunction &other) const override + { + const CustomMF_Constant *other1 = dynamic_cast<const CustomMF_Constant *>(&other); + if (other1 != nullptr) { + return value_ == other1->value_; + } + const CustomMF_GenericConstant *other2 = dynamic_cast<const CustomMF_GenericConstant *>( + &other); + if (other2 != nullptr) { + const CPPType &type = CPPType::get<T>(); + if (type == other2->type_) { + return type.is_equal((const void *)&value_, other2->value_); + } + } + return false; + } }; } // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_data_type.hh b/source/blender/functions/FN_multi_function_data_type.hh index 78f0d96fb80..57aea046006 100644 --- a/source/blender/functions/FN_multi_function_data_type.hh +++ b/source/blender/functions/FN_multi_function_data_type.hh @@ -108,6 +108,11 @@ class MFDataType { BLI_assert(false); return ""; } + + uint hash() const + { + return DefaultHash<CPPType>{}(*type_) + (uint32_t)category_; + } }; inline bool operator==(const MFDataType &a, const MFDataType &b) diff --git a/source/blender/functions/FN_multi_function_network.hh b/source/blender/functions/FN_multi_function_network.hh index a9d8508cdb8..e47c8260057 100644 --- a/source/blender/functions/FN_multi_function_network.hh +++ b/source/blender/functions/FN_multi_function_network.hh @@ -95,8 +95,6 @@ class MFNode : NonCopyable, NonMovable { Span<MFOutputSocket *> outputs(); Span<const MFOutputSocket *> outputs() const; - template<typename FuncT> void foreach_origin_socket(const FuncT &func) const; - bool all_inputs_have_origin() const; private: @@ -150,6 +148,7 @@ class MFSocket : NonCopyable, NonMovable { StringRefNull name() const; uint id() const; + uint index() const; const MFDataType &data_type() const; @@ -216,10 +215,25 @@ class MFNetwork : NonCopyable, NonMovable { void relink(MFOutputSocket &old_output, MFOutputSocket &new_output); void remove(MFNode &node); + void remove(Span<MFNode *> nodes); + + uint socket_id_amount() const; + uint node_id_amount() const; + + Span<MFDummyNode *> dummy_nodes(); + Span<MFFunctionNode *> function_nodes(); + + MFNode *node_or_null_by_id(uint id); + const MFNode *node_or_null_by_id(uint id) const; + + MFSocket *socket_or_null_by_id(uint id); + const MFSocket *socket_or_null_by_id(uint id) const; - uint max_socket_id() const; + void find_dependencies(Span<const MFInputSocket *> sockets, + VectorSet<const MFOutputSocket *> &r_dummy_sockets, + VectorSet<const MFInputSocket *> &r_unlinked_inputs) const; - std::string to_dot() const; + std::string to_dot(Span<const MFNode *> marked_nodes = {}) const; }; /* -------------------------------------------------------------------- @@ -325,16 +339,6 @@ inline Span<const MFOutputSocket *> MFNode::outputs() const return outputs_; } -template<typename FuncT> void MFNode::foreach_origin_socket(const FuncT &func) const -{ - for (const MFInputSocket *socket : inputs_) { - const MFOutputSocket *origin = socket->origin(); - if (origin != nullptr) { - func(*origin); - } - } -} - inline bool MFNode::all_inputs_have_origin() const { for (const MFInputSocket *socket : inputs_) { @@ -402,6 +406,11 @@ inline uint MFSocket::id() const return id_; } +inline uint MFSocket::index() const +{ + return index_; +} + inline const MFDataType &MFSocket::data_type() const { return data_type_; @@ -476,16 +485,51 @@ inline Span<MFInputSocket *> MFOutputSocket::targets() inline Span<const MFInputSocket *> MFOutputSocket::targets() const { - return targets_.as_span(); + return targets_; } /* -------------------------------------------------------------------- * MFNetwork inline methods. */ -inline uint MFNetwork::max_socket_id() const +inline Span<MFDummyNode *> MFNetwork::dummy_nodes() +{ + return dummy_nodes_; +} + +inline Span<MFFunctionNode *> MFNetwork::function_nodes() +{ + return function_nodes_; +} + +inline MFNode *MFNetwork::node_or_null_by_id(uint id) +{ + return node_or_null_by_id_[id]; +} + +inline const MFNode *MFNetwork::node_or_null_by_id(uint id) const +{ + return node_or_null_by_id_[id]; +} + +inline MFSocket *MFNetwork::socket_or_null_by_id(uint id) +{ + return socket_or_null_by_id_[id]; +} + +inline const MFSocket *MFNetwork::socket_or_null_by_id(uint id) const +{ + return socket_or_null_by_id_[id]; +} + +inline uint MFNetwork::socket_id_amount() const +{ + return socket_or_null_by_id_.size(); +} + +inline uint MFNetwork::node_id_amount() const { - return socket_or_null_by_id_.size() - 1; + return node_or_null_by_id_.size(); } } // namespace blender::fn diff --git a/source/blender/functions/FN_multi_function_network_optimization.hh b/source/blender/functions/FN_multi_function_network_optimization.hh new file mode 100644 index 00000000000..3cbabd72c2a --- /dev/null +++ b/source/blender/functions/FN_multi_function_network_optimization.hh @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#ifndef __FN_MULTI_FUNCTION_NETWORK_OPTIMIZATION_HH__ +#define __FN_MULTI_FUNCTION_NETWORK_OPTIMIZATION_HH__ + +#include "FN_multi_function_network.hh" + +#include "BLI_resource_collector.hh" + +namespace blender::fn::mf_network_optimization { + +void dead_node_removal(MFNetwork &network); +void constant_folding(MFNetwork &network, ResourceCollector &resources); +void common_subnetwork_elimination(MFNetwork &network); + +} // namespace blender::fn::mf_network_optimization + +#endif /* __FN_MULTI_FUNCTION_NETWORK_OPTIMIZATION_HH__ */ diff --git a/source/blender/functions/FN_multi_function_param_type.hh b/source/blender/functions/FN_multi_function_param_type.hh index 0e43e355b53..7c16b8cdf10 100644 --- a/source/blender/functions/FN_multi_function_param_type.hh +++ b/source/blender/functions/FN_multi_function_param_type.hh @@ -144,6 +144,11 @@ class MFParamType { return ELEM(interface_type_, Output, Mutable); } + bool is_output() const + { + return interface_type_ == Output; + } + friend bool operator==(const MFParamType &a, const MFParamType &b); friend bool operator!=(const MFParamType &a, const MFParamType &b); }; diff --git a/source/blender/functions/FN_spans.hh b/source/blender/functions/FN_spans.hh index b2622eab95f..c8c98d66628 100644 --- a/source/blender/functions/FN_spans.hh +++ b/source/blender/functions/FN_spans.hh @@ -339,6 +339,16 @@ class GVSpan : public VSpanBase<void> { return ref; } + static GVSpan FromSingleWithMaxSize(const CPPType &type, const void *value) + { + return GVSpan::FromSingle(type, value, UINT32_MAX); + } + + static GVSpan FromDefault(const CPPType &type) + { + return GVSpan::FromSingleWithMaxSize(type, type.default_value()); + } + static GVSpan FromFullPointerArray(const CPPType &type, const void *const *values, uint size) { GVSpan ref; diff --git a/source/blender/functions/intern/multi_function_builder.cc b/source/blender/functions/intern/multi_function_builder.cc new file mode 100644 index 00000000000..889a2595aab --- /dev/null +++ b/source/blender/functions/intern/multi_function_builder.cc @@ -0,0 +1,90 @@ +/* + * 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 "FN_multi_function_builder.hh" + +#include "BLI_hash.hh" + +namespace blender::fn { + +CustomMF_GenericConstant::CustomMF_GenericConstant(const CPPType &type, const void *value) + : type_(type), value_(value) +{ + MFSignatureBuilder signature = this->get_builder("Constant " + type.name()); + std::stringstream ss; + type.debug_print(value, ss); + signature.single_output(ss.str(), type); +} + +void CustomMF_GenericConstant::call(IndexMask mask, + MFParams params, + MFContext UNUSED(context)) const +{ + GMutableSpan output = params.uninitialized_single_output(0); + type_.fill_uninitialized_indices(value_, output.buffer(), mask); +} + +uint CustomMF_GenericConstant::hash() const +{ + return type_.hash(value_); +} + +bool CustomMF_GenericConstant::equals(const MultiFunction &other) const +{ + const CustomMF_GenericConstant *_other = dynamic_cast<const CustomMF_GenericConstant *>(&other); + if (_other == nullptr) { + return false; + } + if (type_ != _other->type_) { + return false; + } + return type_.is_equal(value_, _other->value_); +} + +static std::string gspan_to_string(GSpan array) +{ + std::stringstream ss; + ss << "["; + uint max_amount = 5; + for (uint i : IndexRange(std::min(max_amount, array.size()))) { + array.type().debug_print(array[i], ss); + ss << ", "; + } + if (max_amount < array.size()) { + ss << "..."; + } + ss << "]"; + return ss.str(); +} + +CustomMF_GenericConstantArray::CustomMF_GenericConstantArray(GSpan array) : array_(array) +{ + const CPPType &type = array.type(); + MFSignatureBuilder signature = this->get_builder("Constant " + type.name() + " Vector"); + signature.vector_output(gspan_to_string(array), type); +} + +void CustomMF_GenericConstantArray::call(IndexMask mask, + MFParams params, + MFContext UNUSED(context)) const +{ + GVectorArray &vectors = params.vector_output(0); + for (uint i : mask) { + vectors.extend(i, array_); + } +} + +} // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_network.cc b/source/blender/functions/intern/multi_function_network.cc index 5df70d92a4e..11c9c065f51 100644 --- a/source/blender/functions/intern/multi_function_network.cc +++ b/source/blender/functions/intern/multi_function_network.cc @@ -15,6 +15,8 @@ */ #include "BLI_dot_export.hh" +#include "BLI_stack.hh" + #include "FN_multi_function_network.hh" namespace blender::fn { @@ -184,17 +186,18 @@ void MFNetwork::add_link(MFOutputSocket &from, MFInputSocket &to) MFOutputSocket &MFNetwork::add_input(StringRef name, MFDataType data_type) { - return this->add_dummy(name, {}, {data_type}, {}, {name}).output(0); + return this->add_dummy(name, {}, {data_type}, {}, {"Value"}).output(0); } MFInputSocket &MFNetwork::add_output(StringRef name, MFDataType data_type) { - return this->add_dummy(name, {data_type}, {}, {name}, {}).input(0); + return this->add_dummy(name, {data_type}, {}, {"Value"}, {}).input(0); } void MFNetwork::relink(MFOutputSocket &old_output, MFOutputSocket &new_output) { BLI_assert(&old_output != &new_output); + BLI_assert(old_output.data_type_ == new_output.data_type_); for (MFInputSocket *input : old_output.targets()) { input->origin_ = &new_output; } @@ -230,7 +233,43 @@ void MFNetwork::remove(MFNode &node) node_or_null_by_id_[node.id_] = nullptr; } -std::string MFNetwork::to_dot() const +void MFNetwork::remove(Span<MFNode *> nodes) +{ + for (MFNode *node : nodes) { + this->remove(*node); + } +} + +void MFNetwork::find_dependencies(Span<const MFInputSocket *> sockets, + VectorSet<const MFOutputSocket *> &r_dummy_sockets, + VectorSet<const MFInputSocket *> &r_unlinked_inputs) const +{ + Set<const MFNode *> visited_nodes; + Stack<const MFInputSocket *> sockets_to_check; + sockets_to_check.push_multiple(sockets); + + while (!sockets_to_check.is_empty()) { + const MFInputSocket &socket = *sockets_to_check.pop(); + const MFOutputSocket *origin_socket = socket.origin(); + if (origin_socket == nullptr) { + r_unlinked_inputs.add(&socket); + continue; + } + + const MFNode &origin_node = origin_socket->node(); + + if (origin_node.is_dummy()) { + r_dummy_sockets.add(origin_socket); + continue; + } + + if (visited_nodes.add(&origin_node)) { + sockets_to_check.push_multiple(origin_node.inputs()); + } + } +} + +std::string MFNetwork::to_dot(Span<const MFNode *> marked_nodes) const { dot::DirectedGraph digraph; digraph.set_rankdir(dot::Attr_rankdir::LeftToRight); @@ -256,6 +295,13 @@ std::string MFNetwork::to_dot() const dot_nodes.add_new(node, dot_node_ref); } + for (const MFDummyNode *node : dummy_nodes_) { + dot_nodes.lookup(node).node().set_background_color("#77EE77"); + } + for (const MFNode *node : marked_nodes) { + dot_nodes.lookup(node).node().set_background_color("#7777EE"); + } + for (const MFNode *to_node : all_nodes) { dot::NodeWithSocketsRef to_dot_node = dot_nodes.lookup(to_node); diff --git a/source/blender/functions/intern/multi_function_network_evaluation.cc b/source/blender/functions/intern/multi_function_network_evaluation.cc index 08a254dc300..b59cbc6a1a2 100644 --- a/source/blender/functions/intern/multi_function_network_evaluation.cc +++ b/source/blender/functions/intern/multi_function_network_evaluation.cc @@ -58,7 +58,7 @@ class MFNetworkEvaluationStorage { uint min_array_size_; public: - MFNetworkEvaluationStorage(IndexMask mask, uint max_socket_id); + MFNetworkEvaluationStorage(IndexMask mask, uint socket_id_amount); ~MFNetworkEvaluationStorage(); /* Add the values that have been provided by the caller of the multi-function network. */ @@ -106,30 +106,30 @@ MFNetworkEvaluator::MFNetworkEvaluator(Vector<const MFOutputSocket *> inputs, BLI_assert(outputs_.size() > 0); MFSignatureBuilder signature = this->get_builder("Function Tree"); - for (auto socket : inputs_) { + for (const MFOutputSocket *socket : inputs_) { BLI_assert(socket->node().is_dummy()); MFDataType type = socket->data_type(); switch (type.category()) { case MFDataType::Single: - signature.single_input("Input", type.single_type()); + signature.single_input(socket->name(), type.single_type()); break; case MFDataType::Vector: - signature.vector_input("Input", type.vector_base_type()); + signature.vector_input(socket->name(), type.vector_base_type()); break; } } - for (auto socket : outputs_) { + for (const MFInputSocket *socket : outputs_) { BLI_assert(socket->node().is_dummy()); MFDataType type = socket->data_type(); switch (type.category()) { case MFDataType::Single: - signature.single_output("Output", type.single_type()); + signature.single_output(socket->name(), type.single_type()); break; case MFDataType::Vector: - signature.vector_output("Output", type.vector_base_type()); + signature.vector_output(socket->name(), type.vector_base_type()); break; } } @@ -142,7 +142,7 @@ void MFNetworkEvaluator::call(IndexMask mask, MFParams params, MFContext context } const MFNetwork &network = outputs_[0]->node().network(); - Storage storage(mask, network.max_socket_id()); + Storage storage(mask, network.socket_id_amount()); Vector<const MFInputSocket *> outputs_to_initialize_in_the_end; @@ -219,8 +219,6 @@ BLI_NOINLINE void MFNetworkEvaluator::evaluate_network_to_compute_outputs( sockets_to_compute.push(socket->origin()); } - Vector<const MFOutputSocket *, 32> missing_sockets; - /* This is the main loop that traverses the MFNetwork. */ while (!sockets_to_compute.is_empty()) { const MFOutputSocket &socket = *sockets_to_compute.peek(); @@ -235,17 +233,18 @@ BLI_NOINLINE void MFNetworkEvaluator::evaluate_network_to_compute_outputs( BLI_assert(node.all_inputs_have_origin()); const MFFunctionNode &function_node = node.as_function(); - missing_sockets.clear(); - function_node.foreach_origin_socket([&](const MFOutputSocket &origin) { - if (!storage.socket_is_computed(origin)) { - missing_sockets.append(&origin); + bool all_origins_are_computed = true; + for (const MFInputSocket *input_socket : function_node.inputs()) { + const MFOutputSocket *origin = input_socket->origin(); + if (origin != nullptr) { + if (!storage.socket_is_computed(*origin)) { + sockets_to_compute.push(origin); + all_origins_are_computed = false; + } } - }); - - sockets_to_compute.push_multiple(missing_sockets); + } - bool all_inputs_are_computed = missing_sockets.size() == 0; - if (all_inputs_are_computed) { + if (all_origins_are_computed) { this->evaluate_function(global_context, function_node, storage); sockets_to_compute.pop(); } @@ -507,9 +506,9 @@ struct OwnVectorValue : public Value { /** \name Storage methods * \{ */ -MFNetworkEvaluationStorage::MFNetworkEvaluationStorage(IndexMask mask, uint max_socket_id) +MFNetworkEvaluationStorage::MFNetworkEvaluationStorage(IndexMask mask, uint socket_id_amount) : mask_(mask), - value_per_output_id_(max_socket_id + 1, nullptr), + value_per_output_id_(socket_id_amount, nullptr), min_array_size_(mask.min_array_size()) { } diff --git a/source/blender/functions/intern/multi_function_network_optimization.cc b/source/blender/functions/intern/multi_function_network_optimization.cc new file mode 100644 index 00000000000..849b24a318f --- /dev/null +++ b/source/blender/functions/intern/multi_function_network_optimization.cc @@ -0,0 +1,491 @@ +/* + * 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 fn + */ + +/* Used to check if two multi-functions have the exact same type. */ +#include <typeinfo> + +#include "FN_multi_function_builder.hh" +#include "FN_multi_function_network_evaluation.hh" +#include "FN_multi_function_network_optimization.hh" + +#include "BLI_disjoint_set.hh" +#include "BLI_ghash.h" +#include "BLI_map.hh" +#include "BLI_rand.h" +#include "BLI_stack.hh" + +namespace blender::fn::mf_network_optimization { + +/* -------------------------------------------------------------------- */ +/** \name Utility functions to find nodes in a network. + * + * \{ */ + +static bool set_tag_and_check_if_modified(bool &tag, bool new_value) +{ + if (tag != new_value) { + tag = new_value; + return true; + } + else { + return false; + } +} + +static Array<bool> mask_nodes_to_the_left(MFNetwork &network, Span<MFNode *> nodes) +{ + Array<bool> is_to_the_left(network.node_id_amount(), false); + Stack<MFNode *> nodes_to_check; + + for (MFNode *node : nodes) { + is_to_the_left[node->id()] = true; + nodes_to_check.push(node); + } + + while (!nodes_to_check.is_empty()) { + MFNode &node = *nodes_to_check.pop(); + + for (MFInputSocket *input_socket : node.inputs()) { + MFOutputSocket *origin = input_socket->origin(); + if (origin != nullptr) { + MFNode &origin_node = origin->node(); + if (set_tag_and_check_if_modified(is_to_the_left[origin_node.id()], true)) { + nodes_to_check.push(&origin_node); + } + } + } + } + + return is_to_the_left; +} + +static Array<bool> mask_nodes_to_the_right(MFNetwork &network, Span<MFNode *> nodes) +{ + Array<bool> is_to_the_right(network.node_id_amount(), false); + Stack<MFNode *> nodes_to_check; + + for (MFNode *node : nodes) { + is_to_the_right[node->id()] = true; + nodes_to_check.push(node); + } + + while (!nodes_to_check.is_empty()) { + MFNode &node = *nodes_to_check.pop(); + + for (MFOutputSocket *output_socket : node.outputs()) { + for (MFInputSocket *target_socket : output_socket->targets()) { + MFNode &target_node = target_socket->node(); + if (set_tag_and_check_if_modified(is_to_the_right[target_node.id()], true)) { + nodes_to_check.push(&target_node); + } + } + } + } + + return is_to_the_right; +} + +static Vector<MFNode *> find_nodes_based_on_mask(MFNetwork &network, + Span<bool> id_mask, + bool mask_value) +{ + Vector<MFNode *> nodes; + for (uint id : id_mask.index_range()) { + if (id_mask[id] == mask_value) { + MFNode *node = network.node_or_null_by_id(id); + if (node != nullptr) { + nodes.append(node); + } + } + } + return nodes; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Dead Node Removal + * + * \{ */ + +/** + * Unused nodes are all those nodes that no dummy node depends upon. + */ +void dead_node_removal(MFNetwork &network) +{ + Array<bool> node_is_used_mask = mask_nodes_to_the_left(network, network.dummy_nodes()); + Vector<MFNode *> nodes_to_remove = find_nodes_based_on_mask(network, node_is_used_mask, false); + network.remove(nodes_to_remove); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Constant Folding + * + * \{ */ + +static Vector<MFNode *> find_non_constant_nodes(MFNetwork &network) +{ + Vector<MFNode *> non_constant_nodes; + non_constant_nodes.extend(network.dummy_nodes()); + + for (MFFunctionNode *node : network.function_nodes()) { + if (!node->all_inputs_have_origin()) { + non_constant_nodes.append(node); + } + } + return non_constant_nodes; +} + +static bool output_has_non_constant_target_node(MFOutputSocket *output_socket, + Span<bool> is_not_constant_mask) +{ + for (MFInputSocket *target_socket : output_socket->targets()) { + MFNode &target_node = target_socket->node(); + bool target_is_not_constant = is_not_constant_mask[target_node.id()]; + if (target_is_not_constant) { + return true; + } + } + return false; +} + +static MFInputSocket *try_find_dummy_target_socket(MFOutputSocket *output_socket) +{ + for (MFInputSocket *target_socket : output_socket->targets()) { + if (target_socket->node().is_dummy()) { + return target_socket; + } + } + return nullptr; +} + +static Vector<MFInputSocket *> find_constant_inputs_to_fold( + MFNetwork &network, Vector<MFDummyNode *> &r_temporary_nodes) +{ + Vector<MFNode *> non_constant_nodes = find_non_constant_nodes(network); + Array<bool> is_not_constant_mask = mask_nodes_to_the_right(network, non_constant_nodes); + Vector<MFNode *> constant_nodes = find_nodes_based_on_mask(network, is_not_constant_mask, false); + + Vector<MFInputSocket *> sockets_to_compute; + for (MFNode *node : constant_nodes) { + if (node->inputs().size() == 0) { + continue; + } + + for (MFOutputSocket *output_socket : node->outputs()) { + MFDataType data_type = output_socket->data_type(); + if (output_has_non_constant_target_node(output_socket, is_not_constant_mask)) { + MFInputSocket *dummy_target = try_find_dummy_target_socket(output_socket); + if (dummy_target == nullptr) { + dummy_target = &network.add_output("Dummy", data_type); + network.add_link(*output_socket, *dummy_target); + r_temporary_nodes.append(&dummy_target->node().as_dummy()); + } + + sockets_to_compute.append(dummy_target); + } + } + } + return sockets_to_compute; +} + +static void prepare_params_for_constant_folding(const MultiFunction &network_fn, + MFParamsBuilder ¶ms, + ResourceCollector &resources) +{ + for (uint param_index : network_fn.param_indices()) { + MFParamType param_type = network_fn.param_type(param_index); + MFDataType data_type = param_type.data_type(); + + switch (data_type.category()) { + case MFDataType::Single: { + /* Allocates memory for a single constant folded value. */ + const CPPType &cpp_type = data_type.single_type(); + void *buffer = resources.linear_allocator().allocate(cpp_type.size(), + cpp_type.alignment()); + GMutableSpan array{cpp_type, buffer, 1}; + params.add_uninitialized_single_output(array); + break; + } + case MFDataType::Vector: { + /* Allocates memory for a constant folded vector. */ + const CPPType &cpp_type = data_type.vector_base_type(); + GVectorArray &vector_array = resources.construct<GVectorArray>(AT, cpp_type, 1); + params.add_vector_output(vector_array); + break; + } + } + } +} + +static Array<MFOutputSocket *> add_constant_folded_sockets(const MultiFunction &network_fn, + MFParamsBuilder ¶ms, + ResourceCollector &resources, + MFNetwork &network) +{ + Array<MFOutputSocket *> folded_sockets{network_fn.param_indices().size(), nullptr}; + + for (uint param_index : network_fn.param_indices()) { + MFParamType param_type = network_fn.param_type(param_index); + MFDataType data_type = param_type.data_type(); + + const MultiFunction *constant_fn = nullptr; + + switch (data_type.category()) { + case MFDataType::Single: { + const CPPType &cpp_type = data_type.single_type(); + GMutableSpan array = params.computed_array(param_index); + void *buffer = array.buffer(); + resources.add(buffer, array.type().destruct_cb(), AT); + + constant_fn = &resources.construct<CustomMF_GenericConstant>(AT, cpp_type, buffer); + break; + } + case MFDataType::Vector: { + GVectorArray &vector_array = params.computed_vector_array(param_index); + GSpan array = vector_array[0]; + constant_fn = &resources.construct<CustomMF_GenericConstantArray>(AT, array); + break; + } + } + + MFFunctionNode &folded_node = network.add_function(*constant_fn); + folded_sockets[param_index] = &folded_node.output(0); + } + return folded_sockets; +} + +static Array<MFOutputSocket *> compute_constant_sockets_and_add_folded_nodes( + MFNetwork &network, + Span<const MFInputSocket *> sockets_to_compute, + ResourceCollector &resources) +{ + MFNetworkEvaluator network_fn{{}, sockets_to_compute}; + + MFContextBuilder context; + MFParamsBuilder params{network_fn, 1}; + prepare_params_for_constant_folding(network_fn, params, resources); + network_fn.call({0}, params, context); + return add_constant_folded_sockets(network_fn, params, resources, network); +} + +/** + * Find function nodes that always output the same value and replace those with constant nodes. + */ +void constant_folding(MFNetwork &network, ResourceCollector &resources) +{ + Vector<MFDummyNode *> temporary_nodes; + Vector<MFInputSocket *> inputs_to_fold = find_constant_inputs_to_fold(network, temporary_nodes); + if (inputs_to_fold.size() == 0) { + return; + } + + Array<MFOutputSocket *> folded_sockets = compute_constant_sockets_and_add_folded_nodes( + network, inputs_to_fold, resources); + + for (uint i : inputs_to_fold.index_range()) { + MFOutputSocket &original_socket = *inputs_to_fold[i]->origin(); + network.relink(original_socket, *folded_sockets[i]); + } + + network.remove(temporary_nodes); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Common Sub-network Elimination + * + * \{ */ + +static uint32_t compute_node_hash(MFFunctionNode &node, RNG *rng, Span<uint32_t> node_hashes) +{ + uint32_t combined_inputs_hash = 394659347u; + for (MFInputSocket *input_socket : node.inputs()) { + MFOutputSocket *origin_socket = input_socket->origin(); + uint32_t input_hash; + if (origin_socket == nullptr) { + input_hash = BLI_rng_get_uint(rng); + } + else { + input_hash = BLI_ghashutil_combine_hash(node_hashes[origin_socket->node().id()], + origin_socket->index()); + } + combined_inputs_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, input_hash); + } + + uint32_t function_hash = node.function().hash(); + uint32_t node_hash = BLI_ghashutil_combine_hash(combined_inputs_hash, function_hash); + return node_hash; +} + +/** + * Produces a hash for every node. Two nodes with the same hash should have a high probability of + * outputting the same values. + */ +static Array<uint32_t> compute_node_hashes(MFNetwork &network) +{ + RNG *rng = BLI_rng_new(0); + Array<uint32_t> node_hashes(network.node_id_amount()); + Array<bool> node_is_hashed(network.node_id_amount(), false); + + /* No dummy nodes are not assumed to output the same values. */ + for (MFDummyNode *node : network.dummy_nodes()) { + uint32_t node_hash = BLI_rng_get_uint(rng); + node_hashes[node->id()] = node_hash; + node_is_hashed[node->id()] = true; + } + + Stack<MFFunctionNode *> nodes_to_check; + nodes_to_check.push_multiple(network.function_nodes()); + + while (!nodes_to_check.is_empty()) { + MFFunctionNode &node = *nodes_to_check.peek(); + if (node_is_hashed[node.id()]) { + nodes_to_check.pop(); + continue; + } + + /* Make sure that origin nodes are hashed first. */ + bool all_dependencies_ready = true; + for (MFInputSocket *input_socket : node.inputs()) { + MFOutputSocket *origin_socket = input_socket->origin(); + if (origin_socket != nullptr) { + MFNode &origin_node = origin_socket->node(); + if (!node_is_hashed[origin_node.id()]) { + all_dependencies_ready = false; + nodes_to_check.push(&origin_node.as_function()); + } + } + } + if (!all_dependencies_ready) { + continue; + } + + uint32_t node_hash = compute_node_hash(node, rng, node_hashes); + node_hashes[node.id()] = node_hash; + node_is_hashed[node.id()] = true; + nodes_to_check.pop(); + } + + BLI_rng_free(rng); + return node_hashes; +} + +static Map<uint32_t, Vector<MFNode *, 1>> group_nodes_by_hash(MFNetwork &network, + Span<uint32_t> node_hashes) +{ + Map<uint32_t, Vector<MFNode *, 1>> nodes_by_hash; + for (uint id : IndexRange(network.node_id_amount())) { + MFNode *node = network.node_or_null_by_id(id); + if (node != nullptr) { + uint32_t node_hash = node_hashes[id]; + nodes_by_hash.lookup_or_add_default(node_hash).append(node); + } + } + return nodes_by_hash; +} + +static bool functions_are_equal(const MultiFunction &a, const MultiFunction &b) +{ + if (&a == &b) { + return true; + } + if (typeid(a) == typeid(b)) { + return a.equals(b); + } + return false; +} + +static bool nodes_output_same_values(DisjointSet &cache, const MFNode &a, const MFNode &b) +{ + if (cache.in_same_set(a.id(), b.id())) { + return true; + } + + if (a.is_dummy() || b.is_dummy()) { + return false; + } + if (!functions_are_equal(a.as_function().function(), b.as_function().function())) { + return false; + } + for (uint i : a.inputs().index_range()) { + const MFOutputSocket *origin_a = a.input(i).origin(); + const MFOutputSocket *origin_b = b.input(i).origin(); + if (origin_a == nullptr || origin_b == nullptr) { + return false; + } + if (!nodes_output_same_values(cache, origin_a->node(), origin_b->node())) { + return false; + } + } + + cache.join(a.id(), b.id()); + return true; +} + +static void relink_duplicate_nodes(MFNetwork &network, + Map<uint32_t, Vector<MFNode *, 1>> &nodes_by_hash) +{ + DisjointSet same_node_cache{network.node_id_amount()}; + + for (Span<MFNode *> nodes_with_same_hash : nodes_by_hash.values()) { + if (nodes_with_same_hash.size() <= 1) { + continue; + } + + Vector<MFNode *, 16> nodes_to_check = nodes_with_same_hash; + while (nodes_to_check.size() >= 2) { + Vector<MFNode *, 16> remaining_nodes; + + MFNode &deduplicated_node = *nodes_to_check[0]; + for (MFNode *node : nodes_to_check.as_span().drop_front(1)) { + /* This is true with fairly high probability, but hash collisions can happen. So we have to + * check if the node actually output the same values. */ + if (nodes_output_same_values(same_node_cache, deduplicated_node, *node)) { + for (uint i : deduplicated_node.outputs().index_range()) { + network.relink(node->output(i), deduplicated_node.output(i)); + } + } + else { + remaining_nodes.append(node); + } + } + nodes_to_check = std::move(remaining_nodes); + } + } +} + +/** + * Tries to detect duplicate sub-networks and eliminates them. This can help quite a lot when node + * groups were used to create the network. + */ +void common_subnetwork_elimination(MFNetwork &network) +{ + Array<uint32_t> node_hashes = compute_node_hashes(network); + Map<uint32_t, Vector<MFNode *, 1>> nodes_by_hash = group_nodes_by_hash(network, node_hashes); + relink_duplicate_nodes(network, nodes_by_hash); +} + +/** \} */ + +} // namespace blender::fn::mf_network_optimization diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt index 92aacc74190..497cb4a10a5 100644 --- a/source/blender/gpencil_modifiers/CMakeLists.txt +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -42,7 +42,6 @@ set(INC_SYS ) set(SRC - intern/MOD_gpencil_util.h intern/MOD_gpencil_ui_common.c intern/MOD_gpencil_util.c @@ -65,8 +64,9 @@ set(SRC intern/MOD_gpenciltime.c intern/MOD_gpenciltint.c - intern/MOD_gpencil_ui_common.h MOD_gpencil_modifiertypes.h + intern/MOD_gpencil_ui_common.h + intern/MOD_gpencil_util.h ) set(LIB diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c index 54ed2ffafe1..56d94611b5d 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c @@ -119,7 +119,6 @@ static void gpf_clear_all_strokes(bGPDframe *gpf) /* Reduce the number of points in the stroke * * Note: This won't be called if all points are present/removed - * TODO: Allow blending of growing/shrinking tip (e.g. for more gradual transitions) */ static void reduce_stroke_points(bGPDstroke *gps, const int num_points, @@ -132,7 +131,6 @@ static void reduce_stroke_points(bGPDstroke *gps, } /* Which end should points be removed from */ - // TODO: free stroke weights switch (transition) { case GP_BUILD_TRANSITION_GROW: /* Show in forward order = * Remove ungrown-points from end of stroke. */ @@ -279,7 +277,6 @@ static void build_sequential(BuildGpencilModifierData *mmd, bGPDframe *gpf, floa } else { /* Some proportion of stroke is visible */ - /* XXX: Will the transition settings still be valid now? */ if ((first_visible <= cell->start_idx) && (last_visible >= cell->end_idx)) { /* Do nothing - whole stroke is visible */ } @@ -303,8 +300,6 @@ static void build_sequential(BuildGpencilModifierData *mmd, bGPDframe *gpf, floa /* --------------------------------------------- */ /* Concurrent - Show multiple strokes at once */ -// TODO: Allow random offsets to start times -// TODO: Allow varying speeds? Scaling of progress? static void build_concurrent(BuildGpencilModifierData *mmd, bGPDframe *gpf, float fac) { bGPDstroke *gps, *gps_next; @@ -342,8 +337,7 @@ static void build_concurrent(BuildGpencilModifierData *mmd, bGPDframe *gpf, floa /* Build effect occurs over when fac = 0, to fac = relative_len */ if (fac <= relative_len) { /* Scale fac to fit relative_len */ - /* FIXME: prevent potential div by zero (e.g. very short stroke vs one very long one) */ - const float scaled_fac = fac / relative_len; + const float scaled_fac = fac / MAX2(relative_len, PSEUDOINVERSE_EPSILON); if (reverse) { num_points = (int)roundf((1.0f - scaled_fac) * gps->totpoints); @@ -371,8 +365,7 @@ static void build_concurrent(BuildGpencilModifierData *mmd, bGPDframe *gpf, floa const float start_fac = 1.0f - relative_len; if (fac >= start_fac) { - /* FIXME: prevent potential div by zero (e.g. very short stroke vs one very long one) */ - const float scaled_fac = (fac - start_fac) / relative_len; + const float scaled_fac = (fac - start_fac) / MAX2(relative_len, PSEUDOINVERSE_EPSILON); if (reverse) { num_points = (int)roundf((1.0f - scaled_fac) * gps->totpoints); @@ -393,8 +386,6 @@ static void build_concurrent(BuildGpencilModifierData *mmd, bGPDframe *gpf, floa break; } - - /* TODO... */ } /* Modify the stroke geometry */ diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c index 812bb5628e1..0d8a5f7914e 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c @@ -327,7 +327,7 @@ static void random_panel_draw(const bContext *C, Panel *panel) static void mask_panel_draw(const bContext *C, Panel *panel) { - gpencil_modifier_masking_panel_draw(C, panel, true, false); + gpencil_modifier_masking_panel_draw(C, panel, true, true); } static void panelRegister(ARegionType *region_type) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c index 8d4556421eb..1e75c5926cd 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c @@ -83,7 +83,7 @@ static void deformStroke(GpencilModifierData *md, mmd->material, mmd->pass_index, mmd->layer_pass, - mmd->mode == GP_SIMPLIFY_SAMPLE ? 3 : 4, + mmd->mode == GP_SIMPLIFY_SAMPLE ? 2 : 4, gpl, gps, mmd->flag & GP_SIMPLIFY_INVERT_LAYER, diff --git a/source/blender/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h index 41a29a4d45d..c7e74040568 100644 --- a/source/blender/gpu/GPU_buffers.h +++ b/source/blender/gpu/GPU_buffers.h @@ -67,7 +67,7 @@ GPU_PBVH_Buffers *GPU_pbvh_bmesh_buffers_build(bool smooth_shading); void GPU_pbvh_bmesh_buffers_update_free(GPU_PBVH_Buffers *buffers); void GPU_pbvh_grid_buffers_update_free(GPU_PBVH_Buffers *buffers, const struct DMFlagMat *grid_flag_mats, - int *grid_indices); + const int *grid_indices); /* Update mesh buffers without topology changes. Threaded. */ enum { diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index eeb2d2caef2..c372bfaf218 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -169,8 +169,8 @@ void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link); void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3], - short *falloff_type, - float *sharpness); + const short *falloff_type, + const float *sharpness); struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, int sample_len, struct GPUTexture **tex_profile); diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index 9c21f9040da..10d5a860f6a 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -38,6 +38,7 @@ #include "BLI_utildefines.h" #include "DNA_meshdata_types.h" +#include "DNA_userdef_types.h" #include "BKE_DerivedMesh.h" #include "BKE_ccg.h" @@ -234,7 +235,8 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers, const bool show_mask = vmask && (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0; const bool show_face_sets = sculpt_face_sets && (update_flags & GPU_PBVH_BUFFERS_SHOW_SCULPT_FACE_SETS) != 0; - const bool show_vcol = (vcol || vtcol) && (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0; + const bool show_vcol = (vcol || (vtcol && U.experimental.use_sculpt_vertex_colors)) && + (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0; bool empty_mask = true; bool default_face_set = true; @@ -317,7 +319,7 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers, /* Vertex Colors. */ if (show_vcol) { ushort scol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX}; - if (vtcol) { + if (vtcol && U.experimental.use_sculpt_vertex_colors) { scol[0] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[0]); scol[1] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[1]); scol[2] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[2]); @@ -450,7 +452,7 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const MPoly *mpoly, static void gpu_pbvh_grid_fill_index_buffers(GPU_PBVH_Buffers *buffers, SubdivCCG *UNUSED(subdiv_ccg), const int *UNUSED(face_sets), - int *grid_indices, + const int *grid_indices, uint visible_quad_len, int totgrid, int gridsize) @@ -581,7 +583,7 @@ static void gpu_pbvh_grid_fill_index_buffers(GPU_PBVH_Buffers *buffers, void GPU_pbvh_grid_buffers_update_free(GPU_PBVH_Buffers *buffers, const struct DMFlagMat *grid_flag_mats, - int *grid_indices) + const int *grid_indices) { const bool smooth = grid_flag_mats[grid_indices[0]].flag & ME_SMOOTH; diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.c index fbeb2edc266..d1c7aba37df 100644 --- a/source/blender/gpu/intern/gpu_extensions.c +++ b/source/blender/gpu/intern/gpu_extensions.c @@ -339,7 +339,6 @@ void gpu_extensions_init(void) GG.depth_blitting_workaround = true; GG.unused_fb_slot_workaround = true; GG.texture_copy_workaround = true; - GG.context_local_shaders_workaround = GLEW_ARB_get_program_binary; } /* Special fix for theses specific GPUs. @@ -347,7 +346,12 @@ void gpu_extensions_init(void) if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_OFFICIAL) && (strstr(renderer, "HD Graphics 620") || strstr(renderer, "HD Graphics 630"))) { GG.mip_render_workaround = true; - GG.context_local_shaders_workaround = GLEW_ARB_get_program_binary; + } + + /* Intel Ivy Bridge GPU's seems to have buggy cube-map array support. (see T75943) */ + if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_OFFICIAL) && + (strstr(renderer, "HD Graphics 4000") || strstr(renderer, "HD Graphics 2500"))) { + GG.glew_arb_texture_cube_map_array_is_supported = false; } /* df/dy calculation factors, those are dependent on driver */ diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index d2384b9c065..c65c1046b8f 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -497,8 +497,8 @@ static void compute_sss_translucence_kernel(const GPUSssKernelData *kd, void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3], - short *falloff_type, - float *sharpness) + const short *falloff_type, + const float *sharpness) { copy_v3_v3(material->sss_radii, radii); material->sss_falloff = (falloff_type) ? *falloff_type : 0.0; diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl index 7309549062c..d15f48c8f8a 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl @@ -9,25 +9,33 @@ uniform vec4 parameters[MAX_PARAM * MAX_INSTANCE]; uniform vec4 parameters[MAX_PARAM]; #endif -/* gl_InstanceID is 0 if not drawing instances. */ -#define recti parameters[gl_InstanceID * MAX_PARAM + 0] -#define rect parameters[gl_InstanceID * MAX_PARAM + 1] -#define radsi parameters[gl_InstanceID * MAX_PARAM + 2].x -#define rads parameters[gl_InstanceID * MAX_PARAM + 2].y -#define faci parameters[gl_InstanceID * MAX_PARAM + 2].zw -#define roundCorners parameters[gl_InstanceID * MAX_PARAM + 3] -#define colorInner1 parameters[gl_InstanceID * MAX_PARAM + 4] -#define colorInner2 parameters[gl_InstanceID * MAX_PARAM + 5] -#define colorEdge parameters[gl_InstanceID * MAX_PARAM + 6] -#define colorEmboss parameters[gl_InstanceID * MAX_PARAM + 7] -#define colorTria parameters[gl_InstanceID * MAX_PARAM + 8] -#define tria1Center parameters[gl_InstanceID * MAX_PARAM + 9].xy -#define tria2Center parameters[gl_InstanceID * MAX_PARAM + 9].zw -#define tria1Size parameters[gl_InstanceID * MAX_PARAM + 10].x -#define tria2Size parameters[gl_InstanceID * MAX_PARAM + 10].y -#define shadeDir parameters[gl_InstanceID * MAX_PARAM + 10].z -#define alphaDiscard parameters[gl_InstanceID * MAX_PARAM + 10].w -#define triaType parameters[gl_InstanceID * MAX_PARAM + 11].x +/* gl_InstanceID is supposed to be 0 if not drawing instances, but this seems + * to be violated in some drivers. For example, macOS 10.15.4 and Intel Iris + * causes T78307 when using gl_InstanceID outside of instance. */ +#ifdef USE_INSTANCE +# define widgetID gl_InstanceID +#else +# define widgetID 0 +#endif + +#define recti parameters[widgetID * MAX_PARAM + 0] +#define rect parameters[widgetID * MAX_PARAM + 1] +#define radsi parameters[widgetID * MAX_PARAM + 2].x +#define rads parameters[widgetID * MAX_PARAM + 2].y +#define faci parameters[widgetID * MAX_PARAM + 2].zw +#define roundCorners parameters[widgetID * MAX_PARAM + 3] +#define colorInner1 parameters[widgetID * MAX_PARAM + 4] +#define colorInner2 parameters[widgetID * MAX_PARAM + 5] +#define colorEdge parameters[widgetID * MAX_PARAM + 6] +#define colorEmboss parameters[widgetID * MAX_PARAM + 7] +#define colorTria parameters[widgetID * MAX_PARAM + 8] +#define tria1Center parameters[widgetID * MAX_PARAM + 9].xy +#define tria2Center parameters[widgetID * MAX_PARAM + 9].zw +#define tria1Size parameters[widgetID * MAX_PARAM + 10].x +#define tria2Size parameters[widgetID * MAX_PARAM + 10].y +#define shadeDir parameters[widgetID * MAX_PARAM + 10].z +#define alphaDiscard parameters[widgetID * MAX_PARAM + 10].w +#define triaType parameters[widgetID * MAX_PARAM + 11].x /* We encode alpha check and discard factor together. */ #define doAlphaCheck (alphaDiscard < 0.0) @@ -179,10 +187,4 @@ void main() vec2 pos = (is_tria) ? do_tria() : do_widget(); gl_Position = ModelViewProjectionMatrix * vec4(pos, 0.0, 1.0); - -#ifdef OS_MAC - /* Generate a dummy read to avoid the driver bug with shaders having no - * vertex reads on macOS (T78307) */ - gl_Position.x += dummy * 0.0; -#endif } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_sky.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_sky.glsl index 981d17b4283..b6aad5904ff 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_sky.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_sky.glsl @@ -1,4 +1,150 @@ -void node_tex_sky(vec3 co, out vec4 color) +float sky_angle_between(float thetav, float phiv, float theta, float phi) +{ + float cospsi = sin(thetav) * sin(theta) * cos(phi - phiv) + cos(thetav) * cos(theta); + + if (cospsi > 1.0) { + return 0.0; + } + if (cospsi < -1.0) { + return M_PI; + } + + return acos(cospsi); +} + +vec3 sky_spherical_coordinates(vec3 dir) +{ + return vec3(M_PI_2 - atan(dir.z, length(dir.xy)), atan(dir.x, dir.y), 0); +} + +/* Preetham */ +/* lam03+lam4: 5 floats passed as vec4+float */ +float sky_perez_function(vec4 lam03, float lam4, float theta, float gamma) +{ + float ctheta = cos(theta); + float cgamma = cos(gamma); + + return (1.0 + lam03[0] * exp(lam03[1] / ctheta)) * + (1.0 + lam03[2] * exp(lam03[3] * gamma) + lam4 * cgamma * cgamma); +} + +vec3 xyY_to_xyz(float x, float y, float Y) +{ + float X, Z; + + if (y != 0.0) { + X = (x / y) * Y; + } + else { + X = 0.0; + } + + if (y != 0.0 && Y != 0.0) { + Z = ((1.0 - x - y) / y) * Y; + } + else { + Z = 0.0; + } + + return vec3(X, Y, Z); +} + +void node_tex_sky_preetham(vec3 co, + vec4 config_Y03, + float config_Y4, + vec4 config_x03, + float config_x4, + vec4 config_y03, + float config_y4, + vec2 sun_angles, + vec3 radiance, + vec3 xyz_to_r, + vec3 xyz_to_g, + vec3 xyz_to_b, + out vec4 color) +{ + /* convert vector to spherical coordinates */ + vec3 spherical = sky_spherical_coordinates(co); + float theta = spherical[0]; + float phi = spherical[1]; + + float suntheta = sun_angles[0]; + float sunphi = sun_angles[1]; + + /* angle between sun direction and dir */ + float gamma = sky_angle_between(theta, phi, suntheta, sunphi); + + /* clamp theta to horizon */ + theta = min(theta, M_PI_2 - 0.001); + + /* compute xyY color space values */ + float Y = radiance[0] * sky_perez_function(config_Y03, config_Y4, theta, gamma); + float x = radiance[1] * sky_perez_function(config_x03, config_x4, theta, gamma); + float y = radiance[2] * sky_perez_function(config_y03, config_y4, theta, gamma); + + /* convert to RGB */ + vec3 xyz = xyY_to_xyz(x, y, Y); + color = vec4(dot(xyz_to_r, xyz), dot(xyz_to_g, xyz), dot(xyz_to_b, xyz), 1); +} + +/* Hosek / Wilkie */ +float sky_radiance_hosekwilkie( + vec4 config03, vec4 config47, float config8, float theta, float gamma) +{ + float ctheta = cos(theta); + float cgamma = cos(gamma); + + float expM = exp(config47[0] * gamma); + float rayM = cgamma * cgamma; + float mieM = (1.0 + rayM) / pow((1.0 + config8 * config8 - 2.0 * config8 * cgamma), 1.5); + float zenith = sqrt(ctheta); + + return (1.0 + config03[0] * exp(config03[1] / (ctheta + 0.01))) * + (config03[2] + config03[3] * expM + config47[1] * rayM + config47[2] * mieM + + config47[3] * zenith); +} + +void node_tex_sky_hosekwilkie(vec3 co, + vec4 config_x03, + vec4 config_x47, + vec4 config_y03, + vec4 config_y47, + vec4 config_z03, + vec4 config_z47, + vec3 config_xyz8, + vec2 sun_angles, + vec3 radiance, + vec3 xyz_to_r, + vec3 xyz_to_g, + vec3 xyz_to_b, + out vec4 color) +{ + /* convert vector to spherical coordinates */ + vec3 spherical = sky_spherical_coordinates(co); + float theta = spherical[0]; + float phi = spherical[1]; + + float suntheta = sun_angles[0]; + float sunphi = sun_angles[1]; + + /* angle between sun direction and dir */ + float gamma = sky_angle_between(theta, phi, suntheta, sunphi); + + /* clamp theta to horizon */ + theta = min(theta, M_PI_2 - 0.001); + + vec3 xyz; + xyz.x = sky_radiance_hosekwilkie(config_x03, config_x47, config_xyz8[0], theta, gamma) * + radiance.x; + xyz.y = sky_radiance_hosekwilkie(config_y03, config_y47, config_xyz8[1], theta, gamma) * + radiance.y; + xyz.z = sky_radiance_hosekwilkie(config_z03, config_z47, config_xyz8[2], theta, gamma) * + radiance.z; + + color = vec4(dot(xyz_to_r, xyz), dot(xyz_to_g, xyz), dot(xyz_to_b, xyz), 1); +} + +void node_tex_sky_nishita(vec3 co, out vec4 color) { color = vec4(1.0); } diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h index 3158e3419b0..4530f6c9fc0 100644 --- a/source/blender/imbuf/IMB_colormanagement.h +++ b/source/blender/imbuf/IMB_colormanagement.h @@ -72,6 +72,7 @@ BLI_INLINE float IMB_colormanagement_get_luminance(const float rgb[3]); BLI_INLINE unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char[3]); BLI_INLINE void IMB_colormangement_xyz_to_rgb(float rgb[3], const float xyz[3]); BLI_INLINE void IMB_colormangement_rgb_to_xyz(float xyz[3], const float rgb[3]); +const float *IMB_colormangement_get_xyz_to_rgb(void); /* ** Color space transformation functions ** */ void IMB_colormanagement_transform(float *buffer, diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 83ef910d0bb..fc7e03c3073 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -367,6 +367,7 @@ struct anim *IMB_open_anim(const char *name, void IMB_suffix_anim(struct anim *anim, const char *suffix); void IMB_close_anim(struct anim *anim); void IMB_close_anim_proxies(struct anim *anim); +bool IMB_anim_can_produce_frames(const struct anim *anim); /** * @@ -411,7 +412,7 @@ void IMB_free_anim(struct anim *anim); void IMB_filter(struct ImBuf *ibuf); void IMB_mask_filter_extend(char *mask, int width, int height); -void IMB_mask_clear(struct ImBuf *ibuf, char *mask, int val); +void IMB_mask_clear(struct ImBuf *ibuf, const char *mask, int val); void IMB_filter_extend(struct ImBuf *ibuf, char *mask, int filter); void IMB_makemipmap(struct ImBuf *ibuf, int use_filter); void IMB_remakemipmap(struct ImBuf *ibuf, int use_filter); @@ -599,7 +600,7 @@ void bilinear_interpolation_color_wrap( struct ImBuf *in, unsigned char col[4], float col_float[4], float u, float v); void IMB_alpha_under_color_float(float *rect_float, int x, int y, float backcol[3]); -void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, float backcol[3]); +void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, const float backcol[3]); void IMB_sampleImageAtLocation( struct ImBuf *ibuf, float x, float y, bool make_linear_rgb, float color[4]); diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 11b30a24cde..220801137f5 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -304,6 +304,21 @@ struct anim *IMB_open_anim(const char *name, return (anim); } +bool IMB_anim_can_produce_frames(const struct anim *anim) +{ +#ifdef WITH_AVI + if (anim->avi != NULL) { + return true; + } +#endif +#ifdef WITH_FFMPEG + if (anim->pCodecCtx != NULL) { + return true; + } +#endif + return false; +} + void IMB_suffix_anim(struct anim *anim, const char *suffix) { BLI_strncpy(anim->suffix, suffix, sizeof(anim->suffix)); diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 3f5a0f25cc5..5c9ebbaba21 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -1456,6 +1456,11 @@ bool IMB_colormanagement_space_name_is_data(const char *name) return (colorspace && colorspace->is_data); } +const float *IMB_colormangement_get_xyz_to_rgb() +{ + return &imbuf_xyz_to_rgb[0][0]; +} + /*********************** Threaded display buffer transform routines *************************/ typedef struct DisplayBufferThread { @@ -1718,7 +1723,7 @@ static void *do_display_buffer_apply_thread(void *handle_v) } static void display_buffer_apply_threaded(ImBuf *ibuf, - float *buffer, + const float *buffer, unsigned char *byte_buffer, float *display_buffer, unsigned char *display_buffer_byte, diff --git a/source/blender/imbuf/intern/dds/BlockDXT.cpp b/source/blender/imbuf/intern/dds/BlockDXT.cpp index 4397c1febab..9fd6d71e091 100644 --- a/source/blender/imbuf/intern/dds/BlockDXT.cpp +++ b/source/blender/imbuf/intern/dds/BlockDXT.cpp @@ -241,7 +241,7 @@ void BlockDXT1::decodeBlockNV5x(ColorBlock *block) const } } -void BlockDXT1::setIndices(int *idx) +void BlockDXT1::setIndices(const int *idx) { indices = 0; for (uint i = 0; i < 16; i++) { @@ -580,7 +580,7 @@ void BlockCTX1::decodeBlock(ColorBlock *block) const } } -void BlockCTX1::setIndices(int *idx) +void BlockCTX1::setIndices(const int *idx) { indices = 0; for (uint i = 0; i < 16; i++) { diff --git a/source/blender/imbuf/intern/dds/BlockDXT.h b/source/blender/imbuf/intern/dds/BlockDXT.h index 16937bce042..57430dbaea2 100644 --- a/source/blender/imbuf/intern/dds/BlockDXT.h +++ b/source/blender/imbuf/intern/dds/BlockDXT.h @@ -76,7 +76,7 @@ struct BlockDXT1 { void decodeBlock(ColorBlock *block) const; void decodeBlockNV5x(ColorBlock *block) const; - void setIndices(int *idx); + void setIndices(const int *idx); void flip4(); void flip2(); @@ -289,7 +289,7 @@ struct BlockCTX1 { }; void evaluatePalette(Color32 color_array[4]) const; - void setIndices(int *idx); + void setIndices(const int *idx); void decodeBlock(ColorBlock *block) const; diff --git a/source/blender/imbuf/intern/filter.c b/source/blender/imbuf/intern/filter.c index e36088f8eac..d8a5096af71 100644 --- a/source/blender/imbuf/intern/filter.c +++ b/source/blender/imbuf/intern/filter.c @@ -363,7 +363,7 @@ void IMB_mask_filter_extend(char *mask, int width, int height) MEM_freeN(temprect); } -void IMB_mask_clear(ImBuf *ibuf, char *mask, int val) +void IMB_mask_clear(ImBuf *ibuf, const char *mask, int val) { int x, y; if (ibuf->rect_float) { diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c index 7ebbd1a7409..bf58f047773 100644 --- a/source/blender/imbuf/intern/imageprocess.c +++ b/source/blender/imbuf/intern/imageprocess.c @@ -456,7 +456,7 @@ void IMB_alpha_under_color_float(float *rect_float, int x, int y, float backcol[ } } -void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, float backcol[3]) +void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, const float backcol[3]) { size_t a = ((size_t)x) * y; unsigned char *cp = rect; diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 985a8e977ca..7cc31b99077 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -894,7 +894,7 @@ static void index_rebuild_ffmpeg_proc_decoded_frame(FFmpegIndexBuilderContext *c } static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context, - short *stop, + const short *stop, short *do_update, float *progress) { @@ -1090,7 +1090,7 @@ static void index_rebuild_fallback_finish(FallbackIndexBuilderContext *context, } static void index_rebuild_fallback(FallbackIndexBuilderContext *context, - short *stop, + const short *stop, short *do_update, float *progress) { diff --git a/source/blender/imbuf/intern/iris.c b/source/blender/imbuf/intern/iris.c index bfcd1ec2cee..2516df22151 100644 --- a/source/blender/imbuf/intern/iris.c +++ b/source/blender/imbuf/intern/iris.c @@ -122,7 +122,7 @@ static int expandrow2( static void interleaverow(uchar *lptr, const uchar *cptr, int z, int n); static void interleaverow2(float *lptr, const uchar *cptr, int z, int n); static int compressrow(uchar *lbuf, uchar *rlebuf, int z, int cnt); -static void lumrow(uchar *rgbptr, uchar *lumptr, int n); +static void lumrow(const uchar *rgbptr, uchar *lumptr, int n); /* * byte order independent read/write of shorts and ints. @@ -900,7 +900,7 @@ static int output_iris(uint *lptr, int xsize, int ysize, int zsize, const char * /* static utility functions for output_iris */ -static void lumrow(uchar *rgbptr, uchar *lumptr, int n) +static void lumrow(const uchar *rgbptr, uchar *lumptr, int n) { lumptr += CHANOFFSET(0); while (n--) { diff --git a/source/blender/imbuf/intern/radiance_hdr.c b/source/blender/imbuf/intern/radiance_hdr.c index 46d07e74ce3..6ed01c73f04 100644 --- a/source/blender/imbuf/intern/radiance_hdr.c +++ b/source/blender/imbuf/intern/radiance_hdr.c @@ -176,7 +176,7 @@ static void RGBE2FLOAT(RGBE rgbe, fCOLOR fcol) } /* float color -> rgbe */ -static void FLOAT2RGBE(fCOLOR fcol, RGBE rgbe) +static void FLOAT2RGBE(const fCOLOR fcol, RGBE rgbe) { int e; float d = (fcol[RED] > fcol[GRN]) ? fcol[RED] : fcol[GRN]; @@ -308,7 +308,8 @@ struct ImBuf *imb_loadhdr(const unsigned char *mem, } /* ImBuf write */ -static int fwritecolrs(FILE *file, int width, int channels, unsigned char *ibufscan, float *fpscan) +static int fwritecolrs( + FILE *file, int width, int channels, const unsigned char *ibufscan, const float *fpscan) { int beg, c2, cnt = 0; fCOLOR fcol; diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c index 309de25db03..715f2aaf621 100644 --- a/source/blender/imbuf/intern/tiff.c +++ b/source/blender/imbuf/intern/tiff.c @@ -81,14 +81,24 @@ typedef struct ImbTIFFMemFile { * Function implementations. * *****************************/ -static void imb_tiff_DummyUnmapProc(thandle_t fd, tdata_t base, toff_t size) +static void imb_tiff_DummyUnmapProc( + thandle_t fd, + tdata_t base, + /* Cannot be const, because this function implements #TIFFUnmapFileProc. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + toff_t size) { (void)fd; (void)base; (void)size; } -static int imb_tiff_DummyMapProc(thandle_t fd, tdata_t *pbase, toff_t *psize) +static int imb_tiff_DummyMapProc( + thandle_t fd, + tdata_t *pbase, + /* Cannot be const, because this function implements #TIFFMapFileProc. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + toff_t *psize) { (void)fd; (void)pbase; @@ -100,7 +110,7 @@ static int imb_tiff_DummyMapProc(thandle_t fd, tdata_t *pbase, toff_t *psize) /** * Reads data from an in-memory TIFF file. * - * \param handle: Handle of the TIFF file (pointer to ImbTIFFMemFile). + * \param handle: Handle of the TIFF file (pointer to #ImbTIFFMemFile). * \param data: Buffer to contain data (treat as (void *)). * \param n: Number of bytes to read. * diff --git a/source/blender/io/alembic/CMakeLists.txt b/source/blender/io/alembic/CMakeLists.txt index da36272b850..681d6d04acd 100644 --- a/source/blender/io/alembic/CMakeLists.txt +++ b/source/blender/io/alembic/CMakeLists.txt @@ -63,8 +63,8 @@ set(SRC exporter/abc_writer_camera.cc exporter/abc_writer_curves.cc exporter/abc_writer_hair.cc - exporter/abc_writer_mesh.cc exporter/abc_writer_mball.cc + exporter/abc_writer_mesh.cc exporter/abc_writer_nurbs.cc exporter/abc_writer_points.cc exporter/abc_writer_transform.cc @@ -89,8 +89,8 @@ set(SRC exporter/abc_writer_camera.h exporter/abc_writer_curves.h exporter/abc_writer_hair.h - exporter/abc_writer_mesh.h exporter/abc_writer_mball.h + exporter/abc_writer_mesh.h exporter/abc_writer_nurbs.h exporter/abc_writer_points.h exporter/abc_writer_transform.h diff --git a/source/blender/io/alembic/exporter/abc_export_capi.cc b/source/blender/io/alembic/exporter/abc_export_capi.cc index fbc5b2d5c02..98c551c635c 100644 --- a/source/blender/io/alembic/exporter/abc_export_capi.cc +++ b/source/blender/io/alembic/exporter/abc_export_capi.cc @@ -73,7 +73,12 @@ static void build_depsgraph(Depsgraph *depsgraph, Main *bmain) DEG_graph_build_from_view_layer(depsgraph, bmain, scene, view_layer); } -static void export_startjob(void *customdata, short *stop, short *do_update, float *progress) +static void export_startjob(void *customdata, + /* Cannot be const, this function implements wm_jobs_start_callback. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + short *stop, + short *do_update, + float *progress) { ExportJobData *data = static_cast<ExportJobData *>(customdata); data->was_canceled = false; diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc index 90004c0e85b..c83eaf3eede 100644 --- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc +++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.cc @@ -107,20 +107,23 @@ AbstractHierarchyIterator::ExportGraph::key_type ABCHierarchyIterator:: determine_graph_index_object(const HierarchyContext *context) { if (params_.flatten_hierarchy) { - return std::make_pair(nullptr, nullptr); + return ObjectIdentifier::for_graph_root(); } return AbstractHierarchyIterator::determine_graph_index_object(context); } AbstractHierarchyIterator::ExportGraph::key_type ABCHierarchyIterator::determine_graph_index_dupli( - const HierarchyContext *context, const std::set<Object *> &dupli_set) + const HierarchyContext *context, + const DupliObject *dupli_object, + const DupliParentFinder &dupli_parent_finder) { if (params_.flatten_hierarchy) { - return std::make_pair(nullptr, nullptr); + return ObjectIdentifier::for_graph_root(); } - return AbstractHierarchyIterator::determine_graph_index_dupli(context, dupli_set); + return AbstractHierarchyIterator::determine_graph_index_dupli( + context, dupli_object, dupli_parent_finder); } Alembic::Abc::OObject ABCHierarchyIterator::get_alembic_parent( diff --git a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h index edcb31806ba..3fe2d2c43d2 100644 --- a/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h +++ b/source/blender/io/alembic/exporter/abc_hierarchy_iterator.h @@ -67,7 +67,9 @@ class ABCHierarchyIterator : public AbstractHierarchyIterator { virtual ExportGraph::key_type determine_graph_index_object( const HierarchyContext *context) override; virtual AbstractHierarchyIterator::ExportGraph::key_type determine_graph_index_dupli( - const HierarchyContext *context, const std::set<Object *> &dupli_set) override; + const HierarchyContext *context, + const DupliObject *dupli_object, + const DupliParentFinder &dupli_parent_finder) override; virtual AbstractHierarchyWriter *create_transform_writer( const HierarchyContext *context) override; diff --git a/source/blender/io/avi/intern/avi_mjpeg.c b/source/blender/io/avi/intern/avi_mjpeg.c index 70ddca28060..75059c202e5 100644 --- a/source/blender/io/avi/intern/avi_mjpeg.c +++ b/source/blender/io/avi/intern/avi_mjpeg.c @@ -20,7 +20,7 @@ /** \file * \ingroup avi * - * This is external code. Converts between avi and mpeg/jpeg. + * This is external code. Converts between AVI and MPEG/JPEG. */ #include <stdlib.h> @@ -39,7 +39,9 @@ #include "avi_mjpeg.h" static void jpegmemdestmgr_build(j_compress_ptr cinfo, unsigned char *buffer, size_t bufsize); -static void jpegmemsrcmgr_build(j_decompress_ptr dinfo, unsigned char *buffer, size_t bufsize); +static void jpegmemsrcmgr_build(j_decompress_ptr dinfo, + const unsigned char *buffer, + size_t bufsize); static size_t numbytes; @@ -381,7 +383,10 @@ static void deinterlace(int odd, unsigned char *to, unsigned char *from, int wid } } -void *avi_converter_from_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) +void *avi_converter_from_mjpeg(AviMovie *movie, + int stream, + unsigned char *buffer, + const size_t *size) { int deint; unsigned char *buf; @@ -553,7 +558,9 @@ static void jpegmemsrcmgr_term_source(j_decompress_ptr dinfo) MEM_freeN(dinfo->src); } -static void jpegmemsrcmgr_build(j_decompress_ptr dinfo, unsigned char *buffer, size_t bufsize) +static void jpegmemsrcmgr_build(j_decompress_ptr dinfo, + const unsigned char *buffer, + size_t bufsize) { dinfo->src = MEM_mallocN(sizeof(*(dinfo->src)), "avi.jpegmemsrcmgr_build"); diff --git a/source/blender/io/avi/intern/avi_mjpeg.h b/source/blender/io/avi/intern/avi_mjpeg.h index 30e46bf1d0c..13153fa41f0 100644 --- a/source/blender/io/avi/intern/avi_mjpeg.h +++ b/source/blender/io/avi/intern/avi_mjpeg.h @@ -24,7 +24,10 @@ #ifndef __AVI_MJPEG_H__ #define __AVI_MJPEG_H__ -void *avi_converter_from_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); +void *avi_converter_from_mjpeg(AviMovie *movie, + int stream, + unsigned char *buffer, + const size_t *size); void *avi_converter_to_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); #endif /* __AVI_MJPEG_H__ */ diff --git a/source/blender/io/avi/intern/avi_rgb.c b/source/blender/io/avi/intern/avi_rgb.c index 6f4f33d72d1..44542af96ae 100644 --- a/source/blender/io/avi/intern/avi_rgb.c +++ b/source/blender/io/avi/intern/avi_rgb.c @@ -37,7 +37,10 @@ /* implementation */ -void *avi_converter_from_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) +void *avi_converter_from_avi_rgb(AviMovie *movie, + int stream, + unsigned char *buffer, + const size_t *size) { unsigned char *buf; AviBitmapInfoHeader *bi; diff --git a/source/blender/io/avi/intern/avi_rgb.h b/source/blender/io/avi/intern/avi_rgb.h index 7c8ce590d27..3a37fad94e1 100644 --- a/source/blender/io/avi/intern/avi_rgb.h +++ b/source/blender/io/avi/intern/avi_rgb.h @@ -24,7 +24,10 @@ #ifndef __AVI_RGB_H__ #define __AVI_RGB_H__ -void *avi_converter_from_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); +void *avi_converter_from_avi_rgb(AviMovie *movie, + int stream, + unsigned char *buffer, + const size_t *size); void *avi_converter_to_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); #endif /* __AVI_RGB_H__ */ diff --git a/source/blender/io/collada/ArmatureImporter.cpp b/source/blender/io/collada/ArmatureImporter.cpp index 3755b71f300..bd5bd913a18 100644 --- a/source/blender/io/collada/ArmatureImporter.cpp +++ b/source/blender/io/collada/ArmatureImporter.cpp @@ -969,8 +969,8 @@ void ArmatureImporter::make_shape_keys(bContext *C) /* insert other shape keys */ for (int i = 0; i < morphTargetIds.getCount(); i++) { - /* better to have a separate map of morph objects, - * This'll do for now since only mesh morphing is imported */ + /* Better to have a separate map of morph objects, + * This will do for now since only mesh morphing is imported. */ Mesh *me = this->mesh_importer->get_mesh_by_geom_uid(morphTargetIds[i]); diff --git a/source/blender/io/collada/MeshImporter.cpp b/source/blender/io/collada/MeshImporter.cpp index 495af60488b..9fbb7324f8f 100644 --- a/source/blender/io/collada/MeshImporter.cpp +++ b/source/blender/io/collada/MeshImporter.cpp @@ -205,7 +205,7 @@ MeshImporter::MeshImporter( } bool MeshImporter::set_poly_indices( - MPoly *mpoly, MLoop *mloop, int loop_index, unsigned int *indices, int loop_count) + MPoly *mpoly, MLoop *mloop, int loop_index, const unsigned int *indices, int loop_count) { mpoly->loopstart = loop_index; mpoly->totloop = loop_count; diff --git a/source/blender/io/collada/MeshImporter.h b/source/blender/io/collada/MeshImporter.h index 2f2a18ff11a..18e56e8f9df 100644 --- a/source/blender/io/collada/MeshImporter.h +++ b/source/blender/io/collada/MeshImporter.h @@ -105,7 +105,7 @@ class MeshImporter : public MeshImporterBase { std::multimap<COLLADAFW::UniqueId, COLLADAFW::UniqueId> materials_mapped_to_geom; bool set_poly_indices( - MPoly *mpoly, MLoop *mloop, int loop_index, unsigned int *indices, int loop_count); + MPoly *mpoly, MLoop *mloop, int loop_index, const unsigned int *indices, int loop_count); void set_face_uv(MLoopUV *mloopuv, UVDataWrapper &uvs, diff --git a/source/blender/io/collada/TransformWriter.cpp b/source/blender/io/collada/TransformWriter.cpp index 0a66db72cb9..b7455837379 100644 --- a/source/blender/io/collada/TransformWriter.cpp +++ b/source/blender/io/collada/TransformWriter.cpp @@ -129,9 +129,9 @@ void TransformWriter::add_node_transform_identity(COLLADASW::Node &node, } void TransformWriter::add_transform(COLLADASW::Node &node, - float loc[3], - float rot[3], - float scale[3]) + const float loc[3], + const float rot[3], + const float scale[3]) { node.addScale("scale", scale[0], scale[1], scale[2]); node.addRotateZ("rotationZ", RAD2DEGF(rot[2])); diff --git a/source/blender/io/collada/TransformWriter.h b/source/blender/io/collada/TransformWriter.h index 3c71fc9d36e..db8ef3f5ee2 100644 --- a/source/blender/io/collada/TransformWriter.h +++ b/source/blender/io/collada/TransformWriter.h @@ -42,7 +42,10 @@ class TransformWriter { void add_node_transform_identity(COLLADASW::Node &node, BCExportSettings &export_settings); private: - void add_transform(COLLADASW::Node &node, float loc[3], float rot[3], float scale[3]); + void add_transform(COLLADASW::Node &node, + const float loc[3], + const float rot[3], + const float scale[3]); }; #endif diff --git a/source/blender/io/collada/collada_utils.cpp b/source/blender/io/collada/collada_utils.cpp index e74808df466..2c54a49198a 100644 --- a/source/blender/io/collada/collada_utils.cpp +++ b/source/blender/io/collada/collada_utils.cpp @@ -603,7 +603,7 @@ float BoneExtended::get_roll() return this->roll; } -void BoneExtended::set_tail(float vec[]) +void BoneExtended::set_tail(const float vec[]) { this->tail[0] = vec[0]; this->tail[1] = vec[1]; diff --git a/source/blender/io/collada/collada_utils.h b/source/blender/io/collada/collada_utils.h index b1ec2c8b81a..11a9376294b 100644 --- a/source/blender/io/collada/collada_utils.h +++ b/source/blender/io/collada/collada_utils.h @@ -343,7 +343,7 @@ class BoneExtended { bool has_roll(); float get_roll(); - void set_tail(float vec[]); + void set_tail(const float vec[]); float *get_tail(); bool has_tail(); diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt index 4ed6f12762e..708f24ca0e2 100644 --- a/source/blender/io/common/CMakeLists.txt +++ b/source/blender/io/common/CMakeLists.txt @@ -31,8 +31,13 @@ set(INC_SYS set(SRC intern/abstract_hierarchy_iterator.cc + intern/dupli_parent_finder.cc + intern/dupli_persistent_id.cc + intern/object_identifier.cc IO_abstract_hierarchy_iterator.h + IO_dupli_persistent_id.hh + intern/dupli_parent_finder.hh ) set(LIB diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h index 5f84fd48b71..2669f137fd4 100644 --- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h +++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h @@ -36,6 +36,8 @@ #ifndef __ABSTRACT_HIERARCHY_ITERATOR_H__ #define __ABSTRACT_HIERARCHY_ITERATOR_H__ +#include "IO_dupli_persistent_id.hh" + #include <map> #include <set> #include <string> @@ -52,6 +54,7 @@ namespace blender { namespace io { class AbstractHierarchyWriter; +class DupliParentFinder; /* HierarchyContext structs are created by the AbstractHierarchyIterator. Each HierarchyContext * struct contains everything necessary to export a single object to a file. */ @@ -60,6 +63,7 @@ struct HierarchyContext { Object *object; /* Evaluated object. */ Object *export_parent; Object *duplicator; + PersistentID persistent_id; float matrix_world[4][4]; std::string export_name; @@ -161,6 +165,35 @@ class EnsuredWriter { AbstractHierarchyWriter *operator->(); }; +/* Unique identifier for a (potentially duplicated) object. + * + * Instances of this class serve as key in the export graph of the + * AbstractHierarchyIterator. */ +class ObjectIdentifier { + public: + Object *object; + Object *duplicated_by; /* nullptr for real objects. */ + PersistentID persistent_id; + + protected: + ObjectIdentifier(Object *object, Object *duplicated_by, const PersistentID &persistent_id); + + public: + ObjectIdentifier(const ObjectIdentifier &other); + ~ObjectIdentifier(); + + static ObjectIdentifier for_graph_root(); + static ObjectIdentifier for_real_object(Object *object); + static ObjectIdentifier for_hierarchy_context(const HierarchyContext *context); + static ObjectIdentifier for_duplicated_object(const DupliObject *dupli_object, + Object *duplicated_by); + + bool is_root() const; +}; + +bool operator<(const ObjectIdentifier &obj_ident_a, const ObjectIdentifier &obj_ident_b); +bool operator==(const ObjectIdentifier &obj_ident_a, const ObjectIdentifier &obj_ident_b); + /* AbstractHierarchyIterator iterates over objects in a dependency graph, and constructs export * writers. These writers are then called to perform the actual writing to a USD or Alembic file. * @@ -172,14 +205,10 @@ class AbstractHierarchyIterator { public: /* Mapping from export path to writer. */ typedef std::map<std::string, AbstractHierarchyWriter *> WriterMap; - /* Pair of a (potentially duplicated) object and its duplicator (or nullptr). - * This is typically used to store a pair of HierarchyContext::object and - * HierarchyContext::duplicator. */ - typedef std::pair<Object *, Object *> DupliAndDuplicator; /* All the children of some object, as per the export hierarchy. */ typedef std::set<HierarchyContext *> ExportChildren; /* Mapping from an object and its duplicator to the object's export-children. */ - typedef std::map<DupliAndDuplicator, ExportChildren> ExportGraph; + typedef std::map<ObjectIdentifier, ExportChildren> ExportGraph; /* Mapping from ID to its export path. This is used for instancing; given an * instanced datablock, the export path of the original can be looked up. */ typedef std::map<ID *, std::string> ExportPathMap; @@ -237,7 +266,7 @@ class AbstractHierarchyIterator { void visit_object(Object *object, Object *export_parent, bool weak_export); void visit_dupli_object(DupliObject *dupli_object, Object *duplicator, - const std::set<Object *> &dupli_set); + const DupliParentFinder &dupli_parent_finder); void context_update_for_graph_index(HierarchyContext *context, const ExportGraph::key_type &graph_index) const; @@ -291,8 +320,10 @@ class AbstractHierarchyIterator { virtual bool should_visit_dupli_object(const DupliObject *dupli_object) const; virtual ExportGraph::key_type determine_graph_index_object(const HierarchyContext *context); - virtual ExportGraph::key_type determine_graph_index_dupli(const HierarchyContext *context, - const std::set<Object *> &dupli_set); + virtual ExportGraph::key_type determine_graph_index_dupli( + const HierarchyContext *context, + const DupliObject *dupli_object, + const DupliParentFinder &dupli_parent_finder); /* These functions should create an AbstractHierarchyWriter subclass instance, or return * nullptr if the object or its data should not be exported. Returning a nullptr for diff --git a/source/blender/io/common/IO_dupli_persistent_id.hh b/source/blender/io/common/IO_dupli_persistent_id.hh new file mode 100644 index 00000000000..5dc54164684 --- /dev/null +++ b/source/blender/io/common/IO_dupli_persistent_id.hh @@ -0,0 +1,68 @@ +/* + * 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) 2020 Blender Foundation. + * All rights reserved. + */ +#ifndef __IO_COMMON_DUPLI_PERSISTENT_ID_H__ +#define __IO_COMMON_DUPLI_PERSISTENT_ID_H__ + +#include "BKE_duplilist.h" + +#include "DNA_object_types.h" /* For MAX_DUPLI_RECUR */ + +#include <array> +#include <optional> +#include <ostream> + +namespace blender::io { + +/* Wrapper for DupliObject::persistent_id that can act as a map key. */ +class PersistentID { + protected: + constexpr static int array_length_ = MAX_DUPLI_RECUR; + typedef std::array<int, array_length_> PIDArray; + PIDArray persistent_id_; + + explicit PersistentID(const PIDArray &persistent_id_values); + + public: + PersistentID(); + explicit PersistentID(const DupliObject *dupli_ob); + + /* Return true iff the persistent IDs are the same, ignoring the first digit. */ + bool is_from_same_instancer_as(const PersistentID &other) const; + + /* Construct the persistent ID of this instance's instancer. */ + PersistentID instancer_pid() const; + + /* Construct a string representation by reversing the persistent ID. + * In case of a duplicator that is duplicated itself as well, this + * results in strings like: + * "3" for the duplicated duplicator, and + * "3-0", "3-1", etc. for its duplis. */ + std::string as_object_name_suffix() const; + + friend bool operator==(const PersistentID &persistent_id_a, const PersistentID &persistent_id_b); + friend bool operator<(const PersistentID &persistent_id_a, const PersistentID &persistent_id_b); + friend std::ostream &operator<<(std::ostream &os, const PersistentID &persistent_id); + + private: + void copy_values_from(const PIDArray &persistent_id_values); +}; + +} // namespace blender::io + +#endif // __IO_COMMON_DUPLI_PARENT_FINDER_H__ diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc index dce6b8e178b..3622c1eb7cd 100644 --- a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc +++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc @@ -17,6 +17,7 @@ * All rights reserved. */ #include "IO_abstract_hierarchy_iterator.h" +#include "dupli_parent_finder.hh" #include <iostream> #include <limits.h> @@ -200,9 +201,9 @@ void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &grap { size_t total_graph_size = 0; for (const ExportGraph::value_type &map_iter : graph) { - const DupliAndDuplicator &parent_info = map_iter.first; - Object *const export_parent = parent_info.first; - Object *const duplicator = parent_info.second; + const ObjectIdentifier &parent_info = map_iter.first; + const Object *const export_parent = parent_info.object; + const Object *const duplicator = parent_info.duplicated_by; if (duplicator != nullptr) { printf(" DU %s (as dupped by %s):\n", @@ -217,7 +218,7 @@ void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &grap for (HierarchyContext *child_ctx : map_iter.second) { if (child_ctx->duplicator == nullptr) { printf(" - %s%s%s\n", - child_ctx->object->id.name + 2, + child_ctx->export_name.c_str(), child_ctx->weak_export ? " (weak)" : "", child_ctx->original_export_path.empty() ? "" : @@ -225,7 +226,7 @@ void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &grap } else { printf(" - %s (dup by %s%s) %s\n", - child_ctx->object->id.name + 2, + child_ctx->export_name.c_str(), child_ctx->duplicator->id.name + 2, child_ctx->weak_export ? ", weak" : "", child_ctx->original_export_path.empty() ? @@ -234,7 +235,7 @@ void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &grap } } } - printf(" (Total graph size: %zu objects\n", total_graph_size); + printf(" (Total graph size: %zu objects)\n", total_graph_size); } void AbstractHierarchyIterator::export_graph_construct() @@ -257,22 +258,21 @@ void AbstractHierarchyIterator::export_graph_construct() // Export the duplicated objects instanced by this object. ListBase *lb = object_duplilist(depsgraph_, scene, object); if (lb) { - // Construct the set of duplicated objects, so that later we can determine whether a parent - // is also duplicated itself. - std::set<Object *> dupli_set; + DupliParentFinder dupli_parent_finder; + LISTBASE_FOREACH (DupliObject *, dupli_object, lb) { + PersistentID persistent_id(dupli_object); if (!should_visit_dupli_object(dupli_object)) { continue; } - dupli_set.insert(dupli_object->ob); + dupli_parent_finder.insert(dupli_object); } LISTBASE_FOREACH (DupliObject *, dupli_object, lb) { if (!should_visit_dupli_object(dupli_object)) { continue; } - - visit_dupli_object(dupli_object, object, dupli_set); + visit_dupli_object(dupli_object, object, dupli_parent_finder); } } @@ -291,29 +291,30 @@ void AbstractHierarchyIterator::connect_loose_objects() for (const ExportGraph::value_type &map_iter : export_graph_) { for (const HierarchyContext *child : map_iter.second) { // An object that is marked as a child of another object is not considered 'loose'. - loose_objects_graph.erase(std::make_pair(child->object, child->duplicator)); + ObjectIdentifier child_oid = ObjectIdentifier::for_hierarchy_context(child); + loose_objects_graph.erase(child_oid); } } // The root of the hierarchy is always found, so it's never considered 'loose'. - loose_objects_graph.erase(std::make_pair(nullptr, nullptr)); + loose_objects_graph.erase(ObjectIdentifier::for_graph_root()); // Iterate over the loose objects and connect them to their export parent. for (const ExportGraph::value_type &map_iter : loose_objects_graph) { - const DupliAndDuplicator &export_info = map_iter.first; - Object *object = export_info.first; + const ObjectIdentifier &graph_key = map_iter.first; + Object *object = graph_key.object; while (true) { // Loose objects will all be real objects, as duplicated objects always have // their duplicator or other exported duplicated object as ancestor. ExportGraph::iterator found_parent_iter = export_graph_.find( - std::make_pair(object->parent, nullptr)); + ObjectIdentifier::for_real_object(object->parent)); visit_object(object, object->parent, true); if (found_parent_iter != export_graph_.end()) { break; } // 'object->parent' will never be nullptr here, as the export graph contains the - // tuple <nullptr, nullptr> as root and thus will cause a break. + // root as nullptr and thus will cause a break above. BLI_assert(object->parent != nullptr); object = object->parent; @@ -326,10 +327,8 @@ static bool remove_weak_subtrees(const HierarchyContext *context, const AbstractHierarchyIterator::ExportGraph &input_graph) { bool all_is_weak = context != nullptr && context->weak_export; - Object *object = context != nullptr ? context->object : nullptr; - Object *duplicator = context != nullptr ? context->duplicator : nullptr; + const ObjectIdentifier map_key = ObjectIdentifier::for_hierarchy_context(context); - const AbstractHierarchyIterator::DupliAndDuplicator map_key = std::make_pair(object, duplicator); AbstractHierarchyIterator::ExportGraph::const_iterator child_iterator; child_iterator = input_graph.find(map_key); @@ -399,7 +398,7 @@ void AbstractHierarchyIterator::visit_object(Object *object, // check on whether an object is part of the export, rather than having to check all objects in // the map. Note that it's not possible to simply search for (object->parent, nullptr), as the // object's parent in Blender may not be the same as its export-parent. - ExportGraph::key_type object_key = std::make_pair(object, nullptr); + ExportGraph::key_type object_key = ObjectIdentifier::for_real_object(object); if (export_graph_.find(object_key) == export_graph_.end()) { export_graph_[object_key] = ExportChildren(); } @@ -408,16 +407,17 @@ void AbstractHierarchyIterator::visit_object(Object *object, AbstractHierarchyIterator::ExportGraph::key_type AbstractHierarchyIterator:: determine_graph_index_object(const HierarchyContext *context) { - return std::make_pair(context->export_parent, nullptr); + return ObjectIdentifier::for_real_object(context->export_parent); } void AbstractHierarchyIterator::visit_dupli_object(DupliObject *dupli_object, Object *duplicator, - const std::set<Object *> &dupli_set) + const DupliParentFinder &dupli_parent_finder) { HierarchyContext *context = new HierarchyContext(); context->object = dupli_object->ob; context->duplicator = duplicator; + context->persistent_id = PersistentID(dupli_object); context->weak_export = false; context->export_path = ""; context->original_export_path = ""; @@ -427,38 +427,36 @@ void AbstractHierarchyIterator::visit_dupli_object(DupliObject *dupli_object, copy_m4_m4(context->matrix_world, dupli_object->mat); // Construct export name for the dupli-instance. - std::stringstream suffix_stream; - suffix_stream << std::hex; - for (int i = 0; i < MAX_DUPLI_RECUR && dupli_object->persistent_id[i] != INT_MAX; i++) { - suffix_stream << "-" << dupli_object->persistent_id[i]; - } - context->export_name = make_valid_name(get_object_name(context->object) + suffix_stream.str()); + std::stringstream export_name_stream; + export_name_stream << get_object_name(context->object) << "-" + << context->persistent_id.as_object_name_suffix(); + context->export_name = make_valid_name(export_name_stream.str()); - ExportGraph::key_type graph_index = determine_graph_index_dupli(context, dupli_set); + ExportGraph::key_type graph_index = determine_graph_index_dupli( + context, dupli_object, dupli_parent_finder); context_update_for_graph_index(context, graph_index); + export_graph_[graph_index].insert(context); } AbstractHierarchyIterator::ExportGraph::key_type AbstractHierarchyIterator:: determine_graph_index_dupli(const HierarchyContext *context, - const std::set<Object *> &dupli_set) + const DupliObject *dupli_object, + const DupliParentFinder &dupli_parent_finder) { - /* If the dupli-object's parent is also instanced by this object, use that as the - * export parent. Otherwise use the dupli-parent as export parent. */ + const DupliObject *dupli_parent = dupli_parent_finder.find_suitable_export_parent(dupli_object); - Object *parent = context->object->parent; - if (parent != nullptr && dupli_set.find(parent) != dupli_set.end()) { - // The parent object is part of the duplicated collection. - return std::make_pair(parent, context->duplicator); + if (dupli_parent != nullptr) { + return ObjectIdentifier::for_duplicated_object(dupli_parent, context->duplicator); } - return std::make_pair(context->duplicator, nullptr); + return ObjectIdentifier::for_real_object(context->duplicator); } void AbstractHierarchyIterator::context_update_for_graph_index( HierarchyContext *context, const ExportGraph::key_type &graph_index) const { // Update the HierarchyContext so that it is consistent with the graph index. - context->export_parent = graph_index.first; + context->export_parent = graph_index.object; if (context->export_parent != context->object->parent) { /* The parent object in Blender is NOT used as the export parent. This means * that the world transform of this object can be influenced by objects that @@ -470,11 +468,7 @@ void AbstractHierarchyIterator::context_update_for_graph_index( AbstractHierarchyIterator::ExportChildren &AbstractHierarchyIterator::graph_children( const HierarchyContext *context) { - if (context == nullptr) { - return export_graph_[std::make_pair(nullptr, nullptr)]; - } - - return export_graph_[std::make_pair(context->object, context->duplicator)]; + return export_graph_[ObjectIdentifier::for_hierarchy_context(context)]; } void AbstractHierarchyIterator::determine_export_paths(const HierarchyContext *parent_context) diff --git a/source/blender/io/common/intern/dupli_parent_finder.cc b/source/blender/io/common/intern/dupli_parent_finder.cc new file mode 100644 index 00000000000..73e33eff164 --- /dev/null +++ b/source/blender/io/common/intern/dupli_parent_finder.cc @@ -0,0 +1,104 @@ +/* + * 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) 2020 Blender Foundation. + * All rights reserved. + */ + +#include "dupli_parent_finder.hh" + +#include "BLI_utildefines.h" + +#include <iostream> + +namespace blender::io { + +DupliParentFinder::DupliParentFinder() +{ +} + +DupliParentFinder::~DupliParentFinder() +{ +} + +void DupliParentFinder::insert(const DupliObject *dupli_ob) +{ + dupli_set_.insert(dupli_ob->ob); + + PersistentID dupli_pid(dupli_ob); + pid_to_dupli_[dupli_pid] = dupli_ob; + instancer_pid_to_duplis_[dupli_pid.instancer_pid()].insert(dupli_ob); +} + +bool DupliParentFinder::is_duplicated(const Object *object) const +{ + return dupli_set_.find(object) != dupli_set_.end(); +} + +const DupliObject *DupliParentFinder::find_suitable_export_parent( + const DupliObject *dupli_ob) const +{ + if (dupli_ob->ob->parent != nullptr) { + const DupliObject *parent = find_duplicated_parent(dupli_ob); + if (parent != nullptr) { + return parent; + } + } + + return find_instancer(dupli_ob); +} + +const DupliObject *DupliParentFinder::find_duplicated_parent(const DupliObject *dupli_ob) const +{ + const PersistentID dupli_pid(dupli_ob); + PersistentID parent_pid = dupli_pid.instancer_pid(); + + const Object *parent_ob = dupli_ob->ob->parent; + BLI_assert(parent_ob != nullptr); + + InstancerPIDToDuplisMap::const_iterator found = instancer_pid_to_duplis_.find(parent_pid); + if (found == instancer_pid_to_duplis_.end()) { + /* Unexpected, as there should be at least one entry here, for the dupli_ob itself. */ + return nullptr; + } + + for (const DupliObject *potential_parent_dupli : found->second) { + if (potential_parent_dupli->ob != parent_ob) { + continue; + } + + PersistentID potential_parent_pid(potential_parent_dupli); + if (potential_parent_pid.is_from_same_instancer_as(dupli_pid)) { + return potential_parent_dupli; + } + } + return nullptr; +} + +const DupliObject *DupliParentFinder::find_instancer(const DupliObject *dupli_ob) const +{ + PersistentID dupli_pid(dupli_ob); + PersistentID parent_pid = dupli_pid.instancer_pid(); + + PIDToDupliMap::const_iterator found = pid_to_dupli_.find(parent_pid); + if (found == pid_to_dupli_.end()) { + return nullptr; + } + + const DupliObject *instancer_dupli = found->second; + return instancer_dupli; +} + +} // namespace blender::io diff --git a/source/blender/io/common/intern/dupli_parent_finder.hh b/source/blender/io/common/intern/dupli_parent_finder.hh new file mode 100644 index 00000000000..e7e628665ee --- /dev/null +++ b/source/blender/io/common/intern/dupli_parent_finder.hh @@ -0,0 +1,62 @@ +/* + * 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) 2020 Blender Foundation. + * All rights reserved. + */ +#ifndef __IO_COMMON_DUPLI_PARENT_FINDER_H__ +#define __IO_COMMON_DUPLI_PARENT_FINDER_H__ + +#include "IO_dupli_persistent_id.hh" + +#include "BKE_duplilist.h" + +#include <map> +#include <set> + +namespace blender::io { + +/* Find relations between duplicated objects. This class should be instanced for a single real + * object, and fed its dupli-objects. */ +class DupliParentFinder final { + private: + /* To check whether an Object * is instanced by this duplicator. */ + std::set<const Object *> dupli_set_; + + /* To find the DupliObject given its Persistent ID. */ + typedef std::map<const PersistentID, const DupliObject *> PIDToDupliMap; + PIDToDupliMap pid_to_dupli_; + + /* Mapping from instancer PID to duplis instanced by it. */ + typedef std::map<const PersistentID, std::set<const DupliObject *>> InstancerPIDToDuplisMap; + InstancerPIDToDuplisMap instancer_pid_to_duplis_; + + public: + DupliParentFinder(); + ~DupliParentFinder(); + + void insert(const DupliObject *dupli_ob); + + bool is_duplicated(const Object *object) const; + const DupliObject *find_suitable_export_parent(const DupliObject *dupli_ob) const; + + private: + const DupliObject *find_duplicated_parent(const DupliObject *dupli_ob) const; + const DupliObject *find_instancer(const DupliObject *dupli_ob) const; +}; + +} // namespace blender::io + +#endif
\ No newline at end of file diff --git a/source/blender/io/common/intern/dupli_persistent_id.cc b/source/blender/io/common/intern/dupli_persistent_id.cc new file mode 100644 index 00000000000..b40cd83ef44 --- /dev/null +++ b/source/blender/io/common/intern/dupli_persistent_id.cc @@ -0,0 +1,167 @@ +/* + * 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) 2020 Blender Foundation. + * All rights reserved. + */ + +#include "dupli_parent_finder.hh" + +#include <climits> +#include <cstring> +#include <ostream> +#include <sstream> + +namespace blender::io { + +PersistentID::PersistentID() +{ + persistent_id_[0] = INT_MAX; +} + +PersistentID::PersistentID(const DupliObject *dupli_ob) +{ + for (int index = 0; index < array_length_; ++index) { + persistent_id_[index] = dupli_ob->persistent_id[index]; + } +} + +PersistentID::PersistentID(const PIDArray &persistent_id_values) +{ + persistent_id_ = persistent_id_values; +} + +bool PersistentID::is_from_same_instancer_as(const PersistentID &other) const +{ + if (persistent_id_[0] == INT_MAX || other.persistent_id_[0] == INT_MAX) { + /* Either one or the other is not instanced at all, so definitely not from the same instancer. + */ + return false; + } + + /* Start at index 1 to skip the first digit. */ + for (int index = 1; index < array_length_; ++index) { + const int pid_digit_a = persistent_id_[index]; + const int pid_digit_b = other.persistent_id_[index]; + + if (pid_digit_a != pid_digit_b) { + return false; + } + + if (pid_digit_a == INT_MAX) { + /* Both persistent IDs were identical so far, and this marks the end of the useful data. */ + break; + } + } + return true; +} + +PersistentID PersistentID::instancer_pid() const +{ + if (persistent_id_[0] == INT_MAX) { + return PersistentID(); + } + + /* Left-shift the entire PID by 1. */ + PIDArray new_pid_values; + int index; + for (index = 0; index < array_length_ - 1; ++index) { + new_pid_values[index] = persistent_id_[index + 1]; + } + new_pid_values[index] = INT_MAX; + + return PersistentID(new_pid_values); +} + +std::string PersistentID::as_object_name_suffix() const +{ + std::stringstream stream; + + /* Find one past the last index. */ + int index; + for (index = 0; index < array_length_ && persistent_id_[index] < INT_MAX; ++index) { + ; + } + + /* Iterate backward to construct the string. */ + --index; + for (; index >= 0; --index) { + stream << persistent_id_[index]; + if (index > 0) { + stream << "-"; + } + } + + return stream.str(); +} + +bool operator<(const PersistentID &persistent_id_a, const PersistentID &persistent_id_b) +{ + const PersistentID::PIDArray &pid_a = persistent_id_a.persistent_id_; + const PersistentID::PIDArray &pid_b = persistent_id_b.persistent_id_; + + for (int index = 0; index < PersistentID::array_length_; ++index) { + const int pid_digit_a = pid_a[index]; + const int pid_digit_b = pid_b[index]; + + if (pid_digit_a != pid_digit_b) { + return pid_digit_a < pid_digit_b; + } + + if (pid_a[index] == INT_MAX) { + break; + } + } + /* Both Persistent IDs were equal, so not less-than. */ + return false; +} + +bool operator==(const PersistentID &persistent_id_a, const PersistentID &persistent_id_b) +{ + const PersistentID::PIDArray &pid_a = persistent_id_a.persistent_id_; + const PersistentID::PIDArray &pid_b = persistent_id_b.persistent_id_; + + for (int index = 0; index < PersistentID::array_length_; ++index) { + const int pid_digit_a = pid_a[index]; + const int pid_digit_b = pid_b[index]; + + if (pid_digit_a != pid_digit_b) { + return false; + } + + if (pid_a[index] == INT_MAX) { + break; + } + } + return true; +} + +std::ostream &operator<<(std::ostream &os, const PersistentID &persistent_id) +{ + if (persistent_id.persistent_id_[0] == INT_MAX) { + return os; + } + + const PersistentID::PIDArray &pid_array = persistent_id.persistent_id_; + for (int index = 0; index < PersistentID::array_length_ && pid_array[index] < INT_MAX; ++index) { + if (index > 0) { + os << "-"; + } + os << pid_array[index]; + } + return os; +} + +} // namespace blender::io diff --git a/source/blender/io/common/intern/object_identifier.cc b/source/blender/io/common/intern/object_identifier.cc new file mode 100644 index 00000000000..696bc5d2c34 --- /dev/null +++ b/source/blender/io/common/intern/object_identifier.cc @@ -0,0 +1,116 @@ +/* + * 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) 2019 Blender Foundation. + * All rights reserved. + */ +#include "IO_abstract_hierarchy_iterator.h" + +#include "BKE_duplilist.h" + +extern "C" { +#include <limits.h> /* For INT_MAX. */ +} +#include <cstring> +#include <sstream> + +namespace blender { +namespace io { + +ObjectIdentifier::ObjectIdentifier(Object *object, + Object *duplicated_by, + const PersistentID &persistent_id) + : object(object), duplicated_by(duplicated_by), persistent_id(persistent_id) +{ +} + +ObjectIdentifier::ObjectIdentifier(const ObjectIdentifier &other) + : object(other.object), duplicated_by(other.duplicated_by), persistent_id(other.persistent_id) +{ +} + +ObjectIdentifier::~ObjectIdentifier() +{ +} + +ObjectIdentifier ObjectIdentifier::for_real_object(Object *object) +{ + return ObjectIdentifier(object, nullptr, PersistentID()); +} + +ObjectIdentifier ObjectIdentifier::for_hierarchy_context(const HierarchyContext *context) +{ + if (context == nullptr) { + return for_graph_root(); + } + if (context->duplicator != nullptr) { + return ObjectIdentifier(context->object, context->duplicator, context->persistent_id); + } + return for_real_object(context->object); +} + +ObjectIdentifier ObjectIdentifier::for_duplicated_object(const DupliObject *dupli_object, + Object *duplicated_by) +{ + return ObjectIdentifier(dupli_object->ob, duplicated_by, PersistentID(dupli_object)); +} + +ObjectIdentifier ObjectIdentifier::for_graph_root() +{ + return ObjectIdentifier(nullptr, nullptr, PersistentID()); +} + +bool ObjectIdentifier::is_root() const +{ + return object == nullptr; +} + +bool operator<(const ObjectIdentifier &obj_ident_a, const ObjectIdentifier &obj_ident_b) +{ + if (obj_ident_a.object != obj_ident_b.object) { + return obj_ident_a.object < obj_ident_b.object; + } + + if (obj_ident_a.duplicated_by != obj_ident_b.duplicated_by) { + return obj_ident_a.duplicated_by < obj_ident_b.duplicated_by; + } + + if (obj_ident_a.duplicated_by == nullptr) { + /* Both are real objects, no need to check the persistent ID. */ + return false; + } + + /* Same object, both are duplicated, use the persistent IDs to determine order. */ + return obj_ident_a.persistent_id < obj_ident_b.persistent_id; +} + +bool operator==(const ObjectIdentifier &obj_ident_a, const ObjectIdentifier &obj_ident_b) +{ + if (obj_ident_a.object != obj_ident_b.object) { + return false; + } + if (obj_ident_a.duplicated_by != obj_ident_b.duplicated_by) { + return false; + } + if (obj_ident_a.duplicated_by == nullptr) { + return true; + } + + /* Same object, both are duplicated, use the persistent IDs to determine equality. */ + return obj_ident_a.persistent_id == obj_ident_b.persistent_id; +} + +} // namespace io +} // namespace blender diff --git a/source/blender/io/usd/intern/usd_capi.cc b/source/blender/io/usd/intern/usd_capi.cc index 2d3972410a4..98aef62f38e 100644 --- a/source/blender/io/usd/intern/usd_capi.cc +++ b/source/blender/io/usd/intern/usd_capi.cc @@ -59,7 +59,12 @@ struct ExportJobData { bool export_ok; }; -static void export_startjob(void *customdata, short *stop, short *do_update, float *progress) +static void export_startjob(void *customdata, + /* Cannot be const, this function implements wm_jobs_start_callback. + * NOLINTNEXTLINE: readability-non-const-parameter. */ + short *stop, + short *do_update, + float *progress) { ExportJobData *data = static_cast<ExportJobData *>(customdata); data->export_ok = false; diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 4056faf359f..0ad249ef2cd 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -343,6 +343,12 @@ typedef enum eBrushPoseOriginType { BRUSH_POSE_ORIGIN_FACE_SETS_FK = 2, } eBrushPoseOriginType; +typedef enum eBrushSmearDeformType { + BRUSH_SMEAR_DEFORM_DRAG = 0, + BRUSH_SMEAR_DEFORM_PINCH = 1, + BRUSH_SMEAR_DEFORM_EXPAND = 2, +} eBrushSmearDeformType; + /* Gpencilsettings.Vertex_mode */ typedef enum eGp_Vertex_Mode { /* Affect to Stroke only. */ @@ -500,7 +506,7 @@ typedef struct Brush { char gpencil_sculpt_tool; /** Active grease pencil weight tool. */ char gpencil_weight_tool; - char _pad1[2]; + char _pad1[6]; float autosmooth_factor; @@ -555,6 +561,9 @@ typedef struct Brush { /* multiplane scrape */ float multiplane_scrape_angle; + /* smear */ + int smear_deform_type; + /* overlay */ int texture_overlay_alpha; int mask_overlay_alpha; diff --git a/source/blender/makesdna/DNA_fluid_types.h b/source/blender/makesdna/DNA_fluid_types.h index b54ff7ccc17..909170523a3 100644 --- a/source/blender/makesdna/DNA_fluid_types.h +++ b/source/blender/makesdna/DNA_fluid_types.h @@ -471,9 +471,10 @@ typedef struct FluidDomainSettings { int res_max[3]; /* Cell max. */ int res[3]; /* Data resolution (res_max-res_min). */ int total_cells; - float dx; /* 1.0f / res. */ - float scale; /* Largest domain size. */ - int boundary_width; /* Usually this is just 1. */ + float dx; /* 1.0f / res. */ + float scale; /* Largest domain size. */ + int boundary_width; /* Usually this is just 1. */ + float gravity_final[3]; /* Scene or domain gravity multiplied with gravity weight. */ /* -- User-accesible fields (from here on). -- */ @@ -481,7 +482,6 @@ typedef struct FluidDomainSettings { int adapt_margin; int adapt_res; float adapt_threshold; - char _pad1[4]; /* Unused. */ /* Fluid domain options */ int maxres; /* Longest axis on the BB gets this resolution assigned. */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 60ad0eae576..50b4739e09f 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -2146,7 +2146,7 @@ typedef struct SimulationModifierData { ModifierData modifier; struct Simulation *simulation; - char data_path[64]; + char *data_path; } SimulationModifierData; #ifdef __cplusplus diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 993aad92564..42ccbc657d8 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -845,14 +845,15 @@ typedef struct NodeTexSky { float turbidity; float ground_albedo; float sun_size; + float sun_intensity; float sun_elevation; float sun_rotation; - int altitude; + float altitude; float air_density; float dust_density; float ozone_density; char sun_disc; - char _pad[3]; + char _pad[7]; } NodeTexSky; typedef struct NodeTexImage { diff --git a/source/blender/makesdna/DNA_simulation_types.h b/source/blender/makesdna/DNA_simulation_types.h index 93ba9c425f0..a2b81b731d3 100644 --- a/source/blender/makesdna/DNA_simulation_types.h +++ b/source/blender/makesdna/DNA_simulation_types.h @@ -31,7 +31,7 @@ typedef struct Simulation { struct bNodeTree *nodetree; int flag; - int _pad; + float current_frame; /** List containing SimulationState objects. */ struct ListBase states; @@ -41,23 +41,19 @@ typedef struct SimulationState { struct SimulationState *next; struct SimulationState *prev; - /** This is only initialized on cow copies of the simulation. It points to the state on the - * original data block. That is where the cache is stored. */ - struct SimulationState *orig_state; - /** eSimulationStateType */ int type; int _pad; - char name[64]; + char *name; } SimulationState; typedef struct ParticleSimulationState { SimulationState head; - /** Contains the state of the particles at time current_frame. */ - float current_frame; + /** Contains the state of the particles at time Simulation->current_frame. */ int tot_particles; + int _pad; struct CustomData attributes; /** Caches the state of the particles over time. The cache only exists on the original data diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 1cb42b333c7..0892eff6de9 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -1148,8 +1148,9 @@ typedef enum eSpaceImage_Flag { SI_FLAG_UNUSED_17 = (1 << 17), /* cleared */ SI_FLAG_UNUSED_18 = (1 << 18), /* cleared */ - /* this means that the image is drawn until it reaches the view edge, - * in the image view, it's unrelated to the 'tile' mode for texface + /** + * This means that the image is drawn until it reaches the view edge, + * in the image view, it's unrelated to UDIM tiles. */ SI_DRAW_TILE = (1 << 19), SI_SMOOTH_UV = (1 << 20), diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index d751ad9ac47..8ea4d3b6476 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -622,8 +622,9 @@ typedef struct UserDef_Experimental { char use_new_particle_system; char use_new_hair_type; char use_cycles_debug; + char use_sculpt_vertex_colors; /** `makesdna` does not allow empty structs. */ - char _pad0[4]; + char _pad[3]; } UserDef_Experimental; #define USER_EXPERIMENTAL_TEST(userdef, member) \ @@ -1160,6 +1161,8 @@ typedef enum eDupli_ID_Flags { USER_DUP_OBJECT = (1 << 24), /* USER_DUP_COLLECTION = (1 << 25), */ /* UNUSED, keep because we may implement. */ + /* Duplicate (and hence make local) linked data. */ + USER_DUP_LINKED_ID = (1 << 30), } eDupli_ID_Flags; /** diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index ef174f5858f..4bb32fedd1d 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -191,6 +191,7 @@ typedef struct View3DShading { int render_pass; struct IDProperty *prop; + void *_pad2; } View3DShading; /** 3D Viewport Overlay settings. */ diff --git a/source/blender/makesdna/DNA_world_types.h b/source/blender/makesdna/DNA_world_types.h index f9198194a89..dff07f515b1 100644 --- a/source/blender/makesdna/DNA_world_types.h +++ b/source/blender/makesdna/DNA_world_types.h @@ -51,12 +51,10 @@ typedef struct World { float horr, horg, horb; /** - * Exposure= mult factor. unused now, but maybe back later. Kept in to be upward compat. - * New is exp/range control. linfac & logfac are constants... don't belong in - * file, but allocating 8 bytes for temp mem isn't useful either. + * Exposure is a multiplication factor. Unused now, but maybe back later. + * Kept in to be upward compatible. */ float exposure, exp, range; - float linfac, logfac; /** * Some world modes diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 9c2ee00a900..c2f1e204073 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -69,6 +69,7 @@ set(DEFSRC rna_packedfile.c rna_palette.c rna_particle.c + rna_pointcloud.c rna_pose.c rna_render.c rna_rigidbody.c @@ -82,7 +83,6 @@ set(DEFSRC rna_sound.c rna_space.c rna_speaker.c - rna_pointcloud.c rna_test.c rna_text.c rna_texture.c diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 46001f27fef..8d498ab569b 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -518,7 +518,7 @@ static ID *rna_ID_copy(ID *id, Main *bmain) static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages) { - if (!BKE_lib_override_library_is_enabled() || !ID_IS_OVERRIDABLE_LIBRARY(id)) { + if (!ID_IS_OVERRIDABLE_LIBRARY(id)) { return NULL; } diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 79cf993e0cc..793552c5c34 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -558,9 +558,15 @@ static PropertyRNA *arraytypemap[IDP_NUMTYPES] = { (PropertyRNA *)&rna_PropertyGroupItem_double_array, }; -static void *rna_idproperty_check_ex(PropertyRNA **prop, - PointerRNA *ptr, - const bool return_rnaprop) +/* This function initializes a PropertyRNAOrID with all required info, from a given PropertyRNA + * and PointerRNA data. It deals properly with the three cases (static RNA, runtime RNA, and + * IDProperty). + * WARNING: given `ptr` PointerRNA is assumed to be a valid data one here, calling code is + * responsible to ensure that. + */ +void rna_property_rna_or_id_get(PropertyRNA *prop, + PointerRNA *ptr, + PropertyRNAOrID *r_prop_rna_or_id) { /* This is quite a hack, but avoids some complexity in the API. we * pass IDProperty structs as PropertyRNA pointers to the outside. @@ -568,36 +574,64 @@ static void *rna_idproperty_check_ex(PropertyRNA **prop, * distinguish it from IDProperty structs. If it is an ID property, * we look up an IDP PropertyRNA based on the type, and set the data * pointer to the IDProperty. */ + memset(r_prop_rna_or_id, 0, sizeof(*r_prop_rna_or_id)); - if ((*prop)->magic == RNA_MAGIC) { - if ((*prop)->flag & PROP_IDPROPERTY) { - IDProperty *idprop = rna_idproperty_find(ptr, (*prop)->identifier); + r_prop_rna_or_id->ptr = *ptr; + r_prop_rna_or_id->rawprop = prop; + + if (prop->magic == RNA_MAGIC) { + r_prop_rna_or_id->rnaprop = prop; + r_prop_rna_or_id->identifier = prop->identifier; - if (idprop && !rna_idproperty_verify_valid(ptr, *prop, idprop)) { + r_prop_rna_or_id->is_array = prop->getlength || prop->totarraylength; + if (r_prop_rna_or_id->is_array) { + int arraylen[RNA_MAX_ARRAY_DIMENSION]; + r_prop_rna_or_id->array_len = (prop->getlength && ptr->data) ? + (uint)prop->getlength(ptr, arraylen) : + prop->totarraylength; + } + + if (prop->flag & PROP_IDPROPERTY) { + IDProperty *idprop = rna_idproperty_find(ptr, prop->identifier); + + if (idprop != NULL && !rna_idproperty_verify_valid(ptr, prop, idprop)) { IDProperty *group = RNA_struct_idprops(ptr, 0); IDP_FreeFromGroup(group, idprop); - return NULL; + idprop = NULL; } - return idprop; + r_prop_rna_or_id->idprop = idprop; + r_prop_rna_or_id->is_set = idprop != NULL && (idprop->flag & IDP_FLAG_GHOST) == 0; } else { - return return_rnaprop ? *prop : NULL; + /* Full static RNA properties are always set. */ + r_prop_rna_or_id->is_set = true; } } + else { + IDProperty *idprop = (IDProperty *)prop; + /* Given prop may come from the custom properties of another data, ensure we get the one from + * given data ptr. */ + IDProperty *idprop_evaluated = rna_idproperty_find(ptr, idprop->name); + if (idprop_evaluated != NULL && idprop->type != idprop_evaluated->type) { + idprop_evaluated = NULL; + } - { - IDProperty *idprop = (IDProperty *)(*prop); + r_prop_rna_or_id->idprop = idprop_evaluated; + r_prop_rna_or_id->is_idprop = true; + /* Full IDProperties are always set, if it exists. */ + r_prop_rna_or_id->is_set = (idprop_evaluated != NULL); + r_prop_rna_or_id->identifier = idprop->name; if (idprop->type == IDP_ARRAY) { - *prop = arraytypemap[(int)(idprop->subtype)]; + r_prop_rna_or_id->rnaprop = arraytypemap[(int)(idprop->subtype)]; + r_prop_rna_or_id->is_array = true; + r_prop_rna_or_id->array_len = idprop_evaluated != NULL ? (uint)idprop_evaluated->len : 0; } else { - *prop = typemap[(int)(idprop->type)]; + r_prop_rna_or_id->rnaprop = typemap[(int)(idprop->type)]; } - - return idprop; } } @@ -605,14 +639,26 @@ static void *rna_idproperty_check_ex(PropertyRNA **prop, * or NULL (in case IDProp could not be found, or prop is a real RNA property). */ IDProperty *rna_idproperty_check(PropertyRNA **prop, PointerRNA *ptr) { - return rna_idproperty_check_ex(prop, ptr, false); + PropertyRNAOrID prop_rna_or_id; + + rna_property_rna_or_id_get(*prop, ptr, &prop_rna_or_id); + + *prop = prop_rna_or_id.rnaprop; + return prop_rna_or_id.idprop; } /* This function always return the valid, real data pointer, be it a regular RNA property one, * or an IDProperty one. */ PropertyRNA *rna_ensure_property_realdata(PropertyRNA **prop, PointerRNA *ptr) { - return rna_idproperty_check_ex(prop, ptr, true); + PropertyRNAOrID prop_rna_or_id; + + rna_property_rna_or_id_get(*prop, ptr, &prop_rna_or_id); + + *prop = prop_rna_or_id.rnaprop; + return (prop_rna_or_id.is_idprop || prop_rna_or_id.idprop != NULL) ? + (PropertyRNA *)prop_rna_or_id.idprop : + prop_rna_or_id.rnaprop; } PropertyRNA *rna_ensure_property(PropertyRNA *prop) @@ -2102,9 +2148,7 @@ bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char * return false; } if (ID_IS_OVERRIDE_LIBRARY(id)) { - /* We need the real data property in case of IDProperty here... */ - PropertyRNA *real_prop = rna_ensure_property_realdata(&prop, ptr); - if (real_prop == NULL || !RNA_property_overridable_get(ptr, real_prop)) { + if (!RNA_property_overridable_get(ptr, prop)) { if (!(*r_info)[0]) { *r_info = N_("Can't edit this property from an override data-block"); } diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index 8cd8f80b7c8..1b846fd898c 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -177,16 +177,13 @@ bool RNA_property_copy( } static int rna_property_override_diff(Main *bmain, - PointerRNA *ptr_a, - PointerRNA *ptr_b, - PropertyRNA *prop, - PropertyRNA *prop_a, - PropertyRNA *prop_b, + PropertyRNAOrID *prop_a, + PropertyRNAOrID *prop_b, const char *rna_path, const size_t rna_path_len, eRNACompareMode mode, IDOverrideLibrary *override, - const int flags, + const eRNAOverrideMatch flags, eRNAOverrideMatchResult *r_report_flags); bool RNA_property_equals( @@ -194,8 +191,12 @@ bool RNA_property_equals( { BLI_assert(ELEM(mode, RNA_EQ_STRICT, RNA_EQ_UNSET_MATCH_ANY, RNA_EQ_UNSET_MATCH_NONE)); - return (rna_property_override_diff( - bmain, ptr_a, ptr_b, prop, NULL, NULL, NULL, 0, mode, NULL, 0, NULL) == 0); + PropertyRNAOrID prop_a, prop_b; + + rna_property_rna_or_id_get(prop, ptr_a, &prop_a); + rna_property_rna_or_id_get(prop, ptr_b, &prop_b); + + return (rna_property_override_diff(bmain, &prop_a, &prop_b, NULL, 0, mode, NULL, 0, NULL) == 0); } bool RNA_struct_equals(Main *bmain, PointerRNA *ptr_a, PointerRNA *ptr_b, eRNACompareMode mode) @@ -247,59 +248,42 @@ bool RNA_struct_equals(Main *bmain, PointerRNA *ptr_a, PointerRNA *ptr_b, eRNACo * but we cannot determine an order (greater than/lesser than), we return 1. */ static int rna_property_override_diff(Main *bmain, - PointerRNA *ptr_a, - PointerRNA *ptr_b, - PropertyRNA *prop, - PropertyRNA *prop_a, - PropertyRNA *prop_b, + PropertyRNAOrID *prop_a, + PropertyRNAOrID *prop_b, const char *rna_path, const size_t rna_path_len, eRNACompareMode mode, IDOverrideLibrary *override, - const int flags, + const eRNAOverrideMatch flags, eRNAOverrideMatchResult *r_report_flags) { - if (prop != NULL) { - BLI_assert(prop_a == NULL && prop_b == NULL); - prop_a = prop; - prop_b = prop; - } - - if (ELEM(NULL, prop_a, prop_b)) { - return (prop_a == prop_b) ? 0 : 1; - } + BLI_assert(!ELEM(NULL, prop_a, prop_b)); - if (!RNA_property_comparable(ptr_a, prop_a) || !RNA_property_comparable(ptr_b, prop_b)) { + if (prop_a->rnaprop->flag_override & PROPOVERRIDE_NO_COMPARISON || + prop_b->rnaprop->flag_override & PROPOVERRIDE_NO_COMPARISON) { return 0; } if (mode == RNA_EQ_UNSET_MATCH_ANY) { - /* uninitialized properties are assumed to match anything */ - if (!RNA_property_is_set(ptr_a, prop_a) || !RNA_property_is_set(ptr_b, prop_b)) { + /* Unset properties are assumed to match anything. */ + if (!prop_a->is_set || !prop_b->is_set) { return 0; } } else if (mode == RNA_EQ_UNSET_MATCH_NONE) { - /* unset properties never match set properties */ - if (RNA_property_is_set(ptr_a, prop_a) != RNA_property_is_set(ptr_b, prop_b)) { + /* Unset properties never match set properties. */ + if (prop_a->is_set != prop_b->is_set) { return 1; } } - if (prop != NULL) { - /* Ensure we get real property data, be it an actual RNA property, - * or an IDProperty in disguise. */ - prop_a = rna_ensure_property_realdata(&prop_a, ptr_a); - prop_b = rna_ensure_property_realdata(&prop_b, ptr_b); - - if (ELEM(NULL, prop_a, prop_b)) { - return (prop_a == prop_b) ? 0 : 1; - } + if (prop_a->is_idprop && ELEM(NULL, prop_a->idprop, prop_b->idprop)) { + return (prop_a->idprop == prop_b->idprop) ? 0 : 1; } /* Check if we are working with arrays. */ - const bool is_array_a = RNA_property_array_check(prop_a); - const bool is_array_b = RNA_property_array_check(prop_b); + const bool is_array_a = prop_a->is_array; + const bool is_array_b = prop_b->is_array; if (is_array_a != is_array_b) { /* Should probably never happen actually... */ @@ -308,8 +292,8 @@ static int rna_property_override_diff(Main *bmain, } /* Get the length of the array to work with. */ - const int len_a = RNA_property_array_length(ptr_a, prop_a); - const int len_b = RNA_property_array_length(ptr_b, prop_b); + const uint len_a = prop_a->array_len; + const uint len_b = prop_b->array_len; if (len_a != len_b) { /* Do not handle override in that case, @@ -324,47 +308,44 @@ static int rna_property_override_diff(Main *bmain, RNAPropOverrideDiff override_diff = NULL; /* Special case for IDProps, we use default callback then. */ - if (prop_a->magic != RNA_MAGIC) { + if (prop_a->is_idprop) { override_diff = rna_property_override_diff_default; - if (prop_b->magic == RNA_MAGIC && prop_b->override_diff != override_diff) { + if (!prop_b->is_idprop && prop_b->rnaprop->override_diff != override_diff) { override_diff = NULL; } } - else if (prop_b->magic != RNA_MAGIC) { + else if (prop_b->is_idprop) { override_diff = rna_property_override_diff_default; - if (prop_a->override_diff != override_diff) { + if (prop_a->rnaprop->override_diff != override_diff) { override_diff = NULL; } } - else if (prop_a->override_diff == prop_b->override_diff) { - override_diff = prop_a->override_diff; + else if (prop_a->rnaprop->override_diff == prop_b->rnaprop->override_diff) { + override_diff = prop_a->rnaprop->override_diff; + if (override_diff == NULL) { + override_diff = rna_property_override_diff_default; + } } if (override_diff == NULL) { #ifndef NDEBUG printf("'%s' gives unmatching or NULL RNA diff callbacks, should not happen (%d vs. %d).\n", - rna_path ? - rna_path : - (prop_a->magic != RNA_MAGIC ? ((IDProperty *)prop_a)->name : prop_a->identifier), - prop_a->magic == RNA_MAGIC, - prop_b->magic == RNA_MAGIC); + rna_path ? rna_path : prop_a->identifier, + !prop_a->is_idprop, + !prop_b->is_idprop); #endif BLI_assert(0); return 1; } bool override_changed = false; - int diff_flags = flags; - if (!RNA_property_overridable_get(ptr_a, prop_a)) { + eRNAOverrideMatch diff_flags = flags; + if (!RNA_property_overridable_get(&prop_a->ptr, prop_a->rawprop)) { diff_flags &= ~RNA_OVERRIDE_COMPARE_CREATE; } const int diff = override_diff(bmain, - ptr_a, - ptr_b, prop_a, prop_b, - len_a, - len_b, mode, override, rna_path, @@ -426,10 +407,13 @@ static bool rna_property_override_operation_store(Main *bmain, } else if (prop_local->override_store == prop_reference->override_store) { override_store = prop_local->override_store; + if (override_store == NULL) { + override_store = rna_property_override_store_default; + } } if (ptr_storage != NULL && prop_storage->magic == RNA_MAGIC && - prop_storage->override_store != override_store) { + !ELEM(prop_storage->override_store, NULL, override_store)) { override_store = NULL; } @@ -512,10 +496,13 @@ static bool rna_property_override_operation_apply(Main *bmain, } else if (prop_dst->override_apply == prop_src->override_apply) { override_apply = prop_dst->override_apply; + if (override_apply == NULL) { + override_apply = rna_property_override_apply_default; + } } if (ptr_storage && prop_storage->magic == RNA_MAGIC && - prop_storage->override_apply != override_apply) { + !ELEM(prop_storage->override_apply, NULL, override_apply)) { override_apply = NULL; } @@ -612,38 +599,29 @@ bool RNA_struct_override_matches(Main *bmain, for (RNA_property_collection_begin(ptr_local, iterprop, &iter); iter.valid; RNA_property_collection_next(&iter)) { - PropertyRNA *prop_local = iter.ptr.data; - PropertyRNA *prop_reference = iter.ptr.data; - - /* Ensure we get real property data, be it an actual RNA property, - * or an IDProperty in disguise. */ - prop_local = rna_ensure_property_realdata(&prop_local, ptr_local); - prop_reference = rna_ensure_property_realdata(&prop_reference, ptr_reference); - - /* IDProps (custom properties) are even more of a PITA here, we cannot use - * `rna_ensure_property_realdata()` to deal with them, we have to use the path generated from - * `prop_local` (which is valid) to access to the actual reference counterpart... */ - if (prop_local != NULL && prop_local->magic != RNA_MAGIC && prop_local == prop_reference) { - /* We could also use (lower in this code, after rna_path has been computed): - * RNA_path_resolve_property(ptr_reference, rna_path, &some_rna_ptr, &prop_reference); - * But that would be much more costly, and would also fail when ptr_reference - * is not an ID pointer itself, so we'd need to rebuild it from its owner_id, then check that - * generated some_rna_ptr and ptr_reference do point to the same data, etc. - * For now, let's try that simple access, it won't cover all cases but should handle fine - * most basic custom properties situations. */ - prop_reference = (PropertyRNA *)rna_idproperty_find(ptr_reference, - ((IDProperty *)prop_local)->name); - } + PropertyRNA *rawprop = iter.ptr.data; + + PropertyRNAOrID prop_local; + PropertyRNAOrID prop_reference; + + rna_property_rna_or_id_get(rawprop, ptr_local, &prop_local); + rna_property_rna_or_id_get(rawprop, ptr_reference, &prop_reference); - if (ELEM(NULL, prop_local, prop_reference)) { + BLI_assert(prop_local.rnaprop != NULL); + BLI_assert(prop_local.rnaprop == prop_reference.rnaprop); + BLI_assert(prop_local.is_idprop == prop_reference.is_idprop); + + if ((prop_local.is_idprop && prop_local.idprop == NULL) || + (prop_reference.is_idprop && prop_reference.idprop == NULL)) { continue; } - if (ignore_non_overridable && !RNA_property_overridable_get(ptr_local, prop_local)) { + if (ignore_non_overridable && !RNA_property_overridable_get(&prop_local.ptr, rawprop)) { continue; } - if (RNA_property_override_flag(prop_local) & PROPOVERRIDE_IGNORE) { + if (!prop_local.is_idprop && + RNA_property_override_flag(prop_local.rnaprop) & PROPOVERRIDE_IGNORE) { continue; } @@ -665,11 +643,11 @@ bool RNA_struct_override_matches(Main *bmain, if (root_path) { BLI_assert(strlen(root_path) == root_path_len); - const char *prop_name = RNA_property_identifier(prop_local); + const char *prop_name = prop_local.identifier; const size_t prop_name_len = strlen(prop_name); /* Inlined building, much much more efficient. */ - if (prop_local->magic == RNA_MAGIC) { + if (!prop_local.is_idprop) { rna_path_len = root_path_len + 1 + prop_name_len; if (rna_path_len >= RNA_PATH_BUFFSIZE) { rna_path = MEM_mallocN(rna_path_len + 1, __func__); @@ -697,7 +675,7 @@ bool RNA_struct_override_matches(Main *bmain, } else { /* This is rather slow, but is not much called, so not really worth optimizing. */ - rna_path = RNA_path_from_ID_to_property(ptr_local, prop_local); + rna_path = RNA_path_from_ID_to_property(ptr_local, rawprop); if (rna_path != NULL) { rna_path_len = strlen(rna_path); } @@ -726,11 +704,8 @@ bool RNA_struct_override_matches(Main *bmain, eRNAOverrideMatchResult report_flags = 0; const int diff = rna_property_override_diff(bmain, - ptr_local, - ptr_reference, - NULL, - prop_local, - prop_reference, + &prop_local, + &prop_reference, rna_path, rna_path_len, RNA_EQ_STRICT, @@ -764,7 +739,7 @@ bool RNA_struct_override_matches(Main *bmain, /* We are allowed to restore to reference's values. */ if (ELEM(NULL, op, opop) || opop->operation == IDOVERRIDE_LIBRARY_OP_NOOP) { /* We should restore that property to its reference value */ - if (RNA_property_editable(ptr_local, prop_local)) { + if (RNA_property_editable(ptr_local, rawprop)) { IDOverrideLibraryPropertyOperation opop_tmp = { .operation = IDOVERRIDE_LIBRARY_OP_REPLACE, .subitem_reference_index = -1, @@ -774,8 +749,8 @@ bool RNA_struct_override_matches(Main *bmain, ptr_local, ptr_reference, NULL, - prop_local, - prop_reference, + rawprop, + rawprop, NULL, NULL, NULL, @@ -1202,10 +1177,6 @@ eRNAOverrideStatus RNA_property_override_library_status(PointerRNA *ptr, { uint override_status = 0; - if (!BKE_lib_override_library_is_enabled()) { - return override_status; - } - if (!ptr || !prop || !ptr->owner_id || !ID_IS_OVERRIDE_LIBRARY(ptr->owner_id)) { return override_status; } diff --git a/source/blender/makesrna/intern/rna_access_internal.h b/source/blender/makesrna/intern/rna_access_internal.h index c7995746d08..a5b554ec7a6 100644 --- a/source/blender/makesrna/intern/rna_access_internal.h +++ b/source/blender/makesrna/intern/rna_access_internal.h @@ -26,8 +26,11 @@ #include "rna_internal_types.h" struct IDProperty; +struct PropertyRNAOrID; -PropertyRNA *rna_ensure_property(PropertyRNA *prop); +void rna_property_rna_or_id_get(PropertyRNA *prop, + PointerRNA *ptr, + PropertyRNAOrID *r_prop_rna_or_id); void rna_idproperty_touch(struct IDProperty *idprop); struct IDProperty *rna_idproperty_find(PointerRNA *ptr, const char *name); diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index b139e4609cd..b4703ab9aa2 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1997,6 +1997,13 @@ static void rna_def_brush(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem brush_smear_deform_type_items[] = { + {BRUSH_SMEAR_DEFORM_DRAG, "DRAG", 0, "Drag", ""}, + {BRUSH_SMEAR_DEFORM_PINCH, "PINCH", 0, "Pinch", ""}, + {BRUSH_SMEAR_DEFORM_EXPAND, "EXPAND", 0, "Expand", ""}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "Brush", "ID"); RNA_def_struct_ui_text( srna, "Brush", "Brush data-block for storing brush settings for painting and sculpting"); @@ -2117,6 +2124,11 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "smear_deform_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, brush_smear_deform_type_items); + RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "pose_deform_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, brush_pose_deform_type_items); RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush"); diff --git a/source/blender/makesrna/intern/rna_depsgraph.c b/source/blender/makesrna/intern/rna_depsgraph.c index ca34f69ab1e..da1ed166eb2 100644 --- a/source/blender/makesrna/intern/rna_depsgraph.c +++ b/source/blender/makesrna/intern/rna_depsgraph.c @@ -551,7 +551,7 @@ static void rna_def_depsgraph_instance(BlenderRNA *brna) prop, "Persistent ID", "Persistent identifier for inter-frame matching of objects with motion blur"); - RNA_def_property_array(prop, 2 * MAX_DUPLI_RECUR); + RNA_def_property_array(prop, MAX_DUPLI_RECUR); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE); RNA_def_property_int_funcs(prop, "rna_DepsgraphObjectInstance_persistent_id_get", NULL, NULL); diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c index ab0cc6def6f..3387958c2f6 100644 --- a/source/blender/makesrna/intern/rna_fluid.c +++ b/source/blender/makesrna/intern/rna_fluid.c @@ -161,7 +161,7 @@ static void rna_Fluid_flow_reset(Main *bmain, Scene *scene, PointerRNA *ptr) rna_Fluid_update(bmain, scene, ptr); } -static void rna_Fluid_domain_reset(Main *bmain, Scene *scene, PointerRNA *ptr) +static void rna_Fluid_domain_data_reset(Main *bmain, Scene *scene, PointerRNA *ptr) { # ifdef WITH_FLUID FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data; @@ -172,6 +172,39 @@ static void rna_Fluid_domain_reset(Main *bmain, Scene *scene, PointerRNA *ptr) rna_Fluid_update(bmain, scene, ptr); } +static void rna_Fluid_domain_noise_reset(Main *bmain, Scene *scene, PointerRNA *ptr) +{ +# ifdef WITH_FLUID + FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data; + BKE_fluid_modifier_reset(settings->fmd); +# endif + + rna_Fluid_noisecache_reset(bmain, scene, ptr); + rna_Fluid_update(bmain, scene, ptr); +} + +static void rna_Fluid_domain_mesh_reset(Main *bmain, Scene *scene, PointerRNA *ptr) +{ +# ifdef WITH_FLUID + FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data; + BKE_fluid_modifier_reset(settings->fmd); +# endif + + rna_Fluid_meshcache_reset(bmain, scene, ptr); + rna_Fluid_update(bmain, scene, ptr); +} + +static void rna_Fluid_domain_particles_reset(Main *bmain, Scene *scene, PointerRNA *ptr) +{ +# ifdef WITH_FLUID + FluidDomainSettings *settings = (FluidDomainSettings *)ptr->data; + BKE_fluid_modifier_reset(settings->fmd); +# endif + + rna_Fluid_particlescache_reset(bmain, scene, ptr); + rna_Fluid_update(bmain, scene, ptr); +} + static void rna_Fluid_reset_dependency(Main *bmain, Scene *scene, PointerRNA *ptr) { # ifdef WITH_FLUID @@ -232,7 +265,7 @@ static void rna_Fluid_flip_parts_update(Main *bmain, Scene *scene, PointerRNA *p if (fmd->domain->type != FLUID_DOMAIN_TYPE_LIQUID) { rna_Fluid_parts_delete(ptr, PART_FLUID_FLIP); fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FLIP; - rna_Fluid_domain_reset(bmain, scene, ptr); + rna_Fluid_domain_data_reset(bmain, scene, ptr); return; } @@ -1350,7 +1383,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Adaptive Domain", "Adapt simulation resolution and size to fluid"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_data_reset"); /* fluid domain options */ @@ -1364,7 +1397,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) "Resolution used for the fluid domain. Value corresponds to the longest domain side " "(resolution for other domain sides is calculated automatically)"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_data_reset"); prop = RNA_def_property(srna, "use_collision_border_front", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "border_collisions", FLUID_DOMAIN_BORDER_FRONT); @@ -1547,7 +1580,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) "The noise simulation is scaled up by this factor (compared to the " "base resolution of the domain)"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_noise_reset"); prop = RNA_def_property(srna, "noise_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "noise_type"); @@ -1555,7 +1588,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Noise Method", "Noise method which is used during the high-res simulation"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_noise_reset"); prop = RNA_def_property(srna, "use_noise", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", FLUID_DOMAIN_USE_NOISE); @@ -1569,7 +1602,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "simulation_method"); RNA_def_property_enum_items(prop, simulation_methods); RNA_def_property_ui_text(prop, "Simulation Method", "Change the underlying simulation method"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_data_reset"); prop = RNA_def_property(srna, "flip_ratio", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.0, 1.0); @@ -1657,7 +1690,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Use Diffusion", "Enable fluid diffusion settings (e.g. viscosity, surface tension)"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_datacache_reset"); prop = RNA_def_property(srna, "surface_tension", PROP_FLOAT, PROP_NONE); RNA_def_property_range(prop, 0.0, 100.0); @@ -1724,7 +1757,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) "resolution of the domain). For best meshing, it is recommended to " "adjust the mesh particle radius alongside this value"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_meshcache_reset"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_mesh_reset"); prop = RNA_def_property(srna, "mesh_generator", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "mesh_generator"); @@ -1924,7 +1957,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) "The particle simulation is scaled up by this factor (compared to the " "base resolution of the domain)"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_particles_reset"); prop = RNA_def_property(srna, "use_spray_particles", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "particle_type", FLUID_DOMAIN_PARTICLE_SPRAY); @@ -2081,7 +2114,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_enum_items(prop, cache_types); RNA_def_property_enum_funcs(prop, NULL, "rna_Fluid_cachetype_set", NULL); RNA_def_property_ui_text(prop, "Type", "Change the cache type of the simulation"); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Fluid_domain_reset"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Fluid_domain_data_reset"); prop = RNA_def_property(srna, "cache_resumable", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", FLUID_DOMAIN_USE_RESUMABLE_CACHE); @@ -2158,7 +2191,7 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) "only needed if you plan to analyze the cache (e.g. view grids, velocity vectors, " "particles) in Mantaflow directly (outside of Blender) after baking the simulation"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_data_reset"); /* time options */ @@ -2655,7 +2688,7 @@ static void rna_def_fluid_effector_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "use_plane_init", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", FLUID_EFFECTOR_USE_PLANE_INIT); RNA_def_property_ui_text(prop, "Is Planar", "Treat this object as a planar, unclosed mesh"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_data_reset"); prop = RNA_def_property(srna, "velocity_factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "vel_multi"); diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 1c39ad3a1a8..9519e3e1433 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -1817,8 +1817,7 @@ static void rna_def_modifier_gpencilmirror(BlenderRNA *brna) PropertyRNA *prop; srna = RNA_def_struct(brna, "MirrorGpencilModifier", "GpencilModifier"); - RNA_def_struct_ui_text( - srna, "Mirror Modifier", "Change stroke using lattice to deform modifier"); + RNA_def_struct_ui_text(srna, "Mirror Modifier", "Create mirroring strokes"); RNA_def_struct_sdna(srna, "MirrorGpencilModifierData"); RNA_def_struct_ui_icon(srna, ICON_MOD_MIRROR); diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 0783addd78b..a8085c00cb3 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -23,6 +23,8 @@ #include "BLI_utildefines.h" +#include "BLI_compiler_attrs.h" + #include "rna_internal_types.h" #include "UI_resources.h" @@ -478,9 +480,11 @@ extern StructRNA RNA_PropertyGroupItem; extern StructRNA RNA_PropertyGroup; #endif -struct IDProperty *rna_idproperty_check(struct PropertyRNA **prop, struct PointerRNA *ptr); +struct IDProperty *rna_idproperty_check(struct PropertyRNA **prop, + struct PointerRNA *ptr) ATTR_WARN_UNUSED_RESULT; struct PropertyRNA *rna_ensure_property_realdata(struct PropertyRNA **prop, - struct PointerRNA *ptr); + struct PointerRNA *ptr) ATTR_WARN_UNUSED_RESULT; +struct PropertyRNA *rna_ensure_property(struct PropertyRNA *prop) ATTR_WARN_UNUSED_RESULT; /* Override default callbacks. */ /* Default override callbacks for all types. */ @@ -489,12 +493,8 @@ struct PropertyRNA *rna_ensure_property_realdata(struct PropertyRNA **prop, * Not obvious though, those are fairly more complicated than basic SDNA access. */ int rna_property_override_diff_default(struct Main *bmain, - struct PointerRNA *ptr_a, - struct PointerRNA *ptr_b, - struct PropertyRNA *prop_a, - struct PropertyRNA *prop_b, - const int len_a, - const int len_b, + struct PropertyRNAOrID *prop_a, + struct PropertyRNAOrID *prop_b, const int mode, struct IDOverrideLibrary *override, const char *rna_path, diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h index 345d84fc5b1..20c8743f768 100644 --- a/source/blender/makesrna/intern/rna_internal_types.h +++ b/source/blender/makesrna/intern/rna_internal_types.h @@ -41,6 +41,8 @@ struct Scene; struct StructRNA; struct bContext; +typedef struct IDProperty IDProperty; + /* store local properties here */ #define RNA_IDP_UI "_RNA_UI" @@ -155,24 +157,55 @@ typedef void (*PropEnumSetFuncEx)(struct PointerRNA *ptr, struct PropertyRNA *pr /* Handling override operations, and also comparison. */ +/** Structure storing all needed data to process all three kinds of RNA properties. */ +typedef struct PropertyRNAOrID { + PointerRNA ptr; + + /** The PropertyRNA passed as parameter, used to generate that structure's content: + * - Static RNA: The RNA property (same as `rnaprop`), never NULL. + * - Runtime RNA: The RNA property (same as `rnaprop`), never NULL. + * - IDProperty: The IDProperty, never NULL. + */ + PropertyRNA *rawprop; + /** The real RNA property of this property, never NULL: + * - Static RNA: The rna property, also gives direct access to the data (from any matching + * PointerRNA). + * - Runtime RNA: The rna property, does not directly gives access to the data. + * - IDProperty: The generic PropertyRNA matching its type. + */ + PropertyRNA *rnaprop; + /** The IDProperty storing the data of this property, may be NULL: + * - Static RNA: Always NULL. + * - Runtime RNA: The IDProperty storing the data of that property, may be NULL if never set yet. + * - IDProperty: The IDProperty, never NULL. + */ + IDProperty *idprop; + /** The name of the property. */ + const char *identifier; + + /** Whether this property is a 'pure' IDProperty or not. */ + bool is_idprop; + /** For runtime RNA properties, whether it is set, defined, or not. + * WARNING: This DOES take into account the `IDP_FLAG_GHOST` flag, i.e. it matches result of + * `RNA_property_is_set`. */ + bool is_set; + + bool is_array; + uint array_len; +} PropertyRNAOrID; + /** - * If \a override is NULL, merely do comparison between prop_a from ptr_a and prop_b from ptr_b, + * If \a override is NULL, merely do comparison between prop_a and prop_b, * following comparison mode given. * If \a override and \a rna_path are not NULL, it will add a new override operation for * overridable properties that differ and have not yet been overridden * (and set accordingly \a r_override_changed if given). * - * \note Given PropertyRNA are final (in case of IDProps...). - * \note In non-array cases, \a len values are 0. * \note \a override, \a rna_path and \a r_override_changed may be NULL pointers. */ typedef int (*RNAPropOverrideDiff)(struct Main *bmain, - struct PointerRNA *ptr_a, - struct PointerRNA *ptr_b, - struct PropertyRNA *prop_a, - struct PropertyRNA *prop_b, - const int len_a, - const int len_b, + struct PropertyRNAOrID *prop_a, + struct PropertyRNAOrID *prop_b, const int mode, struct IDOverrideLibrary *override, const char *rna_path, diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c index b99457056fe..e7a898b97ae 100644 --- a/source/blender/makesrna/intern/rna_layer.c +++ b/source/blender/makesrna/intern/rna_layer.c @@ -458,7 +458,7 @@ static void rna_def_layer_objects(BlenderRNA *brna, PropertyRNA *cprop) NULL); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_UNLINK); RNA_def_property_ui_text(prop, "Active Object", "Active object for this layer"); - /* Could call: ED_object_base_activate(C, rl->basact); + /* Could call: `ED_object_base_activate(C, view_layer->basact);` * but would be a bad level call and it seems the notifier is enough */ RNA_def_property_update(prop, NC_SCENE | ND_OB_ACTIVE, NULL); diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c index 1670e08325f..97702b06b6f 100644 --- a/source/blender/makesrna/intern/rna_main.c +++ b/source/blender/makesrna/intern/rna_main.c @@ -410,7 +410,7 @@ void RNA_def_main(BlenderRNA *brna) srna = RNA_def_struct(brna, "BlendData", NULL); RNA_def_struct_ui_text(srna, - "Blendfile Data", + "Blend-file Data", "Main data structure representing a .blend file and all its data-blocks"); RNA_def_struct_ui_icon(srna, ICON_BLENDER); @@ -436,7 +436,7 @@ void RNA_def_main(BlenderRNA *brna) prop = RNA_def_property(srna, "use_autopack", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_funcs(prop, "rna_Main_use_autopack_get", "rna_Main_use_autopack_set"); RNA_def_property_ui_text( - prop, "Use Autopack", "Automatically pack all external data into .blend file"); + prop, "Use Auto-pack", "Automatically pack all external data into .blend file"); prop = RNA_def_int_vector(srna, "version", diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index eab7326868b..eb1a09f9c76 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -1639,6 +1639,40 @@ static void rna_SimulationModifier_simulation_update(Main *bmain, Scene *scene, rna_Modifier_dependency_update(bmain, scene, ptr); } +static void rna_SimulationModifier_data_path_get(PointerRNA *ptr, char *value) +{ + SimulationModifierData *smd = ptr->data; + + if (smd->data_path) { + strcpy(value, smd->data_path); + } + else { + value[0] = '\0'; + } +} + +static int rna_SimulationModifier_data_path_length(PointerRNA *ptr) +{ + SimulationModifierData *smd = ptr->data; + return smd->data_path ? strlen(smd->data_path) : 0; +} + +static void rna_SimulationModifier_data_path_set(PointerRNA *ptr, const char *value) +{ + SimulationModifierData *smd = ptr->data; + + if (smd->data_path) { + MEM_freeN(smd->data_path); + } + + if (value[0]) { + smd->data_path = BLI_strdup(value); + } + else { + smd->data_path = NULL; + } +} + /** * Special set callback that just changes the first bit of the expansion flag. * This way the expansion state of all the sub-panels is not changed by RNA. @@ -1654,6 +1688,17 @@ static void rna_Modifier_show_expanded_set(PointerRNA *ptr, bool value) } } +/** + * Only check the first bit of the expansion flag for the main panel's expansion, + * maintaining compatibility with older versions where there was only one expansion + * value. + */ +static bool rna_Modifier_show_expanded_get(PointerRNA *ptr) +{ + ModifierData *md = ptr->data; + return md->ui_expand_flag & (1 << 0); +} + #else /* NOTE: *MUST* return subdivision_type property. */ @@ -6859,6 +6904,10 @@ static void rna_def_modifier_simulation(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_SimulationModifier_simulation_update"); prop = RNA_def_property(srna, "data_path", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, + "rna_SimulationModifier_data_path_get", + "rna_SimulationModifier_data_path_length", + "rna_SimulationModifier_data_path_set"); RNA_def_property_ui_text( prop, "Data Path", "Identifier of the simulation component that should be accessed"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); @@ -6921,7 +6970,8 @@ void RNA_def_modifier(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_funcs(prop, NULL, "rna_Modifier_show_expanded_set"); + RNA_def_property_boolean_funcs( + prop, "rna_Modifier_show_expanded_get", "rna_Modifier_show_expanded_set"); RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); RNA_def_property_boolean_sdna(prop, NULL, "ui_expand_flag", 0); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 6312c84cf9f..332108facb3 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -4421,7 +4421,7 @@ static void def_sh_tex_sky(StructRNA *srna) RNA_def_property_enum_sdna(prop, NULL, "sky_model"); RNA_def_property_enum_items(prop, prop_sky_type); RNA_def_property_ui_text(prop, "Sky Type", "Which sky model should be used"); - RNA_def_property_update(prop, 0, "rna_Node_update"); + RNA_def_property_update(prop, 0, "rna_ShaderNode_socket_update"); prop = RNA_def_property(srna, "sun_direction", PROP_FLOAT, PROP_DIRECTION); RNA_def_property_ui_text(prop, "Sun Direction", "Direction from where the sun is shining"); @@ -4445,7 +4445,7 @@ static void def_sh_tex_sky(StructRNA *srna) RNA_def_property_ui_text(prop, "Sun Disc", "Include the sun itself in the output"); RNA_def_property_boolean_sdna(prop, NULL, "sun_disc", 1); RNA_def_property_boolean_default(prop, true); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + RNA_def_property_update(prop, 0, "rna_ShaderNode_socket_update"); prop = RNA_def_property(srna, "sun_size", PROP_FLOAT, PROP_ANGLE); RNA_def_property_ui_text(prop, "Sun Size", "Size of sun disc (angular diameter)"); @@ -4453,6 +4453,12 @@ static void def_sh_tex_sky(StructRNA *srna) RNA_def_property_float_default(prop, DEG2RADF(0.545)); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + prop = RNA_def_property(srna, "sun_intensity", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, "Sun Intensity", "Strength of sun"); + RNA_def_property_range(prop, 0.0f, 1000.0f); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + prop = RNA_def_property(srna, "sun_elevation", PROP_FLOAT, PROP_ANGLE); RNA_def_property_ui_text(prop, "Sun Elevation", "Angle between sun and horizon"); RNA_def_property_range(prop, -M_PI_2, M_PI_2); @@ -4461,14 +4467,13 @@ static void def_sh_tex_sky(StructRNA *srna) prop = RNA_def_property(srna, "sun_rotation", PROP_FLOAT, PROP_ANGLE); RNA_def_property_ui_text(prop, "Sun Rotation", "Rotation of sun around zenith"); - RNA_def_property_range(prop, 0.0f, 2.0f * M_PI); RNA_def_property_float_default(prop, 0.0f); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); - prop = RNA_def_property(srna, "altitude", PROP_INT, PROP_NONE); - RNA_def_property_ui_text(prop, "Altitude", "Altitude height from sea level in meters"); - RNA_def_property_range(prop, 0, 60000); - RNA_def_property_int_default(prop, 0); + prop = RNA_def_property(srna, "altitude", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, "Altitude", "Height from sea level in km"); + RNA_def_property_range(prop, 0.0f, 60.0f); + RNA_def_property_float_default(prop, 0.0f); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); prop = RNA_def_property(srna, "air_density", PROP_FLOAT, PROP_FACTOR); @@ -8361,6 +8366,8 @@ static void rna_def_node_socket(BlenderRNA *brna) RNA_def_property_pointer_funcs(prop, "rna_NodeSocket_node_get", NULL, NULL, NULL); RNA_def_property_struct_type(prop, "Node"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); RNA_def_property_ui_text(prop, "Node", "Node owning this socket"); /* NB: the type property is used by standard sockets. @@ -9316,6 +9323,8 @@ static void rna_def_node(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "parent"); RNA_def_property_pointer_funcs(prop, NULL, "rna_Node_parent_set", NULL, "rna_Node_parent_poll"); RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); RNA_def_property_struct_type(prop, "Node"); RNA_def_property_ui_text(prop, "Parent", "Parent this node is attached to"); @@ -9530,29 +9539,39 @@ static void rna_def_node_link(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "fromnode"); RNA_def_property_struct_type(prop, "Node"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); RNA_def_property_ui_text(prop, "From node", ""); prop = RNA_def_property(srna, "to_node", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "tonode"); RNA_def_property_struct_type(prop, "Node"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); RNA_def_property_ui_text(prop, "To node", ""); prop = RNA_def_property(srna, "from_socket", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "fromsock"); RNA_def_property_struct_type(prop, "NodeSocket"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); RNA_def_property_ui_text(prop, "From socket", ""); prop = RNA_def_property(srna, "to_socket", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "tosock"); RNA_def_property_struct_type(prop, "NodeSocket"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); RNA_def_property_ui_text(prop, "To socket", ""); prop = RNA_def_property(srna, "is_hidden", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_funcs(prop, "rna_NodeLink_is_hidden_get", NULL); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP); + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); RNA_def_property_ui_text(prop, "Is Hidden", "Link is hidden due to invisible sockets"); } diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 089410ffd25..00d3a6e84b7 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -1579,7 +1579,8 @@ static void rna_Object_modifier_remove(Object *object, PointerRNA *md_ptr) { ModifierData *md = md_ptr->data; - if (ED_object_modifier_remove(reports, CTX_data_main(C), object, md) == false) { + if (ED_object_modifier_remove(reports, CTX_data_main(C), CTX_data_scene(C), object, md) == + false) { /* error is already set */ return; } @@ -1591,7 +1592,7 @@ static void rna_Object_modifier_remove(Object *object, static void rna_Object_modifier_clear(Object *object, bContext *C) { - ED_object_modifier_clear(CTX_data_main(C), object); + ED_object_modifier_clear(CTX_data_main(C), CTX_data_scene(C), object); WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_REMOVED, object); } diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index 0b932f3236f..2524f590051 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -851,7 +851,7 @@ static void rna_CollisionSettings_dependency_update(Main *bmain, Scene *scene, P ED_object_modifier_add(NULL, bmain, scene, ob, NULL, eModifierType_Collision); } else if (!ob->pd->deflect && md) { - ED_object_modifier_remove(NULL, bmain, ob, md); + ED_object_modifier_remove(NULL, bmain, scene, ob, md); } WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob); diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c index 623b5864f5b..f9f6defb2f1 100644 --- a/source/blender/makesrna/intern/rna_rna.c +++ b/source/blender/makesrna/intern/rna_rna.c @@ -479,7 +479,7 @@ static StructRNA *rna_Property_refine(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); /* XXX ptr? */ + prop = rna_ensure_property(prop); switch (prop->type) { case PROP_BOOLEAN: @@ -504,90 +504,90 @@ static StructRNA *rna_Property_refine(PointerRNA *ptr) static void rna_Property_identifier_get(PointerRNA *ptr, char *value) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); strcpy(value, ((PropertyRNA *)prop)->identifier); } static int rna_Property_identifier_length(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return strlen(prop->identifier); } static void rna_Property_name_get(PointerRNA *ptr, char *value) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); strcpy(value, prop->name ? prop->name : ""); } static int rna_Property_name_length(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return prop->name ? strlen(prop->name) : 0; } static void rna_Property_description_get(PointerRNA *ptr, char *value) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); strcpy(value, prop->description ? prop->description : ""); } static int rna_Property_description_length(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return prop->description ? strlen(prop->description) : 0; } static void rna_Property_translation_context_get(PointerRNA *ptr, char *value) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); strcpy(value, prop->translation_context); } static int rna_Property_translation_context_length(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return strlen(prop->translation_context); } static int rna_Property_type_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return prop->type; } static int rna_Property_subtype_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return prop->subtype; } static PointerRNA rna_Property_srna_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return rna_pointer_inherit_refine(ptr, &RNA_Struct, prop->srna); } static int rna_Property_unit_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return RNA_SUBTYPE_UNIT(prop->subtype); } static int rna_Property_icon_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return prop->icon; } @@ -698,7 +698,7 @@ static const EnumPropertyItem *rna_Property_tags_itemf(bContext *UNUSED(C), static int rna_Property_array_length_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return prop->totarraylength; } @@ -706,7 +706,7 @@ static void rna_Property_array_dimensions_get(PointerRNA *ptr, int dimensions[RNA_MAX_ARRAY_DIMENSION]) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); if (prop->arraydimension > 1) { for (int i = RNA_MAX_ARRAY_DIMENSION; i--;) { @@ -740,14 +740,14 @@ static bool rna_Property_is_runtime_get(PointerRNA *ptr) static bool rna_BoolProperty_default_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return ((BoolPropertyRNA *)prop)->defaultvalue; } static int rna_IntProperty_default_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return ((IntPropertyRNA *)prop)->defaultvalue; } /* int/float/bool */ @@ -755,7 +755,7 @@ static int rna_NumberProperty_default_array_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); length[0] = prop->totarraylength; @@ -771,7 +771,7 @@ static bool rna_NumberProperty_is_array_get(PointerRNA *ptr) static void rna_IntProperty_default_array_get(PointerRNA *ptr, int *values) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); if (prop->totarraylength > 0) { PointerRNA null_ptr = PointerRNA_NULL; RNA_property_int_get_default_array(&null_ptr, prop, values); @@ -781,7 +781,7 @@ static void rna_IntProperty_default_array_get(PointerRNA *ptr, int *values) static void rna_BoolProperty_default_array_get(PointerRNA *ptr, bool *values) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); if (prop->totarraylength > 0) { PointerRNA null_ptr = PointerRNA_NULL; RNA_property_boolean_get_default_array(&null_ptr, prop, values); @@ -791,7 +791,7 @@ static void rna_BoolProperty_default_array_get(PointerRNA *ptr, bool *values) static void rna_FloatProperty_default_array_get(PointerRNA *ptr, float *values) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); if (prop->totarraylength > 0) { PointerRNA null_ptr = PointerRNA_NULL; RNA_property_float_get_default_array(&null_ptr, prop, values); @@ -801,103 +801,103 @@ static void rna_FloatProperty_default_array_get(PointerRNA *ptr, float *values) static int rna_IntProperty_hard_min_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return ((IntPropertyRNA *)prop)->hardmin; } static int rna_IntProperty_hard_max_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return ((IntPropertyRNA *)prop)->hardmax; } static int rna_IntProperty_soft_min_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return ((IntPropertyRNA *)prop)->softmin; } static int rna_IntProperty_soft_max_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return ((IntPropertyRNA *)prop)->softmax; } static int rna_IntProperty_step_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return ((IntPropertyRNA *)prop)->step; } static float rna_FloatProperty_default_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return ((FloatPropertyRNA *)prop)->defaultvalue; } static float rna_FloatProperty_hard_min_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return ((FloatPropertyRNA *)prop)->hardmin; } static float rna_FloatProperty_hard_max_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return ((FloatPropertyRNA *)prop)->hardmax; } static float rna_FloatProperty_soft_min_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return ((FloatPropertyRNA *)prop)->softmin; } static float rna_FloatProperty_soft_max_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return ((FloatPropertyRNA *)prop)->softmax; } static float rna_FloatProperty_step_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return ((FloatPropertyRNA *)prop)->step; } static int rna_FloatProperty_precision_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return ((FloatPropertyRNA *)prop)->precision; } static void rna_StringProperty_default_get(PointerRNA *ptr, char *value) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); strcpy(value, ((StringPropertyRNA *)prop)->defaultvalue); } static int rna_StringProperty_default_length(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return strlen(((StringPropertyRNA *)prop)->defaultvalue); } static int rna_StringProperty_max_length_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return ((StringPropertyRNA *)prop)->maxlength; } @@ -909,7 +909,7 @@ static const EnumPropertyItem *rna_EnumProperty_default_itemf(bContext *C, PropertyRNA *prop = (PropertyRNA *)ptr->data; EnumPropertyRNA *eprop; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); eprop = (EnumPropertyRNA *)prop; /* incompatible default attributes */ @@ -931,7 +931,7 @@ static const EnumPropertyItem *rna_EnumProperty_default_itemf(bContext *C, static int rna_EnumProperty_default_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return ((EnumPropertyRNA *)prop)->defaultvalue; } @@ -950,7 +950,7 @@ static void rna_EnumProperty_items_begin(CollectionPropertyIterator *iter, Point int totitem; bool free; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); /* eprop = (EnumPropertyRNA *)prop; */ RNA_property_enum_items_ex( @@ -1016,14 +1016,14 @@ static int rna_EnumPropertyItem_icon_get(PointerRNA *ptr) static PointerRNA rna_PointerProperty_fixed_type_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return rna_pointer_inherit_refine(ptr, &RNA_Struct, ((PointerPropertyRNA *)prop)->type); } static PointerRNA rna_CollectionProperty_fixed_type_get(PointerRNA *ptr) { PropertyRNA *prop = (PropertyRNA *)ptr->data; - rna_idproperty_check(&prop, ptr); + prop = rna_ensure_property(prop); return rna_pointer_inherit_refine(ptr, &RNA_Struct, ((CollectionPropertyRNA *)prop)->item_type); } @@ -1431,12 +1431,8 @@ static int rna_property_override_diff_propptr(Main *bmain, RNA_property_##_typename##_set((_ptr), (_prop), (_value))) int rna_property_override_diff_default(Main *bmain, - PointerRNA *ptr_a, - PointerRNA *ptr_b, - PropertyRNA *prop_a, - PropertyRNA *prop_b, - const int len_a, - const int len_b, + PropertyRNAOrID *prop_a, + PropertyRNAOrID *prop_b, const int mode, IDOverrideLibrary *override, const char *rna_path, @@ -1444,6 +1440,13 @@ int rna_property_override_diff_default(Main *bmain, const int flags, bool *r_override_changed) { + PointerRNA *ptr_a = &prop_a->ptr; + PointerRNA *ptr_b = &prop_b->ptr; + PropertyRNA *rawprop_a = prop_a->rawprop; + PropertyRNA *rawprop_b = prop_b->rawprop; + const uint len_a = prop_a->array_len; + const uint len_b = prop_b->array_len; + BLI_assert(len_a == len_b); /* Note: at this point, we are sure that when len_a is zero, @@ -1452,7 +1455,16 @@ int rna_property_override_diff_default(Main *bmain, const bool do_create = override != NULL && (flags & RNA_OVERRIDE_COMPARE_CREATE) != 0 && rna_path != NULL; - switch (RNA_property_type(prop_a)) { + const bool no_ownership = (prop_a->rnaprop->flag & PROP_PTR_NO_OWNERSHIP) != 0; + const bool no_prop_name = (prop_a->rnaprop->flag_override & PROPOVERRIDE_NO_PROP_NAME) != 0; + + /* Note: we assume we only insert in ptr_a (i.e. we can only get new items in ptr_a), + * and that we never remove anything. */ + const bool use_collection_insertion = (prop_a->rnaprop->flag_override & + PROPOVERRIDE_LIBRARY_INSERTION) && + do_create; + + switch (RNA_property_type(prop_a->rnaprop)) { case PROP_BOOLEAN: { if (len_a) { bool array_stack_a[RNA_STACK_ARRAY], array_stack_b[RNA_STACK_ARRAY]; @@ -1463,8 +1475,8 @@ int rna_property_override_diff_default(Main *bmain, array_b = (len_b > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(bool) * len_b, "RNA equals") : array_stack_b; - RNA_property_boolean_get_array(ptr_a, prop_a, array_a); - RNA_property_boolean_get_array(ptr_b, prop_b, array_b); + RNA_property_boolean_get_array(ptr_a, rawprop_a, array_a); + RNA_property_boolean_get_array(ptr_b, rawprop_b, array_b); const int comp = memcmp(array_a, array_b, sizeof(bool) * len_a); @@ -1496,8 +1508,8 @@ int rna_property_override_diff_default(Main *bmain, return comp; } else { - const bool value_a = RNA_property_boolean_get(ptr_a, prop_a); - const bool value_b = RNA_property_boolean_get(ptr_b, prop_b); + const bool value_a = RNA_property_boolean_get(ptr_a, rawprop_a); + const bool value_b = RNA_property_boolean_get(ptr_b, rawprop_b); const int comp = (value_a < value_b) ? -1 : (value_a > value_b) ? 1 : 0; if (do_create && comp != 0) { @@ -1528,8 +1540,8 @@ int rna_property_override_diff_default(Main *bmain, array_b = (len_b > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(int) * len_b, "RNA equals") : array_stack_b; - RNA_property_int_get_array(ptr_a, prop_a, array_a); - RNA_property_int_get_array(ptr_b, prop_b, array_b); + RNA_property_int_get_array(ptr_a, rawprop_a, array_a); + RNA_property_int_get_array(ptr_b, rawprop_b, array_b); const int comp = memcmp(array_a, array_b, sizeof(int) * len_a); @@ -1561,8 +1573,8 @@ int rna_property_override_diff_default(Main *bmain, return comp; } else { - const int value_a = RNA_property_int_get(ptr_a, prop_a); - const int value_b = RNA_property_int_get(ptr_b, prop_b); + const int value_a = RNA_property_int_get(ptr_a, rawprop_a); + const int value_b = RNA_property_int_get(ptr_b, rawprop_b); const int comp = (value_a < value_b) ? -1 : (value_a > value_b) ? 1 : 0; if (do_create && comp != 0) { @@ -1593,8 +1605,8 @@ int rna_property_override_diff_default(Main *bmain, array_b = (len_b > RNA_STACK_ARRAY) ? MEM_mallocN(sizeof(float) * len_b, "RNA equals") : array_stack_b; - RNA_property_float_get_array(ptr_a, prop_a, array_a); - RNA_property_float_get_array(ptr_b, prop_b, array_b); + RNA_property_float_get_array(ptr_a, rawprop_a, array_a); + RNA_property_float_get_array(ptr_b, rawprop_b, array_b); const int comp = memcmp(array_a, array_b, sizeof(float) * len_a); @@ -1626,8 +1638,8 @@ int rna_property_override_diff_default(Main *bmain, return comp; } else { - const float value_a = RNA_property_float_get(ptr_a, prop_a); - const float value_b = RNA_property_float_get(ptr_b, prop_b); + const float value_a = RNA_property_float_get(ptr_a, rawprop_a); + const float value_b = RNA_property_float_get(ptr_b, rawprop_b); const int comp = (value_a < value_b) ? -1 : (value_a > value_b) ? 1 : 0; if (do_create && comp != 0) { @@ -1649,8 +1661,8 @@ int rna_property_override_diff_default(Main *bmain, } case PROP_ENUM: { - const int value_a = RNA_property_enum_get(ptr_a, prop_a); - const int value_b = RNA_property_enum_get(ptr_b, prop_b); + const int value_a = RNA_property_enum_get(ptr_a, rawprop_a); + const int value_b = RNA_property_enum_get(ptr_b, rawprop_b); const int comp = value_a != value_b; if (do_create && comp != 0) { @@ -1674,9 +1686,9 @@ int rna_property_override_diff_default(Main *bmain, char fixed_a[4096], fixed_b[4096]; int len_str_a, len_str_b; char *value_a = RNA_property_string_get_alloc( - ptr_a, prop_a, fixed_a, sizeof(fixed_a), &len_str_a); + ptr_a, rawprop_a, fixed_a, sizeof(fixed_a), &len_str_a); char *value_b = RNA_property_string_get_alloc( - ptr_b, prop_b, fixed_b, sizeof(fixed_b), &len_str_b); + ptr_b, rawprop_b, fixed_b, sizeof(fixed_b), &len_str_b); /* TODO we could do a check on length too, * but then we would not have a 'real' string comparison... * Maybe behind a eRNAOverrideMatch flag? */ @@ -1712,16 +1724,13 @@ int rna_property_override_diff_default(Main *bmain, } case PROP_POINTER: { - if (STREQ(RNA_property_identifier(prop_a), "rna_type")) { + if (STREQ(prop_a->identifier, "rna_type")) { /* Dummy 'pass' answer, this is a meta-data and must be ignored... */ return 0; } else { - PointerRNA propptr_a = RNA_property_pointer_get(ptr_a, prop_a); - PointerRNA propptr_b = RNA_property_pointer_get(ptr_b, prop_b); - const bool no_ownership = (RNA_property_flag(prop_a) & PROP_PTR_NO_OWNERSHIP) != 0; - const bool no_prop_name = (RNA_property_override_flag(prop_a) & - PROPOVERRIDE_NO_PROP_NAME) != 0; + PointerRNA propptr_a = RNA_property_pointer_get(ptr_a, rawprop_a); + PointerRNA propptr_b = RNA_property_pointer_get(ptr_b, rawprop_b); return rna_property_override_diff_propptr(bmain, &propptr_a, &propptr_b, @@ -1742,14 +1751,6 @@ int rna_property_override_diff_default(Main *bmain, } case PROP_COLLECTION: { - /* Note: we assume we only insert in ptr_a (i.e. we can only get new items in ptr_a), - * and that we never remove anything. */ - const bool use_insertion = (RNA_property_override_flag(prop_a) & - PROPOVERRIDE_LIBRARY_INSERTION) && - do_create; - const bool no_ownership = (RNA_property_flag(prop_a) & PROP_PTR_NO_OWNERSHIP) != 0; - const bool no_prop_name = (RNA_property_override_flag(prop_a) & PROPOVERRIDE_NO_PROP_NAME) != - 0; bool equals = true; bool abort = false; bool is_first_insert = true; @@ -1757,8 +1758,8 @@ int rna_property_override_diff_default(Main *bmain, int idx_b = 0; CollectionPropertyIterator iter_a, iter_b; - RNA_property_collection_begin(ptr_a, prop_a, &iter_a); - RNA_property_collection_begin(ptr_b, prop_b, &iter_b); + RNA_property_collection_begin(ptr_a, rawprop_a, &iter_a); + RNA_property_collection_begin(ptr_b, rawprop_b, &iter_b); char buff_a[4096]; char buff_prev_a[4096] = {0}; @@ -1773,7 +1774,7 @@ int rna_property_override_diff_default(Main *bmain, do { bool is_id = false, is_null = false, is_type_diff = false; - is_valid_for_insertion = use_insertion; + is_valid_for_insertion = use_collection_insertion; /* If false, it means that the whole data itself is different, * so no point in going inside of it at all! */ @@ -1846,7 +1847,7 @@ int rna_property_override_diff_default(Main *bmain, /* Collections do not support replacement of their data (except for collections of ID * pointers), since they do not support removing, only in *some* cases, insertion. We * also assume then that _a data is the one where things are inserted. */ - if (is_valid_for_insertion && use_insertion) { + if (is_valid_for_insertion && use_collection_insertion) { bool created; IDOverrideLibraryProperty *op = BKE_lib_override_library_property_get( override, rna_path, &created); @@ -1930,7 +1931,7 @@ int rna_property_override_diff_default(Main *bmain, break; } - if (!(use_insertion && !(is_id || is_valid_for_diffing))) { + if (!(use_collection_insertion && !(is_id || is_valid_for_diffing))) { break; } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index d141e6f3b19..9b98be61cbf 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -6463,7 +6463,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna) prop = RNA_def_property(srna, "simplify_gpencil_shader_fx", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "simplify_gpencil", SIMPLIFY_GPENCIL_FX); - RNA_def_property_ui_text(prop, "ShadersFX", "Display Shader FX"); + RNA_def_property_ui_text(prop, "Shaders Effects", "Display Shader Effects"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); prop = RNA_def_property(srna, "simplify_gpencil_tint", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index e9c59fc5011..a66e20258d2 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -98,13 +98,13 @@ static void rna_Scene_frame_set(Scene *scene, Main *bmain, int frame, float subf } } -static void rna_Scene_uvedit_aspect(Scene *scene, Object *ob, float *aspect) +static void rna_Scene_uvedit_aspect(Scene *UNUSED(scene), Object *ob, float *aspect) { if ((ob->type == OB_MESH) && (ob->mode == OB_MODE_EDIT)) { BMEditMesh *em; em = BKE_editmesh_from_object(ob); if (EDBM_uv_check(em)) { - ED_uvedit_get_aspect(scene, ob, em->bm, aspect, aspect + 1); + ED_uvedit_get_aspect(ob, aspect, aspect + 1); return; } } diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c index 20ae69dc031..ea6421c8d11 100644 --- a/source/blender/makesrna/intern/rna_screen.c +++ b/source/blender/makesrna/intern/rna_screen.c @@ -90,6 +90,12 @@ static bool rna_Screen_is_animation_playing_get(PointerRNA *UNUSED(ptr)) return wm ? (ED_screen_animation_playing(wm) != NULL) : 0; } +static bool rna_Screen_is_scrubbing_get(PointerRNA *ptr) +{ + bScreen *screen = (bScreen *)ptr->data; + return screen->scrubbing; +} + static int rna_region_alignment_get(PointerRNA *ptr) { ARegion *region = ptr->data; @@ -548,6 +554,12 @@ static void rna_def_screen(BlenderRNA *brna) RNA_def_property_boolean_funcs(prop, "rna_Screen_is_animation_playing_get", NULL); RNA_def_property_ui_text(prop, "Animation Playing", "Animation playback is active"); + prop = RNA_def_property(srna, "is_scrubbing", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_boolean_funcs(prop, "rna_Screen_is_scrubbing_get", NULL); + RNA_def_property_ui_text( + prop, "User is Scrubbing", "True when the user is scrubbing through time"); + prop = RNA_def_property(srna, "is_temporary", PROP_BOOLEAN, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_boolean_sdna(prop, NULL, "temp", 1); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 7342879e9e6..de225e3c685 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -749,6 +749,25 @@ static IDProperty *rna_Sequence_idprops(PointerRNA *ptr, bool create) return seq->prop; } +static bool rna_MovieSequence_reload_if_needed(ID *scene_id, Sequence *seq, Main *bmain) +{ + Scene *scene = (Scene *)scene_id; + bool has_reloaded; + bool can_produce_frames; + + BKE_sequence_movie_reload_if_needed(bmain, scene, seq, &has_reloaded, &can_produce_frames); + + if (has_reloaded && can_produce_frames) { + BKE_sequence_calc(scene, seq); + BKE_sequence_invalidate_cache_raw(scene, seq); + + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); + WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene); + } + + return can_produce_frames; +} + static PointerRNA rna_MovieSequence_metadata_get(Sequence *seq) { if (seq == NULL || seq->anims.first == NULL) { @@ -2385,6 +2404,13 @@ static void rna_def_movie(BlenderRNA *brna) "rna_Sequence_filepath_set"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_filepath_update"); + func = RNA_def_function(srna, "reload_if_needed", "rna_MovieSequence_reload_if_needed"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN); + /* return type */ + parm = RNA_def_boolean( + func, "can_produce_frames", 0, "True if the strip can produce frames, False otherwise", ""); + RNA_def_function_return(func, parm); + /* metadata */ func = RNA_def_function(srna, "metadata", "rna_MovieSequence_metadata_get"); RNA_def_function_ui_description(func, "Retrieve metadata of the movie file"); diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index 277ef4d152f..4157747455d 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -79,7 +79,6 @@ static Sequence *alloc_generic_sequence( Editing *ed, const char *name, int frame_start, int channel, int type, const char *file) { Sequence *seq; - Strip *strip; StripElem *se; seq = BKE_sequence_alloc(ed->seqbasep, frame_start, channel, type); @@ -87,8 +86,7 @@ static Sequence *alloc_generic_sequence( BLI_strncpy(seq->name + 2, name, sizeof(seq->name) - 2); BKE_sequence_base_unique_name_recursive(&ed->seqbase, seq); - seq->strip = strip = MEM_callocN(sizeof(Strip), "strip"); - seq->strip->us = 1; + Strip *strip = seq->strip; if (file) { strip->stripdata = se = MEM_callocN(sizeof(StripElem), "stripelem"); @@ -207,33 +205,28 @@ static Sequence *rna_Sequences_new_image(ID *id, return seq; } -static Sequence *rna_Sequences_new_movie(ID *id, - Editing *ed, - ReportList *reports, - const char *name, - const char *file, - int channel, - int frame_start) +static Sequence *rna_Sequences_new_movie( + ID *id, Editing *ed, const char *name, const char *file, int channel, int frame_start) { Scene *scene = (Scene *)id; Sequence *seq; StripAnim *sanim; - struct anim *an = openanim(file, IB_rect, 0, NULL); + seq = alloc_generic_sequence(ed, name, frame_start, channel, SEQ_TYPE_MOVIE, file); + struct anim *an = openanim(file, IB_rect, 0, NULL); if (an == NULL) { - BKE_report(reports, RPT_ERROR, "Sequences.new_movie: unable to open movie file"); - return NULL; + /* Without anim, the strip gets duration 0, which makes it impossible to select in the UI. */ + seq->len = 1; } + else { + sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); + BLI_addtail(&seq->anims, sanim); + sanim->anim = an; - seq = alloc_generic_sequence(ed, name, frame_start, channel, SEQ_TYPE_MOVIE, file); - - sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); - BLI_addtail(&seq->anims, sanim); - sanim->anim = an; - - seq->anim_preseek = IMB_anim_get_preseek(an); - seq->len = IMB_anim_get_duration(an, IMB_TC_RECORD_RUN); + seq->anim_preseek = IMB_anim_get_preseek(an); + seq->len = IMB_anim_get_duration(an, IMB_TC_RECORD_RUN); + } BKE_sequence_calc_disp(scene, seq); BKE_sequence_invalidate_cache_composite(scene, seq); @@ -669,7 +662,7 @@ void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_function_return(func, parm); func = RNA_def_function(srna, "new_movie", "rna_Sequences_new_movie"); - RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_SELF_ID); + RNA_def_function_flag(func, FUNC_USE_SELF_ID); RNA_def_function_ui_description(func, "Add a new movie sequence"); parm = RNA_def_string(func, "name", "Name", 0, "", "Name for the new sequence"); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); diff --git a/source/blender/makesrna/intern/rna_shader_fx.c b/source/blender/makesrna/intern/rna_shader_fx.c index 80f3cab147c..e1970a57a91 100644 --- a/source/blender/makesrna/intern/rna_shader_fx.c +++ b/source/blender/makesrna/intern/rna_shader_fx.c @@ -168,7 +168,7 @@ static char *rna_ShaderFx_path(PointerRNA *ptr) static void rna_ShaderFx_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { DEG_id_tag_update(ptr->owner_id, ID_RECALC_GEOMETRY); - WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ptr->owner_id); + WM_main_add_notifier(NC_OBJECT | ND_SHADERFX, ptr->owner_id); } static void rna_ShaderFx_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr) @@ -220,7 +220,7 @@ static void rna_def_shader_fx_blur(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "radius"); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_text(prop, "Size", "Factor of Blur"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "samples", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "samples"); @@ -228,18 +228,18 @@ static void rna_def_shader_fx_blur(BlenderRNA *brna) RNA_def_property_ui_range(prop, 0, 32, 2, -1); RNA_def_property_int_default(prop, 4); RNA_def_property_ui_text(prop, "Samples", "Number of Blur Samples (zero, disable blur)"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "rotation"); RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); RNA_def_property_ui_text(prop, "Rotation", "Rotation of the effect"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "use_dof_mode", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_BLUR_DOF_MODE); RNA_def_property_ui_text(prop, "Use as Depth Of Field", "Blur using camera depth of field"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); } static void rna_def_shader_fx_colorize(BlenderRNA *brna) @@ -256,27 +256,27 @@ static void rna_def_shader_fx_colorize(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "factor"); RNA_def_property_range(prop, 0, 1.0); RNA_def_property_ui_text(prop, "Factor", "Mix factor"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "low_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "low_color"); RNA_def_property_array(prop, 4); RNA_def_property_ui_text(prop, "Low Color", "First color used for effect"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "high_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "high_color"); RNA_def_property_array(prop, 4); RNA_def_property_ui_text(prop, "High Color", "Second color used for effect"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "mode"); RNA_def_property_enum_items(prop, rna_enum_shaderfx_colorize_modes_items); RNA_def_property_ui_text(prop, "Mode", "Effect mode"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); } static void rna_def_shader_fx_wave(BlenderRNA *brna) @@ -298,25 +298,25 @@ static void rna_def_shader_fx_wave(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "orientation"); RNA_def_property_enum_items(prop, prop_shaderfx_wave_type_items); RNA_def_property_ui_text(prop, "Orientation", "Direction of the wave"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "amplitude", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "amplitude"); RNA_def_property_range(prop, 0, FLT_MAX); RNA_def_property_ui_text(prop, "Amplitude", "Amplitude of Wave"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "period", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "period"); RNA_def_property_range(prop, 0, FLT_MAX); RNA_def_property_ui_text(prop, "Period", "Period of Wave"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "phase", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "phase"); RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); RNA_def_property_ui_text(prop, "Phase", "Phase Shift of Wave"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); } static void rna_def_shader_fx_pixel(BlenderRNA *brna) @@ -334,12 +334,12 @@ static void rna_def_shader_fx_pixel(BlenderRNA *brna) RNA_def_property_range(prop, 1, SHRT_MAX); RNA_def_property_array(prop, 2); RNA_def_property_ui_text(prop, "Size", "Pixel size"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "use_antialiasing", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", FX_PIXEL_FILTER_NEAREST); RNA_def_property_ui_text(prop, "Antialiasing", "Antialiase pixels"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); } static void rna_def_shader_fx_rim(BlenderRNA *brna) @@ -356,34 +356,34 @@ static void rna_def_shader_fx_rim(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "offset"); RNA_def_property_range(prop, SHRT_MIN, SHRT_MAX); RNA_def_property_ui_text(prop, "Offset", "Offset of the rim"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "rim_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "rim_rgb"); RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Rim Color", "Color used for Rim"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "mask_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "mask_rgb"); RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Mask Color", "Color that must be kept"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "mode"); RNA_def_property_enum_items(prop, rna_enum_shaderfx_rim_modes_items); RNA_def_property_ui_text(prop, "Mode", "Blend mode"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "blur", PROP_INT, PROP_PIXEL); RNA_def_property_int_sdna(prop, NULL, "blur"); RNA_def_property_range(prop, 0, SHRT_MAX); RNA_def_property_ui_text( prop, "Blur", "Number of pixels for blurring rim (set to 0 to disable)"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "samples", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "samples"); @@ -391,7 +391,7 @@ static void rna_def_shader_fx_rim(BlenderRNA *brna) RNA_def_property_ui_range(prop, 0, 32, 2, -1); RNA_def_property_int_default(prop, 4); RNA_def_property_ui_text(prop, "Samples", "Number of Blur Samples (zero, disable blur)"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); } static void rna_def_shader_fx_shadow(BlenderRNA *brna) @@ -420,58 +420,58 @@ static void rna_def_shader_fx_shadow(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "offset"); RNA_def_property_range(prop, SHRT_MIN, SHRT_MAX); RNA_def_property_ui_text(prop, "Offset", "Offset of the shadow"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_XYZ); RNA_def_property_float_sdna(prop, NULL, "scale"); RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); RNA_def_property_ui_text(prop, "Scale", "Offset of the shadow"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "shadow_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "shadow_rgba"); RNA_def_property_array(prop, 4); RNA_def_property_ui_text(prop, "Shadow Color", "Color used for Shadow"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "orientation", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "orientation"); RNA_def_property_enum_items(prop, prop_shaderfx_shadow_type_items); RNA_def_property_ui_text(prop, "Orientation", "Direction of the wave"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "amplitude", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "amplitude"); RNA_def_property_range(prop, 0, FLT_MAX); RNA_def_property_ui_text(prop, "Amplitude", "Amplitude of Wave"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "period", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "period"); RNA_def_property_range(prop, 0, FLT_MAX); RNA_def_property_ui_text(prop, "Period", "Period of Wave"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "phase", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "phase"); RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); RNA_def_property_ui_text(prop, "Phase", "Phase Shift of Wave"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "rotation"); RNA_def_property_range(prop, DEG2RAD(-360), DEG2RAD(360)); RNA_def_property_ui_range(prop, DEG2RAD(-360), DEG2RAD(360), 5, 2); RNA_def_property_ui_text(prop, "Rotation", "Rotation around center or object"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "blur", PROP_INT, PROP_PIXEL); RNA_def_property_int_sdna(prop, NULL, "blur"); RNA_def_property_range(prop, 0, SHRT_MAX); RNA_def_property_ui_text( prop, "Blur", "Number of pixels for blurring shadow (set to 0 to disable)"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "samples", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "samples"); @@ -479,17 +479,17 @@ static void rna_def_shader_fx_shadow(BlenderRNA *brna) RNA_def_property_ui_range(prop, 0, 32, 2, -1); RNA_def_property_int_default(prop, 4); RNA_def_property_ui_text(prop, "Samples", "Number of Blur Samples (zero, disable blur)"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "use_object", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_SHADOW_USE_OBJECT); RNA_def_property_ui_text(prop, "Use Object", "Use object as center of rotation"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "use_wave", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_SHADOW_USE_WAVE); RNA_def_property_ui_text(prop, "Wave", "Use wave effect"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); } static void rna_def_shader_fx_glow(BlenderRNA *brna) @@ -507,41 +507,41 @@ static void rna_def_shader_fx_glow(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "glow_color"); RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Glow Color", "Color used for generated glow"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "opacity", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "glow_color[3]"); RNA_def_property_range(prop, 0.0, 1.0f); RNA_def_property_ui_text(prop, "Opacity", "Effect Opacity"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "select_color", PROP_FLOAT, PROP_COLOR); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "select_color"); RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Select Color", "Color selected to apply glow"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "mode"); RNA_def_property_enum_items(prop, rna_enum_shaderfx_glow_modes_items); RNA_def_property_ui_text(prop, "Mode", "Glow mode"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "threshold", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "threshold"); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 3); RNA_def_property_ui_text(prop, "Threshold", "Limit to select color for glow effect"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); /* Use blur fields to make compatible with blur filter */ prop = RNA_def_property(srna, "size", PROP_FLOAT, PROP_XYZ); RNA_def_property_float_sdna(prop, NULL, "blur"); RNA_def_property_range(prop, 0.0f, FLT_MAX); RNA_def_property_ui_text(prop, "Size", "Size of the effect"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "samples", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "samples"); @@ -549,26 +549,26 @@ static void rna_def_shader_fx_glow(BlenderRNA *brna) RNA_def_property_ui_range(prop, 1, 32, 2, -1); RNA_def_property_int_default(prop, 4); RNA_def_property_ui_text(prop, "Samples", "Number of Blur Samples"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "use_glow_under", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_GLOW_USE_ALPHA); RNA_def_property_ui_text( prop, "Glow Under", "Glow only areas with alpha (not supported with Regular blend mode)"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "rotation"); RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); RNA_def_property_ui_text(prop, "Rotation", "Rotation of the effect"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); /* blend mode */ prop = RNA_def_property(srna, "blend_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "blend_mode"); RNA_def_property_enum_items(prop, rna_enum_glow_blend_modes_items); RNA_def_property_ui_text(prop, "Blend Mode", "Blend mode"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); } static void rna_def_shader_fx_swirl(BlenderRNA *brna) @@ -585,19 +585,19 @@ static void rna_def_shader_fx_swirl(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "radius"); RNA_def_property_range(prop, 0, SHRT_MAX); RNA_def_property_ui_text(prop, "Radius", "Radius to apply"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "angle", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "angle"); RNA_def_property_range(prop, DEG2RAD(-5 * 360), DEG2RAD(5 * 360)); RNA_def_property_ui_range(prop, DEG2RAD(-5 * 360), DEG2RAD(5 * 360), 5, 2); RNA_def_property_ui_text(prop, "Angle", "Angle of rotation"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "use_transparent", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_SWIRL_MAKE_TRANSPARENT); RNA_def_property_ui_text(prop, "Transparent", "Make image transparent outside of radius"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); RNA_def_property_ui_text(prop, "Object", "Object to determine center location"); @@ -620,12 +620,12 @@ static void rna_def_shader_fx_flip(BlenderRNA *brna) prop = RNA_def_property(srna, "flip_horizontal", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_FLIP_HORIZONTAL); RNA_def_property_ui_text(prop, "Horizontal", "Flip image horizontally"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); prop = RNA_def_property(srna, "flip_vertical", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", FX_FLIP_VERTICAL); RNA_def_property_ui_text(prop, "Vertical", "Flip image vertically"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); } void RNA_def_shader_fx(BlenderRNA *brna) @@ -644,7 +644,7 @@ void RNA_def_shader_fx(BlenderRNA *brna) prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); RNA_def_property_string_funcs(prop, NULL, NULL, "rna_ShaderFx_name_set"); RNA_def_property_ui_text(prop, "Name", "Effect name"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER | NA_RENAME, NULL); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX | NA_RENAME, NULL); RNA_def_struct_name_property(srna, prop); /* enums */ @@ -661,7 +661,7 @@ void RNA_def_shader_fx(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Realtime", "Display effect in viewport"); RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_ON, 1); prop = RNA_def_property(srna, "show_render", PROP_BOOLEAN, PROP_NONE); @@ -669,12 +669,12 @@ void RNA_def_shader_fx(BlenderRNA *brna) RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Render", "Use effect during render"); RNA_def_property_ui_icon(prop, ICON_RESTRICT_RENDER_ON, 1); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, NULL); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, NULL); prop = RNA_def_property(srna, "show_in_editmode", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "mode", eShaderFxMode_Editmode); RNA_def_property_ui_text(prop, "Edit Mode", "Display effect in Edit mode"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_ShaderFx_update"); + RNA_def_property_update(prop, NC_OBJECT | ND_SHADERFX, "rna_ShaderFx_update"); RNA_def_property_ui_icon(prop, ICON_EDITMODE_HLT, 0); prop = RNA_def_property(srna, "show_expanded", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index c31b313d827..956fb65054b 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -3973,12 +3973,13 @@ static void rna_def_userdef_studiolights(BlenderRNA *brna) func = RNA_def_function(srna, "new", "rna_StudioLights_new"); RNA_def_function_ui_description(func, "Create studiolight from default lighting"); - parm = RNA_def_string(func, - "path", - NULL, - 0, - "Path", - "Path to the file that will contain the lighing info (without extension)"); + parm = RNA_def_string( + func, + "path", + NULL, + 0, + "Path", + "Path to the file that will contain the lighting info (without extension)"); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_pointer(func, "studio_light", "StudioLight", "", "Newly created StudioLight"); RNA_def_function_return(func, parm); @@ -6084,6 +6085,10 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "use_cycles_debug", 1); RNA_def_property_ui_text(prop, "Cycles Debug", "Enable Cycles debugging options for developers"); RNA_def_property_update(prop, 0, "rna_userdef_update"); + + prop = RNA_def_property(srna, "use_sculpt_vertex_colors", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_sculpt_vertex_colors", 1); + RNA_def_property_ui_text(prop, "Sculpt Vertex Colors", "Use the new Vertex Painting system"); } static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop) diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 80a1ebab64e..cf87e3598b6 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -23,9 +23,9 @@ set(INC intern ../blenfont ../blenkernel - ../blentranslation ../blenlib ../blenloader + ../blentranslation ../bmesh ../depsgraph ../editors/include diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c index f6ad25ce282..4e46e135b72 100644 --- a/source/blender/modifiers/intern/MOD_explode.c +++ b/source/blender/modifiers/intern/MOD_explode.c @@ -254,7 +254,7 @@ static void remap_faces_3_6_9_12(Mesh *mesh, Mesh *split, MFace *mf, int *facepa, - int *vertpa, + const int *vertpa, int i, EdgeHash *eh, int cur, @@ -322,7 +322,7 @@ static void remap_faces_5_10(Mesh *mesh, Mesh *split, MFace *mf, int *facepa, - int *vertpa, + const int *vertpa, int i, EdgeHash *eh, int cur, @@ -378,7 +378,7 @@ static void remap_faces_15(Mesh *mesh, Mesh *split, MFace *mf, int *facepa, - int *vertpa, + const int *vertpa, int i, EdgeHash *eh, int cur, @@ -462,7 +462,7 @@ static void remap_faces_7_11_13_14(Mesh *mesh, Mesh *split, MFace *mf, int *facepa, - int *vertpa, + const int *vertpa, int i, EdgeHash *eh, int cur, @@ -531,7 +531,7 @@ static void remap_faces_19_21_22(Mesh *mesh, Mesh *split, MFace *mf, int *facepa, - int *vertpa, + const int *vertpa, int i, EdgeHash *eh, int cur, @@ -585,7 +585,7 @@ static void remap_faces_23(Mesh *mesh, Mesh *split, MFace *mf, int *facepa, - int *vertpa, + const int *vertpa, int i, EdgeHash *eh, int cur, diff --git a/source/blender/modifiers/intern/MOD_simulation.cc b/source/blender/modifiers/intern/MOD_simulation.cc index 85eb66cd826..69a7bfd91ac 100644 --- a/source/blender/modifiers/intern/MOD_simulation.cc +++ b/source/blender/modifiers/intern/MOD_simulation.cc @@ -29,6 +29,7 @@ #include "BLI_float3.hh" #include "BLI_listbase.h" +#include "BLI_string.h" #include "BLI_utildefines.h" #include "DNA_mesh_types.h" @@ -47,6 +48,8 @@ #include "BKE_pointcloud.h" #include "BKE_simulation.h" +#include "BLO_read_write.h" + /* SpaceType struct has a member called 'new' which obviously conflicts with C++ * so temporarily redefining the new keyword to make it compile. */ #define new extern_new @@ -93,9 +96,14 @@ static const ParticleSimulationState *find_particle_state(SimulationModifierData if (smd->simulation == nullptr) { return nullptr; } + if (smd->data_path == nullptr) { + return nullptr; + } LISTBASE_FOREACH (const SimulationState *, state, &smd->simulation->states) { - if (state->type == SIM_STATE_TYPE_PARTICLES) { - return (ParticleSimulationState *)state; + if (STREQ(smd->data_path, state->name)) { + if (state->type == SIM_STATE_TYPE_PARTICLES) { + return (ParticleSimulationState *)state; + } } } return nullptr; @@ -121,7 +129,7 @@ static PointCloud *modifyPointCloud(ModifierData *md, memcpy(pointcloud->co, positions, sizeof(float3) * state->tot_particles); for (int i = 0; i < state->tot_particles; i++) { - pointcloud->radius[i] = 0.05f; + pointcloud->radius[i] = 0.1f; } return pointcloud; @@ -146,6 +154,37 @@ static void panelRegister(ARegionType *region_type) modifier_panel_register(region_type, eModifierType_Simulation, panel_draw); } +static void blendWrite(BlendWriter *writer, const ModifierData *md) +{ + const SimulationModifierData *smd = (const SimulationModifierData *)md; + BLO_write_string(writer, smd->data_path); +} + +static void blendRead(BlendDataReader *reader, ModifierData *md) +{ + SimulationModifierData *smd = (SimulationModifierData *)md; + BLO_read_data_address(reader, &smd->data_path); +} + +static void copyData(const ModifierData *md, ModifierData *target, const int flag) +{ + const SimulationModifierData *smd = (const SimulationModifierData *)md; + SimulationModifierData *tsmd = (SimulationModifierData *)target; + + BKE_modifier_copydata_generic(md, target, flag); + if (smd->data_path != nullptr) { + tsmd->data_path = BLI_strdup(smd->data_path); + } +} + +static void freeData(ModifierData *md) +{ + SimulationModifierData *smd = (SimulationModifierData *)md; + if (smd->data_path) { + MEM_freeN(smd->data_path); + } +} + ModifierTypeInfo modifierType_Simulation = { /* name */ "Simulation", /* structName */ "SimulationModifierData", @@ -153,7 +192,7 @@ ModifierTypeInfo modifierType_Simulation = { /* type */ eModifierTypeType_None, /* flags */ (ModifierTypeFlag)0, - /* copyData */ BKE_modifier_copydata_generic, + /* copyData */ copyData, /* deformVerts */ NULL, /* deformMatrices */ NULL, @@ -166,7 +205,7 @@ ModifierTypeInfo modifierType_Simulation = { /* initData */ NULL, /* requiredDataMask */ NULL, - /* freeData */ NULL, + /* freeData */ freeData, /* isDisabled */ isDisabled, /* updateDepsgraph */ updateDepsgraph, /* dependsOnTime */ NULL, @@ -176,6 +215,6 @@ ModifierTypeInfo modifierType_Simulation = { /* foreachTexLink */ NULL, /* freeRuntimeData */ NULL, /* panelRegister */ panelRegister, - /* blendWrite */ NULL, - /* blendRead */ NULL, + /* blendWrite */ blendWrite, + /* blendRead */ blendRead, }; diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c index a56194354f8..7e5e4ecd9d3 100644 --- a/source/blender/modifiers/intern/MOD_solidify_extrude.c +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -284,7 +284,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex new_edge_arr = MEM_malloc_arrayN(((numEdges * 2) + numVerts), sizeof(*new_edge_arr), __func__); edge_users = MEM_malloc_arrayN(numEdges, sizeof(*edge_users), "solid_mod edges"); - edge_order = MEM_malloc_arrayN(numEdges, sizeof(*edge_order), "solid_mod eorder"); + edge_order = MEM_malloc_arrayN(numEdges, sizeof(*edge_order), "solid_mod order"); /* save doing 2 loops here... */ #if 0 diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c index 137f52782a9..01b9e972086 100644 --- a/source/blender/modifiers/intern/MOD_ui_common.c +++ b/source/blender/modifiers/intern/MOD_ui_common.c @@ -231,12 +231,19 @@ static void modifier_ops_extra_draw(bContext *C, uiLayout *layout, void *md_v) /* Apply as shapekey. */ if (BKE_modifier_is_same_topology(md) && !BKE_modifier_is_non_geometrical(md)) { - uiItemEnumO(layout, - "OBJECT_OT_modifier_apply", - CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply As Shapekey"), - ICON_SHAPEKEY_DATA, - "apply_as", - MODIFIER_APPLY_SHAPE); + uiItemBooleanO(layout, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply As Shapekey"), + ICON_SHAPEKEY_DATA, + "OBJECT_OT_modifier_apply_as_shapekey", + "keep_modifier", + false); + + uiItemBooleanO(layout, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Save As Shapekey"), + ICON_SHAPEKEY_DATA, + "OBJECT_OT_modifier_apply_as_shapekey", + "keep_modifier", + true); } /* Duplicate. */ diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 31b5e922dab..2381e499eee 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -30,6 +30,7 @@ set(INC ../blenlib ../blentranslation ../depsgraph + ../functions ../gpu ../imbuf ../makesdna @@ -37,6 +38,7 @@ set(INC ../render/extern/include ../../../intern/glew-mx ../../../intern/guardedalloc + ../../../intern/sky/include ) set(INC_SYS @@ -176,7 +178,7 @@ set(SRC shader/nodes/node_shader_light_path.c shader/nodes/node_shader_map_range.c shader/nodes/node_shader_mapping.c - shader/nodes/node_shader_math.c + shader/nodes/node_shader_math.cc shader/nodes/node_shader_mixRgb.c shader/nodes/node_shader_mix_shader.c shader/nodes/node_shader_normal.c @@ -192,7 +194,7 @@ set(SRC shader/nodes/node_shader_script.c shader/nodes/node_shader_sepcombHSV.c shader/nodes/node_shader_sepcombRGB.c - shader/nodes/node_shader_sepcombXYZ.c + shader/nodes/node_shader_sepcombXYZ.cc shader/nodes/node_shader_shaderToRgb.c shader/nodes/node_shader_squeeze.c shader/nodes/node_shader_subsurface_scattering.c @@ -214,10 +216,10 @@ set(SRC shader/nodes/node_shader_uvAlongStroke.c shader/nodes/node_shader_uvmap.c shader/nodes/node_shader_valToRgb.c - shader/nodes/node_shader_value.c + shader/nodes/node_shader_value.cc shader/nodes/node_shader_vectTransform.c shader/nodes/node_shader_vector_displacement.c - shader/nodes/node_shader_vector_math.c + shader/nodes/node_shader_vector_math.cc shader/nodes/node_shader_vector_rotate.c shader/nodes/node_shader_vertex_color.c shader/nodes/node_shader_volume_absorption.c @@ -277,7 +279,7 @@ set(SRC intern/node_util.c composite/node_composite_util.h - function/node_function_util.h + function/node_function_util.hh shader/node_shader_util.h simulation/node_simulation_util.h texture/node_texture_util.h @@ -296,6 +298,8 @@ set(SRC ) set(LIB + bf_functions + bf_intern_sky ) if(WITH_PYTHON) diff --git a/source/blender/nodes/function/node_function_util.cc b/source/blender/nodes/function/node_function_util.cc index 0927ba335fe..342c330a8fa 100644 --- a/source/blender/nodes/function/node_function_util.cc +++ b/source/blender/nodes/function/node_function_util.cc @@ -14,7 +14,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "node_function_util.h" +#include "node_function_util.hh" #include "node_util.h" bool fn_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree) diff --git a/source/blender/nodes/function/node_function_util.h b/source/blender/nodes/function/node_function_util.hh index 85e252f9bdd..938cb5dd593 100644 --- a/source/blender/nodes/function/node_function_util.h +++ b/source/blender/nodes/function/node_function_util.hh @@ -19,6 +19,7 @@ #include <string.h> +#include "BLI_float3.hh" #include "BLI_utildefines.h" #include "MEM_guardedalloc.h" @@ -26,6 +27,7 @@ #include "DNA_node_types.h" #include "BKE_node.h" +#include "BKE_node_tree_multi_function.hh" #include "BLT_translation.h" @@ -33,6 +35,8 @@ #include "node_util.h" +#include "FN_multi_function_builder.hh" + void fn_node_type_base( struct bNodeType *ntype, int type, const char *name, short nclass, short flag); bool fn_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree); diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc index 615ad4c6733..3a145311a08 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -19,7 +19,7 @@ #include "RNA_enum_types.h" -#include "node_function_util.h" +#include "node_function_util.hh" static bNodeSocketTemplate fn_node_boolean_math_in[] = { {SOCK_BOOLEAN, N_("Boolean")}, @@ -50,6 +50,33 @@ static void node_boolean_math_label(bNodeTree *UNUSED(ntree), bNode *node, char BLI_strncpy(label, IFACE_(name), maxlen); } +static const blender::fn::MultiFunction &get_multi_function(bNode &bnode) +{ + static blender::fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{ + "And", [](bool a, bool b) { return a && b; }}; + static blender::fn::CustomMF_SI_SI_SO<bool, bool, bool> or_fn{ + "Or", [](bool a, bool b) { return a || b; }}; + static blender::fn::CustomMF_SI_SO<bool, bool> not_fn{"Not", [](bool a) { return !a; }}; + + switch (bnode.custom1) { + case NODE_BOOLEAN_MATH_AND: + return and_fn; + case NODE_BOOLEAN_MATH_OR: + return or_fn; + case NODE_BOOLEAN_MATH_NOT: + return not_fn; + } + + BLI_assert(false); + return blender::fn::dummy_multi_function; +} + +static void node_boolean_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder) +{ + const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode()); + builder.set_matching_fn(fn); +} + void register_node_type_fn_boolean_math() { static bNodeType ntype; @@ -58,5 +85,6 @@ void register_node_type_fn_boolean_math() node_type_socket_templates(&ntype, fn_node_boolean_math_in, fn_node_boolean_math_out); node_type_label(&ntype, node_boolean_math_label); node_type_update(&ntype, node_boolean_math_update); + ntype.expand_in_mf_network = node_boolean_expand_in_mf_network; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_combine_strings.cc b/source/blender/nodes/function/nodes/node_fn_combine_strings.cc index 1b6091451d9..a880933bc12 100644 --- a/source/blender/nodes/function/nodes/node_fn_combine_strings.cc +++ b/source/blender/nodes/function/nodes/node_fn_combine_strings.cc @@ -1,4 +1,20 @@ -#include "node_function_util.h" +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_function_util.hh" static bNodeSocketTemplate fn_node_combine_strings_in[] = { {SOCK_STRING, N_("A")}, @@ -11,11 +27,20 @@ static bNodeSocketTemplate fn_node_combine_strings_out[] = { {-1, ""}, }; +static void fn_node_combine_strings_expand_in_mf_network( + blender::bke::NodeMFNetworkBuilder &builder) +{ + static blender::fn::CustomMF_SI_SI_SO<std::string, std::string, std::string> combine_fn{ + "Combine Strings", [](const std::string &a, const std::string &b) { return a + b; }}; + builder.set_matching_fn(combine_fn); +} + void register_node_type_fn_combine_strings() { static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_COMBINE_STRINGS, "Combine Strings", 0, 0); node_type_socket_templates(&ntype, fn_node_combine_strings_in, fn_node_combine_strings_out); + ntype.expand_in_mf_network = fn_node_combine_strings_expand_in_mf_network; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc index 9788402850b..fb2c4d88caf 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc @@ -14,12 +14,14 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include <cmath> + #include "BLI_listbase.h" #include "BLI_string.h" #include "RNA_enum_types.h" -#include "node_function_util.h" +#include "node_function_util.hh" static bNodeSocketTemplate fn_node_float_compare_in[] = { {SOCK_FLOAT, N_("A"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f}, @@ -54,6 +56,46 @@ static void node_float_compare_label(bNodeTree *UNUSED(ntree), BLI_strncpy(label, IFACE_(name), maxlen); } +static const blender::fn::MultiFunction &get_multi_function(bNode &node) +{ + static blender::fn::CustomMF_SI_SI_SO<float, float, bool> less_than_fn{ + "Less Than", [](float a, float b) { return a < b; }}; + static blender::fn::CustomMF_SI_SI_SO<float, float, bool> less_equal_fn{ + "Less Equal", [](float a, float b) { return a <= b; }}; + static blender::fn::CustomMF_SI_SI_SO<float, float, bool> greater_than_fn{ + "Greater Than", [](float a, float b) { return a > b; }}; + static blender::fn::CustomMF_SI_SI_SO<float, float, bool> greater_equal_fn{ + "Greater Equal", [](float a, float b) { return a >= b; }}; + static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, bool> equal_fn{ + "Equal", [](float a, float b, float epsilon) { return std::abs(a - b) <= epsilon; }}; + static blender::fn::CustomMF_SI_SI_SI_SO<float, float, float, bool> not_equal_fn{ + "Not Equal", [](float a, float b, float epsilon) { return std::abs(a - b) > epsilon; }}; + + switch (node.custom1) { + case NODE_FLOAT_COMPARE_LESS_THAN: + return less_than_fn; + case NODE_FLOAT_COMPARE_LESS_EQUAL: + return less_equal_fn; + case NODE_FLOAT_COMPARE_GREATER_THAN: + return greater_than_fn; + case NODE_FLOAT_COMPARE_GREATER_EQUAL: + return greater_equal_fn; + case NODE_FLOAT_COMPARE_EQUAL: + return equal_fn; + case NODE_FLOAT_COMPARE_NOT_EQUAL: + return not_equal_fn; + } + + BLI_assert(false); + return blender::fn::dummy_multi_function; +} + +static void node_float_compare_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder) +{ + const blender::fn::MultiFunction &fn = get_multi_function(builder.bnode()); + builder.set_matching_fn(fn); +} + void register_node_type_fn_float_compare() { static bNodeType ntype; @@ -62,5 +104,6 @@ void register_node_type_fn_float_compare() node_type_socket_templates(&ntype, fn_node_float_compare_in, fn_node_float_compare_out); node_type_label(&ntype, node_float_compare_label); node_type_update(&ntype, node_float_compare_update); + ntype.expand_in_mf_network = node_float_compare_expand_in_mf_network; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_group_instance_id.cc b/source/blender/nodes/function/nodes/node_fn_group_instance_id.cc index 2ac86ee2407..c61c941ee0d 100644 --- a/source/blender/nodes/function/nodes/node_fn_group_instance_id.cc +++ b/source/blender/nodes/function/nodes/node_fn_group_instance_id.cc @@ -1,15 +1,45 @@ -#include "node_function_util.h" +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_function_util.hh" static bNodeSocketTemplate fn_node_group_instance_id_out[] = { {SOCK_STRING, N_("Identifier")}, {-1, ""}, }; +static void fn_node_group_instance_id_expand_in_mf_network( + blender::bke::NodeMFNetworkBuilder &builder) +{ + const blender::bke::DNode &node = builder.dnode(); + std::string id = "/"; + for (const blender::bke::DParentNode *parent = node.parent(); parent; + parent = parent->parent()) { + id = "/" + parent->node_ref().name() + id; + } + builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<std::string>>( + std::move(id)); +} + void register_node_type_fn_group_instance_id() { static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_GROUP_INSTANCE_ID, "Group Instance ID", 0, 0); node_type_socket_templates(&ntype, nullptr, fn_node_group_instance_id_out); + ntype.expand_in_mf_network = fn_node_group_instance_id_expand_in_mf_network; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_switch.cc b/source/blender/nodes/function/nodes/node_fn_switch.cc index cb721058875..281ddb05c76 100644 --- a/source/blender/nodes/function/nodes/node_fn_switch.cc +++ b/source/blender/nodes/function/nodes/node_fn_switch.cc @@ -15,7 +15,7 @@ */ #include "BLI_listbase.h" -#include "node_function_util.h" +#include "node_function_util.hh" static bNodeSocketTemplate fn_node_switch_in[] = { {SOCK_BOOLEAN, N_("Switch")}, diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index b23511c3bdb..02124465dda 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -25,6 +25,8 @@ #include "DNA_node_types.h" +#include "BLI_color.hh" +#include "BLI_float3.hh" #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_string.h" @@ -32,6 +34,7 @@ #include "BKE_lib_id.h" #include "BKE_node.h" +#include "BKE_node_tree_multi_function.hh" #include "RNA_access.h" #include "RNA_types.h" @@ -510,35 +513,105 @@ static bNodeSocketType *make_socket_type_control_flow(int type) return stype; } +static bNodeSocketType *make_socket_type_bool() +{ + bNodeSocketType *socktype = make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE); + socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<bool>(); }; + socktype->expand_in_mf_network = [](blender::bke::SocketMFNetworkBuilder &builder) { + bool value = builder.socket_default_value<bNodeSocketValueBoolean>()->value; + builder.set_constant_value(value); + }; + return socktype; +} + +static bNodeSocketType *make_socket_type_float(PropertySubType subtype) +{ + bNodeSocketType *socktype = make_standard_socket_type(SOCK_FLOAT, subtype); + socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<float>(); }; + socktype->expand_in_mf_network = [](blender::bke::SocketMFNetworkBuilder &builder) { + float value = builder.socket_default_value<bNodeSocketValueFloat>()->value; + builder.set_constant_value(value); + }; + return socktype; +} + +static bNodeSocketType *make_socket_type_int(PropertySubType subtype) +{ + bNodeSocketType *socktype = make_standard_socket_type(SOCK_INT, subtype); + socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<int>(); }; + socktype->expand_in_mf_network = [](blender::bke::SocketMFNetworkBuilder &builder) { + int value = builder.socket_default_value<bNodeSocketValueInt>()->value; + builder.set_constant_value(value); + }; + return socktype; +} + +static bNodeSocketType *make_socket_type_vector(PropertySubType subtype) +{ + bNodeSocketType *socktype = make_standard_socket_type(SOCK_VECTOR, subtype); + socktype->get_mf_data_type = []() { + return blender::fn::MFDataType::ForSingle<blender::float3>(); + }; + socktype->expand_in_mf_network = [](blender::bke::SocketMFNetworkBuilder &builder) { + blender::float3 value = builder.socket_default_value<bNodeSocketValueVector>()->value; + builder.set_constant_value(value); + }; + return socktype; +} + +static bNodeSocketType *make_socket_type_rgba() +{ + bNodeSocketType *socktype = make_standard_socket_type(SOCK_RGBA, PROP_NONE); + socktype->get_mf_data_type = []() { + return blender::fn::MFDataType::ForSingle<blender::Color4f>(); + }; + socktype->expand_in_mf_network = [](blender::bke::SocketMFNetworkBuilder &builder) { + blender::Color4f value = builder.socket_default_value<bNodeSocketValueRGBA>()->value; + builder.set_constant_value(value); + }; + return socktype; +} + +static bNodeSocketType *make_socket_type_string() +{ + bNodeSocketType *socktype = make_standard_socket_type(SOCK_STRING, PROP_NONE); + socktype->get_mf_data_type = []() { return blender::fn::MFDataType::ForSingle<std::string>(); }; + socktype->expand_in_mf_network = [](blender::bke::SocketMFNetworkBuilder &builder) { + std::string value = builder.socket_default_value<bNodeSocketValueString>()->value; + builder.set_constant_value(value); + }; + return socktype; +} + void register_standard_node_socket_types(void) { /* draw callbacks are set in drawnode.c to avoid bad-level calls */ - nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_NONE)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_UNSIGNED)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_PERCENTAGE)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_FACTOR)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_ANGLE)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_FLOAT, PROP_TIME)); + nodeRegisterSocketType(make_socket_type_float(PROP_NONE)); + nodeRegisterSocketType(make_socket_type_float(PROP_UNSIGNED)); + nodeRegisterSocketType(make_socket_type_float(PROP_PERCENTAGE)); + nodeRegisterSocketType(make_socket_type_float(PROP_FACTOR)); + nodeRegisterSocketType(make_socket_type_float(PROP_ANGLE)); + nodeRegisterSocketType(make_socket_type_float(PROP_TIME)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_INT, PROP_NONE)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_INT, PROP_UNSIGNED)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_INT, PROP_PERCENTAGE)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_INT, PROP_FACTOR)); + nodeRegisterSocketType(make_socket_type_int(PROP_NONE)); + nodeRegisterSocketType(make_socket_type_int(PROP_UNSIGNED)); + nodeRegisterSocketType(make_socket_type_int(PROP_PERCENTAGE)); + nodeRegisterSocketType(make_socket_type_int(PROP_FACTOR)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_BOOLEAN, PROP_NONE)); + nodeRegisterSocketType(make_socket_type_bool()); - nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_NONE)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_TRANSLATION)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_DIRECTION)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_VELOCITY)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_ACCELERATION)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_EULER)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_VECTOR, PROP_XYZ)); + nodeRegisterSocketType(make_socket_type_vector(PROP_NONE)); + nodeRegisterSocketType(make_socket_type_vector(PROP_TRANSLATION)); + nodeRegisterSocketType(make_socket_type_vector(PROP_DIRECTION)); + nodeRegisterSocketType(make_socket_type_vector(PROP_VELOCITY)); + nodeRegisterSocketType(make_socket_type_vector(PROP_ACCELERATION)); + nodeRegisterSocketType(make_socket_type_vector(PROP_EULER)); + nodeRegisterSocketType(make_socket_type_vector(PROP_XYZ)); - nodeRegisterSocketType(make_standard_socket_type(SOCK_RGBA, PROP_NONE)); + nodeRegisterSocketType(make_socket_type_rgba()); - nodeRegisterSocketType(make_standard_socket_type(SOCK_STRING, PROP_NONE)); + nodeRegisterSocketType(make_socket_type_string()); nodeRegisterSocketType(make_standard_socket_type(SOCK_SHADER, PROP_NONE)); diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h index fbb9979cdfa..fc262544b4f 100644 --- a/source/blender/nodes/shader/node_shader_util.h +++ b/source/blender/nodes/shader/node_shader_util.h @@ -70,6 +70,12 @@ #include "GPU_uniformbuffer.h" #ifdef __cplusplus +# include "FN_multi_function_builder.hh" + +# include "BKE_node_tree_multi_function.hh" + +# include "BLI_float3.hh" + extern "C" { #endif diff --git a/source/blender/nodes/shader/nodes/node_shader_math.c b/source/blender/nodes/shader/nodes/node_shader_math.c deleted file mode 100644 index 8abebbf5081..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_math.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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) 2005 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup shdnodes - */ - -#include "node_shader_util.h" - -/* **************** SCALAR MATH ******************** */ -static bNodeSocketTemplate sh_node_math_in[] = { - {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Value"), 0.0f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {-1, ""}}; - -static bNodeSocketTemplate sh_node_math_out[] = {{SOCK_FLOAT, N_("Value")}, {-1, ""}}; - -static int gpu_shader_math(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - static const char *names[] = { - [NODE_MATH_ADD] = "math_add", - [NODE_MATH_SUBTRACT] = "math_subtract", - [NODE_MATH_MULTIPLY] = "math_multiply", - [NODE_MATH_DIVIDE] = "math_divide", - [NODE_MATH_MULTIPLY_ADD] = "math_multiply_add", - - [NODE_MATH_POWER] = "math_power", - [NODE_MATH_LOGARITHM] = "math_logarithm", - [NODE_MATH_EXPONENT] = "math_exponent", - [NODE_MATH_SQRT] = "math_sqrt", - [NODE_MATH_INV_SQRT] = "math_inversesqrt", - [NODE_MATH_ABSOLUTE] = "math_absolute", - [NODE_MATH_RADIANS] = "math_radians", - [NODE_MATH_DEGREES] = "math_degrees", - - [NODE_MATH_MINIMUM] = "math_minimum", - [NODE_MATH_MAXIMUM] = "math_maximum", - [NODE_MATH_LESS_THAN] = "math_less_than", - [NODE_MATH_GREATER_THAN] = "math_greater_than", - [NODE_MATH_SIGN] = "math_sign", - [NODE_MATH_COMPARE] = "math_compare", - [NODE_MATH_SMOOTH_MIN] = "math_smoothmin", - [NODE_MATH_SMOOTH_MAX] = "math_smoothmax", - - [NODE_MATH_ROUND] = "math_round", - [NODE_MATH_FLOOR] = "math_floor", - [NODE_MATH_CEIL] = "math_ceil", - [NODE_MATH_FRACTION] = "math_fraction", - [NODE_MATH_MODULO] = "math_modulo", - [NODE_MATH_TRUNC] = "math_trunc", - [NODE_MATH_SNAP] = "math_snap", - [NODE_MATH_WRAP] = "math_wrap", - [NODE_MATH_PINGPONG] = "math_pingpong", - - [NODE_MATH_SINE] = "math_sine", - [NODE_MATH_COSINE] = "math_cosine", - [NODE_MATH_TANGENT] = "math_tangent", - [NODE_MATH_SINH] = "math_sinh", - [NODE_MATH_COSH] = "math_cosh", - [NODE_MATH_TANH] = "math_tanh", - [NODE_MATH_ARCSINE] = "math_arcsine", - [NODE_MATH_ARCCOSINE] = "math_arccosine", - [NODE_MATH_ARCTANGENT] = "math_arctangent", - [NODE_MATH_ARCTAN2] = "math_arctan2", - }; - - if (node->custom1 < ARRAY_SIZE(names) && names[node->custom1]) { - int ret = GPU_stack_link(mat, node, names[node->custom1], in, out); - - if (ret && node->custom2 & SHD_MATH_CLAMP) { - float min[3] = {0.0f, 0.0f, 0.0f}; - float max[3] = {1.0f, 1.0f, 1.0f}; - GPU_link( - mat, "clamp_value", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link); - } - return ret; - } - else { - return 0; - } -} - -void register_node_type_sh_math(void) -{ - static bNodeType ntype; - - sh_fn_node_type_base(&ntype, SH_NODE_MATH, "Math", NODE_CLASS_CONVERTOR, 0); - node_type_socket_templates(&ntype, sh_node_math_in, sh_node_math_out); - node_type_label(&ntype, node_math_label); - node_type_gpu(&ntype, gpu_shader_math); - node_type_update(&ntype, node_math_update); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc new file mode 100644 index 00000000000..a0eb5099f9d --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -0,0 +1,196 @@ +/* + * 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) 2005 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup shdnodes + */ + +#include "node_shader_util.h" + +/* **************** SCALAR MATH ******************** */ +static bNodeSocketTemplate sh_node_math_in[] = { + {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, + {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, + {SOCK_FLOAT, N_("Value"), 0.0f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, + {-1, ""}}; + +static bNodeSocketTemplate sh_node_math_out[] = {{SOCK_FLOAT, N_("Value")}, {-1, ""}}; + +static const char *gpu_shader_get_name(int mode) +{ + switch (mode) { + case NODE_MATH_ADD: + return "math_add"; + case NODE_MATH_SUBTRACT: + return "math_subtract"; + case NODE_MATH_MULTIPLY: + return "math_multiply"; + case NODE_MATH_DIVIDE: + return "math_divide"; + case NODE_MATH_MULTIPLY_ADD: + return "math_multiply_add"; + + case NODE_MATH_POWER: + return "math_power"; + case NODE_MATH_LOGARITHM: + return "math_logarithm"; + case NODE_MATH_EXPONENT: + return "math_exponent"; + case NODE_MATH_SQRT: + return "math_sqrt"; + case NODE_MATH_INV_SQRT: + return "math_inversesqrt"; + case NODE_MATH_ABSOLUTE: + return "math_absolute"; + case NODE_MATH_RADIANS: + return "math_radians"; + case NODE_MATH_DEGREES: + return "math_degrees"; + + case NODE_MATH_MINIMUM: + return "math_minimum"; + case NODE_MATH_MAXIMUM: + return "math_maximum"; + case NODE_MATH_LESS_THAN: + return "math_less_than"; + case NODE_MATH_GREATER_THAN: + return "math_greater_than"; + case NODE_MATH_SIGN: + return "math_sign"; + case NODE_MATH_COMPARE: + return "math_compare"; + case NODE_MATH_SMOOTH_MIN: + return "math_smoothmin"; + case NODE_MATH_SMOOTH_MAX: + return "math_smoothmax"; + + case NODE_MATH_ROUND: + return "math_round"; + case NODE_MATH_FLOOR: + return "math_floor"; + case NODE_MATH_CEIL: + return "math_ceil"; + case NODE_MATH_FRACTION: + return "math_fraction"; + case NODE_MATH_MODULO: + return "math_modulo"; + case NODE_MATH_TRUNC: + return "math_trunc"; + case NODE_MATH_SNAP: + return "math_snap"; + case NODE_MATH_WRAP: + return "math_wrap"; + case NODE_MATH_PINGPONG: + return "math_pingpong"; + + case NODE_MATH_SINE: + return "math_sine"; + case NODE_MATH_COSINE: + return "math_cosine"; + case NODE_MATH_TANGENT: + return "math_tangent"; + case NODE_MATH_SINH: + return "math_sinh"; + case NODE_MATH_COSH: + return "math_cosh"; + case NODE_MATH_TANH: + return "math_tanh"; + case NODE_MATH_ARCSINE: + return "math_arcsine"; + case NODE_MATH_ARCCOSINE: + return "math_arccosine"; + case NODE_MATH_ARCTANGENT: + return "math_arctangent"; + case NODE_MATH_ARCTAN2: + return "math_arctan2"; + } + return nullptr; +} + +static int gpu_shader_math(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + const char *name = gpu_shader_get_name(node->custom1); + if (name != nullptr) { + int ret = GPU_stack_link(mat, node, name, in, out); + + if (ret && node->custom2 & SHD_MATH_CLAMP) { + float min[3] = {0.0f, 0.0f, 0.0f}; + float max[3] = {1.0f, 1.0f, 1.0f}; + GPU_link( + mat, "clamp_value", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link); + } + return ret; + } + else { + return 0; + } +} + +static void sh_node_math_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder) +{ + /* TODO: Implement clamp and other operations. */ + const int mode = builder.bnode().custom1; + switch (mode) { + case NODE_MATH_ADD: { + static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{ + "Add", [](float a, float b) { return a + b; }}; + builder.set_matching_fn(fn); + break; + } + case NODE_MATH_SUBTRACT: { + static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{ + "Subtract", [](float a, float b) { return a - b; }}; + builder.set_matching_fn(fn); + break; + } + case NODE_MATH_MULTIPLY: { + static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{ + "Multiply", [](float a, float b) { return a * b; }}; + builder.set_matching_fn(fn); + break; + } + case NODE_MATH_DIVIDE: { + static blender::fn::CustomMF_SI_SI_SO<float, float, float> fn{ + "Divide", [](float a, float b) { return (b != 0.0f) ? a / b : 0.0f; }}; + builder.set_matching_fn(fn); + break; + } + default: + BLI_assert(false); + break; + } +} + +void register_node_type_sh_math(void) +{ + static bNodeType ntype; + + sh_fn_node_type_base(&ntype, SH_NODE_MATH, "Math", NODE_CLASS_CONVERTOR, 0); + node_type_socket_templates(&ntype, sh_node_math_in, sh_node_math_out); + node_type_label(&ntype, node_math_label); + node_type_gpu(&ntype, gpu_shader_math); + node_type_update(&ntype, node_math_update); + ntype.expand_in_mf_network = sh_node_math_expand_in_mf_network; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.c b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc index 429b1a3e818..4dbe10f3982 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.c +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc @@ -44,6 +44,42 @@ static int gpu_shader_sepxyz(GPUMaterial *mat, return GPU_stack_link(mat, node, "separate_xyz", in, out); } +class MF_SeparateXYZ : public blender::fn::MultiFunction { + public: + MF_SeparateXYZ() + { + blender::fn::MFSignatureBuilder signature = this->get_builder("Separate XYZ"); + signature.single_input<blender::float3>("XYZ"); + signature.single_output<float>("X"); + signature.single_output<float>("Y"); + signature.single_output<float>("Z"); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + blender::fn::VSpan<blender::float3> vectors = params.readonly_single_input<blender::float3>( + 0, "XYZ"); + blender::MutableSpan<float> xs = params.uninitialized_single_output<float>(1, "X"); + blender::MutableSpan<float> ys = params.uninitialized_single_output<float>(2, "Y"); + blender::MutableSpan<float> zs = params.uninitialized_single_output<float>(3, "Z"); + + for (uint i : mask) { + blender::float3 xyz = vectors[i]; + xs[i] = xyz.x; + ys[i] = xyz.y; + zs[i] = xyz.z; + } + } +}; + +static void sh_node_sepxyz_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder) +{ + static MF_SeparateXYZ separate_fn; + builder.set_matching_fn(separate_fn); +} + void register_node_type_sh_sepxyz(void) { static bNodeType ntype; @@ -51,6 +87,7 @@ void register_node_type_sh_sepxyz(void) sh_fn_node_type_base(&ntype, SH_NODE_SEPXYZ, "Separate XYZ", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, sh_node_sepxyz_in, sh_node_sepxyz_out); node_type_gpu(&ntype, gpu_shader_sepxyz); + ntype.expand_in_mf_network = sh_node_sepxyz_expand_in_mf_network; nodeRegisterType(&ntype); } @@ -76,6 +113,39 @@ static int gpu_shader_combxyz(GPUMaterial *mat, return GPU_stack_link(mat, node, "combine_xyz", in, out); } +class MF_CombineXYZ : public blender::fn::MultiFunction { + public: + MF_CombineXYZ() + { + blender::fn::MFSignatureBuilder signature = this->get_builder("Combine XYZ"); + signature.single_input<float>("X"); + signature.single_input<float>("Y"); + signature.single_input<float>("Z"); + signature.single_output<blender::float3>("XYZ"); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + blender::fn::VSpan<float> xs = params.readonly_single_input<float>(0, "X"); + blender::fn::VSpan<float> ys = params.readonly_single_input<float>(1, "Y"); + blender::fn::VSpan<float> zs = params.readonly_single_input<float>(2, "Z"); + blender::MutableSpan<blender::float3> vectors = + params.uninitialized_single_output<blender::float3>(3, "XYZ"); + + for (uint i : mask) { + vectors[i] = {xs[i], ys[i], zs[i]}; + } + } +}; + +static void sh_node_combxyz_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder) +{ + static MF_CombineXYZ combine_fn; + builder.set_matching_fn(combine_fn); +} + void register_node_type_sh_combxyz(void) { static bNodeType ntype; @@ -83,6 +153,7 @@ void register_node_type_sh_combxyz(void) sh_fn_node_type_base(&ntype, SH_NODE_COMBXYZ, "Combine XYZ", NODE_CLASS_CONVERTOR, 0); node_type_socket_templates(&ntype, sh_node_combxyz_in, sh_node_combxyz_out); node_type_gpu(&ntype, gpu_shader_combxyz); + ntype.expand_in_mf_network = sh_node_combxyz_expand_in_mf_network; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_sky.c b/source/blender/nodes/shader/nodes/node_shader_tex_sky.c index 0daa948c139..d2c4413b862 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_sky.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_sky.c @@ -18,6 +18,7 @@ */ #include "../node_shader_util.h" +#include "sky_model.h" /* **************** OUTPUT ******************** */ @@ -43,9 +44,10 @@ static void node_shader_init_tex_sky(bNodeTree *UNUSED(ntree), bNode *node) tex->ground_albedo = 0.3f; tex->sun_disc = true; tex->sun_size = DEG2RADF(0.545); + tex->sun_intensity = 1.0f; tex->sun_elevation = M_PI_2; tex->sun_rotation = 0.0f; - tex->altitude = 0; + tex->altitude = 0.0f; tex->air_density = 1.0f; tex->dust_density = 1.0f; tex->ozone_density = 1.0f; @@ -53,19 +55,172 @@ static void node_shader_init_tex_sky(bNodeTree *UNUSED(ntree), bNode *node) node->storage = tex; } +typedef struct SkyModelPreetham { + float config_Y[5], config_x[5], config_y[5]; /* named after xyY color space */ + float radiance[3]; +} SkyModelPreetham; + +typedef struct XYZ_to_RGB /* transposed imbuf_xyz_to_rgb, passed as 3x vec3 */ +{ + float r[3], g[3], b[3]; +} XYZ_to_RGB; + +static float sky_perez_function(const float *lam, float theta, float gamma) +{ + float ctheta = cosf(theta); + float cgamma = cosf(gamma); + + return (1.0 + lam[0] * expf(lam[1] / ctheta)) * + (1.0 + lam[2] * expf(lam[3] * gamma) + lam[4] * cgamma * cgamma); +} + +static void sky_precompute_old(SkyModelPreetham *sunsky, const float sun_angles[], float turbidity) +{ + float theta = sun_angles[0]; + float theta2 = theta * theta; + float theta3 = theta2 * theta; + float T = turbidity; + float T2 = T * T; + float chi = (4.0f / 9.0f - T / 120.0f) * (M_PI - 2.0f * theta); + + sunsky->radiance[0] = (4.0453f * T - 4.9710f) * tanf(chi) - 0.2155f * T + 2.4192f; + sunsky->radiance[0] *= 0.06f; + + sunsky->radiance[1] = (0.00166f * theta3 - 0.00375f * theta2 + 0.00209f * theta) * T2 + + (-0.02903f * theta3 + 0.06377f * theta2 - 0.03202f * theta + 0.00394f) * + T + + (0.11693f * theta3 - 0.21196f * theta2 + 0.06052f * theta + 0.25886f); + + sunsky->radiance[2] = (0.00275f * theta3 - 0.00610f * theta2 + 0.00317f * theta) * T2 + + (-0.04214f * theta3 + 0.08970f * theta2 - 0.04153f * theta + 0.00516f) * + T + + (0.15346f * theta3 - 0.26756f * theta2 + 0.06670f * theta + 0.26688f); + + sunsky->config_Y[0] = (0.1787f * T - 1.4630f); + sunsky->config_Y[1] = (-0.3554f * T + 0.4275f); + sunsky->config_Y[2] = (-0.0227f * T + 5.3251f); + sunsky->config_Y[3] = (0.1206f * T - 2.5771f); + sunsky->config_Y[4] = (-0.0670f * T + 0.3703f); + + sunsky->config_x[0] = (-0.0193f * T - 0.2592f); + sunsky->config_x[1] = (-0.0665f * T + 0.0008f); + sunsky->config_x[2] = (-0.0004f * T + 0.2125f); + sunsky->config_x[3] = (-0.0641f * T - 0.8989f); + sunsky->config_x[4] = (-0.0033f * T + 0.0452f); + + sunsky->config_y[0] = (-0.0167f * T - 0.2608f); + sunsky->config_y[1] = (-0.0950f * T + 0.0092f); + sunsky->config_y[2] = (-0.0079f * T + 0.2102f); + sunsky->config_y[3] = (-0.0441f * T - 1.6537f); + sunsky->config_y[4] = (-0.0109f * T + 0.0529f); + + sunsky->radiance[0] /= sky_perez_function(sunsky->config_Y, 0, theta); + sunsky->radiance[1] /= sky_perez_function(sunsky->config_x, 0, theta); + sunsky->radiance[2] /= sky_perez_function(sunsky->config_y, 0, theta); +} + +static void get_XYZ_to_RGB_for_gpu(XYZ_to_RGB *data) +{ + const float *xyz_to_rgb = IMB_colormangement_get_xyz_to_rgb(); + data->r[0] = xyz_to_rgb[0]; + data->r[1] = xyz_to_rgb[3]; + data->r[2] = xyz_to_rgb[6]; + data->g[0] = xyz_to_rgb[1]; + data->g[1] = xyz_to_rgb[4]; + data->g[2] = xyz_to_rgb[7]; + data->b[0] = xyz_to_rgb[2]; + data->b[1] = xyz_to_rgb[5]; + data->b[2] = xyz_to_rgb[8]; +} + static int node_shader_gpu_tex_sky(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { - if (!in[0].link) { - in[0].link = GPU_attribute(mat, CD_ORCO, ""); + node_shader_gpu_default_tex_coord(mat, node, &in[0].link); + node_shader_gpu_tex_mapping(mat, node, in, out); + NodeTexSky *tex = (NodeTexSky *)node->storage; + float sun_angles[2]; /* [0]=theta=zenith angle [1]=phi=azimuth */ + sun_angles[0] = acosf(tex->sun_direction[2]); + sun_angles[1] = atan2f(tex->sun_direction[0], tex->sun_direction[1]); + + if (tex->sky_model == 0) { + /* Preetham */ + SkyModelPreetham sunsky; + sky_precompute_old(&sunsky, sun_angles, tex->turbidity); + XYZ_to_RGB xyz_to_rgb; + get_XYZ_to_RGB_for_gpu(&xyz_to_rgb); + return GPU_stack_link(mat, + node, + "node_tex_sky_preetham", + in, + out, + /* Pass config_Y/x/y as 3x(vec4+float) */ + GPU_uniform(&sunsky.config_Y[0]), + GPU_uniform(&sunsky.config_Y[4]), + GPU_uniform(&sunsky.config_x[0]), + GPU_uniform(&sunsky.config_x[4]), + GPU_uniform(&sunsky.config_y[0]), + GPU_uniform(&sunsky.config_y[4]), + GPU_uniform(sun_angles), + GPU_uniform(sunsky.radiance), + GPU_uniform(xyz_to_rgb.r), + GPU_uniform(xyz_to_rgb.g), + GPU_uniform(xyz_to_rgb.b)); + } + else if (tex->sky_model == 1) { + /* Hosek / Wilkie */ + sun_angles[0] = fmin(M_PI_2, sun_angles[0]); /* clamp to horizon */ + SKY_ArHosekSkyModelState *sky_state = SKY_arhosek_xyz_skymodelstate_alloc_init( + tex->turbidity, tex->ground_albedo, fmax(0.0, M_PI_2 - sun_angles[0])); + /* Pass sky_state->configs[3][9] as 3*(vec4+vec4)+vec3 */ + float config_x07[8], config_y07[8], config_z07[8], config_xyz8[3]; + for (int i = 0; i < 8; ++i) { + config_x07[i] = (float)sky_state->configs[0][i]; + config_y07[i] = (float)sky_state->configs[1][i]; + config_z07[i] = (float)sky_state->configs[2][i]; + } + for (int i = 0; i < 3; ++i) { + config_xyz8[i] = (float)sky_state->configs[i][8]; + } + float radiance[3]; + for (int i = 0; i < 3; i++) { + radiance[i] = sky_state->radiances[i] * (2 * M_PI / 683); + } + SKY_arhosekskymodelstate_free(sky_state); + XYZ_to_RGB xyz_to_rgb; + get_XYZ_to_RGB_for_gpu(&xyz_to_rgb); + return GPU_stack_link(mat, + node, + "node_tex_sky_hosekwilkie", + in, + out, + GPU_uniform(&config_x07[0]), + GPU_uniform(&config_x07[4]), + GPU_uniform(&config_y07[0]), + GPU_uniform(&config_y07[4]), + GPU_uniform(&config_z07[0]), + GPU_uniform(&config_z07[4]), + GPU_uniform(config_xyz8), + GPU_uniform(sun_angles), + GPU_uniform(radiance), + GPU_uniform(xyz_to_rgb.r), + GPU_uniform(xyz_to_rgb.g), + GPU_uniform(xyz_to_rgb.b)); + } + else { + return GPU_stack_link(mat, node, "node_tex_sky_nishita", in, out); } +} - node_shader_gpu_tex_mapping(mat, node, in, out); +static void node_shader_update_sky(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *sockVector = nodeFindSocket(node, SOCK_IN, "Vector"); - return GPU_stack_link(mat, node, "node_tex_sky", in, out); + NodeTexSky *tex = (NodeTexSky *)node->storage; + nodeSetSocketAvailability(sockVector, !(tex->sky_model == 2 && tex->sun_disc == 1)); } /* node type definition */ @@ -79,6 +234,8 @@ void register_node_type_sh_tex_sky(void) node_type_init(&ntype, node_shader_init_tex_sky); node_type_storage(&ntype, "NodeTexSky", node_free_standard_storage, node_copy_standard_storage); node_type_gpu(&ntype, node_shader_gpu_tex_sky); + /* remove Vector input for Nishita */ + node_type_update(&ntype, node_shader_update_sky); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_value.c b/source/blender/nodes/shader/nodes/node_shader_value.cc index c32e9e1d581..64701018d63 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.c +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -39,13 +39,21 @@ static int gpu_shader_value(GPUMaterial *mat, return GPU_stack_link(mat, node, "set_value", in, out, link); } +static void sh_node_value_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder) +{ + const bNodeSocket *bsocket = builder.dnode().output(0).bsocket(); + const bNodeSocketValueFloat *value = (const bNodeSocketValueFloat *)bsocket->default_value; + builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<float>>(value->value); +} + void register_node_type_sh_value(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0); + sh_fn_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0); node_type_socket_templates(&ntype, NULL, sh_node_value_out); node_type_gpu(&ntype, gpu_shader_value); + ntype.expand_in_mf_network = sh_node_value_expand_in_mf_network; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.c b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc index b719fe03d9b..414d05e996a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.c +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -34,44 +34,74 @@ static bNodeSocketTemplate sh_node_vector_math_in[] = { static bNodeSocketTemplate sh_node_vector_math_out[] = { {SOCK_VECTOR, N_("Vector")}, {SOCK_FLOAT, N_("Value")}, {-1, ""}}; +static const char *gpu_shader_get_name(int mode) +{ + switch (mode) { + case NODE_VECTOR_MATH_ADD: + return "vector_math_add"; + case NODE_VECTOR_MATH_SUBTRACT: + return "vector_math_subtract"; + case NODE_VECTOR_MATH_MULTIPLY: + return "vector_math_multiply"; + case NODE_VECTOR_MATH_DIVIDE: + return "vector_math_divide"; + + case NODE_VECTOR_MATH_CROSS_PRODUCT: + return "vector_math_cross"; + case NODE_VECTOR_MATH_PROJECT: + return "vector_math_project"; + case NODE_VECTOR_MATH_REFLECT: + return "vector_math_reflect"; + case NODE_VECTOR_MATH_DOT_PRODUCT: + return "vector_math_dot"; + + case NODE_VECTOR_MATH_DISTANCE: + return "vector_math_distance"; + case NODE_VECTOR_MATH_LENGTH: + return "vector_math_length"; + case NODE_VECTOR_MATH_SCALE: + return "vector_math_scale"; + case NODE_VECTOR_MATH_NORMALIZE: + return "vector_math_normalize"; + + case NODE_VECTOR_MATH_SNAP: + return "vector_math_snap"; + case NODE_VECTOR_MATH_FLOOR: + return "vector_math_floor"; + case NODE_VECTOR_MATH_CEIL: + return "vector_math_ceil"; + case NODE_VECTOR_MATH_MODULO: + return "vector_math_modulo"; + case NODE_VECTOR_MATH_FRACTION: + return "vector_math_fraction"; + case NODE_VECTOR_MATH_ABSOLUTE: + return "vector_math_absolute"; + case NODE_VECTOR_MATH_MINIMUM: + return "vector_math_minimum"; + case NODE_VECTOR_MATH_MAXIMUM: + return "vector_math_maximum"; + case NODE_VECTOR_MATH_WRAP: + return "vector_math_wrap"; + case NODE_VECTOR_MATH_SINE: + return "vector_math_sine"; + case NODE_VECTOR_MATH_COSINE: + return "vector_math_cosine"; + case NODE_VECTOR_MATH_TANGENT: + return "vector_math_tangent"; + } + + return nullptr; +} + static int gpu_shader_vector_math(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) { - static const char *names[] = { - [NODE_VECTOR_MATH_ADD] = "vector_math_add", - [NODE_VECTOR_MATH_SUBTRACT] = "vector_math_subtract", - [NODE_VECTOR_MATH_MULTIPLY] = "vector_math_multiply", - [NODE_VECTOR_MATH_DIVIDE] = "vector_math_divide", - - [NODE_VECTOR_MATH_CROSS_PRODUCT] = "vector_math_cross", - [NODE_VECTOR_MATH_PROJECT] = "vector_math_project", - [NODE_VECTOR_MATH_REFLECT] = "vector_math_reflect", - [NODE_VECTOR_MATH_DOT_PRODUCT] = "vector_math_dot", - - [NODE_VECTOR_MATH_DISTANCE] = "vector_math_distance", - [NODE_VECTOR_MATH_LENGTH] = "vector_math_length", - [NODE_VECTOR_MATH_SCALE] = "vector_math_scale", - [NODE_VECTOR_MATH_NORMALIZE] = "vector_math_normalize", - - [NODE_VECTOR_MATH_SNAP] = "vector_math_snap", - [NODE_VECTOR_MATH_FLOOR] = "vector_math_floor", - [NODE_VECTOR_MATH_CEIL] = "vector_math_ceil", - [NODE_VECTOR_MATH_MODULO] = "vector_math_modulo", - [NODE_VECTOR_MATH_FRACTION] = "vector_math_fraction", - [NODE_VECTOR_MATH_ABSOLUTE] = "vector_math_absolute", - [NODE_VECTOR_MATH_MINIMUM] = "vector_math_minimum", - [NODE_VECTOR_MATH_MAXIMUM] = "vector_math_maximum", - [NODE_VECTOR_MATH_WRAP] = "vector_math_wrap", - [NODE_VECTOR_MATH_SINE] = "vector_math_sine", - [NODE_VECTOR_MATH_COSINE] = "vector_math_cosine", - [NODE_VECTOR_MATH_TANGENT] = "vector_math_tangent", - }; - - if (node->custom1 < ARRAY_SIZE(names) && names[node->custom1]) { - return GPU_stack_link(mat, node, names[node->custom1], in, out); + const char *name = gpu_shader_get_name(node->custom1); + if (name != nullptr) { + return GPU_stack_link(mat, node, name, in, out); } else { return 0; @@ -80,8 +110,8 @@ static int gpu_shader_vector_math(GPUMaterial *mat, static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node) { - bNodeSocket *sockB = BLI_findlink(&node->inputs, 1); - bNodeSocket *sockC = BLI_findlink(&node->inputs, 2); + bNodeSocket *sockB = (bNodeSocket *)BLI_findlink(&node->inputs, 1); + bNodeSocket *sockC = (bNodeSocket *)BLI_findlink(&node->inputs, 2); bNodeSocket *sockScale = nodeFindSocket(node, SOCK_IN, "Scale"); bNodeSocket *sockVector = nodeFindSocket(node, SOCK_OUT, "Vector"); @@ -130,6 +160,43 @@ static void node_shader_update_vector_math(bNodeTree *UNUSED(ntree), bNode *node } } +static void sh_node_vector_math_expand_in_mf_network(blender::bke::NodeMFNetworkBuilder &builder) +{ + using blender::float3; + + /* TODO: Implement other operations. */ + const int mode = builder.bnode().custom1; + switch (mode) { + case NODE_VECTOR_MATH_ADD: { + static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{ + "Add", [](float3 a, float3 b) { return a + b; }}; + builder.set_matching_fn(fn); + break; + } + case NODE_VECTOR_MATH_SUBTRACT: { + static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{ + "Subtract", [](float3 a, float3 b) { return a - b; }}; + builder.set_matching_fn(fn); + break; + } + case NODE_VECTOR_MATH_MULTIPLY: { + static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{ + "Multiply", [](float3 a, float3 b) { return a * b; }}; + builder.set_matching_fn(fn); + break; + } + case NODE_VECTOR_MATH_DIVIDE: { + static blender::fn::CustomMF_SI_SI_SO<float3, float3, float3> fn{ + "Divide", [](float3 a, float3 b) { return float3::safe_divide(a, b); }}; + builder.set_matching_fn(fn); + break; + } + default: + BLI_assert(false); + break; + }; +} + void register_node_type_sh_vect_math(void) { static bNodeType ntype; @@ -139,6 +206,7 @@ void register_node_type_sh_vect_math(void) node_type_label(&ntype, node_vector_math_label); node_type_gpu(&ntype, gpu_shader_vector_math); node_type_update(&ntype, node_shader_update_vector_math); + ntype.expand_in_mf_network = sh_node_vector_math_expand_in_mf_network; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_vertex_color.c b/source/blender/nodes/shader/nodes/node_shader_vertex_color.c index 70c924a7f85..40576b68dd5 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vertex_color.c +++ b/source/blender/nodes/shader/nodes/node_shader_vertex_color.c @@ -39,6 +39,10 @@ static int node_shader_gpu_vertex_color(GPUMaterial *mat, GPUNodeStack *out) { NodeShaderVertexColor *vertexColor = (NodeShaderVertexColor *)node->storage; + if (U.experimental.use_sculpt_vertex_colors) { + GPUNodeLink *vertexColorLink = GPU_attribute(mat, CD_PROP_COLOR, vertexColor->layer_name); + return GPU_stack_link(mat, node, "node_vertex_color", in, out, vertexColorLink); + } GPUNodeLink *vertexColorLink = GPU_attribute(mat, CD_MCOL, vertexColor->layer_name); return GPU_stack_link(mat, node, "node_vertex_color", in, out, vertexColorLink); } diff --git a/source/blender/physics/intern/hair_volume.cpp b/source/blender/physics/intern/hair_volume.cpp index 6246bf54f75..1764d0a910c 100644 --- a/source/blender/physics/intern/hair_volume.cpp +++ b/source/blender/physics/intern/hair_volume.cpp @@ -303,7 +303,7 @@ void BPH_hair_volume_grid_clear(HairGrid *grid) } } -BLI_INLINE bool hair_grid_point_valid(const float vec[3], float gmin[3], float gmax[3]) +BLI_INLINE bool hair_grid_point_valid(const float vec[3], const float gmin[3], const float gmax[3]) { return !(vec[0] < gmin[0] || vec[1] < gmin[1] || vec[2] < gmin[2] || vec[0] > gmax[0] || vec[1] > gmax[1] || vec[2] > gmax[2]); diff --git a/source/blender/physics/intern/implicit_blender.c b/source/blender/physics/intern/implicit_blender.c index 5ec4c750d5d..54d38f3c10b 100644 --- a/source/blender/physics/intern/implicit_blender.c +++ b/source/blender/physics/intern/implicit_blender.c @@ -90,7 +90,7 @@ typedef struct fmatrix3x3 { /////////////////////////// /* simple vector code */ /* STATUS: verified */ -DO_INLINE void mul_fvector_S(float to[3], float from[3], float scalar) +DO_INLINE void mul_fvector_S(float to[3], const float from[3], float scalar) { to[0] = from[0] * scalar; to[1] = from[1] * scalar; @@ -429,7 +429,7 @@ DO_INLINE void mul_fmatrix_S(float matrix[3][3], float scalar) /* a vector multiplied by a 3x3 matrix */ /* STATUS: verified */ -DO_INLINE void mul_fvector_fmatrix(float *to, float *from, float matrix[3][3]) +DO_INLINE void mul_fvector_fmatrix(float *to, const float *from, float matrix[3][3]) { to[0] = matrix[0][0] * from[0] + matrix[1][0] * from[1] + matrix[2][0] * from[2]; to[1] = matrix[0][1] * from[0] + matrix[1][1] * from[1] + matrix[2][1] * from[2]; @@ -478,7 +478,7 @@ DO_INLINE void muladd_fmatrix_fvector(float to[3], float matrix[3][3], float fro to[2] += dot_v3v3(matrix[2], from); } -DO_INLINE void muladd_fmatrixT_fvector(float to[3], float matrix[3][3], float from[3]) +DO_INLINE void muladd_fmatrixT_fvector(float to[3], float matrix[3][3], const float from[3]) { to[0] += matrix[0][0] * from[0] + matrix[1][0] * from[1] + matrix[2][0] * from[2]; to[1] += matrix[0][1] * from[0] + matrix[1][1] * from[1] + matrix[2][1] * from[2]; @@ -1869,7 +1869,7 @@ bool BPH_mass_spring_force_spring_bending( } } -BLI_INLINE void poly_avg(lfVector *data, int *inds, int len, float r_avg[3]) +BLI_INLINE void poly_avg(lfVector *data, const int *inds, int len, float r_avg[3]) { float fact = 1.0f / (float)len; diff --git a/source/blender/python/generic/bgl.c b/source/blender/python/generic/bgl.c index 5471fc25f37..2ad2794c76f 100644 --- a/source/blender/python/generic/bgl.c +++ b/source/blender/python/generic/bgl.c @@ -487,7 +487,7 @@ static int gl_buffer_type_from_py_buffer(Py_buffer *pybuffer) return -1; /* UNKNOWN */ } -static bool compare_dimensions(int ndim, int *dim1, Py_ssize_t *dim2) +static bool compare_dimensions(int ndim, const int *dim1, const Py_ssize_t *dim2) { for (int i = 0; i < ndim; i++) { if (dim1[i] != dim2[i]) { diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index 957d49eb04e..4ee936aff91 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -50,7 +50,6 @@ #include "BKE_appdir.h" #include "BKE_blender_version.h" #include "BKE_global.h" -#include "BKE_lib_override.h" #include "DNA_ID.h" @@ -392,29 +391,6 @@ static PyObject *bpy_app_autoexec_fail_message_get(PyObject *UNUSED(self), void return PyC_UnicodeFromByte(G.autoexec_fail); } -PyDoc_STRVAR(bpy_app_use_override_library_doc, - "Boolean, whether library override is exposed in UI or not."); -static PyObject *bpy_app_use_override_library_get(PyObject *UNUSED(self), void *UNUSED(closure)) -{ - return PyBool_FromLong((long)BKE_lib_override_library_is_enabled()); -} - -static int bpy_app_use_override_library_set(PyObject *UNUSED(self), - PyObject *value, - void *UNUSED(closure)) -{ - const int param = PyC_Long_AsBool(value); - - if (param == -1 && PyErr_Occurred()) { - PyErr_SetString(PyExc_TypeError, "bpy.app.use_override_library must be a boolean"); - return -1; - } - - BKE_lib_override_library_enable((const bool)param); - - return 0; -} - static PyGetSetDef bpy_app_getsets[] = { {"debug", bpy_app_debug_get, bpy_app_debug_set, bpy_app_debug_doc, (void *)G_DEBUG}, {"debug_ffmpeg", @@ -485,11 +461,6 @@ static PyGetSetDef bpy_app_getsets[] = { (void *)G_DEBUG_GPU_MEM}, {"debug_io", bpy_app_debug_get, bpy_app_debug_set, bpy_app_debug_doc, (void *)G_DEBUG_IO}, - {"use_override_library", - bpy_app_use_override_library_get, - bpy_app_use_override_library_set, - bpy_app_use_override_library_doc, - NULL}, {"use_event_simulate", bpy_app_global_flag_get, bpy_app_global_flag_set__only_disable, diff --git a/source/blender/python/intern/bpy_capi_utils.h b/source/blender/python/intern/bpy_capi_utils.h index fe086b61097..97344ce1326 100644 --- a/source/blender/python/intern/bpy_capi_utils.h +++ b/source/blender/python/intern/bpy_capi_utils.h @@ -48,6 +48,6 @@ struct bContext *BPy_GetContext(void); void BPy_SetContext(struct bContext *C); extern void bpy_context_set(struct bContext *C, PyGILState_STATE *gilstate); -extern void bpy_context_clear(struct bContext *C, PyGILState_STATE *gilstate); +extern void bpy_context_clear(struct bContext *C, const PyGILState_STATE *gilstate); #endif /* __BPY_CAPI_UTILS_H__ */ diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 6da1715b02d..be5a92309f2 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -136,7 +136,7 @@ void bpy_context_set(bContext *C, PyGILState_STATE *gilstate) } /* context should be used but not now because it causes some bugs */ -void bpy_context_clear(bContext *UNUSED(C), PyGILState_STATE *gilstate) +void bpy_context_clear(bContext *UNUSED(C), const PyGILState_STATE *gilstate) { py_call_level--; diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index 3df0d805c5b..a1f9d4afc51 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -88,6 +88,37 @@ static const EnumPropertyItem property_flag_enum_items[] = { "'LIBRARY_EDITABLE'].\n" \ " :type options: set\n" +static const EnumPropertyItem property_flag_override_items[] = { + {PROPOVERRIDE_OVERRIDABLE_LIBRARY, + "LIBRARY_OVERRIDABLE", + 0, + "Library Overridable", + "Allow that property to be overridable from library linked data-blocks"}, + {0, NULL, 0, NULL, NULL}, +}; + +#define BPY_PROPDEF_OPTIONS_OVERRIDE_DOC \ + " :arg options: Enumerator in ['LIBRARY_OVERRIDE'].\n" \ + " :type options: set\n" + +static const EnumPropertyItem property_flag_override_collection_items[] = { + {PROPOVERRIDE_OVERRIDABLE_LIBRARY, + "LIBRARY_OVERRIDABLE", + 0, + "Library Overridable", + "Make that property editable in library overrides of linked data-blocks"}, + {PROPOVERRIDE_NO_PROP_NAME, + "NO_PROPERTY_NAME", + 0, + "No Name", + "Do not use the names of the items, only their indices in the collection"}, + {0, NULL, 0, NULL, NULL}, +}; + +#define BPY_PROPDEF_OPTIONS_OVERRIDE_COLLECTION_DOC \ + " :arg options: Enumerator in ['LIBRARY_OVERRIDE', 'NO_PROPERTY_NAME'].\n" \ + " :type options: set\n" + /* subtypes */ /* XXX Keep in sync with rna_rna.c's rna_enum_property_subtype_items ??? * Currently it is not... @@ -202,6 +233,11 @@ static void bpy_prop_assign_flag(PropertyRNA *prop, const int flag) } } +static void bpy_prop_assign_flag_override(PropertyRNA *prop, const int flag_override) +{ + RNA_def_property_override_flag(prop, flag_override); +} + /* operators and classes use this so it can store the args given but defer * running it until the operator runs where these values are used to setup * the default args for that operator instance */ @@ -1959,7 +1995,7 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop, /* terse macros for error checks shared between all funcs cant use function * calls because of static strings passed to pyrna_set_to_enum_bitfield */ -#define BPY_PROPDEF_CHECK(_func, _property_flag_items) \ +#define BPY_PROPDEF_CHECK(_func, _property_flag_items, _property_flag_override_items) \ if (UNLIKELY(id_len >= MAX_IDPROP_NAME)) { \ PyErr_Format(PyExc_TypeError, \ #_func "(): '%.200s' too long, max length is %d", \ @@ -1975,6 +2011,12 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop, _property_flag_items, pyopts, &opts, #_func "(options={ ...}):"))) { \ return NULL; \ } \ + if (UNLIKELY(pyopts_override && pyrna_set_to_enum_bitfield(_property_flag_override_items, \ + pyopts_override, \ + &opts_override, \ + #_func "(override={ ...}):"))) { \ + return NULL; \ + } \ { \ const EnumPropertyItem *tag_defines = RNA_struct_property_tag_defines(srna); \ if (py_tags && !tag_defines) { \ @@ -1990,8 +2032,9 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop, } \ (void)0 -#define BPY_PROPDEF_SUBTYPE_CHECK(_func, _property_flag_items, _subtype) \ - BPY_PROPDEF_CHECK(_func, _property_flag_items); \ +#define BPY_PROPDEF_SUBTYPE_CHECK( \ + _func, _property_flag_items, _property_flag_override_items, _subtype) \ + BPY_PROPDEF_CHECK(_func, _property_flag_items, _property_flag_override_items); \ if (UNLIKELY(pysubtype && RNA_enum_value_from_id(_subtype, pysubtype, &subtype) == 0)) { \ const char *enum_str = BPy_enum_as_string(_subtype); \ PyErr_Format(PyExc_TypeError, \ @@ -2099,7 +2142,8 @@ PyDoc_STRVAR(BPy_BoolProperty_doc, "description=\"\", " "default=False, " "options={'ANIMATABLE'}, " - "tags={}, " + "override=set(), " + "tags=set(), " "subtype='NONE', " "update=None, " "get=None, " @@ -2107,8 +2151,9 @@ PyDoc_STRVAR(BPy_BoolProperty_doc, "\n" " Returns a new boolean property definition.\n" "\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC BPY_PROPDEF_OPTIONS_DOC - BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_NUMBER_DOC BPY_PROPDEF_UPDATE_DOC - BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); + BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC + BPY_PROPDEF_SUBTYPE_NUMBER_DOC BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC + BPY_PROPDEF_SET_DOC); static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; @@ -2121,7 +2166,9 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) bool def = false; PropertyRNA *prop; PyObject *pyopts = NULL; + PyObject *pyopts_override = NULL; int opts = 0; + int opts_override = 0; int prop_tags = 0; const char *pysubtype = NULL; int subtype = PROP_NONE; @@ -2136,6 +2183,7 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) "description", "default", "options", + "override", "tags", "subtype", "update", @@ -2143,7 +2191,7 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) "set", NULL, }; - static _PyArg_Parser _parser = {"s#|ssO&O!O!sOOO:BoolProperty", _keywords, 0}; + static _PyArg_Parser _parser = {"s#|ssO&O!O!O!sOOO:BoolProperty", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, @@ -2156,6 +2204,8 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) &PySet_Type, &pyopts, &PySet_Type, + &pyopts_override, + &PySet_Type, &py_tags, &pysubtype, &update_cb, @@ -2164,7 +2214,10 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) return NULL; } - BPY_PROPDEF_SUBTYPE_CHECK(BoolProperty, property_flag_items, property_subtype_number_items); + BPY_PROPDEF_SUBTYPE_CHECK(BoolProperty, + property_flag_items, + property_flag_override_items, + property_subtype_number_items); if (bpy_prop_callback_check(update_cb, "update", 2) == -1) { return NULL; @@ -2186,6 +2239,9 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) if (pyopts) { bpy_prop_assign_flag(prop, opts); } + if (pyopts_override) { + bpy_prop_assign_flag_override(prop, opts_override); + } bpy_prop_callback_assign_update(prop, update_cb); bpy_prop_callback_assign_boolean(prop, get_cb, set_cb); RNA_def_property_duplicate_pointers(srna, prop); @@ -2194,24 +2250,26 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) Py_RETURN_NONE; } -PyDoc_STRVAR(BPy_BoolVectorProperty_doc, - ".. function:: BoolVectorProperty(name=\"\", " - "description=\"\", " - "default=(False, False, False), " - "options={'ANIMATABLE'}, " - "tags={}, " - "subtype='NONE', " - "size=3, " - "update=None, " - "get=None, " - "set=None)\n" - "\n" - " Returns a new vector boolean property definition.\n" - "\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC - " :arg default: sequence of booleans the length of *size*.\n" - " :type default: sequence\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_TAGS_DOC - BPY_PROPDEF_SUBTYPE_ARRAY_DOC BPY_PROPDEF_VECSIZE_DOC BPY_PROPDEF_UPDATE_DOC - BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); +PyDoc_STRVAR( + BPy_BoolVectorProperty_doc, + ".. function:: BoolVectorProperty(name=\"\", " + "description=\"\", " + "default=(False, False, False), " + "options={'ANIMATABLE'}, " + "override=set(), " + "tags=set(), " + "subtype='NONE', " + "size=3, " + "update=None, " + "get=None, " + "set=None)\n" + "\n" + " Returns a new vector boolean property definition.\n" + "\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC + " :arg default: sequence of booleans the length of *size*.\n" + " :type default: sequence\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC + BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_ARRAY_DOC BPY_PROPDEF_VECSIZE_DOC + BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; @@ -2226,7 +2284,9 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject PropertyRNA *prop; PyObject *pydef = NULL; PyObject *pyopts = NULL; + PyObject *pyopts_override = NULL; int opts = 0; + int opts_override = 0; int prop_tags = 0; const char *pysubtype = NULL; int subtype = PROP_NONE; @@ -2241,6 +2301,7 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject "description", "default", "options", + "override", "tags", "subtype", "size", @@ -2249,7 +2310,7 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject "set", NULL, }; - static _PyArg_Parser _parser = {"s#|ssOO!O!siOOO:BoolVectorProperty", _keywords, 0}; + static _PyArg_Parser _parser = {"s#|ssOO!O!O!siOOO:BoolVectorProperty", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, @@ -2261,6 +2322,8 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject &PySet_Type, &pyopts, &PySet_Type, + &pyopts_override, + &PySet_Type, &py_tags, &pysubtype, &size, @@ -2270,8 +2333,10 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject return NULL; } - BPY_PROPDEF_SUBTYPE_CHECK( - BoolVectorProperty, property_flag_items, property_subtype_array_items); + BPY_PROPDEF_SUBTYPE_CHECK(BoolVectorProperty, + property_flag_items, + property_flag_override_items, + property_subtype_array_items); if (size < 1 || size > PYRNA_STACK_ARRAY) { PyErr_Format( @@ -2314,6 +2379,9 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject if (pyopts) { bpy_prop_assign_flag(prop, opts); } + if (pyopts_override) { + bpy_prop_assign_flag_override(prop, opts_override); + } bpy_prop_callback_assign_update(prop, update_cb); bpy_prop_callback_assign_boolean_array(prop, get_cb, set_cb); RNA_def_property_duplicate_pointers(srna, prop); @@ -2322,28 +2390,29 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject Py_RETURN_NONE; } -PyDoc_STRVAR(BPy_IntProperty_doc, - ".. function:: IntProperty(name=\"\", " - "description=\"\", " - "default=0, " - "min=-2**31, max=2**31-1, " - "soft_min=-2**31, soft_max=2**31-1, " - "step=1, " - "options={'ANIMATABLE'}, " - "tags={}, " - "subtype='NONE', " - "update=None, " - "get=None, " - "set=None)\n" - "\n" - " Returns a new int property definition.\n" - "\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC BPY_PROPDEF_NUM_MIN_DOC - " :type min: int\n" BPY_PROPDEF_NUM_MAX_DOC - " :type max: int\n" BPY_PROPDEF_NUM_SOFTMAX_DOC - " :type soft_min: int\n" BPY_PROPDEF_NUM_SOFTMIN_DOC - " :type soft_max: int\n" BPY_PROPDEF_INT_STEP_DOC BPY_PROPDEF_OPTIONS_DOC - BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_NUMBER_DOC BPY_PROPDEF_UPDATE_DOC - BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); +PyDoc_STRVAR( + BPy_IntProperty_doc, + ".. function:: IntProperty(name=\"\", " + "description=\"\", " + "default=0, " + "min=-2**31, max=2**31-1, " + "soft_min=-2**31, soft_max=2**31-1, " + "step=1, " + "options={'ANIMATABLE'}, " + "override=set(), " + "tags=set(), " + "subtype='NONE', " + "update=None, " + "get=None, " + "set=None)\n" + "\n" + " Returns a new int property definition.\n" + "\n" BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC BPY_PROPDEF_NUM_MIN_DOC + " :type min: int\n" BPY_PROPDEF_NUM_MAX_DOC " :type max: int\n" BPY_PROPDEF_NUM_SOFTMAX_DOC + " :type soft_min: int\n" BPY_PROPDEF_NUM_SOFTMIN_DOC + " :type soft_max: int\n" BPY_PROPDEF_INT_STEP_DOC BPY_PROPDEF_OPTIONS_DOC + BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_NUMBER_DOC + BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; @@ -2357,6 +2426,8 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw) PropertyRNA *prop; PyObject *pyopts = NULL; int opts = 0; + PyObject *pyopts_override = NULL; + int opts_override = 0; int prop_tags = 0; const char *pysubtype = NULL; int subtype = PROP_NONE; @@ -2376,6 +2447,7 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw) "soft_max", "step", "options", + "override", "tags", "subtype", "update", @@ -2383,7 +2455,7 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw) "set", NULL, }; - static _PyArg_Parser _parser = {"s#|ssiiiiiiO!O!sOOO:IntProperty", _keywords, 0}; + static _PyArg_Parser _parser = {"s#|ssiiiiiiO!O!O!sOOO:IntProperty", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, @@ -2400,6 +2472,8 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw) &PySet_Type, &pyopts, &PySet_Type, + &pyopts_override, + &PySet_Type, &py_tags, &pysubtype, &update_cb, @@ -2408,7 +2482,10 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw) return NULL; } - BPY_PROPDEF_SUBTYPE_CHECK(IntProperty, property_flag_items, property_subtype_number_items); + BPY_PROPDEF_SUBTYPE_CHECK(IntProperty, + property_flag_items, + property_flag_override_items, + property_subtype_number_items); if (bpy_prop_callback_check(update_cb, "update", 2) == -1) { return NULL; @@ -2432,6 +2509,9 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw) if (pyopts) { bpy_prop_assign_flag(prop, opts); } + if (pyopts_override) { + bpy_prop_assign_flag_override(prop, opts_override); + } bpy_prop_callback_assign_update(prop, update_cb); bpy_prop_callback_assign_int(prop, get_cb, set_cb); RNA_def_property_duplicate_pointers(srna, prop); @@ -2447,7 +2527,8 @@ PyDoc_STRVAR(BPy_IntVectorProperty_doc, "soft_max=2**31-1, " "step=1, " "options={'ANIMATABLE'}, " - "tags={}, " + "override=set(), " + "tags=set(), " "subtype='NONE', " "size=3, " "update=None, " @@ -2462,8 +2543,9 @@ PyDoc_STRVAR(BPy_IntVectorProperty_doc, " :type max: int\n" BPY_PROPDEF_NUM_SOFTMIN_DOC " :type soft_min: int\n" BPY_PROPDEF_NUM_SOFTMAX_DOC " :type soft_max: int\n" BPY_PROPDEF_INT_STEP_DOC BPY_PROPDEF_OPTIONS_DOC - BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_ARRAY_DOC BPY_PROPDEF_VECSIZE_DOC - BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); + BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC + BPY_PROPDEF_SUBTYPE_ARRAY_DOC BPY_PROPDEF_VECSIZE_DOC BPY_PROPDEF_UPDATE_DOC + BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; @@ -2480,6 +2562,8 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject PyObject *pydef = NULL; PyObject *pyopts = NULL; int opts = 0; + PyObject *pyopts_override = NULL; + int opts_override = 0; int prop_tags = 0; const char *pysubtype = NULL; int subtype = PROP_NONE; @@ -2499,6 +2583,7 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject "soft_max", "step", "options", + "override", "tags", "subtype", "size", @@ -2507,7 +2592,7 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject "set", NULL, }; - static _PyArg_Parser _parser = {"s#|ssOiiiiiO!O!siOOO:IntVectorProperty", _keywords, 0}; + static _PyArg_Parser _parser = {"s#|ssOiiiiiO!O!O!siOOO:IntVectorProperty", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, @@ -2524,6 +2609,8 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject &PySet_Type, &pyopts, &PySet_Type, + &pyopts_override, + &PySet_Type, &py_tags, &pysubtype, &size, @@ -2533,8 +2620,10 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject return NULL; } - BPY_PROPDEF_SUBTYPE_CHECK( - IntVectorProperty, property_flag_items, property_subtype_array_items); + BPY_PROPDEF_SUBTYPE_CHECK(IntVectorProperty, + property_flag_items, + property_flag_override_items, + property_subtype_array_items); if (size < 1 || size > PYRNA_STACK_ARRAY) { PyErr_Format( @@ -2575,6 +2664,9 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject if (pyopts) { bpy_prop_assign_flag(prop, opts); } + if (pyopts_override) { + bpy_prop_assign_flag_override(prop, opts_override); + } bpy_prop_callback_assign_update(prop, update_cb); bpy_prop_callback_assign_int_array(prop, get_cb, set_cb); RNA_def_property_duplicate_pointers(srna, prop); @@ -2591,7 +2683,8 @@ PyDoc_STRVAR(BPy_FloatProperty_doc, "step=3, " "precision=2, " "options={'ANIMATABLE'}, " - "tags={}, " + "override=set(), " + "tags=set(), " "subtype='NONE', " "unit='NONE', " "update=None, " @@ -2604,9 +2697,9 @@ PyDoc_STRVAR(BPy_FloatProperty_doc, " :type max: float\n" BPY_PROPDEF_NUM_SOFTMIN_DOC " :type soft_min: float\n" BPY_PROPDEF_NUM_SOFTMAX_DOC " :type soft_max: float\n" BPY_PROPDEF_FLOAT_STEP_DOC BPY_PROPDEF_FLOAT_PREC_DOC - BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_NUMBER_DOC - BPY_PROPDEF_UNIT_DOC BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC - BPY_PROPDEF_SET_DOC); + BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC + BPY_PROPDEF_SUBTYPE_NUMBER_DOC BPY_PROPDEF_UNIT_DOC BPY_PROPDEF_UPDATE_DOC + BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; @@ -2622,6 +2715,8 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw) PropertyRNA *prop; PyObject *pyopts = NULL; int opts = 0; + PyObject *pyopts_override = NULL; + int opts_override = 0; int prop_tags = 0; const char *pysubtype = NULL; int subtype = PROP_NONE; @@ -2633,26 +2728,11 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw) PyObject *py_tags = NULL; static const char *_keywords[] = { - "attr", - "name", - "description", - "default", - "min", - "max", - "soft_min", - "soft_max", - "step", - "precision", - "options", - "tags", - "subtype", - "unit", - "update", - "get", - "set", - NULL, + "attr", "name", "description", "default", "min", "max", "soft_min", + "soft_max", "step", "precision", "options", "override", "tags", "subtype", + "unit", "update", "get", "set", NULL, }; - static _PyArg_Parser _parser = {"s#|ssffffffiO!O!ssOOO:FloatProperty", _keywords, 0}; + static _PyArg_Parser _parser = {"s#|ssffffffiO!O!O!ssOOO:FloatProperty", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, @@ -2670,6 +2750,8 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw) &PySet_Type, &pyopts, &PySet_Type, + &pyopts_override, + &PySet_Type, &py_tags, &pysubtype, &pyunit, @@ -2679,7 +2761,10 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw) return NULL; } - BPY_PROPDEF_SUBTYPE_CHECK(FloatProperty, property_flag_items, property_subtype_number_items); + BPY_PROPDEF_SUBTYPE_CHECK(FloatProperty, + property_flag_items, + property_flag_override_items, + property_subtype_number_items); if (pyunit && RNA_enum_value_from_id(rna_enum_property_unit_items, pyunit, &unit) == 0) { PyErr_Format(PyExc_TypeError, "FloatProperty(unit='%s'): invalid unit", pyunit); @@ -2708,6 +2793,9 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw) if (pyopts) { bpy_prop_assign_flag(prop, opts); } + if (pyopts_override) { + bpy_prop_assign_flag_override(prop, opts_override); + } bpy_prop_callback_assign_update(prop, update_cb); bpy_prop_callback_assign_float(prop, get_cb, set_cb); RNA_def_property_duplicate_pointers(srna, prop); @@ -2724,7 +2812,8 @@ PyDoc_STRVAR(BPy_FloatVectorProperty_doc, "step=3, " "precision=2, " "options={'ANIMATABLE'}, " - "tags={}, " + "override=set(), " + "tags=set(), " "subtype='NONE', " "unit='NONE', " "size=3, " @@ -2739,8 +2828,8 @@ PyDoc_STRVAR(BPy_FloatVectorProperty_doc, " :type min: float\n" BPY_PROPDEF_NUM_MAX_DOC " :type max: float\n" BPY_PROPDEF_NUM_SOFTMIN_DOC " :type soft_min: float\n" BPY_PROPDEF_NUM_SOFTMAX_DOC - " :type soft_max: float\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_TAGS_DOC - BPY_PROPDEF_FLOAT_STEP_DOC BPY_PROPDEF_FLOAT_PREC_DOC + " :type soft_max: float\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC + BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_FLOAT_STEP_DOC BPY_PROPDEF_FLOAT_PREC_DOC BPY_PROPDEF_SUBTYPE_ARRAY_DOC BPY_PROPDEF_UNIT_DOC BPY_PROPDEF_VECSIZE_DOC BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObject *kw) @@ -2759,6 +2848,8 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec PyObject *pydef = NULL; PyObject *pyopts = NULL; int opts = 0; + PyObject *pyopts_override = NULL; + int opts_override = 0; int prop_tags = 0; const char *pysubtype = NULL; int subtype = PROP_NONE; @@ -2770,11 +2861,11 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec PyObject *py_tags = NULL; static const char *_keywords[] = { - "attr", "name", "description", "default", "min", "max", "soft_min", - "soft_max", "step", "precision", "options", "tags", "subtype", "unit", - "size", "update", "get", "set", NULL, + "attr", "name", "description", "default", "min", "max", "soft_min", + "soft_max", "step", "precision", "options", "override", "tags", "subtype", + "unit", "size", "update", "get", "set", NULL, }; - static _PyArg_Parser _parser = {"s#|ssOfffffiO!O!ssiOOO:FloatVectorProperty", _keywords, 0}; + static _PyArg_Parser _parser = {"s#|ssOfffffiO!O!O!ssiOOO:FloatVectorProperty", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, @@ -2792,6 +2883,8 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec &PySet_Type, &pyopts, &PySet_Type, + &pyopts_override, + &PySet_Type, &py_tags, &pysubtype, &pyunit, @@ -2802,8 +2895,10 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec return NULL; } - BPY_PROPDEF_SUBTYPE_CHECK( - FloatVectorProperty, property_flag_items, property_subtype_array_items); + BPY_PROPDEF_SUBTYPE_CHECK(FloatVectorProperty, + property_flag_items, + property_flag_override_items, + property_subtype_array_items); if (pyunit && RNA_enum_value_from_id(rna_enum_property_unit_items, pyunit, &unit) == 0) { PyErr_Format(PyExc_TypeError, "FloatVectorProperty(unit='%s'): invalid unit", pyunit); @@ -2850,6 +2945,9 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec if (pyopts) { bpy_prop_assign_flag(prop, opts); } + if (pyopts_override) { + bpy_prop_assign_flag_override(prop, opts_override); + } bpy_prop_callback_assign_update(prop, update_cb); bpy_prop_callback_assign_float_array(prop, get_cb, set_cb); RNA_def_property_duplicate_pointers(srna, prop); @@ -2863,7 +2961,8 @@ PyDoc_STRVAR(BPy_StringProperty_doc, "default=\"\", " "maxlen=0, " "options={'ANIMATABLE'}, " - "tags={}, " + "options=set(), " + "tags=set(), " "subtype='NONE', " "update=None, " "get=None, " @@ -2874,9 +2973,9 @@ PyDoc_STRVAR(BPy_StringProperty_doc, " :arg default: initializer string.\n" " :type default: string\n" " :arg maxlen: maximum length of the string.\n" - " :type maxlen: int\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_TAGS_DOC - BPY_PROPDEF_SUBTYPE_STRING_DOC BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC - BPY_PROPDEF_SET_DOC); + " :type maxlen: int\n" BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC + BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_SUBTYPE_STRING_DOC BPY_PROPDEF_UPDATE_DOC + BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; @@ -2890,6 +2989,8 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw PropertyRNA *prop; PyObject *pyopts = NULL; int opts = 0; + PyObject *pyopts_override = NULL; + int opts_override = 0; int prop_tags = 0; const char *pysubtype = NULL; int subtype = PROP_NONE; @@ -2905,6 +3006,7 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw "default", "maxlen", "options", + "override", "tags", "subtype", "update", @@ -2912,7 +3014,7 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw "set", NULL, }; - static _PyArg_Parser _parser = {"s#|sssiO!O!sOOO:StringProperty", _keywords, 0}; + static _PyArg_Parser _parser = {"s#|sssiO!O!O!sOOO:StringProperty", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, @@ -2925,6 +3027,8 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw &PySet_Type, &pyopts, &PySet_Type, + &pyopts_override, + &PySet_Type, &py_tags, &pysubtype, &update_cb, @@ -2933,7 +3037,10 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw return NULL; } - BPY_PROPDEF_SUBTYPE_CHECK(StringProperty, property_flag_items, property_subtype_string_items); + BPY_PROPDEF_SUBTYPE_CHECK(StringProperty, + property_flag_items, + property_flag_override_items, + property_subtype_string_items); if (bpy_prop_callback_check(update_cb, "update", 2) == -1) { return NULL; @@ -2961,6 +3068,9 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw if (pyopts) { bpy_prop_assign_flag(prop, opts); } + if (pyopts_override) { + bpy_prop_assign_flag_override(prop, opts_override); + } bpy_prop_callback_assign_update(prop, update_cb); bpy_prop_callback_assign_string(prop, get_cb, set_cb); RNA_def_property_duplicate_pointers(srna, prop); @@ -2975,7 +3085,8 @@ PyDoc_STRVAR( "description=\"\", " "default=None, " "options={'ANIMATABLE'}, " - "tags={}, " + "override=set(), " + "tags=set(), " "update=None, " "get=None, " "set=None)\n" @@ -3019,8 +3130,9 @@ PyDoc_STRVAR( "instead.\n" " WARNING: Strings can not be specified for dynamic enums\n" " (i.e. if a callback function is given as *items* parameter).\n" - " :type default: string, integer or set\n" BPY_PROPDEF_OPTIONS_ENUM_DOC BPY_PROPDEF_TAGS_DOC - BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); + " :type default: string, integer or set\n" BPY_PROPDEF_OPTIONS_ENUM_DOC + BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_UPDATE_DOC + BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; @@ -3037,6 +3149,8 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) PropertyRNA *prop; PyObject *pyopts = NULL; int opts = 0; + PyObject *pyopts_override = NULL; + int opts_override = 0; int prop_tags = 0; bool is_itemf = false; PyObject *update_cb = NULL; @@ -3051,13 +3165,14 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) "description", "default", "options", + "override", "tags", "update", "get", "set", NULL, }; - static _PyArg_Parser _parser = {"s#O|ssOO!O!OOO:EnumProperty", _keywords, 0}; + static _PyArg_Parser _parser = {"s#O|ssOO!O!O!OOO:EnumProperty", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, @@ -3070,6 +3185,8 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) &PySet_Type, &pyopts, &PySet_Type, + &pyopts_override, + &PySet_Type, &py_tags, &update_cb, &get_cb, @@ -3077,7 +3194,7 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) return NULL; } - BPY_PROPDEF_CHECK(EnumProperty, property_flag_enum_items); + BPY_PROPDEF_CHECK(EnumProperty, property_flag_enum_items, property_flag_override_items); if (bpy_prop_callback_check(update_cb, "update", 2) == -1) { return NULL; @@ -3149,6 +3266,9 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) if (pyopts) { bpy_prop_assign_flag(prop, opts); } + if (pyopts_override) { + bpy_prop_assign_flag_override(prop, opts_override); + } bpy_prop_callback_assign_update(prop, update_cb); bpy_prop_callback_assign_enum(prop, get_cb, set_cb, (is_itemf ? items : NULL)); RNA_def_property_duplicate_pointers(srna, prop); @@ -3194,14 +3314,15 @@ PyDoc_STRVAR(BPy_PointerProperty_doc, "name=\"\", " "description=\"\", " "options={'ANIMATABLE'}, " - "tags={}, " + "override=set(), " + "tags=set(), " "poll=None, " "update=None)\n" "\n" " Returns a new pointer property definition.\n" "\n" BPY_PROPDEF_TYPE_DOC BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC - BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_TAGS_DOC BPY_PROPDEF_POLL_DOC - BPY_PROPDEF_UPDATE_DOC); + BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_DOC BPY_PROPDEF_TAGS_DOC + BPY_PROPDEF_POLL_DOC BPY_PROPDEF_UPDATE_DOC); PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; @@ -3215,8 +3336,10 @@ PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw) StructRNA *ptype; PyObject *type = Py_None; PyObject *pyopts = NULL; + PyObject *pyopts_override = NULL; PyObject *py_tags = NULL; int opts = 0; + int opts_override = 0; int prop_tags = 0; PyObject *update_cb = NULL, *poll_cb = NULL; @@ -3226,12 +3349,13 @@ PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw) "name", "description", "options", + "override", "tags", "poll", "update", NULL, }; - static _PyArg_Parser _parser = {"s#O|ssO!O!OO:PointerProperty", _keywords, 0}; + static _PyArg_Parser _parser = {"s#O|ssO!O!O!OO:PointerProperty", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, @@ -3243,13 +3367,15 @@ PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw) &PySet_Type, &pyopts, &PySet_Type, + &pyopts_override, + &PySet_Type, &py_tags, &poll_cb, &update_cb)) { return NULL; } - BPY_PROPDEF_CHECK(PointerProperty, property_flag_items); + BPY_PROPDEF_CHECK(PointerProperty, property_flag_items, property_flag_override_items); ptype = pointer_type_from_py(type, "PointerProperty(...)"); if (!ptype) { @@ -3275,6 +3401,9 @@ PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw) if (pyopts) { bpy_prop_assign_flag(prop, opts); } + if (pyopts_override) { + bpy_prop_assign_flag_override(prop, opts_override); + } if (RNA_struct_idprops_contains_datablock(ptype)) { if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) { @@ -3293,11 +3422,13 @@ PyDoc_STRVAR(BPy_CollectionProperty_doc, "name=\"\", " "description=\"\", " "options={'ANIMATABLE'}, " - "tags={})\n" + "override=set(), " + "tags=set())\n" "\n" " Returns a new collection property definition.\n" "\n" BPY_PROPDEF_TYPE_DOC BPY_PROPDEF_NAME_DOC BPY_PROPDEF_DESC_DOC - BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_TAGS_DOC); + BPY_PROPDEF_OPTIONS_DOC BPY_PROPDEF_OPTIONS_OVERRIDE_COLLECTION_DOC + BPY_PROPDEF_TAGS_DOC); PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw) { StructRNA *srna; @@ -3311,8 +3442,10 @@ PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw) StructRNA *ptype; PyObject *type = Py_None; PyObject *pyopts = NULL; + PyObject *pyopts_override = NULL; PyObject *py_tags = NULL; int opts = 0; + int opts_override = 0; int prop_tags = 0; static const char *_keywords[] = { @@ -3321,10 +3454,11 @@ PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw) "name", "description", "options", + "override", "tags", NULL, }; - static _PyArg_Parser _parser = {"s#O|ssO!O!:CollectionProperty", _keywords, 0}; + static _PyArg_Parser _parser = {"s#O|ssO!O!O!:CollectionProperty", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, @@ -3336,11 +3470,14 @@ PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw) &PySet_Type, &pyopts, &PySet_Type, + &pyopts_override, + &PySet_Type, &py_tags)) { return NULL; } - BPY_PROPDEF_CHECK(CollectionProperty, property_flag_items); + BPY_PROPDEF_CHECK( + CollectionProperty, property_flag_items, property_flag_override_collection_items); ptype = pointer_type_from_py(type, "CollectionProperty(...):"); if (!ptype) { @@ -3362,6 +3499,9 @@ PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw) if (pyopts) { bpy_prop_assign_flag(prop, opts); } + if (pyopts_override) { + bpy_prop_assign_flag_override(prop, opts_override); + } if (RNA_struct_idprops_contains_datablock(ptype)) { if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) { diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c index d792b2032b5..1c84e95672f 100644 --- a/source/blender/python/intern/bpy_rna_anim.c +++ b/source/blender/python/intern/bpy_rna_anim.c @@ -415,7 +415,7 @@ char pyrna_struct_keyframe_delete_doc[] = " :arg group: The name of the group the F-Curve should be added to if it doesn't exist " "yet.\n" " :type group: str\n" - " :return: Success of keyframe deleation.\n" + " :return: Success of keyframe deletion.\n" " :rtype: boolean\n"; PyObject *pyrna_struct_keyframe_delete(BPy_StructRNA *self, PyObject *args, PyObject *kw) { diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c index b607f1635e6..a4df8c3aef1 100644 --- a/source/blender/python/intern/bpy_rna_id_collection.c +++ b/source/blender/python/intern/bpy_rna_id_collection.c @@ -132,7 +132,7 @@ static int foreach_libblock_id_user_map_callback(LibraryIDLinkCallbackData *cb_d PyDoc_STRVAR(bpy_user_map_doc, ".. method:: user_map([subset=(id1, id2, ...)], key_types={..}, value_types={..})\n" "\n" - " Returns a mapping of all ID datablocks in current ``bpy.data`` to a set of all " + " Returns a mapping of all ID data-blocks in current ``bpy.data`` to a set of all " "datablocks using them.\n" "\n" " For list of valid set members for key_types & value_types, see: " diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index 63137e094b7..3e30c81c8c6 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -2526,7 +2526,6 @@ static PyObject *Matrix_mul(PyObject *m1, PyObject *m2) } if (mat1 && mat2) { -#ifdef USE_MATHUTILS_ELEM_MUL /* MATRIX * MATRIX */ float mat[MATRIX_MAX_DIM * MATRIX_MAX_DIM]; @@ -2540,7 +2539,6 @@ static PyObject *Matrix_mul(PyObject *m1, PyObject *m2) mul_vn_vnvn(mat, mat1->matrix, mat2->matrix, mat1->num_col * mat1->num_row); return Matrix_CreatePyObject(mat, mat2->num_col, mat1->num_row, Py_TYPE(mat1)); -#endif } else if (mat2) { /*FLOAT/INT * MATRIX */ @@ -2584,7 +2582,6 @@ static PyObject *Matrix_imul(PyObject *m1, PyObject *m2) } if (mat1 && mat2) { -#ifdef USE_MATHUTILS_ELEM_MUL /* MATRIX *= MATRIX */ if ((mat1->num_row != mat2->num_row) || (mat1->num_col != mat2->num_col)) { PyErr_SetString(PyExc_ValueError, @@ -2594,14 +2591,6 @@ static PyObject *Matrix_imul(PyObject *m1, PyObject *m2) } mul_vn_vn(mat1->matrix, mat2->matrix, mat1->num_col * mat1->num_row); -#else - PyErr_Format(PyExc_TypeError, - "In place element-wise multiplication: " - "not supported between '%.200s' and '%.200s' types", - Py_TYPE(m1)->tp_name, - Py_TYPE(m2)->tp_name); - return NULL; -#endif } else if (mat1 && (((scalar = PyFloat_AsDouble(m2)) == -1.0f && PyErr_Occurred()) == 0)) { /* MATRIX *= FLOAT/INT */ diff --git a/source/blender/python/mathutils/mathutils_Quaternion.c b/source/blender/python/mathutils/mathutils_Quaternion.c index 7ce0ea5f249..2b7761b7678 100644 --- a/source/blender/python/mathutils/mathutils_Quaternion.c +++ b/source/blender/python/mathutils/mathutils_Quaternion.c @@ -962,11 +962,9 @@ static PyObject *Quaternion_mul(PyObject *q1, PyObject *q2) } if (quat1 && quat2) { /* QUAT * QUAT (element-wise product) */ -#ifdef USE_MATHUTILS_ELEM_MUL float quat[QUAT_SIZE]; mul_vn_vnvn(quat, quat1->quat, quat2->quat, QUAT_SIZE); return Quaternion_CreatePyObject(quat, Py_TYPE(q1)); -#endif } /* the only case this can happen (for a supported type is "FLOAT * QUAT") */ else if (quat2) { /* FLOAT * QUAT */ @@ -1007,17 +1005,8 @@ static PyObject *Quaternion_imul(PyObject *q1, PyObject *q2) } } - if (quat1 && quat2) { /* QUAT *= QUAT (inplace element-wise product) */ -#ifdef USE_MATHUTILS_ELEM_MUL + if (quat1 && quat2) { /* QUAT *= QUAT (in-place element-wise product). */ mul_vn_vn(quat1->quat, quat2->quat, QUAT_SIZE); -#else - PyErr_Format(PyExc_TypeError, - "In place element-wise multiplication: " - "not supported between '%.200s' and '%.200s' types", - Py_TYPE(q1)->tp_name, - Py_TYPE(q2)->tp_name); - return NULL; -#endif } else if (quat1 && (((scalar = PyFloat_AsDouble(q2)) == -1.0f && PyErr_Occurred()) == 0)) { /* QUAT *= FLOAT */ diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c index 15ae811fd91..4b47440a530 100644 --- a/source/blender/python/mathutils/mathutils_Vector.c +++ b/source/blender/python/mathutils/mathutils_Vector.c @@ -1738,7 +1738,7 @@ static PyObject *vector_mul_float(VectorObject *vec, const float scalar) mul_vn_vn_fl(tvec, vec->vec, vec->size, scalar); return Vector_CreatePyObject_alloc(tvec, vec->size, Py_TYPE(vec)); } -#ifdef USE_MATHUTILS_ELEM_MUL + static PyObject *vector_mul_vec(VectorObject *vec1, VectorObject *vec2) { float *tvec = PyMem_Malloc(vec1->size * sizeof(float)); @@ -1752,7 +1752,7 @@ static PyObject *vector_mul_vec(VectorObject *vec1, VectorObject *vec2) mul_vn_vnvn(tvec, vec1->vec, vec2->vec, vec1->size); return Vector_CreatePyObject_alloc(tvec, vec1->size, Py_TYPE(vec1)); } -#endif + static PyObject *Vector_mul(PyObject *v1, PyObject *v2) { VectorObject *vec1 = NULL, *vec2 = NULL; @@ -1775,7 +1775,6 @@ static PyObject *Vector_mul(PyObject *v1, PyObject *v2) /* make sure v1 is always the vector */ if (vec1 && vec2) { -#ifdef USE_MATHUTILS_ELEM_MUL if (vec1->size != vec2->size) { PyErr_SetString(PyExc_ValueError, "Vector multiplication: " @@ -1785,7 +1784,6 @@ static PyObject *Vector_mul(PyObject *v1, PyObject *v2) /* element-wise product */ return vector_mul_vec(vec1, vec2); -#endif } else if (vec1) { if (((scalar = PyFloat_AsDouble(v2)) == -1.0f && PyErr_Occurred()) == 0) { /* VEC * FLOAT */ @@ -1832,7 +1830,6 @@ static PyObject *Vector_imul(PyObject *v1, PyObject *v2) /* Intentionally don't support (Quaternion, Matrix) here, uses reverse order instead. */ if (vec1 && vec2) { -#ifdef USE_MATHUTILS_ELEM_MUL if (vec1->size != vec2->size) { PyErr_SetString(PyExc_ValueError, "Vector multiplication: " @@ -1840,16 +1837,8 @@ static PyObject *Vector_imul(PyObject *v1, PyObject *v2) return NULL; } - /* element-wise product inplace */ + /* Element-wise product in-place. */ mul_vn_vn(vec1->vec, vec2->vec, vec1->size); -#else - PyErr_Format(PyExc_TypeError, - "In place element-wise multiplication: " - "not supported between '%.200s' and '%.200s' types", - Py_TYPE(v1)->tp_name, - Py_TYPE(v2)->tp_name); - return NULL; -#endif } else if (vec1 && (((scalar = PyFloat_AsDouble(v2)) == -1.0f && PyErr_Occurred()) == 0)) { /* VEC *= FLOAT */ diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c index 59c0021e0f3..93dbac32c19 100644 --- a/source/blender/python/mathutils/mathutils_geometry.c +++ b/source/blender/python/mathutils/mathutils_geometry.c @@ -1537,9 +1537,9 @@ static PyObject *M_Geometry_convex_hull_2d(PyObject *UNUSED(self), PyObject *poi * to fill values, with start_table and len_table giving the start index * and length of the toplevel_len sub-lists. */ -static PyObject *list_of_lists_from_arrays(int *array, - int *start_table, - int *len_table, +static PyObject *list_of_lists_from_arrays(const int *array, + const int *start_table, + const int *len_table, int toplevel_len) { PyObject *ret, *sublist; diff --git a/source/blender/render/intern/source/bake_api.c b/source/blender/render/intern/source/bake_api.c index 06f77854595..6e336604b59 100644 --- a/source/blender/render/intern/source/bake_api.c +++ b/source/blender/render/intern/source/bake_api.c @@ -694,14 +694,7 @@ void RE_bake_pixels_populate(Mesh *me, const BakeImages *bake_images, const char *uv_layer) { - BakeDataZSpan bd; - size_t i; - int a, p_id; - const MLoopUV *mloopuv; - const int tottri = poly_to_tri_count(me->totpoly, me->totloop); - MLoopTri *looptri; - if ((uv_layer == NULL) || (uv_layer[0] == '\0')) { mloopuv = CustomData_get_layer(&me->ldata, CD_MLOOPUV); } @@ -714,25 +707,26 @@ void RE_bake_pixels_populate(Mesh *me, return; } + BakeDataZSpan bd; bd.pixel_array = pixel_array; bd.zspan = MEM_callocN(sizeof(ZSpan) * bake_images->size, "bake zspan"); /* initialize all pixel arrays so we know which ones are 'blank' */ - for (i = 0; i < num_pixels; i++) { + for (int i = 0; i < num_pixels; i++) { pixel_array[i].primitive_id = -1; pixel_array[i].object_id = 0; } - for (i = 0; i < bake_images->size; i++) { + for (int i = 0; i < bake_images->size; i++) { zbuf_alloc_span(&bd.zspan[i], bake_images->data[i].width, bake_images->data[i].height); } - looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__); + const int tottri = poly_to_tri_count(me->totpoly, me->totloop); + MLoopTri *looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__); BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri); - p_id = -1; - for (i = 0; i < tottri; i++) { + for (int i = 0; i < tottri; i++) { const MLoopTri *lt = &looptri[i]; const MPoly *mp = &me->mpoly[lt->poly]; float vec[3][2]; @@ -744,9 +738,9 @@ void RE_bake_pixels_populate(Mesh *me, } bd.bk_image = &bake_images->data[image_id]; - bd.primitive_id = ++p_id; + bd.primitive_id = i; - for (a = 0; a < 3; a++) { + for (int a = 0; a < 3; a++) { const float *uv = mloopuv[lt->tri[a]].uv; /* Note, workaround for pixel aligned UVs which are common and can screw up our @@ -761,7 +755,7 @@ void RE_bake_pixels_populate(Mesh *me, zspan_scanconvert(&bd.zspan[image_id], (void *)&bd, vec[0], vec[1], vec[2], store_bake_pixel); } - for (i = 0; i < bake_images->size; i++) { + for (int i = 0; i < bake_images->size; i++) { zbuf_free_span(&bd.zspan[i]); } diff --git a/source/blender/render/intern/source/multires_bake.c b/source/blender/render/intern/source/multires_bake.c index 7c301f7d591..b30821a1b73 100644 --- a/source/blender/render/intern/source/multires_bake.c +++ b/source/blender/render/intern/source/multires_bake.c @@ -1319,8 +1319,11 @@ static void bake_ibuf_filter(ImBuf *ibuf, char *mask, const int filter) } } -static void bake_ibuf_normalize_displacement( - ImBuf *ibuf, float *displacement, char *mask, float displacement_min, float displacement_max) +static void bake_ibuf_normalize_displacement(ImBuf *ibuf, + const float *displacement, + const char *mask, + float displacement_min, + float displacement_max) { int i; const float *current_displacement = displacement; diff --git a/source/blender/render/intern/source/render_texture.c b/source/blender/render/intern/source/render_texture.c index 9926e08c968..b37eeed3681 100644 --- a/source/blender/render/intern/source/render_texture.c +++ b/source/blender/render/intern/source/render_texture.c @@ -902,7 +902,7 @@ static void do_2d_mapping( float fx, fy, fac1, area[8]; int ok, proj, areaflag = 0, wrap; - /* mtex variables localized, only cubemap doesn't cooperate yet... */ + /* #MTex variables localized, only cube-map doesn't cooperate yet. */ wrap = mtex->mapping; tex = mtex->tex; diff --git a/source/blender/shader_fx/CMakeLists.txt b/source/blender/shader_fx/CMakeLists.txt index 740f7c126d1..a12f36f9713 100644 --- a/source/blender/shader_fx/CMakeLists.txt +++ b/source/blender/shader_fx/CMakeLists.txt @@ -24,8 +24,8 @@ set(INC intern ../blenfont ../blenkernel - ../blentranslation ../blenlib + ../blentranslation ../bmesh ../depsgraph ../editors/include diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 482589e2ccb..3d4c84805f9 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -744,8 +744,13 @@ void *WM_jobs_customdata_get(struct wmJob *); void WM_jobs_customdata_set(struct wmJob *, void *customdata, void (*free)(void *)); void WM_jobs_timer(struct wmJob *, double timestep, unsigned int note, unsigned int endnote); void WM_jobs_delay_start(struct wmJob *, double delay_time); + +typedef void (*wm_jobs_start_callback)(void *custom_data, + short *stop, + short *do_update, + float *progress); void WM_jobs_callbacks(struct wmJob *, - void (*startjob)(void *, short *, short *, float *), + wm_jobs_start_callback startjob, void (*initjob)(void *), void (*update)(void *), void (*endjob)(void *)); diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 4acce793707..b4b3d0957af 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -361,6 +361,7 @@ typedef struct wmNotifier { #define ND_LOD (30 << 16) #define ND_DRAW_RENDER_VIEWPORT \ (31 << 16) /* for camera & sequencer viewport update, also /w NC_SCENE */ +#define ND_SHADERFX (32 << 16) /* NC_MATERIAL Material */ #define ND_SHADING (30 << 16) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 53d6df915d6..05ef4bfac30 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -591,7 +591,7 @@ static int wm_handler_ui_call(bContext *C, return WM_HANDLER_CONTINUE; } - /* UI is quite aggressive with swallowing events, like scrollwheel */ + /* UI is quite aggressive with swallowing events, like scroll-wheel. */ /* I realize this is not extremely nice code... when UI gets keymaps it can be maybe smarter */ if (do_wheel_ui == false) { if (is_wheel) { @@ -608,7 +608,7 @@ static int wm_handler_ui_call(bContext *C, return WM_UI_HANDLER_CONTINUE; } - /* we set context to where ui handler came from */ + /* We set context to where UI handler came from. */ if (handler->context.area) { CTX_wm_area_set(C, handler->context.area); } @@ -810,7 +810,7 @@ bool WM_operator_check_ui_empty(wmOperatorType *ot) return true; } - /* Assume a ui callback will draw something. */ + /* Assume a UI callback will draw something. */ if (ot->ui) { return false; } @@ -2707,7 +2707,7 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers handler_base = handler_base_next) { handler_base_next = handler_base->next; - /* during this loop, ui handlers for nested menus can tag multiple handlers free */ + /* During this loop, UI handlers for nested menus can tag multiple handlers free. */ if (handler_base->flag & WM_HANDLER_DO_FREE) { /* pass */ } @@ -2829,7 +2829,7 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers /* XXX code this for all modal ops, and ensure free only happens here */ - /* modal ui handler can be tagged to be freed */ + /* Modal UI handler can be tagged to be freed. */ if (BLI_findindex(handlers, handler_base) != -1) { /* could be freed already by regular modal ops */ if (handler_base->flag & WM_HANDLER_DO_FREE) { @@ -4834,7 +4834,7 @@ wmKeyMapItem *WM_event_match_keymap_item_from_handlers(bContext *C, const wmEvent *event) { LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) { - /* during this loop, ui handlers for nested menus can tag multiple handlers free */ + /* During this loop, UI handlers for nested menus can tag multiple handlers free. */ if (handler_base->flag & WM_HANDLER_DO_FREE) { /* pass */ } diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index d5a240a358e..f431a6f431b 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1027,13 +1027,6 @@ void wm_homefile_read(bContext *C, }, NULL); } - if (BLI_listbase_is_empty(&U.themes)) { - if (G.debug & G_DEBUG) { - printf("\nNote: No (valid) '%s' found, fall back to built-in default.\n\n", - filepath_startup); - } - success = false; - } if (success) { if (update_defaults) { if (use_data) { diff --git a/source/blender/windowmanager/intern/wm_jobs.c b/source/blender/windowmanager/intern/wm_jobs.c index c10f03f3dab..87a19d832c9 100644 --- a/source/blender/windowmanager/intern/wm_jobs.c +++ b/source/blender/windowmanager/intern/wm_jobs.c @@ -86,7 +86,7 @@ struct wmJob { * This performs the actual parallel work. * Executed in worker thread(s). */ - void (*startjob)(void *, short *stop, short *do_update, float *progress); + wm_jobs_start_callback startjob; /** * Called if thread defines so (see `do_update` flag), and max once per timer step. * Executed in main thread. @@ -378,7 +378,7 @@ void WM_jobs_delay_start(wmJob *wm_job, double delay_time) } void WM_jobs_callbacks(wmJob *wm_job, - void (*startjob)(void *, short *, short *, float *), + wm_jobs_start_callback startjob, void (*initjob)(void *), void (*update)(void *), void (*endjob)(void *)) diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index a2389b02d8f..20a75ebe3b9 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -51,7 +51,6 @@ # include "BKE_global.h" # include "BKE_image.h" # include "BKE_lib_id.h" -# include "BKE_lib_override.h" # include "BKE_main.h" # include "BKE_report.h" # include "BKE_scene.h" @@ -619,7 +618,6 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo printf("Misc Options:\n"); BLI_argsPrintArgDoc(ba, "--app-template"); BLI_argsPrintArgDoc(ba, "--factory-startup"); - BLI_argsPrintArgDoc(ba, "--disable-library-override"); BLI_argsPrintArgDoc(ba, "--enable-event-simulate"); printf("\n"); BLI_argsPrintArgDoc(ba, "--env-system-datafiles"); @@ -683,7 +681,6 @@ static int arg_handle_print_help(int UNUSED(argc), const char **UNUSED(argv), vo # ifdef WITH_SDL printf(" $SDL_AUDIODRIVER LibSDL audio driver - alsa, esd, dma.\n"); # endif - printf(" $PYTHONHOME Path to the Python directory, eg. /usr/lib/python.\n\n"); exit(0); @@ -1122,17 +1119,6 @@ static int arg_handle_factory_startup_set(int UNUSED(argc), return 0; } -static const char arg_handle_disable_override_library_doc[] = - "\n\t" - "Disable Library Override features in the UI."; -static int arg_handle_disable_override_library(int UNUSED(argc), - const char **UNUSED(argv), - void *UNUSED(data)) -{ - BKE_lib_override_library_enable(false); - return 0; -} - static const char arg_handle_enable_event_simulate_doc[] = "\n\t" "Enable event simulation testing feature 'bpy.types.Window.event_simulate'."; @@ -1460,7 +1446,7 @@ static int arg_handle_image_type_set(int argc, const char **argv, void *data) return 1; } else { - printf("\nError: you must specify a format after '-F / --render-foramt'.\n"); + printf("\nError: you must specify a format after '-F / --render-format'.\n"); return 0; } } @@ -2226,8 +2212,6 @@ void main_args_setup(bContext *C, bArgs *ba) BLI_argsAdd(ba, 1, NULL, "--app-template", CB(arg_handle_app_template), NULL); BLI_argsAdd(ba, 1, NULL, "--factory-startup", CB(arg_handle_factory_startup_set), NULL); - BLI_argsAdd( - ba, 1, NULL, "--disable-library-override", CB(arg_handle_disable_override_library), NULL); BLI_argsAdd(ba, 1, NULL, "--enable-event-simulate", CB(arg_handle_enable_event_simulate), NULL); /* TODO, add user env vars? */ diff --git a/tests/gtests/blenlib/BLI_array_test.cc b/tests/gtests/blenlib/BLI_array_test.cc index 9c77c69e296..3ff5baf1d94 100644 --- a/tests/gtests/blenlib/BLI_array_test.cc +++ b/tests/gtests/blenlib/BLI_array_test.cc @@ -1,3 +1,5 @@ +/* Apache License, Version 2.0 */ + #include "BLI_array.hh" #include "BLI_strict_flags.h" #include "testing/testing.h" @@ -138,23 +140,22 @@ TEST(array, NoInitializationSizeConstructor) { using MyArray = Array<ConstructibleType>; - AlignedBuffer<sizeof(MyArray), alignof(MyArray)> buffer; - char *buffer_ptr = (char *)buffer.ptr(); - memset(buffer_ptr, 100, sizeof(MyArray)); + TypedBuffer<MyArray> buffer; + memset(buffer, 100, sizeof(MyArray)); /* Doing this to avoid some compiler optimization. */ for (uint i : IndexRange(sizeof(MyArray))) { - EXPECT_EQ(buffer_ptr[i], 100); + EXPECT_EQ(((char *)buffer.ptr())[i], 100); } { - MyArray &array = *new (buffer.ptr()) MyArray(1, NoInitialization()); + MyArray &array = *new (buffer) MyArray(1, NoInitialization()); EXPECT_EQ(array[0].value, 100); array.clear_without_destruct(); array.~Array(); } { - MyArray &array = *new (buffer.ptr()) MyArray(1); + MyArray &array = *new (buffer) MyArray(1); EXPECT_EQ(array[0].value, 42); array.~Array(); } diff --git a/tests/gtests/blenlib/BLI_disjoint_set_test.cc b/tests/gtests/blenlib/BLI_disjoint_set_test.cc new file mode 100644 index 00000000000..30503954c62 --- /dev/null +++ b/tests/gtests/blenlib/BLI_disjoint_set_test.cc @@ -0,0 +1,36 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_disjoint_set.hh" +#include "BLI_strict_flags.h" + +#include "testing/testing.h" + +namespace blender { + +TEST(disjoint_set, Test) +{ + DisjointSet disjoint_set(6); + EXPECT_FALSE(disjoint_set.in_same_set(1, 2)); + EXPECT_FALSE(disjoint_set.in_same_set(5, 3)); + EXPECT_TRUE(disjoint_set.in_same_set(2, 2)); + EXPECT_EQ(disjoint_set.find_root(3), 3u); + + disjoint_set.join(1, 2); + + EXPECT_TRUE(disjoint_set.in_same_set(1, 2)); + EXPECT_FALSE(disjoint_set.in_same_set(0, 1)); + + disjoint_set.join(3, 4); + + EXPECT_FALSE(disjoint_set.in_same_set(2, 3)); + EXPECT_TRUE(disjoint_set.in_same_set(3, 4)); + + disjoint_set.join(1, 4); + + EXPECT_TRUE(disjoint_set.in_same_set(1, 4)); + EXPECT_TRUE(disjoint_set.in_same_set(1, 3)); + EXPECT_TRUE(disjoint_set.in_same_set(2, 4)); + EXPECT_FALSE(disjoint_set.in_same_set(0, 4)); +} + +} // namespace blender diff --git a/tests/gtests/blenlib/BLI_ghash_performance_test.cc b/tests/gtests/blenlib/BLI_ghash_performance_test.cc index 1002ff7d2df..201598869e8 100644 --- a/tests/gtests/blenlib/BLI_ghash_performance_test.cc +++ b/tests/gtests/blenlib/BLI_ghash_performance_test.cc @@ -535,42 +535,6 @@ static void int2_ghash_tests(GHash *ghash, const char *id, const unsigned int nb printf("========== ENDED %s ==========\n\n", id); } -TEST(ghash, Int2GHash2000) -{ - GHash *ghash = BLI_ghash_new( - BLI_ghashutil_uinthash_v2_p, BLI_ghashutil_uinthash_v2_cmp, __func__); - - int2_ghash_tests(ghash, "Int2GHash - GHash - 2000", 2000); -} - -#ifdef GHASH_RUN_BIG -TEST(ghash, Int2GHash20000000) -{ - GHash *ghash = BLI_ghash_new( - BLI_ghashutil_uinthash_v2_p, BLI_ghashutil_uinthash_v2_cmp, __func__); - - int2_ghash_tests(ghash, "Int2GHash - GHash - 20000000", 20000000); -} -#endif - -TEST(ghash, Int2Murmur2a2000) -{ - GHash *ghash = BLI_ghash_new( - BLI_ghashutil_uinthash_v2_p_murmur, BLI_ghashutil_uinthash_v2_cmp, __func__); - - int2_ghash_tests(ghash, "Int2GHash - Murmur - 2000", 2000); -} - -#ifdef GHASH_RUN_BIG -TEST(ghash, Int2Murmur2a20000000) -{ - GHash *ghash = BLI_ghash_new( - BLI_ghashutil_uinthash_v2_p_murmur, BLI_ghashutil_uinthash_v2_cmp, __func__); - - int2_ghash_tests(ghash, "Int2GHash - Murmur - 20000000", 20000000); -} -#endif - /* MultiSmall: create and manipulate a lot of very small ghashes * (90% < 10 items, 9% < 100 items, 1% < 1000 items). */ diff --git a/tests/gtests/blenlib/BLI_index_mask_test.cc b/tests/gtests/blenlib/BLI_index_mask_test.cc index 50b32be6364..6d9b7c70ab2 100644 --- a/tests/gtests/blenlib/BLI_index_mask_test.cc +++ b/tests/gtests/blenlib/BLI_index_mask_test.cc @@ -1,3 +1,5 @@ +/* Apache License, Version 2.0 */ + #include "BLI_index_mask.hh" #include "testing/testing.h" diff --git a/tests/gtests/blenlib/BLI_index_range_test.cc b/tests/gtests/blenlib/BLI_index_range_test.cc index e484da5ef42..0f4fb7ef03b 100644 --- a/tests/gtests/blenlib/BLI_index_range_test.cc +++ b/tests/gtests/blenlib/BLI_index_range_test.cc @@ -1,3 +1,5 @@ +/* Apache License, Version 2.0 */ + #include "BLI_index_range.hh" #include "BLI_strict_flags.h" #include "BLI_vector.hh" diff --git a/tests/gtests/blenlib/BLI_linear_allocator_test.cc b/tests/gtests/blenlib/BLI_linear_allocator_test.cc index bbea4c1239f..7ef9d433a4d 100644 --- a/tests/gtests/blenlib/BLI_linear_allocator_test.cc +++ b/tests/gtests/blenlib/BLI_linear_allocator_test.cc @@ -1,3 +1,5 @@ +/* Apache License, Version 2.0 */ + #include "BLI_linear_allocator.hh" #include "BLI_strict_flags.h" #include "testing/testing.h" diff --git a/tests/gtests/blenlib/BLI_map_test.cc b/tests/gtests/blenlib/BLI_map_test.cc index 3b72795d36f..4fb97c40e86 100644 --- a/tests/gtests/blenlib/BLI_map_test.cc +++ b/tests/gtests/blenlib/BLI_map_test.cc @@ -1,3 +1,5 @@ +/* Apache License, Version 2.0 */ + #include "BLI_map.hh" #include "BLI_rand.h" #include "BLI_set.hh" diff --git a/tests/gtests/blenlib/BLI_math_bits_test.cc b/tests/gtests/blenlib/BLI_math_bits_test.cc index 9baa471cf48..4fa4809beed 100644 --- a/tests/gtests/blenlib/BLI_math_bits_test.cc +++ b/tests/gtests/blenlib/BLI_math_bits_test.cc @@ -1,3 +1,5 @@ +/* Apache License, Version 2.0 */ + #include "BLI_math_bits.h" #include "testing/testing.h" #include <iostream> diff --git a/tests/gtests/blenlib/BLI_memory_utils_test.cc b/tests/gtests/blenlib/BLI_memory_utils_test.cc new file mode 100644 index 00000000000..b99db5c5eca --- /dev/null +++ b/tests/gtests/blenlib/BLI_memory_utils_test.cc @@ -0,0 +1,159 @@ +/* Apache License, Version 2.0 */ + +#include "BLI_float3.hh" +#include "BLI_memory_utils.hh" +#include "BLI_strict_flags.h" +#include "testing/testing.h" + +namespace blender { + +struct MyValue { + static inline int alive = 0; + + MyValue() + { + if (alive == 15) { + throw std::exception(); + } + + alive++; + } + + MyValue(const MyValue &other) + { + if (alive == 15) { + throw std::exception(); + } + + alive++; + } + + ~MyValue() + { + alive--; + } +}; + +TEST(memory_utils, DefaultConstructN_ActuallyCallsConstructor) +{ + constexpr int amount = 10; + TypedBuffer<MyValue, amount> buffer; + + EXPECT_EQ(MyValue::alive, 0); + default_construct_n(buffer.ptr(), amount); + EXPECT_EQ(MyValue::alive, amount); + destruct_n(buffer.ptr(), amount); + EXPECT_EQ(MyValue::alive, 0); +} + +TEST(memory_utils, DefaultConstructN_StrongExceptionSafety) +{ + constexpr int amount = 20; + TypedBuffer<MyValue, amount> buffer; + + EXPECT_EQ(MyValue::alive, 0); + EXPECT_THROW(default_construct_n(buffer.ptr(), amount), std::exception); + EXPECT_EQ(MyValue::alive, 0); +} + +TEST(memory_utils, UninitializedCopyN_ActuallyCopies) +{ + constexpr int amount = 5; + TypedBuffer<MyValue, amount> buffer1; + TypedBuffer<MyValue, amount> buffer2; + + EXPECT_EQ(MyValue::alive, 0); + default_construct_n(buffer1.ptr(), amount); + EXPECT_EQ(MyValue::alive, amount); + uninitialized_copy_n(buffer1.ptr(), amount, buffer2.ptr()); + EXPECT_EQ(MyValue::alive, 2 * amount); + destruct_n(buffer1.ptr(), amount); + EXPECT_EQ(MyValue::alive, amount); + destruct_n(buffer2.ptr(), amount); + EXPECT_EQ(MyValue::alive, 0); +} + +TEST(memory_utils, UninitializedCopyN_StrongExceptionSafety) +{ + constexpr int amount = 10; + TypedBuffer<MyValue, amount> buffer1; + TypedBuffer<MyValue, amount> buffer2; + + EXPECT_EQ(MyValue::alive, 0); + default_construct_n(buffer1.ptr(), amount); + EXPECT_EQ(MyValue::alive, amount); + EXPECT_THROW(uninitialized_copy_n(buffer1.ptr(), amount, buffer2.ptr()), std::exception); + EXPECT_EQ(MyValue::alive, amount); + destruct_n(buffer1.ptr(), amount); + EXPECT_EQ(MyValue::alive, 0); +} + +TEST(memory_utils, UninitializedFillN_ActuallyCopies) +{ + constexpr int amount = 10; + TypedBuffer<MyValue, amount> buffer; + + EXPECT_EQ(MyValue::alive, 0); + { + MyValue value; + EXPECT_EQ(MyValue::alive, 1); + uninitialized_fill_n(buffer.ptr(), amount, value); + EXPECT_EQ(MyValue::alive, 1 + amount); + destruct_n(buffer.ptr(), amount); + EXPECT_EQ(MyValue::alive, 1); + } + EXPECT_EQ(MyValue::alive, 0); +} + +TEST(memory_utils, UninitializedFillN_StrongExceptionSafety) +{ + constexpr int amount = 20; + TypedBuffer<MyValue, amount> buffer; + + EXPECT_EQ(MyValue::alive, 0); + { + MyValue value; + EXPECT_EQ(MyValue::alive, 1); + EXPECT_THROW(uninitialized_fill_n(buffer.ptr(), amount, value), std::exception); + EXPECT_EQ(MyValue::alive, 1); + } + EXPECT_EQ(MyValue::alive, 0); +} + +class TestBaseClass { + virtual void mymethod(){}; +}; + +class TestChildClass : public TestBaseClass { + void mymethod() override + { + } +}; + +static_assert(is_convertible_pointer_v<int *, int *>); +static_assert(is_convertible_pointer_v<int *, const int *>); +static_assert(is_convertible_pointer_v<int *, int *const>); +static_assert(is_convertible_pointer_v<int *, const int *const>); +static_assert(!is_convertible_pointer_v<const int *, int *>); +static_assert(!is_convertible_pointer_v<int, int *>); +static_assert(!is_convertible_pointer_v<int *, int>); +static_assert(is_convertible_pointer_v<TestBaseClass *, const TestBaseClass *>); +static_assert(!is_convertible_pointer_v<const TestBaseClass *, TestBaseClass *>); +static_assert(is_convertible_pointer_v<TestChildClass *, TestBaseClass *>); +static_assert(!is_convertible_pointer_v<TestBaseClass *, TestChildClass *>); +static_assert(is_convertible_pointer_v<const TestChildClass *, const TestBaseClass *>); +static_assert(!is_convertible_pointer_v<TestBaseClass, const TestChildClass *>); +static_assert(!is_convertible_pointer_v<float3, float *>); +static_assert(!is_convertible_pointer_v<float *, float3>); +static_assert(!is_convertible_pointer_v<int **, int *>); +static_assert(!is_convertible_pointer_v<int *, int **>); +static_assert(is_convertible_pointer_v<int **, int **>); +static_assert(is_convertible_pointer_v<const int **, const int **>); +static_assert(!is_convertible_pointer_v<const int **, int **>); +static_assert(!is_convertible_pointer_v<int *const *, int **>); +static_assert(!is_convertible_pointer_v<int *const *const, int **>); +static_assert(is_convertible_pointer_v<int **, int **const>); +static_assert(is_convertible_pointer_v<int **, int *const *>); +static_assert(is_convertible_pointer_v<int **, int const *const *>); + +} // namespace blender diff --git a/tests/gtests/blenlib/BLI_set_test.cc b/tests/gtests/blenlib/BLI_set_test.cc index ac78eb786df..189f0c1b134 100644 --- a/tests/gtests/blenlib/BLI_set_test.cc +++ b/tests/gtests/blenlib/BLI_set_test.cc @@ -1,3 +1,5 @@ +/* Apache License, Version 2.0 */ + #include <set> #include <unordered_set> @@ -403,6 +405,51 @@ TEST(set, IntrusiveIntKey) EXPECT_TRUE(set.remove(4)); } +struct MyKeyType { + uint32_t key; + int32_t attached_data; + + uint32_t hash() const + { + return key; + } + + friend bool operator==(const MyKeyType &a, const MyKeyType &b) + { + return a.key == b.key; + } +}; + +TEST(set, LookupKey) +{ + Set<MyKeyType> set; + set.add({1, 10}); + set.add({2, 20}); + EXPECT_EQ(set.lookup_key({1, 30}).attached_data, 10); + EXPECT_EQ(set.lookup_key({2, 0}).attached_data, 20); +} + +TEST(set, LookupKeyDefault) +{ + Set<MyKeyType> set; + set.add({1, 10}); + set.add({2, 20}); + + MyKeyType fallback{5, 50}; + EXPECT_EQ(set.lookup_key_default({1, 66}, fallback).attached_data, 10); + EXPECT_EQ(set.lookup_key_default({4, 40}, fallback).attached_data, 50); +} + +TEST(set, LookupKeyPtr) +{ + Set<MyKeyType> set; + set.add({1, 10}); + set.add({2, 20}); + EXPECT_EQ(set.lookup_key_ptr({1, 50})->attached_data, 10); + EXPECT_EQ(set.lookup_key_ptr({2, 50})->attached_data, 20); + EXPECT_EQ(set.lookup_key_ptr({3, 50}), nullptr); +} + /** * Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot. */ diff --git a/tests/gtests/blenlib/BLI_span_test.cc b/tests/gtests/blenlib/BLI_span_test.cc index 375cb694b7b..ccc63fd80eb 100644 --- a/tests/gtests/blenlib/BLI_span_test.cc +++ b/tests/gtests/blenlib/BLI_span_test.cc @@ -1,3 +1,5 @@ +/* Apache License, Version 2.0 */ + #include "BLI_span.hh" #include "BLI_strict_flags.h" #include "BLI_vector.hh" @@ -283,4 +285,14 @@ TEST(span, CastLargerSize) EXPECT_EQ(new_a_span.size(), 2u); } +TEST(span, VoidPointerSpan) +{ + int a; + float b; + double c; + + auto func1 = [](Span<void *> span) { EXPECT_EQ(span.size(), 3u); }; + func1({&a, &b, &c}); +} + } // namespace blender diff --git a/tests/gtests/blenlib/BLI_stack_cxx_test.cc b/tests/gtests/blenlib/BLI_stack_cxx_test.cc index b59ac1f7ec1..c8433b4fd87 100644 --- a/tests/gtests/blenlib/BLI_stack_cxx_test.cc +++ b/tests/gtests/blenlib/BLI_stack_cxx_test.cc @@ -1,3 +1,5 @@ +/* Apache License, Version 2.0 */ + #include "BLI_stack.hh" #include "BLI_strict_flags.h" #include "BLI_vector.hh" diff --git a/tests/gtests/blenlib/BLI_string_ref_test.cc b/tests/gtests/blenlib/BLI_string_ref_test.cc index 1f84b8577b4..0d1880229c7 100644 --- a/tests/gtests/blenlib/BLI_string_ref_test.cc +++ b/tests/gtests/blenlib/BLI_string_ref_test.cc @@ -1,3 +1,5 @@ +/* Apache License, Version 2.0 */ + #include "BLI_strict_flags.h" #include "BLI_string_ref.hh" #include "BLI_vector.hh" diff --git a/tests/gtests/blenlib/BLI_vector_set_test.cc b/tests/gtests/blenlib/BLI_vector_set_test.cc index 982081f4b4c..3c3b4d55959 100644 --- a/tests/gtests/blenlib/BLI_vector_set_test.cc +++ b/tests/gtests/blenlib/BLI_vector_set_test.cc @@ -1,3 +1,5 @@ +/* Apache License, Version 2.0 */ + #include "BLI_strict_flags.h" #include "BLI_vector_set.hh" #include "testing/testing.h" diff --git a/tests/gtests/blenlib/BLI_vector_test.cc b/tests/gtests/blenlib/BLI_vector_test.cc index ea4711ca015..25435739a43 100644 --- a/tests/gtests/blenlib/BLI_vector_test.cc +++ b/tests/gtests/blenlib/BLI_vector_test.cc @@ -1,3 +1,5 @@ +/* Apache License, Version 2.0 */ + #include "BLI_strict_flags.h" #include "BLI_vector.hh" #include "testing/testing.h" @@ -57,6 +59,18 @@ TEST(vector, InitializerListConstructor) EXPECT_EQ(vec[3], 6); } +TEST(vector, ConvertingConstructor) +{ + std::array<float, 5> values = {5.4f, 7.3f, -8.1f, 5.0f, 0.0f}; + Vector<int> vec = values; + EXPECT_EQ(vec.size(), 5u); + EXPECT_EQ(vec[0], 5); + EXPECT_EQ(vec[1], 7); + EXPECT_EQ(vec[2], -8); + EXPECT_EQ(vec[3], 5); + EXPECT_EQ(vec[4], 0); +} + struct TestListValue { TestListValue *next, *prev; int value; @@ -499,7 +513,7 @@ class TypeConstructMock { { } - TypeConstructMock(TypeConstructMock &&other) : move_constructed(true) + TypeConstructMock(TypeConstructMock &&other) noexcept : move_constructed(true) { } @@ -513,7 +527,7 @@ class TypeConstructMock { return *this; } - TypeConstructMock &operator=(TypeConstructMock &&other) + TypeConstructMock &operator=(TypeConstructMock &&other) noexcept { if (this == &other) { return *this; @@ -637,4 +651,13 @@ TEST(vector, OveralignedValues) } } +TEST(vector, ConstructVoidPointerVector) +{ + int a; + float b; + double c; + Vector<void *> vec = {&a, &b, &c}; + EXPECT_EQ(vec.size(), 3u); +} + } // namespace blender diff --git a/tests/gtests/blenlib/CMakeLists.txt b/tests/gtests/blenlib/CMakeLists.txt index ba493b22b42..496fe44234e 100644 --- a/tests/gtests/blenlib/CMakeLists.txt +++ b/tests/gtests/blenlib/CMakeLists.txt @@ -43,6 +43,7 @@ BLENDER_TEST(BLI_array "bf_blenlib") BLENDER_TEST(BLI_array_store "bf_blenlib") BLENDER_TEST(BLI_array_utils "bf_blenlib") BLENDER_TEST(BLI_delaunay_2d "bf_blenlib") +BLENDER_TEST(BLI_disjoint_set "bf_blenlib") BLENDER_TEST(BLI_edgehash "bf_blenlib") BLENDER_TEST(BLI_expr_pylike_eval "bf_blenlib") BLENDER_TEST(BLI_ghash "bf_blenlib") @@ -63,6 +64,7 @@ BLENDER_TEST(BLI_math_geom "bf_blenlib") BLENDER_TEST(BLI_math_matrix "bf_blenlib") BLENDER_TEST(BLI_math_vector "bf_blenlib") BLENDER_TEST(BLI_memiter "bf_blenlib") +BLENDER_TEST(BLI_memory_utils "bf_blenlib") BLENDER_TEST(BLI_path_util "${BLI_path_util_extra_libs}") BLENDER_TEST(BLI_polyfill_2d "bf_blenlib") BLENDER_TEST(BLI_set "bf_blenlib") diff --git a/tests/gtests/functions/FN_array_spans_test.cc b/tests/gtests/functions/FN_array_spans_test.cc index fa483ebf0f8..988d48fa452 100644 --- a/tests/gtests/functions/FN_array_spans_test.cc +++ b/tests/gtests/functions/FN_array_spans_test.cc @@ -1,18 +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 - * 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. - */ +/* Apache License, Version 2.0 */ #include "testing/testing.h" diff --git a/tests/gtests/functions/FN_attributes_ref_test.cc b/tests/gtests/functions/FN_attributes_ref_test.cc index eda4592d214..1c05bb930db 100644 --- a/tests/gtests/functions/FN_attributes_ref_test.cc +++ b/tests/gtests/functions/FN_attributes_ref_test.cc @@ -1,18 +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 - * 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. - */ +/* Apache License, Version 2.0 */ #include "BLI_float3.hh" #include "FN_attributes_ref.hh" diff --git a/tests/gtests/functions/FN_cpp_type_test.cc b/tests/gtests/functions/FN_cpp_type_test.cc index f6ae0877ed1..da5ce3416ce 100644 --- a/tests/gtests/functions/FN_cpp_type_test.cc +++ b/tests/gtests/functions/FN_cpp_type_test.cc @@ -1,22 +1,9 @@ -/* - * 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. - */ +/* Apache License, Version 2.0 */ #include "testing/testing.h" #include "FN_cpp_type.hh" +#include "FN_cpp_types.hh" namespace blender::fn { @@ -50,7 +37,7 @@ struct TestType { other.value = copy_constructed_from_value; } - TestType(TestType &&other) + TestType(TestType &&other) noexcept { value = move_constructed_value; other.value = move_constructed_from_value; @@ -63,12 +50,28 @@ struct TestType { return *this; } - TestType &operator=(TestType &&other) + TestType &operator=(TestType &&other) noexcept { value = move_assigned_value; other.value = move_assigned_from_value; return *this; } + + friend std::ostream &operator<<(std::ostream &stream, const TestType &value) + { + stream << value.value; + return stream; + } + + friend bool operator==(const TestType &a, const TestType &b) + { + return false; + } + + uint32_t hash() const + { + return 0; + } }; MAKE_CPP_TYPE(TestType, TestType) @@ -305,4 +308,13 @@ TEST(cpp_type, FillUninitialized) EXPECT_EQ(buffer2[9], 0); } +TEST(cpp_type, DebugPrint) +{ + int value = 42; + std::stringstream ss; + CPPType_int32.debug_print((void *)&value, ss); + std::string text = ss.str(); + EXPECT_EQ(text, "42"); +} + } // namespace blender::fn diff --git a/tests/gtests/functions/FN_generic_vector_array_test.cc b/tests/gtests/functions/FN_generic_vector_array_test.cc index 3308913a72e..7ce7b543218 100644 --- a/tests/gtests/functions/FN_generic_vector_array_test.cc +++ b/tests/gtests/functions/FN_generic_vector_array_test.cc @@ -1,18 +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 - * 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. - */ +/* Apache License, Version 2.0 */ #include "FN_cpp_types.hh" #include "FN_generic_vector_array.hh" diff --git a/tests/gtests/functions/FN_multi_function_network_test.cc b/tests/gtests/functions/FN_multi_function_network_test.cc index 07068aecdb6..5507733c8be 100644 --- a/tests/gtests/functions/FN_multi_function_network_test.cc +++ b/tests/gtests/functions/FN_multi_function_network_test.cc @@ -1,18 +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 - * 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. - */ +/* Apache License, Version 2.0 */ #include "testing/testing.h" diff --git a/tests/gtests/functions/FN_multi_function_test.cc b/tests/gtests/functions/FN_multi_function_test.cc index 07dffeeb948..8cc8f91a300 100644 --- a/tests/gtests/functions/FN_multi_function_test.cc +++ b/tests/gtests/functions/FN_multi_function_test.cc @@ -1,18 +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 - * 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. - */ +/* Apache License, Version 2.0 */ #include "testing/testing.h" @@ -329,4 +315,72 @@ TEST(multi_function, CustomMF_Constant) EXPECT_EQ(outputs[3], 42); } +TEST(multi_function, CustomMF_GenericConstant) +{ + int value = 42; + CustomMF_GenericConstant fn{CPPType_int32, (const void *)&value}; + EXPECT_EQ(fn.param_name(0), "42"); + + Array<int> outputs(4, 0); + + MFParamsBuilder params(fn, outputs.size()); + params.add_uninitialized_single_output(outputs.as_mutable_span()); + + MFContextBuilder context; + + fn.call({0, 1, 2}, params, context); + + EXPECT_EQ(outputs[0], 42); + EXPECT_EQ(outputs[1], 42); + EXPECT_EQ(outputs[2], 42); + EXPECT_EQ(outputs[3], 0); +} + +TEST(multi_function, CustomMF_GenericConstantArray) +{ + std::array<int, 4> values = {3, 4, 5, 6}; + CustomMF_GenericConstantArray fn{GSpan(Span(values))}; + EXPECT_EQ(fn.param_name(0), "[3, 4, 5, 6, ]"); + + GVectorArray g_vector_array{CPPType_int32, 4}; + GVectorArrayRef<int> vector_array = g_vector_array; + + MFParamsBuilder params(fn, g_vector_array.size()); + params.add_vector_output(g_vector_array); + + MFContextBuilder context; + + fn.call({1, 2, 3}, params, context); + + EXPECT_EQ(vector_array[0].size(), 0); + EXPECT_EQ(vector_array[1].size(), 4); + EXPECT_EQ(vector_array[2].size(), 4); + EXPECT_EQ(vector_array[3].size(), 4); + for (uint i = 1; i < 4; i++) { + EXPECT_EQ(vector_array[i][0], 3); + EXPECT_EQ(vector_array[i][1], 4); + EXPECT_EQ(vector_array[i][2], 5); + EXPECT_EQ(vector_array[i][3], 6); + } +} + +TEST(multi_function, CustomMF_Convert) +{ + CustomMF_Convert<float, int> fn; + + Array<float> inputs = {5.4f, 7.1f, 9.0f}; + Array<int> outputs(inputs.size(), 0); + + MFParamsBuilder params(fn, inputs.size()); + params.add_readonly_single_input(inputs.as_span()); + params.add_uninitialized_single_output(outputs.as_mutable_span()); + + MFContextBuilder context; + fn.call({0, 2}, params, context); + + EXPECT_EQ(outputs[0], 5); + EXPECT_EQ(outputs[1], 0); + EXPECT_EQ(outputs[2], 9); +} + } // namespace blender::fn diff --git a/tests/gtests/functions/FN_spans_test.cc b/tests/gtests/functions/FN_spans_test.cc index 43deb80ed23..8968d49c082 100644 --- a/tests/gtests/functions/FN_spans_test.cc +++ b/tests/gtests/functions/FN_spans_test.cc @@ -1,18 +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 - * 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. - */ +/* Apache License, Version 2.0 */ #include "testing/testing.h" diff --git a/tests/gtests/usd/CMakeLists.txt b/tests/gtests/usd/CMakeLists.txt index d2bfe5e1306..0caa2fac155 100644 --- a/tests/gtests/usd/CMakeLists.txt +++ b/tests/gtests/usd/CMakeLists.txt @@ -67,6 +67,7 @@ get_property(BLENDER_SORTED_LIBS GLOBAL PROPERTY BLENDER_SORTED_LIBS_PROP) set(SRC abstract_hierarchy_iterator_test.cc hierarchy_context_order_test.cc + object_identifier_test.cc ) # TODO(Sybren): re-enable this unit test. diff --git a/tests/gtests/usd/object_identifier_test.cc b/tests/gtests/usd/object_identifier_test.cc new file mode 100644 index 00000000000..810d4470260 --- /dev/null +++ b/tests/gtests/usd/object_identifier_test.cc @@ -0,0 +1,236 @@ +/* + * 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) 2019 Blender Foundation. + * All rights reserved. + */ +#include "IO_abstract_hierarchy_iterator.h" + +#include "testing/testing.h" + +#include "BLI_utildefines.h" + +#include <climits> + +namespace blender { +namespace io { + +namespace { + +/* Return object pointer for use in tests. This makes it possible to reliably test for + * order/equality functions while using hard-coded values for simplicity. */ +Object *fake_pointer(int value) +{ + return static_cast<Object *>(POINTER_FROM_INT(value)); +} + +/* PersistentID subclass for use in tests, making it easier to construct test values. */ +class TestPersistentID : public PersistentID { + public: + TestPersistentID(int value0, + int value1, + int value2, + int value3, + int value4, + int value5, + int value6, + int value7) + { + persistent_id_[0] = value0; + persistent_id_[1] = value1; + persistent_id_[2] = value2; + persistent_id_[3] = value3; + persistent_id_[4] = value4; + persistent_id_[5] = value5; + persistent_id_[6] = value6; + persistent_id_[7] = value7; + } + TestPersistentID(int value0, int value1, int value2) + : TestPersistentID(value0, value1, value2, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX) + { + } + TestPersistentID(int value0, int value1) : TestPersistentID(value0, value1, INT_MAX) + { + } + explicit TestPersistentID(int value0) : TestPersistentID(value0, INT_MAX) + { + } +}; + +/* ObjectIdentifier subclass for use in tests, making it easier to construct test values. */ +class TestObjectIdentifier : public ObjectIdentifier { + public: + TestObjectIdentifier(Object *object, Object *duplicated_by, const PersistentID &persistent_id) + : ObjectIdentifier(object, duplicated_by, persistent_id) + { + } +}; + +} // namespace + +class ObjectIdentifierOrderTest : public testing::Test { +}; + +TEST_F(ObjectIdentifierOrderTest, graph_root) +{ + ObjectIdentifier id_root_1 = ObjectIdentifier::for_graph_root(); + ObjectIdentifier id_root_2 = ObjectIdentifier::for_graph_root(); + EXPECT_TRUE(id_root_1 == id_root_2); + EXPECT_FALSE(id_root_1 < id_root_2); + EXPECT_FALSE(id_root_2 < id_root_1); + + ObjectIdentifier id_a = ObjectIdentifier::for_real_object(fake_pointer(1)); + EXPECT_FALSE(id_root_1 == id_a); + EXPECT_TRUE(id_root_1 < id_a); + EXPECT_FALSE(id_a < id_root_1); + + ObjectIdentifier id_accidental_root = ObjectIdentifier::for_real_object(nullptr); + EXPECT_TRUE(id_root_1 == id_accidental_root); + EXPECT_FALSE(id_root_1 < id_accidental_root); + EXPECT_FALSE(id_accidental_root < id_root_1); +} + +TEST_F(ObjectIdentifierOrderTest, real_objects) +{ + ObjectIdentifier id_a = ObjectIdentifier::for_real_object(fake_pointer(1)); + ObjectIdentifier id_b = ObjectIdentifier::for_real_object(fake_pointer(2)); + EXPECT_FALSE(id_a == id_b); + EXPECT_TRUE(id_a < id_b); +} + +TEST_F(ObjectIdentifierOrderTest, duplicated_objects) +{ + ObjectIdentifier id_real_a = ObjectIdentifier::for_real_object(fake_pointer(1)); + TestObjectIdentifier id_dupli_a(fake_pointer(1), fake_pointer(2), TestPersistentID(0)); + TestObjectIdentifier id_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(0)); + TestObjectIdentifier id_different_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(1)); + + EXPECT_FALSE(id_real_a == id_dupli_a); + EXPECT_FALSE(id_dupli_a == id_dupli_b); + EXPECT_TRUE(id_real_a < id_dupli_a); + EXPECT_TRUE(id_real_a < id_dupli_b); + EXPECT_TRUE(id_dupli_a < id_dupli_b); + EXPECT_TRUE(id_dupli_a < id_different_dupli_b); + + EXPECT_FALSE(id_dupli_b == id_different_dupli_b); + EXPECT_FALSE(id_dupli_a == id_different_dupli_b); + EXPECT_TRUE(id_dupli_b < id_different_dupli_b); + EXPECT_FALSE(id_different_dupli_b < id_dupli_b); +} + +TEST_F(ObjectIdentifierOrderTest, behaviour_as_map_keys) +{ + ObjectIdentifier id_root = ObjectIdentifier::for_graph_root(); + ObjectIdentifier id_another_root = ObjectIdentifier::for_graph_root(); + ObjectIdentifier id_real_a = ObjectIdentifier::for_real_object(fake_pointer(1)); + TestObjectIdentifier id_dupli_a(fake_pointer(1), fake_pointer(2), TestPersistentID(0)); + TestObjectIdentifier id_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(0)); + AbstractHierarchyIterator::ExportGraph graph; + + /* This inserts the keys with default values. */ + graph[id_root]; + graph[id_real_a]; + graph[id_dupli_a]; + graph[id_dupli_b]; + graph[id_another_root]; + + EXPECT_EQ(4, graph.size()); + + graph.erase(id_another_root); + EXPECT_EQ(3, graph.size()); + + TestObjectIdentifier id_another_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(0)); + graph.erase(id_another_dupli_b); + EXPECT_EQ(2, graph.size()); +} + +TEST_F(ObjectIdentifierOrderTest, map_copy_and_update) +{ + ObjectIdentifier id_root = ObjectIdentifier::for_graph_root(); + ObjectIdentifier id_real_a = ObjectIdentifier::for_real_object(fake_pointer(1)); + TestObjectIdentifier id_dupli_a(fake_pointer(1), fake_pointer(2), TestPersistentID(0)); + TestObjectIdentifier id_dupli_b(fake_pointer(1), fake_pointer(3), TestPersistentID(0)); + TestObjectIdentifier id_dupli_c(fake_pointer(1), fake_pointer(3), TestPersistentID(1)); + AbstractHierarchyIterator::ExportGraph graph; + + /* This inserts the keys with default values. */ + graph[id_root]; + graph[id_real_a]; + graph[id_dupli_a]; + graph[id_dupli_b]; + graph[id_dupli_c]; + EXPECT_EQ(5, graph.size()); + + AbstractHierarchyIterator::ExportGraph graph_copy = graph; + EXPECT_EQ(5, graph_copy.size()); + + // Updating a value in a copy should not update the original. + HierarchyContext ctx1; + HierarchyContext ctx2; + ctx1.object = fake_pointer(1); + ctx2.object = fake_pointer(2); + + graph_copy[id_root].insert(&ctx1); + EXPECT_EQ(0, graph[id_root].size()); + + // Deleting a key in the copy should not update the original. + graph_copy.erase(id_dupli_c); + EXPECT_EQ(4, graph_copy.size()); + EXPECT_EQ(5, graph.size()); +} + +class PersistentIDTest : public testing::Test { +}; + +TEST_F(PersistentIDTest, is_from_same_instancer) +{ + PersistentID child_id_a = TestPersistentID(42, 327); + PersistentID child_id_b = TestPersistentID(17, 327); + PersistentID child_id_c = TestPersistentID(17); + + EXPECT_TRUE(child_id_a.is_from_same_instancer_as(child_id_b)); + EXPECT_FALSE(child_id_a.is_from_same_instancer_as(child_id_c)); +} + +TEST_F(PersistentIDTest, instancer_id) +{ + PersistentID child_id = TestPersistentID(42, 327); + + PersistentID expect_instancer_id = TestPersistentID(327); + EXPECT_EQ(expect_instancer_id, child_id.instancer_pid()); + + PersistentID empty_id; + EXPECT_EQ(empty_id, child_id.instancer_pid().instancer_pid()); + + EXPECT_LT(child_id, expect_instancer_id); + EXPECT_LT(expect_instancer_id, empty_id); +} + +TEST_F(PersistentIDTest, as_object_name_suffix) +{ + EXPECT_EQ("", PersistentID().as_object_name_suffix()); + EXPECT_EQ("47", TestPersistentID(47).as_object_name_suffix()); + EXPECT_EQ("327-47", TestPersistentID(47, 327).as_object_name_suffix()); + EXPECT_EQ("42-327-47", TestPersistentID(47, 327, 42).as_object_name_suffix()); + + EXPECT_EQ("7-6-5-4-3-2-1-0", TestPersistentID(0, 1, 2, 3, 4, 5, 6, 7).as_object_name_suffix()); + + EXPECT_EQ("0-0-0", TestPersistentID(0, 0, 0).as_object_name_suffix()); + EXPECT_EQ("0-0", TestPersistentID(0, 0).as_object_name_suffix()); + EXPECT_EQ("-3--2--1", TestPersistentID(-1, -2, -3).as_object_name_suffix()); +} + +} // namespace io +} // namespace blender diff --git a/tests/python/alembic_tests.py b/tests/python/alembic_tests.py index 5f8113729c3..66545dc85c7 100644 --- a/tests/python/alembic_tests.py +++ b/tests/python/alembic_tests.py @@ -243,6 +243,63 @@ class DupliGroupExportTest(AbstractAlembicTest): 2.0, 3.0, 0.0, 1.0] ) + @with_tempdir + def test_multiple_duplicated_hierarchies(self, tempdir: pathlib.Path): + abc = tempdir / "multiple-duplicated-hierarchies.abc" + script = "import bpy; bpy.ops.wm.alembic_export(filepath='%s', start=1, end=1)" % abc.as_posix() + self.run_blender('multiple-duplicated-hierarchies.blend', script) + + # This is the expected hierarchy: + # ABC + # `--Triangle + # |--Triangle + # |--Empty-1 + # | `--Pole-1-0 + # | |--Pole + # | `--Block-1-1 + # | `--Block + # |--Empty + # | `--Pole-0 + # | |--Pole + # | `--Block-1 + # | `--Block + # |--Empty-2 + # | `--Pole-2-0 + # | |--Pole + # | `--Block-2-1 + # | `--Block + # `--Empty-0 + # `--Pole-0-0 + # |--Pole + # `--Block-0-1 + # `--Block + + # Now check the resulting Alembic file. + xform = self.abcprop(abc, '/Triangle/Empty-1/Pole-1-0/Block-1-1/.xform') + self.assertEqual(1, xform['.inherits']) + self.assertAlmostEqualFloatArray( + xform['.vals'], + [1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 2.0, 0.0, 1.0] + ) + + # If the property can be gotten, the hierarchy is okay. No need to actually check each xform. + self.abcprop(abc, '/Triangle/.xform') + self.abcprop(abc, '/Triangle/Empty-1/.xform') + self.abcprop(abc, '/Triangle/Empty-1/Pole-1-0/.xform') + self.abcprop(abc, '/Triangle/Empty-1/Pole-1-0/Block-1-1/.xform') + self.abcprop(abc, '/Triangle/Empty/.xform') + self.abcprop(abc, '/Triangle/Empty/Pole-0/.xform') + self.abcprop(abc, '/Triangle/Empty/Pole-0/Block-1/.xform') + self.abcprop(abc, '/Triangle/Empty-2/.xform') + self.abcprop(abc, '/Triangle/Empty-2/Pole-2-0/.xform') + self.abcprop(abc, '/Triangle/Empty-2/Pole-2-0/Block-2-1/.xform') + self.abcprop(abc, '/Triangle/Empty-0/.xform') + self.abcprop(abc, '/Triangle/Empty-0/Pole-0-0/.xform') + self.abcprop(abc, '/Triangle/Empty-0/Pole-0-0/Block-0-1/.xform') + class CurveExportTest(AbstractAlembicTest): @with_tempdir |