diff options
author | Joseph Eagar <joeedh@gmail.com> | 2021-07-30 02:08:14 +0300 |
---|---|---|
committer | Joseph Eagar <joeedh@gmail.com> | 2021-07-30 02:08:14 +0300 |
commit | b243eb2646f69a236b65ade05281f0571f341ee1 (patch) | |
tree | 5c35a9fb9a81dcdbfce4c919cf369a942a8f628b | |
parent | af7198494265c9dd9f98ba5a8231a5b6ef857653 (diff) | |
parent | 0f1c84f171520d03ded669565fa7548584fcc724 (diff) |
Merge branch 'master' into temp_bmesh_multires
Also:
* added BMLog function to save mesh IDs.
- Used by SCULPT_UNDO_DYNTOPO_BEGIN/END instead of
saving the whole mesh, which was the previous behavior.
* SCULPT_UNDO_DYNTOPO_BEGIN no longer pushes a non-dyntopo
geomtry undo node, as this is no longer necassary.
This greatly speeds up going into/out of sculpt mode
with dyntopo enabled, as before it was copying
the mesh twice.
660 files changed, 15037 insertions, 8472 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 26ef42fa233..8c6775a52e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -403,14 +403,14 @@ set(CYCLES_TEST_DEVICES CPU CACHE STRING "Run regression tests on the specified set(CYCLES_CUDA_BINARIES_ARCH sm_30 sm_35 sm_37 sm_50 sm_52 sm_60 sm_61 sm_70 sm_75 sm_86 compute_75 CACHE STRING "CUDA architectures to build binaries for") mark_as_advanced(CYCLES_CUDA_BINARIES_ARCH) unset(PLATFORM_DEFAULT) -option(WITH_CYCLES_LOGGING "Build Cycles with logging support" ON) -option(WITH_CYCLES_DEBUG "Build Cycles with extra debug capabilities" OFF) +option(WITH_CYCLES_LOGGING "Build Cycles with logging support" ON) +option(WITH_CYCLES_DEBUG_NAN "Build Cycles with additional asserts for detecting NaNs and invalid values" OFF) option(WITH_CYCLES_NATIVE_ONLY "Build Cycles with native kernel only (which fits current CPU, use for development only)" OFF) option(WITH_CYCLES_KERNEL_ASAN "Build Cycles kernels with address sanitizer when WITH_COMPILER_ASAN is on, even if it's very slow" OFF) mark_as_advanced(WITH_CYCLES_KERNEL_ASAN) mark_as_advanced(WITH_CYCLES_CUBIN_COMPILER) mark_as_advanced(WITH_CYCLES_LOGGING) -mark_as_advanced(WITH_CYCLES_DEBUG) +mark_as_advanced(WITH_CYCLES_DEBUG_NAN) mark_as_advanced(WITH_CYCLES_NATIVE_ONLY) option(WITH_CYCLES_DEVICE_CUDA "Enable Cycles CUDA compute support" ON) diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt index 8c739dcd68e..3a9574b4b2a 100644 --- a/build_files/build_environment/CMakeLists.txt +++ b/build_files/build_environment/CMakeLists.txt @@ -82,7 +82,11 @@ if(UNIX) endif() include(cmake/openimageio.cmake) include(cmake/tiff.cmake) -include(cmake/flexbison.cmake) +if(WIN32) + include(cmake/flexbison.cmake) +else() + include(cmake/flex.cmake) +endif() include(cmake/osl.cmake) include(cmake/tbb.cmake) include(cmake/openvdb.cmake) diff --git a/build_files/build_environment/cmake/download.cmake b/build_files/build_environment/cmake/download.cmake index b4d96ea856f..2a84202ea02 100644 --- a/build_files/build_environment/cmake/download.cmake +++ b/build_files/build_environment/cmake/download.cmake @@ -93,3 +93,4 @@ download_source(GMP) download_source(POTRACE) download_source(HARU) download_source(ZSTD) +download_source(FLEX) diff --git a/build_files/build_environment/cmake/flex.cmake b/build_files/build_environment/cmake/flex.cmake new file mode 100644 index 00000000000..5daad6ce64b --- /dev/null +++ b/build_files/build_environment/cmake/flex.cmake @@ -0,0 +1,28 @@ +# ***** 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_flex + URL file://${PACKAGE_DIR}/${FLEX_FILE} + URL_HASH ${FLEX_HASH_TYPE}=${FLEX_HASH} + DOWNLOAD_DIR ${DOWNLOAD_DIR} + PREFIX ${BUILD_DIR}/flex + CONFIGURE_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/flex/src/external_flex/ && ${CONFIGURE_COMMAND} --prefix=${LIBDIR}/flex + BUILD_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/flex/src/external_flex/ && make -j${MAKE_THREADS} + INSTALL_COMMAND ${CONFIGURE_ENV} && cd ${BUILD_DIR}/flex/src/external_flex/ && make install + INSTALL_DIR ${LIBDIR}/flex +) diff --git a/build_files/build_environment/cmake/harvest.cmake b/build_files/build_environment/cmake/harvest.cmake index a19c332457d..a3740559adc 100644 --- a/build_files/build_environment/cmake/harvest.cmake +++ b/build_files/build_environment/cmake/harvest.cmake @@ -106,6 +106,7 @@ harvest(llvm/include llvm/include "*") harvest(llvm/bin llvm/bin "llvm-config") harvest(llvm/lib llvm/lib "libLLVM*.a") harvest(llvm/lib llvm/lib "libclang*.a") +harvest(llvm/lib/clang llvm/lib/clang "*.h") if(APPLE) harvest(openmp/lib openmp/lib "*") harvest(openmp/include openmp/include "*.h") diff --git a/build_files/build_environment/cmake/ispc.cmake b/build_files/build_environment/cmake/ispc.cmake index e8a4dedcdaa..93fc9dc4846 100644 --- a/build_files/build_environment/cmake/ispc.cmake +++ b/build_files/build_environment/cmake/ispc.cmake @@ -29,12 +29,13 @@ elseif(APPLE) if("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "arm64") set(ISPC_EXTRA_ARGS_APPLE -DBISON_EXECUTABLE=/opt/homebrew/opt/bison/bin/bison - -DFLEX_EXECUTABLE=/opt/homebrew/opt/flex/bin/flex + -DFLEX_EXECUTABLE=${LIBDIR}/flex/bin/flex -DARM_ENABLED=On ) else() set(ISPC_EXTRA_ARGS_APPLE -DBISON_EXECUTABLE=/usr/local/opt/bison/bin/bison + -DFLEX_EXECUTABLE=${LIBDIR}/flex/bin/flex -DARM_ENABLED=Off ) endif() @@ -43,6 +44,7 @@ elseif(UNIX) -DCMAKE_C_COMPILER=${LIBDIR}/llvm/bin/clang -DCMAKE_CXX_COMPILER=${LIBDIR}/llvm/bin/clang++ -DARM_ENABLED=Off + -DFLEX_EXECUTABLE=${LIBDIR}/flex/bin/flex ) endif() @@ -82,4 +84,9 @@ if(WIN32) external_ispc external_flexbison ) +else() + add_dependencies( + external_ispc + external_flex + ) endif() diff --git a/build_files/build_environment/cmake/llvm.cmake b/build_files/build_environment/cmake/llvm.cmake index cbb986410aa..7a8ce2ddfec 100644 --- a/build_files/build_environment/cmake/llvm.cmake +++ b/build_files/build_environment/cmake/llvm.cmake @@ -66,7 +66,11 @@ ExternalProject_Add(ll if(MSVC) if(BUILD_MODE STREQUAL Release) - set(LLVM_HARVEST_COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/llvm/ ${HARVEST_TARGET}/llvm/ ) + set(LLVM_HARVEST_COMMAND + ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/llvm/lib ${HARVEST_TARGET}/llvm/lib && + ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/llvm/include ${HARVEST_TARGET}/llvm/include && + ${CMAKE_COMMAND} -E copy ${LIBDIR}/llvm/bin/clang-format.exe ${HARVEST_TARGET}/llvm/bin/clang-format.exe + ) else() set(LLVM_HARVEST_COMMAND ${CMAKE_COMMAND} -E copy_directory ${LIBDIR}/llvm/lib/ ${HARVEST_TARGET}/llvm/debug/lib/ && diff --git a/build_files/build_environment/cmake/openimagedenoise.cmake b/build_files/build_environment/cmake/openimagedenoise.cmake index f927d6c231c..aca2b4cef9f 100644 --- a/build_files/build_environment/cmake/openimagedenoise.cmake +++ b/build_files/build_environment/cmake/openimagedenoise.cmake @@ -45,7 +45,6 @@ ExternalProject_Add(external_openimagedenoise DOWNLOAD_DIR ${DOWNLOAD_DIR} URL_HASH ${OIDN_HASH_TYPE}=${OIDN_HASH} PREFIX ${BUILD_DIR}/openimagedenoise - PATCH_COMMAND ${PATCH_CMD} -p 1 -N -d ${BUILD_DIR}/openimagedenoise/src/external_openimagedenoise < ${PATCH_DIR}/oidn.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/openimagedenoise ${DEFAULT_CMAKE_FLAGS} ${OIDN_EXTRA_ARGS} INSTALL_DIR ${LIBDIR}/openimagedenoise ) diff --git a/build_files/build_environment/cmake/openmp.cmake b/build_files/build_environment/cmake/openmp.cmake index 96081c7fc49..0e5323ca513 100644 --- a/build_files/build_environment/cmake/openmp.cmake +++ b/build_files/build_environment/cmake/openmp.cmake @@ -22,9 +22,8 @@ ExternalProject_Add(external_openmp DOWNLOAD_DIR ${DOWNLOAD_DIR} URL_HASH ${OPENMP_HASH_TYPE}=${OPENMP_HASH} PREFIX ${BUILD_DIR}/openmp - PATCH_COMMAND ${PATCH_CMD} -p 1 -d ${BUILD_DIR}/openmp/src/external_openmp < ${PATCH_DIR}/openmp.diff CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${LIBDIR}/openmp ${DEFAULT_CMAKE_FLAGS} - INSTALL_COMMAND cd ${BUILD_DIR}/openmp/src/external_openmp-build && install_name_tool -id @executable_path/../Resources/lib/libomp.dylib runtime/src/libomp.dylib && make install + INSTALL_COMMAND cd ${BUILD_DIR}/openmp/src/external_openmp-build && install_name_tool -id @rpath/libomp.dylib runtime/src/libomp.dylib && make install INSTALL_DIR ${LIBDIR}/openmp ) diff --git a/build_files/build_environment/cmake/osl.cmake b/build_files/build_environment/cmake/osl.cmake index 54e924b348a..05aedb1f085 100644 --- a/build_files/build_environment/cmake/osl.cmake +++ b/build_files/build_environment/cmake/osl.cmake @@ -20,12 +20,10 @@ if(WIN32) set(OSL_CMAKE_CXX_STANDARD_LIBRARIES "kernel32${LIBEXT} user32${LIBEXT} gdi32${LIBEXT} winspool${LIBEXT} shell32${LIBEXT} ole32${LIBEXT} oleaut32${LIBEXT} uuid${LIBEXT} comdlg32${LIBEXT} advapi32${LIBEXT} psapi${LIBEXT}") set(OSL_FLEX_BISON -DFLEX_EXECUTABLE=${LIBDIR}/flexbison/win_flex.exe -DBISON_EXECUTABLE=${LIBDIR}/flexbison/win_bison.exe) set(OSL_SIMD_FLAGS -DOIIO_NOSIMD=1 -DOIIO_SIMD=sse2) - SET(OSL_PLATFORM_FLAGS -DLINKSTATIC=ON) else() set(OSL_CMAKE_CXX_STANDARD_LIBRARIES) set(OSL_FLEX_BISON) set(OSL_OPENIMAGEIO_LIBRARY "${LIBDIR}/openimageio/lib/${LIBPREFIX}OpenImageIO${LIBEXT};${LIBDIR}/openimageio/lib/${LIBPREFIX}OpenImageIO_Util${LIBEXT};${LIBDIR}/png/lib/${LIBPREFIX}png16${LIBEXT};${LIBDIR}/jpg/lib/${LIBPREFIX}jpeg${LIBEXT};${LIBDIR}/tiff/lib/${LIBPREFIX}tiff${LIBEXT};${LIBDIR}/openexr/lib/${LIBPREFIX}IlmImf${OPENEXR_VERSION_POSTFIX}${LIBEXT}") - SET(OSL_PLATFORM_FLAGS) endif() set(OSL_ILMBASE_CUSTOM_LIBRARIES "${LIBDIR}/openexr/lib/Imath${OPENEXR_VERSION_POSTFIX}.lib^^${LIBDIR}/openexr/lib/Half{OPENEXR_VERSION_POSTFIX}.lib^^${LIBDIR}/openexr/lib/IlmThread${OPENEXR_VERSION_POSTFIX}.lib^^${LIBDIR}/openexr/lib/Iex${OPENEXR_VERSION_POSTFIX}.lib") @@ -51,12 +49,13 @@ set(OSL_EXTRA_ARGS -DOpenImageIO_ROOT=${LIBDIR}/openimageio/ -DOSL_BUILD_TESTS=OFF -DOSL_BUILD_MATERIALX=OFF + -DPNG_ROOT=${LIBDIR}/png -DZLIB_LIBRARY=${LIBDIR}/zlib/lib/${ZLIB_LIBRARY} -DZLIB_INCLUDE_DIR=${LIBDIR}/zlib/include/ ${OSL_FLEX_BISON} -DCMAKE_CXX_STANDARD_LIBRARIES=${OSL_CMAKE_CXX_STANDARD_LIBRARIES} -DBUILD_SHARED_LIBS=OFF - ${OSL_PLATFORM_FLAGS} + -DLINKSTATIC=ON -DOSL_BUILD_PLUGINS=OFF -DSTOP_ON_WARNING=OFF -DUSE_LLVM_BITCODE=OFF @@ -69,13 +68,9 @@ set(OSL_EXTRA_ARGS ${OSL_SIMD_FLAGS} -Dpugixml_ROOT=${LIBDIR}/pugixml -DUSE_PYTHON=OFF + -DCMAKE_CXX_STANDARD=14 ) -# Apple arm64 uses LLVM 11, LLVM 10+ requires C++14 -if (APPLE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64") - list(APPEND OSL_EXTRA_ARGS -DCMAKE_CXX_STANDARD=14) -endif() - ExternalProject_Add(external_osl URL file://${PACKAGE_DIR}/${OSL_FILE} DOWNLOAD_DIR ${DOWNLOAD_DIR} @@ -93,10 +88,20 @@ add_dependencies( ll external_openexr external_zlib - external_flexbison external_openimageio external_pugixml ) +if(WIN32) + add_dependencies( + external_osl + external_flexbison + ) +else() + add_dependencies( + external_osl + external_flex + ) +endif() if(WIN32) if(BUILD_MODE STREQUAL Release) diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index a71fc4b85e4..bc220c596c1 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -152,35 +152,20 @@ set(OPENCOLORIO_HASH 1a2e3478b6cd9a1549f24e1b2205e3f0) set(OPENCOLORIO_HASH_TYPE MD5) set(OPENCOLORIO_FILE OpenColorIO-${OPENCOLORIO_VERSION}.tar.gz) -if(BLENDER_PLATFORM_ARM) - # Newer version required by ISPC with arm support. - set(LLVM_VERSION 11.0.1) - set(LLVM_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz) - set(LLVM_HASH e700af40ab83463e4e9ab0ba3708312e) - set(LLVM_HASH_TYPE MD5) - set(LLVM_FILE llvm-project-${LLVM_VERSION}.src.tar.xz) - - set(OPENMP_VERSION 9.0.1) - set(OPENMP_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${OPENMP_VERSION}/openmp-${OPENMP_VERSION}.src.tar.xz) - set(OPENMP_HASH 6eade16057edbdecb3c4eef9daa2bfcf) - set(OPENMP_HASH_TYPE MD5) - set(OPENMP_FILE openmp-${OPENMP_VERSION}.src.tar.xz) -else() - set(LLVM_VERSION 9.0.1) - set(LLVM_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.tar.xz) - set(LLVM_HASH b4268e733dfe352960140dc07ef2efcb) - set(LLVM_HASH_TYPE MD5) - set(LLVM_FILE llvm-project-${LLVM_VERSION}.tar.xz) - - set(OPENMP_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/openmp-${LLVM_VERSION}.src.tar.xz) - set(OPENMP_HASH 6eade16057edbdecb3c4eef9daa2bfcf) - set(OPENMP_HASH_TYPE MD5) - set(OPENMP_FILE openmp-${LLVM_VERSION}.src.tar.xz) -endif() - -set(OPENIMAGEIO_VERSION 2.1.15.0) +set(LLVM_VERSION 12.0.0) +set(LLVM_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz) +set(LLVM_HASH 5a4fab4d7fc84aefffb118ac2c8a4fc0) +set(LLVM_HASH_TYPE MD5) +set(LLVM_FILE llvm-project-${LLVM_VERSION}.src.tar.xz) + +set(OPENMP_URI https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/openmp-${LLVM_VERSION}.src.tar.xz) +set(OPENMP_HASH ac48ce3e4582ccb82f81ab59eb3fc9dc) +set(OPENMP_HASH_TYPE MD5) +set(OPENMP_FILE openmp-${LLVM_VERSION}.src.tar.xz) + +set(OPENIMAGEIO_VERSION 2.2.15.1) set(OPENIMAGEIO_URI https://github.com/OpenImageIO/oiio/archive/Release-${OPENIMAGEIO_VERSION}.tar.gz) -set(OPENIMAGEIO_HASH f03aa5e3ac4795af04771ee4146e9832) +set(OPENIMAGEIO_HASH 3db5c5f0b3dc91597c75e5df09eb9072) set(OPENIMAGEIO_HASH_TYPE MD5) set(OPENIMAGEIO_FILE OpenImageIO-${OPENIMAGEIO_VERSION}.tar.gz) @@ -190,9 +175,9 @@ set(TIFF_HASH 2165e7aba557463acc0664e71a3ed424) set(TIFF_HASH_TYPE MD5) set(TIFF_FILE tiff-${TIFF_VERSION}.tar.gz) -set(OSL_VERSION 1.11.10.0) +set(OSL_VERSION 1.11.14.1) set(OSL_URI https://github.com/imageworks/OpenShadingLanguage/archive/Release-${OSL_VERSION}.tar.gz) -set(OSL_HASH dfdc23597aeef083832cbada62211756) +set(OSL_HASH 1abd7ce40481771a9fa937f19595d2f2) set(OSL_HASH_TYPE MD5) set(OSL_FILE OpenShadingLanguage-${OSL_VERSION}.tar.gz) @@ -370,12 +355,18 @@ set(PUGIXML_HASH 0c208b0664c7fb822bf1b49ad035e8fd) set(PUGIXML_HASH_TYPE MD5) set(PUGIXML_FILE pugixml-${PUGIXML_VERSION}.tar.gz) -set(FLEXBISON_VERSION 2.5.5) +set(FLEXBISON_VERSION 2.5.24) set(FLEXBISON_URI http://prdownloads.sourceforge.net/winflexbison/win_flex_bison-${FLEXBISON_VERSION}.zip) -set(FLEXBISON_HASH d87a3938194520d904013abef3df10ce) +set(FLEXBISON_HASH 6b549d43e34ece0e8ed05af92daa31c4) set(FLEXBISON_HASH_TYPE MD5) set(FLEXBISON_FILE win_flex_bison-${FLEXBISON_VERSION}.zip) +set(FLEX_VERSION 2.6.4) +set(FLEX_URI https://github.com/westes/flex/releases/download/v${FLEX_VERSION}/flex-${FLEX_VERSION}.tar.gz) +set(FLEX_HASH 2882e3179748cc9f9c23ec593d6adc8d) +set(FLEX_HASH_TYPE MD5) +set(FLEX_FILE flex-${FLEX_VERSION}.tar.gz) + # Libraries to keep Python modules static on Linux. # NOTE: bzip.org domain does no longer belong to BZip 2 project, so we download @@ -432,9 +423,9 @@ set(USD_HASH 1dd1e2092d085ed393c1f7c450a4155a) set(USD_HASH_TYPE MD5) set(USD_FILE usd-v${USD_VERSION}.tar.gz) -set(OIDN_VERSION 1.4.0) +set(OIDN_VERSION 1.4.1) set(OIDN_URI https://github.com/OpenImageDenoise/oidn/releases/download/v${OIDN_VERSION}/oidn-${OIDN_VERSION}.src.tar.gz) -set(OIDN_HASH 421824019becc5b664a22a2b98332bc5) +set(OIDN_HASH df4007b0ab93b1c41cdf223b075d01c0) set(OIDN_HASH_TYPE MD5) set(OIDN_FILE oidn-${OIDN_VERSION}.src.tar.gz) @@ -444,10 +435,10 @@ set(LIBGLU_HASH 151aef599b8259efe9acd599c96ea2a3) set(LIBGLU_HASH_TYPE MD5) set(LIBGLU_FILE glu-${LIBGLU_VERSION}.tar.xz) -set(MESA_VERSION 20.3.4) +set(MESA_VERSION 21.1.5) set(MESA_URI ftp://ftp.freedesktop.org/pub/mesa/mesa-${MESA_VERSION}.tar.xz) -set(MESA_HASH 556338446aef8ae947a789b3e0b5e056) -set(MESA_HASH_TYPE MD5) +set(MESA_HASH 022c7293074aeeced2278c872db4fa693147c70f8595b076cf3f1ef81520766d) +set(MESA_HASH_TYPE SHA256) set(MESA_FILE mesa-${MESA_VERSION}.tar.xz) set(NASM_VERSION 2.15.02) @@ -468,19 +459,11 @@ set(WL_PROTOCOLS_URI https://gitlab.freedesktop.org/wayland/wayland-protocols/-/ set(WL_PROTOCOLS_HASH af5ca07e13517cdbab33504492cef54a) set(WL_PROTOCOLS_HASH_TYPE MD5) -if(BLENDER_PLATFORM_ARM) - # Unreleased version with macOS arm support. - set(ISPC_URI https://github.com/ispc/ispc/archive/f5949c055eb9eeb93696978a3da4bfb3a6a30b35.zip) - set(ISPC_HASH d382fea18d01dbd0cd05d9e1ede36d7d) - set(ISPC_HASH_TYPE MD5) - set(ISPC_FILE f5949c055eb9eeb93696978a3da4bfb3a6a30b35.zip) -else() - set(ISPC_VERSION v1.14.1) - set(ISPC_URI https://github.com/ispc/ispc/archive/${ISPC_VERSION}.tar.gz) - set(ISPC_HASH 968fbc8dfd16a60ba4e32d2e0e03ea7a) - set(ISPC_HASH_TYPE MD5) - set(ISPC_FILE ispc-${ISPC_VERSION}.tar.gz) -endif() +set(ISPC_VERSION v1.16.0) +set(ISPC_URI https://github.com/ispc/ispc/archive/${ISPC_VERSION}.tar.gz) +set(ISPC_HASH 2e3abedbc0ea9aaec17d6562c632454d) +set(ISPC_HASH_TYPE MD5) +set(ISPC_FILE ispc-${ISPC_VERSION}.tar.gz) set(GMP_VERSION 6.2.0) set(GMP_URI https://gmplib.org/download/gmp/gmp-${GMP_VERSION}.tar.xz) diff --git a/build_files/build_environment/patches/oidn.diff b/build_files/build_environment/patches/oidn.diff deleted file mode 100644 index db1015b8958..00000000000 --- a/build_files/build_environment/patches/oidn.diff +++ /dev/null @@ -1,10 +0,0 @@ ---- external_openimagedenoise/cmake/oidn_ispc.cmake 2021-02-15 17:29:34.000000000 +0100 -+++ external_openimagedenoise/cmake/oidn_ispc.cmake2 2021-02-15 17:29:28.000000000 +0100 -@@ -98,7 +98,7 @@ - elseif(OIDN_ARCH STREQUAL "ARM64") - set(ISPC_ARCHITECTURE "aarch64") - if(APPLE) -- set(ISPC_TARGET_OS "--target-os=ios") -+ set(ISPC_TARGET_OS "--target-os=macos") - endif() - endif() diff --git a/build_files/build_environment/patches/openimageio.diff b/build_files/build_environment/patches/openimageio.diff index 9db037db288..d05fc1f295f 100644 --- a/build_files/build_environment/patches/openimageio.diff +++ b/build_files/build_environment/patches/openimageio.diff @@ -34,24 +34,3 @@ diff -Naur orig/src/include/OpenImageIO/platform.h external_openimageio/src/incl # include <windows.h> #endif -diff -Naur orig/src/libutil/ustring.cpp external_openimageio/src/libutil/ustring.cpp ---- orig/src/libutil/ustring.cpp 2020-05-11 05:43:52.000000000 +0200 -+++ external_openimageio/src/libutil/ustring.cpp 2020-11-26 12:06:08.000000000 +0100 -@@ -337,6 +337,8 @@ - // the std::string to make it point to our chars! In such a case, the - // destructor will be careful not to allow a deallocation. - -+ // Disable internal std::string for Apple silicon based Macs -+#if !(defined(__APPLE__) && defined(__arm64__)) - #if defined(__GNUC__) && !defined(_LIBCPP_VERSION) \ - && defined(_GLIBCXX_USE_CXX11_ABI) && _GLIBCXX_USE_CXX11_ABI - // NEW gcc ABI -@@ -382,7 +384,7 @@ - return; - } - #endif -- -+#endif - // Remaining cases - just assign the internal string. This may result - // in double allocation for the chars. If you care about that, do - // something special for your platform, much like we did for gcc and
\ No newline at end of file diff --git a/build_files/build_environment/patches/openmp.diff b/build_files/build_environment/patches/openmp.diff deleted file mode 100644 index 201ab5c7713..00000000000 --- a/build_files/build_environment/patches/openmp.diff +++ /dev/null @@ -1,23 +0,0 @@ -diff --git a/runtime/src/z_Linux_asm.S b/runtime/src/z_Linux_asm.S -index 0d8885e..42aa5ad 100644 ---- a/runtime/src/z_Linux_asm.S -+++ b/runtime/src/z_Linux_asm.S -@@ -1540,10 +1540,12 @@ __kmp_unnamed_critical_addr: - .comm .gomp_critical_user_,32,8 - .data - .align 8 -- .global __kmp_unnamed_critical_addr --__kmp_unnamed_critical_addr: -+ .global ___kmp_unnamed_critical_addr -+___kmp_unnamed_critical_addr: - .8byte .gomp_critical_user_ -- .size __kmp_unnamed_critical_addr,8 -+# if !(KMP_OS_DARWIN) -+ .size ___kmp_unnamed_critical_addr,8 -+# endif - #endif /* KMP_ARCH_PPC64 || KMP_ARCH_AARCH64 */ - - #if KMP_OS_LINUX - - - diff --git a/build_files/build_environment/patches/osl.diff b/build_files/build_environment/patches/osl.diff index cd1b58bf580..8553115b50d 100644 --- a/build_files/build_environment/patches/osl.diff +++ b/build_files/build_environment/patches/osl.diff @@ -63,19 +63,50 @@ diff -Naur org/CMakeLists.txt external_osl/CMakeLists.txt set (OSL_NO_DEFAULT_TEXTURESYSTEM OFF CACHE BOOL "Do not use create a raw OIIO::TextureSystem") if (OSL_NO_DEFAULT_TEXTURESYSTEM) -diff --git a/src/liboslexec/llvm_util.cpp b/src/liboslexec/llvm_util.cpp -index 445f6400..3d468de2 100644 ---- a/src/liboslexec/llvm_util.cpp -+++ b/src/liboslexec/llvm_util.cpp -@@ -3430,8 +3430,9 @@ LLVM_Util::call_function (llvm::Value *func, cspan<llvm::Value *> args) - #endif - //llvm_gen_debug_printf (std::string("start ") + std::string(name)); - #if OSL_LLVM_VERSION >= 110 -- OSL_DASSERT(llvm::isa<llvm::Function>(func)); -- llvm::Value *r = builder().CreateCall(llvm::cast<llvm::Function>(func), llvm::ArrayRef<llvm::Value *>(args.data(), args.size())); -+ llvm::Value* r = builder().CreateCall( -+ llvm::cast<llvm::FunctionType>(func->getType()->getPointerElementType()), func, -+ llvm::ArrayRef<llvm::Value*>(args.data(), args.size())); - #else - llvm::Value *r = builder().CreateCall (func, llvm::ArrayRef<llvm::Value *>(args.data(), args.size())); +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 990f50d69..46ef7351d 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -252,11 +252,9 @@ install (EXPORT OSL_EXPORTED_TARGETS + FILE ${OSL_TARGETS_EXPORT_NAME} + NAMESPACE ${PROJECT_NAME}::) + +- +- +- +-osl_add_all_tests() +- ++if (${PROJECT_NAME}_BUILD_TESTS AND NOT ${PROJECT_NAME}_IS_SUBPROJECT) ++ osl_add_all_tests() ++endif () + + if (NOT ${PROJECT_NAME}_IS_SUBPROJECT) + include (packaging) +diff -Naur external_osl_orig/src/cmake/externalpackages.cmake external_osl/src/cmake/externalpackages.cmake +--- external_osl_orig/src/cmake/externalpackages.cmake 2021-06-01 13:44:18 -0600 ++++ external_osl/src/cmake/externalpackages.cmake 2021-06-28 07:44:32 -0600 +@@ -80,6 +80,7 @@ + + + checked_find_package (ZLIB REQUIRED) # Needed by several packages ++checked_find_package (PNG REQUIRED) # Needed since OIIO needs it + + # IlmBase & OpenEXR + checked_find_package (OpenEXR REQUIRED +diff -Naur external_osl_orig/src/liboslcomp/oslcomp.cpp external_osl/src/liboslcomp/oslcomp.cpp +--- external_osl_orig/src/liboslcomp/oslcomp.cpp 2021-06-01 13:44:18 -0600 ++++ external_osl/src/liboslcomp/oslcomp.cpp 2021-06-28 09:11:06 -0600 +@@ -21,6 +21,13 @@ + #if !defined(__STDC_CONSTANT_MACROS) + # define __STDC_CONSTANT_MACROS 1 #endif ++ ++// clang uses CALLBACK in its templates which causes issues if it is already defined ++#ifdef _WIN32 && defined(CALLBACK) ++# undef CALLBACK ++#endif ++ ++// + #include <clang/Basic/TargetInfo.h> + #include <clang/Frontend/CompilerInstance.h> + #include <clang/Frontend/TextDiagnosticPrinter.h> diff --git a/build_files/cmake/cmake_netbeans_project.py b/build_files/cmake/cmake_netbeans_project.py index cfc3ff6b6fa..a16fc42c9b7 100755 --- a/build_files/cmake/cmake_netbeans_project.py +++ b/build_files/cmake/cmake_netbeans_project.py @@ -82,7 +82,7 @@ def create_nb_project_main(): make_exe = cmake_cache_var("CMAKE_MAKE_PROGRAM") make_exe_basename = os.path.basename(make_exe) - # --------------- NB specific + # --------------- NetBeans specific. defines = [("%s=%s" % cdef) if cdef[1] else cdef[0] for cdef in defines] defines += [cdef.replace("#define", "").strip() for cdef in cmake_compiler_defines()] diff --git a/build_files/cmake/config/blender_developer.cmake b/build_files/cmake/config/blender_developer.cmake index 94f87a2d362..7be9516d9dc 100644 --- a/build_files/cmake/config/blender_developer.cmake +++ b/build_files/cmake/config/blender_developer.cmake @@ -7,7 +7,6 @@ set(WITH_ASSERT_ABORT ON CACHE BOOL "" FORCE) set(WITH_BUILDINFO OFF CACHE BOOL "" FORCE) set(WITH_COMPILER_ASAN ON CACHE BOOL "" FORCE) -set(WITH_CYCLES_DEBUG ON CACHE BOOL "" FORCE) set(WITH_CYCLES_NATIVE_ONLY ON CACHE BOOL "" FORCE) set(WITH_DOC_MANPAGE OFF CACHE BOOL "" FORCE) set(WITH_GTESTS ON CACHE BOOL "" FORCE) diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake index 90188751fc0..70973eeda99 100644 --- a/build_files/cmake/platform/platform_apple.cmake +++ b/build_files/cmake/platform/platform_apple.cmake @@ -404,7 +404,7 @@ endif() # CMake FindOpenMP doesn't know about AppleClang before 3.12, so provide custom flags. if(WITH_OPENMP) - if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "7.0") + if(CMAKE_C_COMPILER_ID MATCHES "Clang") # Use OpenMP from our precompiled libraries. message(STATUS "Using ${LIBDIR}/openmp for OpenMP") set(OPENMP_CUSTOM ON) @@ -480,10 +480,8 @@ else() set(CMAKE_CXX_FLAGS_RELEASE "-O2 -mdynamic-no-pic") endif() -if(${XCODE_VERSION} VERSION_EQUAL 5 OR ${XCODE_VERSION} VERSION_GREATER 5) - # Xcode 5 is always using CLANG, which has too low template depth of 128 for libmv - string(APPEND CMAKE_CXX_FLAGS " -ftemplate-depth=1024") -endif() +# Clang has too low template depth of 128 for libmv. +string(APPEND CMAKE_CXX_FLAGS " -ftemplate-depth=1024") # Avoid conflicts with Luxrender, and other plug-ins that may use the same # libraries as Blender with a different version or build options. diff --git a/build_files/config/pipeline_config.json b/build_files/config/pipeline_config.json index c9a5e25eaaf..d914cf85eae 100644 --- a/build_files/config/pipeline_config.json +++ b/build_files/config/pipeline_config.json @@ -41,7 +41,7 @@ }, "cuda11": { - "version": "11.3" + "version": "11.4" } }, "cmake": diff --git a/build_files/config/pipeline_config.yaml b/build_files/config/pipeline_config.yaml new file mode 100644 index 00000000000..611df59caec --- /dev/null +++ b/build_files/config/pipeline_config.yaml @@ -0,0 +1,70 @@ +# +# Used by Buildbot build pipeline make_update.py script only for now +# We intended to update the make_update.py in the branches to use this file eventually +# +update-code: + git: + submodules: + - branch: master + commit_id: HEAD + path: release/scripts/addons + - branch: master + commit_id: HEAD + path: release/scripts/addons_contrib + - branch: master + commit_id: HEAD + path: release/datafiles/locale + - branch: master + commit_id: HEAD + path: source/tools + svn: + libraries: + darwin-arm64: + branch: trunk + commit_id: HEAD + path: lib/darwin_arm64 + darwin-x86_64: + branch: trunk + commit_id: HEAD + path: lib/darwin + linux-x86_64: + branch: trunk + commit_id: HEAD + path: lib/linux_centos7_x86_64 + windows-amd64: + branch: trunk + commit_id: HEAD + path: lib/win64_vc15 + tests: + branch: trunk + commit_id: HEAD + path: lib/tests + benchmarks: + branch: trunk + commit_id: HEAD + path: lib/benchmarks + +# +# Buildbot only configs +# +buildbot: + gcc: + version: '9.0.0' + cuda10: + version: '10.1.0' + cuda11: + version: '11.4.0' + optix: + version: '7.1.0' + cmake: + default: + version: any + overrides: {} + darwin-arm64: + overrides: {} + darwin-x86_64: + overrides: {} + linux-x86_64: + overrides: {} + windows-amd64: + overrides: {} diff --git a/doc/manpage/blender.1.py b/doc/manpage/blender.1.py index 950a2119f91..020bab05a21 100755 --- a/doc/manpage/blender.1.py +++ b/doc/manpage/blender.1.py @@ -122,7 +122,7 @@ is a full-featured 3D application. It supports the entirety of the 3D pipeline - '''modeling, rigging, animation, simulation, rendering, compositing, motion tracking, and video editing. Use Blender to create 3D images and animations, films and commercials, content for games, ''' -r'''architectural and industrial visualizatons, and scientific visualizations. +r'''architectural and industrial visualizations, and scientific visualizations. https://www.blender.org''') diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 4be27e0f0e8..d6c1d7b51b8 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1047,6 +1047,7 @@ context_type_map = { "annotation_data": ("GreasePencil", False), "annotation_data_owner": ("ID", False), "armature": ("Armature", False), + "asset_library": ("AssetLibraryReference", False), "bone": ("Bone", False), "brush": ("Brush", False), "camera": ("Camera", False), @@ -1113,6 +1114,7 @@ context_type_map = { "texture_slot": ("MaterialTextureSlot", False), "texture_user": ("ID", False), "texture_user_property": ("Property", False), + "ui_list": ("UIList", False), "vertex_paint_object": ("Object", False), "view_layer": ("ViewLayer", False), "visible_bones": ("EditBone", True), diff --git a/extern/rangetree/intern/generic_alloc_impl.h b/extern/rangetree/intern/generic_alloc_impl.h index 0f9f5184637..fd6e85a155a 100644 --- a/extern/rangetree/intern/generic_alloc_impl.h +++ b/extern/rangetree/intern/generic_alloc_impl.h @@ -32,7 +32,7 @@ * - #TPOOL_STRUCT: Name for pool struct name. * - #TPOOL_CHUNK_SIZE: Chunk size (optional), use 64kb when not defined. * - * \note #TPOOL_ALLOC_TYPE must be at least ``sizeof(void *)``. + * \note #TPOOL_ALLOC_TYPE must be at least `sizeof(void *)`. * * Defines the API, uses #TPOOL_IMPL_PREFIX to prefix each function. * diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt index b01bf1bd1e2..381248e9bf1 100644 --- a/intern/cycles/CMakeLists.txt +++ b/intern/cycles/CMakeLists.txt @@ -307,9 +307,9 @@ if(WITH_CYCLES_LOGGING) ) endif() -# Debugging capabilities (debug passes etc). -if(WITH_CYCLES_DEBUG) - add_definitions(-DWITH_CYCLES_DEBUG) +# NaN debugging +if(WITH_CYCLES_DEBUG_NAN) + add_definitions(-DWITH_CYCLES_DEBUG_NAN) endif() if(NOT OPENIMAGEIO_PUGIXML_FOUND) diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py index dc53c4db48f..29f8cd8f8cd 100644 --- a/intern/cycles/blender/addon/engine.py +++ b/intern/cycles/blender/addon/engine.py @@ -262,10 +262,6 @@ def list_render_passes(scene, srl): # Cycles specific passes. crl = srl.cycles if crl.pass_debug_render_time: yield ("Debug Render Time", "X", 'VALUE') - if crl.pass_debug_bvh_traversed_nodes: yield ("Debug BVH Traversed Nodes", "X", 'VALUE') - if crl.pass_debug_bvh_traversed_instances: yield ("Debug BVH Traversed Instances", "X", 'VALUE') - if crl.pass_debug_bvh_intersections: yield ("Debug BVH Intersections", "X", 'VALUE') - if crl.pass_debug_ray_bounces: yield ("Debug Ray Bounces", "X", 'VALUE') if crl.pass_debug_sample_count: yield ("Debug Sample Count", "X", 'VALUE') if crl.use_pass_volume_direct: yield ("VolumeDir", "RGB", 'COLOR') if crl.use_pass_volume_indirect: yield ("VolumeInd", "RGB", 'COLOR') diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index b014811211f..4cd75edee5f 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -263,6 +263,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): name="Use Denoising", description="Denoise the rendered image", default=False, + update=update_render_passes, ) use_preview_denoising: BoolProperty( name="Use Viewport Denoising", @@ -1324,30 +1325,6 @@ class CyclesCurveRenderSettings(bpy.types.PropertyGroup): class CyclesRenderLayerSettings(bpy.types.PropertyGroup): - pass_debug_bvh_traversed_nodes: BoolProperty( - name="Debug BVH Traversed Nodes", - description="Store Debug BVH Traversed Nodes pass", - default=False, - update=update_render_passes, - ) - pass_debug_bvh_traversed_instances: BoolProperty( - name="Debug BVH Traversed Instances", - description="Store Debug BVH Traversed Instances pass", - default=False, - update=update_render_passes, - ) - pass_debug_bvh_intersections: BoolProperty( - name="Debug BVH Intersections", - description="Store Debug BVH Intersections", - default=False, - update=update_render_passes, - ) - pass_debug_ray_bounces: BoolProperty( - name="Debug Ray Bounces", - description="Store Debug Ray Bounces pass", - default=False, - update=update_render_passes, - ) pass_debug_render_time: BoolProperty( name="Debug Render Time", description="Render time in milliseconds per sample and pixel", diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 30892cdbbc0..e9a4ce77080 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -936,29 +936,6 @@ class CYCLES_RENDER_PT_passes_crypto(CyclesButtonsPanel, ViewLayerCryptomattePan bl_parent_id = "CYCLES_RENDER_PT_passes" -class CYCLES_RENDER_PT_passes_debug(CyclesButtonsPanel, Panel): - bl_label = "Debug" - bl_context = "view_layer" - bl_parent_id = "CYCLES_RENDER_PT_passes" - - @classmethod - def poll(cls, context): - import _cycles - return _cycles.with_cycles_debug - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - cycles_view_layer = context.view_layer.cycles - - layout.prop(cycles_view_layer, "pass_debug_bvh_traversed_nodes") - layout.prop(cycles_view_layer, "pass_debug_bvh_traversed_instances") - layout.prop(cycles_view_layer, "pass_debug_bvh_intersections") - layout.prop(cycles_view_layer, "pass_debug_ray_bounces") - - class CYCLES_RENDER_PT_passes_aov(CyclesButtonsPanel, ViewLayerAOVPanel): bl_label = "Shader AOV" bl_context = "view_layer" @@ -1136,7 +1113,7 @@ class CYCLES_PT_context_material(CyclesButtonsPanel, Panel): col = row.column(align=True) col.operator("object.material_slot_add", icon='ADD', text="") col.operator("object.material_slot_remove", icon='REMOVE', text="") - + col.separator() col.menu("MATERIAL_MT_context_menu", icon='DOWNARROW_HLT', text="") if is_sortable: @@ -1151,16 +1128,15 @@ class CYCLES_PT_context_material(CyclesButtonsPanel, Panel): row.operator("object.material_slot_select", text="Select") row.operator("object.material_slot_deselect", text="Deselect") - split = layout.split(factor=0.65) + row = layout.row() if ob: - split.template_ID(ob, "active_material", new="material.new") - row = split.row() + row.template_ID(ob, "active_material", new="material.new") if slot: - row.prop(slot, "link", text="") - else: - row.label() + icon_link = 'MESH_DATA' if slot.link == 'DATA' else 'OBJECT_DATA' + row.prop(slot, "link", text="", icon=icon_link, icon_only=True) + elif mat: split.template_ID(space, "pin_id") split.separator() @@ -2318,7 +2294,6 @@ classes = ( CYCLES_RENDER_PT_passes_data, CYCLES_RENDER_PT_passes_light, CYCLES_RENDER_PT_passes_crypto, - CYCLES_RENDER_PT_passes_debug, CYCLES_RENDER_PT_passes_aov, CYCLES_RENDER_PT_filter, CYCLES_RENDER_PT_override, diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index 11158532738..ecadc78cbbf 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -1055,10 +1055,45 @@ static BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b_ob) return BL::MeshSequenceCacheModifier(PointerRNA_NULL); } +/* Check whether some of "built-in" motion-related attributes are needed to be exported (includes + * things like velocity from cache modifier, fluid simulation). + * + * NOTE: This code is run prior to object motion blur initialization. so can not access properties + * set by `sync_object_motion_init()`. */ +static bool mesh_need_motion_attribute(BL::Object &b_ob, Scene *scene) +{ + const Scene::MotionType need_motion = scene->need_motion(); + if (need_motion == Scene::MOTION_NONE) { + /* Simple case: neither motion pass nor motion blur is needed, no need in the motion related + * attributes. */ + return false; + } + + if (need_motion == Scene::MOTION_BLUR) { + /* A bit tricky and implicit case: + * - Motion blur is enabled in the scene, which implies specific number of time steps for + * objects. + * - If the object has motion blur disabled on it, it will have 0 time steps. + * - Motion attribute expects non-zero time steps. + * + * Avoid adding motion attributes if the motion blur will enforce 0 motion steps. */ + PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles"); + const bool use_motion = get_boolean(cobject, "use_motion_blur"); + if (!use_motion) { + return false; + } + } + + /* Motion pass which implies 3 motion steps, or motion blur which is not disabled on object + * level. */ + return true; +} + static void sync_mesh_cached_velocities(BL::Object &b_ob, Scene *scene, Mesh *mesh) { - if (scene->need_motion() == Scene::MOTION_NONE) + if (!mesh_need_motion_attribute(b_ob, scene)) { return; + } BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob); @@ -1102,8 +1137,9 @@ static void sync_mesh_cached_velocities(BL::Object &b_ob, Scene *scene, Mesh *me static void sync_mesh_fluid_motion(BL::Object &b_ob, Scene *scene, Mesh *mesh) { - if (scene->need_motion() == Scene::MOTION_NONE) + if (!mesh_need_motion_attribute(b_ob, scene)) { return; + } BL::FluidDomainSettings b_fluid_domain = object_fluid_liquid_domain_find(b_ob); diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/blender_python.cpp index c58d2eb6e04..fd145effde7 100644 --- a/intern/cycles/blender/blender_python.cpp +++ b/intern/cycles/blender/blender_python.cpp @@ -1098,14 +1098,6 @@ void *CCL_python_module_init() PyModule_AddStringConstant(mod, "osl_version_string", "unknown"); #endif -#ifdef WITH_CYCLES_DEBUG - PyModule_AddObject(mod, "with_cycles_debug", Py_True); - Py_INCREF(Py_True); -#else - PyModule_AddObject(mod, "with_cycles_debug", Py_False); - Py_INCREF(Py_False); -#endif - #ifdef WITH_NETWORK PyModule_AddObject(mod, "with_network", Py_True); Py_INCREF(Py_True); diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index ce399579e25..298353203ad 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -538,12 +538,6 @@ PassType BlenderSync::get_pass_type(BL::RenderPass &b_pass) MAP_PASS("BakePrimitive", PASS_BAKE_PRIMITIVE); MAP_PASS("BakeDifferential", PASS_BAKE_DIFFERENTIAL); -#ifdef __KERNEL_DEBUG__ - MAP_PASS("Debug BVH Traversed Nodes", PASS_BVH_TRAVERSED_NODES); - MAP_PASS("Debug BVH Traversed Instances", PASS_BVH_TRAVERSED_INSTANCES); - MAP_PASS("Debug BVH Intersections", PASS_BVH_INTERSECTIONS); - MAP_PASS("Debug Ray Bounces", PASS_RAY_BOUNCES); -#endif MAP_PASS("Debug Render Time", PASS_RENDER_TIME); MAP_PASS("AdaptiveAuxBuffer", PASS_ADAPTIVE_AUX_BUFFER); MAP_PASS("Debug Sample Count", PASS_SAMPLE_COUNT); @@ -641,24 +635,6 @@ vector<Pass> BlenderSync::sync_render_passes(BL::Scene &b_scene, } } -#ifdef __KERNEL_DEBUG__ - if (get_boolean(crl, "pass_debug_bvh_traversed_nodes")) { - b_engine.add_pass("Debug BVH Traversed Nodes", 1, "X", b_view_layer.name().c_str()); - Pass::add(PASS_BVH_TRAVERSED_NODES, passes, "Debug BVH Traversed Nodes"); - } - if (get_boolean(crl, "pass_debug_bvh_traversed_instances")) { - b_engine.add_pass("Debug BVH Traversed Instances", 1, "X", b_view_layer.name().c_str()); - Pass::add(PASS_BVH_TRAVERSED_INSTANCES, passes, "Debug BVH Traversed Instances"); - } - if (get_boolean(crl, "pass_debug_bvh_intersections")) { - b_engine.add_pass("Debug BVH Intersections", 1, "X", b_view_layer.name().c_str()); - Pass::add(PASS_BVH_INTERSECTIONS, passes, "Debug BVH Intersections"); - } - if (get_boolean(crl, "pass_debug_ray_bounces")) { - b_engine.add_pass("Debug Ray Bounces", 1, "X", b_view_layer.name().c_str()); - Pass::add(PASS_RAY_BOUNCES, passes, "Debug Ray Bounces"); - } -#endif if (get_boolean(crl, "pass_debug_render_time")) { b_engine.add_pass("Debug Render Time", 1, "X", b_view_layer.name().c_str()); Pass::add(PASS_RENDER_TIME, passes, "Debug Render Time"); diff --git a/intern/cycles/device/cuda/device_cuda_impl.cpp b/intern/cycles/device/cuda/device_cuda_impl.cpp index 5b62292ca55..2d2fcb38705 100644 --- a/intern/cycles/device/cuda/device_cuda_impl.cpp +++ b/intern/cycles/device/cuda/device_cuda_impl.cpp @@ -351,9 +351,6 @@ string CUDADevice::compile_kernel_get_common_cflags( if (extra_cflags) { cflags += string(" ") + string(extra_cflags); } -# ifdef WITH_CYCLES_DEBUG - cflags += " -D__KERNEL_DEBUG__"; -# endif if (split) { cflags += " -D__SPLIT__"; @@ -461,18 +458,19 @@ string CUDADevice::compile_kernel(const DeviceRequestedFeatures &requested_featu const int nvcc_cuda_version = cuewCompilerVersion(); VLOG(1) << "Found nvcc " << nvcc << ", CUDA version " << nvcc_cuda_version << "."; - if (nvcc_cuda_version < 80) { + if (nvcc_cuda_version < 101) { printf( "Unsupported CUDA version %d.%d detected, " - "you need CUDA 8.0 or newer.\n", + "you need CUDA 10.1 or newer.\n", nvcc_cuda_version / 10, nvcc_cuda_version % 10); return string(); } - else if (!(nvcc_cuda_version == 101 || nvcc_cuda_version == 102)) { + else if (!(nvcc_cuda_version == 101 || nvcc_cuda_version == 102 || nvcc_cuda_version == 111 || + nvcc_cuda_version == 112 || nvcc_cuda_version == 113 || nvcc_cuda_version == 114)) { printf( "CUDA version %d.%d detected, build may succeed but only " - "CUDA 10.1 and 10.2 are officially supported.\n", + "CUDA 10.1 to 11.4 are officially supported.\n", nvcc_cuda_version / 10, nvcc_cuda_version % 10); } diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp index 392fec4d57b..6f9a7943722 100644 --- a/intern/cycles/device/device_optix.cpp +++ b/intern/cycles/device/device_optix.cpp @@ -234,9 +234,6 @@ class OptiXDevice : public CUDADevice { } }; # endif -# if OPTIX_ABI_VERSION >= 41 && defined(WITH_CYCLES_DEBUG) - options.validationMode = OPTIX_DEVICE_CONTEXT_VALIDATION_MODE_ALL; -# endif check_result_optix(optixDeviceContextCreate(cuContext, &options, &context)); # ifdef WITH_CYCLES_LOGGING check_result_optix(optixDeviceContextSetLogCallback( @@ -369,13 +366,8 @@ class OptiXDevice : public CUDADevice { OptixModuleCompileOptions module_options = {}; module_options.maxRegisterCount = 0; // Do not set an explicit register limit -# ifdef WITH_CYCLES_DEBUG - module_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_0; - module_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_FULL; -# else module_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_3; module_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_LINEINFO; -# endif # if OPTIX_ABI_VERSION >= 41 module_options.boundValues = nullptr; @@ -578,11 +570,7 @@ class OptiXDevice : public CUDADevice { OptixPipelineLinkOptions link_options = {}; link_options.maxTraceDepth = 1; -# ifdef WITH_CYCLES_DEBUG - link_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_FULL; -# else link_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_LINEINFO; -# endif # if OPTIX_ABI_VERSION < 24 link_options.overrideUsesMotionBlur = motion_blur; # endif diff --git a/intern/cycles/device/opencl/device_opencl_impl.cpp b/intern/cycles/device/opencl/device_opencl_impl.cpp index 715213175c9..31a2265700c 100644 --- a/intern/cycles/device/opencl/device_opencl_impl.cpp +++ b/intern/cycles/device/opencl/device_opencl_impl.cpp @@ -1968,10 +1968,6 @@ string OpenCLDevice::kernel_build_options(const string *debug_src) build_options += "-D__KERNEL_OPENCL_DEBUG__ "; } -# ifdef WITH_CYCLES_DEBUG - build_options += "-D__KERNEL_DEBUG__ "; -# endif - # ifdef WITH_NANOVDB if (info.has_nanovdb) { build_options += "-DWITH_NANOVDB "; diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index ea0f16c9233..0ce33c51778 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -380,11 +380,16 @@ if(WITH_CYCLES_CUDA_BINARIES) set(CUDA_VERSION "${CUDA_VERSION_MAJOR}${CUDA_VERSION_MINOR}") # warn for other versions - if((CUDA_VERSION MATCHES "101") OR (CUDA_VERSION MATCHES "102") OR (CUDA_VERSION MATCHES "111")) + if((CUDA_VERSION MATCHES "101") OR + (CUDA_VERSION MATCHES "102") OR + (CUDA_VERSION MATCHES "111") OR + (CUDA_VERSION MATCHES "112") OR + (CUDA_VERSION MATCHES "113") OR + (CUDA_VERSION MATCHES "114")) else() message(WARNING "CUDA version ${CUDA_VERSION_MAJOR}.${CUDA_VERSION_MINOR} detected, " - "build may succeed but only CUDA 10.1, 10.2 and 11.1 are officially supported") + "build may succeed but only CUDA 10.1 to 11.4 are officially supported") endif() # build for each arch @@ -439,10 +444,6 @@ if(WITH_CYCLES_CUDA_BINARIES) set(name ${name}_experimental) endif() - if(WITH_CYCLES_DEBUG) - set(cuda_flags ${cuda_flags} -D __KERNEL_DEBUG__) - endif() - if(WITH_NANOVDB) set(cuda_flags ${cuda_flags} -D WITH_NANOVDB @@ -557,11 +558,6 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) --use_fast_math -o ${output}) - if(WITH_CYCLES_DEBUG) - set(cuda_flags ${cuda_flags} - -D __KERNEL_DEBUG__) - endif() - if(WITH_NANOVDB) set(cuda_flags ${cuda_flags} -D WITH_NANOVDB diff --git a/intern/cycles/kernel/bvh/bvh_traversal.h b/intern/cycles/kernel/bvh/bvh_traversal.h index 8b2699ab807..89250a8d60a 100644 --- a/intern/cycles/kernel/bvh/bvh_traversal.h +++ b/intern/cycles/kernel/bvh/bvh_traversal.h @@ -67,8 +67,6 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, isect->prim = PRIM_NONE; isect->object = OBJECT_NONE; - BVH_DEBUG_INIT(); - /* traversal loop */ do { do { @@ -118,7 +116,6 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, --stack_ptr; } } - BVH_DEBUG_NEXT_NODE(); } /* if node is leaf, fetch triangle list */ @@ -138,7 +135,6 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, switch (type & PRIMITIVE_ALL) { case PRIMITIVE_TRIANGLE: { for (; prim_addr < prim_addr2; prim_addr++) { - BVH_DEBUG_NEXT_INTERSECTION(); kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type); if (triangle_intersect(kg, isect, P, dir, visibility, object, prim_addr)) { /* shadow ray early termination */ @@ -151,7 +147,6 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, #if BVH_FEATURE(BVH_MOTION) case PRIMITIVE_MOTION_TRIANGLE: { for (; prim_addr < prim_addr2; prim_addr++) { - BVH_DEBUG_NEXT_INTERSECTION(); kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type); if (motion_triangle_intersect( kg, isect, P, dir, ray->time, visibility, object, prim_addr)) { @@ -169,7 +164,6 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, case PRIMITIVE_CURVE_RIBBON: case PRIMITIVE_MOTION_CURVE_RIBBON: { for (; prim_addr < prim_addr2; prim_addr++) { - BVH_DEBUG_NEXT_INTERSECTION(); const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr); kernel_assert((curve_type & PRIMITIVE_ALL) == (type & PRIMITIVE_ALL)); const bool hit = curve_intersect( @@ -201,8 +195,6 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, traversal_stack[stack_ptr] = ENTRYPOINT_SENTINEL; node_addr = kernel_tex_fetch(__object_node, object); - - BVH_DEBUG_NEXT_INSTANCE(); } } } while (node_addr != ENTRYPOINT_SENTINEL); diff --git a/intern/cycles/kernel/bvh/bvh_types.h b/intern/cycles/kernel/bvh/bvh_types.h index 9f0879a2069..98e6ec25d15 100644 --- a/intern/cycles/kernel/bvh/bvh_types.h +++ b/intern/cycles/kernel/bvh/bvh_types.h @@ -42,33 +42,6 @@ CCL_NAMESPACE_BEGIN #define BVH_FEATURE(f) (((BVH_FUNCTION_FEATURES) & (f)) != 0) -/* Debugging helpers. */ -#ifdef __KERNEL_DEBUG__ -# define BVH_DEBUG_INIT() \ - do { \ - isect->num_traversed_nodes = 0; \ - isect->num_traversed_instances = 0; \ - isect->num_intersections = 0; \ - } while (0) -# define BVH_DEBUG_NEXT_NODE() \ - do { \ - ++isect->num_traversed_nodes; \ - } while (0) -# define BVH_DEBUG_NEXT_INTERSECTION() \ - do { \ - ++isect->num_intersections; \ - } while (0) -# define BVH_DEBUG_NEXT_INSTANCE() \ - do { \ - ++isect->num_traversed_instances; \ - } while (0) -#else /* __KERNEL_DEBUG__ */ -# define BVH_DEBUG_INIT() -# define BVH_DEBUG_NEXT_NODE() -# define BVH_DEBUG_NEXT_INTERSECTION() -# define BVH_DEBUG_NEXT_INSTANCE() -#endif /* __KERNEL_DEBUG__ */ - CCL_NAMESPACE_END #endif /* __BVH_TYPES__ */ diff --git a/intern/cycles/kernel/kernel_accumulate.h b/intern/cycles/kernel/kernel_accumulate.h index 76e0f2be02c..61653d328f1 100644 --- a/intern/cycles/kernel/kernel_accumulate.h +++ b/intern/cycles/kernel/kernel_accumulate.h @@ -225,13 +225,6 @@ ccl_device_inline void path_radiance_init(KernelGlobals *kg, PathRadiance *L) L->denoising_albedo = zero_float3(); L->denoising_depth = 0.0f; #endif - -#ifdef __KERNEL_DEBUG__ - L->debug_data.num_bvh_traversed_nodes = 0; - L->debug_data.num_bvh_traversed_instances = 0; - L->debug_data.num_bvh_intersections = 0; - L->debug_data.num_ray_bounces = 0; -#endif } ccl_device_inline void path_radiance_bsdf_bounce(KernelGlobals *kg, @@ -595,7 +588,9 @@ ccl_device_inline void path_radiance_sum_shadowcatcher(KernelGlobals *kg, float shadow; if (UNLIKELY(!isfinite_safe(path_total))) { +# ifdef __KERNEL_DEBUG_NAN__ kernel_assert(!"Non-finite total radiance along the path"); +# endif shadow = 0.0f; } else if (path_total == 0.0f) { @@ -641,7 +636,9 @@ ccl_device_inline float3 path_radiance_clamp_and_sum(KernelGlobals *kg, /* Reject invalid value */ if (!isfinite_safe(sum)) { +# ifdef __KERNEL_DEBUG_NAN__ kernel_assert(!"Non-finite sum in path_radiance_clamp_and_sum!"); +# endif L_sum = zero_float3(); L->direct_diffuse = zero_float3(); @@ -667,7 +664,9 @@ ccl_device_inline float3 path_radiance_clamp_and_sum(KernelGlobals *kg, /* Reject invalid value */ float sum = fabsf((L_sum).x) + fabsf((L_sum).y) + fabsf((L_sum).z); if (!isfinite_safe(sum)) { +#ifdef __KERNEL_DEBUG_NAN__ kernel_assert(!"Non-finite final sum in path_radiance_clamp_and_sum!"); +#endif L_sum = zero_float3(); } } diff --git a/intern/cycles/kernel/kernel_passes.h b/intern/cycles/kernel/kernel_passes.h index 52be2ed87b7..8f58b8c3079 100644 --- a/intern/cycles/kernel/kernel_passes.h +++ b/intern/cycles/kernel/kernel_passes.h @@ -122,31 +122,6 @@ ccl_device_inline void kernel_update_denoising_features(KernelGlobals *kg, } #endif /* __DENOISING_FEATURES__ */ -#ifdef __KERNEL_DEBUG__ -ccl_device_inline void kernel_write_debug_passes(KernelGlobals *kg, - ccl_global float *buffer, - PathRadiance *L) -{ - int flag = kernel_data.film.pass_flag; - if (flag & PASSMASK(BVH_TRAVERSED_NODES)) { - kernel_write_pass_float(buffer + kernel_data.film.pass_bvh_traversed_nodes, - L->debug_data.num_bvh_traversed_nodes); - } - if (flag & PASSMASK(BVH_TRAVERSED_INSTANCES)) { - kernel_write_pass_float(buffer + kernel_data.film.pass_bvh_traversed_instances, - L->debug_data.num_bvh_traversed_instances); - } - if (flag & PASSMASK(BVH_INTERSECTIONS)) { - kernel_write_pass_float(buffer + kernel_data.film.pass_bvh_intersections, - L->debug_data.num_bvh_intersections); - } - if (flag & PASSMASK(RAY_BOUNCES)) { - kernel_write_pass_float(buffer + kernel_data.film.pass_ray_bounces, - L->debug_data.num_ray_bounces); - } -} -#endif /* __KERNEL_DEBUG__ */ - #ifdef __KERNEL_CPU__ # define WRITE_ID_SLOT(buffer, depth, id, matte_weight, name) \ kernel_write_id_pass_cpu(buffer, depth * 2, id, matte_weight, kg->coverage_##name) @@ -389,10 +364,6 @@ ccl_device_inline void kernel_write_result(KernelGlobals *kg, } #endif /* __DENOISING_FEATURES__ */ -#ifdef __KERNEL_DEBUG__ - kernel_write_debug_passes(kg, buffer, L); -#endif - /* Adaptive Sampling. Fill the additional buffer with the odd samples and calculate our stopping criteria. This is the heuristic from "A hierarchical automatic stopping condition for Monte Carlo global illumination" except that here it is applied per pixel and not in hierarchical diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h index dd2390808ea..3430543bc8e 100644 --- a/intern/cycles/kernel/kernel_path.h +++ b/intern/cycles/kernel/kernel_path.h @@ -70,15 +70,6 @@ ccl_device_forceinline bool kernel_path_scene_intersect(KernelGlobals *kg, bool hit = scene_intersect(kg, ray, visibility, isect); -#ifdef __KERNEL_DEBUG__ - if (state->flag & PATH_RAY_CAMERA) { - L->debug_data.num_bvh_traversed_nodes += isect->num_traversed_nodes; - L->debug_data.num_bvh_traversed_instances += isect->num_traversed_instances; - L->debug_data.num_bvh_intersections += isect->num_intersections; - } - L->debug_data.num_ray_bounces++; -#endif /* __KERNEL_DEBUG__ */ - return hit; } diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index d224db91edc..f9ea3a2d0a8 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -182,9 +182,8 @@ CCL_NAMESPACE_BEGIN # undef __SHADER_RAYTRACE__ #endif -/* Features that enable others */ -#ifdef WITH_CYCLES_DEBUG -# define __KERNEL_DEBUG__ +#ifdef WITH_CYCLES_DEBUG_NAN +# define __KERNEL_DEBUG_NAN__ #endif #if defined(__SUBSURFACE__) || defined(__SHADER_RAYTRACE__) @@ -356,12 +355,6 @@ typedef enum PassType { PASS_MATERIAL_ID, PASS_MOTION, PASS_MOTION_WEIGHT, -#ifdef __KERNEL_DEBUG__ - PASS_BVH_TRAVERSED_NODES, - PASS_BVH_TRAVERSED_INSTANCES, - PASS_BVH_INTERSECTIONS, - PASS_RAY_BOUNCES, -#endif PASS_RENDER_TIME, PASS_CRYPTOMATTE, PASS_AOV_COLOR, @@ -465,18 +458,6 @@ typedef enum DenoiseFlag { DENOISING_CLEAN_ALL_PASSES = (1 << 6) - 1, } DenoiseFlag; -#ifdef __KERNEL_DEBUG__ -/* NOTE: This is a runtime-only struct, alignment is not - * really important here. - */ -typedef struct DebugData { - int num_bvh_traversed_nodes; - int num_bvh_traversed_instances; - int num_bvh_intersections; - int num_ray_bounces; -} DebugData; -#endif - typedef ccl_addr_space struct PathRadianceState { #ifdef __PASSES__ float3 diffuse; @@ -552,10 +533,6 @@ typedef ccl_addr_space struct PathRadiance { float3 denoising_albedo; float denoising_depth; #endif /* __DENOISING_FEATURES__ */ - -#ifdef __KERNEL_DEBUG__ - DebugData debug_data; -#endif /* __KERNEL_DEBUG__ */ } PathRadiance; typedef struct BsdfEval { @@ -671,12 +648,6 @@ typedef struct Intersection { int prim; int object; int type; - -#ifdef __KERNEL_DEBUG__ - int num_traversed_nodes; - int num_traversed_instances; - int num_intersections; -#endif } Intersection; /* Primitives */ @@ -1265,13 +1236,6 @@ typedef struct KernelFilm { int pass_bake_differential; int pad; -#ifdef __KERNEL_DEBUG__ - int pass_bvh_traversed_nodes; - int pass_bvh_traversed_instances; - int pass_bvh_intersections; - int pass_ray_bounces; -#endif - /* viewport rendering options */ int display_pass_stride; int display_pass_components; diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp index bf9d69cb47e..ea5a5f50f2d 100644 --- a/intern/cycles/render/attribute.cpp +++ b/intern/cycles/render/attribute.cpp @@ -20,6 +20,7 @@ #include "render/mesh.h" #include "util/util_foreach.h" +#include "util/util_logging.h" #include "util/util_transform.h" CCL_NAMESPACE_BEGIN @@ -208,6 +209,7 @@ size_t Attribute::element_size(Geometry *geom, AttributePrimitive prim) const case ATTR_ELEMENT_VERTEX_MOTION: if (geom->geometry_type == Geometry::MESH) { Mesh *mesh = static_cast<Mesh *>(geom); + DCHECK_GT(mesh->get_motion_steps(), 0); size = (mesh->get_verts().size() + mesh->get_num_ngons()) * (mesh->get_motion_steps() - 1); if (prim == ATTR_PRIM_SUBD) { size -= mesh->get_num_subd_verts() * (mesh->get_motion_steps() - 1); @@ -252,6 +254,7 @@ size_t Attribute::element_size(Geometry *geom, AttributePrimitive prim) const case ATTR_ELEMENT_CURVE_KEY_MOTION: if (geom->geometry_type == Geometry::HAIR) { Hair *hair = static_cast<Hair *>(geom); + DCHECK_GT(hair->get_motion_steps(), 0); size = hair->get_curve_keys().size() * (hair->get_motion_steps() - 1); } break; diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp index 0948b20628f..fcfad58995e 100644 --- a/intern/cycles/render/buffers.cpp +++ b/intern/cycles/render/buffers.cpp @@ -322,15 +322,6 @@ bool RenderBuffers::get_pass_rect( pixels[0] = saturate(f * scale_exposure); } } -#ifdef WITH_CYCLES_DEBUG - else if (type == PASS_BVH_TRAVERSED_NODES || type == PASS_BVH_TRAVERSED_INSTANCES || - type == PASS_BVH_INTERSECTIONS || type == PASS_RAY_BOUNCES) { - for (int i = 0; i < size; i++, in += pass_stride, pixels++) { - float f = *in; - pixels[0] = f * scale; - } - } -#endif else { for (int i = 0; i < size; i++, in += pass_stride, pixels++) { float f = *in; diff --git a/intern/cycles/render/colorspace.cpp b/intern/cycles/render/colorspace.cpp index 4540793f78d..3842f8e4726 100644 --- a/intern/cycles/render/colorspace.cpp +++ b/intern/cycles/render/colorspace.cpp @@ -385,7 +385,7 @@ void ColorSpaceManager::free_memory() { #ifdef WITH_OCIO map_free_memory(cached_colorspaces); - map_free_memory(cached_colorspaces); + map_free_memory(cached_processors); #endif } diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp index c4ff89fc838..5df396394c4 100644 --- a/intern/cycles/render/film.cpp +++ b/intern/cycles/render/film.cpp @@ -51,12 +51,6 @@ static NodeEnum *get_pass_type_enum() pass_type_enum.insert("material_id", PASS_MATERIAL_ID); pass_type_enum.insert("motion", PASS_MOTION); pass_type_enum.insert("motion_weight", PASS_MOTION_WEIGHT); -#ifdef __KERNEL_DEBUG__ - pass_type_enum.insert("traversed_nodes", PASS_BVH_TRAVERSED_NODES); - pass_type_enum.insert("traverse_instances", PASS_BVH_TRAVERSED_INSTANCES); - pass_type_enum.insert("bvh_intersections", PASS_BVH_INTERSECTIONS); - pass_type_enum.insert("ray_bounces", PASS_RAY_BOUNCES); -#endif pass_type_enum.insert("render_time", PASS_RENDER_TIME); pass_type_enum.insert("cryptomatte", PASS_CRYPTOMATTE); pass_type_enum.insert("aov_color", PASS_AOV_COLOR); @@ -200,15 +194,6 @@ void Pass::add(PassType type, vector<Pass> &passes, const char *name) */ pass.components = 0; break; -#ifdef WITH_CYCLES_DEBUG - case PASS_BVH_TRAVERSED_NODES: - case PASS_BVH_TRAVERSED_INSTANCES: - case PASS_BVH_INTERSECTIONS: - case PASS_RAY_BOUNCES: - pass.components = 1; - pass.exposure = false; - break; -#endif case PASS_RENDER_TIME: /* This pass is handled entirely on the host side. */ pass.components = 0; @@ -570,20 +555,6 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) kfilm->pass_bake_differential = kfilm->pass_stride; break; -#ifdef WITH_CYCLES_DEBUG - case PASS_BVH_TRAVERSED_NODES: - kfilm->pass_bvh_traversed_nodes = kfilm->pass_stride; - break; - case PASS_BVH_TRAVERSED_INSTANCES: - kfilm->pass_bvh_traversed_instances = kfilm->pass_stride; - break; - case PASS_BVH_INTERSECTIONS: - kfilm->pass_bvh_intersections = kfilm->pass_stride; - break; - case PASS_RAY_BOUNCES: - kfilm->pass_ray_bounces = kfilm->pass_stride; - break; -#endif case PASS_RENDER_TIME: break; case PASS_CRYPTOMATTE: diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index 9bd6e3a5e2d..7dc79f48145 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -993,7 +993,7 @@ void OSLCompiler::parameter_array(const char *name, const float f[], int arrayle void OSLCompiler::parameter_color_array(const char *name, const array<float3> &f) { - /* NB: cycles float3 type is actually 4 floats! need to use an explicit array */ + /* NOTE: cycles float3 type is actually 4 floats! need to use an explicit array. */ array<float[3]> table(f.size()); for (int i = 0; i < f.size(); ++i) { diff --git a/intern/cycles/util/util_logging.cpp b/intern/cycles/util/util_logging.cpp index 3782187d78c..8272728a7a0 100644 --- a/intern/cycles/util/util_logging.cpp +++ b/intern/cycles/util/util_logging.cpp @@ -26,9 +26,9 @@ CCL_NAMESPACE_BEGIN +#ifdef WITH_CYCLES_LOGGING static bool is_verbosity_set() { -#ifdef WITH_CYCLES_LOGGING using CYCLES_GFLAGS_NAMESPACE::GetCommandLineOption; std::string verbosity; @@ -36,10 +36,8 @@ static bool is_verbosity_set() return false; } return verbosity != "0"; -#else - return false; -#endif } +#endif void util_logging_init(const char *argv0) { diff --git a/intern/cycles/util/util_logging.h b/intern/cycles/util/util_logging.h index 3e56f0a0193..c161299acd0 100644 --- a/intern/cycles/util/util_logging.h +++ b/intern/cycles/util/util_logging.h @@ -40,7 +40,7 @@ class LogMessageVoidify { LogMessageVoidify() { } - void operator&(StubStream &) + void operator&(const StubStream &) { } }; @@ -49,6 +49,36 @@ class LogMessageVoidify { # define LOG(severity) LOG_SUPPRESS() # define VLOG(severity) LOG_SUPPRESS() # define VLOG_IF(severity, condition) LOG_SUPPRESS() + +# define CHECK(expression) LOG_SUPPRESS() + +# define CHECK_NOTNULL(expression) LOG_SUPPRESS() +# define CHECK_NULL(expression) LOG_SUPPRESS() + +# define CHECK_NEAR(actual, expected, eps) LOG_SUPPRESS() + +# define CHECK_GE(a, b) LOG_SUPPRESS() +# define CHECK_NE(a, b) LOG_SUPPRESS() +# define CHECK_EQ(a, b) LOG_SUPPRESS() +# define CHECK_GT(a, b) LOG_SUPPRESS() +# define CHECK_LT(a, b) LOG_SUPPRESS() +# define CHECK_LE(a, b) LOG_SUPPRESS() + +# define DCHECK(expression) LOG_SUPPRESS() + +# define DCHECK_NOTNULL(expression) LOG_SUPPRESS() +# define DCHECK_NULL(expression) LOG_SUPPRESS() + +# define DCHECK_NEAR(actual, expected, eps) LOG_SUPPRESS() + +# define DCHECK_GE(a, b) LOG_SUPPRESS() +# define DCHECK_NE(a, b) LOG_SUPPRESS() +# define DCHECK_EQ(a, b) LOG_SUPPRESS() +# define DCHECK_GT(a, b) LOG_SUPPRESS() +# define DCHECK_LT(a, b) LOG_SUPPRESS() +# define DCHECK_LE(a, b) LOG_SUPPRESS() + +# define LOG_ASSERT(expression) LOG_SUPPRESS() #endif #define VLOG_ONCE(level, flag) \ diff --git a/intern/cycles/util/util_math_fast.h b/intern/cycles/util/util_math_fast.h index 1113ede0f2d..38afa163db5 100644 --- a/intern/cycles/util/util_math_fast.h +++ b/intern/cycles/util/util_math_fast.h @@ -103,7 +103,7 @@ ccl_device float fast_sinf(float x) * 1.19209e-07 max error */ int q = fast_rint(x * M_1_PI_F); - float qf = q; + float qf = (float)q; x = madd(qf, -0.78515625f * 4, x); x = madd(qf, -0.00024187564849853515625f * 4, x); x = madd(qf, -3.7747668102383613586e-08f * 4, x); @@ -132,7 +132,7 @@ ccl_device float fast_cosf(float x) { /* Same argument reduction as fast_sinf(). */ int q = fast_rint(x * M_1_PI_F); - float qf = q; + float qf = (float)q; x = madd(qf, -0.78515625f * 4, x); x = madd(qf, -0.00024187564849853515625f * 4, x); x = madd(qf, -3.7747668102383613586e-08f * 4, x); @@ -160,7 +160,7 @@ ccl_device void fast_sincosf(float x, float *sine, float *cosine) { /* Same argument reduction as fast_sin. */ int q = fast_rint(x * M_1_PI_F); - float qf = q; + float qf = (float)q; x = madd(qf, -0.78515625f * 4, x); x = madd(qf, -0.00024187564849853515625f * 4, x); x = madd(qf, -3.7747668102383613586e-08f * 4, x); @@ -207,7 +207,7 @@ ccl_device float fast_tanf(float x) * we sometimes need to take the reciprocal of the polynomial */ int q = fast_rint(x * 2.0f * M_1_PI_F); - float qf = q; + float qf = (float)q; x = madd(qf, -0.78515625f * 2, x); x = madd(qf, -0.00024187564849853515625f * 2, x); x = madd(qf, -3.7747668102383613586e-08f * 2, x); @@ -407,7 +407,7 @@ ccl_device float fast_logb(float x) x = fabsf(x); x = clamp(x, FLT_MIN, FLT_MAX); unsigned bits = __float_as_uint(x); - return (int)(bits >> 23) - 127; + return (float)((int)(bits >> 23) - 127); } ccl_device float fast_exp2f(float x) diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index db3f9bd561e..46e3888a367 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -188,8 +188,8 @@ extern GHOST_WindowHandle GHOST_CreateWindow(GHOST_SystemHandle systemhandle, GHOST_GLSettings glSettings); /** - * Create a new offscreen context. - * Never explicitly delete the context, use disposeContext() instead. + * Create a new off-screen context. + * Never explicitly delete the context, use #disposeContext() instead. * \param systemhandle: The handle to the system. * \param platform_support_callback: An optional callback to check platform support. * \return A handle to the new context ( == NULL if creation failed). @@ -628,7 +628,7 @@ extern void GHOST_ScreenToClient( GHOST_WindowHandle windowhandle, int32_t inX, int32_t inY, int32_t *outX, int32_t *outY); /** - * Converts a point in screen coordinates to client rectangle coordinates + * Converts a point in client rectangle coordinates to screen coordinates. * \param windowhandle: The handle to the window. * \param inX: The x-coordinate in the client rectangle. * \param inY: The y-coordinate in the client rectangle. diff --git a/intern/ghost/GHOST_IContext.h b/intern/ghost/GHOST_IContext.h index 278a9a40bd1..1b5f996cb54 100644 --- a/intern/ghost/GHOST_IContext.h +++ b/intern/ghost/GHOST_IContext.h @@ -29,8 +29,8 @@ /** * Interface for GHOST context. * - * You can create a offscreen context (windowless) with the system's - * GHOST_ISystem::createOffscreenContext method. + * You can create a off-screen context (windowless) with the system's + * #GHOST_ISystem::createOffscreenContext method. * \see GHOST_ISystem#createOffscreenContext */ class GHOST_IContext { diff --git a/intern/ghost/GHOST_ISystem.h b/intern/ghost/GHOST_ISystem.h index 4c395f720df..05c6c9d907f 100644 --- a/intern/ghost/GHOST_ISystem.h +++ b/intern/ghost/GHOST_ISystem.h @@ -261,8 +261,8 @@ class GHOST_ISystem { virtual GHOST_TSuccess disposeWindow(GHOST_IWindow *window) = 0; /** - * Create a new offscreen context. - * Never explicitly delete the context, use disposeContext() instead. + * Create a new off-screen context. + * Never explicitly delete the context, use #disposeContext() instead. * \return The new context (or 0 if creation failed). */ virtual GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings) = 0; diff --git a/intern/ghost/GHOST_IWindow.h b/intern/ghost/GHOST_IWindow.h index f870791b345..5f9bd808c8c 100644 --- a/intern/ghost/GHOST_IWindow.h +++ b/intern/ghost/GHOST_IWindow.h @@ -133,7 +133,7 @@ class GHOST_IWindow { virtual void screenToClient(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const = 0; /** - * Converts a point in screen coordinates to client rectangle coordinates + * Converts a point in client rectangle coordinates to screen coordinates. * \param inX: The x-coordinate in the client rectangle. * \param inY: The y-coordinate in the client rectangle. * \param outX: The x-coordinate on the screen. diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index ff93de4f203..94a3fd86b73 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -611,9 +611,9 @@ typedef void (*GHOST_TimerProcPtr)(struct GHOST_TimerTaskHandle__ *task, uint64_ struct GHOST_XrDrawViewInfo; struct GHOST_XrError; /** - * The XR view (i.e. the OpenXR runtime) may require a different graphics library than OpenGL. An - * offscreen texture of the viewport will then be drawn into using OpenGL, but the final texture - * draw call will happen through another lib (say DirectX). + * The XR view (i.e. the OpenXR runtime) may require a different graphics library than OpenGL. + * An off-screen texture of the viewport will then be drawn into using OpenGL, + * but the final texture draw call will happen through another library (say DirectX). * * This enum defines the possible graphics bindings to attempt to enable. */ @@ -683,6 +683,10 @@ typedef struct GHOST_XrDrawViewInfo { /** Set if the buffer should be submitted with a SRGB transfer applied. */ char expects_srgb_buffer; + + /** The view that this info represents. Not necessarily the "eye index" (e.g. for quad view + * systems, etc). */ + char view_idx; } GHOST_XrDrawViewInfo; typedef struct GHOST_XrError { diff --git a/intern/ghost/intern/GHOST_ContextCGL.mm b/intern/ghost/intern/GHOST_ContextCGL.mm index 687173ded09..7af243846c2 100644 --- a/intern/ghost/intern/GHOST_ContextCGL.mm +++ b/intern/ghost/intern/GHOST_ContextCGL.mm @@ -217,7 +217,7 @@ static void makeAttribList(std::vector<NSOpenGLPixelFormatAttribute> &attribs, attribs.push_back(NSOpenGLPFAOpenGLProfile); attribs.push_back(coreProfile ? NSOpenGLProfileVersion3_2Core : NSOpenGLProfileVersionLegacy); - // Pixel Format Attributes for the windowed NSOpenGLContext + /* Pixel Format Attributes for the windowed NSOpenGLContext. */ attribs.push_back(NSOpenGLPFADoubleBuffer); if (softwareGL) { @@ -250,7 +250,8 @@ GHOST_TSuccess GHOST_ContextCGL::initializeDrawingContext() static const bool needAlpha = false; #endif - static bool softwareGL = getenv("BLENDER_SOFTWAREGL"); // command-line argument would be better + /* Command-line argument would be better. */ + static bool softwareGL = getenv("BLENDER_SOFTWAREGL"); std::vector<NSOpenGLPixelFormatAttribute> attribs; attribs.reserve(40); @@ -287,7 +288,7 @@ GHOST_TSuccess GHOST_ContextCGL::initializeDrawingContext() if (m_metalView) { if (m_defaultFramebuffer == 0) { - // Create a virtual framebuffer + /* Create a virtual frame-buffer. */ [m_openGLContext makeCurrentContext]; metalInitFramebuffer(); initClearGL(); @@ -342,11 +343,11 @@ void GHOST_ContextCGL::metalInit() /* clang-format on */ id<MTLDevice> device = m_metalLayer.device; - // Create a command queue for blit/present operation + /* Create a command queue for blit/present operation. */ m_metalCmdQueue = (MTLCommandQueue *)[device newCommandQueue]; [m_metalCmdQueue retain]; - // Create shaders for blit operation + /* Create shaders for blit operation. */ NSString *source = @R"msl( using namespace metal; @@ -387,7 +388,7 @@ void GHOST_ContextCGL::metalInit() "GHOST_ContextCGL::metalInit: newLibraryWithSource:options:error: failed!"); } - // Create a render pipeline for blit operation + /* Create a render pipeline for blit operation. */ MTLRenderPipelineDescriptor *desc = [[[MTLRenderPipelineDescriptor alloc] init] autorelease]; desc.fragmentFunction = [library newFunctionWithName:@"fragment_shader"]; @@ -460,7 +461,7 @@ void GHOST_ContextCGL::metalUpdateFramebuffer() "GHOST_ContextCGL::metalUpdateFramebuffer: CVPixelBufferCreate failed!"); } - // Create an OpenGL texture + /* Create an OpenGL texture. */ CVOpenGLTextureCacheRef cvGLTexCache = nil; cvret = CVOpenGLTextureCacheCreate(kCFAllocatorDefault, nil, @@ -485,7 +486,7 @@ void GHOST_ContextCGL::metalUpdateFramebuffer() unsigned int glTex; glTex = CVOpenGLTextureGetName(cvGLTex); - // Create a Metal texture + /* Create a Metal texture. */ CVMetalTextureCacheRef cvMetalTexCache = nil; cvret = CVMetalTextureCacheCreate( kCFAllocatorDefault, nil, m_metalLayer.device, nil, &cvMetalTexCache); diff --git a/intern/ghost/intern/GHOST_ContextEGL.cpp b/intern/ghost/intern/GHOST_ContextEGL.cpp index 0fee200ea1a..a64b2aef6a5 100644 --- a/intern/ghost/intern/GHOST_ContextEGL.cpp +++ b/intern/ghost/intern/GHOST_ContextEGL.cpp @@ -283,8 +283,8 @@ GHOST_TSuccess GHOST_ContextEGL::setSwapInterval(int interval) GHOST_TSuccess GHOST_ContextEGL::getSwapInterval(int &intervalOut) { - // This is a bit of a kludge because there does not seem to - // be a way to query the swap interval with EGL. + /* This is a bit of a kludge because there does not seem to + * be a way to query the swap interval with EGL. */ intervalOut = m_swap_interval; return GHOST_kSuccess; @@ -365,21 +365,21 @@ static const std::string &api_string(EGLenum api) GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext() { - // objects have to be declared here due to the use of goto + /* Objects have to be declared here due to the use of `goto`. */ std::vector<EGLint> attrib_list; EGLint num_config = 0; if (m_stereoVisual) fprintf(stderr, "Warning! Stereo OpenGL ES contexts are not supported.\n"); - m_stereoVisual = false; // It doesn't matter what the Window wants. + m_stereoVisual = false; /* It doesn't matter what the Window wants. */ if (!initContextEGLEW()) { return GHOST_kFailure; } #ifdef WITH_GL_ANGLE - // d3dcompiler_XX.dll needs to be loaded before ANGLE will work + /* `d3dcompiler_XX.dll` needs to be loaded before ANGLE will work. */ if (s_d3dcompiler == NULL) { s_d3dcompiler = LoadLibrary(D3DCOMPILER); @@ -410,13 +410,13 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext() if (!bindAPI(m_api)) goto error; - // build attribute list + /* Build attribute list. */ attrib_list.reserve(20); if (m_api == EGL_OPENGL_ES_API && EGLEW_VERSION_1_2) { - // According to the spec it seems that you are required to set EGL_RENDERABLE_TYPE, - // but some implementations (ANGLE) do not seem to care. + /* According to the spec it seems that you are required to set EGL_RENDERABLE_TYPE, + * but some implementations (ANGLE) do not seem to care. */ if (m_contextMajorVersion == 1) { attrib_list.push_back(EGL_RENDERABLE_TYPE); @@ -469,7 +469,7 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext() #endif if (m_nativeWindow == 0) { - // off-screen surface + /* Off-screen surface. */ attrib_list.push_back(EGL_SURFACE_TYPE); attrib_list.push_back(EGL_PBUFFER_BIT); } @@ -479,8 +479,8 @@ GHOST_TSuccess GHOST_ContextEGL::initializeDrawingContext() if (!EGL_CHK(::eglChooseConfig(m_display, &(attrib_list[0]), &m_config, 1, &num_config))) goto error; - // A common error is to assume that ChooseConfig worked because it returned EGL_TRUE - if (num_config != 1) // num_config should be exactly 1 + /* A common error is to assume that ChooseConfig worked because it returned EGL_TRUE. */ + if (num_config != 1) /* `num_config` should be exactly 1. */ goto error; if (m_nativeWindow != 0) { diff --git a/intern/ghost/intern/GHOST_ContextGLX.cpp b/intern/ghost/intern/GHOST_ContextGLX.cpp index da8b1fd4941..69c118da7a8 100644 --- a/intern/ghost/intern/GHOST_ContextGLX.cpp +++ b/intern/ghost/intern/GHOST_ContextGLX.cpp @@ -239,7 +239,7 @@ GHOST_TSuccess GHOST_ContextGLX::initializeDrawingContext() } attribs[i++] = 0; - /* Some drivers don't like having a true offscreen context. + /* Some drivers don't like having a true off-screen context. * Create a pixel buffer instead of a window to render to. * even if it will never be used for drawing. */ int pbuffer_attribs[] = {GLX_PBUFFER_WIDTH, 1, GLX_PBUFFER_HEIGHT, 1, None}; diff --git a/intern/ghost/intern/GHOST_ContextWGL.cpp b/intern/ghost/intern/GHOST_ContextWGL.cpp index ddb34a8afd9..b5b3fab838d 100644 --- a/intern/ghost/intern/GHOST_ContextWGL.cpp +++ b/intern/ghost/intern/GHOST_ContextWGL.cpp @@ -335,10 +335,11 @@ void GHOST_ContextWGL::initContextWGLEW(PIXELFORMATDESCRIPTOR &preferredPFD) if (!WIN32_CHK(::wglMakeCurrent(dummyHDC, dummyHGLRC))) goto finalize; - if (GLEW_CHK(glewInit()) != GLEW_OK) + if (GLEW_CHK(glewInit()) != GLEW_OK) { fprintf(stderr, "Warning! Dummy GLEW/WGLEW failed to initialize properly.\n"); + } - // the following are not technially WGLEW, but they also require a context to work + /* The following are not technically WGLEW, but they also require a context to work. */ #ifndef NDEBUG free((void *)m_dummyRenderer); diff --git a/intern/ghost/intern/GHOST_DisplayManager.cpp b/intern/ghost/intern/GHOST_DisplayManager.cpp index fe12a76753d..9abc652378a 100644 --- a/intern/ghost/intern/GHOST_DisplayManager.cpp +++ b/intern/ghost/intern/GHOST_DisplayManager.cpp @@ -51,7 +51,7 @@ GHOST_TSuccess GHOST_DisplayManager::initialize(void) GHOST_TSuccess GHOST_DisplayManager::getNumDisplays(uint8_t & /*numDisplays*/) const { - // Don't know if we have a display... + /* Don't know if we have a display. */ return GHOST_kFailure; } @@ -120,18 +120,18 @@ GHOST_TSuccess GHOST_DisplayManager::findMatch(uint8_t display, (int)setting.xPixels, (int)setting.yPixels, (int)setting.bpp, (int)setting.frequency}; int capabilities[4]; double field, score; - double best = 1e12; // A big number + double best = 1e12; /* A big number. */ int found = 0; - // Look at all the display modes + /* Look at all the display modes. */ for (int i = 0; (i < (int)m_settings[display].size()); i++) { - // Store the capabilities of the display device + /* Store the capabilities of the display device. */ capabilities[0] = m_settings[display][i].xPixels; capabilities[1] = m_settings[display][i].yPixels; capabilities[2] = m_settings[display][i].bpp; capabilities[3] = m_settings[display][i].frequency; - // Match against all the fields of the display settings + /* Match against all the fields of the display settings. */ score = 0; for (int j = 0; j < 4; j++) { field = capabilities[j] - criteria[j]; diff --git a/intern/ghost/intern/GHOST_DropTargetX11.cpp b/intern/ghost/intern/GHOST_DropTargetX11.cpp index 8758a27930e..dba1d305144 100644 --- a/intern/ghost/intern/GHOST_DropTargetX11.cpp +++ b/intern/ghost/intern/GHOST_DropTargetX11.cpp @@ -115,8 +115,10 @@ GHOST_DropTargetX11::~GHOST_DropTargetX11() /* Based on: https://stackoverflow.com/a/2766963/432509 */ typedef enum DecodeState_e { - STATE_SEARCH = 0, ///< searching for an ampersand to convert - STATE_CONVERTING ///< convert the two proceeding characters from hex + /** Searching for an ampersand to convert. */ + STATE_SEARCH = 0, + /** Convert the two proceeding characters from hex. */ + STATE_CONVERTING } DecodeState_e; void GHOST_DropTargetX11::UrlDecode(char *decodedOut, int bufferSize, const char *encodedIn) diff --git a/intern/ghost/intern/GHOST_EventDragnDrop.h b/intern/ghost/intern/GHOST_EventDragnDrop.h index 537717b1717..0095cedb8c8 100644 --- a/intern/ghost/intern/GHOST_EventDragnDrop.h +++ b/intern/ghost/intern/GHOST_EventDragnDrop.h @@ -90,7 +90,7 @@ class GHOST_EventDragnDrop : public GHOST_Event { ~GHOST_EventDragnDrop() { - // Free the dropped object data + /* Free the dropped object data. */ if (m_dragnDropEventData.data == NULL) return; diff --git a/intern/ghost/intern/GHOST_EventManager.cpp b/intern/ghost/intern/GHOST_EventManager.cpp index 15befb9afcb..6ddc362ac77 100644 --- a/intern/ghost/intern/GHOST_EventManager.cpp +++ b/intern/ghost/intern/GHOST_EventManager.cpp @@ -108,12 +108,12 @@ GHOST_TSuccess GHOST_EventManager::addConsumer(GHOST_IEventConsumer *consumer) GHOST_TSuccess success; GHOST_ASSERT(consumer, "invalid consumer"); - // Check to see whether the consumer is already in our list + /* Check to see whether the consumer is already in our list. */ TConsumerVector::const_iterator iter = std::find( m_consumers.begin(), m_consumers.end(), consumer); if (iter == m_consumers.end()) { - // Add the consumer + /* Add the consumer. */ m_consumers.push_back(consumer); success = GHOST_kSuccess; } @@ -128,11 +128,11 @@ GHOST_TSuccess GHOST_EventManager::removeConsumer(GHOST_IEventConsumer *consumer GHOST_TSuccess success; GHOST_ASSERT(consumer, "invalid consumer"); - // Check to see whether the consumer is in our list + /* Check to see whether the consumer is in our list. */ TConsumerVector::iterator iter = std::find(m_consumers.begin(), m_consumers.end(), consumer); if (iter != m_consumers.end()) { - // Remove the consumer + /* Remove the consumer. */ m_consumers.erase(iter); success = GHOST_kSuccess; } diff --git a/intern/ghost/intern/GHOST_ISystem.cpp b/intern/ghost/intern/GHOST_ISystem.cpp index 7c12bfe0306..d9fecda22a4 100644 --- a/intern/ghost/intern/GHOST_ISystem.cpp +++ b/intern/ghost/intern/GHOST_ISystem.cpp @@ -60,6 +60,8 @@ GHOST_TSuccess GHOST_ISystem::createSystem() } catch (const std::runtime_error &) { /* fallback to X11. */ + delete m_system; + m_system = nullptr; } if (!m_system) { m_system = new GHOST_SystemX11(); diff --git a/intern/ghost/intern/GHOST_NDOFManager.cpp b/intern/ghost/intern/GHOST_NDOFManager.cpp index 079ad67f737..0317c175273 100644 --- a/intern/ghost/intern/GHOST_NDOFManager.cpp +++ b/intern/ghost/intern/GHOST_NDOFManager.cpp @@ -22,51 +22,51 @@ #include <limits.h> #include <math.h> -#include <stdio.h> // for error/info reporting -#include <string.h> // for memory functions +#include <stdio.h> /* For error/info reporting. */ +#include <string.h> /* For memory functions. */ #ifdef DEBUG_NDOF_MOTION -// printable version of each GHOST_TProgress value +/* Printable version of each GHOST_TProgress value. */ static const char *progress_string[] = { "not started", "starting", "in progress", "finishing", "finished"}; #endif #ifdef DEBUG_NDOF_BUTTONS static const char *ndof_button_names[] = { - // used internally, never sent + /* used internally, never sent */ "NDOF_BUTTON_NONE", - // these two are available from any 3Dconnexion device + /* these two are available from any 3Dconnexion device */ "NDOF_BUTTON_MENU", "NDOF_BUTTON_FIT", - // standard views + /* standard views */ "NDOF_BUTTON_TOP", "NDOF_BUTTON_BOTTOM", "NDOF_BUTTON_LEFT", "NDOF_BUTTON_RIGHT", "NDOF_BUTTON_FRONT", "NDOF_BUTTON_BACK", - // more views + /* more views */ "NDOF_BUTTON_ISO1", "NDOF_BUTTON_ISO2", - // 90 degree rotations + /* 90 degree rotations */ "NDOF_BUTTON_ROLL_CW", "NDOF_BUTTON_ROLL_CCW", "NDOF_BUTTON_SPIN_CW", "NDOF_BUTTON_SPIN_CCW", "NDOF_BUTTON_TILT_CW", "NDOF_BUTTON_TILT_CCW", - // device control + /* device control */ "NDOF_BUTTON_ROTATE", "NDOF_BUTTON_PANZOOM", "NDOF_BUTTON_DOMINANT", "NDOF_BUTTON_PLUS", "NDOF_BUTTON_MINUS", - // keyboard emulation + /* keyboard emulation */ "NDOF_BUTTON_ESC", "NDOF_BUTTON_ALT", "NDOF_BUTTON_SHIFT", "NDOF_BUTTON_CTRL", - // general-purpose buttons + /* general-purpose buttons */ "NDOF_BUTTON_1", "NDOF_BUTTON_2", "NDOF_BUTTON_3", @@ -77,17 +77,17 @@ static const char *ndof_button_names[] = { "NDOF_BUTTON_8", "NDOF_BUTTON_9", "NDOF_BUTTON_10", - // more general-purpose buttons + /* more general-purpose buttons */ "NDOF_BUTTON_A", "NDOF_BUTTON_B", "NDOF_BUTTON_C", - // the end + /* the end */ "NDOF_BUTTON_LAST"}; #endif -// shared by the latest 3Dconnexion hardware -// SpacePilotPro uses all of these -// smaller devices use only some, based on button mask +/* Shared by the latest 3Dconnexion hardware + * SpacePilotPro uses all of these + * smaller devices use only some, based on button mask. */ static const NDOF_ButtonT Modern3Dx_HID_map[] = { NDOF_BUTTON_MENU, NDOF_BUTTON_FIT, NDOF_BUTTON_TOP, NDOF_BUTTON_LEFT, NDOF_BUTTON_RIGHT, NDOF_BUTTON_FRONT, NDOF_BUTTON_BOTTOM, NDOF_BUTTON_BACK, @@ -116,15 +116,15 @@ static const NDOF_ButtonT SpaceExplorer_HID_map[] = { NDOF_BUTTON_ROTATE, }; -// this is the older SpacePilot (sans Pro) -// thanks to polosson for info about this device +/* This is the older SpacePilot (sans Pro) + * thanks to polosson for info about this device. */ static const NDOF_ButtonT SpacePilot_HID_map[] = { NDOF_BUTTON_1, NDOF_BUTTON_2, NDOF_BUTTON_3, NDOF_BUTTON_4, NDOF_BUTTON_5, NDOF_BUTTON_6, NDOF_BUTTON_TOP, NDOF_BUTTON_LEFT, NDOF_BUTTON_RIGHT, NDOF_BUTTON_FRONT, NDOF_BUTTON_ESC, NDOF_BUTTON_ALT, NDOF_BUTTON_SHIFT, NDOF_BUTTON_CTRL, NDOF_BUTTON_FIT, NDOF_BUTTON_MENU, NDOF_BUTTON_PLUS, NDOF_BUTTON_MINUS, NDOF_BUTTON_DOMINANT, NDOF_BUTTON_ROTATE, - NDOF_BUTTON_NONE // the CONFIG button -- what does it do? + NDOF_BUTTON_NONE /* the CONFIG button -- what does it do? */ }; static const NDOF_ButtonT Generic_HID_map[] = { @@ -146,7 +146,7 @@ static const int genericButtonCount = sizeof(Generic_HID_map) / sizeof(NDOF_Butt GHOST_NDOFManager::GHOST_NDOFManager(GHOST_System &sys) : m_system(sys), - m_deviceType(NDOF_UnknownDevice), // each platform has its own device detection code + m_deviceType(NDOF_UnknownDevice), /* Each platform has its own device detection code. */ m_buttonCount(genericButtonCount), m_buttonMask(0), m_hidMap(Generic_HID_map), @@ -157,37 +157,37 @@ GHOST_NDOFManager::GHOST_NDOFManager(GHOST_System &sys) m_motionEventPending(false), m_deadZone(0.0f) { - // to avoid the rare situation where one triple is updated and - // the other is not, initialize them both here: + /* To avoid the rare situation where one triple is updated and + * the other is not, initialize them both here: */ memset(m_translation, 0, sizeof(m_translation)); memset(m_rotation, 0, sizeof(m_rotation)); } bool GHOST_NDOFManager::setDevice(unsigned short vendor_id, unsigned short product_id) { - // call this function until it returns true - // it's a good idea to stop calling it after that, as it will "forget" - // whichever device it already found + /* Call this function until it returns true + * it's a good idea to stop calling it after that, as it will "forget" + * whichever device it already found */ - // default to safe generic behavior for "unknown" devices - // unidentified devices will emit motion events like normal - // rogue buttons do nothing by default, but can be customized by the user + /* Default to safe generic behavior for "unknown" devices + * unidentified devices will emit motion events like normal + * rogue buttons do nothing by default, but can be customized by the user. */ m_deviceType = NDOF_UnknownDevice; m_hidMap = Generic_HID_map; m_buttonCount = genericButtonCount; m_buttonMask = 0; - // "mystery device" owners can help build a HID_map for their hardware - // A few users have already contributed information about several older devices - // that I don't have access to. Thanks! + /* "mystery device" owners can help build a HID_map for their hardware + * A few users have already contributed information about several older devices + * that I don't have access to. Thanks! */ switch (vendor_id) { - case 0x046D: // Logitech (3Dconnexion was a subsidiary) + case 0x046D: /* Logitech (3Dconnexion was a subsidiary). */ switch (product_id) { - // -- current devices -- - case 0xC626: // full-size SpaceNavigator - case 0xC628: // the "for Notebooks" one + /* -- current devices -- */ + case 0xC626: /* full-size SpaceNavigator */ + case 0xC628: /* the "for Notebooks" one */ puts("ndof: using SpaceNavigator"); m_deviceType = NDOF_SpaceNavigator; m_buttonCount = 2; @@ -209,12 +209,12 @@ bool GHOST_NDOFManager::setDevice(unsigned short vendor_id, unsigned short produ puts("ndof: using SpaceMouse Pro"); m_deviceType = NDOF_SpaceMousePro; m_buttonCount = 27; - // ^^ actually has 15 buttons, but their HID codes range from 0 to 26 + /* ^^ actually has 15 buttons, but their HID codes range from 0 to 26 */ m_buttonMask = 0x07C0F137; m_hidMap = Modern3Dx_HID_map; break; - // -- older devices -- + /* -- older devices -- */ case 0xC625: puts("ndof: using SpacePilot"); m_deviceType = NDOF_SpacePilot; @@ -236,21 +236,21 @@ bool GHOST_NDOFManager::setDevice(unsigned short vendor_id, unsigned short produ printf("ndof: unknown Logitech product %04hx\n", product_id); } break; - case 0x256F: // 3Dconnexion + case 0x256F: /* 3Dconnexion */ switch (product_id) { - case 0xC62E: // plugged in - case 0xC62F: // wireless + case 0xC62E: /* Plugged in. */ + case 0xC62F: /* Wireless. */ puts("ndof: using SpaceMouse Wireless"); m_deviceType = NDOF_SpaceMouseWireless; m_buttonCount = 2; m_hidMap = Modern3Dx_HID_map; break; - case 0xC631: // plugged in - case 0xC632: // wireless + case 0xC631: /* Plugged in. */ + case 0xC632: /* Wireless. */ puts("ndof: using SpaceMouse Pro Wireless"); m_deviceType = NDOF_SpaceMouseProWireless; m_buttonCount = 27; - // ^^ actually has 15 buttons, but their HID codes range from 0 to 26 + /* ^^ actually has 15 buttons, but their HID codes range from 0 to 26. */ m_buttonMask = 0x07C0F137; m_hidMap = Modern3Dx_HID_map; break; @@ -364,16 +364,16 @@ void GHOST_NDOFManager::updateButton(int button_number, bool press, uint64_t tim int mask = 1 << button_number; if (press) { - m_buttons |= mask; // set this button's bit + m_buttons |= mask; /* Set this button's bit. */ } else { - m_buttons &= ~mask; // clear this button's bit + m_buttons &= ~mask; /* Clear this button's bit. */ } } void GHOST_NDOFManager::updateButtons(int button_bits, uint64_t time) { - button_bits &= m_buttonMask; // discard any "garbage" bits + button_bits &= m_buttonMask; /* Discard any "garbage" bits. */ int diff = m_buttons ^ button_bits; @@ -390,11 +390,11 @@ void GHOST_NDOFManager::updateButtons(int button_bits, uint64_t time) void GHOST_NDOFManager::setDeadZone(float dz) { if (dz < 0.0f) { - // negative values don't make sense, so clamp at zero + /* Negative values don't make sense, so clamp at zero. */ dz = 0.0f; } else if (dz > 0.5f) { - // warn the rogue user/developer, but allow it + /* Warn the rogue user/developer, but allow it. */ GHOST_PRINTF("ndof: dead zone of %.2f is rather high...\n", dz); } m_deadZone = dz; @@ -426,22 +426,22 @@ bool GHOST_NDOFManager::sendMotionEvent() if (!m_motionEventPending) return false; - m_motionEventPending = false; // any pending motion is handled right now + m_motionEventPending = false; /* Any pending motion is handled right now. */ GHOST_IWindow *window = m_system.getWindowManager()->getActiveWindow(); if (window == NULL) { - m_motionState = GHOST_kNotStarted; // avoid large 'dt' times when changing windows - return false; // delivery will fail, so don't bother sending + m_motionState = GHOST_kNotStarted; /* Avoid large `dt` times when changing windows. */ + return false; /* Delivery will fail, so don't bother sending. */ } GHOST_EventNDOFMotion *event = new GHOST_EventNDOFMotion(m_motionTime, window); GHOST_TEventNDOFMotionData *data = (GHOST_TEventNDOFMotionData *)event->getData(); - // scale axis values here to normalize them to around +/- 1 - // they are scaled again for overall sensitivity in the WM based on user prefs + /* Scale axis values here to normalize them to around +/- 1 + * they are scaled again for overall sensitivity in the WM based on user preferences. */ - const float scale = 1.0f / 350.0f; // 3Dconnexion devices send +/- 350 usually + const float scale = 1.0f / 350.0f; /* 3Dconnexion devices send +/- 350 usually */ data->tx = scale * m_translation[0]; data->ty = scale * m_translation[1]; @@ -451,24 +451,24 @@ bool GHOST_NDOFManager::sendMotionEvent() data->ry = scale * m_rotation[1]; data->rz = scale * m_rotation[2]; - data->dt = 0.001f * (m_motionTime - m_prevMotionTime); // in seconds + data->dt = 0.001f * (m_motionTime - m_prevMotionTime); /* In seconds. */ m_prevMotionTime = m_motionTime; bool weHaveMotion = !nearHomePosition(data, m_deadZone); - // determine what kind of motion event to send (Starting, InProgress, Finishing) - // and where that leaves this NDOF manager (NotStarted, InProgress, Finished) + /* Determine what kind of motion event to send `(Starting, InProgress, Finishing)` + * and where that leaves this NDOF manager `(NotStarted, InProgress, Finished)`. */ switch (m_motionState) { case GHOST_kNotStarted: case GHOST_kFinished: if (weHaveMotion) { data->progress = GHOST_kStarting; m_motionState = GHOST_kInProgress; - // prev motion time will be ancient, so just make up a reasonable time delta + /* Previous motion time will be ancient, so just make up a reasonable time delta. */ data->dt = 0.0125f; } else { - // send no event and keep current state + /* Send no event and keep current state. */ #ifdef DEBUG_NDOF_MOTION printf("ndof motion ignored -- %s\n", progress_string[data->progress]); #endif @@ -479,20 +479,22 @@ bool GHOST_NDOFManager::sendMotionEvent() case GHOST_kInProgress: if (weHaveMotion) { data->progress = GHOST_kInProgress; - // remain 'InProgress' + /* Remain 'InProgress'. */ } else { data->progress = GHOST_kFinishing; m_motionState = GHOST_kFinished; } break; - default:; // will always be one of the above + default: + /* Will always be one of the above. */ + break; } #ifdef DEBUG_NDOF_MOTION printf("ndof motion sent -- %s\n", progress_string[data->progress]); - // show details about this motion event + /* Show details about this motion event. */ printf(" T=(%d,%d,%d) R=(%d,%d,%d) raw\n", m_translation[0], m_translation[1], diff --git a/intern/ghost/intern/GHOST_NDOFManager.h b/intern/ghost/intern/GHOST_NDOFManager.h index 7be129c327c..31b11a352db 100644 --- a/intern/ghost/intern/GHOST_NDOFManager.h +++ b/intern/ghost/intern/GHOST_NDOFManager.h @@ -28,7 +28,7 @@ typedef enum { NDOF_UnknownDevice, - // current devices + /* Current devices. */ NDOF_SpaceNavigator, NDOF_SpaceExplorer, NDOF_SpacePilotPro, @@ -37,51 +37,51 @@ typedef enum { NDOF_SpaceMouseProWireless, NDOF_SpaceMouseEnterprise, - // older devices + /* Older devices. */ NDOF_SpacePilot, NDOF_Spaceball5000, NDOF_SpaceTraveler } NDOF_DeviceT; -// NDOF device button event types +/* NDOF device button event types */ typedef enum { - // used internally, never sent + /* Used internally, never sent. */ NDOF_BUTTON_NONE, - // these two are available from any 3Dconnexion device + /* These two are available from any 3Dconnexion device. */ NDOF_BUTTON_MENU, NDOF_BUTTON_FIT, - // standard views + /* Standard views. */ NDOF_BUTTON_TOP, NDOF_BUTTON_BOTTOM, NDOF_BUTTON_LEFT, NDOF_BUTTON_RIGHT, NDOF_BUTTON_FRONT, NDOF_BUTTON_BACK, - // more views + /* More views. */ NDOF_BUTTON_ISO1, NDOF_BUTTON_ISO2, - // 90 degree rotations - // these don't all correspond to physical buttons + /* 90 degree rotations. + * These don't all correspond to physical buttons. */ NDOF_BUTTON_ROLL_CW, NDOF_BUTTON_ROLL_CCW, NDOF_BUTTON_SPIN_CW, NDOF_BUTTON_SPIN_CCW, NDOF_BUTTON_TILT_CW, NDOF_BUTTON_TILT_CCW, - // device control + /* Device control. */ NDOF_BUTTON_ROTATE, NDOF_BUTTON_PANZOOM, NDOF_BUTTON_DOMINANT, NDOF_BUTTON_PLUS, NDOF_BUTTON_MINUS, - // keyboard emulation + /* Keyboard emulation. */ NDOF_BUTTON_ESC, NDOF_BUTTON_ALT, NDOF_BUTTON_SHIFT, NDOF_BUTTON_CTRL, - // general-purpose buttons - // users can assign functions via keymap editor + /* General-purpose buttons. + * Users can assign functions via keymap editor. */ NDOF_BUTTON_1, NDOF_BUTTON_2, NDOF_BUTTON_3, @@ -92,11 +92,11 @@ typedef enum { NDOF_BUTTON_8, NDOF_BUTTON_9, NDOF_BUTTON_10, - // more general-purpose buttons + /* More general-purpose buttons. */ NDOF_BUTTON_A, NDOF_BUTTON_B, NDOF_BUTTON_C, - // the end + /* The end. */ NDOF_BUTTON_LAST } NDOF_ButtonT; @@ -107,40 +107,53 @@ class GHOST_NDOFManager { { } - // whether multi-axis functionality is available (via the OS or driver) - // does not imply that a device is plugged in or being used + /** + * Whether multi-axis functionality is available (via the OS or driver) + * does not imply that a device is plugged in or being used. + */ virtual bool available() = 0; - // each platform's device detection should call this - // use standard USB/HID identifiers + /** + * Each platform's device detection should call this + * use standard USB/HID identifiers. + */ bool setDevice(unsigned short vendor_id, unsigned short product_id); - // filter out small/accidental/uncalibrated motions by - // setting up a "dead zone" around home position - // set to 0 to disable - // 0.1 is a safe and reasonable value + /** + * Filter out small/accidental/un-calibrated motions by + * setting up a "dead zone" around home position + * set to 0 to disable + * 0.1 is a safe and reasonable value. + */ void setDeadZone(float); - // the latest raw axis data from the device - // NOTE: axis data should be in blender view coordinates - // +X is to the right - // +Y is up - // +Z is out of the screen - // for rotations, look from origin to each +axis - // rotations are + when CCW, - when CW - // each platform is responsible for getting axis data into this form - // these values should not be scaled (just shuffled or flipped) + /** + * The latest raw axis data from the device. + * + * \note axis data should be in blender view coordinates + * - +X is to the right. + * - +Y is up. + * - +Z is out of the screen. + * - for rotations, look from origin to each +axis. + * - rotations are + when CCW, - when CW. + * Each platform is responsible for getting axis data into this form + * these values should not be scaled (just shuffled or flipped). + */ void updateTranslation(const int t[3], uint64_t time); void updateRotation(const int r[3], uint64_t time); - // the latest raw button data from the device - // use HID button encoding (not NDOF_ButtonT) + /** + * The latest raw button data from the device + * use HID button encoding (not #NDOF_ButtonT). + */ void updateButton(int button_number, bool press, uint64_t time); void updateButtons(int button_bits, uint64_t time); - // NDOFButton events are sent immediately + /* #NDOFButton events are sent immediately */ - // processes and sends most recent raw data as an NDOFMotion event - // returns whether an event was sent + /** + * Processes and sends most recent raw data as an #NDOFMotion event + * returns whether an event was sent. + */ bool sendMotionEvent(); protected: @@ -157,12 +170,12 @@ class GHOST_NDOFManager { int m_translation[3]; int m_rotation[3]; - int m_buttons; // bit field + int m_buttons; /* Bit field. */ - uint64_t m_motionTime; // in milliseconds - uint64_t m_prevMotionTime; // time of most recent Motion event sent + uint64_t m_motionTime; /* In milliseconds. */ + uint64_t m_prevMotionTime; /* Time of most recent motion event sent. */ GHOST_TProgress m_motionState; bool m_motionEventPending; - float m_deadZone; // discard motion with each component < this + float m_deadZone; /* Discard motion with each component < this. */ }; diff --git a/intern/ghost/intern/GHOST_Rect.cpp b/intern/ghost/intern/GHOST_Rect.cpp index 8ef9486f35a..78c88cb0a71 100644 --- a/intern/ghost/intern/GHOST_Rect.cpp +++ b/intern/ghost/intern/GHOST_Rect.cpp @@ -26,14 +26,14 @@ void GHOST_Rect::inset(int32_t i) { if (i > 0) { - // Grow the rectangle + /* Grow the rectangle. */ m_l -= i; m_r += i; m_t -= i; m_b += i; } else if (i < 0) { - // Shrink the rectangle, check for insets larger than half the size + /* Shrink the rectangle, check for insets larger than half the size. */ int32_t i2 = i * 2; if (getWidth() > i2) { m_l += i; @@ -62,12 +62,12 @@ GHOST_TVisibility GHOST_Rect::getVisibility(GHOST_Rect &r) const bool rb = isInside(r.m_r, r.m_b); GHOST_TVisibility v; if (lt && rt && lb && rb) { - // All points inside, rectangle is inside this + /* All points inside, rectangle is inside this. */ v = GHOST_kFullyVisible; } else if (!(lt || rt || lb || rb)) { - // None of the points inside - // Check to see whether the rectangle is larger than this one + /* None of the points inside. + * Check to see whether the rectangle is larger than this one. */ if ((r.m_l < m_l) && (r.m_t < m_t) && (r.m_r > m_r) && (r.m_b > m_b)) { v = GHOST_kPartiallyVisible; } @@ -76,7 +76,7 @@ GHOST_TVisibility GHOST_Rect::getVisibility(GHOST_Rect &r) const } } else { - // Some of the points inside, rectangle is partially inside + /* Some of the points inside, rectangle is partially inside. */ v = GHOST_kPartiallyVisible; } return v; diff --git a/intern/ghost/intern/GHOST_System.cpp b/intern/ghost/intern/GHOST_System.cpp index f6659cf50dc..d09c167cb95 100644 --- a/intern/ghost/intern/GHOST_System.cpp +++ b/intern/ghost/intern/GHOST_System.cpp @@ -72,7 +72,7 @@ GHOST_ITimerTask *GHOST_System::installTimer(uint64_t delay, GHOST_TimerTask *timer = new GHOST_TimerTask(millis + delay, interval, timerProc, userData); if (timer) { if (m_timerManager->addTimer(timer) == GHOST_kSuccess) { - // Check to see whether we need to fire the timer right away + /* Check to see whether we need to fire the timer right away. */ m_timerManager->fireTimers(millis); } else { @@ -208,7 +208,7 @@ bool GHOST_System::getFullScreen(void) void GHOST_System::dispatchEvents() { #ifdef WITH_INPUT_NDOF - // NDOF Motion event is sent only once per dispatch, so do it now: + /* NDOF Motion event is sent only once per dispatch, so do it now: */ if (m_ndofManager) { m_ndofManager->sendMotionEvent(); } @@ -260,10 +260,10 @@ GHOST_TSuccess GHOST_System::pushEvent(GHOST_IEvent *event) GHOST_TSuccess GHOST_System::getModifierKeyState(GHOST_TModifierKeyMask mask, bool &isDown) const { GHOST_ModifierKeys keys; - // Get the state of all modifier keys + /* Get the state of all modifier keys. */ GHOST_TSuccess success = getModifierKeys(keys); if (success) { - // Isolate the state of the key requested + /* Isolate the state of the key requested. */ isDown = keys.get(mask); } return success; @@ -272,10 +272,10 @@ GHOST_TSuccess GHOST_System::getModifierKeyState(GHOST_TModifierKeyMask mask, bo GHOST_TSuccess GHOST_System::getButtonState(GHOST_TButtonMask mask, bool &isDown) const { GHOST_Buttons buttons; - // Get the state of all mouse buttons + /* Get the state of all mouse buttons. */ GHOST_TSuccess success = getButtons(buttons); if (success) { - // Isolate the state of the mouse button requested + /* Isolate the state of the mouse button requested. */ isDown = buttons.get(mask); } return success; @@ -311,7 +311,7 @@ GHOST_TSuccess GHOST_System::init() m_eventPrinter = new GHOST_EventPrinter(); m_eventManager->addConsumer(m_eventPrinter); } -#endif // WITH_GHOST_DEBUG +#endif /* WITH_GHOST_DEBUG */ if (m_timerManager && m_windowManager && m_eventManager) { return GHOST_kSuccess; @@ -359,7 +359,7 @@ GHOST_TSuccess GHOST_System::createFullScreenWindow(GHOST_Window **window, if (alphaBackground) glSettings.flags |= GHOST_glAlphaBackground; - /* note: don't use getCurrentDisplaySetting() because on X11 we may + /* NOTE: don't use #getCurrentDisplaySetting() because on X11 we may * be zoomed in and the desktop may be bigger than the viewport. */ GHOST_ASSERT(m_displayManager, "GHOST_System::createFullScreenWindow(): invalid display manager"); diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h index 9164687c5b5..16c34ff1a6d 100644 --- a/intern/ghost/intern/GHOST_System.h +++ b/intern/ghost/intern/GHOST_System.h @@ -113,8 +113,8 @@ class GHOST_System : public GHOST_ISystem { GHOST_TSuccess disposeWindow(GHOST_IWindow *window); /** - * Create a new offscreen context. - * Never explicitly delete the context, use disposeContext() instead. + * Create a new off-screen context. + * Never explicitly delete the context, use #disposeContext() instead. * \return The new context (or 0 if creation failed). */ virtual GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings) = 0; diff --git a/intern/ghost/intern/GHOST_SystemCocoa.h b/intern/ghost/intern/GHOST_SystemCocoa.h index 48a64b155fc..5950da6813d 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.h +++ b/intern/ghost/intern/GHOST_SystemCocoa.h @@ -112,8 +112,8 @@ class GHOST_SystemCocoa : public GHOST_System { const GHOST_IWindow *parentWindow = NULL); /** - * Create a new offscreen context. - * Never explicitly delete the context, use disposeContext() instead. + * Create a new off-screen context. + * Never explicitly delete the context, use #disposeContext() instead. * \return The new context (or 0 if creation failed). */ GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings); diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index 0f10d5815f4..108c2e50c0c 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -761,7 +761,7 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const char *title, } /** - * Create a new offscreen context. + * Create a new off-screen context. * Never explicitly delete the context, use #disposeContext() instead. * \return The new context (or 0 if creation failed). */ diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index f2f1b26b8e5..0309a4f9c52 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -143,7 +143,7 @@ GHOST_IContext *GHOST_SystemSDL::createOffscreenContext(GHOST_GLSettings glSetti { GHOST_Context *context = new GHOST_ContextSDL(0, NULL, - 0, // profile bit + 0, /* Profile bit. */ 3, 3, GHOST_OPENGL_SDL_CONTEXT_FLAGS, @@ -279,7 +279,7 @@ static GHOST_TKey convertSDLKey(SDL_Scancode key) GXMAP(type, SDL_SCANCODE_AUDIOPLAY, GHOST_kKeyMediaPlay); GXMAP(type, SDL_SCANCODE_AUDIOSTOP, GHOST_kKeyMediaStop); GXMAP(type, SDL_SCANCODE_AUDIOPREV, GHOST_kKeyMediaFirst); - // GXMAP(type,XF86XK_AudioRewind, GHOST_kKeyMediaFirst); + // GXMAP(type, XF86XK_AudioRewind, GHOST_kKeyMediaFirst); GXMAP(type, SDL_SCANCODE_AUDIONEXT, GHOST_kKeyMediaLast); default: @@ -315,7 +315,10 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) SDL_WindowEvent &sdl_sub_evt = sdl_event->window; GHOST_WindowSDL *window = findGhostWindow( SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID)); - // assert(window != NULL); // can be NULL on close window. + /* Can be NULL on close window. */ +#if 0 + assert(window != NULL); +#endif switch (sdl_sub_evt.event) { case SDL_WINDOWEVENT_EXPOSED: @@ -376,14 +379,14 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) bounds.wrapPoint(x_new, y_new, 8, window->getCursorGrabAxis()); window->getCursorGrabAccum(x_accum, y_accum); - // can't use setCursorPosition because the mouse may have no focus! + /* Can't use #setCursorPosition because the mouse may have no focus! */ if (x_new != x_root || y_new != y_root) { - if (1) { //xme.time > m_last_warp) { + if (1 /* `xme.time > m_last_warp` */ ) { /* when wrapping we don't need to add an event because the - * setCursorPosition call will cause a new event after */ + * #setCursorPosition call will cause a new event after */ SDL_WarpMouseInWindow(sdl_win, x_new - x_win, y_new - y_win); /* wrap */ window->setCursorGrabAccum(x_accum + (x_root - x_new), y_accum + (y_root - y_new)); - // m_last_warp= lastEventTime(xme.time); + // m_last_warp = lastEventTime(xme.time); } else { // setCursorPosition(x_new, y_new); /* wrap but don't accumulate */ @@ -659,8 +662,8 @@ bool GHOST_SystemSDL::generateWindowExposeEvents() bool GHOST_SystemSDL::processEvents(bool waitForEvent) { - // Get all the current events -- translate them into - // ghost events and call base class pushEvent() method. + /* Get all the current events - translate them into + * ghost events and call base class #pushEvent() method. */ bool anyProcessed = false; @@ -679,7 +682,7 @@ bool GHOST_SystemSDL::processEvents(bool waitForEvent) if (maxSleep >= 0) { SDL_WaitEventTimeout(NULL, next - getMilliSeconds()); - // SleepTillEvent(m_display, next - getMilliSeconds()); // X11 + // SleepTillEvent(m_display, next - getMilliSeconds()); /* X11. */ } } } @@ -707,10 +710,10 @@ GHOST_WindowSDL *GHOST_SystemSDL::findGhostWindow(SDL_Window *sdl_win) if (sdl_win == NULL) return NULL; - // It is not entirely safe to do this as the backptr may point - // to a window that has recently been removed. - // We should always check the window manager's list of windows - // and only process events on these windows. + /* It is not entirely safe to do this as the backptr may point + * to a window that has recently been removed. + * We should always check the window manager's list of windows + * and only process events on these windows. */ const std::vector<GHOST_IWindow *> &win_vec = m_windowManager->getWindows(); diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 4f5e957077d..f2ed981751c 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -259,7 +259,7 @@ GHOST_IWindow *GHOST_SystemWin32::createWindow(const char *title, } /** - * Create a new offscreen context. + * Create a new off-screen context. * Never explicitly delete the window, use #disposeContext() instead. * \return The new context (or 0 if creation failed). */ @@ -363,7 +363,7 @@ GHOST_TSuccess GHOST_SystemWin32::disposeContext(GHOST_IContext *context) } /** - * Create a new offscreen DirectX 11 context. + * Create a new off-screen DirectX 11 context. * Never explicitly delete the window, use #disposeContext() instead. * \return The new context (or 0 if creation failed). */ diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h index 6c786aedfb1..4794982dc65 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.h +++ b/intern/ghost/intern/GHOST_SystemWin32.h @@ -138,8 +138,8 @@ class GHOST_SystemWin32 : public GHOST_System { const GHOST_IWindow *parentWindow = 0); /** - * Create a new offscreen context. - * Never explicitly delete the window, use disposeContext() instead. + * Create a new off-screen context. + * Never explicitly delete the window, use #disposeContext() instead. * \return The new context (or 0 if creation failed). */ GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings); @@ -152,8 +152,8 @@ class GHOST_SystemWin32 : public GHOST_System { GHOST_TSuccess disposeContext(GHOST_IContext *context); /** - * Create a new offscreen DirectX context. - * Never explicitly delete the context, use disposeContext() instead. + * Create a new off-screen DirectX context. + * Never explicitly delete the context, use #disposeContext() instead. * This is for GHOST internal, Win32 specific use, so it can be called statically. * * \return The new context (or 0 if creation failed). @@ -360,8 +360,8 @@ class GHOST_SystemWin32 : public GHOST_System { static GHOST_EventKey *processKeyEvent(GHOST_WindowWin32 *window, RAWINPUT const &raw); /** - * Process special keys (VK_OEM_*), to see if current key layout - * gives us anything special, like ! on french AZERTY. + * Process special keys `VK_OEM_*`, to see if current key layout + * gives us anything special, like `!` on French AZERTY. * \param vKey: The virtual key from #hardKey. * \param scanCode: The ScanCode of pressed key (similar to PS/2 Set 1). */ diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 172fcbeb3de..7af42e0c5bb 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -390,21 +390,21 @@ GHOST_IWindow *GHOST_SystemX11::createWindow(const char *title, } /** - * Create a new offscreen context. - * Never explicitly delete the context, use disposeContext() instead. + * Create a new off-screen context. + * Never explicitly delete the context, use #disposeContext() instead. * \return The new context (or 0 if creation failed). */ GHOST_IContext *GHOST_SystemX11::createOffscreenContext(GHOST_GLSettings glSettings) { - // During development: - // try 4.x compatibility profile - // try 3.3 compatibility profile - // fall back to 3.0 if needed - // - // Final Blender 2.8: - // try 4.x core profile - // try 3.3 core profile - // no fallbacks + /* During development: + * try 4.x compatibility profile + * try 3.3 compatibility profile + * fall back to 3.0 if needed + * + * Final Blender 2.8: + * try 4.x core profile + * try 3.3 core profile + * no fall-backs. */ const bool debug_context = (glSettings.flags & GHOST_glDebugContext) != 0; @@ -2014,7 +2014,7 @@ void GHOST_SystemX11::getClipboard_xcout(const XEvent *evt, return; } - // not using INCR mechanism, just read the property + /* Not using INCR mechanism, just read the property. */ XGetWindowProperty(m_display, win, m_atom.XCLIP_OUT, diff --git a/intern/ghost/intern/GHOST_SystemX11.h b/intern/ghost/intern/GHOST_SystemX11.h index 15ccde4a14b..d4803f88fbb 100644 --- a/intern/ghost/intern/GHOST_SystemX11.h +++ b/intern/ghost/intern/GHOST_SystemX11.h @@ -148,8 +148,8 @@ class GHOST_SystemX11 : public GHOST_System { const GHOST_IWindow *parentWindow = 0); /** - * Create a new offscreen context. - * Never explicitly delete the context, use disposeContext() instead. + * Create a new off-screen context. + * Never explicitly delete the context, use #disposeContext() instead. * \return The new context (or 0 if creation failed). */ GHOST_IContext *createOffscreenContext(GHOST_GLSettings glSettings); diff --git a/intern/ghost/intern/GHOST_TimerManager.cpp b/intern/ghost/intern/GHOST_TimerManager.cpp index 195135f5f85..0c88150381f 100644 --- a/intern/ghost/intern/GHOST_TimerManager.cpp +++ b/intern/ghost/intern/GHOST_TimerManager.cpp @@ -55,7 +55,7 @@ GHOST_TSuccess GHOST_TimerManager::addTimer(GHOST_TimerTask *timer) { GHOST_TSuccess success; if (!getTimerFound(timer)) { - // Add the timer task + /* Add the timer task. */ m_timers.push_back(timer); success = GHOST_kSuccess; } @@ -70,7 +70,7 @@ GHOST_TSuccess GHOST_TimerManager::removeTimer(GHOST_TimerTask *timer) GHOST_TSuccess success; TTimerVector::iterator iter = std::find(m_timers.begin(), m_timers.end(), timer); if (iter != m_timers.end()) { - // Remove the timer task + /* Remove the timer task. */ m_timers.erase(iter); delete timer; success = GHOST_kSuccess; @@ -113,14 +113,14 @@ bool GHOST_TimerManager::fireTimer(uint64_t time, GHOST_TimerTask *task) { uint64_t next = task->getNext(); - // Check if the timer should be fired + /* Check if the timer should be fired. */ if (time > next) { - // Fire the timer + /* Fire the timer. */ GHOST_TimerProcPtr timerProc = task->getTimerProc(); uint64_t start = task->getStart(); timerProc(task, time - start); - // Update the time at which we will fire it again + /* Update the time at which we will fire it again. */ uint64_t interval = task->getInterval(); uint64_t numCalls = (next - start) / interval; numCalls++; diff --git a/intern/ghost/intern/GHOST_WindowCocoa.h b/intern/ghost/intern/GHOST_WindowCocoa.h index 0fd70514ac6..68ac507f0e0 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.h +++ b/intern/ghost/intern/GHOST_WindowCocoa.h @@ -157,7 +157,7 @@ class GHOST_WindowCocoa : public GHOST_Window { void screenToClient(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const; /** - * Converts a point in screen coordinates to client rectangle coordinates + * Converts a point in client rectangle coordinates to screen coordinates. * \param inX: The x-coordinate in the client rectangle. * \param inY: The y-coordinate in the client rectangle. * \param outX: The x-coordinate on the screen. @@ -166,7 +166,7 @@ class GHOST_WindowCocoa : public GHOST_Window { void clientToScreen(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const; /** - * Converts a point in screen coordinates to client rectangle coordinates + * Converts a point in client rectangle coordinates to screen coordinates. * but without the y coordinate conversion needed for ghost compatibility. * \param inX: The x-coordinate in the client rectangle. * \param inY: The y-coordinate in the client rectangle. @@ -178,10 +178,10 @@ class GHOST_WindowCocoa : public GHOST_Window { /** * Converts a point in screen coordinates to client rectangle coordinates, * but without the y coordinate conversion needed for ghost compatibility. - * \param inX: The x-coordinate in the client rectangle. - * \param inY: The y-coordinate in the client rectangle. - * \param outX: The x-coordinate on the screen. - * \param outY: The y-coordinate on the screen. + * \param inX: The x-coordinate on the screen. + * \param inY: The y-coordinate on the screen. + * \param outX: The x-coordinate in the client rectangle. + * \param outY: The y-coordinate in the client rectangle. */ void screenToClientIntern(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const; diff --git a/intern/ghost/intern/GHOST_WindowManager.cpp b/intern/ghost/intern/GHOST_WindowManager.cpp index eec4bc5f7d0..4cb80884209 100644 --- a/intern/ghost/intern/GHOST_WindowManager.cpp +++ b/intern/ghost/intern/GHOST_WindowManager.cpp @@ -45,7 +45,7 @@ GHOST_TSuccess GHOST_WindowManager::addWindow(GHOST_IWindow *window) GHOST_TSuccess success = GHOST_kFailure; if (window) { if (!getWindowFound(window)) { - // Store the pointer to the window + /* Store the pointer to the window. */ m_windows.push_back(window); success = GHOST_kSuccess; } diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp index d0c8cfb9e73..71062a4b6d6 100644 --- a/intern/ghost/intern/GHOST_WindowWayland.cpp +++ b/intern/ghost/intern/GHOST_WindowWayland.cpp @@ -34,7 +34,7 @@ static constexpr size_t base_dpi = 96; struct window_t { GHOST_WindowWayland *w; wl_surface *surface; - // outputs on which the window is currently shown on + /* Outputs on which the window is currently shown on. */ std::unordered_set<const output_t *> outputs; uint16_t dpi = 0; int scale = 1; @@ -154,8 +154,8 @@ static bool update_scale(GHOST_WindowWayland *window) if (scale > 0 && window->scale() != scale) { window->scale() = scale; - // using the real DPI will cause wrong scaling of the UI - // use a multiplier for the default DPI as workaround + /* Using the real DPI will cause wrong scaling of the UI + * use a multiplier for the default DPI as workaround. */ window->dpi() = scale * base_dpi; wl_surface_set_buffer_scale(window->surface(), scale); return true; diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index 40a658bf88b..ed5292f1712 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -183,7 +183,7 @@ class GHOST_WindowWin32 : public GHOST_Window { void screenToClient(int32_t inX, int32_t inY, int32_t &outX, int32_t &outY) const; /** - * Converts a point in screen coordinates to client rectangle coordinates + * Converts a point in client rectangle coordinates to screen coordinates. * \param inX: The x-coordinate in the client rectangle. * \param inY: The y-coordinate in the client rectangle. * \param outX: The x-coordinate on the screen. diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp index 35280e77e22..919d11d22a9 100644 --- a/intern/ghost/intern/GHOST_XrSession.cpp +++ b/intern/ghost/intern/GHOST_XrSession.cpp @@ -120,7 +120,7 @@ static void create_reference_spaces(OpenXRSessionData &oxr, const GHOST_XrPose & XrReferenceSpaceCreateInfo create_info = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; create_info.poseInReferenceSpace.orientation.w = 1.0f; - create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE; #if 0 /* TODO * @@ -144,8 +144,47 @@ static void create_reference_spaces(OpenXRSessionData &oxr, const GHOST_XrPose & (void)base_pose; #endif - CHECK_XR(xrCreateReferenceSpace(oxr.session, &create_info, &oxr.reference_space), - "Failed to create reference space."); + XrResult result = xrCreateReferenceSpace(oxr.session, &create_info, &oxr.reference_space); + + if (XR_FAILED(result)) { + /* One of the rare cases where we don't want to immediately throw an exception on failure, + * since run-times are not required to support the stage reference space. Although we need the + * stage reference space for absolute tracking, if the runtime doesn't support it then just + * fallback to the local space. */ + if (result == XR_ERROR_REFERENCE_SPACE_UNSUPPORTED) { + printf( + "Warning: XR runtime does not support stage reference space, disabling absolute " + "tracking.\n"); + + create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + CHECK_XR(xrCreateReferenceSpace(oxr.session, &create_info, &oxr.reference_space), + "Failed to create local reference space."); + } + else { + throw GHOST_XrException("Failed to create stage reference space.", result); + } + } + else { + /* Check if tracking bounds are valid. Tracking bounds may be invalid if the user did not + * define a tracking space via the XR runtime. */ + XrExtent2Df extents; + CHECK_XR(xrGetReferenceSpaceBoundsRect(oxr.session, XR_REFERENCE_SPACE_TYPE_STAGE, &extents), + "Failed to get stage reference space bounds."); + if (extents.width == 0.0f || extents.height == 0.0f) { + printf( + "Warning: Invalid stage reference space bounds, disabling absolute tracking. To enable " + "absolute tracking, please define a tracking space via the XR runtime.\n"); + + /* Fallback to local space. */ + if (oxr.reference_space != XR_NULL_HANDLE) { + CHECK_XR(xrDestroySpace(oxr.reference_space), "Failed to destroy stage reference space."); + } + + create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + CHECK_XR(xrCreateReferenceSpace(oxr.session, &create_info, &oxr.reference_space), + "Failed to create local reference space."); + } + } create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW; CHECK_XR(xrCreateReferenceSpace(oxr.session, &create_info, &oxr.view_space), @@ -370,6 +409,7 @@ void GHOST_XrSession::drawView(GHOST_XrSwapchain &swapchain, XrCompositionLayerProjectionView &r_proj_layer_view, XrSpaceLocation &view_location, XrView &view, + uint32_t view_idx, void *draw_customdata) { XrSwapchainImageBaseHeader *swapchain_image = swapchain.acquireDrawableSwapchainImage(); @@ -380,6 +420,8 @@ void GHOST_XrSession::drawView(GHOST_XrSwapchain &swapchain, r_proj_layer_view.fov = view.fov; swapchain.updateCompositionLayerProjectViewSubImage(r_proj_layer_view.subImage); + assert(view_idx < 256); + draw_view_info.view_idx = (char)view_idx; draw_view_info.expects_srgb_buffer = swapchain.isBufferSRGB(); draw_view_info.ofsx = r_proj_layer_view.subImage.imageRect.offset.x; draw_view_info.ofsy = r_proj_layer_view.subImage.imageRect.offset.y; @@ -429,6 +471,7 @@ XrCompositionLayerProjection GHOST_XrSession::drawLayer( r_proj_layer_views[view_idx], view_location, m_oxr->views[view_idx], + view_idx, draw_customdata); } diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h index c871b98da46..d448585d14c 100644 --- a/intern/ghost/intern/GHOST_XrSession.h +++ b/intern/ghost/intern/GHOST_XrSession.h @@ -117,6 +117,7 @@ class GHOST_XrSession { XrCompositionLayerProjectionView &r_proj_layer_view, XrSpaceLocation &view_location, XrView &view, + uint32_t view_idx, void *draw_customdata); void beginFrameDrawing(); void endFrameDrawing(std::vector<XrCompositionLayerBaseHeader *> &layers); diff --git a/intern/guardedalloc/intern/mallocn.c b/intern/guardedalloc/intern/mallocn.c index 673821546e8..261a23a1196 100644 --- a/intern/guardedalloc/intern/mallocn.c +++ b/intern/guardedalloc/intern/mallocn.c @@ -67,7 +67,7 @@ const char *(*MEM_name_ptr)(void *vmemh) = MEM_lockfree_name_ptr; void *aligned_malloc(size_t size, size_t alignment) { - /* posix_memalign requires alignment to be a multiple of sizeof(void *). */ + /* #posix_memalign requires alignment to be a multiple of `sizeof(void *)`. */ assert(alignment >= ALIGNED_MALLOC_MINIMUM_ALIGNMENT); #ifdef _WIN32 diff --git a/intern/libmv/CMakeLists.txt b/intern/libmv/CMakeLists.txt index 6fcf664d1f5..91ef1f4d038 100644 --- a/intern/libmv/CMakeLists.txt +++ b/intern/libmv/CMakeLists.txt @@ -217,39 +217,39 @@ if(WITH_LIBMV) if(WITH_GTESTS) include(GTestTesting) - blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "" "" "") + blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "${INC}" "${INC_SYS}" "") - BLENDER_SRC_GTEST("libmv_predict_tracks" "./libmv/autotrack/predict_tracks_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_tracks" "./libmv/autotrack/tracks_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_scoped_ptr" "./libmv/base/scoped_ptr_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_vector" "./libmv/base/vector_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_array_nd" "./libmv/image/array_nd_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_convolve" "./libmv/image/convolve_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_image" "./libmv/image/image_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_sample" "./libmv/image/sample_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_tuple" "./libmv/image/tuple_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_euclidean_resection" "./libmv/multiview/euclidean_resection_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_fundamental" "./libmv/multiview/fundamental_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_homography" "./libmv/multiview/homography_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_nviewtriangulation" "./libmv/multiview/nviewtriangulation_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_panography" "./libmv/multiview/panography_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_projection" "./libmv/multiview/projection_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_resection" "./libmv/multiview/resection_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_triangulation" "./libmv/multiview/triangulation_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_dogleg" "./libmv/numeric/dogleg_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_function_derivative" "./libmv/numeric/function_derivative_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_levenberg_marquardt" "./libmv/numeric/levenberg_marquardt_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_numeric" "./libmv/numeric/numeric_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_poly" "./libmv/numeric/poly_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_camera_intrinsics" "./libmv/simple_pipeline/camera_intrinsics_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_detect" "./libmv/simple_pipeline/detect_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_intersect" "./libmv/simple_pipeline/intersect_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_keyframe_selection" "./libmv/simple_pipeline/keyframe_selection_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_modal_solver" "./libmv/simple_pipeline/modal_solver_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_resect" "./libmv/simple_pipeline/resect_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_brute_region_tracker" "./libmv/tracking/brute_region_tracker_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_klt_region_tracker" "./libmv/tracking/klt_region_tracker_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") - BLENDER_SRC_GTEST("libmv_pyramid_region_tracker" "./libmv/tracking/pyramid_region_tracker_test.cc" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_predict_tracks" "./libmv/autotrack/predict_tracks_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_tracks" "./libmv/autotrack/tracks_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_scoped_ptr" "./libmv/base/scoped_ptr_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_vector" "./libmv/base/vector_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_array_nd" "./libmv/image/array_nd_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_convolve" "./libmv/image/convolve_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_image" "./libmv/image/image_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_sample" "./libmv/image/sample_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_tuple" "./libmv/image/tuple_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_euclidean_resection" "./libmv/multiview/euclidean_resection_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_fundamental" "./libmv/multiview/fundamental_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_homography" "./libmv/multiview/homography_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_nviewtriangulation" "./libmv/multiview/nviewtriangulation_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_panography" "./libmv/multiview/panography_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_projection" "./libmv/multiview/projection_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_resection" "./libmv/multiview/resection_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_triangulation" "./libmv/multiview/triangulation_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_dogleg" "./libmv/numeric/dogleg_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_function_derivative" "./libmv/numeric/function_derivative_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_levenberg_marquardt" "./libmv/numeric/levenberg_marquardt_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_numeric" "./libmv/numeric/numeric_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_poly" "./libmv/numeric/poly_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_camera_intrinsics" "./libmv/simple_pipeline/camera_intrinsics_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_detect" "./libmv/simple_pipeline/detect_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_intersect" "./libmv/simple_pipeline/intersect_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_keyframe_selection" "./libmv/simple_pipeline/keyframe_selection_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_modal_solver" "./libmv/simple_pipeline/modal_solver_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_resect" "./libmv/simple_pipeline/resect_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_brute_region_tracker" "./libmv/tracking/brute_region_tracker_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_klt_region_tracker" "./libmv/tracking/klt_region_tracker_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") + blender_add_test_executable("libmv_pyramid_region_tracker" "./libmv/tracking/pyramid_region_tracker_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres") endif() else() list(APPEND SRC diff --git a/intern/memutil/MEM_Allocator.h b/intern/memutil/MEM_Allocator.h index 50a3e978197..1dbb2d5a9f7 100644 --- a/intern/memutil/MEM_Allocator.h +++ b/intern/memutil/MEM_Allocator.h @@ -62,8 +62,8 @@ template<typename _Tp> struct MEM_Allocator { return &__x; } - // NB: __n is permitted to be 0. The C++ standard says nothing - // about what the return value is when __n == 0. + /* NOTE: `__n` is permitted to be 0. + * The C++ standard says nothing about what the return value is when `__n == 0`. */ _Tp *allocate(size_type __n, const void * = 0) { _Tp *__ret = NULL; diff --git a/intern/opensubdiv/CMakeLists.txt b/intern/opensubdiv/CMakeLists.txt index 16334a80761..bce8a8baa84 100644 --- a/intern/opensubdiv/CMakeLists.txt +++ b/intern/opensubdiv/CMakeLists.txt @@ -129,5 +129,5 @@ if(WITH_GTESTS AND WITH_OPENSUBDIV) add_definitions(${GLOG_DEFINES}) add_definitions(-DBLENDER_GFLAGS_NAMESPACE=${GFLAGS_NAMESPACE}) - BLENDER_SRC_GTEST(opensubdiv_mesh_topology_test "internal/topology/mesh_topology_test.cc" "${LIB};bf_intern_opensubdiv") + blender_add_test_executable(opensubdiv_mesh_topology_test "internal/topology/mesh_topology_test.cc" "${INC}" "${INC_SYS}" "${LIB};bf_intern_opensubdiv") endif() diff --git a/intern/rigidbody/RBI_api.h b/intern/rigidbody/RBI_api.h index 2e09f8952cb..f13f321a2c6 100644 --- a/intern/rigidbody/RBI_api.h +++ b/intern/rigidbody/RBI_api.h @@ -64,7 +64,7 @@ typedef struct rbConstraint rbConstraint; /* Setup ---------------------------- */ /* Create a new dynamics world instance */ -// TODO: add args to set the type of constraint solvers, etc. +/* TODO: add args to set the type of constraint solvers, etc. */ rbDynamicsWorld *RB_dworld_new(const float gravity[3]); /* Delete the given dynamics world, and free any extra data it may require */ diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject f3791fbfdb839860035241ba477bf8872966af9 +Subproject ca39c1459bcd99300afe3591fa5ffe40f5ba5ee diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c index 6220edce28f..d51a82c482b 100644 --- a/release/datafiles/userdef/userdef_default.c +++ b/release/datafiles/userdef/userdef_default.c @@ -113,7 +113,7 @@ const UserDef U_default = { .gp_eraser = 25, .gp_settings = 0, - /** Initialized by: #BKE_studiolight_default . */ + /** Initialized by: #BKE_studiolight_default. */ .light_param = {{0}}, .light_ambient = {0, 0, 0}, diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c index 441a92127ea..85532d01d5c 100644 --- a/release/datafiles/userdef/userdef_default_theme.c +++ b/release/datafiles/userdef/userdef_default_theme.c @@ -638,7 +638,7 @@ const bTheme U_theme_default = { .sub_back = RGBA(0x0000003e), }, .shade1 = RGBA(0xa0a0a000), - .grid = RGBA(0x404040ff), + .grid = RGBA(0x212121ff), .vertex_select = RGBA(0xff8500ff), .bone_pose = RGBA(0x50c8ff50), .cframe = RGBA(0x5680c2ff), diff --git a/release/scripts/addons b/release/scripts/addons -Subproject e25068ef471c6d6cd5ee64a2eef9b7d672c5702 +Subproject c2c5392e004bb8bafea84d8d69df31ef9f03d46 diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib -Subproject 7d78c8a63f2f4b146f9327ddc0d567a5921b94e +Subproject 788441f2930465bbfba8f0797b12dcef1d46694 diff --git a/release/scripts/modules/bl_i18n_utils/utils_cli.py b/release/scripts/modules/bl_i18n_utils/utils_cli.py index e18491fa042..ea74ba832d9 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_cli.py +++ b/release/scripts/modules/bl_i18n_utils/utils_cli.py @@ -71,7 +71,7 @@ def rtl_process_po(args, settings): po.write(kind="PO", dest=args.dst) -def language_menu(_args, settings): +def language_menu(args, settings): # 'DEFAULT' and en_US are always valid, fully-translated "languages"! stats = {"DEFAULT": 1.0, "en_US": 1.0} diff --git a/release/scripts/modules/nodeitems_utils.py b/release/scripts/modules/nodeitems_utils.py index a50997fab5f..a5c18cee463 100644 --- a/release/scripts/modules/nodeitems_utils.py +++ b/release/scripts/modules/nodeitems_utils.py @@ -77,7 +77,7 @@ class NodeItem: else: return bpy.app.translations.contexts.default - # NB: is a staticmethod because called with an explicit self argument + # NOTE: is a staticmethod because called with an explicit self argument # NodeItemCustom sets this as a variable attribute in __init__ @staticmethod def draw(self, layout, _context): diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index 57fcd6f9752..f9756811bde 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -139,6 +139,7 @@ url_manual_mapping = ( ("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.geometrynodecurveprimitivebeziersegment*", "modeling/geometry_nodes/curve_primitives/bezier_segment.html#bpy-types-geometrynodecurveprimitivebeziersegment"), ("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.materialgpencilstyle.use_stroke_holdout*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-stroke-holdout"), @@ -215,6 +216,7 @@ url_manual_mapping = ( ("bpy.types.spacesequenceeditor.proxy_render_size*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-proxy-render-size"), ("bpy.types.spacesequenceeditor.show_strip_offset*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-strip-offset"), ("bpy.types.spacesequenceeditor.show_strip_source*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-strip-source"), + ("bpy.types.spacespreadsheetrowfilter.column_name*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-column-name"), ("bpy.types.toolsettings.gpencil_stroke_placement*", "grease_pencil/modes/draw/stroke_placement.html#bpy-types-toolsettings-gpencil-stroke-placement"), ("bpy.types.toolsettings.use_keyframe_cycle_aware*", "editors/timeline.html#bpy-types-toolsettings-use-keyframe-cycle-aware"), ("bpy.types.toolsettings.use_keyframe_insert_auto*", "editors/timeline.html#bpy-types-toolsettings-use-keyframe-insert-auto"), @@ -233,6 +235,7 @@ url_manual_mapping = ( ("bpy.types.rendersettings.resolution_percentage*", "render/output/properties/dimensions.html#bpy-types-rendersettings-resolution-percentage"), ("bpy.types.rendersettings_simplify_gpencil_tint*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-tint"), ("bpy.types.spaceoutliner.use_filter_object_mesh*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-mesh"), + ("bpy.types.spaceoutliner.use_filter_view_layers*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-view-layers"), ("bpy.types.spacesequenceeditor.show_overexposed*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-show-overexposed"), ("bpy.types.toolsettings.use_gpencil_draw_onback*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-draw-onback"), ("bpy.types.toolsettings.use_snap_align_rotation*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-align-rotation"), @@ -273,6 +276,8 @@ url_manual_mapping = ( ("bpy.types.spacesequenceeditor.show_safe_areas*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-safe-areas"), ("bpy.types.spacesequenceeditor.show_strip_name*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-strip-name"), ("bpy.types.spacespreadsheet.show_only_selected*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-show-only-selected"), + ("bpy.types.spacespreadsheetrowfilter.operation*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-operation"), + ("bpy.types.spacespreadsheetrowfilter.threshold*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-threshold"), ("bpy.types.toolsettings.use_snap_grid_absolute*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-grid-absolute"), ("bpy.types.view3doverlay.show_face_orientation*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-show-face-orientation"), ("bpy.ops.object.blenderkit_material_thumbnail*", "addons/3d_view/blenderkit.html#bpy-ops-object-blenderkit-material-thumbnail"), @@ -343,6 +348,7 @@ url_manual_mapping = ( ("bpy.types.spaceoutliner.use_filter_complete*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-complete"), ("bpy.types.spacesequenceeditor.show_metadata*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-metadata"), ("bpy.types.spacespreadsheet.attribute_domain*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-attribute-domain"), + ("bpy.types.spacespreadsheetrowfilter.enabled*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-enabled"), ("bpy.types.spaceview3d.transform_orientation*", "editors/3dview/controls/orientation.html#bpy-types-spaceview3d-transform-orientation"), ("bpy.types.spaceview3d.use_local_collections*", "editors/3dview/sidebar.html#bpy-types-spaceview3d-use-local-collections"), ("bpy.types.toolsettings.use_snap_peel_object*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-peel-object"), @@ -350,7 +356,6 @@ url_manual_mapping = ( ("bpy.types.view3doverlay.wireframe_threshold*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-wireframe-threshold"), ("bpy.ops.object.constraint_add_with_targets*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-constraint-add-with-targets"), ("bpy.ops.object.material_slot_remove_unused*", "scene_layout/object/editing/cleanup.html#bpy-ops-object-material-slot-remove-unused"), - ("bpy.ops.object.vertex_group_copy_to_linked*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-copy-to-linked"), ("bpy.ops.outliner.collection_disable_render*", "editors/outliner/editing.html#bpy-ops-outliner-collection-disable-render"), ("bpy.types.brush.cloth_simulation_area_type*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth-simulation-area-type"), ("bpy.types.brushgpencilsettings.fill_factor*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-factor"), @@ -372,6 +377,7 @@ url_manual_mapping = ( ("bpy.types.fluidflowsettings.use_plane_init*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-plane-init"), ("bpy.types.fluidflowsettings.velocity_coord*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-velocity-coord"), ("bpy.types.fluidflowsettings.volume_density*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-volume-density"), + ("bpy.types.geometrynodecurvequadraticbezier*", "modeling/geometry_nodes/curve_primitives/quadratic_bezier.html#bpy-types-geometrynodecurvequadraticbezier"), ("bpy.types.materialgpencilstyle.show_stroke*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-show-stroke"), ("bpy.types.movietrackingcamera.focal_length*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-focal-length"), ("bpy.types.movietrackingcamera.pixel_aspect*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-pixel-aspect"), @@ -471,6 +477,7 @@ url_manual_mapping = ( ("bpy.types.geometrynodeattributecolorramp*", "modeling/geometry_nodes/attribute/attribute_color_ramp.html#bpy-types-geometrynodeattributecolorramp"), ("bpy.types.geometrynodeattributeproximity*", "modeling/geometry_nodes/attribute/attribute_proximity.html#bpy-types-geometrynodeattributeproximity"), ("bpy.types.geometrynodeattributerandomize*", "modeling/geometry_nodes/attribute/attribute_randomize.html#bpy-types-geometrynodeattributerandomize"), + ("bpy.types.geometrynodecurvequadrilateral*", "modeling/geometry_nodes/curve_primitives/quadrilateral.html#bpy-types-geometrynodecurvequadrilateral"), ("bpy.types.geometrynodeseparatecomponents*", "modeling/geometry_nodes/geometry/separate_components.html#bpy-types-geometrynodeseparatecomponents"), ("bpy.types.geometrynodesubdivisionsurface*", "modeling/geometry_nodes/mesh/subdivision_surface.html#bpy-types-geometrynodesubdivisionsurface"), ("bpy.types.imageformatsettings.color_mode*", "render/output/properties/output.html#bpy-types-imageformatsettings-color-mode"), @@ -490,7 +497,7 @@ url_manual_mapping = ( ("bpy.types.rendersettings.use_compositing*", "render/output/properties/post_processing.html#bpy-types-rendersettings-use-compositing"), ("bpy.types.rendersettings.use_placeholder*", "render/output/properties/output.html#bpy-types-rendersettings-use-placeholder"), ("bpy.types.shadernodesubsurfacescattering*", "render/shader_nodes/shader/sss.html#bpy-types-shadernodesubsurfacescattering"), - ("bpy.types.spaceclipeditor.lock_selection*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-lock-selection"), + ("bpy.types.spaceclipeditor.lock_selection*", "editors/clip/introduction.html#bpy-types-spaceclipeditor-lock-selection"), ("bpy.types.spacedopesheeteditor.auto_snap*", "editors/dope_sheet/editing.html#bpy-types-spacedopesheeteditor-auto-snap"), ("bpy.types.spaceoutliner.show_mode_column*", "editors/outliner/interface.html#bpy-types-spaceoutliner-show-mode-column"), ("bpy.types.spacetexteditor.use_match_case*", "editors/text_editor.html#bpy-types-spacetexteditor-use-match-case"), @@ -508,6 +515,7 @@ url_manual_mapping = ( ("bpy.ops.outliner.collection_show_inside*", "editors/outliner/editing.html#bpy-ops-outliner-collection-show-inside"), ("bpy.ops.preferences.reset_default_theme*", "editors/preferences/themes.html#bpy-ops-preferences-reset-default-theme"), ("bpy.ops.sequencer.strip_transform_clear*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-strip-transform-clear"), + ("bpy.ops.spreadsheet.add_row_filter_rule*", "editors/spreadsheet.html#bpy-ops-spreadsheet-add-row-filter-rule"), ("bpy.types.animdata.action_extrapolation*", "editors/nla/sidebar.html#bpy-types-animdata-action-extrapolation"), ("bpy.types.bakesettings.max_ray_distance*", "render/cycles/baking.html#bpy-types-bakesettings-max-ray-distance"), ("bpy.types.brush.multiplane_scrape_angle*", "sculpt_paint/sculpting/tools/multiplane_scrape.html#bpy-types-brush-multiplane-scrape-angle"), @@ -695,6 +703,7 @@ url_manual_mapping = ( ("bpy.types.fmodifierfunctiongenerator*", "editors/graph_editor/fcurves/sidebar/modifiers.html#bpy-types-fmodifierfunctiongenerator"), ("bpy.types.geometrynodeattributeclamp*", "modeling/geometry_nodes/attribute/attribute_clamp.html#bpy-types-geometrynodeattributeclamp"), ("bpy.types.geometrynodecollectioninfo*", "modeling/geometry_nodes/input/collection_info.html#bpy-types-geometrynodecollectioninfo"), + ("bpy.types.geometrynodecurveendpoints*", "modeling/geometry_nodes/curve/curve_endpoints.html#bpy-types-geometrynodecurveendpoints"), ("bpy.types.geometrynodecurvesubdivide*", "modeling/geometry_nodes/curve/curve_subdivide.html#bpy-types-geometrynodecurvesubdivide"), ("bpy.types.geometrynodedeletegeometry*", "modeling/geometry_nodes/geometry/delete_geometry.html#bpy-types-geometrynodedeletegeometry"), ("bpy.types.geometrynodematerialassign*", "modeling/geometry_nodes/material/assign.html#bpy-types-geometrynodematerialassign"), @@ -765,6 +774,7 @@ url_manual_mapping = ( ("bpy.types.geometrynodecurvetopoints*", "modeling/geometry_nodes/curve/curve_to_points.html#bpy-types-geometrynodecurvetopoints"), ("bpy.types.geometrynodeinputmaterial*", "modeling/geometry_nodes/input/material.html#bpy-types-geometrynodeinputmaterial"), ("bpy.types.geometrynodemeshicosphere*", "modeling/geometry_nodes/mesh_primitives/icosphere.html#bpy-types-geometrynodemeshicosphere"), + ("bpy.types.geometrynodemeshsubdivide*", "modeling/geometry_nodes/mesh/subdivide.html#bpy-types-geometrynodemeshsubdivide"), ("bpy.types.geometrynodepointinstance*", "modeling/geometry_nodes/point/point_instance.html#bpy-types-geometrynodepointinstance"), ("bpy.types.geometrynodepointseparate*", "modeling/geometry_nodes/point/point_separate.html#bpy-types-geometrynodepointseparate"), ("bpy.types.geometrynoderesamplecurve*", "modeling/geometry_nodes/curve/resample_curve.html#bpy-types-geometrynoderesamplecurve"), @@ -795,6 +805,7 @@ url_manual_mapping = ( ("bpy.types.spline.tilt_interpolation*", "modeling/curves/properties/active_spline.html#bpy-types-spline-tilt-interpolation"), ("bpy.types.transformorientation.name*", "editors/3dview/controls/orientation.html#bpy-types-transformorientation-name"), ("bpy.types.volumedisplay.slice_depth*", "modeling/volumes/properties.html#bpy-types-volumedisplay-slice-depth"), + ("bpy.ops.clip.lock_selection_toggle*", "editors/clip/introduction.html#bpy-ops-clip-lock-selection-toggle"), ("bpy.ops.mesh.customdata_mask_clear*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-mesh-customdata-mask-clear"), ("bpy.ops.mesh.extrude_vertices_move*", "modeling/meshes/editing/vertex/extrude_vertices.html#bpy-ops-mesh-extrude-vertices-move"), ("bpy.ops.mesh.mod_weighted_strength*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-mod-weighted-strength"), @@ -802,7 +813,6 @@ url_manual_mapping = ( ("bpy.ops.mesh.select_interior_faces*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-interior-faces"), ("bpy.ops.mesh.select_similar_region*", "modeling/meshes/selecting/similar.html#bpy-ops-mesh-select-similar-region"), ("bpy.ops.mesh.tris_convert_to_quads*", "modeling/meshes/editing/face/triangles_quads.html#bpy-ops-mesh-tris-convert-to-quads"), - ("bpy.ops.node.active_preview_toggle*", "modeling/geometry_nodes/introduction.html#bpy-ops-node-active-preview-toggle"), ("bpy.ops.object.datalayout_transfer*", "scene_layout/object/editing/link_transfer/transfer_mesh_data_layout.html#bpy-ops-object-datalayout-transfer"), ("bpy.ops.object.multires_base_apply*", "modeling/modifiers/generate/multiresolution.html#bpy-ops-object-multires-base-apply"), ("bpy.ops.object.randomize_transform*", "scene_layout/object/editing/transform/randomize.html#bpy-ops-object-randomize-transform"), @@ -872,6 +882,7 @@ url_manual_mapping = ( ("bpy.types.volumedisplay.slice_axis*", "modeling/volumes/properties.html#bpy-types-volumedisplay-slice-axis"), ("bpy.ops.anim.channels_clean_empty*", "editors/nla/editing.html#bpy-ops-anim-channels-clean-empty"), ("bpy.ops.armature.select_hierarchy*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-hierarchy"), + ("bpy.ops.armature.switch_direction*", "animation/armatures/bones/editing/switch_direction.html#bpy-ops-armature-switch-direction"), ("bpy.ops.clip.apply_solution_scale*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-apply-solution-scale"), ("bpy.ops.clip.set_center_principal*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-set-center-principal"), ("bpy.ops.clip.setup_tracking_scene*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-setup-tracking-scene"), @@ -925,7 +936,9 @@ url_manual_mapping = ( ("bpy.types.functionnodeinputstring*", "modeling/geometry_nodes/input/string.html#bpy-types-functionnodeinputstring"), ("bpy.types.functionnodeinputvector*", "modeling/geometry_nodes/input/vector.html#bpy-types-functionnodeinputvector"), ("bpy.types.functionnoderandomfloat*", "modeling/geometry_nodes/input/random_float.html#bpy-types-functionnoderandomfloat"), + ("bpy.types.geometrynodecurvecircle*", "modeling/geometry_nodes/curve_primitives/circle.html#bpy-types-geometrynodecurvecircle"), ("bpy.types.geometrynodecurvelength*", "modeling/geometry_nodes/curve/curve_length.html#bpy-types-geometrynodecurvelength"), + ("bpy.types.geometrynodecurvespiral*", "modeling/geometry_nodes/curve_primitives/spiral.html#bpy-types-geometrynodecurvespiral"), ("bpy.types.geometrynodecurvetomesh*", "modeling/geometry_nodes/curve/curve_to_mesh.html#bpy-types-geometrynodecurvetomesh"), ("bpy.types.geometrynodemeshtocurve*", "modeling/geometry_nodes/curve/mesh_to_curve.html#bpy-types-geometrynodemeshtocurve"), ("bpy.types.geometrynodepointrotate*", "modeling/geometry_nodes/point/point_rotate.html#bpy-types-geometrynodepointrotate"), @@ -989,6 +1002,7 @@ url_manual_mapping = ( ("bpy.ops.sequencer.select_handles*", "video_editing/sequencer/selecting.html#bpy-ops-sequencer-select-handles"), ("bpy.ops.uv.average_islands_scale*", "modeling/meshes/uv/editing.html#bpy-ops-uv-average-islands-scale"), ("bpy.ops.view3d.blenderkit_search*", "addons/3d_view/blenderkit.html#bpy-ops-view3d-blenderkit-search"), + ("bpy.types.armature.axes_position*", "animation/armatures/properties/display.html#bpy-types-armature-axes-position"), ("bpy.types.armature.pose_position*", "animation/armatures/properties/skeleton.html#bpy-types-armature-pose-position"), ("bpy.types.bakesettings.use_clear*", "render/cycles/baking.html#bpy-types-bakesettings-use-clear"), ("bpy.types.bone.envelope_distance*", "animation/armatures/bones/properties/deform.html#bpy-types-bone-envelope-distance"), @@ -1024,6 +1038,7 @@ url_manual_mapping = ( ("bpy.types.editbone.bbone_scalein*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-scalein"), ("bpy.types.editbone.inherit_scale*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-inherit-scale"), ("bpy.types.geometrynodeconvexhull*", "modeling/geometry_nodes/geometry/convex_hull.html#bpy-types-geometrynodeconvexhull"), + ("bpy.types.geometrynodefloattoint*", "modeling/geometry_nodes/utilities/float_to_int.html#bpy-types-geometrynodefloattoint"), ("bpy.types.geometrynodeisviewport*", "modeling/geometry_nodes/input/is_viewport.html#bpy-types-geometrynodeisviewport"), ("bpy.types.geometrynodemeshcircle*", "modeling/geometry_nodes/mesh_primitives/circle.html#bpy-types-geometrynodemeshcircle"), ("bpy.types.geometrynodeobjectinfo*", "modeling/geometry_nodes/input/object_info.html#bpy-types-geometrynodeobjectinfo"), @@ -1051,6 +1066,8 @@ url_manual_mapping = ( ("bpy.types.volumerender.step_size*", "modeling/volumes/properties.html#bpy-types-volumerender-step-size"), ("bpy.types.weightednormalmodifier*", "modeling/modifiers/modify/weighted_normal.html#bpy-types-weightednormalmodifier"), ("bpy.ops.armature.autoside_names*", "animation/armatures/bones/editing/naming.html#bpy-ops-armature-autoside-names"), + ("bpy.ops.armature.calculate_roll*", "animation/armatures/bones/editing/bone_roll.html#bpy-ops-armature-calculate-roll"), + ("bpy.ops.armature.duplicate_move*", "animation/armatures/bones/editing/duplicate.html#bpy-ops-armature-duplicate-move"), ("bpy.ops.armature.select_similar*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-similar"), ("bpy.ops.clip.create_plane_track*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-create-plane-track"), ("bpy.ops.curve.spline_weight_set*", "modeling/curves/editing/other.html#bpy-ops-curve-spline-weight-set"), @@ -1099,6 +1116,7 @@ url_manual_mapping = ( ("bpy.ops.wm.operator_cheat_sheet*", "advanced/operators.html#bpy-ops-wm-operator-cheat-sheet"), ("bpy.ops.wm.previews_batch_clear*", "files/blend/previews.html#bpy-ops-wm-previews-batch-clear"), ("bpy.ops.wm.recover_last_session*", "files/blend/open_save.html#bpy-ops-wm-recover-last-session"), + ("bpy.types.armature.display_type*", "animation/armatures/properties/display.html#bpy-types-armature-display-type"), ("bpy.types.armature.use_mirror_x*", "animation/armatures/bones/tools/tool_settings.html#bpy-types-armature-use-mirror-x"), ("bpy.types.bakesettings.normal_b*", "render/cycles/baking.html#bpy-types-bakesettings-normal-b"), ("bpy.types.bakesettings.normal_g*", "render/cycles/baking.html#bpy-types-bakesettings-normal-g"), @@ -1127,8 +1145,10 @@ url_manual_mapping = ( ("bpy.types.editbone.bbone_rollin*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-rollin"), ("bpy.types.fluideffectorsettings*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings"), ("bpy.types.followtrackconstraint*", "animation/constraints/motion_tracking/follow_track.html#bpy-types-followtrackconstraint"), + ("bpy.types.geometrynodecurveline*", "modeling/geometry_nodes/curve_primitives/line.html#bpy-types-geometrynodecurveline"), + ("bpy.types.geometrynodecurvestar*", "modeling/geometry_nodes/curve_primitives/star.html#bpy-types-geometrynodecurvestar"), + ("bpy.types.geometrynodecurvetrim*", "modeling/geometry_nodes/curve/curve_trim.html#bpy-types-geometrynodecurvetrim"), ("bpy.types.geometrynodeedgesplit*", "modeling/geometry_nodes/mesh/edge_split.html#bpy-types-geometrynodeedgesplit"), - ("bpy.types.geometrynodesubdivide*", "modeling/geometry_nodes/mesh/subdivide.html#bpy-types-geometrynodesubdivide"), ("bpy.types.geometrynodetransform*", "modeling/geometry_nodes/geometry/transform.html#bpy-types-geometrynodetransform"), ("bpy.types.gpencilsculptsettings*", "grease_pencil/properties/index.html#bpy-types-gpencilsculptsettings"), ("bpy.types.light.cutoff_distance*", "render/eevee/lighting.html#bpy-types-light-cutoff-distance"), @@ -1247,6 +1267,8 @@ url_manual_mapping = ( ("bpy.types.volumetomeshmodifier*", "modeling/modifiers/generate/volume_to_mesh.html#bpy-types-volumetomeshmodifier"), ("bpy.types.whitebalancemodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-whitebalancemodifier"), ("bpy.ops.anim.channels_ungroup*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-ungroup"), + ("bpy.ops.armature.extrude_move*", "animation/armatures/bones/editing/extrude.html#bpy-ops-armature-extrude-move"), + ("bpy.ops.armature.parent_clear*", "animation/armatures/bones/editing/parenting.html#bpy-ops-armature-parent-clear"), ("bpy.ops.clip.clear_track_path*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-clear-track-path"), ("bpy.ops.clip.set_scene_frames*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-set-scene-frames"), ("bpy.ops.curve.handle_type_set*", "modeling/curves/editing/control_points.html#bpy-ops-curve-handle-type-set"), @@ -1424,6 +1446,7 @@ url_manual_mapping = ( ("bpy.types.freestylelinestyle*", "render/freestyle/parameter_editor/line_style/index.html#bpy-types-freestylelinestyle"), ("bpy.types.gammacrosssequence*", "video_editing/sequencer/strips/transitions/gamma_cross.html#bpy-types-gammacrosssequence"), ("bpy.types.geometrynodeswitch*", "modeling/geometry_nodes/utilities/switch.html#bpy-types-geometrynodeswitch"), + ("bpy.types.geometrynodeviewer*", "modeling/geometry_nodes/output/viewer.html#bpy-types-geometrynodeviewer"), ("bpy.types.gpencilsculptguide*", "grease_pencil/modes/draw/guides.html#bpy-types-gpencilsculptguide"), ("bpy.types.huecorrectmodifier*", "video_editing/sequencer/sidebar/modifiers.html#bpy-types-huecorrectmodifier"), ("bpy.types.imagepaint.stencil*", "sculpt_paint/texture_paint/tool_settings/mask.html#bpy-types-imagepaint-stencil"), @@ -1462,7 +1485,9 @@ url_manual_mapping = ( ("bpy.ops.anim.channels_group*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-group"), ("bpy.ops.anim.keyframe_clear*", "animation/keyframes/editing.html#bpy-ops-anim-keyframe-clear"), ("bpy.ops.armature.flip_names*", "animation/armatures/bones/editing/naming.html#bpy-ops-armature-flip-names"), + ("bpy.ops.armature.parent_set*", "animation/armatures/bones/editing/parenting.html#bpy-ops-armature-parent-set"), ("bpy.ops.armature.select_all*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-all"), + ("bpy.ops.armature.symmetrize*", "animation/armatures/bones/editing/symmetrize.html#bpy-ops-armature-symmetrize"), ("bpy.ops.clip.average_tracks*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-average-tracks"), ("bpy.ops.clip.refine_markers*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-refine-markers"), ("bpy.ops.clip.select_grouped*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-select-grouped"), @@ -1501,6 +1526,8 @@ url_manual_mapping = ( ("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.pose.armature_apply*", "animation/armatures/posing/editing/apply.html#bpy-ops-pose-armature-apply"), + ("bpy.ops.pose.group_deselect*", "animation/armatures/properties/bone_groups.html#bpy-ops-pose-group-deselect"), + ("bpy.ops.pose.group_unassign*", "animation/armatures/properties/bone_groups.html#bpy-ops-pose-group-unassign"), ("bpy.ops.pose.select_grouped*", "animation/armatures/posing/selecting.html#bpy-ops-pose-select-grouped"), ("bpy.ops.poselib.pose_remove*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-pose-remove"), ("bpy.ops.poselib.pose_rename*", "animation/armatures/posing/editing/pose_library.html#bpy-ops-poselib-pose-rename"), @@ -1574,6 +1601,7 @@ url_manual_mapping = ( ("bpy.types.wireframemodifier*", "modeling/modifiers/generate/wireframe.html#bpy-types-wireframemodifier"), ("bpy.types.worldmistsettings*", "render/cycles/world_settings.html#bpy-types-worldmistsettings"), ("bpy.ops.anim.channels_move*", "editors/graph_editor/channels.html#bpy-ops-anim-channels-move"), + ("bpy.ops.armature.subdivide*", "animation/armatures/bones/editing/subdivide.html#bpy-ops-armature-subdivide"), ("bpy.ops.buttons.toggle_pin*", "editors/properties_editor.html#bpy-ops-buttons-toggle-pin"), ("bpy.ops.clip.filter_tracks*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-filter-tracks"), ("bpy.ops.clip.select_circle*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-select-circle"), @@ -1675,6 +1703,8 @@ url_manual_mapping = ( ("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/introduction.html#bpy-types-viewlayer-use-ao"), + ("bpy.ops.armature.dissolve*", "animation/armatures/bones/editing/delete.html#bpy-ops-armature-dissolve"), + ("bpy.ops.armature.separate*", "animation/armatures/bones/editing/separate_bones.html#bpy-ops-armature-separate"), ("bpy.ops.clip.clean_tracks*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-clean-tracks"), ("bpy.ops.clip.delete_track*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-delete-track"), ("bpy.ops.clip.solve_camera*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-solve-camera"), @@ -1703,6 +1733,8 @@ url_manual_mapping = ( ("bpy.ops.object.proxy_make*", "files/linked_libraries/library_proxies.html#bpy-ops-object-proxy-make"), ("bpy.ops.object.select_all*", "scene_layout/object/selecting.html#bpy-ops-object-select-all"), ("bpy.ops.object.shade_flat*", "scene_layout/object/editing/shading.html#bpy-ops-object-shade-flat"), + ("bpy.ops.pose.group_assign*", "animation/armatures/properties/bone_groups.html#bpy-ops-pose-group-assign"), + ("bpy.ops.pose.group_select*", "animation/armatures/properties/bone_groups.html#bpy-ops-pose-group-select"), ("bpy.ops.poselib.pose_move*", "animation/armatures/properties/pose_library.html#bpy-ops-poselib-pose-move"), ("bpy.ops.preferences.addon*", "editors/preferences/addons.html#bpy-ops-preferences-addon"), ("bpy.ops.scene.light_cache*", "render/eevee/render_settings/indirect_lighting.html#bpy-ops-scene-light-cache"), @@ -1872,6 +1904,7 @@ url_manual_mapping = ( ("bpy.ops.wm.owner_enable*", "interface/window_system/workspaces.html#bpy-ops-wm-owner-enable"), ("bpy.ops.wm.redraw_timer*", "advanced/operators.html#bpy-ops-wm-redraw-timer"), ("bpy.types.*light.shadow*", "render/eevee/lighting.html#bpy-types-light-shadow"), + ("bpy.types.armature.show*", "animation/armatures/properties/display.html#bpy-types-armature-show"), ("bpy.types.armaturebones*", "animation/armatures/bones/index.html#bpy-types-armaturebones"), ("bpy.types.arraymodifier*", "modeling/modifiers/generate/array.html#bpy-types-arraymodifier"), ("bpy.types.bevelmodifier*", "modeling/modifiers/generate/bevel.html#bpy-types-bevelmodifier"), @@ -1919,6 +1952,8 @@ url_manual_mapping = ( ("bpy.types.volumedisplay*", "modeling/volumes/properties.html#bpy-types-volumedisplay"), ("bpy.types.windowmanager*", "interface/index.html#bpy-types-windowmanager"), ("bpy.ops.*.select_lasso*", "interface/selecting.html#bpy-ops-select-lasso"), + ("bpy.ops.armature.align*", "animation/armatures/bones/editing/transform.html#bpy-ops-armature-align"), + ("bpy.ops.armature.split*", "animation/armatures/bones/editing/split.html#bpy-ops-armature-split"), ("bpy.ops.clip.set_plane*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-set-plane"), ("bpy.ops.clip.set_scale*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-set-scale"), ("bpy.ops.curve.decimate*", "modeling/curves/editing/curve.html#bpy-ops-curve-decimate"), @@ -1993,6 +2028,7 @@ url_manual_mapping = ( ("bpy.types.wavemodifier*", "modeling/modifiers/deform/wave.html#bpy-types-wavemodifier"), ("bpy.types.weldmodifier*", "modeling/modifiers/generate/weld.html#bpy-types-weldmodifier"), ("bpy.types.wipesequence*", "video_editing/sequencer/strips/transitions/wipe.html#bpy-types-wipesequence"), + ("bpy.ops.armature.fill*", "animation/armatures/bones/editing/fill_between_joints.html#bpy-ops-armature-fill"), ("bpy.ops.clip.prefetch*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-prefetch"), ("bpy.ops.clip.set_axis*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-set-axis"), ("bpy.ops.file.pack_all*", "files/blend/packed_data.html#bpy-ops-file-pack-all"), @@ -2064,6 +2100,7 @@ url_manual_mapping = ( ("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"), + ("bpy.types.bonegroups*", "animation/armatures/properties/bone_groups.html#bpy-types-bonegroups"), ("bpy.types.bpy_struct*", "files/data_blocks.html#bpy-types-bpy-struct"), ("bpy.types.collection*", "scene_layout/collections/collections.html#bpy-types-collection"), ("bpy.types.compositor*", "compositing/index.html#bpy-types-compositor"), diff --git a/release/scripts/startup/bl_operators/assets.py b/release/scripts/startup/bl_operators/assets.py index d2655784afd..c782cd0646e 100644 --- a/release/scripts/startup/bl_operators/assets.py +++ b/release/scripts/startup/bl_operators/assets.py @@ -18,7 +18,6 @@ # <pep8 compliant> from __future__ import annotations -from pathlib import Path import bpy from bpy.types import Operator diff --git a/release/scripts/startup/bl_operators/geometry_nodes.py b/release/scripts/startup/bl_operators/geometry_nodes.py index 71ef89a066b..ec2887a1a74 100644 --- a/release/scripts/startup/bl_operators/geometry_nodes.py +++ b/release/scripts/startup/bl_operators/geometry_nodes.py @@ -81,7 +81,10 @@ class NewGeometryNodeTreeAssign(Operator): return geometry_modifier_poll(context) def execute(self, context): - modifier = context.object.modifiers.active + if context.area.type == 'PROPERTIES': + modifier = context.modifier + else: + modifier = context.object.modifiers.active if not modifier: return {'CANCELLED'} diff --git a/release/scripts/startup/bl_operators/rigidbody.py b/release/scripts/startup/bl_operators/rigidbody.py index bc80500c888..7f5edac4dfb 100644 --- a/release/scripts/startup/bl_operators/rigidbody.py +++ b/release/scripts/startup/bl_operators/rigidbody.py @@ -60,17 +60,22 @@ class CopyRigidbodySettings(Operator): def execute(self, context): obj_act = context.object - view_layer = context.view_layer - # deselect all but mesh objects + # Deselect all non mesh objects and objects that + # already have a rigid body attached. + rb_objects = [] for o in context.selected_objects: - if o.type != 'MESH': + if o.type != 'MESH' or o.rigid_body is not None: o.select_set(False) - elif o.rigid_body is None: - # Add rigidbody to object! - view_layer.objects.active = o - bpy.ops.rigidbody.object_add() - view_layer.objects.active = obj_act + if o.rigid_body is not None: + rb_objects.append(o) + + bpy.ops.rigidbody.objects_add() + + # Ensure that the rigid body objects + # we've de-selected are selected again. + for o in rb_objects: + o.select_set(True) objects = context.selected_objects if objects: diff --git a/release/scripts/startup/bl_ui/properties_collection.py b/release/scripts/startup/bl_ui/properties_collection.py index b51d7157c06..3f7c0735eec 100644 --- a/release/scripts/startup/bl_ui/properties_collection.py +++ b/release/scripts/startup/bl_ui/properties_collection.py @@ -86,12 +86,15 @@ class COLLECTION_PT_lineart_collection(CollectionButtonsPanel, Panel): row = layout.row() row.prop(collection, "lineart_usage") - layout.prop(collection, "lineart_use_intersection_mask") + layout.prop(collection, "lineart_use_intersection_mask", text="Collection Mask") - row = layout.row(align=True, heading="Masks") - row.active = collection.lineart_use_intersection_mask + col = layout.column(align=True) + col.active = collection.lineart_use_intersection_mask + row = col.row(align=True, heading="Masks") for i in range(8): - row.prop(collection, "lineart_intersection_mask", index=i, text=str(i), toggle=True) + row.prop(collection, "lineart_intersection_mask", index=i, text=" ", toggle=True) + if i == 3: + row = col.row(align=True) classes = ( diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py index b217e33de12..1c7f3639f0a 100644 --- a/release/scripts/startup/bl_ui/properties_material.py +++ b/release/scripts/startup/bl_ui/properties_material.py @@ -291,18 +291,18 @@ class MATERIAL_PT_lineart(MaterialButtonsPanel, Panel): mat = context.material lineart = mat.lineart - layout.prop(lineart, "use_material_mask") + layout.prop(lineart, "use_material_mask", text="Material Mask") - row = layout.row(align=True, heading="Masks") - row.active = lineart.use_material_mask + col = layout.column(align=True) + col.active = lineart.use_material_mask + row = col.row(align=True, heading="Masks") for i in range(8): - row.prop(lineart, "use_material_mask_bits", text=str(i), index=i, toggle=True) + row.prop(lineart, "use_material_mask_bits", text=" ", index=i, toggle=True) + if i == 3: + row = col.row(align=True) row = layout.row(align=True, heading="Custom Occlusion") - row.prop(lineart, "use_mat_occlusion", text="") - sub = row.row(align=False) - sub.active = lineart.use_mat_occlusion - sub.prop(lineart, "mat_occlusion", slider=True, text="Levels") + row.prop(lineart, "mat_occlusion", text="Levels") classes = ( diff --git a/release/scripts/startup/bl_ui/properties_physics_fluid.py b/release/scripts/startup/bl_ui/properties_physics_fluid.py index d168d9ab6dd..6408d4096fe 100644 --- a/release/scripts/startup/bl_ui/properties_physics_fluid.py +++ b/release/scripts/startup/bl_ui/properties_physics_fluid.py @@ -734,11 +734,9 @@ class PHYSICS_PT_noise(PhysicButtonsPanel, Panel): col = flow.column() col.prop(domain, "noise_scale", text="Upres Factor") - # TODO (sebbas): Mantaflow only supports wavelet noise. Maybe get rid of noise type field. - col.prop(domain, "noise_type", text="Noise Method") + col.prop(domain, "noise_strength", text="Strength") col = flow.column() - col.prop(domain, "noise_strength", text="Strength") col.prop(domain, "noise_pos_scale", text="Scale") col.prop(domain, "noise_time_anim", text="Time") diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 55714e0b0a5..30467521c3d 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -263,6 +263,7 @@ class SEQUENCER_PT_sequencer_overlay(Panel): layout.prop(st, "show_strip_offset", text="Offsets") layout.prop(st, "show_fcurves", text="F-Curves") + layout.prop(st, "show_grid", text="Grid") layout.separator() @@ -1156,14 +1157,19 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel): flow.prop(strip, "use_only_boost") elif strip_type == 'SPEED': - layout.prop(strip, "use_default_fade", text="Stretch to Input Strip Length") - if not strip.use_default_fade: - layout.prop(strip, "use_as_speed") - if strip.use_as_speed: - layout.prop(strip, "speed_factor") - else: - layout.prop(strip, "speed_factor", text="Frame Number") - layout.prop(strip, "use_scale_to_length") + col = layout.column(align=True) + col.prop(strip, "speed_control", text="Speed Control") + if strip.speed_control == "MULTIPLY": + col.prop(strip, "speed_factor", text=" ") + elif strip.speed_control == "LENGTH": + col.prop(strip, "speed_length", text=" ") + elif strip.speed_control == "FRAME_NUMBER": + col.prop(strip, "speed_frame_number", text=" ") + + row = layout.row(align=True) + row.enabled = strip.speed_control != "STRETCH" + row = layout.row(align=True, heading="Interpolation") + row.prop(strip, "use_frame_interpolate", text="") elif strip_type == 'TRANSFORM': col = layout.column() @@ -1233,11 +1239,7 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel): layout.prop(strip, "wrap_width", text="Wrap Width") col = layout.column(align=True) - if strip_type == 'SPEED': - col.prop(strip, "multiply_speed") - col.prop(strip, "use_frame_interpolate") - - elif strip_type in {'CROSS', 'GAMMA_CROSS', 'WIPE', 'ALPHA_OVER', 'ALPHA_UNDER', 'OVER_DROP'}: + if strip_type in {'CROSS', 'GAMMA_CROSS', 'WIPE', 'ALPHA_OVER', 'ALPHA_UNDER', 'OVER_DROP'}: col.prop(strip, "use_default_fade", text="Default Fade") if not strip.use_default_fade: col.prop(strip, "effect_fader", text="Effect Fader") @@ -2293,8 +2295,8 @@ class SEQUENCER_PT_snapping(Panel): col.prop(sequencer_tool_settings, "snap_ignore_muted", text="Muted Strips") col.prop(sequencer_tool_settings, "snap_ignore_sound", text="Sound Strips") - col = layout.column() - col.prop(sequencer_tool_settings, "use_snap_current_frame_to_strips") + col = layout.column(heading="Current Frame", align=True) + col.prop(sequencer_tool_settings, "use_snap_current_frame_to_strips", text="Snap to Strips") classes = ( diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index a9640e8e5c0..bb23983453a 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -191,6 +191,11 @@ class _defs_annotate: row.prop(tool_settings.gpencil_sculpt, "lockaxis") elif tool_settings.gpencil_stroke_placement_view3d in {'SURFACE', 'STROKE'}: row.prop(tool_settings, "use_gpencil_stroke_endpoints") + elif space_type in {'IMAGE_EDITOR', 'NODE_EDITOR', 'SEQUENCE_EDITOR', 'CLIP_EDITOR'}: + layout.separator() + + row = layout.row(align=True) + row.prop(tool_settings, "annotation_stroke_placement_view2d", text="Placement") if tool.idname == "builtin.annotate_line": layout.separator() diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 1d616947db6..09820291222 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -507,8 +507,10 @@ geometry_node_categories = [ NodeItem("GeometryNodeMeshToCurve"), NodeItem("GeometryNodeCurveToPoints"), NodeItem("GeometryNodeCurveEndpoints"), + NodeItem("GeometryNodeCurveTrim"), NodeItem("GeometryNodeCurveLength"), NodeItem("GeometryNodeCurveReverse"), + NodeItem("GeometryNodeCurveSetHandles"), ]), GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[ NodeItem("GeometryNodeCurvePrimitiveLine"), diff --git a/release/scripts/templates_py/custom_nodes.py b/release/scripts/templates_py/custom_nodes.py index ca9534e7cd3..fba94e23d0d 100644 --- a/release/scripts/templates_py/custom_nodes.py +++ b/release/scripts/templates_py/custom_nodes.py @@ -147,7 +147,7 @@ node_categories = [ MyNodeCategory('OTHERNODES', "Other Nodes", items=[ # the node item can have additional settings, # which are applied to new nodes - # NB: settings values are stored as string expressions, + # NOTE: settings values are stored as string expressions, # for this reason they should be converted to strings using repr() NodeItem("CustomNodeType", label="Node A", settings={ "my_string_prop": repr("Lorem ipsum dolor sit amet"), diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index 1f39257a4c2..7e92f79a523 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -53,6 +53,8 @@ int BLF_load_mem_unique(const char *name, const unsigned char *mem, int mem_size void BLF_unload(const char *name) ATTR_NONNULL(); void BLF_unload_id(int fontid); +char *BLF_display_name_from_file(const char *filename); + /* Check if font supports a particular glyph. */ bool BLF_has_glyph(int fontid, unsigned int unicode); diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index 7428798581d..9168e7aa19c 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -915,6 +915,17 @@ void BLF_draw_buffer(int fontid, const char *str, size_t len) BLF_draw_buffer_ex(fontid, str, len, NULL); } +char *BLF_display_name_from_file(const char *filename) +{ + FontBLF *font = blf_font_new("font_name", filename); + if (!font) { + return NULL; + } + char *name = blf_display_name(font); + blf_font_free(font); + return name; +} + #ifdef DEBUG void BLF_state_print(int fontid) { diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 2c7ffbe8e42..53c4135254a 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -66,7 +66,7 @@ /* Batching buffer for drawing. */ BatchBLF g_batch; -/* freetype2 handle ONLY for this file!. */ +/* freetype2 handle ONLY for this file! */ static FT_Library ft_lib; static SpinLock ft_lib_mutex; static SpinLock blf_glyph_cache_mutex; @@ -1465,3 +1465,11 @@ float blf_font_ascender(FontBLF *font) blf_glyph_cache_release(font); return ascender; } + +char *blf_display_name(FontBLF *font) +{ + if (!font->face->family_name) { + return NULL; + } + return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name); +} diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 63e1eb999cd..35a6d019eac 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -99,6 +99,8 @@ int blf_font_width_max(struct FontBLF *font); float blf_font_descender(struct FontBLF *font); float blf_font_ascender(struct FontBLF *font); +char *blf_display_name(struct FontBLF *font); + void blf_font_boundbox_foreach_glyph(struct FontBLF *font, const char *str, size_t len, diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index 904b7bb8718..e3954e134da 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -158,7 +158,7 @@ struct DerivedMesh { int (*getNumPolys)(DerivedMesh *dm); /** Copy a single vert/edge/tessellated face from the derived mesh into - * ``*r_{vert/edge/face}``. note that the current implementation + * `*r_{vert/edge/face}`. note that the current implementation * of this function can be quite slow, iterating over all * elements (editmesh) */ diff --git a/source/blender/blenkernel/BKE_action.hh b/source/blender/blenkernel/BKE_action.hh new file mode 100644 index 00000000000..b9f106d367e --- /dev/null +++ b/source/blender/blenkernel/BKE_action.hh @@ -0,0 +1,35 @@ +/* + * 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. + */ +#pragma once + +/** \file + * \ingroup bke + */ +#ifndef __cplusplus +# error This is a C++ only header. +#endif + +#include "BLI_function_ref.hh" + +struct bAction; +struct FCurve; + +namespace blender::bke { + +using FoundFCurveCallback = blender::FunctionRef<void(FCurve *fcurve, const char *bone_name)>; +void BKE_action_find_fcurves_with_bones(const bAction *action, FoundFCurveCallback callback); + +}; // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_armature.hh b/source/blender/blenkernel/BKE_armature.hh new file mode 100644 index 00000000000..e3f5b528156 --- /dev/null +++ b/source/blender/blenkernel/BKE_armature.hh @@ -0,0 +1,48 @@ +/* + * 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. + */ +#pragma once + +/** \file + * \ingroup bke + */ +#ifndef __cplusplus +# error This is a C++ only header. +#endif + +#include "BKE_armature.h" + +#include "BLI_function_ref.hh" +#include "BLI_set.hh" + +namespace blender::bke { + +struct SelectedBonesResult { + bool all_bones_selected = true; + bool no_bones_selected = true; +}; + +using SelectedBoneCallback = blender::FunctionRef<void(Bone *bone)>; +SelectedBonesResult BKE_armature_find_selected_bones(const bArmature *armature, + SelectedBoneCallback callback); + +using BoneNameSet = blender::Set<std::string>; +/** + * Return a set of names of the selected bones. An empty set means "ignore bone + * selection", which either means all bones are selected, or none are. + */ +BoneNameSet BKE_armature_find_selected_bone_names(const bArmature *armature); + +}; // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 28903c4787c..e76e3ed8fe0 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 12 +#define BLENDER_FILE_SUBVERSION 15 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_cloth.h b/source/blender/blenkernel/BKE_cloth.h index a0e3d5dc142..dbf285feb92 100644 --- a/source/blender/blenkernel/BKE_cloth.h +++ b/source/blender/blenkernel/BKE_cloth.h @@ -39,14 +39,14 @@ struct Scene; #define DO_INLINE MALWAYS_INLINE -/* goal defines */ +/* Goal defines. */ #define SOFTGOALSNAP 0.999f /* This is approximately the smallest number that can be * represented by a float, given its precision. */ #define ALMOST_ZERO FLT_EPSILON -/* Bits to or into the ClothVertex.flags. */ +/* Bits to or into the #ClothVertex.flags. */ typedef enum eClothVertexFlag { CLOTH_VERT_FLAG_PINNED = (1 << 0), CLOTH_VERT_FLAG_NOSELFCOLL = (1 << 1), /* vertex NOT used for self collisions */ @@ -150,7 +150,7 @@ typedef struct ClothSpring { float target[3]; } ClothSpring; -// some macro enhancements for vector treatment +/* Some macro enhancements for vector treatment. */ #define VECSUBADDSS(v1, v2, aS, v3, bS) \ { \ *(v1) -= *(v2)*aS + *(v3)*bS; \ @@ -211,9 +211,8 @@ typedef enum { CLOTH_SPRING_FLAG_NEEDED = (1 << 2), /* Springs has values to be applied. */ } CLOTH_SPRINGS_FLAGS; -///////////////////////////////////////////////// -// collision.c -//////////////////////////////////////////////// +/* -------------------------------------------------------------------- */ +/* collision.c */ struct CollPair; @@ -225,20 +224,17 @@ typedef struct ColliderContacts { int totcollisions; } ColliderContacts; -// needed for implicit.c +/* needed for implicit.c */ int cloth_bvh_collision(struct Depsgraph *depsgraph, struct Object *ob, struct ClothModifierData *clmd, float step, float dt); -//////////////////////////////////////////////// +/* -------------------------------------------------------------------- */ +/* cloth.c */ -///////////////////////////////////////////////// -// cloth.c -//////////////////////////////////////////////// - -// needed for modifier.c +/* Needed for modifier.c */ void cloth_free_modifier_extern(struct ClothModifierData *clmd); void cloth_free_modifier(struct ClothModifierData *clmd); void clothModifier_do(struct ClothModifierData *clmd, @@ -250,18 +246,16 @@ void clothModifier_do(struct ClothModifierData *clmd, int cloth_uses_vgroup(struct ClothModifierData *clmd); -// needed for collision.c +/* Needed for collision.c */ void bvhtree_update_from_cloth(struct ClothModifierData *clmd, bool moving, bool self); -// needed for button_object.c +/* Needed for button_object.c */ void cloth_clear_cache(struct Object *ob, struct ClothModifierData *clmd, float framenr); void cloth_parallel_transport_hair_frame(float mat[3][3], const float dir_old[3], const float dir_new[3]); -//////////////////////////////////////////////// - #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h index c7c5f59cab2..5e474c0c5ac 100644 --- a/source/blender/blenkernel/BKE_curve.h +++ b/source/blender/blenkernel/BKE_curve.h @@ -97,7 +97,6 @@ struct BoundBox *BKE_curve_boundbox_get(struct Object *ob); void BKE_curve_texspace_calc(struct Curve *cu); void BKE_curve_texspace_ensure(struct Curve *cu); -void BKE_curve_texspace_get(struct Curve *cu, float r_loc[3], float r_size[3]); bool BKE_curve_minmax(struct Curve *cu, bool use_radius, float min[3], float max[3]); bool BKE_curve_center_median(struct Curve *cu, float cent[3]); diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 6db68de8103..d36991c4444 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -517,7 +517,7 @@ enum { CD_FAKE = 1 << 8, /* Vertices. */ - CD_FAKE_MDEFORMVERT = CD_FAKE | CD_MDEFORMVERT, /* *sigh* due to how vgroups are stored :( . */ + CD_FAKE_MDEFORMVERT = CD_FAKE | CD_MDEFORMVERT, /* *sigh* due to how vgroups are stored :(. */ CD_FAKE_SHAPEKEY = CD_FAKE | CD_SHAPEKEY, /* Not available as real CD layer in non-bmesh context. */ diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h index 404f344919c..c8af1a91725 100644 --- a/source/blender/blenkernel/BKE_layer.h +++ b/source/blender/blenkernel/BKE_layer.h @@ -98,6 +98,9 @@ struct LayerCollection *BKE_layer_collection_from_index(struct ViewLayer *view_l const int index); int BKE_layer_collection_findindex(struct ViewLayer *view_layer, const struct LayerCollection *lc); +void BKE_layer_collection_resync_forbid(void); +void BKE_layer_collection_resync_allow(void); + void BKE_main_collection_sync(const struct Main *bmain); void BKE_scene_collection_sync(const struct Scene *scene); void BKE_layer_collection_sync(const struct Scene *scene, struct ViewLayer *view_layer); diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index ed930fe539d..ae60a5563b5 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -48,6 +48,7 @@ struct BLI_mempool; struct BlendThumbnail; struct GHash; struct GSet; +struct IDNameLib_Map; struct ImBuf; struct Library; struct MainLock; @@ -191,6 +192,9 @@ typedef struct Main { */ struct MainIDRelations *relations; + /* IDMap of IDs. Currently used when reading (expanding) libraries. */ + struct IDNameLib_Map *id_map; + struct MainLock *lock; } Main; diff --git a/source/blender/blenkernel/BKE_main_idmap.h b/source/blender/blenkernel/BKE_main_idmap.h index bffb12a5136..ff69883f0fb 100644 --- a/source/blender/blenkernel/BKE_main_idmap.h +++ b/source/blender/blenkernel/BKE_main_idmap.h @@ -50,8 +50,13 @@ struct IDNameLib_Map *BKE_main_idmap_create(struct Main *bmain, const int idmap_types) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); void BKE_main_idmap_destroy(struct IDNameLib_Map *id_map) ATTR_NONNULL(); + +void BKE_main_idmap_insert_id(struct IDNameLib_Map *id_map, struct ID *id) ATTR_NONNULL(); +void BKE_main_idmap_remove_id(struct IDNameLib_Map *id_map, struct ID *id) ATTR_NONNULL(); + struct Main *BKE_main_idmap_main_get(struct IDNameLib_Map *id_map) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); + struct ID *BKE_main_idmap_lookup_name(struct IDNameLib_Map *id_map, short id_type, const char *name, diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 821bf1c4fea..5034e426ad9 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -127,8 +127,8 @@ void BKE_mesh_eval_delete(struct Mesh *mesh_eval); struct Mesh *BKE_mesh_copy_for_eval(struct Mesh *source, bool reference); /* These functions construct a new Mesh, - * contrary to BKE_mesh_from_nurbs which modifies ob itself. */ -struct Mesh *BKE_mesh_new_nomain_from_curve(struct Object *ob); + * contrary to BKE_mesh_to_curve_nurblist which modifies ob itself. */ +struct Mesh *BKE_mesh_new_nomain_from_curve(const struct Object *ob); struct Mesh *BKE_mesh_new_nomain_from_curve_displist(const struct Object *ob, const struct ListBase *dispbase); @@ -144,32 +144,11 @@ int BKE_mesh_mface_index_validate(struct MFace *mface, struct Mesh *BKE_mesh_from_object(struct Object *ob); void BKE_mesh_assign_object(struct Main *bmain, struct Object *ob, struct Mesh *me); void BKE_mesh_from_metaball(struct ListBase *lb, struct Mesh *me); -int BKE_mesh_nurbs_to_mdata(struct Object *ob, - struct MVert **r_allvert, - int *r_totvert, - struct MEdge **r_alledge, - int *r_totedge, - struct MLoop **r_allloop, - struct MPoly **r_allpoly, - int *r_totloop, - int *r_totpoly); -int BKE_mesh_nurbs_displist_to_mdata(const struct Object *ob, - const struct ListBase *dispbase, - struct MVert **r_allvert, - int *r_totvert, - struct MEdge **r_alledge, - int *r_totedge, - struct MLoop **r_allloop, - struct MPoly **r_allpoly, - struct MLoopUV **r_alluv, - int *r_totloop, - int *r_totpoly); void BKE_mesh_from_nurbs_displist(struct Main *bmain, struct Object *ob, struct ListBase *dispbase, const char *obdata_name, bool temporary); -void BKE_mesh_from_nurbs(struct Main *bmain, struct Object *ob); void BKE_mesh_to_curve_nurblist(const struct Mesh *me, struct ListBase *nurblist, const int edge_users_test); @@ -421,6 +400,12 @@ void BKE_lnor_spacearr_init(MLoopNorSpaceArray *lnors_spacearr, const char data_type); void BKE_lnor_spacearr_clear(MLoopNorSpaceArray *lnors_spacearr); void BKE_lnor_spacearr_free(MLoopNorSpaceArray *lnors_spacearr); + +void BKE_lnor_spacearr_tls_init(MLoopNorSpaceArray *lnors_spacearr, + MLoopNorSpaceArray *lnors_spacearr_tls); +void BKE_lnor_spacearr_tls_join(MLoopNorSpaceArray *lnors_spacearr, + MLoopNorSpaceArray *lnors_spacearr_tls); + MLoopNorSpace *BKE_lnor_space_create(MLoopNorSpaceArray *lnors_spacearr); void BKE_lnor_space_define(MLoopNorSpace *lnor_space, const float lnor[3], diff --git a/source/blender/blenkernel/BKE_mesh_iterators.h b/source/blender/blenkernel/BKE_mesh_iterators.h index 103e7b5b78f..a65f25ee182 100644 --- a/source/blender/blenkernel/BKE_mesh_iterators.h +++ b/source/blender/blenkernel/BKE_mesh_iterators.h @@ -41,6 +41,7 @@ void BKE_mesh_foreach_mapped_vert(struct Mesh *mesh, MeshForeachFlag flag); void BKE_mesh_foreach_mapped_edge( struct Mesh *mesh, + int tot_edges, void (*func)(void *userData, int index, const float v0co[3], const float v1co[3]), void *userData); void BKE_mesh_foreach_mapped_loop(struct Mesh *mesh, diff --git a/source/blender/blenkernel/BKE_mesh_types.h b/source/blender/blenkernel/BKE_mesh_types.h index b223d3872ff..aed8c44a031 100644 --- a/source/blender/blenkernel/BKE_mesh_types.h +++ b/source/blender/blenkernel/BKE_mesh_types.h @@ -27,7 +27,6 @@ typedef enum eMeshBatchDirtyMode { BKE_MESH_BATCH_DIRTY_SELECT, BKE_MESH_BATCH_DIRTY_SELECT_PAINT, BKE_MESH_BATCH_DIRTY_SHADING, - BKE_MESH_BATCH_DIRTY_DEFORM, BKE_MESH_BATCH_DIRTY_UVEDIT_ALL, BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT, } eMeshBatchDirtyMode; diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 63b11a194ff..cecb3118038 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1464,6 +1464,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_PRIMITIVE_LINE 1068 #define GEO_NODE_CURVE_ENDPOINTS 1069 #define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070 +#define GEO_NODE_CURVE_TRIM 1071 +#define GEO_NODE_CURVE_SET_HANDLES 1072 /** \} */ diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 3485bd8ce38..5958d1cd73c 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -376,10 +376,6 @@ void BKE_object_runtime_free_data(struct Object *object); void BKE_object_batch_cache_dirty_tag(struct Object *ob); void BKE_object_data_batch_cache_dirty_tag(struct ID *object_data); -void BKE_object_data_eval_batch_cache_dirty_tag(struct Depsgraph *depsgraph, - struct ID *object_data); -void BKE_object_data_eval_batch_cache_deform_tag(struct Depsgraph *depsgraph, - struct ID *object_data); /* this function returns a superset of the scenes selection based on relationships */ diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index f85e62768f7..53485ecbd6b 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -306,6 +306,7 @@ class BezierSpline final : public Spline { blender::MutableSpan<HandleType> handle_types_right(); blender::Span<blender::float3> handle_positions_right() const; blender::MutableSpan<blender::float3> handle_positions_right(); + void ensure_auto_handles() const; void translate(const blender::float3 &translation) override; void transform(const blender::float4x4 &matrix) override; @@ -353,8 +354,6 @@ class BezierSpline final : public Spline { void correct_end_tangents() const final; void copy_settings(Spline &dst) const final; void copy_data(Spline &dst) const final; - - void ensure_auto_handles() const; }; /** @@ -544,6 +543,7 @@ struct CurveEval { blender::Span<SplinePtr> splines() const; blender::MutableSpan<SplinePtr> splines(); + bool has_spline_with_type(const Spline::Type type) const; void resize(const int size); void add_spline(SplinePtr spline); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 714ea8b1c23..04083d4a79a 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -69,6 +69,7 @@ set(SRC intern/CCGSubSurf_util.c intern/DerivedMesh.cc intern/action.c + intern/action_bones.cc intern/action_mirror.c intern/addon.c intern/anim_data.c @@ -77,6 +78,7 @@ set(SRC intern/anim_visualization.c intern/appdir.c intern/armature.c + intern/armature_selection.cc intern/armature_deform.c intern/armature_pose.cc intern/armature_update.c @@ -288,6 +290,7 @@ set(SRC BKE_DerivedMesh.h BKE_action.h + BKE_action.hh BKE_addon.h BKE_anim_data.h BKE_anim_path.h @@ -295,6 +298,7 @@ set(SRC BKE_animsys.h BKE_appdir.h BKE_armature.h + BKE_armature.hh BKE_asset.h BKE_attribute.h BKE_attribute_access.hh diff --git a/source/blender/blenkernel/intern/action_bones.cc b/source/blender/blenkernel/intern/action_bones.cc new file mode 100644 index 00000000000..b8d185e6a81 --- /dev/null +++ b/source/blender/blenkernel/intern/action_bones.cc @@ -0,0 +1,48 @@ +/* + * 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) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup bke + */ + +#include "BKE_action.hh" + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "DNA_action_types.h" +#include "DNA_anim_types.h" + +#include "MEM_guardedalloc.h" + +namespace blender::bke { + +void BKE_action_find_fcurves_with_bones(const bAction *action, FoundFCurveCallback callback) +{ + LISTBASE_FOREACH (FCurve *, fcu, &action->curves) { + char *bone_name = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones["); + if (!bone_name) { + continue; + } + callback(fcu, bone_name); + MEM_freeN(bone_name); + } +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 2879a995ad6..ffc060bb64e 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -1947,7 +1947,7 @@ static void nlaevalchan_blendOrcombine(NlaEvalChannelSnapshot *lower_necs, return; } default: - BLI_assert("Mix mode should've been handled"); + BLI_assert_msg(0, "Mix mode should've been handled"); } return; } @@ -1960,7 +1960,7 @@ static void nlaevalchan_blendOrcombine(NlaEvalChannelSnapshot *lower_necs, return; } default: - BLI_assert("Blend mode should've been handled"); + BLI_assert_msg(0, "Blend mode should've been handled"); } } @@ -2110,7 +2110,7 @@ static void nlaevalchan_blendOrcombine_get_inverted_upper_evalchan( return; } default: - BLI_assert("Mix mode should've been handled"); + BLI_assert_msg(0, "Mix mode should've been handled"); } return; } @@ -2123,7 +2123,7 @@ static void nlaevalchan_blendOrcombine_get_inverted_upper_evalchan( return; } default: - BLI_assert("Blend mode should've been handled"); + BLI_assert_msg(0, "Blend mode should've been handled"); } } diff --git a/source/blender/blenkernel/intern/armature_pose.cc b/source/blender/blenkernel/intern/armature_pose.cc index 09e1c7d6615..a62a32d9633 100644 --- a/source/blender/blenkernel/intern/armature_pose.cc +++ b/source/blender/blenkernel/intern/armature_pose.cc @@ -23,8 +23,9 @@ * \ingroup bke */ +#include "BKE_action.hh" #include "BKE_animsys.h" -#include "BKE_armature.h" +#include "BKE_armature.hh" #include "BLI_function_ref.hh" #include "BLI_set.hh" @@ -36,14 +37,14 @@ #include "RNA_access.h" +using namespace blender::bke; + namespace { -using BoneNameSet = blender::Set<std::string>; using ActionApplier = blender::FunctionRef<void(PointerRNA *, bAction *, const AnimationEvalContext *)>; -// Forward declarations. -BoneNameSet pose_apply_find_selected_bones(const bArmature *armature, const bPose *pose); +/* Forward declarations. */ void pose_apply_disable_fcurves_for_unselected_bones(bAction *action, const BoneNameSet &selected_bone_names); void pose_apply_restore_fcurves(bAction *action); @@ -102,7 +103,7 @@ void pose_apply(struct Object *ob, } const bArmature *armature = (bArmature *)ob->data; - const BoneNameSet selected_bone_names = pose_apply_find_selected_bones(armature, pose); + const BoneNameSet selected_bone_names = BKE_armature_find_selected_bone_names(armature); const bool limit_to_selected_bones = !selected_bone_names.is_empty(); if (limit_to_selected_bones) { @@ -122,30 +123,6 @@ void pose_apply(struct Object *ob, } } -BoneNameSet pose_apply_find_selected_bones(const bArmature *armature, const bPose *pose) -{ - BoneNameSet selected_bone_names; - bool all_bones_selected = true; - bool no_bones_selected = true; - - LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) { - const bool is_selected = PBONE_SELECTED(armature, pchan->bone); - all_bones_selected &= is_selected; - no_bones_selected &= !is_selected; - - if (is_selected) { - /* Bone names are unique, so no need to check for duplicates. */ - selected_bone_names.add_new(pchan->name); - } - } - - /* If no bones are selected, act as if all are. */ - if (all_bones_selected || no_bones_selected) { - return BoneNameSet(); /* An empty set means "ignore bone selection". */ - } - return selected_bone_names; -} - void pose_apply_restore_fcurves(bAction *action) { /* TODO(Sybren): Restore the FCurve flags, instead of just erasing the 'disabled' flag. */ @@ -157,24 +134,13 @@ void pose_apply_restore_fcurves(bAction *action) void pose_apply_disable_fcurves_for_unselected_bones(bAction *action, const BoneNameSet &selected_bone_names) { - LISTBASE_FOREACH (FCurve *, fcu, &action->curves) { - if (!fcu->rna_path || !strstr(fcu->rna_path, "pose.bones[")) { - continue; - } - - /* Get bone name, and check if this bone is selected. */ - char *bone_name = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones["); - if (!bone_name) { - continue; - } - const bool is_selected = selected_bone_names.contains(bone_name); - MEM_freeN(bone_name); - if (is_selected) { - continue; + auto disable_unselected_fcurve = [&](FCurve *fcu, const char *bone_name) { + const bool is_bone_selected = selected_bone_names.contains(bone_name); + if (!is_bone_selected) { + fcu->flag |= FCURVE_DISABLED; } - - fcu->flag |= FCURVE_DISABLED; - } + }; + BKE_action_find_fcurves_with_bones(action, disable_unselected_fcurve); } } // namespace diff --git a/source/blender/blenkernel/intern/armature_selection.cc b/source/blender/blenkernel/intern/armature_selection.cc new file mode 100644 index 00000000000..c7dbc9d05b1 --- /dev/null +++ b/source/blender/blenkernel/intern/armature_selection.cc @@ -0,0 +1,78 @@ +/* + * 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 bke + */ + +#include "BKE_armature.hh" + +#include "BLI_listbase.h" + +#include "DNA_action_types.h" +#include "DNA_armature_types.h" + +namespace blender::bke { + +namespace { + +void find_selected_bones__visit_bone(const bArmature *armature, + SelectedBoneCallback callback, + SelectedBonesResult &result, + Bone *bone) +{ + const bool is_selected = PBONE_SELECTED(armature, bone); + result.all_bones_selected &= is_selected; + result.no_bones_selected &= !is_selected; + + if (is_selected) { + callback(bone); + } + + LISTBASE_FOREACH (Bone *, child_bone, &bone->childbase) { + find_selected_bones__visit_bone(armature, callback, result, child_bone); + } +} +} // namespace + +SelectedBonesResult BKE_armature_find_selected_bones(const bArmature *armature, + SelectedBoneCallback callback) +{ + SelectedBonesResult result; + LISTBASE_FOREACH (Bone *, root_bone, &armature->bonebase) { + find_selected_bones__visit_bone(armature, callback, result, root_bone); + } + + return result; +} + +BoneNameSet BKE_armature_find_selected_bone_names(const bArmature *armature) +{ + BoneNameSet selected_bone_names; + + /* Iterate over the selected bones to fill the set of bone names. */ + auto callback = [&](Bone *bone) { selected_bone_names.add(bone->name); }; + SelectedBonesResult result = BKE_armature_find_selected_bones(armature, callback); + + /* If no bones are selected, act as if all are. */ + if (result.all_bones_selected || result.no_bones_selected) { + return BoneNameSet(); + } + + return selected_bone_names; +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/armature_test.cc b/source/blender/blenkernel/intern/armature_test.cc index 589337d9d01..47853deec3e 100644 --- a/source/blender/blenkernel/intern/armature_test.cc +++ b/source/blender/blenkernel/intern/armature_test.cc @@ -17,10 +17,13 @@ * All rights reserved. */ -#include "BKE_armature.h" +#include "BKE_armature.hh" +#include "BLI_listbase.h" #include "BLI_math.h" +#include "DNA_armature_types.h" + #include "testing/testing.h" namespace blender::bke::tests { @@ -157,4 +160,80 @@ TEST(vec_roll_to_mat3_normalized, Rotationmatrix) } } +class BKE_armature_find_selected_bones_test : public testing::Test { + protected: + bArmature arm; + Bone bone1, bone2, bone3; + + void SetUp() override + { + strcpy(bone1.name, "bone1"); + strcpy(bone2.name, "bone2"); + strcpy(bone3.name, "bone3"); + + arm.bonebase = {nullptr, nullptr}; + bone1.childbase = {nullptr, nullptr}; + bone2.childbase = {nullptr, nullptr}; + bone3.childbase = {nullptr, nullptr}; + + BLI_addtail(&arm.bonebase, &bone1); // bone1 is root bone + BLI_addtail(&arm.bonebase, &bone2); // bone2 is root bone + BLI_addtail(&bone2.childbase, &bone3); // bone3 has bone2 as parent + + // Make sure the armature & its bones are visible, to make them selectable. + arm.layer = bone1.layer = bone2.layer = bone3.layer = 1; + } +}; + +TEST_F(BKE_armature_find_selected_bones_test, some_bones_selected) +{ + bone1.flag = BONE_SELECTED; + bone2.flag = 0; + bone3.flag = BONE_SELECTED; + + std::vector<Bone *> seen_bones; + auto callback = [&](Bone *bone) { seen_bones.push_back(bone); }; + + SelectedBonesResult result = BKE_armature_find_selected_bones(&arm, callback); + + ASSERT_EQ(seen_bones.size(), 2) << "Expected 2 selected bones, got " << seen_bones.size(); + EXPECT_EQ(seen_bones[0], &bone1); + EXPECT_EQ(seen_bones[1], &bone3); + + EXPECT_FALSE(result.all_bones_selected); // Bone 2 was not selected. + EXPECT_FALSE(result.no_bones_selected); // Bones 1 and 3 were selected. +} + +TEST_F(BKE_armature_find_selected_bones_test, no_bones_selected) +{ + bone1.flag = bone2.flag = bone3.flag = 0; + + std::vector<Bone *> seen_bones; + auto callback = [&](Bone *bone) { seen_bones.push_back(bone); }; + + SelectedBonesResult result = BKE_armature_find_selected_bones(&arm, callback); + + EXPECT_TRUE(seen_bones.empty()) << "Expected no selected bones, got " << seen_bones.size(); + EXPECT_FALSE(result.all_bones_selected); + EXPECT_TRUE(result.no_bones_selected); +} + +TEST_F(BKE_armature_find_selected_bones_test, all_bones_selected) +{ + bone1.flag = bone2.flag = bone3.flag = BONE_SELECTED; + + std::vector<Bone *> seen_bones; + auto callback = [&](Bone *bone) { seen_bones.push_back(bone); }; + + SelectedBonesResult result = BKE_armature_find_selected_bones(&arm, callback); + + ASSERT_EQ(seen_bones.size(), 3) << "Expected 3 selected bones, got " << seen_bones.size(); + EXPECT_EQ(seen_bones[0], &bone1); + EXPECT_EQ(seen_bones[1], &bone2); + EXPECT_EQ(seen_bones[2], &bone3); + + EXPECT_TRUE(result.all_bones_selected); + EXPECT_FALSE(result.no_bones_selected); +} + } // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/attribute.c b/source/blender/blenkernel/intern/attribute.c index ad2be4ffe30..e9444cf88a6 100644 --- a/source/blender/blenkernel/intern/attribute.c +++ b/source/blender/blenkernel/intern/attribute.c @@ -39,6 +39,7 @@ #include "BKE_attribute.h" #include "BKE_customdata.h" +#include "BKE_editmesh.h" #include "BKE_hair.h" #include "BKE_pointcloud.h" #include "BKE_report.h" @@ -63,14 +64,28 @@ static void get_domains(ID *id, DomainInfo info[ATTR_DOMAIN_NUM]) } case ID_ME: { Mesh *mesh = (Mesh *)id; - info[ATTR_DOMAIN_POINT].customdata = &mesh->vdata; - info[ATTR_DOMAIN_POINT].length = mesh->totvert; - info[ATTR_DOMAIN_EDGE].customdata = &mesh->edata; - info[ATTR_DOMAIN_EDGE].length = mesh->totedge; - info[ATTR_DOMAIN_CORNER].customdata = &mesh->ldata; - info[ATTR_DOMAIN_CORNER].length = mesh->totloop; - info[ATTR_DOMAIN_FACE].customdata = &mesh->pdata; - info[ATTR_DOMAIN_FACE].length = mesh->totpoly; + BMEditMesh *em = mesh->edit_mesh; + if (em != NULL) { + BMesh *bm = em->bm; + info[ATTR_DOMAIN_POINT].customdata = &bm->vdata; + info[ATTR_DOMAIN_POINT].length = bm->totvert; + info[ATTR_DOMAIN_EDGE].customdata = &bm->edata; + info[ATTR_DOMAIN_EDGE].length = bm->totedge; + info[ATTR_DOMAIN_CORNER].customdata = &bm->ldata; + info[ATTR_DOMAIN_CORNER].length = bm->totloop; + info[ATTR_DOMAIN_FACE].customdata = &bm->pdata; + info[ATTR_DOMAIN_FACE].length = bm->totface; + } + else { + info[ATTR_DOMAIN_POINT].customdata = &mesh->vdata; + info[ATTR_DOMAIN_POINT].length = mesh->totvert; + info[ATTR_DOMAIN_EDGE].customdata = &mesh->edata; + info[ATTR_DOMAIN_EDGE].length = mesh->totedge; + info[ATTR_DOMAIN_CORNER].customdata = &mesh->ldata; + info[ATTR_DOMAIN_CORNER].length = mesh->totloop; + info[ATTR_DOMAIN_FACE].customdata = &mesh->pdata; + info[ATTR_DOMAIN_FACE].length = mesh->totpoly; + } break; } case ID_HA: { @@ -146,7 +161,24 @@ CustomDataLayer *BKE_id_attribute_new( return NULL; } - CustomData_add_layer_named(customdata, type, CD_DEFAULT, NULL, info[domain].length, name); + switch (GS(id->name)) { + case ID_ME: { + Mesh *me = (Mesh *)id; + BMEditMesh *em = me->edit_mesh; + if (em != NULL) { + BM_data_layer_add_named(em->bm, customdata, type, name); + } + else { + CustomData_add_layer_named(customdata, type, CD_DEFAULT, NULL, info[domain].length, name); + } + break; + } + default: { + CustomData_add_layer_named(customdata, type, CD_DEFAULT, NULL, info[domain].length, name); + break; + } + } + const int index = CustomData_get_named_layer_index(customdata, type, name); return (index == -1) ? NULL : &(customdata->layers[index]); } @@ -168,8 +200,26 @@ bool BKE_id_attribute_remove(ID *id, CustomDataLayer *layer, ReportList *reports return false; } - const int length = BKE_id_attribute_data_length(id, layer); - CustomData_free_layer(customdata, layer->type, length, index); + switch (GS(id->name)) { + case ID_ME: { + Mesh *me = (Mesh *)id; + BMEditMesh *em = me->edit_mesh; + if (em != NULL) { + BM_data_layer_free(em->bm, customdata, layer->type); + } + else { + const int length = BKE_id_attribute_data_length(id, layer); + CustomData_free_layer(customdata, layer->type, length, index); + } + break; + } + default: { + const int length = BKE_id_attribute_data_length(id, layer); + CustomData_free_layer(customdata, layer->type, length, index); + break; + } + } + return true; } @@ -316,7 +366,7 @@ CustomData *BKE_id_attributes_iterator_next_domain(ID *id, CustomDataLayer *laye for (AttributeDomain domain = 0; domain < ATTR_DOMAIN_NUM; domain++) { CustomData *customdata = info[domain].customdata; - if (customdata && customdata->layers) { + if (customdata && customdata->layers && customdata->totlayer) { if (customdata->layers == layers) { use_next = true; } diff --git a/source/blender/blenkernel/intern/boids.c b/source/blender/blenkernel/intern/boids.c index e69173cc1d5..9caf416cd0c 100644 --- a/source/blender/blenkernel/intern/boids.c +++ b/source/blender/blenkernel/intern/boids.c @@ -231,7 +231,7 @@ static bool rule_avoid_collision(BoidRule *rule, int n, neighbors = 0, nearest = 0; bool ret = 0; - // check deflector objects first + /* Check deflector objects first. */ if (acbr->options & BRULE_ACOLL_WITH_DEFLECTORS && bbd->sim->colliders) { ParticleCollision col; BVHTreeRayHit hit; @@ -293,7 +293,7 @@ static bool rule_avoid_collision(BoidRule *rule, } } - // check boids in own system + /* Check boids in own system. */ if (acbr->options & BRULE_ACOLL_WITH_BOIDS) { neighbors = BLI_kdtree_3d_range_search_with_len_squared_cb(bbd->sim->psys->tree, pa->prev_state.co, @@ -823,11 +823,13 @@ static boid_rule_cb boid_rules[] = { rule_follow_leader, rule_average_speed, rule_fight, - // rule_help, - // rule_protect, - // rule_hide, - // rule_follow_path, - // rule_follow_wall, +#if 0 + rule_help, + rule_protect, + rule_hide, + rule_follow_path, + rule_follow_wall, +#endif }; static void set_boid_values(BoidValues *val, BoidSettings *boids, ParticleData *pa) @@ -1069,11 +1071,13 @@ static BoidState *get_boid_state(BoidSettings *boids, ParticleData *pa) return state; } -// static int boid_condition_is_true(BoidCondition *cond) -//{ -// /* TODO */ -// return 0; -//} + +#if 0 /* TODO */ +static int boid_condition_is_true(BoidCondition *cond) +{ + return 0; +} +#endif /* determines the velocity the boid wants to have */ void boid_brain(BoidBrainData *bbd, int p, ParticleData *pa) @@ -1085,7 +1089,6 @@ void boid_brain(BoidBrainData *bbd, int p, ParticleData *pa) BoidParticle *bpa = pa->boid; ParticleSystem *psys = bbd->sim->psys; int rand; - // BoidCondition *cond; if (bpa->data.health <= 0.0f) { pa->alive = PARS_DYING; @@ -1093,15 +1096,17 @@ void boid_brain(BoidBrainData *bbd, int p, ParticleData *pa) return; } - // planned for near future - // cond = state->conditions.first; - // for (; cond; cond=cond->next) { - // if (boid_condition_is_true(cond)) { - // pa->boid->state_id = cond->state_id; - // state = get_boid_state(boids, pa); - // break; /* only first true condition is used */ - // } - //} + /* Planned for near future. */ +#if 0 + BoidCondition *cond = state->conditions.first; + for (; cond; cond = cond->next) { + if (boid_condition_is_true(cond)) { + pa->boid->state_id = cond->state_id; + state = get_boid_state(boids, pa); + break; /* only first true condition is used */ + } + } +#endif zero_v3(bbd->wanted_co); bbd->wanted_speed = 0.0f; @@ -1507,20 +1512,22 @@ void boid_body(BoidBrainData *bbd, ParticleData *pa) } case eBoidMode_Climbing: { boid_climb(boids, pa, ground_co, ground_nor); - // float nor[3]; - // copy_v3_v3(nor, ground_nor); - - ///* gather apparent gravity to r_ve */ - // madd_v3_v3fl(pa->r_ve, ground_nor, -1.0); - // normalize_v3(pa->r_ve); - - ///* raise boid it's size from surface */ - // mul_v3_fl(nor, pa->size * boids->height); - // add_v3_v3v3(pa->state.co, ground_co, nor); - - ///* remove normal component from velocity */ - // project_v3_v3v3(v, pa->state.vel, ground_nor); - // sub_v3_v3v3(pa->state.vel, pa->state.vel, v); +#if 0 + float nor[3]; + copy_v3_v3(nor, ground_nor); + + /* Gather apparent gravity to r_ve. */ + madd_v3_v3fl(pa->r_ve, ground_nor, -1.0); + normalize_v3(pa->r_ve); + + /* Raise boid it's size from surface. */ + mul_v3_fl(nor, pa->size * boids->height); + add_v3_v3v3(pa->state.co, ground_co, nor); + + /* Remove normal component from velocity. */ + project_v3_v3v3(v, pa->state.vel, ground_nor); + sub_v3_v3v3(pa->state.vel, pa->state.vel, v); +#endif break; } case eBoidMode_OnLand: { diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 30198297347..7c1639b44d2 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -1810,125 +1810,6 @@ bool BKE_collection_objects_select(ViewLayer *view_layer, Collection *collection /** \name Collection move (outliner drag & drop) * \{ */ -/* Local temporary storage for layer collection flags. */ -typedef struct LayerCollectionFlag { - struct LayerCollectionFlag *next, *prev; - /** The view layer for the collections being moved, NULL for their children. */ - ViewLayer *view_layer; - /** The original #LayerCollection's collection field. */ - Collection *collection; - /** The original #LayerCollection's flag. */ - int flag; - /** Corresponds to #LayerCollection->layer_collections. */ - ListBase children; -} LayerCollectionFlag; - -static void layer_collection_flags_store_recursive(const LayerCollection *layer_collection, - LayerCollectionFlag *flag) -{ - flag->collection = layer_collection->collection; - flag->flag = layer_collection->flag; - - LISTBASE_FOREACH (const LayerCollection *, child, &layer_collection->layer_collections) { - LayerCollectionFlag *child_flag = MEM_callocN(sizeof(LayerCollectionFlag), __func__); - BLI_addtail(&flag->children, child_flag); - layer_collection_flags_store_recursive(child, child_flag); - } -} - -/** - * For every view layer, find the \a collection and save flags - * for it and its children in a temporary tree structure. - */ -static void layer_collection_flags_store(Main *bmain, - const Collection *collection, - ListBase *r_layer_level_list) -{ - BLI_listbase_clear(r_layer_level_list); - LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { - LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { - LayerCollection *layer_collection = BKE_layer_collection_first_from_scene_collection( - view_layer, collection); - /* Skip this view layer if the collection isn't found for some reason. */ - if (layer_collection == NULL) { - continue; - } - - /* Store the flags for the collection and all of its children. */ - LayerCollectionFlag *flag = MEM_callocN(sizeof(LayerCollectionFlag), __func__); - flag->view_layer = view_layer; - - /* Recursively save flags from collection children. */ - layer_collection_flags_store_recursive(layer_collection, flag); - - BLI_addtail(r_layer_level_list, flag); - } - } -} - -static void layer_collection_flags_free_recursive(LayerCollectionFlag *flag) -{ - LISTBASE_FOREACH (LayerCollectionFlag *, child, &flag->children) { - layer_collection_flags_free_recursive(child); - } - - BLI_freelistN(&flag->children); -} - -static void layer_collection_flags_restore_recursive(LayerCollection *layer_collection, - LayerCollectionFlag *flag) -{ - /* There should be a flag struct for every layer collection. */ - BLI_assert(BLI_listbase_count(&layer_collection->layer_collections) == - BLI_listbase_count(&flag->children)); - /* The flag and the layer collection should actually correspond. */ - BLI_assert(flag->collection == layer_collection->collection); - - LayerCollectionFlag *child_flag = flag->children.first; - LISTBASE_FOREACH (LayerCollection *, child, &layer_collection->layer_collections) { - layer_collection_flags_restore_recursive(child, child_flag); - - child_flag = child_flag->next; - } - - /* We treat exclude as a special case. - * - * If in a different view layer the parent collection was disabled (e.g., background) - * and now we moved a new collection to be part of the background this collection should - * probably be disabled. - * - * NOTE: If we were to also keep the exclude flag we would need to re-sync the collections. - */ - layer_collection->flag = flag->flag | (layer_collection->flag & LAYER_COLLECTION_EXCLUDE); -} - -/** - * Restore a collection's (and its children's) flags for each view layer - * from the structure built in #layer_collection_flags_store. - */ -static void layer_collection_flags_restore(ListBase *flags, const Collection *collection) -{ - LISTBASE_FOREACH (LayerCollectionFlag *, flag, flags) { - ViewLayer *view_layer = flag->view_layer; - /* The top level of flag structs must have this set. */ - BLI_assert(view_layer != NULL); - - LayerCollection *layer_collection = BKE_layer_collection_first_from_scene_collection( - view_layer, collection); - /* Check that the collection is still in the scene (and therefore its view layers). In most - * cases this is true, but if we move a sub-collection shared by several scenes to a collection - * local to the target scene, it is effectively removed from every other scene's hierarchy - * (e.g. moving into current scene's master collection). Then the other scene's view layers - * won't contain a matching layer collection anymore, so there is nothing to restore to. */ - if (layer_collection != NULL) { - layer_collection_flags_restore_recursive(layer_collection, flag); - } - layer_collection_flags_free_recursive(flag); - } - - BLI_freelistN(flags); -} - bool BKE_collection_move(Main *bmain, Collection *to_parent, Collection *from_parent, @@ -1969,26 +1850,7 @@ bool BKE_collection_move(Main *bmain, } } - /* Make sure we store the flag of the layer collections before we remove and re-create them. - * Otherwise they will get lost and everything will be copied from the new parent collection. - * Don't use flag syncing when moving a collection to a different scene, as it no longer exists - * in the same view layers anyway. */ - const bool do_flag_sync = BKE_scene_find_from_collection(bmain, to_parent) == - BKE_scene_find_from_collection(bmain, collection); - ListBase layer_flags; - if (do_flag_sync) { - layer_collection_flags_store(bmain, collection, &layer_flags); - } - - /* Create and remove layer collections. */ - BKE_main_collection_sync(bmain); - - /* Restore the original layer collection flags and free their temporary storage. */ - if (do_flag_sync) { - layer_collection_flags_restore(&layer_flags, collection); - } - - /* We need to sync it again to pass the correct flags to the collections objects. */ + /* Update layer collections. */ BKE_main_collection_sync(bmain); return true; diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 24615dd8c2b..f1369254347 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -332,16 +332,6 @@ IDTypeInfo IDType_ID_CU = { .lib_override_apply_post = NULL, }; -static int cu_isectLL(const float v1[3], - const float v2[3], - const float v3[3], - const float v4[3], - short cox, - short coy, - float *lambda, - float *mu, - float vec[3]); - /* frees editcurve entirely */ void BKE_curve_editfont_free(Curve *cu) { @@ -566,18 +556,6 @@ void BKE_curve_texspace_ensure(Curve *cu) } } -void BKE_curve_texspace_get(Curve *cu, float r_loc[3], float r_size[3]) -{ - BKE_curve_texspace_ensure(cu); - - if (r_loc) { - copy_v3_v3(r_loc, cu->loc); - } - if (r_size) { - copy_v3_v3(r_size, cu->size); - } -} - bool BKE_nurbList_index_get_co(ListBase *nurb, const int index, float r_co[3]) { int tot = 0; diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 8e1577ab072..5c18f6f3807 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -48,6 +48,22 @@ blender::MutableSpan<SplinePtr> CurveEval::splines() return splines_; } +/** + * \return True if the curve contains a spline with the given type. + * + * \note If you are looping over all of the splines in the same scope anyway, + * it's better to avoid calling this function, in case there are many splines. + */ +bool CurveEval::has_spline_with_type(const Spline::Type type) const +{ + for (const SplinePtr &spline : this->splines()) { + if (spline->type() == type) { + return true; + } + } + return false; +} + void CurveEval::resize(const int size) { splines_.resize(size); diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index a4ffaa8b10b..99dc1db9d38 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -903,6 +903,7 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, Mesh *modified = nullptr; float(*vertCos)[3] = nullptr; + int totvert = 0; for (; md; md = md->next) { const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); @@ -929,7 +930,6 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, if (mti->type == eModifierTypeType_OnlyDeform || (mti->type == eModifierTypeType_DeformOrConstruct && !modified)) { if (modified) { - int totvert = 0; if (!vertCos) { vertCos = BKE_mesh_vert_coords_alloc(modified, &totvert); } @@ -939,7 +939,6 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, mti->deformVerts(md, &mectx_deform, modified, vertCos, totvert); } else { - int totvert = 0; if (!vertCos) { vertCos = displist_vert_coords_alloc(dispbase, &totvert); } @@ -1668,7 +1667,7 @@ void BKE_displist_minmax(const ListBase *dispbase, float min[3], float max[3]) LISTBASE_FOREACH (const DispList *, dl, dispbase) { const int tot = (dl->type == DL_INDEX3) ? dl->nr : dl->nr * dl->parts; for (const int i : IndexRange(tot)) { - minmax_v3v3_v3(min, max, &dl->verts[i]); + minmax_v3v3_v3(min, max, &dl->verts[i * 3]); } if (tot != 0) { doit = true; diff --git a/source/blender/blenkernel/intern/dyntopo.c b/source/blender/blenkernel/intern/dyntopo.c index b2e909d1f36..374cb3c8941 100644 --- a/source/blender/blenkernel/intern/dyntopo.c +++ b/source/blender/blenkernel/intern/dyntopo.c @@ -1445,7 +1445,7 @@ static void short_edge_queue_task_cb(void *__restrict userdata, TGSET_ITER_END } -ATTR_NO_OPT static bool check_face_is_tri(PBVH *pbvh, BMFace *f) +static bool check_face_is_tri(PBVH *pbvh, BMFace *f) { if (f->len == 3) { return true; @@ -1534,7 +1534,7 @@ ATTR_NO_OPT static bool check_face_is_tri(PBVH *pbvh, BMFace *f) return false; } -ATTR_NO_OPT static bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) +static bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) { BMFace **fs = NULL; BLI_array_staticdeclare(fs, 32); @@ -1869,12 +1869,12 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx, } /** - * The 2 new faces created and assigned to ``f_new`` have their + * The 2 new faces created and assigned to `f_new` have their * verts & edges shuffled around. * * - faces wind anticlockwise in this example. - * - original edge is ``(v1, v2)`` - * - original face is ``(v1, v2, v3)`` + * - original edge is `(v1, v2)` + * - original face is `(v1, v2, v3)` * * <pre> * + v3(v_opp) @@ -1890,8 +1890,8 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx, * (first) (second) * </pre> * - * - f_new (first): ``v_tri=(v1, v4, v3), e_tri=(e1, e5, e4)`` - * - f_new (second): ``v_tri=(v4, v2, v3), e_tri=(e2, e3, e5)`` + * - f_new (first): `v_tri=(v1, v4, v3), e_tri=(e1, e5, e4)` + * - f_new (second): `v_tri=(v4, v2, v3), e_tri=(e2, e3, e5)` */ /* Create two new faces */ @@ -2503,13 +2503,18 @@ static bool pbvh_bmesh_collapse_short_edges(EdgeQueueContext *eq_ctx, return any_collapsed; } -// need to file a CLANG bug -ATTR_NO_OPT static bool cleanup_valence_3_4(PBVH *pbvh, - const float center[3], - const float view_normal[3], - float radius, - const bool use_frontface, - const bool use_projected) +// need to file a CLANG bug, getting weird behavior here +#ifdef __clang__ +__attribute__((optnone)) +#endif + +static bool +cleanup_valence_3_4(PBVH *pbvh, + const float center[3], + const float view_normal[3], + float radius, + const bool use_frontface, + const bool use_projected) { bool modified = false; diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index 9cae74e4e9a..83e03ef44f5 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -306,7 +306,7 @@ const float (*BKE_editmesh_vert_coords_when_deformed(struct Depsgraph *depsgraph } else if ((em->mesh_eval_final != NULL) && (em->mesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH)) { - /* If this is an edit-mesh type, leave NULL as we can use the vertex coords. . */ + /* If this is an edit-mesh type, leave NULL as we can use the vertex coords. */ } else { /* Constructive modifiers have been used, we need to allocate coordinates. */ diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 1b628b16802..fc1721eaf3a 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -547,7 +547,7 @@ static float eff_calc_visibility(ListBase *colliders, return visibility; } -// noise function for wind e.g. +/* Noise function for wind e.g. */ static float wind_func(struct RNG *rng, float strength) { int random = (BLI_rng_get_int(rng) + 1) % 128; /* max 2357 */ diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 8f47a7e75d4..5decc7a1792 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -918,7 +918,7 @@ void BKE_fcurve_active_keyframe_set(FCurve *fcu, const BezTriple *active_bezt) } /* The active keyframe should always be selected. */ - BLI_assert(BEZT_ISSEL_ANY(active_bezt) || !"active keyframe must be selected"); + BLI_assert_msg(BEZT_ISSEL_ANY(active_bezt), "active keyframe must be selected"); fcu->active_keyframe_index = (int)offset; } diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 2b48683a3a8..92fd220549a 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -1545,7 +1545,7 @@ static void emit_from_particles(Object *flow_ob, float dt) { if (ffs && ffs->psys && ffs->psys->part && - ELEM(ffs->psys->part->type, PART_EMITTER, PART_FLUID)) // is particle system selected + ELEM(ffs->psys->part->type, PART_EMITTER, PART_FLUID)) /* Is particle system selected. */ { ParticleSimulationData sim; ParticleSystem *psys = ffs->psys; @@ -5005,7 +5005,6 @@ void BKE_fluid_modifier_copy(const struct FluidModifierData *fmd, tfds->noise_pos_scale = fds->noise_pos_scale; tfds->noise_time_anim = fds->noise_time_anim; tfds->noise_scale = fds->noise_scale; - tfds->noise_type = fds->noise_type; /* liquid domain options */ tfds->flip_ratio = fds->flip_ratio; diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index b5c49dbb8b2..0b6ba966974 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -854,17 +854,9 @@ class PositionAttributeProvider final : public BuiltinPointAttributeProvider<flo return {}; } - bool curve_has_bezier_spline = false; - for (SplinePtr &spline : curve->splines()) { - if (spline->type() == Spline::Type::Bezier) { - curve_has_bezier_spline = true; - break; - } - } - /* Use the regular position virtual array when there aren't any Bezier splines * to avoid the overhead of checking the spline type for every point. */ - if (!curve_has_bezier_spline) { + if (!curve->has_spline_with_type(Spline::Type::Bezier)) { return BuiltinPointAttributeProvider<float3>::try_get_for_write(component); } diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 785f63a7ba2..f8a07939096 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -2094,13 +2094,14 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int /** * Reduce a series of points when the distance is below a threshold. - * Special case for first and last points (both are keeped) for other points, + * Special case for first and last points (both are kept) for other points, * the merge point always is at first point. - * \param gpd: Grease pencil data-block - * \param gpf: Grease Pencil frame - * \param gps: Grease Pencil stroke - * \param threshold: Distance between points - * \param use_unselected: Set to true to analyze all stroke and not only selected points + * + * \param gpd: Grease pencil data-block. + * \param gpf: Grease Pencil frame. + * \param gps: Grease Pencil stroke. + * \param threshold: Distance between points. + * \param use_unselected: Set to true to analyze all stroke and not only selected points. */ void BKE_gpencil_stroke_merge_distance(bGPdata *gpd, bGPDframe *gpf, @@ -2276,7 +2277,7 @@ static void gpencil_generate_edgeloops(Object *ob, } /* Arrays for all edge vertices (forward and backward) that form a edge loop. - * This is reused for each edgeloop to create gpencil stroke. */ + * This is reused for each edge-loop to create gpencil stroke. */ uint *stroke = (uint *)MEM_callocN(sizeof(uint) * me->totedge * 2, __func__); uint *stroke_fw = (uint *)MEM_callocN(sizeof(uint) * me->totedge, __func__); uint *stroke_bw = (uint *)MEM_callocN(sizeof(uint) * me->totedge, __func__); @@ -2468,7 +2469,7 @@ static void make_element_name(const char *obname, const char *name, const int ma * \param scene: Original scene. * \param ob_gp: Grease pencil object to add strokes. * \param ob_mesh: Mesh to convert. - * \param angle: Limit angle to consider a edgeloop ends. + * \param angle: Limit angle to consider a edge-loop ends. * \param thickness: Thickness of the strokes. * \param offset: Offset along the normals. * \param matrix: Transformation matrix. diff --git a/source/blender/blenkernel/intern/icons.cc b/source/blender/blenkernel/intern/icons.cc index 7e9f81f9c83..12a0a1e3ae7 100644 --- a/source/blender/blenkernel/intern/icons.cc +++ b/source/blender/blenkernel/intern/icons.cc @@ -667,7 +667,7 @@ void BKE_previewimg_blend_read(BlendDataReader *reader, PreviewImage *prv) BKE_previewimg_finish(prv, i); } else { - /* Only for old files that didn't write the flag . */ + /* Only for old files that didn't write the flag. */ prv->flag[i] |= PRV_UNFINISHED; } } diff --git a/source/blender/blenkernel/intern/image_save.c b/source/blender/blenkernel/intern/image_save.c index 9e3d5a162ae..360bad3e786 100644 --- a/source/blender/blenkernel/intern/image_save.c +++ b/source/blender/blenkernel/intern/image_save.c @@ -135,8 +135,8 @@ static void imbuf_save_post(ImBuf *ibuf, ImBuf *colormanaged_ibuf) /** * \return success. - * \note ``ima->filepath`` and ``ibuf->name`` should end up the same. - * \note for multiview the first ``ibuf`` is important to get the settings. + * \note `ima->filepath` and `ibuf->name` should end up the same. + * \note for multi-view the first `ibuf` is important to get the settings. */ static bool image_save_single(ReportList *reports, Image *ima, diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 730c989abc9..262950bfab5 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -23,7 +23,10 @@ #include <string.h> +#include "CLG_log.h" + #include "BLI_listbase.h" +#include "BLI_mempool.h" #include "BLI_string.h" #include "BLI_string_utf8.h" #include "BLI_string_utils.h" @@ -63,6 +66,8 @@ #include "BLO_read_write.h" +static CLG_LogRef LOG = {"bke.layercollection"}; + /* Set of flags which are dependent on a collection settings. */ static const short g_base_collection_flags = (BASE_VISIBLE_DEPSGRAPH | BASE_VISIBLE_VIEWLAYER | BASE_SELECTABLE | BASE_ENABLED_VIEWPORT | @@ -737,6 +742,228 @@ int BKE_layer_collection_findindex(ViewLayer *view_layer, const LayerCollection * in at least one layer collection. That list is also synchronized here, and * stores state like selection. */ +/* This API allows to temporarily forbid resync of LayerCollections. + * + * This can greatly improve performances in cases where those functions get + * called a lot (e.g. during massive remappings of IDs). + * + * Usage of these should be done very carefully though. In particular, calling + * code must ensures it resync LayerCollections before any UI/Event loop + * handling can happen. + * + * WARNING: This is not threadsafe at all, only use from main thread. + * + * NOTE: It is probably needed to use #BKE_main_collection_sync_remap instead + * of just #BKE_main_collection_sync after disabling LayerCollection resync, + * unless it is absolutely certain that no ID remapping (or any other process + * that may invalidate the caches) will happen while it is disabled. + * + * NOTE: This is a quick and safe band-aid around the long-known issue + * regarding this resync process. + * Proper fix would be to make resync itself lazy, i.e. only happen + * when actually needed. + * See also T73411. + */ +static bool no_resync = false; + +void BKE_layer_collection_resync_forbid(void) +{ + no_resync = true; +} + +void BKE_layer_collection_resync_allow(void) +{ + no_resync = false; +} + +typedef struct LayerCollectionResync { + struct LayerCollectionResync *prev, *next; + + /* Temp data used to generate a queue during valid layer search. See + * #layer_collection_resync_find. */ + struct LayerCollectionResync *queue_next; + + /* LayerCollection and Collection wrapped by this data. */ + LayerCollection *layer; + Collection *collection; + + /* Hierarchical relationships in the old, existing ViewLayer state (except for newly created + * layers). */ + struct LayerCollectionResync *parent_layer_resync; + ListBase children_layer_resync; + + /* This layer still points to a valid collection. */ + bool is_usable; + /* This layer is still valid as a parent, i.e. at least one of its original layer children is + * usable and matches one of its current children collections. */ + bool is_valid_as_parent; + /* This layer is still valid as a child, i.e. its original layer parent is usable and matches one + * of its current parents collections. */ + bool is_valid_as_child; + /* This layer is still fully valid in the new collection hierarchy, i.e. itself and all of its + * parents fully match the current collection hierarchy. + * OR + * This layer has already been re-used to match the new collections hierarchy. */ + bool is_used; +} LayerCollectionResync; + +static LayerCollectionResync *layer_collection_resync_create_recurse( + LayerCollectionResync *parent_layer_resync, LayerCollection *layer, BLI_mempool *mempool) +{ + LayerCollectionResync *layer_resync = BLI_mempool_calloc(mempool); + + layer_resync->layer = layer; + layer_resync->collection = layer->collection; + layer_resync->parent_layer_resync = parent_layer_resync; + if (parent_layer_resync != NULL) { + BLI_addtail(&parent_layer_resync->children_layer_resync, layer_resync); + } + + layer_resync->is_usable = (layer->collection != NULL); + layer_resync->is_valid_as_child = + layer_resync->is_usable && (parent_layer_resync == NULL || + (parent_layer_resync->is_usable && + BLI_findptr(&parent_layer_resync->layer->collection->children, + layer->collection, + offsetof(CollectionChild, collection)) != NULL)); + if (layer_resync->is_valid_as_child) { + layer_resync->is_used = parent_layer_resync != NULL ? parent_layer_resync->is_used : true; + } + else { + layer_resync->is_used = false; + } + + if (BLI_listbase_is_empty(&layer->layer_collections)) { + layer_resync->is_valid_as_parent = layer_resync->is_usable; + } + else { + LISTBASE_FOREACH (LayerCollection *, child_layer, &layer->layer_collections) { + LayerCollectionResync *child_layer_resync = layer_collection_resync_create_recurse( + layer_resync, child_layer, mempool); + if (layer_resync->is_usable && child_layer_resync->is_valid_as_child) { + layer_resync->is_valid_as_parent = true; + } + } + } + + CLOG_INFO(&LOG, + 4, + "Old LayerCollection for %s is...\n\tusable: %d\n\tvalid parent: %d\n\tvalid child: " + "%d\n\tused: %d\n", + layer_resync->collection ? layer_resync->collection->id.name : "<NONE>", + layer_resync->is_usable, + layer_resync->is_valid_as_parent, + layer_resync->is_valid_as_child, + layer_resync->is_used); + + return layer_resync; +} + +static LayerCollectionResync *layer_collection_resync_find(LayerCollectionResync *layer_resync, + Collection *child_collection) +{ + /* Given the given parent, valid layer collection, find in the old hierarchy the best possible + * unused layer matching the given child collection. + * + * This uses the following heuristics: + * - Prefer a layer descendant of the given parent one if possible. + * - Prefer a layer as closely related as possible from the given parent. + * - Do not used layers that are not head (highest possible ancestor) of a local valid hierarchy + * branch, since we can assume we could then re-use its ancestor instead. + * + * A queue is used to ensure this order of preferences. + */ + + BLI_assert(layer_resync->collection != child_collection); + BLI_assert(child_collection != NULL); + + LayerCollectionResync *current_layer_resync = NULL; + LayerCollectionResync *root_layer_resync = layer_resync; + + LayerCollectionResync *queue_head = layer_resync, *queue_tail = layer_resync; + layer_resync->queue_next = NULL; + + while (queue_head != NULL) { + current_layer_resync = queue_head; + queue_head = current_layer_resync->queue_next; + + if (current_layer_resync->collection == child_collection && + (current_layer_resync->parent_layer_resync == layer_resync || + (!current_layer_resync->is_used && !current_layer_resync->is_valid_as_child))) { + /* This layer is a valid candidate, because its collection matches the seeked one, AND: + * - It is a direct child of the initial given parent ('unchanged hierarchy' case), OR + * - It is not currently used, and not part of a valid hierarchy (sub-)chain. + */ + break; + } + + /* Else, add all its direct children for further searching. */ + LISTBASE_FOREACH (LayerCollectionResync *, + child_layer_resync, + ¤t_layer_resync->children_layer_resync) { + /* Add to tail of the queue. */ + queue_tail->queue_next = child_layer_resync; + child_layer_resync->queue_next = NULL; + queue_tail = child_layer_resync; + if (queue_head == NULL) { + queue_head = queue_tail; + } + } + + /* If all descendants from current layer have been processed, go one step higher and + * process all of its other siblings. */ + if (queue_head == NULL && root_layer_resync->parent_layer_resync != NULL) { + LISTBASE_FOREACH (LayerCollectionResync *, + sibling_layer_resync, + &root_layer_resync->parent_layer_resync->children_layer_resync) { + if (sibling_layer_resync == root_layer_resync) { + continue; + } + /* Add to tail of the queue. */ + queue_tail->queue_next = sibling_layer_resync; + sibling_layer_resync->queue_next = NULL; + queue_tail = sibling_layer_resync; + if (queue_head == NULL) { + queue_head = queue_tail; + } + } + root_layer_resync = root_layer_resync->parent_layer_resync; + } + + current_layer_resync = NULL; + } + + return current_layer_resync; +} + +static void layer_collection_resync_unused_layers_free(ViewLayer *view_layer, + LayerCollectionResync *layer_resync) +{ + LISTBASE_FOREACH ( + LayerCollectionResync *, child_layer_resync, &layer_resync->children_layer_resync) { + layer_collection_resync_unused_layers_free(view_layer, child_layer_resync); + } + + if (!layer_resync->is_used) { + CLOG_INFO(&LOG, + 4, + "Freeing unused LayerCollection for %s", + layer_resync->collection != NULL ? layer_resync->collection->id.name : + "<Deleted Collection>"); + + if (layer_resync->layer == view_layer->active_collection) { + view_layer->active_collection = NULL; + } + + /* We do not want to go recursive here, this is handled through the LayerCollectionResync data + * wrapper. */ + MEM_freeN(layer_resync->layer); + layer_resync->layer = NULL; + layer_resync->collection = NULL; + layer_resync->is_usable = false; + } +} + static void layer_collection_objects_sync(ViewLayer *view_layer, LayerCollection *layer, ListBase *r_lb_new_object_bases, @@ -805,55 +1032,91 @@ static void layer_collection_objects_sync(ViewLayer *view_layer, } static void layer_collection_sync(ViewLayer *view_layer, - const ListBase *lb_children_collections, - ListBase *r_lb_children_layers, + LayerCollectionResync *layer_resync, + BLI_mempool *layer_resync_mempool, ListBase *r_lb_new_object_bases, const short parent_layer_flag, const short parent_collection_restrict, const short parent_layer_restrict, const ushort parent_local_collections_bits) { - /* TODO: support recovery after removal of intermediate collections, reordering, .. - * For local edits we can make editing operating do the appropriate thing, but for - * linking we can only sync after the fact. */ - - /* Remove layer collections that no longer have a corresponding scene collection. */ - LISTBASE_FOREACH_MUTABLE (LayerCollection *, child_layer, r_lb_children_layers) { - /* Note that ID remap can set child_layer->collection to NULL when deleting collections. */ - Collection *child_collection = (child_layer->collection != NULL) ? - BLI_findptr(lb_children_collections, - child_layer->collection, - offsetof(CollectionChild, collection)) : - NULL; - - if (child_collection == NULL) { - if (child_layer == view_layer->active_collection) { - view_layer->active_collection = NULL; - } + /* This function assumes current 'parent' layer collection is already fully (re)synced and valid + * regarding current Collection hierarchy. + * + * It will process all the children collections of the collection from the given 'parent' layer, + * re-use or create layer collections for each of them, and ensure orders also match. + * + * Then it will ensure that the objects owned by the given parent collection have a proper base. + * + * NOTE: This process is recursive. + */ - /* Free recursively. */ - layer_collection_free(view_layer, child_layer); - BLI_freelinkN(r_lb_children_layers, child_layer); - } - } + /* Temporary storage for all valid (new or reused) children layers. */ + ListBase new_lb_layer = {NULL, NULL}; - /* Add layer collections for any new scene collections, and ensure order is the same. */ - ListBase lb_new_children_layers = {NULL, NULL}; + BLI_assert(layer_resync->is_used); - LISTBASE_FOREACH (const CollectionChild *, child, lb_children_collections) { + LISTBASE_FOREACH (CollectionChild *, child, &layer_resync->collection->children) { Collection *child_collection = child->collection; - LayerCollection *child_layer = BLI_findptr( - r_lb_children_layers, child_collection, offsetof(LayerCollection, collection)); + LayerCollectionResync *child_layer_resync = layer_collection_resync_find(layer_resync, + child_collection); + + if (child_layer_resync != NULL) { + BLI_assert(child_layer_resync->collection != NULL); + BLI_assert(child_layer_resync->layer != NULL); + BLI_assert(child_layer_resync->is_usable); + + if (child_layer_resync->is_used) { + CLOG_INFO(&LOG, + 4, + "Found same existing LayerCollection for %s as child of %s", + child_collection->id.name, + layer_resync->collection->id.name); + } + else { + CLOG_INFO(&LOG, + 4, + "Found a valid unused LayerCollection for %s as child of %s, re-using it", + child_collection->id.name, + layer_resync->collection->id.name); + } + + child_layer_resync->is_used = true; - if (child_layer) { - BLI_remlink(r_lb_children_layers, child_layer); - BLI_addtail(&lb_new_children_layers, child_layer); + /* NOTE: Do not move the resync wrapper to match the new layer hierarchy, so that the old + * parenting info remains available. In case a search for a valid layer in the children of + * the current is required again, the old parenting hierarchy is needed as reference, not the + * new one. + */ + BLI_remlink(&child_layer_resync->parent_layer_resync->layer->layer_collections, + child_layer_resync->layer); + BLI_addtail(&new_lb_layer, child_layer_resync->layer); } else { - child_layer = layer_collection_add(&lb_new_children_layers, child_collection); + CLOG_INFO(&LOG, + 4, + "No available LayerCollection for %s as child of %s, creating a new one", + child_collection->id.name, + layer_resync->collection->id.name); + + LayerCollection *child_layer = layer_collection_add(&new_lb_layer, child_collection); child_layer->flag = parent_layer_flag; + + child_layer_resync = BLI_mempool_calloc(layer_resync_mempool); + child_layer_resync->collection = child_collection; + child_layer_resync->layer = child_layer; + child_layer_resync->is_usable = true; + child_layer_resync->is_used = true; + child_layer_resync->is_valid_as_child = true; + child_layer_resync->is_valid_as_parent = true; + /* NOTE: Needs to be added to the layer_resync hierarchy so that the resync wrapper gets + * freed at the end. */ + child_layer_resync->parent_layer_resync = layer_resync; + BLI_addtail(&layer_resync->children_layer_resync, child_layer_resync); } + LayerCollection *child_layer = child_layer_resync->layer; + const ushort child_local_collections_bits = parent_local_collections_bits & child_layer->local_collections_bits; @@ -871,16 +1134,15 @@ static void layer_collection_sync(ViewLayer *view_layer, /* Sync child collections. */ layer_collection_sync(view_layer, - &child_collection->children, - &child_layer->layer_collections, + child_layer_resync, + layer_resync_mempool, r_lb_new_object_bases, child_layer->flag, child_collection_restrict, child_layer_restrict, child_local_collections_bits); - /* Layer collection exclude is not inherited, we can skip the remaining process, including - * object bases synchronization. */ + /* Layer collection exclude is not inherited. */ child_layer->runtime_flag = 0; if (child_layer->flag & LAYER_COLLECTION_EXCLUDE) { continue; @@ -896,34 +1158,20 @@ static void layer_collection_sync(ViewLayer *view_layer, ((child_layer_restrict & LAYER_COLLECTION_HIDE) == 0)) { child_layer->runtime_flag |= LAYER_COLLECTION_VISIBLE_VIEW_LAYER; } - - layer_collection_objects_sync(view_layer, - child_layer, - r_lb_new_object_bases, - child_collection_restrict, - child_layer_restrict, - child_local_collections_bits); - } - - /* Free potentially remaining unused layer collections in old list. - * NOTE: While this does not happen in typical situations, some corner cases (like remapping - * several different collections to a single one) can lead to this list having extra unused - * items. */ - LISTBASE_FOREACH_MUTABLE (LayerCollection *, lc, r_lb_children_layers) { - if (lc == view_layer->active_collection) { - view_layer->active_collection = NULL; - } - - /* Free recursively. */ - layer_collection_free(view_layer, lc); - BLI_freelinkN(r_lb_children_layers, lc); } - BLI_assert(BLI_listbase_is_empty(r_lb_children_layers)); /* Replace layer collection list with new one. */ - *r_lb_children_layers = lb_new_children_layers; - BLI_assert(BLI_listbase_count(lb_children_collections) == - BLI_listbase_count(r_lb_children_layers)); + layer_resync->layer->layer_collections = new_lb_layer; + BLI_assert(BLI_listbase_count(&layer_resync->collection->children) == + BLI_listbase_count(&new_lb_layer)); + + /* Update bases etc. for objects. */ + layer_collection_objects_sync(view_layer, + layer_resync->layer, + r_lb_new_object_bases, + parent_collection_restrict, + parent_layer_restrict, + parent_local_collections_bits); } /** @@ -933,11 +1181,21 @@ static void layer_collection_sync(ViewLayer *view_layer, */ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer) { + if (no_resync) { + return; + } + if (!scene->master_collection) { /* Happens for old files that don't have versioning applied yet. */ return; } + /* In some cases (from older files) we do have a master collection, yet no matching layer. Create + * the master one here, so that the rest of the code can work as expected. */ + if (BLI_listbase_is_empty(&view_layer->layer_collections)) { + layer_collection_add(&view_layer->layer_collections, scene->master_collection); + } + /* Free cache. */ MEM_SAFE_FREE(view_layer->object_bases_array); @@ -952,21 +1210,29 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer) base->flag_from_collection &= ~g_base_collection_flags; } + /* Generate temporary data representing the old layers hierarchy, and how well it matches the + * new collections hierarchy. */ + BLI_mempool *layer_resync_mempool = BLI_mempool_create( + sizeof(LayerCollectionResync), 1024, 1024, BLI_MEMPOOL_NOP); + LayerCollectionResync *master_layer_resync = layer_collection_resync_create_recurse( + NULL, view_layer->layer_collections.first, layer_resync_mempool); + /* Generate new layer connections and object bases when collections changed. */ - CollectionChild child = {.next = NULL, .prev = NULL, .collection = scene->master_collection}; - const ListBase collections = {.first = &child, .last = &child}; ListBase new_object_bases = {.first = NULL, .last = NULL}; - const short parent_exclude = 0, parent_restrict = 0, parent_layer_restrict = 0; layer_collection_sync(view_layer, - &collections, - &view_layer->layer_collections, + master_layer_resync, + layer_resync_mempool, &new_object_bases, parent_exclude, parent_restrict, parent_layer_restrict, ~(0)); + layer_collection_resync_unused_layers_free(view_layer, master_layer_resync); + BLI_mempool_destroy(layer_resync_mempool); + master_layer_resync = NULL; + /* Any remaining object bases are to be removed. */ LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { if (view_layer->basact == base) { @@ -997,6 +1263,10 @@ void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer) void BKE_scene_collection_sync(const Scene *scene) { + if (no_resync) { + return; + } + LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { BKE_layer_collection_sync(scene, view_layer); } @@ -1004,6 +1274,10 @@ void BKE_scene_collection_sync(const Scene *scene) void BKE_main_collection_sync(const Main *bmain) { + if (no_resync) { + return; + } + /* TODO: if a single collection changed, figure out which * scenes it belongs to and only update those. */ @@ -1018,6 +1292,10 @@ void BKE_main_collection_sync(const Main *bmain) void BKE_main_collection_sync_remap(const Main *bmain) { + if (no_resync) { + return; + } + /* On remapping of object or collection pointers free caches. */ /* TODO: try to make this faster */ @@ -1237,7 +1515,7 @@ void BKE_layer_collection_isolate_global(Scene *scene, bool hide_it = extend && (lc->runtime_flag & LAYER_COLLECTION_VISIBLE_VIEW_LAYER); if (!extend) { - /* Hide all collections . */ + /* Hide all collections. */ LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc_master->layer_collections) { layer_collection_flag_set_recursive(lc_iter, LAYER_COLLECTION_HIDE); } @@ -1320,6 +1598,10 @@ static void layer_collection_local_sync(ViewLayer *view_layer, void BKE_layer_collection_local_sync(ViewLayer *view_layer, const View3D *v3d) { + if (no_resync) { + return; + } + const unsigned short local_collections_uuid = v3d->local_collections_uuid; /* Reset flags and set the bases visible by default. */ @@ -1337,6 +1619,10 @@ void BKE_layer_collection_local_sync(ViewLayer *view_layer, const View3D *v3d) */ void BKE_layer_collection_local_sync_all(const Main *bmain) { + if (no_resync) { + return; + } + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 9871bf5dc83..8e67547b719 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -772,7 +772,11 @@ static void lib_override_library_create_post_process(Main *bmain, /* NOTE: We only care about local IDs here, if a linked object is not instantiated in any way we * do not do anything about it. */ - BKE_main_collection_sync(bmain); + /* We need to use the `_remap` version here as we prevented any LayerCollection resync during the + * whole liboverride resyncing, which involves a lot of ID remapping. + * + * Otherwise, cached Base GHash e.g. can contain invalid stale data. */ + BKE_main_collection_sync_remap(bmain); /* We create a set of all objects referenced into the scene by its hierarchy of collections. * NOTE: This is different that the list of bases, since objects in excluded collections etc. @@ -1013,6 +1017,15 @@ bool BKE_lib_override_library_resync(Main *bmain, ID *id_root_reference = id_root->override_library->reference; + if (id_root_reference->tag & LIB_TAG_MISSING) { + BKE_reportf(reports != NULL ? reports->reports : NULL, + RPT_ERROR, + "impossible to resync data-block %s and its dependencies, as its linked reference " + "is missing", + id_root->name + 2); + return false; + } + BKE_main_relations_create(bmain, 0); LibOverrideGroupTagData data = {.bmain = bmain, .scene = scene, @@ -1722,6 +1735,11 @@ void BKE_lib_override_library_main_resync(Main *bmain, COLLECTION_RESTRICT_RENDER; } + /* Necessary to improve performances, and prevent layers matching override sub-collections to be + * lost when re-syncing the parent override collection. + * Ref. T73411. */ + BKE_layer_collection_resync_forbid(); + int library_indirect_level = lib_override_libraries_index_define(bmain); while (library_indirect_level >= 0) { /* Update overrides from each indirect level separately. */ @@ -1734,6 +1752,8 @@ void BKE_lib_override_library_main_resync(Main *bmain, library_indirect_level--; } + BKE_layer_collection_resync_allow(); + /* Essentially ensures that potentially new overrides of new objects will be instantiated. */ lib_override_library_create_post_process( bmain, scene, view_layer, NULL, NULL, override_resync_residual_storage, true); diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index 655b6d3732c..bb33f5f9f87 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -38,6 +38,7 @@ #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_main.h" +#include "BKE_main_idmap.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -194,6 +195,10 @@ void BKE_main_free(Main *mainvar) BKE_main_relations_free(mainvar); } + if (mainvar->id_map) { + BKE_main_idmap_destroy(mainvar->id_map); + } + BLI_spin_end((SpinLock *)mainvar->lock); MEM_freeN(mainvar->lock); MEM_freeN(mainvar); diff --git a/source/blender/blenkernel/intern/main_idmap.c b/source/blender/blenkernel/intern/main_idmap.c index 1d362db4432..c75365a788d 100644 --- a/source/blender/blenkernel/intern/main_idmap.c +++ b/source/blender/blenkernel/intern/main_idmap.c @@ -21,6 +21,7 @@ #include "BLI_ghash.h" #include "BLI_listbase.h" +#include "BLI_mempool.h" #include "BLI_utildefines.h" #include "DNA_ID.h" @@ -49,17 +50,15 @@ * \{ */ struct IDNameLib_Key { - /** ``ID.name + 2``: without the ID type prefix, since each id type gets its own 'map' */ + /** `ID.name + 2`: without the ID type prefix, since each id type gets its own 'map'. */ const char *name; - /** ``ID.lib``: */ + /** `ID.lib`: */ const Library *lib; }; struct IDNameLib_TypeMap { GHash *map; short id_type; - /* only for storage of keys in the ghash, avoid many single allocs */ - struct IDNameLib_Key *keys; }; /** @@ -71,6 +70,9 @@ struct IDNameLib_Map { struct Main *bmain; struct GSet *valid_id_pointers; int idmap_types; + + /* For storage of keys for the TypeMap ghash, avoids many single allocs. */ + BLI_mempool *type_maps_keys_pool; }; static struct IDNameLib_TypeMap *main_idmap_from_idcode(struct IDNameLib_Map *id_map, @@ -115,6 +117,7 @@ struct IDNameLib_Map *BKE_main_idmap_create(struct Main *bmain, BLI_assert(type_map->id_type != 0); } BLI_assert(index == INDEX_ID_MAX); + id_map->type_maps_keys_pool = NULL; if (idmap_types & MAIN_IDMAP_TYPE_UUID) { ID *id; @@ -148,6 +151,60 @@ struct IDNameLib_Map *BKE_main_idmap_create(struct Main *bmain, return id_map; } +void BKE_main_idmap_insert_id(struct IDNameLib_Map *id_map, ID *id) +{ + if (id_map->idmap_types & MAIN_IDMAP_TYPE_NAME) { + const short id_type = GS(id->name); + struct IDNameLib_TypeMap *type_map = main_idmap_from_idcode(id_map, id_type); + + /* No need to do anything if map has not been lazily created yet. */ + if (LIKELY(type_map != NULL) && type_map->map != NULL) { + BLI_assert(id_map->type_maps_keys_pool != NULL); + + struct IDNameLib_Key *key = BLI_mempool_alloc(id_map->type_maps_keys_pool); + key->name = id->name + 2; + key->lib = id->lib; + BLI_ghash_insert(type_map->map, key, id); + } + } + + if (id_map->idmap_types & MAIN_IDMAP_TYPE_UUID) { + BLI_assert(id_map->uuid_map != NULL); + BLI_assert(id->session_uuid != MAIN_ID_SESSION_UUID_UNSET); + void **id_ptr_v; + const bool existing_key = BLI_ghash_ensure_p( + id_map->uuid_map, POINTER_FROM_UINT(id->session_uuid), &id_ptr_v); + BLI_assert(existing_key == false); + UNUSED_VARS_NDEBUG(existing_key); + + *id_ptr_v = id; + } +} + +void BKE_main_idmap_remove_id(struct IDNameLib_Map *id_map, ID *id) +{ + if (id_map->idmap_types & MAIN_IDMAP_TYPE_NAME) { + const short id_type = GS(id->name); + struct IDNameLib_TypeMap *type_map = main_idmap_from_idcode(id_map, id_type); + + /* No need to do anything if map has not been lazily created yet. */ + if (LIKELY(type_map != NULL) && type_map->map != NULL) { + BLI_assert(id_map->type_maps_keys_pool != NULL); + + /* NOTE: We cannot free the key from the MemPool here, would need new API from GHash to also + * retrieve key pointer. Not a big deal for now */ + BLI_ghash_remove(type_map->map, &(struct IDNameLib_Key){id->name + 2, id->lib}, NULL, NULL); + } + } + + if (id_map->idmap_types & MAIN_IDMAP_TYPE_UUID) { + BLI_assert(id_map->uuid_map != NULL); + BLI_assert(id->session_uuid != MAIN_ID_SESSION_UUID_UNSET); + + BLI_ghash_remove(id_map->uuid_map, POINTER_FROM_UINT(id->session_uuid), NULL, NULL); + } +} + struct Main *BKE_main_idmap_main_get(struct IDNameLib_Map *id_map) { return id_map->bmain; @@ -181,20 +238,17 @@ ID *BKE_main_idmap_lookup_name(struct IDNameLib_Map *id_map, return NULL; } - /* lazy init */ + /* Lazy init. */ if (type_map->map == NULL) { - ListBase *lb = which_libbase(id_map->bmain, id_type); - const int lb_len = BLI_listbase_count(lb); - if (lb_len == 0) { - return NULL; + if (id_map->type_maps_keys_pool == NULL) { + id_map->type_maps_keys_pool = BLI_mempool_create( + sizeof(struct IDNameLib_Key), 1024, 1024, BLI_MEMPOOL_NOP); } - type_map->map = BLI_ghash_new_ex(idkey_hash, idkey_cmp, __func__, lb_len); - type_map->keys = MEM_mallocN(sizeof(struct IDNameLib_Key) * lb_len, __func__); - - GHash *map = type_map->map; - struct IDNameLib_Key *key = type_map->keys; - for (ID *id = lb->first; id; id = id->next, key++) { + GHash *map = type_map->map = BLI_ghash_new(idkey_hash, idkey_cmp, __func__); + ListBase *lb = which_libbase(id_map->bmain, id_type); + for (ID *id = lb->first; id; id = id->next) { + struct IDNameLib_Key *key = BLI_mempool_alloc(id_map->type_maps_keys_pool); key->name = id->name + 2; key->lib = id->lib; BLI_ghash_insert(map, key, id); @@ -235,14 +289,19 @@ void BKE_main_idmap_destroy(struct IDNameLib_Map *id_map) if (type_map->map) { BLI_ghash_free(type_map->map, NULL, NULL); type_map->map = NULL; - MEM_freeN(type_map->keys); } } + if (id_map->type_maps_keys_pool != NULL) { + BLI_mempool_destroy(id_map->type_maps_keys_pool); + id_map->type_maps_keys_pool = NULL; + } } if (id_map->idmap_types & MAIN_IDMAP_TYPE_UUID) { BLI_ghash_free(id_map->uuid_map, NULL, NULL); } + BLI_assert(id_map->type_maps_keys_pool == NULL); + if (id_map->valid_id_pointers != NULL) { BLI_gset_free(id_map->valid_id_pointers, NULL); } diff --git a/source/blender/blenkernel/intern/mball_tessellate.c b/source/blender/blenkernel/intern/mball_tessellate.c index 760febaca91..9dd583b4c6b 100644 --- a/source/blender/blenkernel/intern/mball_tessellate.c +++ b/source/blender/blenkernel/intern/mball_tessellate.c @@ -305,7 +305,7 @@ static void build_bvh_spatial(PROCESS *process, /** * Computes density from given metaball at given position. - * Metaball equation is: ``(1 - r^2 / R^2)^3 * s`` + * Metaball equation is: `(1 - r^2 / R^2)^3 * s` * * r = distance from center * R = metaball radius diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index c8bf485dade..6bfd62ee846 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -1881,6 +1881,8 @@ void BKE_mesh_calc_normals_split_ex(Mesh *mesh, MLoopNorSpaceArray *r_lnors_spac } mesh->runtime.cd_dirty_vert &= ~CD_MASK_NORMAL; + mesh->runtime.cd_dirty_poly &= ~CD_MASK_NORMAL; + mesh->runtime.cd_dirty_loop &= ~CD_MASK_NORMAL; } void BKE_mesh_calc_normals_split(Mesh *mesh) diff --git a/source/blender/blenkernel/intern/mesh_boolean_convert.cc b/source/blender/blenkernel/intern/mesh_boolean_convert.cc index 798d9562150..3086f117707 100644 --- a/source/blender/blenkernel/intern/mesh_boolean_convert.cc +++ b/source/blender/blenkernel/intern/mesh_boolean_convert.cc @@ -97,6 +97,8 @@ class MeshesToIMeshInfo { /* Transformation matrix to transform a coordinate in the corresponding * Mesh to the local space of the first Mesh. */ Array<float4x4> to_target_transform; + /* For each input mesh, whether or not their transform is negative. */ + Array<bool> has_negative_transform; /* For each input mesh, how to remap the material slot numbers to * the material slots in the first mesh. */ Span<Array<short>> material_remaps; @@ -277,6 +279,7 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes, r_info->mesh_edge_offset = Array<int>(nmeshes); r_info->mesh_poly_offset = Array<int>(nmeshes); r_info->to_target_transform = Array<float4x4>(nmeshes); + r_info->has_negative_transform = Array<bool>(nmeshes); r_info->material_remaps = material_remaps; int v = 0; int e = 0; @@ -309,6 +312,12 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes, const float4x4 objn_mat = (obmats[mi] == nullptr) ? float4x4::identity() : clean_obmat(*obmats[mi]); r_info->to_target_transform[mi] = inv_target_mat * objn_mat; + r_info->has_negative_transform[mi] = objn_mat.is_negative(); + + /* All meshes 1 and up will be transformed into the local space of operand 0. + * Historical behavior of the modifier has been to flip the faces of any meshes + * that would have a negative transform if you do that. */ + bool need_face_flip = r_info->has_negative_transform[mi] != r_info->has_negative_transform[0]; Vector<Vert *> verts(me->totvert); Span<MVert> mverts = Span(me->mvert, me->totvert); @@ -346,14 +355,21 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes, for (const MPoly &poly : Span(me->mpoly, me->totpoly)) { int flen = poly.totloop; - face_vert.clear(); - face_edge_orig.clear(); + face_vert.resize(flen); + face_edge_orig.resize(flen); const MLoop *l = &me->mloop[poly.loopstart]; for (int i = 0; i < flen; ++i) { int mverti = r_info->mesh_vert_offset[mi] + l->v; const Vert *fv = r_info->mesh_to_imesh_vert[mverti]; - face_vert.append(fv); - face_edge_orig.append(e + l->e); + if (need_face_flip) { + face_vert[flen - i - 1] = fv; + int iedge = i < flen - 1 ? flen - i - 2 : flen - 1; + face_edge_orig[iedge] = e + l->e; + } + else { + face_vert[i] = fv; + face_edge_orig[i] = e + l->e; + } ++l; } r_info->mesh_to_imesh_face[f] = arena.add_face(face_vert, f, face_edge_orig); diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index e777eb7ffe9..0e4fe91e577 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -212,54 +212,19 @@ static void make_edges_mdata_extend( } /* Initialize mverts, medges and, faces for converting nurbs to mesh and derived mesh */ -/* return non-zero on error */ -int BKE_mesh_nurbs_to_mdata(Object *ob, - MVert **r_allvert, - int *r_totvert, - MEdge **r_alledge, - int *r_totedge, - MLoop **r_allloop, - MPoly **r_allpoly, - int *r_totloop, - int *r_totpoly) -{ - ListBase disp = {NULL, NULL}; - - if (ob->runtime.curve_cache) { - disp = ob->runtime.curve_cache->disp; - } - - return BKE_mesh_nurbs_displist_to_mdata(ob, - &disp, - r_allvert, - r_totvert, - r_alledge, - r_totedge, - r_allloop, - r_allpoly, - NULL, - r_totloop, - r_totpoly); -} - -/* BMESH: this doesn't calculate all edges from polygons, - * only free standing edges are calculated */ - -/* Initialize mverts, medges and, faces for converting nurbs to mesh and derived mesh */ /* use specified dispbase */ -int BKE_mesh_nurbs_displist_to_mdata(const Object *ob, - const ListBase *dispbase, - MVert **r_allvert, - int *r_totvert, - MEdge **r_alledge, - int *r_totedge, - MLoop **r_allloop, - MPoly **r_allpoly, - MLoopUV **r_alluv, - int *r_totloop, - int *r_totpoly) +static int mesh_nurbs_displist_to_mdata(const Curve *cu, + const ListBase *dispbase, + MVert **r_allvert, + int *r_totvert, + MEdge **r_alledge, + int *r_totedge, + MLoop **r_allloop, + MPoly **r_allpoly, + MLoopUV **r_alluv, + int *r_totloop, + int *r_totpoly) { - const Curve *cu = ob->data; MVert *mvert; MPoly *mpoly; MLoop *mloop; @@ -272,7 +237,7 @@ int BKE_mesh_nurbs_displist_to_mdata(const Object *ob, /* 2d polys are filled with DL_INDEX3 displists */ (CU_DO_2DFILL(cu) == false) || /* surf polys are never filled */ - (ob->type == OB_SURF)); + BKE_curve_type_get(cu) == OB_SURF); /* count */ LISTBASE_FOREACH (const DispList *, dl, dispbase) { @@ -527,17 +492,17 @@ Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase * MLoopUV *alluv = NULL; int totvert, totedge, totloop, totpoly; - if (BKE_mesh_nurbs_displist_to_mdata(ob, - dispbase, - &allvert, - &totvert, - &alledge, - &totedge, - &allloop, - &allpoly, - &alluv, - &totloop, - &totpoly) != 0) { + if (mesh_nurbs_displist_to_mdata(ob->data, + dispbase, + &allvert, + &totvert, + &alledge, + &totedge, + &allloop, + &allpoly, + &alluv, + &totloop, + &totpoly) != 0) { /* Error initializing mdata. This often happens when curve is empty */ return BKE_mesh_new_nomain(0, 0, 0, 0, 0); } @@ -571,7 +536,7 @@ Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase * return mesh; } -Mesh *BKE_mesh_new_nomain_from_curve(Object *ob) +Mesh *BKE_mesh_new_nomain_from_curve(const Object *ob) { ListBase disp = {NULL, NULL}; @@ -589,7 +554,6 @@ void BKE_mesh_from_nurbs_displist( Object *ob1; Mesh *me_eval = (Mesh *)ob->runtime.data_eval; Mesh *me; - Curve *cu; MVert *allvert = NULL; MEdge *alledge = NULL; MLoop *allloop = NULL; @@ -597,20 +561,20 @@ void BKE_mesh_from_nurbs_displist( MPoly *allpoly = NULL; int totvert, totedge, totloop, totpoly; - cu = ob->data; + Curve *cu = ob->data; if (me_eval == NULL) { - if (BKE_mesh_nurbs_displist_to_mdata(ob, - dispbase, - &allvert, - &totvert, - &alledge, - &totedge, - &allloop, - &allpoly, - &alluv, - &totloop, - &totpoly) != 0) { + if (mesh_nurbs_displist_to_mdata(cu, + dispbase, + &allvert, + &totvert, + &alledge, + &totedge, + &allloop, + &allpoly, + &alluv, + &totloop, + &totpoly) != 0) { /* Error initializing */ return; } @@ -702,18 +666,6 @@ void BKE_mesh_from_nurbs_displist( } } -void BKE_mesh_from_nurbs(Main *bmain, Object *ob) -{ - Curve *cu = (Curve *)ob->data; - ListBase disp = {NULL, NULL}; - - if (ob->runtime.curve_cache) { - disp = ob->runtime.curve_cache->disp; - } - - BKE_mesh_from_nurbs_displist(bmain, ob, &disp, cu->id.name, false); -} - typedef struct EdgeLink { struct EdgeLink *next, *prev; void *edge; @@ -1152,8 +1104,8 @@ static Mesh *mesh_new_from_curve_type_object(Object *object) BKE_mesh_from_nurbs_displist( NULL, temp_object, &temp_object->runtime.curve_cache->disp, curve->id.name + 2, true); - /* BKE_mesh_from_nurbs changes the type to a mesh, check it worked. If it didn't the curve did - * not have any segments or otherwise would have generated an empty mesh. */ + /* BKE_mesh_from_nurbs_displist changes the type to a mesh, check it worked. If it didn't + * the curve did not have any segments or otherwise would have generated an empty mesh. */ if (temp_object->type != OB_MESH) { BKE_id_free(NULL, temp_object->data); BKE_id_free(NULL, temp_object); diff --git a/source/blender/blenkernel/intern/mesh_iterators.c b/source/blender/blenkernel/intern/mesh_iterators.c index 5ecf5ae316d..7a776b0ecb7 100644 --- a/source/blender/blenkernel/intern/mesh_iterators.c +++ b/source/blender/blenkernel/intern/mesh_iterators.c @@ -95,9 +95,14 @@ void BKE_mesh_foreach_mapped_vert(Mesh *mesh, } } -/* Copied from cdDM_foreachMappedEdge */ +/** + * Copied from #cdDM_foreachMappedEdge. + * \param tot_edges: Number of original edges. Used to avoid calling the callback with invalid + * edge indices. + */ void BKE_mesh_foreach_mapped_edge( Mesh *mesh, + const int tot_edges, void (*func)(void *userData, int index, const float v0co[3], const float v1co[3]), void *userData) { @@ -138,7 +143,7 @@ void BKE_mesh_foreach_mapped_edge( func(userData, orig, mv[med->v1].co, mv[med->v2].co); } } - else { + else if (mesh->totedge == tot_edges) { for (int i = 0; i < mesh->totedge; i++, med++) { func(userData, i, mv[med->v1].co, mv[med->v2].co); } diff --git a/source/blender/blenkernel/intern/mesh_mapping.c b/source/blender/blenkernel/intern/mesh_mapping.c index ca6c60557a6..d28bb9c0744 100644 --- a/source/blender/blenkernel/intern/mesh_mapping.c +++ b/source/blender/blenkernel/intern/mesh_mapping.c @@ -539,8 +539,8 @@ void BKE_mesh_edge_poly_map_create(MeshElemMap **r_map, * \param totfinal: The size of \a final_origindex * \param final_origindex: The size of the final array. * - * \note ``totsource`` could be ``totpoly``, - * ``totfinal`` could be ``tottessface`` and ``final_origindex`` its ORIGINDEX customdata. + * \note `totsource` could be `totpoly`, + * `totfinal` could be `tottessface` and `final_origindex` its ORIGINDEX custom-data. * This would allow an MPoly to loop over its tessfaces. */ void BKE_mesh_origindex_map_create(MeshElemMap **r_map, diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc index 2fe132fc684..f496d6eada1 100644 --- a/source/blender/blenkernel/intern/mesh_normals.cc +++ b/source/blender/blenkernel/intern/mesh_normals.cc @@ -530,6 +530,36 @@ void BKE_lnor_spacearr_init(MLoopNorSpaceArray *lnors_spacearr, lnors_spacearr->data_type = data_type; } +/** + * Utility for multi-threaded calculation that ensures + * `lnors_spacearr_tls` doesn't share memory with `lnors_spacearr` + * that would cause it not to be thread safe. + * + * \note This works as long as threads never operate on the same loops at once. + */ +void BKE_lnor_spacearr_tls_init(MLoopNorSpaceArray *lnors_spacearr, + MLoopNorSpaceArray *lnors_spacearr_tls) +{ + *lnors_spacearr_tls = *lnors_spacearr; + lnors_spacearr_tls->mem = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); +} + +/** + * Utility for multi-threaded calculation + * that merges `lnors_spacearr_tls` into `lnors_spacearr`. + */ +void BKE_lnor_spacearr_tls_join(MLoopNorSpaceArray *lnors_spacearr, + MLoopNorSpaceArray *lnors_spacearr_tls) +{ + BLI_assert(lnors_spacearr->data_type == lnors_spacearr_tls->data_type); + BLI_assert(lnors_spacearr->mem != lnors_spacearr_tls->mem); + lnors_spacearr->num_spaces += lnors_spacearr_tls->num_spaces; + BLI_memarena_merge(lnors_spacearr->mem, lnors_spacearr_tls->mem); + BLI_memarena_free(lnors_spacearr_tls->mem); + lnors_spacearr_tls->mem = nullptr; + BKE_lnor_spacearr_clear(lnors_spacearr_tls); +} + void BKE_lnor_spacearr_clear(MLoopNorSpaceArray *lnors_spacearr) { lnors_spacearr->num_spaces = 0; diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 85d30fc8c8b..9888e23a7bd 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -1212,10 +1212,12 @@ static void update_typeinfo(Main *bmain, FOREACH_NODETREE_END; } -/* Try to initialize all typeinfo in a node tree. - * NB: In general undefined typeinfo is a perfectly valid case, +/** + * Try to initialize all type-info in a node tree. + * + * \note In general undefined type-info is a perfectly valid case, * the type may just be registered later. - * In that case the update_typeinfo function will set typeinfo on registration + * In that case the update_typeinfo function will set type-info on registration * and do necessary updates. */ void ntreeSetTypes(const struct bContext *C, bNodeTree *ntree) @@ -2502,6 +2504,22 @@ bool nodeLinkIsHidden(const bNodeLink *link) return nodeSocketIsHidden(link->fromsock) || nodeSocketIsHidden(link->tosock); } +/* Adjust the indices of links connected to the given multi input socket after deleting the link at + * `deleted_index`. This function also works if the link has not yet been deleted. */ +static void adjust_multi_input_indices_after_removed_link(bNodeTree *ntree, + bNodeSocket *sock, + int deleted_index) +{ + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + /* We only need to adjust those with a greater index, because the others will have the same + * index. */ + if (link->tosock != sock || link->multi_input_socket_index <= deleted_index) { + continue; + } + link->multi_input_socket_index -= 1; + } +} + void nodeInternalRelink(bNodeTree *ntree, bNode *node) { /* store link pointers in output sockets, for efficient lookup */ @@ -2535,10 +2553,18 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node) ntree->update |= NTREE_UPDATE_LINKS; } else { + if (link->tosock->flag & SOCK_MULTI_INPUT) { + adjust_multi_input_indices_after_removed_link( + ntree, link->tosock, link->multi_input_socket_index); + } nodeRemLink(ntree, link); } } else { + if (link->tosock->flag & SOCK_MULTI_INPUT) { + adjust_multi_input_indices_after_removed_link( + ntree, link->tosock, link->multi_input_socket_index); + }; nodeRemLink(ntree, link); } } @@ -2989,6 +3015,11 @@ void nodeUnlinkNode(bNodeTree *ntree, bNode *node) } if (lb) { + /* Only bother adjusting if the socket is not on the node we're deleting. */ + if (link->tonode != node && link->tosock->flag & SOCK_MULTI_INPUT) { + adjust_multi_input_indices_after_removed_link( + ntree, link->tosock, link->multi_input_socket_index); + } LISTBASE_FOREACH (bNodeSocket *, sock, lb) { if (link->fromsock == sock || link->tosock == sock) { nodeRemLink(ntree, link); @@ -5117,9 +5148,11 @@ static void registerGeometryNodes() register_node_type_geo_curve_primitive_star(); register_node_type_geo_curve_resample(); register_node_type_geo_curve_reverse(); + register_node_type_geo_curve_set_handles(); register_node_type_geo_curve_subdivide(); register_node_type_geo_curve_to_mesh(); register_node_type_geo_curve_to_points(); + register_node_type_geo_curve_trim(); register_node_type_geo_delete_geometry(); register_node_type_geo_edge_split(); register_node_type_geo_input_material(); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 941db80b76c..89de37d6e4b 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1653,7 +1653,7 @@ static void object_update_from_subsurf_ccg(Object *object) * * All this is defeating all the designs we need to follow to allow safe * threaded evaluation, but this is as good as we can make it within the - * current sculpt//evaluated mesh design. This is also how we've survived + * current sculpt/evaluated mesh design. This is also how we've survived * with old DerivedMesh based solutions. So, while this is all wrong and * needs reconsideration, doesn't seem to be a big stopper for real * production artists. diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 7cdea14e9bd..7e15ac5de5d 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -388,31 +388,12 @@ void BKE_object_batch_cache_dirty_tag(Object *ob) BKE_object_data_batch_cache_dirty_tag(ob->data); } -void BKE_object_data_eval_batch_cache_dirty_tag(Depsgraph *depsgraph, ID *object_data) -{ - DEG_debug_print_eval(depsgraph, __func__, object_data->name, object_data); - BKE_object_data_batch_cache_dirty_tag(object_data); -} - -void BKE_object_data_eval_batch_cache_deform_tag(Depsgraph *depsgraph, ID *object_data) -{ - DEG_debug_print_eval(depsgraph, __func__, object_data->name, object_data); - switch (GS(object_data->name)) { - case ID_ME: - BKE_mesh_batch_cache_dirty_tag((Mesh *)object_data, BKE_MESH_BATCH_DIRTY_DEFORM); - break; - default: - /* Only mesh is currently supported. Fallback to dirty all for other datablocks types. */ - BKE_object_data_batch_cache_dirty_tag(object_data); - break; - } -} - void BKE_object_eval_uber_data(Depsgraph *depsgraph, Scene *scene, Object *ob) { DEG_debug_print_eval(depsgraph, __func__, ob->id.name, ob); BLI_assert(ob->type != OB_ARMATURE); BKE_object_handle_data_update(depsgraph, scene, ob); + BKE_object_batch_cache_dirty_tag(ob); } void BKE_object_eval_ptcache_reset(Depsgraph *depsgraph, Scene *scene, Object *object) diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index c4e4bd732cd..800dbaaa1af 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -887,7 +887,7 @@ static int palettecolor_compare_luminance(const void *a1, const void *a2) void BKE_palette_sort_hsv(tPaletteColorHSV *color_array, const int totcol) { - /* Sort by Hue , Saturation and Value. */ + /* Sort by Hue, Saturation and Value. */ qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_hsv); } diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index db1fbb56125..f2f3c5d4ca6 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -4418,12 +4418,12 @@ void psys_get_texture( if ((event & mtex->mapto) & PAMAP_TIME) { /* the first time has to set the base value for time regardless of blend mode */ - if ((setvars & MAP_PA_TIME) == 0) { + if ((setvars & PAMAP_TIME) == 0) { int flip = (mtex->timefac < 0.0f); float timefac = fabsf(mtex->timefac); ptex->time *= 1.0f - timefac; ptex->time += timefac * ((flip) ? 1.0f - value : value); - setvars |= MAP_PA_TIME; + setvars |= PAMAP_TIME; } else { ptex->time = texture_value_blend(def, ptex->time, value, mtex->timefac, blend); diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c index e2b082e7996..c7f40d95109 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.c +++ b/source/blender/blenkernel/intern/pbvh_bmesh.c @@ -1203,13 +1203,13 @@ void BKE_pbvh_recalc_bmesh_boundary(PBVH *pbvh) } /* Build a PBVH from a BMesh */ -ATTR_NO_OPT void BKE_pbvh_build_bmesh(PBVH *pbvh, - BMesh *bm, - bool smooth_shading, - BMLog *log, - const int cd_vert_node_offset, - const int cd_face_node_offset, - const int cd_dyn_vert) +void BKE_pbvh_build_bmesh(PBVH *pbvh, + BMesh *bm, + bool smooth_shading, + BMLog *log, + const int cd_vert_node_offset, + const int cd_face_node_offset, + const int cd_dyn_vert) { pbvh->cd_vert_node_offset = cd_vert_node_offset; pbvh->cd_face_node_offset = cd_face_node_offset; @@ -1594,7 +1594,7 @@ BLI_INLINE void pbvh_tribuf_add_edge(PBVHTriBuf *tribuf, int v1, int v2) * (currently just raycast), store the node's triangles and vertices. * * Skips triangles that are hidden. */ -ATTR_NO_OPT bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) +bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) { BMesh *bm = pbvh->bm; diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 318acfc3892..dbc7ce9902a 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -231,7 +231,6 @@ static void scene_init_data(ID *id) /* Sequencer */ scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init(); - scene->toolsettings->snap_flag |= SCE_SNAP_SEQ; for (size_t i = 0; i < ARRAY_SIZE(scene->orientation_slots); i++) { scene->orientation_slots[i].index_custom = -1; @@ -2105,7 +2104,7 @@ Object *BKE_scene_object_find_by_name(const Scene *scene, const char *name) /** * Sets the active scene, mainly used when running in background mode - * (``--scene`` command line argument). + * (`--scene` command line argument). * This is also called to set the scene directly, bypassing windowing code. * Otherwise #WM_window_set_active_scene is used when changing scenes by the user. */ @@ -3243,9 +3242,9 @@ void BKE_scene_multiview_filepath_get(SceneRenderView *srv, const char *filepath } /** - * When multiview is not used the filepath is as usual (e.g., ``Image.jpg``). + * When multiview is not used the filepath is as usual (e.g., `Image.jpg`). * When multiview is on, even if only one view is enabled the view is incorporated - * into the file name (e.g., ``Image_L.jpg``). That allows for the user to re-render + * into the file name (e.g., `Image_L.jpg`). That allows for the user to re-render * individual views. */ void BKE_scene_multiview_view_filepath_get(const RenderData *rd, diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index 6234cdf87e2..a7caae967f6 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -410,7 +410,7 @@ Spline::LookupResult Spline::lookup_evaluated_length(const float length) const const float *offset = std::lower_bound(lengths.begin(), lengths.end(), length); const int index = offset - lengths.begin(); - const int next_index = (index == this->size() - 1) ? 0 : index + 1; + const int next_index = (index == this->evaluated_points_size() - 1) ? 0 : index + 1; const float previous_length = (index == 0) ? 0.0f : lengths[index - 1]; const float factor = (length - previous_length) / (lengths[index] - previous_length); diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index cff2c5cc562..a1b45c2ac7d 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -423,7 +423,7 @@ static void set_subsurf_legacy_uv(CCGSubSurf *ss, DerivedMesh *dm, DerivedMesh * /* get some info from CCGSubSurf */ totface = ccgSubSurf_getNumFaces(uvss); - /* edgeSize = ccgSubSurf_getEdgeSize(uvss); */ /*UNUSED*/ + // edgeSize = ccgSubSurf_getEdgeSize(uvss); /* UNUSED */ gridSize = ccgSubSurf_getGridSize(uvss); gridFaces = gridSize - 1; diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c index d9f02ce4c75..4581d410444 100644 --- a/source/blender/blenkernel/intern/unit.c +++ b/source/blender/blenkernel/intern/unit.c @@ -1303,7 +1303,7 @@ const char *BKE_unit_identifier_get(const void *usys_pt, int index) { const bUnitDef *unit = ((const bUnitCollection *)usys_pt)->units + index; if (unit->identifier == NULL) { - BLI_assert(false && "identifier for this unit is not specified yet"); + BLI_assert_msg(0, "identifier for this unit is not specified yet"); } return unit->identifier; } diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index 5cac149c503..059dc68b1dc 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -58,7 +58,7 @@ static void workspace_init_data(ID *id) { WorkSpace *workspace = (WorkSpace *)id; - BKE_asset_library_reference_init_default(&workspace->active_asset_library); + BKE_asset_library_reference_init_default(&workspace->asset_library); } static void workspace_free_data(ID *id) diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 560ae30967f..9b3103a638b 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -1747,9 +1747,10 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset) rd->ffcodecdata.type = FFMPEG_MPEG2; rd->ffcodecdata.video_bitrate = 6000; - /* Don't set resolution, see T21351. - * rd->xsch = 720; - * rd->ysch = isntsc ? 480 : 576; */ +# if 0 /* Don't set resolution, see T21351. */ + rd->xsch = 720; + rd->ysch = isntsc ? 480 : 576; +# endif rd->ffcodecdata.gop_size = isntsc ? 18 : 15; rd->ffcodecdata.rc_max_rate = 9000; diff --git a/source/blender/blenlib/BLI_array.h b/source/blender/blenlib/BLI_array.h index e40a79dad21..6bf29a6168f 100644 --- a/source/blender/blenlib/BLI_array.h +++ b/source/blender/blenlib/BLI_array.h @@ -58,7 +58,7 @@ void _bli_array_grow_func(void **arr_p, /** \name Public defines * \{ */ -/** use ``sizeof(*(arr))`` to ensure the array exists and is an array */ +/** use `sizeof(*(arr))` to ensure the array exists and is an array */ #define BLI_array_declare(arr) \ int _##arr##_len = ((void)(sizeof(*(arr))), 0); \ void *_##arr##_static = NULL diff --git a/source/blender/blenlib/BLI_color.hh b/source/blender/blenlib/BLI_color.hh index 76dfcf0b57d..d93bd7f6f76 100644 --- a/source/blender/blenlib/BLI_color.hh +++ b/source/blender/blenlib/BLI_color.hh @@ -34,11 +34,11 @@ namespace blender { * Usage: * * Convert a theme byte color to a linearrgb premultiplied. - * ``` + * \code{.cc} * ColorTheme4b theme_color; * ColorSceneLinear4f<eAlpha::Premultiplied> linearrgb_color = * BLI_color_convert_to_scene_linear(theme_color).premultiply_alpha(); - * ``` + * \endcode * * The API is structured to make most use of inlining. Most notable are space * conversions done via `BLI_color_convert_to*` functions. diff --git a/source/blender/blenlib/BLI_compiler_typecheck.h b/source/blender/blenlib/BLI_compiler_typecheck.h index d9c2bfc1d58..18be2190c1b 100644 --- a/source/blender/blenlib/BLI_compiler_typecheck.h +++ b/source/blender/blenlib/BLI_compiler_typecheck.h @@ -86,7 +86,7 @@ /** * CHECK_TYPE_ANY: handy macro, eg: - * ``CHECK_TYPE_ANY(var, Foo *, Bar *, Baz *)`` + * `CHECK_TYPE_ANY(var, Foo *, Bar *, Baz *)` * * excuse ridiculously long generated args. * \code{.py} diff --git a/source/blender/blenlib/BLI_delaunay_2d.h b/source/blender/blenlib/BLI_delaunay_2d.h index d42bd6af637..658dcadadce 100644 --- a/source/blender/blenlib/BLI_delaunay_2d.h +++ b/source/blender/blenlib/BLI_delaunay_2d.h @@ -110,6 +110,10 @@ extern "C" { * If zero is supplied for epsilon, an internal value of 1e-8 used * instead, since this code will not work correctly if it is not allowed * to merge "too near" vertices. + * + * Normally the output will contain mappings from outputs to inputs. + * If this is not needed, set need_ids to false and the execution may be much + * faster in some circumstances. */ typedef struct CDT_input { int verts_len; @@ -121,6 +125,7 @@ typedef struct CDT_input { int *faces_start_table; int *faces_len_table; float epsilon; + bool need_ids; } CDT_input; /** @@ -140,6 +145,7 @@ typedef struct CDT_input { * a run-together array and a "start" and "len" extra array, * similar triples are used to represent the output to input * mapping of vertices, edges, and faces. + * These are only set if need_ids is true in the input. * * Those triples are: * - verts_orig, verts_orig_start_table, verts_orig_len_table @@ -236,6 +242,7 @@ template<typename Arith_t> class CDT_input { Array<std::pair<int, int>> edge; Array<Vector<int>> face; Arith_t epsilon{0}; + bool need_ids{true}; }; template<typename Arith_t> class CDT_result { @@ -243,6 +250,7 @@ template<typename Arith_t> class CDT_result { Array<vec2<Arith_t>> vert; Array<std::pair<int, int>> edge; Array<Vector<int>> face; + /* The orig vectors are only populated if the need_ids input field is true. */ /** For each output vert, which input verts correspond to it? */ Array<Vector<int>> vert_orig; /** diff --git a/source/blender/blenlib/BLI_dlrbTree.h b/source/blender/blenlib/BLI_dlrbTree.h index 8c20e3d3988..03aab8d2895 100644 --- a/source/blender/blenlib/BLI_dlrbTree.h +++ b/source/blender/blenlib/BLI_dlrbTree.h @@ -111,20 +111,22 @@ void BLI_dlrbTree_linkedlist_sync(DLRBT_Tree *tree); /* Searching ------------------------------------ */ /* Find the node which matches or is the closest to the requested node */ -DLRBT_Node *BLI_dlrbTree_search(DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data); +DLRBT_Node *BLI_dlrbTree_search(const DLRBT_Tree *tree, + DLRBT_Comparator_FP cmp_cb, + void *search_data); /* Find the node which exactly matches the required data */ -DLRBT_Node *BLI_dlrbTree_search_exact(DLRBT_Tree *tree, +DLRBT_Node *BLI_dlrbTree_search_exact(const DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data); /* Find the node which occurs immediately before the best matching node */ -DLRBT_Node *BLI_dlrbTree_search_prev(DLRBT_Tree *tree, +DLRBT_Node *BLI_dlrbTree_search_prev(const DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data); /* Find the node which occurs immediately after the best matching node */ -DLRBT_Node *BLI_dlrbTree_search_next(DLRBT_Tree *tree, +DLRBT_Node *BLI_dlrbTree_search_next(const DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data); @@ -137,7 +139,8 @@ short BLI_dlrbTree_contains(DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void * */ /* Add the given data to the tree, and return the node added */ -// NOTE: for duplicates, the update_cb is called (if available), and the existing node is returned +/* NOTE: for duplicates, the update_cb is called (if available), + * and the existing node is returned. */ DLRBT_Node *BLI_dlrbTree_add(DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, DLRBT_NAlloc_FP new_cb, @@ -145,7 +148,7 @@ DLRBT_Node *BLI_dlrbTree_add(DLRBT_Tree *tree, void *data); /* Remove the given element from the tree and balance again */ -// FIXME: this is not implemented yet... +/* FIXME: this is not implemented yet... */ // void BLI_dlrbTree_remove(DLRBT_Tree *tree, DLRBT_Node *node); /* Node Operations (Manual) --------------------- */ diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh index 396b0b1bd21..347ce2caa34 100644 --- a/source/blender/blenlib/BLI_float4x4.hh +++ b/source/blender/blenlib/BLI_float4x4.hh @@ -51,8 +51,14 @@ struct float4x4 { { BLI_ASSERT_UNIT_V3(forward); BLI_ASSERT_UNIT_V3(up); + + /* Negate the cross product so that the resulting matrix has determinant 1 (instead of -1). + * Without the negation, the result would be a so called improper rotation. That means it + * contains a reflection. Such an improper rotation matrix could not be converted to another + * representation of a rotation such as euler angles. */ + const float3 cross = -float3::cross(forward, up); + float4x4 matrix; - const float3 cross = float3::cross(forward, up); matrix.values[0][0] = forward.x; matrix.values[1][0] = cross.x; matrix.values[2][0] = up.x; @@ -212,6 +218,11 @@ struct float4x4 { return result; } + bool is_negative() const + { + return is_negative_m4(ptr()); + } + uint64_t hash() const { uint64_t h = 435109; diff --git a/source/blender/blenlib/BLI_ghash.h b/source/blender/blenlib/BLI_ghash.h index 4386f52140d..d8de0ead1e2 100644 --- a/source/blender/blenlib/BLI_ghash.h +++ b/source/blender/blenlib/BLI_ghash.h @@ -77,7 +77,7 @@ enum { /* -------------------------------------------------------------------- */ /** \name GHash API * - * Defined in ``BLI_ghash.c`` + * Defined in `BLI_ghash.c` * \{ */ GHash *BLI_ghash_new_ex(GHashHashFP hashfp, @@ -376,11 +376,11 @@ double BLI_gset_calc_quality(GSet *gs); /* -------------------------------------------------------------------- */ /** \name GHash/GSet Utils * - * Defined in ``BLI_ghash_utils.c`` + * Defined in `BLI_ghash_utils.c` * \{ */ /** - * Callbacks for GHash (``BLI_ghashutil_``) + * Callbacks for GHash (`BLI_ghashutil_`) * * \note '_p' suffix denotes void pointer arg, * so we can have functions that take correctly typed args too. diff --git a/source/blender/blenlib/BLI_link_utils.h b/source/blender/blenlib/BLI_link_utils.h index e1e4a8dbd4a..7aa7300da16 100644 --- a/source/blender/blenlib/BLI_link_utils.h +++ b/source/blender/blenlib/BLI_link_utils.h @@ -20,7 +20,7 @@ * \ingroup bli * \brief Single link-list utility macros. (header only api). * - * Use this api when the structure defines its own ``next`` pointer + * Use this api when the structure defines its own `next` pointer * and a double linked list such as #ListBase isn't needed. */ diff --git a/source/blender/blenlib/BLI_linklist_stack.h b/source/blender/blenlib/BLI_linklist_stack.h index c07a0376f5b..7f1f52fd691 100644 --- a/source/blender/blenlib/BLI_linklist_stack.h +++ b/source/blender/blenlib/BLI_linklist_stack.h @@ -28,7 +28,7 @@ * \note These macros follow STACK_* macros defined in 'BLI_utildefines.h' * and should be kept (mostly) interchangeable. * - * \note ``_##var##_type`` is a dummy variable only used for typechecks. + * \note `_##var##_type` is a dummy variable only used for type-checks. */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenlib/BLI_math.h b/source/blender/blenlib/BLI_math.h index 3b61c0feb51..5768b098d2f 100644 --- a/source/blender/blenlib/BLI_math.h +++ b/source/blender/blenlib/BLI_math.h @@ -26,32 +26,32 @@ * * \section mathabbrev Abbreviations * - * - ``fl`` = float - * - ``db`` = double - * - ``v2`` = vec2 = vector 2 - * - ``v3`` = vec3 = vector 3 - * - ``v4`` = vec4 = vector 4 - * - ``vn`` = vec4 = vector N dimensions, *passed as an arg, after the vector*. - * - ``qt`` = quat = quaternion - * - ``dq`` = dquat = dual quaternion - * - ``m2`` = mat2 = matrix 2x2 - * - ``m3`` = mat3 = matrix 3x3 - * - ``m4`` = mat4 = matrix 4x4 - * - ``eul`` = euler rotation - * - ``eulO`` = euler with order - * - ``plane`` = plane 4, (vec3, distance) - * - ``plane3`` = plane 3 (same as a ``plane`` with a zero 4th component) + * - `fl` = float + * - `db` = double + * - `v2` = vec2 = vector 2 + * - `v3` = vec3 = vector 3 + * - `v4` = vec4 = vector 4 + * - `vn` = vec4 = vector N dimensions, *passed as an arg, after the vector*. + * - `qt` = quat = quaternion + * - `dq` = dquat = dual quaternion + * - `m2` = mat2 = matrix 2x2 + * - `m3` = mat3 = matrix 3x3 + * - `m4` = mat4 = matrix 4x4 + * - `eul` = euler rotation + * - `eulO` = euler with order + * - `plane` = plane 4, (vec3, distance) + * - `plane3` = plane 3 (same as a `plane` with a zero 4th component) * * \subsection mathabbrev_all Function Type Abbreviations * * For non float versions of functions (which typically operate on floats), * use single suffix abbreviations. * - * - ``_d`` = double - * - ``_i`` = int - * - ``_u`` = unsigned int - * - ``_char`` = char - * - ``_uchar`` = unsigned char + * - `_d` = double + * - `_i` = int + * - `_u` = unsigned int + * - `_char` = char + * - `_uchar` = unsigned char * * \section mathvarnames Variable Names * diff --git a/source/blender/blenlib/BLI_string_ref.hh b/source/blender/blenlib/BLI_string_ref.hh index 0cff8fa7fb4..257a0ba143e 100644 --- a/source/blender/blenlib/BLI_string_ref.hh +++ b/source/blender/blenlib/BLI_string_ref.hh @@ -208,6 +208,18 @@ class StringRefBase { constexpr int64_t find_first_not_of(char c, int64_t pos = 0) const; constexpr int64_t find_last_not_of(StringRef chars, int64_t pos = INT64_MAX) const; constexpr int64_t find_last_not_of(char c, int64_t pos = INT64_MAX) const; + + /** + * Return a new StringRef that does not contain leading and trailing whitespace. + */ + constexpr StringRef trim() const; + + /** + * Return a new StringRef that removes all the leading and trailing characters + * that occur in `characters_to_remove`. + */ + constexpr StringRef trim(StringRef characters_to_remove) const; + constexpr StringRef trim(char character_to_remove) const; }; /** @@ -540,4 +552,34 @@ constexpr inline int64_t StringRefBase::find_last_not_of(char c, int64_t pos) co return this->find_last_not_of(StringRef(&c, 1), pos); } +constexpr StringRef StringRefBase::trim() const +{ + return this->trim(" \t\r\n"); +} + +constexpr StringRef StringRefBase::trim(const char character_to_remove) const +{ + return this->trim(StringRef(&character_to_remove, 1)); +} + +/** + * Return a new StringRef that removes all the leading and trailing characters + * that occur in `characters_to_remove`. + */ +constexpr StringRef StringRefBase::trim(StringRef characters_to_remove) const +{ + const int64_t find_front = this->find_first_not_of(characters_to_remove); + if (find_front == not_found) { + return StringRef(); + } + const int64_t find_end = this->find_last_not_of(characters_to_remove); + /* `find_end` cannot be `not_found`, because that means the string is only + * `characters_to_remove`, in which case `find_front` would already have + * been `not_found`. */ + BLI_assert_msg(find_end != not_found, + "forward search found characters-to-not-remove, but backward search did not"); + const int64_t substr_len = find_end - find_front + 1; + return this->substr(find_front, substr_len); +} + } // namespace blender diff --git a/source/blender/blenlib/intern/BLI_dynstr.c b/source/blender/blenlib/intern/BLI_dynstr.c index 86784557b25..8f7f722c71b 100644 --- a/source/blender/blenlib/intern/BLI_dynstr.c +++ b/source/blender/blenlib/intern/BLI_dynstr.c @@ -291,7 +291,7 @@ int BLI_dynstr_get_len(const DynStr *ds) /** * Get a DynStr's contents as a c-string. * The \a rets argument must be allocated to be at - * least the size of ``BLI_dynstr_get_len(ds) + 1``. + * least the size of `BLI_dynstr_get_len(ds) + 1`. * * \param ds: The DynStr of interest. * \param rets: The string to fill. diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c index 46e599b7cf3..2c9285e418a 100644 --- a/source/blender/blenlib/intern/BLI_ghash.c +++ b/source/blender/blenlib/intern/BLI_ghash.c @@ -1270,7 +1270,7 @@ void BLI_gset_flag_clear(GSet *gs, uint flag) /* -------------------------------------------------------------------- */ /** \name GSet Combined Key/Value Usage * - * \note Not typical ``set`` use, only use when the pointer identity matters. + * \note Not typical `set` use, only use when the pointer identity matters. * This can be useful when the key references data stored outside the GSet. * \{ */ diff --git a/source/blender/blenlib/intern/BLI_ghash_utils.c b/source/blender/blenlib/intern/BLI_ghash_utils.c index 034f71e4deb..465723b79d3 100644 --- a/source/blender/blenlib/intern/BLI_ghash_utils.c +++ b/source/blender/blenlib/intern/BLI_ghash_utils.c @@ -138,8 +138,8 @@ size_t BLI_ghashutil_combine_hash(size_t hash_a, size_t hash_b) * This function implements the widely used "djb" hash apparently posted * by Daniel Bernstein to comp.lang.c some time ago. The 32 bit * unsigned hash value starts at 5381 and for each byte 'c' in the - * string, is updated: ``hash = hash * 33 + c``. This - * function uses the signed value of each byte. + * string, is updated: `hash = hash * 33 + c`. + * This function uses the signed value of each byte. * * NOTE: this is the same hash method that glib 2.34.0 uses. */ diff --git a/source/blender/blenlib/intern/BLI_kdopbvh.c b/source/blender/blenlib/intern/BLI_kdopbvh.c index 25939323b73..674654c99a8 100644 --- a/source/blender/blenlib/intern/BLI_kdopbvh.c +++ b/source/blender/blenlib/intern/BLI_kdopbvh.c @@ -22,7 +22,7 @@ * \brief BVH-tree implementation. * * k-DOP BVH (Discrete Oriented Polytope, Bounding Volume Hierarchy). - * A k-DOP is represented as k/2 pairs of min , max values for k/2 directions (intervals, "slabs"). + * A k-DOP is represented as k/2 pairs of min, max values for k/2 directions (intervals, "slabs"). * * See: http://www.gris.uni-tuebingen.de/people/staff/jmezger/papers/bvh.pdf * @@ -868,7 +868,7 @@ static void non_recursive_bvh_div_nodes(const BVHTree *tree, * \{ */ /** - * \note many callers don't check for ``NULL`` return. + * \note many callers don't check for `NULL` return. */ BVHTree *BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis) { diff --git a/source/blender/blenlib/intern/BLI_memiter.c b/source/blender/blenlib/intern/BLI_memiter.c index 3f255ce0e16..effbe5da5c4 100644 --- a/source/blender/blenlib/intern/BLI_memiter.c +++ b/source/blender/blenlib/intern/BLI_memiter.c @@ -73,7 +73,7 @@ typedef struct BLI_memiter_chunk { struct BLI_memiter_chunk *next; /** * internal format is: - * ``[next_pointer, size:data, size:data, ..., negative_offset]`` + * `[next_pointer, size:data, size:data, ..., negative_offset]` * * Where negative offset rewinds to the start. */ diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c index 523bb300247..65ebf38debf 100644 --- a/source/blender/blenlib/intern/BLI_mempool.c +++ b/source/blender/blenlib/intern/BLI_mempool.c @@ -70,9 +70,9 @@ #endif /** - * Important that this value is an is _not_ aligned with ``sizeof(void *)``. + * Important that this value is an is _not_ aligned with `sizeof(void *)`. * So having a pointer to 2/4/8... aligned memory is enough to ensure - * the freeword will never be used. + * the `freeword` will never be used. * To be safe, use a word that's the same in both directions. */ #define FREEWORD \ diff --git a/source/blender/blenlib/intern/BLI_mmap.c b/source/blender/blenlib/intern/BLI_mmap.c index 71510b62ba1..c25e89df818 100644 --- a/source/blender/blenlib/intern/BLI_mmap.c +++ b/source/blender/blenlib/intern/BLI_mmap.c @@ -31,11 +31,11 @@ #ifndef WIN32 # include <signal.h> # include <stdlib.h> -# include <sys/mman.h> // for mmap -# include <unistd.h> // for read close +# include <sys/mman.h> /* For mmap. */ +# include <unistd.h> /* For read close. */ #else # include "BLI_winstuff.h" -# include <io.h> // for open close read +# include <io.h> /* For open close read. */ #endif struct BLI_mmap_file { diff --git a/source/blender/blenlib/intern/DLRB_tree.c b/source/blender/blenlib/intern/DLRB_tree.c index 09234dcfa26..436b9b8d782 100644 --- a/source/blender/blenlib/intern/DLRB_tree.c +++ b/source/blender/blenlib/intern/DLRB_tree.c @@ -128,7 +128,9 @@ void BLI_dlrbTree_linkedlist_sync(DLRBT_Tree *tree) /* Tree Search Utilities */ /* Find the node which matches or is the closest to the requested node */ -DLRBT_Node *BLI_dlrbTree_search(DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data) +DLRBT_Node *BLI_dlrbTree_search(const DLRBT_Tree *tree, + DLRBT_Comparator_FP cmp_cb, + void *search_data) { DLRBT_Node *node = (tree) ? tree->root : NULL; short found = 0; @@ -174,7 +176,7 @@ DLRBT_Node *BLI_dlrbTree_search(DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, vo } /* Find the node which exactly matches the required data */ -DLRBT_Node *BLI_dlrbTree_search_exact(DLRBT_Tree *tree, +DLRBT_Node *BLI_dlrbTree_search_exact(const DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data) { @@ -222,7 +224,7 @@ DLRBT_Node *BLI_dlrbTree_search_exact(DLRBT_Tree *tree, } /* Find the node which occurs immediately before the best matching node */ -DLRBT_Node *BLI_dlrbTree_search_prev(DLRBT_Tree *tree, +DLRBT_Node *BLI_dlrbTree_search_prev(const DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data) { @@ -253,7 +255,7 @@ DLRBT_Node *BLI_dlrbTree_search_prev(DLRBT_Tree *tree, } /* Find the node which occurs immediately after the best matching node */ -DLRBT_Node *BLI_dlrbTree_search_next(DLRBT_Tree *tree, +DLRBT_Node *BLI_dlrbTree_search_next(const DLRBT_Tree *tree, DLRBT_Comparator_FP cmp_cb, void *search_data) { diff --git a/source/blender/blenlib/intern/array_store.c b/source/blender/blenlib/intern/array_store.c index e1a7ee98ce5..5ad57a7bec8 100644 --- a/source/blender/blenlib/intern/array_store.c +++ b/source/blender/blenlib/intern/array_store.c @@ -1403,16 +1403,16 @@ static BChunkList *bchunk_list_from_data_merge(const BArrayInfo *info, * Create a new array store, which can store any number of arrays * as long as their stride matches. * - * \param stride: ``sizeof()`` each element, + * \param stride: `sizeof()` each element, * - * \note while a stride of ``1`` will always work, + * \note while a stride of `1` will always work, * its less efficient since duplicate chunks of memory will be searched * at positions unaligned with the array data. * * \param chunk_count: Number of elements to split each chunk into. * - A small value increases the ability to de-duplicate chunks, * but adds overhead by increasing the number of chunks to look up when searching for duplicates, - * as well as some overhead constructing the original array again, with more calls to ``memcpy``. + * as well as some overhead constructing the original array again, with more calls to `memcpy`. * - Larger values reduce the *book keeping* overhead, * but increase the chance a small, * isolated change will cause a larger amount of data to be duplicated. diff --git a/source/blender/blenlib/intern/convexhull_2d.c b/source/blender/blenlib/intern/convexhull_2d.c index cb4ef54bfb7..233a1430fe7 100644 --- a/source/blender/blenlib/intern/convexhull_2d.c +++ b/source/blender/blenlib/intern/convexhull_2d.c @@ -188,7 +188,7 @@ static int pointref_cmp_yx(const void *a_, const void *b_) * \param points: An array of 2D points. * \param n: The number of points in points. * \param r_points: An array of the convex hull vertex indices (max is n). - * _must_ be allocated as ``n * 2`` because of how its used internally, + * _must_ be allocated as `n * 2` because of how its used internally, * even though the final result will be no more than \a n in size. * \returns the number of points in r_points. */ diff --git a/source/blender/blenlib/intern/delaunay_2d.cc b/source/blender/blenlib/intern/delaunay_2d.cc index 24a58103a10..4582ea69d9b 100644 --- a/source/blender/blenlib/intern/delaunay_2d.cc +++ b/source/blender/blenlib/intern/delaunay_2d.cc @@ -19,6 +19,7 @@ */ #include <algorithm> +#include <atomic> #include <fstream> #include <iostream> #include <sstream> @@ -29,6 +30,8 @@ #include "BLI_math_boolean.hh" #include "BLI_math_mpq.hh" #include "BLI_mpq2.hh" +#include "BLI_set.hh" +#include "BLI_task.hh" #include "BLI_vector.hh" #include "BLI_delaunay_2d.h" @@ -77,8 +80,8 @@ template<> double math_to_double<double>(const double v) * Define a templated 2D arrangement of vertices, edges, and faces. * The #SymEdge data structure is the basis for a structure that allows * easy traversal to neighboring (by topology) geometric elements. - * Each of #CDTVert, #CDTEdge, and #CDTFace have an input_id linked list, - * whose nodes contain integers that keep track of which input verts, edges, + * Each of #CDTVert, #CDTEdge, and #CDTFace have an input_id set, + * which contain integers that keep track of which input verts, edges, * and faces, respectively, that the element was derived from. * * While this could be cleaned up some, it is usable by other routines in Blender @@ -195,8 +198,8 @@ template<typename T> struct CDTVert { FatCo<T> co; /** Some edge attached to it. */ SymEdge<T> *symedge{nullptr}; - /** List of corresponding vertex input ids. */ - LinkNode *input_ids{nullptr}; + /** Set of corresponding vertex input ids. Not used if don't need_ids. */ + blender::Set<int> input_ids; /** Index into array that #CDTArrangement keeps. */ int index{-1}; /** Index of a CDTVert that this has merged to. -1 if no merge. */ @@ -209,8 +212,10 @@ template<typename T> struct CDTVert { }; template<typename Arith_t> struct CDTEdge { - /** List of input edge ids that this is part of. */ - LinkNode *input_ids{nullptr}; + /** Set of input edge ids that this is part of. + * If don't need_ids, then should contain 0 if it is a constrained edge, + * else empty. */ + blender::Set<int> input_ids; /** The directed edges for this edge. */ SymEdge<Arith_t> symedges[2]{SymEdge<Arith_t>(), SymEdge<Arith_t>()}; @@ -220,8 +225,10 @@ template<typename Arith_t> struct CDTEdge { template<typename Arith_t> struct CDTFace { /** A symedge in face; only used during output, so only valid then. */ SymEdge<Arith_t> *symedge{nullptr}; - /** List of input face ids that this is part of. */ - LinkNode *input_ids{nullptr}; + /** Set of input face ids that this is part of. + * If don't need_ids, then should contain 0 if it is part of a constrained face, + * else empty. */ + blender::Set<int> input_ids; /** Used by algorithms operating on CDT structures. */ int visit_index{0}; /** Marks this face no longer used. */ @@ -334,27 +341,30 @@ template<typename T> class CDT_state { int face_edge_offset; /** How close before coords considered equal. */ T epsilon; + /** Do we need to track ids? */ + bool need_ids; - explicit CDT_state(int num_input_verts, int num_input_edges, int num_input_faces, T epsilon); + explicit CDT_state( + int num_input_verts, int num_input_edges, int num_input_faces, T epsilon, bool need_ids); }; template<typename T> CDTArrangement<T>::~CDTArrangement() { for (int i : this->verts.index_range()) { CDTVert<T> *v = this->verts[i]; - BLI_linklist_free(v->input_ids, nullptr); + v->input_ids.clear(); delete v; this->verts[i] = nullptr; } for (int i : this->edges.index_range()) { CDTEdge<T> *e = this->edges[i]; - BLI_linklist_free(e->input_ids, nullptr); + e->input_ids.clear(); delete e; this->edges[i] = nullptr; } for (int i : this->faces.index_range()) { CDTFace<T> *f = this->faces[i]; - BLI_linklist_free(f->input_ids, nullptr); + f->input_ids.clear(); delete f; this->faces[i] = nullptr; } @@ -431,11 +441,11 @@ template<typename T> std::ostream &operator<<(std::ostream &os, const CDT_state< if (se) { os << " edges out:\n"; do { - if (se->next == NULL) { + if (se->next == nullptr) { os << " [NULL] next/rot symedge, se=" << trunc_ptr(se) << "\n"; break; } - if (se->next->next == NULL) { + if (se->next->next == nullptr) { os << " [NULL] next-next/rot symedge, se=" << trunc_ptr(se) << "\n"; break; } @@ -495,6 +505,7 @@ template<typename T> void cdt_draw(const std::string &label, const CDTArrangemen constexpr int vert_radius = 3; constexpr bool draw_vert_labels = true; constexpr bool draw_edge_labels = false; + constexpr bool draw_face_labels = false; if (cdt.verts.size() == 0) { return; @@ -559,7 +570,7 @@ template<typename T> void cdt_draw(const std::string &label, const CDTArrangemen const CDTVert<T> *v = e->symedges[1].vert; const vec2<double> &uco = u->co.approx; const vec2<double> &vco = v->co.approx; - int strokew = e->input_ids == nullptr ? thin_line : thick_line; + int strokew = e->input_ids.size() == 0 ? thin_line : thick_line; f << R"(<line fill="none" stroke="black" stroke-width=")" << strokew << "\" x1=\"" << SX(uco[0]) << "\" y1=\"" << SY(uco[1]) << "\" x2=\"" << SX(vco[0]) << "\" y2=\"" << SY(vco[1]) << "\">\n"; @@ -586,6 +597,54 @@ template<typename T> void cdt_draw(const std::string &label, const CDTArrangemen ++i; } + if (draw_face_labels) { + for (const CDTFace<T> *face : cdt.faces) { + if (!face->deleted) { + /* Since may not have prepared output yet, need a slow find of a SymEdge for this face. */ + const SymEdge<T> *se_face_start = nullptr; + for (const CDTEdge<T> *e : cdt.edges) { + if (e->symedges[0].face == face) { + se_face_start = &e->symedges[0]; + break; + } + if (e->symedges[1].face == face) { + se_face_start = &e->symedges[1]; + } + } + if (se_face_start != nullptr) { + /* Find center of face. */ + int face_nverts = 0; + vec2<double> cen(0.0, 0.0); + if (face == cdt.outer_face) { + cen.x = minx; + cen.y = miny; + } + else { + const SymEdge<T> *se = se_face_start; + do { + if (se->face == face) { + cen = cen + se->vert->co.approx; + face_nverts++; + } + } while ((se = se->next) != se_face_start); + if (face_nverts > 0) { + cen = cen / double(face_nverts); + } + } + f << "<text x=\"" << SX(cen[0]) << "\" y=\"" << SY(cen[1]) << "\"" + << " font-size=\"small\">["; + f << trunc_ptr(face); + if (face->input_ids.size() > 0) { + for (int id : face->input_ids) { + f << " " << id; + } + } + f << "]</text>\n"; + } + } + } + } + append = true; # undef SX # undef SY @@ -754,7 +813,6 @@ template<> CDTVert<double>::CDTVert(const vec2<double> &pt) this->co.exact = pt; this->co.approx = pt; this->co.abs_approx = pt; /* Not used, so doesn't matter. */ - this->input_ids = nullptr; this->symedge = nullptr; this->index = -1; this->merge_to_index = -1; @@ -767,7 +825,6 @@ template<> CDTVert<mpq_class>::CDTVert(const vec2<mpq_class> &pt) this->co.exact = pt; this->co.approx = double2(pt.x.get_d(), pt.y.get_d()); this->co.abs_approx = double2(fabs(this->co.approx.x), fabs(this->co.approx.y)); - this->input_ids = nullptr; this->symedge = nullptr; this->index = -1; this->merge_to_index = -1; @@ -824,35 +881,21 @@ template<typename T> void CDTArrangement<T>::reserve(int num_verts, int num_edge } template<typename T> -CDT_state<T>::CDT_state(int num_input_verts, int num_input_edges, int num_input_faces, T epsilon) +CDT_state<T>::CDT_state( + int num_input_verts, int num_input_edges, int num_input_faces, T epsilon, bool need_ids) { this->input_vert_tot = num_input_verts; this->cdt.reserve(num_input_verts, num_input_edges, num_input_faces); this->cdt.outer_face = this->cdt.add_face(); this->epsilon = epsilon; + this->need_ids = need_ids; this->visit_count = 0; } -static bool id_in_list(const LinkNode *id_list, int id) -{ - const LinkNode *ln; - - for (ln = id_list; ln != nullptr; ln = ln->next) { - if (POINTER_AS_INT(ln->link) == id) { - return true; - } - } - return false; -} - /* Is any id in (range_start, range_start+1, ... , range_end) in id_list? */ -static bool id_range_in_list(const LinkNode *id_list, int range_start, int range_end) +static bool id_range_in_list(const blender::Set<int> &id_list, int range_start, int range_end) { - const LinkNode *ln; - int id; - - for (ln = id_list; ln != nullptr; ln = ln->next) { - id = POINTER_AS_INT(ln->link); + for (int id : id_list) { if (id >= range_start && id <= range_end) { return true; } @@ -860,19 +903,15 @@ static bool id_range_in_list(const LinkNode *id_list, int range_start, int range return false; } -static void add_to_input_ids(LinkNode **dst, int input_id) +static void add_to_input_ids(blender::Set<int> &dst, int input_id) { - if (!id_in_list(*dst, input_id)) { - BLI_linklist_prepend(dst, POINTER_FROM_INT(input_id)); - } + dst.add(input_id); } -static void add_list_to_input_ids(LinkNode **dst, const LinkNode *src) +static void add_list_to_input_ids(blender::Set<int> &dst, const blender::Set<int> &src) { - const LinkNode *ln; - - for (ln = src; ln != nullptr; ln = ln->next) { - add_to_input_ids(dst, POINTER_AS_INT(ln->link)); + for (int value : src) { + dst.add(value); } } @@ -883,7 +922,7 @@ template<typename T> inline bool is_border_edge(const CDTEdge<T> *e, const CDT_s template<typename T> inline bool is_constrained_edge(const CDTEdge<T> *e) { - return e->input_ids != nullptr; + return e->input_ids.size() > 0; } template<typename T> inline bool is_deleted_edge(const CDTEdge<T> *e) @@ -979,7 +1018,7 @@ template<typename T> CDTEdge<T> *CDTArrangement<T>::add_diagonal(SymEdge<T> *s1, for (SymEdge<T> *se = s2; se != sdiag; se = se->next) { se->face = fnew; } - add_list_to_input_ids(&fnew->input_ids, fold->input_ids); + add_list_to_input_ids(fnew->input_ids, fold->input_ids); return ediag; } @@ -1058,7 +1097,7 @@ template<typename T> CDTEdge<T> *CDTArrangement<T>::split_edge(SymEdge<T> *se, T if (newsesym->vert->symedge == sesym) { newsesym->vert->symedge = newsesym; } - add_list_to_input_ids(&e->input_ids, se->edge->input_ids); + add_list_to_input_ids(e->input_ids, se->edge->input_ids); return e; } @@ -1880,7 +1919,7 @@ void add_edge_constraint( SymEdge<T> *t = find_symedge_between_verts(v1, v2); if (t != nullptr) { /* Segment already there. */ - add_to_input_ids(&t->edge->input_ids, input_id); + add_to_input_ids(t->edge->input_ids, input_id); if (r_edges != nullptr) { BLI_linklist_append(&edge_list, t->edge); *r_edges = edge_list.list; @@ -2041,7 +2080,7 @@ void add_edge_constraint( BLI_assert(cd_prev->lambda == 0.0); BLI_assert(cd_prev->out->next->vert == cd->vert); edge = cd_prev->out->edge; - add_to_input_ids(&edge->input_ids, input_id); + add_to_input_ids(edge->input_ids, input_id); if (r_edges != nullptr) { BLI_linklist_append(&edge_list, edge); } @@ -2054,7 +2093,7 @@ void add_edge_constraint( else { edge = cdt_state->cdt.add_diagonal(tstart, t); } - add_to_input_ids(&edge->input_ids, input_id); + add_to_input_ids(edge->input_ids, input_id); if (r_edges != nullptr) { BLI_linklist_append(&edge_list, edge); } @@ -2093,7 +2132,8 @@ template<typename T> void add_edge_constraints(CDT_state<T> *cdt_state, const CD } CDTVert<T> *v1 = cdt_state->cdt.get_vert_resolve_merge(iv1); CDTVert<T> *v2 = cdt_state->cdt.get_vert_resolve_merge(iv2); - add_edge_constraint(cdt_state, v1, v2, i, nullptr); + int id = cdt_state->need_ids ? i : 0; + add_edge_constraint(cdt_state, v1, v2, id, nullptr); } cdt_state->face_edge_offset = ne; } @@ -2132,7 +2172,7 @@ void add_face_ids( continue; } face->visit_index = visit; - add_to_input_ids(&face->input_ids, face_id); + add_to_input_ids(face->input_ids, face_id); SymEdge<T> *se_start = se; for (se = se->next; se != se_start; se = se->next) { if (!id_range_in_list(se->edge->input_ids, fedge_start, fedge_end)) { @@ -2168,7 +2208,10 @@ static int power_of_10_greater_equal_to(int x) * order around each face in turn. And then the next face starts at * cdt->face_edge_offset beyond the start for the previous face. */ -template<typename T> void add_face_constraints(CDT_state<T> *cdt_state, const CDT_input<T> &input) +template<typename T> +void add_face_constraints(CDT_state<T> *cdt_state, + const CDT_input<T> &input, + CDT_output_type output_type) { int nv = input.vert.size(); int nf = input.face.size(); @@ -2206,7 +2249,8 @@ template<typename T> void add_face_constraints(CDT_state<T> *cdt_state, const CD CDTVert<T> *v1 = cdt->get_vert_resolve_merge(iv1); CDTVert<T> *v2 = cdt->get_vert_resolve_merge(iv2); LinkNode *edge_list; - add_edge_constraint(cdt_state, v1, v2, face_edge_id, &edge_list); + int id = cdt_state->need_ids ? face_edge_id : 0; + add_edge_constraint(cdt_state, v1, v2, id, &edge_list); /* Set a new face_symedge0 each time since earlier ones may not * survive later symedge splits. Really, just want the one when * i == flen -1, but this code guards against that one somehow @@ -2224,7 +2268,16 @@ template<typename T> void add_face_constraints(CDT_state<T> *cdt_state, const CD } int fedge_end = fedge_start + flen - 1; if (face_symedge0 != nullptr) { - add_face_ids(cdt_state, face_symedge0, f, fedge_start, fedge_end); + /* We need to propagate face ids to all faces that represent #f, if #need_ids. + * Even if `need_ids == false`, we need to propagate at least the fact that + * the face ids set would be non-empty if the output type is one of the ones + * making valid BMesh faces. */ + int id = cdt_state->need_ids ? f : 0; + add_face_ids(cdt_state, face_symedge0, id, fedge_start, fedge_end); + if (cdt_state->need_ids || (output_type == CDT_CONSTRAINTS_VALID_BMESH || + output_type == CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES)) { + add_face_ids(cdt_state, face_symedge0, f, fedge_start, fedge_end); + } } fstart += flen; } @@ -2349,7 +2402,7 @@ template<typename T> void remove_non_constraint_edges_leave_valid_bmesh(CDT_stat CDTFace<T> *fleft = se->face; CDTFace<T> *fright = sym(se)->face; if (fleft != cdt->outer_face && fright != cdt->outer_face && - (fleft->input_ids != nullptr || fright->input_ids != nullptr)) { + (fleft->input_ids.size() > 0 || fright->input_ids.size() > 0)) { /* Is there another #SymEdge with same left and right faces? * Or is there a vertex not part of e touching the same left and right faces? */ for (SymEdge<T> *se2 = se->next; dissolve && se2 != se; se2 = se2->next) { @@ -2507,30 +2560,33 @@ template<typename T> void detect_holes(CDT_state<T> *cdt_state) mid.exact[1] = (f->symedge->vert->co.exact[1] + f->symedge->next->vert->co.exact[1] + f->symedge->next->next->vert->co.exact[1]) / 3; - int hits = 0; + std::atomic<int> hits = 0; /* TODO: Use CDT data structure here to greatly reduce search for intersections! */ - for (const CDTEdge<T> *e : cdt->edges) { - if (!is_deleted_edge(e) && is_constrained_edge(e)) { - if (e->symedges[0].face->visit_index == e->symedges[1].face->visit_index) { - continue; /* Don't count hits on edges between faces in same region. */ - } - auto isect = vec2<T>::isect_seg_seg(ray_end.exact, - mid.exact, - e->symedges[0].vert->co.exact, - e->symedges[1].vert->co.exact); - switch (isect.kind) { - case vec2<T>::isect_result::LINE_LINE_CROSS: { - hits++; - break; + threading::parallel_for(cdt->edges.index_range(), 256, [&](IndexRange range) { + for (const int i : range) { + const CDTEdge<T> *e = cdt->edges[i]; + if (!is_deleted_edge(e) && is_constrained_edge(e)) { + if (e->symedges[0].face->visit_index == e->symedges[1].face->visit_index) { + continue; /* Don't count hits on edges between faces in same region. */ + } + auto isect = vec2<T>::isect_seg_seg(ray_end.exact, + mid.exact, + e->symedges[0].vert->co.exact, + e->symedges[1].vert->co.exact); + switch (isect.kind) { + case vec2<T>::isect_result::LINE_LINE_CROSS: { + hits++; + break; + } + case vec2<T>::isect_result::LINE_LINE_EXACT: + case vec2<T>::isect_result::LINE_LINE_NONE: + case vec2<T>::isect_result::LINE_LINE_COLINEAR: + break; } - case vec2<T>::isect_result::LINE_LINE_EXACT: - case vec2<T>::isect_result::LINE_LINE_NONE: - case vec2<T>::isect_result::LINE_LINE_COLINEAR: - break; } } - } - f->hole = (hits % 2) == 0; + }); + f->hole = (hits.load() % 2) == 0; } /* Finally, propagate hole status to all holes of a region. */ @@ -2631,25 +2687,31 @@ CDT_result<T> get_cdt_output(CDT_state<T> *cdt_state, for (int i = 0; i < verts_size; ++i) { CDTVert<T> *v = cdt->verts[i]; if (v->merge_to_index != -1) { - if (i < cdt_state->input_vert_tot) { - add_to_input_ids(&cdt->verts[v->merge_to_index]->input_ids, i); + if (cdt_state->need_ids) { + if (i < cdt_state->input_vert_tot) { + add_to_input_ids(cdt->verts[v->merge_to_index]->input_ids, i); + } } vert_to_output_map[i] = vert_to_output_map[v->merge_to_index]; } } } result.vert = Array<vec2<T>>(nv); - result.vert_orig = Array<Vector<int>>(nv); + if (cdt_state->need_ids) { + result.vert_orig = Array<Vector<int>>(nv); + } int i_out = 0; for (int i = 0; i < verts_size; ++i) { CDTVert<T> *v = cdt->verts[i]; if (v->merge_to_index == -1) { result.vert[i_out] = v->co.exact; - if (i < cdt_state->input_vert_tot) { - result.vert_orig[i_out].append(i); - } - for (LinkNode *ln = v->input_ids; ln; ln = ln->next) { - result.vert_orig[i_out].append(POINTER_AS_INT(ln->link)); + if (cdt_state->need_ids) { + if (i < cdt_state->input_vert_tot) { + result.vert_orig[i_out].append(i); + } + for (int vert : v->input_ids) { + result.vert_orig[i_out].append(vert); + } } ++i_out; } @@ -2660,15 +2722,19 @@ CDT_result<T> get_cdt_output(CDT_state<T> *cdt_state, return !is_deleted_edge(e); }); result.edge = Array<std::pair<int, int>>(ne); - result.edge_orig = Array<Vector<int>>(ne); + if (cdt_state->need_ids) { + result.edge_orig = Array<Vector<int>>(ne); + } int e_out = 0; for (const CDTEdge<T> *e : cdt->edges) { if (!is_deleted_edge(e)) { int vo1 = vert_to_output_map[e->symedges[0].vert->index]; int vo2 = vert_to_output_map[e->symedges[1].vert->index]; result.edge[e_out] = std::pair<int, int>(vo1, vo2); - for (LinkNode *ln = e->input_ids; ln; ln = ln->next) { - result.edge_orig[e_out].append(POINTER_AS_INT(ln->link)); + if (cdt_state->need_ids) { + for (int edge : e->input_ids) { + result.edge_orig[e_out].append(edge); + } } ++e_out; } @@ -2679,7 +2745,9 @@ CDT_result<T> get_cdt_output(CDT_state<T> *cdt_state, return !f->deleted && f != cdt->outer_face; }); result.face = Array<Vector<int>>(nf); - result.face_orig = Array<Vector<int>>(nf); + if (cdt_state->need_ids) { + result.face_orig = Array<Vector<int>>(nf); + } int f_out = 0; for (const CDTFace<T> *f : cdt->faces) { if (!f->deleted && f != cdt->outer_face) { @@ -2690,8 +2758,10 @@ CDT_result<T> get_cdt_output(CDT_state<T> *cdt_state, result.face[f_out].append(vert_to_output_map[se->vert->index]); se = se->next; } while (se != se_start); - for (LinkNode *ln = f->input_ids; ln; ln = ln->next) { - result.face_orig[f_out].append(POINTER_AS_INT(ln->link)); + if (cdt_state->need_ids) { + for (int face : f->input_ids) { + result.face_orig[f_out].append(face); + } } ++f_out; } @@ -2716,11 +2786,11 @@ CDT_result<T> delaunay_calc(const CDT_input<T> &input, CDT_output_type output_ty int nv = input.vert.size(); int ne = input.edge.size(); int nf = input.face.size(); - CDT_state<T> cdt_state(nv, ne, nf, input.epsilon); + CDT_state<T> cdt_state(nv, ne, nf, input.epsilon, input.need_ids); add_input_verts(&cdt_state, input); initial_triangulation(&cdt_state.cdt); add_edge_constraints(&cdt_state, input); - add_face_constraints(&cdt_state, input); + add_face_constraints(&cdt_state, input, output_type); return get_cdt_output(&cdt_state, input, output_type); } @@ -2772,6 +2842,7 @@ extern "C" ::CDT_result *BLI_delaunay_2d_cdt_calc(const ::CDT_input *input, } } in.epsilon = static_cast<double>(input->epsilon); + in.need_ids = input->need_ids; blender::meshintersect::CDT_result<double> res = blender::meshintersect::delaunay_2d_calc( in, output_type); @@ -2784,74 +2855,101 @@ extern "C" ::CDT_result *BLI_delaunay_2d_cdt_calc(const ::CDT_input *input, int tot_e_orig = 0; int tot_f_orig = 0; int tot_f_lens = 0; - for (int v = 0; v < nv; ++v) { - tot_v_orig += res.vert_orig[v].size(); - } - for (int e = 0; e < ne; ++e) { - tot_e_orig += res.edge_orig[e].size(); + if (input->need_ids) { + for (int v = 0; v < nv; ++v) { + tot_v_orig += res.vert_orig[v].size(); + } + for (int e = 0; e < ne; ++e) { + tot_e_orig += res.edge_orig[e].size(); + } } for (int f = 0; f < nf; ++f) { - tot_f_orig += res.face_orig[f].size(); + if (input->need_ids) { + tot_f_orig += res.face_orig[f].size(); + } tot_f_lens += res.face[f].size(); } output->vert_coords = static_cast<decltype(output->vert_coords)>( MEM_malloc_arrayN(nv, sizeof(output->vert_coords[0]), __func__)); - output->verts_orig = static_cast<int *>(MEM_malloc_arrayN(tot_v_orig, sizeof(int), __func__)); - output->verts_orig_start_table = static_cast<int *>( - MEM_malloc_arrayN(nv, sizeof(int), __func__)); - output->verts_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(nv, sizeof(int), __func__)); output->edges = static_cast<decltype(output->edges)>( MEM_malloc_arrayN(ne, sizeof(output->edges[0]), __func__)); - output->edges_orig = static_cast<int *>(MEM_malloc_arrayN(tot_e_orig, sizeof(int), __func__)); - output->edges_orig_start_table = static_cast<int *>( - MEM_malloc_arrayN(ne, sizeof(int), __func__)); - output->edges_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(ne, sizeof(int), __func__)); output->faces = static_cast<int *>(MEM_malloc_arrayN(tot_f_lens, sizeof(int), __func__)); output->faces_start_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__)); output->faces_len_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__)); - output->faces_orig = static_cast<int *>(MEM_malloc_arrayN(tot_f_orig, sizeof(int), __func__)); - output->faces_orig_start_table = static_cast<int *>( - MEM_malloc_arrayN(nf, sizeof(int), __func__)); - output->faces_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__)); + if (input->need_ids) { + output->verts_orig = static_cast<int *>(MEM_malloc_arrayN(tot_v_orig, sizeof(int), __func__)); + output->verts_orig_start_table = static_cast<int *>( + MEM_malloc_arrayN(nv, sizeof(int), __func__)); + output->verts_orig_len_table = static_cast<int *>( + MEM_malloc_arrayN(nv, sizeof(int), __func__)); + output->edges_orig = static_cast<int *>(MEM_malloc_arrayN(tot_e_orig, sizeof(int), __func__)); + output->edges_orig_start_table = static_cast<int *>( + MEM_malloc_arrayN(ne, sizeof(int), __func__)); + output->edges_orig_len_table = static_cast<int *>( + MEM_malloc_arrayN(ne, sizeof(int), __func__)); + output->faces_orig = static_cast<int *>(MEM_malloc_arrayN(tot_f_orig, sizeof(int), __func__)); + output->faces_orig_start_table = static_cast<int *>( + MEM_malloc_arrayN(nf, sizeof(int), __func__)); + output->faces_orig_len_table = static_cast<int *>( + MEM_malloc_arrayN(nf, sizeof(int), __func__)); + } + else { + output->verts_orig = nullptr; + output->verts_orig_start_table = nullptr; + output->verts_orig_len_table = nullptr; + output->edges_orig = nullptr; + output->edges_orig_start_table = nullptr; + output->edges_orig_len_table = nullptr; + output->faces_orig = nullptr; + output->faces_orig_start_table = nullptr; + output->faces_orig_len_table = nullptr; + } int v_orig_index = 0; for (int v = 0; v < nv; ++v) { output->vert_coords[v][0] = static_cast<float>(res.vert[v][0]); output->vert_coords[v][1] = static_cast<float>(res.vert[v][1]); - int this_start = v_orig_index; - output->verts_orig_start_table[v] = this_start; - for (int j : res.vert_orig[v].index_range()) { - output->verts_orig[v_orig_index++] = res.vert_orig[v][j]; + if (input->need_ids) { + int this_start = v_orig_index; + output->verts_orig_start_table[v] = this_start; + for (int j : res.vert_orig[v].index_range()) { + output->verts_orig[v_orig_index++] = res.vert_orig[v][j]; + } + output->verts_orig_len_table[v] = v_orig_index - this_start; } - output->verts_orig_len_table[v] = v_orig_index - this_start; } int e_orig_index = 0; for (int e = 0; e < ne; ++e) { output->edges[e][0] = res.edge[e].first; output->edges[e][1] = res.edge[e].second; - int this_start = e_orig_index; - output->edges_orig_start_table[e] = this_start; - for (int j : res.edge_orig[e].index_range()) { - output->edges_orig[e_orig_index++] = res.edge_orig[e][j]; + if (input->need_ids) { + int this_start = e_orig_index; + output->edges_orig_start_table[e] = this_start; + for (int j : res.edge_orig[e].index_range()) { + output->edges_orig[e_orig_index++] = res.edge_orig[e][j]; + } + output->edges_orig_len_table[e] = e_orig_index - this_start; } - output->edges_orig_len_table[e] = e_orig_index - this_start; } int f_orig_index = 0; int f_index = 0; for (int f = 0; f < nf; ++f) { + output->faces_start_table[f] = f_index; int flen = res.face[f].size(); output->faces_len_table[f] = flen; for (int j = 0; j < flen; ++j) { output->faces[f_index++] = res.face[f][j]; } - int this_start = f_orig_index; - output->faces_orig_start_table[f] = this_start; - for (int k : res.face_orig[f].index_range()) { - output->faces_orig[f_orig_index++] = res.face_orig[f][k]; + if (input->need_ids) { + int this_start = f_orig_index; + output->faces_orig_start_table[f] = this_start; + for (int k : res.face_orig[f].index_range()) { + output->faces_orig[f_orig_index++] = res.face_orig[f][k]; + } + output->faces_orig_len_table[f] = f_orig_index - this_start; } - output->faces_orig_len_table[f] = f_orig_index - this_start; } return output; } @@ -2863,14 +2961,16 @@ extern "C" void BLI_delaunay_2d_cdt_free(::CDT_result *result) MEM_freeN(result->faces); MEM_freeN(result->faces_start_table); MEM_freeN(result->faces_len_table); - MEM_freeN(result->verts_orig); - MEM_freeN(result->verts_orig_start_table); - MEM_freeN(result->verts_orig_len_table); - MEM_freeN(result->edges_orig); - MEM_freeN(result->edges_orig_start_table); - MEM_freeN(result->edges_orig_len_table); - MEM_freeN(result->faces_orig); - MEM_freeN(result->faces_orig_start_table); - MEM_freeN(result->faces_orig_len_table); + if (result->verts_orig) { + MEM_freeN(result->verts_orig); + MEM_freeN(result->verts_orig_start_table); + MEM_freeN(result->verts_orig_len_table); + MEM_freeN(result->edges_orig); + MEM_freeN(result->edges_orig_start_table); + MEM_freeN(result->edges_orig_len_table); + MEM_freeN(result->faces_orig); + MEM_freeN(result->faces_orig_start_table); + MEM_freeN(result->faces_orig_len_table); + } MEM_freeN(result); } diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c index 58b109eca10..0a213fa8696 100644 --- a/source/blender/blenlib/intern/math_base_inline.c +++ b/source/blender/blenlib/intern/math_base_inline.c @@ -410,7 +410,7 @@ MINLINE float pingpongf(float value, float scale) return fabsf(fractf((value - scale) / (scale * 2.0f)) * scale * 2.0f - scale); } -// Square. +/* Square. */ MINLINE int square_s(short a) { @@ -442,7 +442,7 @@ MINLINE double square_d(double a) return a * a; } -// Cube. +/* Cube. */ MINLINE int cube_s(short a) { @@ -474,7 +474,7 @@ MINLINE double cube_d(double a) return a * a * a; } -// Min/max +/* Min/max */ MINLINE float min_ff(float a, float b) { diff --git a/source/blender/blenlib/intern/math_color_inline.c b/source/blender/blenlib/intern/math_color_inline.c index 4c50c1c7af8..a5a687ef9fe 100644 --- a/source/blender/blenlib/intern/math_color_inline.c +++ b/source/blender/blenlib/intern/math_color_inline.c @@ -276,14 +276,14 @@ MINLINE void cpack_cpy_3ub(unsigned char r_col[3], const unsigned int pack) * https://en.wikipedia.org/wiki/Relative_luminance * * Real values are: - * ``Y = 0.2126390059(R) + 0.7151686788(G) + 0.0721923154(B)`` + * `Y = 0.2126390059(R) + 0.7151686788(G) + 0.0721923154(B)` * according to: "Derivation of Basic Television Color Equations", RP 177-1993 * * As this sums slightly above 1.0, the document recommends to use: - * ``0.2126(R) + 0.7152(G) + 0.0722(B)``, as used here. + * `0.2126(R) + 0.7152(G) + 0.0722(B)`, as used here. * * The high precision values are used to calculate the rounded byte weights so they add up to 255: - * ``54(R) + 182(G) + 19(B)`` + * `54(R) + 182(G) + 19(B)` */ MINLINE float rgb_to_grayscale(const float rgb[3]) { diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 80f0008c7eb..803291e4a3b 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -165,7 +165,7 @@ float area_squared_poly_v3(const float verts[][3], unsigned int nr) /** * Scalar cross product of a 2d polygon. * - * - equivalent to ``area * 2`` + * - equivalent to `area * 2` * - useful for checking polygon winding (a positive value is clockwise). */ float cross_poly_v2(const float verts[][2], unsigned int nr) @@ -518,7 +518,7 @@ float dist_to_line_v3(const float p[3], const float l1[3], const float l2[3]) } /** - * Check if \a p is inside the 2x planes defined by ``(v1, v2, v3)`` + * Check if \a p is inside the 2x planes defined by `(v1, v2, v3)` * where the 3x points define 2x planes. * * \param axis_ref: used when v1,v2,v3 form a line and to check if the corner is concave/convex. @@ -527,7 +527,7 @@ float dist_to_line_v3(const float p[3], const float l1[3], const float l2[3]) * (it just defines the planes). * * \return the lowest squared distance to either of the planes. - * where ``(return < 0.0)`` is outside. + * where `(return < 0.0)` is outside. * * <pre> * v1 @@ -1421,7 +1421,7 @@ int isect_seg_seg_v2_lambda_mu_db(const double v1[2], * \return r_p1, r_p2: Intersection coordinates. * * \note The order of assignment for intersection points (\a r_p1, \a r_p2) is predictable, - * based on the direction defined by ``l2 - l1``, + * based on the direction defined by `l2 - l1`, * this direction compared with the normal of each point on the sphere: * \a r_p1 always has a >= 0.0 dot product. * \a r_p2 always has a <= 0.0 dot product. @@ -3426,7 +3426,7 @@ float ray_point_factor_v3(const float p[3], /** * A simplified version of #closest_to_line_v3 - * we only need to return the ``lambda`` + * we only need to return the `lambda` * * \param epsilon: avoid approaching divide-by-zero. * Passing a zero will just check for nonzero division. diff --git a/source/blender/blenlib/intern/math_geom_inline.c b/source/blender/blenlib/intern/math_geom_inline.c index 655d3fcc4c0..1757b0dd525 100644 --- a/source/blender/blenlib/intern/math_geom_inline.c +++ b/source/blender/blenlib/intern/math_geom_inline.c @@ -272,7 +272,7 @@ MINLINE float shell_angle_to_dist(const float angle) return (UNLIKELY(angle < SMALL_NUMBER)) ? 1.0f : fabsf(1.0f / cosf(angle)); } /** - * equivalent to ``shell_angle_to_dist(angle_normalized_v3v3(a, b))`` + * Equivalent to `shell_angle_to_dist(angle_normalized_v3v3(a, b))`. */ MINLINE float shell_v3v3_normalized_to_dist(const float a[3], const float b[3]) { @@ -282,7 +282,7 @@ MINLINE float shell_v3v3_normalized_to_dist(const float a[3], const float b[3]) return (UNLIKELY(angle_cos < SMALL_NUMBER)) ? 1.0f : (1.0f / angle_cos); } /** - * equivalent to ``shell_angle_to_dist(angle_normalized_v2v2(a, b))`` + * Equivalent to `shell_angle_to_dist(angle_normalized_v2v2(a, b))`. */ MINLINE float shell_v2v2_normalized_to_dist(const float a[2], const float b[2]) { @@ -293,7 +293,7 @@ MINLINE float shell_v2v2_normalized_to_dist(const float a[2], const float b[2]) } /** - * equivalent to ``shell_angle_to_dist(angle_normalized_v3v3(a, b) / 2)`` + * Equivalent to `shell_angle_to_dist(angle_normalized_v3v3(a, b) / 2)`. */ MINLINE float shell_v3v3_mid_normalized_to_dist(const float a[3], const float b[3]) { @@ -307,7 +307,7 @@ MINLINE float shell_v3v3_mid_normalized_to_dist(const float a[3], const float b[ } /** - * equivalent to ``shell_angle_to_dist(angle_normalized_v2v2(a, b) / 2)`` + * Equivalent to `shell_angle_to_dist(angle_normalized_v2v2(a, b) / 2)`. */ MINLINE float shell_v2v2_mid_normalized_to_dist(const float a[2], const float b[2]) { diff --git a/source/blender/blenlib/intern/math_interp.c b/source/blender/blenlib/intern/math_interp.c index 04fae6a0e68..bd48edf70c0 100644 --- a/source/blender/blenlib/intern/math_interp.c +++ b/source/blender/blenlib/intern/math_interp.c @@ -625,7 +625,7 @@ void BLI_ewa_filter(const int width, * Use a different radius based on interpolation switch, * just enough to anti-alias when interpolation is off, * and slightly larger to make result a bit smoother than bilinear interpolation when - * interpolation is on (minimum values: const float rmin = intpol ? 1.0f : 0.5f;) */ + * interpolation is on (minimum values: `const float rmin = intpol ? 1.0f : 0.5f;`) */ const float rmin = (intpol ? 1.5625f : 0.765625f) / ff2; BLI_ewa_imp2radangle(A, B, C, F, &a, &b, &th, &ecc); if ((b2 = b * b) < rmin) { diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index 5920788821c..b605c3eeead 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -276,7 +276,7 @@ void mul_m4_m4m4_uniq(float R[4][4], const float A[4][4], const float B[4][4]) { BLI_assert(!ELEM(R, A, B)); - /* matrix product: R[j][k] = A[j][i] . B[i][k] */ + /* Matrix product: `R[j][k] = A[j][i] . B[i][k]`. */ #ifdef BLI_HAVE_SSE2 __m128 A0 = _mm_loadu_ps(A[0]); __m128 A1 = _mm_loadu_ps(A[1]); @@ -321,7 +321,7 @@ void mul_m4_m4m4_db_uniq(double R[4][4], const double A[4][4], const double B[4] { BLI_assert(!ELEM(R, A, B)); - /* matrix product: R[j][k] = A[j][i] . B[i][k] */ + /* Matrix product: `R[j][k] = A[j][i] . B[i][k]`. */ R[0][0] = B[0][0] * A[0][0] + B[0][1] * A[1][0] + B[0][2] * A[2][0] + B[0][3] * A[3][0]; R[0][1] = B[0][0] * A[0][1] + B[0][1] * A[1][1] + B[0][2] * A[2][1] + B[0][3] * A[3][1]; @@ -349,7 +349,7 @@ void mul_m4db_m4db_m4fl_uniq(double R[4][4], const double A[4][4], const float B /* Remove second check since types don't match. */ BLI_assert(!ELEM(R, A /*, B */)); - /* matrix product: R[j][k] = A[j][i] . B[i][k] */ + /* Matrix product: `R[j][k] = A[j][i] . B[i][k]`. */ R[0][0] = B[0][0] * A[0][0] + B[0][1] * A[1][0] + B[0][2] * A[2][0] + B[0][3] * A[3][0]; R[0][1] = B[0][0] * A[0][1] + B[0][1] * A[1][1] + B[0][2] * A[2][1] + B[0][3] * A[3][1]; diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index db9ece81c59..55f7a152b83 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -1312,7 +1312,7 @@ MINLINE bool is_one_v3(const float v[3]) /* -------------------------------------------------------------------- */ /** \name Vector Comparison * - * \note use ``value <= limit``, so a limit of zero doesn't fail on an exact match. + * \note use `value <= limit`, so a limit of zero doesn't fail on an exact match. * \{ */ MINLINE bool equals_v2v2(const float v1[2], const float v2[2]) diff --git a/source/blender/blenlib/intern/memory_utils.c b/source/blender/blenlib/intern/memory_utils.c index 5ca7b96c136..4bb93877401 100644 --- a/source/blender/blenlib/intern/memory_utils.c +++ b/source/blender/blenlib/intern/memory_utils.c @@ -19,7 +19,7 @@ * \brief Generic memory manipulation API. * * This is to extend on existing functions - * such as ``memcpy`` & ``memcmp``. + * such as `memcpy` & `memcmp`. */ #include <string.h> diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 8b8850c7cdb..90ffebdb422 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -151,7 +151,7 @@ class TriMeshTopology : NonCopyable { * Else return NO_INDEX. */ int other_tri_if_manifold(Edge e, int t) const { - auto p = edge_tri_.lookup_ptr(e); + const auto *p = edge_tri_.lookup_ptr(e); if (p != nullptr && (*p)->size() == 2) { return ((**p)[0] == t) ? (**p)[1] : (**p)[0]; } @@ -204,7 +204,7 @@ TriMeshTopology::TriMeshTopology(const IMesh &tm) } edges->append_non_duplicates(e); - auto p = edge_tri_.lookup_ptr(Edge(v, vnext)); + auto *p = edge_tri_.lookup_ptr(Edge(v, vnext)); if (p == nullptr) { edge_tri_.add_new(e, new Vector<int>{t}); } @@ -238,7 +238,7 @@ TriMeshTopology::~TriMeshTopology() Vector<Vector<int> *> values; /* Deconstructing is faster in parallel, so it is worth building an array of things to delete. */ - for (auto item : edge_tri_.values()) { + for (auto *item : edge_tri_.values()) { values.append(item); } diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc index f91dd762e70..dcd432a88d5 100644 --- a/source/blender/blenlib/intern/mesh_intersect.cc +++ b/source/blender/blenlib/intern/mesh_intersect.cc @@ -1875,6 +1875,16 @@ static void do_cdt(CDT_data &cd) } } +/* Find an original edge index that goes with the edge that the CDT output edge + * that goes between verts i0 and i1 (in the CDT output vert indexing scheme). + * There may be more than one: if so, prefer one that was originally a face edge. + * The input to CDT for a triangle with some intersecting segments from other triangles + * will have both edges and a face constraint for the main triangle (this is redundant + * but allows us to discover which face edge goes with which output edges). + * If there is any face edge, return one of those as the original. + * If there is no face edge but there is another edge in the input problem, then that + * edge must have come from intersection with another triangle, so set *r_is_intersect + * to true in that case. */ static int get_cdt_edge_orig( int i0, int i1, const CDT_data &cd, const IMesh &in_tm, bool *r_is_intersect) { @@ -1900,35 +1910,50 @@ static int get_cdt_edge_orig( } /* Pick an arbitrary orig, but not one equal to NO_INDEX, if we can help it. */ - /* TODO: if edge has origs from more than on part of the nary input, + /* TODO: if edge has origs from more than one part of the nary input, * then want to set *r_is_intersect to true. */ + int face_eorig = NO_INDEX; + bool have_non_face_eorig = false; for (int orig_index : cd.cdt_out.edge_orig[e]) { /* orig_index encodes the triangle and pos within the triangle of the input edge. */ if (orig_index >= foff) { - int in_face_index = (orig_index / foff) - 1; - int pos = orig_index % foff; - /* We need to retrieve the edge orig field from the Face used to populate the - * in_face_index'th face of the CDT, at the pos'th position of the face. */ - int in_tm_face_index = cd.input_face[in_face_index]; - BLI_assert(in_tm_face_index < in_tm.face_size()); - const Face *facep = in_tm.face(in_tm_face_index); - BLI_assert(pos < facep->size()); - bool is_rev = cd.is_reversed[in_face_index]; - int eorig = is_rev ? facep->edge_orig[2 - pos] : facep->edge_orig[pos]; - if (eorig != NO_INDEX) { - return eorig; + if (face_eorig == NO_INDEX) { + int in_face_index = (orig_index / foff) - 1; + int pos = orig_index % foff; + /* We need to retrieve the edge orig field from the Face used to populate the + * in_face_index'th face of the CDT, at the pos'th position of the face. */ + int in_tm_face_index = cd.input_face[in_face_index]; + BLI_assert(in_tm_face_index < in_tm.face_size()); + const Face *facep = in_tm.face(in_tm_face_index); + BLI_assert(pos < facep->size()); + bool is_rev = cd.is_reversed[in_face_index]; + int eorig = is_rev ? facep->edge_orig[2 - pos] : facep->edge_orig[pos]; + if (eorig != NO_INDEX) { + face_eorig = eorig; + } } } else { - /* This edge came from an edge input to the CDT problem, - * so it is an intersect edge. */ - *r_is_intersect = true; - /* TODO: maybe there is an orig index: - * This happens if an input edge was formed by an input face having - * an edge that is co-planar with the cluster, while the face as a whole is not. */ - return NO_INDEX; + if (!have_non_face_eorig) { + have_non_face_eorig = true; + } + if (face_eorig != NO_INDEX && have_non_face_eorig) { + /* Only need at most one orig for each type. */ + break; + } } } + if (face_eorig != NO_INDEX) { + return face_eorig; + } + if (have_non_face_eorig) { + /* This must have been an input to the CDT problem that was an intersection edge. */ + /* TODO: maybe there is an orig index: + * This happens if an input edge was formed by an input face having + * an edge that is co-planar with the cluster, while the face as a whole is not. */ + *r_is_intersect = true; + return NO_INDEX; + } return NO_INDEX; } diff --git a/source/blender/blenlib/intern/noise.c b/source/blender/blenlib/intern/noise.c index 01aad5b078f..9850de69b5a 100644 --- a/source/blender/blenlib/intern/noise.c +++ b/source/blender/blenlib/intern/noise.c @@ -1294,13 +1294,12 @@ float BLI_noise_generic_turbulence( * source code in the book "Texturing and Modeling: A procedural approach" */ -/* - * Procedural fBm evaluated at "point"; returns value stored in "value". +/** + * Procedural `fBm` evaluated at "point"; returns value stored in "value". * - * Parameters: - * ``H'' is the fractal increment parameter - * ``lacunarity'' is the gap between successive frequencies - * ``octaves'' is the number of frequencies in the fBm + * \param H: is the fractal increment parameter. + * \param lacunarity: is the gap between successive frequencies. + * \param octaves: is the number of frequencies in the `fBm`. */ float BLI_noise_mg_fbm( float x, float y, float z, float H, float lacunarity, float octaves, int noisebasis) @@ -1359,15 +1358,16 @@ float BLI_noise_mg_fbm( } /* fBm() */ -/* - * Procedural multifractal evaluated at "point"; +/** + * Procedural multi-fractal evaluated at "point"; * returns value stored in "value". * - * Parameters: - * ``H'' determines the highest fractal dimension - * ``lacunarity'' is gap between successive frequencies - * ``octaves'' is the number of frequencies in the fBm - * ``offset'' is the zero offset, which determines multifractality (NOT USED??) + * \param H: determines the highest fractal dimension. + * \param lacunarity: is gap between successive frequencies. + * \param octaves: is the number of frequencies in the `fBm`. + * + * \note There used to be a parameter called `offset`, old docs read: + * is the zero offset, which determines multi-fractality. */ /* this one is in fact rather confusing, @@ -1429,15 +1429,14 @@ float BLI_noise_mg_multi_fractal( } /* multifractal() */ -/* +/** * Heterogeneous procedural terrain function: stats by altitude method. * Evaluated at "point"; returns value stored in "value". * - * Parameters: - * ``H'' determines the fractal dimension of the roughest areas - * ``lacunarity'' is the gap between successive frequencies - * ``octaves'' is the number of frequencies in the fBm - * ``offset'' raises the terrain from `sea level' + * \param H: Determines the fractal dimension of the roughest areas. + * \param lacunarity: Is the gap between successive frequencies. + * \param octaves: Is the number of frequencies in the `fBm`. + * \param offset: Raises the terrain from `sea level`. */ float BLI_noise_mg_hetero_terrain(float x, float y, diff --git a/source/blender/blenlib/intern/polyfill_2d.c b/source/blender/blenlib/intern/polyfill_2d.c index 817572ba85c..9af98359199 100644 --- a/source/blender/blenlib/intern/polyfill_2d.c +++ b/source/blender/blenlib/intern/polyfill_2d.c @@ -77,9 +77,9 @@ typedef signed char eSign; #ifdef USE_KDTREE /** * Spatial optimization for point-in-triangle intersection checks. - * The simple version of this algorithm is ``O(n^2)`` complexity + * The simple version of this algorithm is `O(n^2)` complexity * (every point needing to check the triangle defined by every other point), - * Using a binary-tree reduces the complexity to ``O(n log n)`` + * Using a binary-tree reduces the complexity to `O(n log n)` * plus some overhead of creating the tree. * * This is a single purpose KDTree based on BLI_kdtree with some modifications @@ -898,7 +898,7 @@ void BLI_polyfill_calc_arena(const float (*coords)[2], * \param coords_sign: Pass this when we know the sign in advance to avoid extra calculations. * * \param r_tris: This array is filled in with triangle indices in clockwise order. - * The length of the array must be ``coords_tot - 2``. + * The length of the array must be `coords_tot - 2`. * Indices are guaranteed to be assigned to unique triangles, with valid indices, * even in the case of degenerate input (self intersecting polygons, zero area ears... etc). */ diff --git a/source/blender/blenlib/intern/smallhash.c b/source/blender/blenlib/intern/smallhash.c index 6e5a3e961a5..006a3798dcd 100644 --- a/source/blender/blenlib/intern/smallhash.c +++ b/source/blender/blenlib/intern/smallhash.c @@ -34,11 +34,11 @@ * Otherwise #GHash should be used instead. * * #SmallHashEntry.key - * - ``SMHASH_KEY_UNUSED`` means the key in the cell has not been initialized. + * - `SMHASH_KEY_UNUSED` means the key in the cell has not been initialized. * * #SmallHashEntry.val - * - ``SMHASH_CELL_UNUSED`` means this cell is inside a key series. - * - ``SMHASH_CELL_FREE`` means this cell terminates a key series. + * - `SMHASH_CELL_UNUSED` means this cell is inside a key series. + * - `SMHASH_CELL_FREE` means this cell terminates a key series. * * Note that the values and keys are often pointers or index values, * use the maximum values to avoid real pointers colliding with magic numbers. diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index 61d095658a3..5541d75bc73 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -232,7 +232,7 @@ size_t BLI_vsnprintf(char *__restrict buffer, } /** - * A version of #BLI_vsnprintf that returns ``strlen(buffer)`` + * A version of #BLI_vsnprintf that returns `strlen(buffer)` */ size_t BLI_vsnprintf_rlen(char *__restrict buffer, size_t maxncpy, @@ -278,7 +278,7 @@ size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict } /** - * A version of #BLI_snprintf that returns ``strlen(dst)`` + * A version of #BLI_snprintf that returns `strlen(dst)` */ size_t BLI_snprintf_rlen(char *__restrict dst, size_t maxncpy, const char *__restrict format, ...) { diff --git a/source/blender/blenlib/intern/string_utils.c b/source/blender/blenlib/intern/string_utils.c index 6a2ed8fac2c..d2666c6fe63 100644 --- a/source/blender/blenlib/intern/string_utils.c +++ b/source/blender/blenlib/intern/string_utils.c @@ -182,8 +182,8 @@ void BLI_string_flip_side_name(char *r_name, /* We first check the case with a .### extension, let's find the last period */ if (isdigit(r_name[len - 1])) { - index = strrchr(r_name, '.'); /* last occurrence. */ - if (index && isdigit(index[1])) { /* doesn't handle case bone.1abc2 correct..., whatever! */ + index = strrchr(r_name, '.'); /* Last occurrence. */ + if (index && isdigit(index[1])) { /* Doesn't handle case `bone.1abc2` correct..., whatever! */ if (strip_number == false) { BLI_strncpy(number, index, name_len); } @@ -194,7 +194,7 @@ void BLI_string_flip_side_name(char *r_name, BLI_strncpy(prefix, r_name, name_len); - /* first case; separator . - _ with extensions r R l L. */ + /* First case; separator (`.` or `_`) with extensions in `r R l L`. */ if ((len > 1) && is_char_sep(r_name[len - 2])) { is_set = true; switch (r_name[len - 1]) { diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc index 367a3d7599f..b9e9db6e697 100644 --- a/source/blender/blenlib/intern/task_pool.cc +++ b/source/blender/blenlib/intern/task_pool.cc @@ -533,7 +533,7 @@ bool BLI_task_pool_current_canceled(TaskPool *pool) case TASK_POOL_BACKGROUND_SERIAL: return background_task_pool_canceled(pool); } - BLI_assert("BLI_task_pool_canceled: Control flow should not come here!"); + BLI_assert_msg(0, "BLI_task_pool_canceled: Control flow should not come here!"); return false; } diff --git a/source/blender/blenlib/intern/timecode.c b/source/blender/blenlib/intern/timecode.c index 7d7436411ac..69c383061fc 100644 --- a/source/blender/blenlib/intern/timecode.c +++ b/source/blender/blenlib/intern/timecode.c @@ -39,7 +39,7 @@ * Generate time-code/frame number string and store in \a str * * \param str: destination string - * \param maxncpy: maximum number of characters to copy ``sizeof(str)`` + * \param maxncpy: maximum number of characters to copy `sizeof(str)` * \param brevity_level: special setting for #View2D grid drawing, * used to specify how detailed we need to be * \param time_seconds: time total time in seconds @@ -115,7 +115,7 @@ size_t BLI_timecode_string_from_time(char *str, str, maxncpy, "%s%02d:%02d+%02d", neg, minutes, seconds, frames); } else { - rlen = BLI_snprintf_rlen(str, maxncpy, "%s%d+%02d", neg, seconds, frames); + rlen = BLI_snprintf_rlen(str, maxncpy, "%s00:%02d+%02d", neg, seconds, frames); } } else { @@ -199,7 +199,7 @@ size_t BLI_timecode_string_from_time(char *str, * Generate time string and store in \a str * * \param str: destination string - * \param maxncpy: maximum number of characters to copy ``sizeof(str)`` + * \param maxncpy: maximum number of characters to copy `sizeof(str)` * \param time_seconds: time total time in seconds * \return length of \a str */ @@ -229,7 +229,7 @@ size_t BLI_timecode_string_from_time_simple(char *str, * Generate time string and store in \a str * * \param str: destination string - * \param maxncpy: maximum number of characters to copy ``sizeof(str)`` + * \param maxncpy: maximum number of characters to copy `sizeof(str)` * \param brevity_level: special setting for #View2D grid drawing, * used to specify how detailed we need to be * \param time_seconds: time total time in seconds diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c index beeae175869..d5c9c5cd5e6 100644 --- a/source/blender/blenlib/intern/winstuff.c +++ b/source/blender/blenlib/intern/winstuff.c @@ -30,7 +30,7 @@ # include "MEM_guardedalloc.h" -# define WIN32_SKIP_HKEY_PROTECTION // need to use HKEY +# define WIN32_SKIP_HKEY_PROTECTION /* Need to use HKEY. */ # include "BLI_path_util.h" # include "BLI_string.h" # include "BLI_utildefines.h" diff --git a/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc b/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc index 08a3818e18f..f221036419e 100644 --- a/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc +++ b/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc @@ -241,7 +241,7 @@ template<typename T> std::ostream &operator<<(std::ostream &os, const CDT_result for (int i : r.edge.index_range()) { os << "e" << i << " = (" << r.edge[i].first << ", " << r.edge[i].second << ")\n"; os << " orig: "; - for (int j : r.edge_orig[i].size()) { + for (int j : r.edge_orig[i].index_range()) { os << r.edge_orig[i][j] << " "; } os << "\n"; @@ -797,6 +797,55 @@ template<typename T> void crosssegs_test() } } +template<typename T> void cutacrosstri_test() +{ + /* Right triangle with horizontal segment exactly crossing in the middle. */ + const char *spec = R"(5 1 1 + 0.0 0.0 + 1.0 0.0 + 0.0 1.0 + 0.0 0.5 + 0.5 0.5 + 3 4 + 0 1 2 + )"; + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 5); + EXPECT_EQ(out.edge.size(), 7); + EXPECT_EQ(out.face.size(), 3); + int v0_out = get_orig_index(out.vert_orig, 0); + int v1_out = get_orig_index(out.vert_orig, 1); + int v2_out = get_orig_index(out.vert_orig, 2); + int v3_out = get_orig_index(out.vert_orig, 3); + int v4_out = get_orig_index(out.vert_orig, 4); + EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1 && v3_out != -1 && v4_out != -1); + if (out.face.size() == 3) { + int e0_out = get_orig_index(out.edge_orig, 0); + EXPECT_NE(e0_out, -1); + int fe0_out = get_output_edge_index(out, v0_out, v1_out); + EXPECT_NE(fe0_out, -1); + int fe1a_out = get_output_edge_index(out, v1_out, v4_out); + EXPECT_NE(fe1a_out, -1); + int fe1b_out = get_output_edge_index(out, v4_out, v2_out); + EXPECT_NE(fe1b_out, -1); + if (fe1a_out != 0 && fe1b_out != 0) { + EXPECT_EQ(e0_out, get_orig_index(out.edge_orig, 0)); + EXPECT_TRUE(out.edge_orig[fe1a_out].size() == 1 && out.edge_orig[fe1a_out][0] == 11); + EXPECT_TRUE(out.edge_orig[fe1b_out].size() == 1 && out.edge_orig[fe1b_out][0] == 11); + } + int e_diag = get_output_edge_index(out, v0_out, v4_out); + EXPECT_NE(e_diag, -1); + if (e_diag != -1) { + EXPECT_EQ(out.edge_orig[e_diag].size(), 0); + } + } + if (DO_DRAW) { + graph_draw<T>("CutAcrossTri", out.vert, out.edge, out.face); + } +} + template<typename T> void diamondcross_test() { /* Diamond with constraint edge from top to bottom. Some dup verts. */ @@ -1470,6 +1519,11 @@ TEST(delaunay_d, CrossSegs) crosssegs_test<double>(); } +TEST(delaunay_d, CutAcrossTri) +{ + cutacrosstri_test<double>(); +} + TEST(delaunay_d, DiamondCross) { diamondcross_test<double>(); @@ -1610,6 +1664,11 @@ TEST(delaunay_m, CrossSegs) crosssegs_test<mpq_class>(); } +TEST(delaunay_m, CutAcrossTri) +{ + cutacrosstri_test<mpq_class>(); +} + TEST(delaunay_m, DiamondCross) { diamondcross_test<mpq_class>(); @@ -1697,14 +1756,40 @@ TEST(delaunay_d, CintTwoFace) input.faces_len_table = faces_len; input.faces_start_table = faces_start; input.epsilon = 1e-5f; + input.need_ids = false; ::CDT_result *output = BLI_delaunay_2d_cdt_calc(&input, CDT_FULL); BLI_delaunay_2d_cdt_free(output); } + +TEST(delaunay_d, CintTwoFaceNoIds) +{ + float vert_coords[][2] = { + {0.0, 0.0}, {1.0, 0.0}, {0.5, 1.0}, {1.1, 1.0}, {1.1, 0.0}, {1.6, 1.0}}; + int faces[] = {0, 1, 2, 3, 4, 5}; + int faces_len[] = {3, 3}; + int faces_start[] = {0, 3}; + + ::CDT_input input; + input.verts_len = 6; + input.edges_len = 0; + input.faces_len = 2; + input.vert_coords = vert_coords; + input.edges = nullptr; + input.faces = faces; + input.faces_len_table = faces_len; + input.faces_start_table = faces_start; + input.epsilon = 1e-5f; + input.need_ids = true; + ::CDT_result *output = BLI_delaunay_2d_cdt_calc(&input, CDT_FULL); + BLI_delaunay_2d_cdt_free(output); +} + #endif #if DO_TEXT_TESTS template<typename T> -void text_test(int num_arc_points, int num_lets_per_line, int num_lines, CDT_output_type otype) +void text_test( + int num_arc_points, int num_lets_per_line, int num_lines, CDT_output_type otype, bool need_ids) { constexpr bool print_timing = true; /* @@ -1789,7 +1874,7 @@ void text_test(int num_arc_points, int num_lets_per_line, int num_lines, CDT_out vec2<T> start_co = b_vert[arc_origin_vert]; vec2<T> end_co = b_vert[arc_terminal_vert]; vec2<T> center_co = 0.5 * (start_co + end_co); - BLI_assert(start_co[0] == end_co[2]); + BLI_assert(start_co[0] == end_co[0]); double radius = abs(math_to_double<T>(end_co[1] - center_co[1])); double angle_delta = M_PI / (num_arc_points + 1); int start_vert = b_before_arcs_in.vert.size() + arc * num_arc_points; @@ -1843,12 +1928,18 @@ void text_test(int num_arc_points, int num_lets_per_line, int num_lines, CDT_out } } in.epsilon = b_before_arcs_in.epsilon; + in.need_ids = need_ids; double tstart = PIL_check_seconds_timer(); CDT_result<T> out = delaunay_2d_calc(in, otype); double tend = PIL_check_seconds_timer(); if (print_timing) { std::cout << "time = " << tend - tstart << "\n"; } + if (!need_ids) { + EXPECT_EQ(out.vert_orig.size(), 0); + EXPECT_EQ(out.edge_orig.size(), 0); + EXPECT_EQ(out.face_orig.size(), 0); + } if (DO_DRAW) { std::string label = "Text arcpts=" + std::to_string(num_arc_points); if (num_lets_per_line > 1) { @@ -1857,25 +1948,103 @@ void text_test(int num_arc_points, int num_lets_per_line, int num_lines, CDT_out if (num_lines > 1) { label += " lines=" + std::to_string(num_lines); } + if (!need_ids) { + label += " no_ids"; + } + if (otype != CDT_INSIDE_WITH_HOLES) { + label += " otype=" + std::to_string(otype); + } graph_draw<T>(label, out.vert, out.edge, out.face); } } TEST(delaunay_d, TextB10) { - text_test<double>(10, 1, 1, CDT_INSIDE_WITH_HOLES); + text_test<double>(10, 1, 1, CDT_INSIDE_WITH_HOLES, true); +} + +TEST(delaunay_d, TextB10_noids) +{ + text_test<double>(10, 1, 1, CDT_INSIDE_WITH_HOLES, false); +} + +TEST(delaunay_d, TextB10_inside) +{ + text_test<double>(10, 1, 1, CDT_INSIDE, true); +} + +TEST(delaunay_d, TextB10_inside_noids) +{ + text_test<double>(10, 1, 1, CDT_INSIDE, false); +} + +TEST(delaunay_d, TextB10_constraints) +{ + text_test<double>(10, 1, 1, CDT_CONSTRAINTS, true); +} + +TEST(delaunay_d, TextB10_constraints_noids) +{ + text_test<double>(10, 1, 1, CDT_CONSTRAINTS, false); +} + +TEST(delaunay_d, TextB10_constraints_valid_bmesh) +{ + text_test<double>(10, 1, 1, CDT_CONSTRAINTS_VALID_BMESH, true); +} + +TEST(delaunay_d, TextB10_constraints_valid_bmesh_noids) +{ + text_test<double>(10, 1, 1, CDT_CONSTRAINTS_VALID_BMESH, false); +} + +TEST(delaunay_d, TextB10_constraints_valid_bmesh_with_holes) +{ + text_test<double>(10, 1, 1, CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES, true); +} + +TEST(delaunay_d, TextB10_constraints_valid_bmesh_with_holes_noids) +{ + text_test<double>(10, 1, 1, CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES, false); } TEST(delaunay_d, TextB200) { - text_test<double>(200, 1, 1, CDT_INSIDE_WITH_HOLES); + text_test<double>(200, 1, 1, CDT_INSIDE_WITH_HOLES, true); } TEST(delaunay_d, TextB10_10_10) { - text_test<double>(10, 10, 10, CDT_INSIDE_WITH_HOLES); + text_test<double>(10, 10, 10, CDT_INSIDE_WITH_HOLES, true); +} + +TEST(delaunay_d, TextB10_10_10_noids) +{ + text_test<double>(10, 10, 10, CDT_INSIDE_WITH_HOLES, false); } +# ifdef WITH_GMP +TEST(delaunay_m, TextB10) +{ + text_test<mpq_class>(10, 1, 1, CDT_INSIDE_WITH_HOLES, true); +} + +TEST(delaunay_m, TextB200) +{ + text_test<mpq_class>(200, 1, 1, CDT_INSIDE_WITH_HOLES, true); +} + +TEST(delaunay_m, TextB10_10_10) +{ + text_test<mpq_class>(10, 10, 10, CDT_INSIDE_WITH_HOLES, true); +} + +TEST(delaunay_m, TextB10_10_10_noids) +{ + text_test<mpq_class>(10, 10, 10, CDT_INSIDE_WITH_HOLES, false); +} +# endif + #endif #if DO_RANDOM_TESTS diff --git a/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc index 24fa7f1a476..0329fc156c0 100644 --- a/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc +++ b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc @@ -866,11 +866,11 @@ static void fill_sphere_data(int nrings, }; Array<int> eid = {0, 0, 0, 0}; /* Don't care about edge ids. */ /* - * (x, y , z) is given from inclination theta and azimuth phi, - * where 0 <= theta <= pi; 0 <= phi <= 2pi. - * x = radius * sin(theta) cos(phi) - * y = radius * sin(theta) sin(phi) - * z = radius * cos(theta) + * (x, y, z) is given from inclination theta and azimuth phi, + * where: `0 <= theta <= pi; 0 <= phi <= 2pi`. + * `x = radius * sin(theta) cos(phi)` + * `y = radius * sin(theta) sin(phi)` + * `z = radius * cos(theta)` */ for (int s = 0; s < nsegs; ++s) { double phi = s * delta_phi; diff --git a/source/blender/blenlib/tests/BLI_string_ref_test.cc b/source/blender/blenlib/tests/BLI_string_ref_test.cc index fb8b894bfd5..4ecea8031ca 100644 --- a/source/blender/blenlib/tests/BLI_string_ref_test.cc +++ b/source/blender/blenlib/tests/BLI_string_ref_test.cc @@ -278,6 +278,44 @@ TEST(string_ref, DropSuffixLargeN) EXPECT_EQ(ref2, ""); } +TEST(string_ref, TrimArbitrary) +{ + StringRef ref1("test"); + StringRef ref2(" test "); + StringRef ref3(" \t Urož with spaces "); + StringRef ref4("žžžžleepyžžž"); + EXPECT_EQ(ref1.trim("t"), "es"); + EXPECT_EQ(ref1.trim("te"), "s"); + EXPECT_EQ(ref1.trim("test"), ""); + EXPECT_EQ(ref2.trim("t"), " test "); + EXPECT_EQ(ref2.trim(""), " test "); + EXPECT_EQ(ref3.trim(" "), "\t Urož with spaces"); /* TAB should be kept. */ + EXPECT_EQ(ref4.trim("ž"), "leepy"); +} + +TEST(string_ref, TrimWhitespace) +{ + StringRef ref1("test"); + StringRef ref2(" test "); + StringRef ref3(" \t Urož with spaces "); + StringRef ref4(" \t \n\r \t "); + EXPECT_EQ(ref1.trim(), "test"); + EXPECT_EQ(ref2.trim(), "test"); + EXPECT_EQ(ref3.trim(), "Urož with spaces"); + EXPECT_EQ(ref4.trim(), ""); +} + +TEST(string_ref, TrimCharacter) +{ + StringRef ref1("test"); + StringRef ref2(" test "); + StringRef ref3("does this work?"); + EXPECT_EQ(ref1.trim('t'), "es"); + EXPECT_EQ(ref1.trim('p'), "test"); + EXPECT_EQ(ref2.trim(' '), "test"); + EXPECT_EQ(ref3.trim('\000'), "does this work?"); +} + TEST(string_ref, Substr) { StringRef ref("hello world"); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 03fb4149d7b..e48c305fc4b 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -464,6 +464,13 @@ void blo_join_main(ListBase *mainlist) Main *tojoin, *mainl; mainl = mainlist->first; + + if (mainl->id_map != NULL) { + /* Cannot keep this since we add some IDs from joined mains. */ + BKE_main_idmap_destroy(mainl->id_map); + mainl->id_map = NULL; + } + while ((tojoin = mainl->next)) { add_main_to_main(mainl, tojoin); BLI_remlink(mainlist, tojoin); @@ -502,6 +509,12 @@ void blo_split_main(ListBase *mainlist, Main *main) return; } + if (main->id_map != NULL) { + /* Cannot keep this since we remove some IDs from given main. */ + BKE_main_idmap_destroy(main->id_map); + main->id_map = NULL; + } + /* (Library.temp_index -> Main), lookup table */ const uint lib_main_array_len = BLI_listbase_count(&main->libraries); Main **lib_main_array = MEM_malloc_arrayN(lib_main_array_len, sizeof(*lib_main_array), __func__); @@ -2444,7 +2457,7 @@ static void direct_link_id_common( BlendDataReader *reader, Library *current_library, ID *id, ID *id_old, const int tag) { if (!BLO_read_data_is_undo(reader)) { - /* When actually reading a file , we do want to reset/re-generate session uuids. + /* When actually reading a file, we do want to reset/re-generate session uuids. * In undo case, we want to re-use existing ones. */ id->session_uuid = MAIN_ID_SESSION_UUID_UNSET; } @@ -3209,6 +3222,10 @@ static ID *create_placeholder(Main *mainvar, const short idcode, const char *idn BLI_addtail(lb, ph_id); id_sort_by_name(lb, ph_id, NULL); + if (mainvar->id_map != NULL) { + BKE_main_idmap_insert_id(mainvar->id_map, ph_id); + } + if ((tag & LIB_TAG_TEMP_MAIN) == 0) { BKE_lib_libblock_session_uuid_ensure(ph_id); } @@ -3667,6 +3684,10 @@ static BHead *read_libblock(FileData *fd, if (r_id) { *r_id = id_old; } + if (main->id_map != NULL) { + BKE_main_idmap_insert_id(main->id_map, id_old); + } + return blo_bhead_next(fd, bhead); } } @@ -3725,6 +3746,11 @@ static BHead *read_libblock(FileData *fd, } direct_link_id(fd, main, id_tag, id, id_old); + + if (main->id_map != NULL) { + BKE_main_idmap_insert_id(main->id_map, id); + } + return blo_bhead_next(fd, bhead); } @@ -3748,6 +3774,13 @@ static BHead *read_libblock(FileData *fd, else if (id_old) { /* For undo, store contents read into id at id_old. */ read_libblock_undo_restore_at_old_address(fd, main, id, id_old); + + if (main->id_map != NULL) { + BKE_main_idmap_insert_id(main->id_map, id_old); + } + } + else if (main->id_map != NULL) { + BKE_main_idmap_insert_id(main->id_map, id); } return bhead; @@ -4299,6 +4332,8 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) fd->mainlist = NULL; /* Safety, this is local variable, shall not be used afterward. */ + BLI_assert(bfd->main->id_map == NULL); + return bfd; } @@ -4443,9 +4478,16 @@ static BHead *find_bhead_from_idname(FileData *fd, const char *idname) static ID *is_yet_read(FileData *fd, Main *mainvar, BHead *bhead) { + if (mainvar->id_map == NULL) { + mainvar->id_map = BKE_main_idmap_create(mainvar, false, NULL, MAIN_IDMAP_TYPE_NAME); + } + BLI_assert(BKE_main_idmap_main_get(mainvar->id_map) == mainvar); + const char *idname = blo_bhead_id_name(fd, bhead); - /* which_libbase can be NULL, intentionally not using idname+2 */ - return BLI_findstring(which_libbase(mainvar, GS(idname)), idname, offsetof(ID, name)); + + ID *id = BKE_main_idmap_lookup_name(mainvar->id_map, GS(idname), idname + 2, mainvar->curlib); + BLI_assert(id == BLI_findstring(which_libbase(mainvar, GS(idname)), idname, offsetof(ID, name))); + return id; } /** \} */ @@ -5196,6 +5238,10 @@ static void library_link_end(Main *mainl, Main *mainvar; Library *curlib; + if (mainl->id_map == NULL) { + mainl->id_map = BKE_main_idmap_create(mainl, false, NULL, MAIN_IDMAP_TYPE_NAME); + } + /* expander now is callback function */ BLO_main_expander(expand_doit_library); @@ -5401,6 +5447,9 @@ static void read_library_linked_ids(FileData *basefd, ID *id_next = id->next; if ((id->tag & LIB_TAG_ID_LINK_PLACEHOLDER) && !(id->flag & LIB_INDIRECT_WEAK_LINK)) { BLI_remlink(lbarray[a], id); + if (mainvar->id_map != NULL) { + BKE_main_idmap_remove_id(mainvar->id_map, id); + } /* When playing with lib renaming and such, you may end with cases where * you have more than one linked ID of the same data-block from same @@ -5569,6 +5618,10 @@ static void read_libraries(FileData *basefd, ListBase *mainlist) if (fd) { do_it = true; + + if (mainptr->id_map == NULL) { + mainptr->id_map = BKE_main_idmap_create(mainptr, false, NULL, MAIN_IDMAP_TYPE_NAME); + } } /* Read linked data-locks for each link placeholder, and replace diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 8a7bc375ea9..e56c1995363 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -132,7 +132,7 @@ static void sequencer_init_preview_region(ARegion *region) region->v2d.max[0] = 12000.0f; region->v2d.max[1] = 12000.0f; region->v2d.cur = region->v2d.tot; - region->v2d.align = V2D_ALIGN_FREE; // (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_NEG_Y); + region->v2d.align = V2D_ALIGN_FREE; /* `(V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_NEG_Y)` */ region->v2d.keeptot = V2D_KEEPTOT_FREE; } @@ -655,8 +655,10 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) Tex *tx; ParticleSettings *part; Object *ob; - // PTCacheID *pid; - // ListBase pidlist; +#if 0 + PTCacheID *pid; + ListBase pidlist; +#endif bSound *sound; Sequence *seq; @@ -766,12 +768,15 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) /* set old pointcaches to have disk cache flag */ for (ob = bmain->objects.first; ob; ob = ob->id.next) { - // BKE_ptcache_ids_from_object(&pidlist, ob); +#if 0 + BKE_ptcache_ids_from_object(&pidlist, ob); - // for (pid = pidlist.first; pid; pid = pid->next) - // pid->cache->flag |= PTCACHE_DISK_CACHE; + for (pid = pidlist.first; pid; pid = pid->next) { + pid->cache->flag |= PTCACHE_DISK_CACHE; + } - // BLI_freelistN(&pidlist); + BLI_freelistN(&pidlist); +#endif } /* type was a mixed flag & enum. move the 2d flag elsewhere */ @@ -789,18 +794,23 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) Tex *tex; Scene *sce; ToolSettings *ts; - // PTCacheID *pid; - // ListBase pidlist; +#if 0 + PTCacheID *pid; + ListBase pidlist; +#endif for (ob = bmain->objects.first; ob; ob = ob->id.next) { - // BKE_ptcache_ids_from_object(&pidlist, ob); +#if 0 + BKE_ptcache_ids_from_object(&pidlist, ob); - // for (pid = pidlist.first; pid; pid = pid->next) { - // if (BLI_listbase_is_empty(pid->ptcaches)) - // pid->ptcaches->first = pid->ptcaches->last = pid->cache; - //} + for (pid = pidlist.first; pid; pid = pid->next) { + if (BLI_listbase_is_empty(pid->ptcaches)) { + pid->ptcaches->first = pid->ptcaches->last = pid->cache; + } + } - // BLI_freelistN(&pidlist); + BLI_freelistN(&pidlist); +#endif if (ob->totcol && ob->matbits == NULL) { int a; @@ -842,7 +852,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) Object *ob; for (ob = bmain->objects.first; ob; ob = ob->id.next) { - if (ob->flag & 8192) { // OB_POSEMODE = 8192 + if (ob->flag & 8192) { /* OB_POSEMODE = 8192. */ ob->mode |= OB_MODE_POSE; } } @@ -1395,7 +1405,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) } if ((sce->r.ffcodecdata.flags & FFMPEG_MULTIPLEX_AUDIO) == 0) { - sce->r.ffcodecdata.audio_codec = 0x0; // CODEC_ID_NONE + sce->r.ffcodecdata.audio_codec = 0x0; /* `CODEC_ID_NONE` */ } SEQ_ALL_BEGIN (sce->ed, seq) { @@ -1735,7 +1745,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) /* New Settings */ if (!MAIN_VERSION_ATLEAST(bmain, 252, 5)) { - brush->flag |= BRUSH_SPACE_ATTEN; // explicitly enable adaptive space + brush->flag |= BRUSH_SPACE_ATTEN; /* Explicitly enable adaptive space. */ /* spacing was originally in pixels, convert it to percentage for new version * size should not be zero due to sanity check above diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index 428a612ec50..12ee4f37450 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -2149,7 +2149,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) } for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - /* NB: scene->nodetree is a local ID block, has been direct_link'ed */ + /* NOTE: `scene->nodetree` is a local ID block, has been direct_link'ed. */ if (scene->nodetree) { scene->nodetree->active_viewer_key = active_viewer_key; } diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 776f6c54363..1d46c0d5790 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -1115,8 +1115,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!DNA_struct_elem_find(fd->filesdna, "ToolSettings", "char", "gpencil_v3d_align")) { ts->gpencil_v3d_align = GP_PROJECT_VIEWSPACE; ts->gpencil_v2d_align = GP_PROJECT_VIEWSPACE; - ts->gpencil_seq_align = GP_PROJECT_VIEWSPACE; - ts->gpencil_ima_align = GP_PROJECT_VIEWSPACE; } } diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 313ce734bbc..95440f78cd2 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -29,8 +29,10 @@ #include "DNA_armature_types.h" #include "DNA_brush_types.h" #include "DNA_collection_types.h" +#include "DNA_curve_types.h" #include "DNA_genfile.h" #include "DNA_listBase.h" +#include "DNA_material_types.h" #include "DNA_modifier_types.h" #include "DNA_text_types.h" #include "DNA_workspace_types.h" @@ -40,6 +42,7 @@ #include "BKE_asset.h" #include "BKE_collection.h" #include "BKE_deform.h" +#include "BKE_fcurve.h" #include "BKE_fcurve_driver.h" #include "BKE_lib_id.h" #include "BKE_main.h" @@ -48,11 +51,10 @@ #include "BLO_readfile.h" #include "MEM_guardedalloc.h" #include "readfile.h" -#include "versioning_common.h" #include "SEQ_sequencer.h" -#include "MEM_guardedalloc.h" +#include "RNA_access.h" #include "versioning_common.h" @@ -99,11 +101,85 @@ static void move_vertex_group_names_to_object_data(Main *bmain) if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) { ListBase *new_defbase = BKE_object_defgroup_list_mutable(object); - /* Clear the list in case the it was already assigned from another object. */ - BLI_freelistN(new_defbase); - *new_defbase = object->defbase; + /* Choose the longest vertex group name list among all linked duplicates. */ + if (BLI_listbase_count(&object->defbase) < BLI_listbase_count(new_defbase)) { + BLI_freelistN(&object->defbase); + } + else { + /* Clear the list in case the it was already assigned from another object. */ + BLI_freelistN(new_defbase); + *new_defbase = object->defbase; + } + } + } +} + +static void do_versions_sequencer_speed_effect_recursive(Scene *scene, const ListBase *seqbase) +{ + /* Old SpeedControlVars->flags. */ +#define SEQ_SPEED_INTEGRATE (1 << 0) +#define SEQ_SPEED_COMPRESS_IPO_Y (1 << 2) + + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (seq->type == SEQ_TYPE_SPEED) { + SpeedControlVars *v = (SpeedControlVars *)seq->effectdata; + const char *substr = NULL; + float globalSpeed = v->globalSpeed; + if (seq->flag & SEQ_USE_EFFECT_DEFAULT_FADE) { + if (globalSpeed == 1.0f) { + v->speed_control_type = SEQ_SPEED_STRETCH; + } + else { + v->speed_control_type = SEQ_SPEED_MULTIPLY; + v->speed_fader = globalSpeed * + ((float)seq->seq1->len / + max_ff((float)(seq->seq1->enddisp - seq->seq1->start), 1.0f)); + } + } + else if (v->flags & SEQ_SPEED_INTEGRATE) { + v->speed_control_type = SEQ_SPEED_MULTIPLY; + v->speed_fader = seq->speed_fader * globalSpeed; + } + else if (v->flags & SEQ_SPEED_COMPRESS_IPO_Y) { + globalSpeed *= 100.0f; + v->speed_control_type = SEQ_SPEED_LENGTH; + v->speed_fader_length = seq->speed_fader * globalSpeed; + substr = "speed_length"; + } + else { + v->speed_control_type = SEQ_SPEED_FRAME_NUMBER; + v->speed_fader_frame_number = (int)(seq->speed_fader * globalSpeed); + substr = "speed_frame_number"; + } + + v->flags &= ~(SEQ_SPEED_INTEGRATE | SEQ_SPEED_COMPRESS_IPO_Y); + + if (substr || globalSpeed != 1.0f) { + FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_factor", 0, NULL); + if (fcu) { + if (globalSpeed != 1.0f) { + for (int i = 0; i < fcu->totvert; i++) { + BezTriple *bezt = &fcu->bezt[i]; + bezt->vec[0][1] *= globalSpeed; + bezt->vec[1][1] *= globalSpeed; + bezt->vec[2][1] *= globalSpeed; + } + } + if (substr) { + char *new_path = BLI_str_replaceN(fcu->rna_path, "speed_factor", substr); + MEM_freeN(fcu->rna_path); + fcu->rna_path = new_path; + } + } + } + } + else if (seq->type == SEQ_TYPE_META) { + do_versions_sequencer_speed_effect_recursive(scene, &seq->seqbase); } } + +#undef SEQ_SPEED_INTEGRATE +#undef SEQ_SPEED_COMPRESS_IPO_Y } void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) @@ -154,6 +230,14 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) move_vertex_group_names_to_object_data(bmain); } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 13)) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->ed != NULL) { + do_versions_sequencer_speed_effect_recursive(scene, &scene->ed->seqbase); + } + } + } + /** * Versioning code until next subversion bump goes here. * @@ -499,6 +583,11 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) sizeof(scene->master_collection->id.name) - 2); } } + LISTBASE_FOREACH (Material *, mat, &bmain->materials) { + if (!(mat->lineart.flags & LRT_MATERIAL_CUSTOM_OCCLUSION_EFFECTIVENESS)) { + mat->lineart.mat_occlusion = 1; + } + } } if (!MAIN_VERSION_ATLEAST(bmain, 300, 9)) { @@ -519,15 +608,6 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } FOREACH_NODETREE_END; - - { - if (!DNA_struct_elem_find( - fd->filesdna, "WorkSpace", "AssetLibraryReference", "active_asset_library")) { - LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) { - BKE_asset_library_reference_init_default(&workspace->active_asset_library); - } - } - } } if (!MAIN_VERSION_ATLEAST(bmain, 300, 10)) { @@ -540,18 +620,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ - + if (!MAIN_VERSION_ATLEAST(bmain, 300, 13)) { /* Convert Surface Deform to sparse-capable bind structure. */ if (!DNA_struct_elem_find( fd->filesdna, "SurfaceDeformModifierData", "int", "num_mesh_verts")) { @@ -570,5 +639,68 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + if (!DNA_struct_elem_find( + fd->filesdna, "WorkSpace", "AssetLibraryReference", "asset_library")) { + LISTBASE_FOREACH (WorkSpace *, workspace, &bmain->workspaces) { + BKE_asset_library_reference_init_default(&workspace->asset_library); + } + } + + if (!DNA_struct_elem_find( + fd->filesdna, "FileAssetSelectParams", "AssetLibraryReference", "asset_library")) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) { + if (space->spacetype == SPACE_FILE) { + SpaceFile *sfile = (SpaceFile *)space; + if (sfile->browse_mode != FILE_BROWSE_MODE_ASSETS) { + continue; + } + BKE_asset_library_reference_init_default(&sfile->asset_params->asset_library); + } + } + } + } + } + + /* Set default 2D annotation placement. */ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + ToolSettings *ts = scene->toolsettings; + ts->gpencil_v2d_align = GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR; + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 14)) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + ToolSettings *tool_settings = scene->toolsettings; + tool_settings->snap_flag &= ~SCE_SNAP_SEQ; + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 15)) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = (SpaceSeq *)sl; + sseq->flag |= SEQ_SHOW_GRID; + } + } + } + } + } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index d0a2fe541cc..6edd46f83fb 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -183,7 +183,7 @@ static void blo_update_defaults_screen(bScreen *screen, else if (area->spacetype == SPACE_SEQ) { SpaceSeq *seq = area->spacedata.first; seq->flag |= SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES | SEQ_ZOOM_TO_FIT | SEQ_SHOW_STRIP_OVERLAY | - SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_NAME | SEQ_SHOW_STRIP_DURATION; + SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_NAME | SEQ_SHOW_STRIP_DURATION | SEQ_SHOW_GRID; seq->render_size = SEQ_RENDER_SIZE_PROXY_100; seq->flag |= SEQ_USE_PROXIES; diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 30587418f84..c409f0a71fc 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -286,6 +286,11 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) FROM_DEFAULT_V4_UCHAR(space_spreadsheet.selected_highlight); } + if (!USER_VERSION_ATLEAST(300, 15)) { + copy_v4_uchar(btheme->space_sequencer.grid, 33); + btheme->space_sequencer.grid[3] = 255; + } + /** * Versioning code until next subversion bump goes here. * @@ -883,6 +888,10 @@ void blo_do_versions_userdef(UserDef *userdef) userdef->sequencer_proxy_setup = USER_SEQ_PROXY_SETUP_AUTOMATIC; } + if (!USER_VERSION_ATLEAST(293, 13)) { + BKE_addon_ensure(&userdef->addons, "pose_library"); + } + /** * Versioning code until next subversion bump goes here. * @@ -894,7 +903,6 @@ void blo_do_versions_userdef(UserDef *userdef) */ { /* Keep this block, even when empty. */ - BKE_addon_ensure(&userdef->addons, "pose_library"); } LISTBASE_FOREACH (bTheme *, btheme, &userdef->themes) { diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index fc29b1d8915..12839a155e4 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -69,7 +69,7 @@ * - write #TEST (#RenderInfo struct. 128x128 blend file preview is optional). * - write #GLOB (#FileGlobal struct) (some global vars). * - write #DNA1 (#SDNA struct) - * - write #USER (#UserDef struct) if filename is ``~/.config/blender/X.XX/config/startup.blend``. + * - write #USER (#UserDef struct) if filename is `~/.config/blender/X.XX/config/startup.blend`. */ #include <fcntl.h> diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h index a5241f6b36d..40db423ba2f 100644 --- a/source/blender/bmesh/bmesh.h +++ b/source/blender/bmesh/bmesh.h @@ -37,7 +37,7 @@ * * BMHeader flags should **never** be read or written to by bmesh operators (see Operators below). * - * Access to header flags is done with ``BM_elem_flag_*()`` functions. + * Access to header flags is done with `BM_elem_flag_*()` functions. * \subsection bm_faces Faces * * Faces in BMesh are stored as a circular linked list of loops. Loops store per-face-vertex data @@ -55,7 +55,7 @@ * * - BMLoop#v - pointer to the vertex associated with this loop. * - BMLoop#e - pointer to the edge associated with this loop, - * between verts ``(loop->v, loop->next->v)`` + * between verts `(loop->v, loop->next->v)` * - BMLoop#f - pointer to the face associated with this loop. * \subsection bm_two_side_face 2-Sided Faces * @@ -113,7 +113,7 @@ * * These slots are identified by name, using strings. * - * Access to slots is done with ``BMO_slot_***()`` functions. + * Access to slots is done with `BMO_slot_***()` functions. * \subsection bm_tool_flags Tool Flags * * The BMesh API provides a set of flags for faces, edges and vertices, @@ -126,7 +126,7 @@ * These flags should not be confused with header flags, which are used to store persistent flags * (e.g. selection, hide status, etc). * - * Access to tool flags is done with ``BMO_elem_flag_***()`` functions. + * Access to tool flags is done with `BMO_elem_flag_***()` functions. * * \warning Operators are **never** allowed to read or write to header flags. * They act entirely on the data inside their input slots. @@ -162,14 +162,14 @@ * * These conventions should be used throughout the bmesh module. * - * - ``bmesh_kernel_*()`` - Low level API, for primitive functions that others are built ontop of. - * - ``bmesh_***()`` - Low level API function. - * - ``bm_***()`` - 'static' functions, not a part of the API at all, + * - `bmesh_kernel_*()` - Low level API, for primitive functions that others are built ontop of. + * - `bmesh_***()` - Low level API function. + * - `bm_***()` - 'static' functions, not a part of the API at all, * but use prefix since they operate on BMesh data. - * - ``BM_***()`` - High level BMesh API function for use anywhere. - * - ``BMO_***()`` - High level operator API function for use anywhere. - * - ``bmo_***()`` - Low level / internal operator API functions. - * - ``_bm_***()`` - Functions which are called via macros only. + * - `BM_***()` - High level BMesh API function for use anywhere. + * - `BMO_***()` - High level operator API function for use anywhere. + * - `bmo_***()` - Low level / internal operator API functions. + * - `_bm_***()` - Functions which are called via macros only. * * \section bm_todo BMesh TODO's * diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h index a2b848c3b52..d5d75b394b0 100644 --- a/source/blender/bmesh/bmesh_class.h +++ b/source/blender/bmesh/bmesh_class.h @@ -36,6 +36,7 @@ struct BMFace; struct BMLoop; struct BMVert; struct BMesh; +struct GSet; struct MLoopNorSpaceArray; @@ -187,7 +188,7 @@ typedef struct BMLoop { struct BMFace *f; /** - * Other loops connected to this edge,. + * Other loops connected to this edge. * * This is typically use for accessing an edges faces, * however this is done by stepping over it's loops. @@ -299,6 +300,8 @@ typedef struct BMFlagLayer { struct RangeTreeUInt; +//#define WITH_BM_ID_FREELIST + typedef struct BMesh { int totvert, totedge, totloop, totface; int totvertsel, totedgesel, totfacesel; @@ -388,7 +391,13 @@ typedef struct BMesh { struct { int flag; +#ifdef WITH_BM_ID_FREELIST + uint *freelist; + int freelist_len, freelist_size; + struct GSet *free_ids; +#else struct RangeTreeUInt *idtree; +#endif uint maxid; struct BMElem **map; int map_size; diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index 3e5f1b7a086..6aa7b0ca44b 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -26,6 +26,7 @@ #include "MEM_guardedalloc.h" #include "BLI_alloca.h" +#include "BLI_ghash.h" #include "BLI_math.h" #include "BLI_sort_utils.h" @@ -40,6 +41,70 @@ #define SELECT 1 +#ifdef WITH_BM_ID_FREELIST +static uint bm_id_freelist_pop(BMesh *bm) +{ + if (bm->idmap.freelist_len > 0) { + return bm->idmap.freelist[--bm->idmap.freelist_len]; + } + + return 0; +} + +static void bm_id_freelist_take(BMesh *bm, uint id) +{ + if (!bm->idmap.free_ids || !BLI_gset_haskey(bm->idmap.free_ids, POINTER_FROM_UINT(id))) { + return; + } + + for (int i = 0; i < bm->idmap.freelist_len; i++) { + if (bm->idmap.freelist[i] == id) { + // swap with end + bm->idmap.freelist[i] = bm->idmap.freelist[bm->idmap.freelist_len - 1]; + bm->idmap.freelist_len--; + } + } +} + +static bool bm_id_freelist_has(BMesh *bm, uint id) +{ + if (!bm->idmap.free_ids) { + return false; + } + + return BLI_gset_haskey(bm->idmap.free_ids, POINTER_FROM_UINT(id)); +} + +void bm_id_freelist_push(BMesh *bm, uint id) +{ + bm->idmap.freelist_len++; + + if (!bm->idmap.free_ids) { + bm->idmap.free_ids = BLI_gset_ptr_new("free_ids"); + } + + if (bm->idmap.freelist_len >= bm->idmap.freelist_size) { + int size = 2 + bm->idmap.freelist_size + (bm->idmap.freelist_size >> 1); + + uint *newlist; + + if (bm->idmap.freelist) { + newlist = MEM_reallocN(bm->idmap.freelist, size * sizeof(uint)); + memcpy((void *)newlist, (void *)bm->idmap.freelist, bm->idmap.freelist_size); + } + else { + newlist = MEM_malloc_arrayN(size, sizeof(uint), "bm->idmap.freelist"); + } + + bm->idmap.freelist_size = size; + bm->idmap.freelist = newlist; + } + + bm->idmap.freelist[bm->idmap.freelist_len - 1] = id; + BLI_gset_add(bm->idmap.free_ids, POINTER_FROM_UINT(id)); +} +#endif + static const int _typemap[] = {0, 0, 1, 0, 2, 0, 0, 0, 3}; static void bm_assign_id_intern(BMesh *bm, BMElem *elem, uint id) @@ -71,7 +136,11 @@ static void bm_assign_id_intern(BMesh *bm, BMElem *elem, uint id) void bm_assign_id(BMesh *bm, BMElem *elem, uint id) { +#ifdef WITH_BM_ID_FREELIST + bm_id_freelist_take(bm, id); +#else range_tree_uint_retake(bm->idmap.idtree, id); +#endif bm_assign_id_intern(bm, elem, id); } @@ -81,7 +150,19 @@ void bm_alloc_id(BMesh *bm, BMElem *elem) return; } +#ifdef WITH_BM_ID_FREELIST + uint id; + + if (bm->idmap.freelist_len > 0) { + id = bm_id_freelist_pop(bm); + } + else { + id = bm->idmap.maxid + 1; + } +#else uint id = range_tree_uint_take_any(bm->idmap.idtree); +#endif + bm_assign_id_intern(bm, elem, id); } @@ -91,10 +172,15 @@ void bm_free_id(BMesh *bm, BMElem *elem) return; } - uint id = BM_ELEM_CD_GET_INT(elem, bm->idmap.cd_id_off[elem->head.htype]); - if (range_tree_uint_has(bm->idmap.idtree, id)) { + uint id = (uint)BM_ELEM_CD_GET_INT(elem, bm->idmap.cd_id_off[elem->head.htype]); + +#ifndef WITH_BM_ID_FREELIST + if (!range_tree_uint_has(bm->idmap.idtree, id)) { range_tree_uint_release(bm->idmap.idtree, id); } +#else + +#endif if ((bm->idmap.flag & BM_HAS_ID_MAP) && bm->idmap.map && id >= 0 && id < bm->idmap.map_size) { bm->idmap.map[id] = NULL; diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index 3d16da9a38c..9cf96ed320e 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -789,7 +789,12 @@ static void bm_kill_only_vert(BMesh *bm, BMVert *v) BLI_mempool_free(bm->vpool, v); } -void BM_reassign_ids(BMesh *bm) +#ifdef WITH_BM_ID_FREELIST +void bm_id_freelist_push(BMesh *bm, uint id); +#endif + +// does not modify actual element ids +void BM_clear_ids(BMesh *bm) { if (!(bm->idmap.flag & BM_HAS_IDS)) { return; @@ -799,11 +804,31 @@ void BM_reassign_ids(BMesh *bm) memset(bm->idmap.map, 0, sizeof(void *) * bm->idmap.map_size); } +#ifndef WITH_BM_ID_FREELIST if (bm->idmap.idtree) { range_tree_uint_free(bm->idmap.idtree); } bm->idmap.idtree = range_tree_uint_alloc(0, (uint)-1); +#else + if (bm->idmap.freelist) { + MEM_freeN(bm->idmap.freelist); + bm->idmap.freelist = NULL; + } + + if (bm->idmap.free_ids) { + BLI_gset_free(bm->idmap.free_ids, NULL); + bm->idmap.free_ids = NULL; + } + + bm->idmap.freelist_len = 0; + bm->idmap.freelist_size = 0; +#endif +} + +void BM_reassign_ids(BMesh *bm) +{ + BM_clear_ids(bm); int iters[] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH, BM_FACES_OF_MESH}; diff --git a/source/blender/bmesh/intern/bmesh_core.h b/source/blender/bmesh/intern/bmesh_core.h index fb2c7b9d9d2..c58f1921e3d 100644 --- a/source/blender/bmesh/intern/bmesh_core.h +++ b/source/blender/bmesh/intern/bmesh_core.h @@ -131,3 +131,4 @@ BMVert *bmesh_kernel_unglue_region_make_vert_multi(BMesh *bm, BMLoop **larr, int BMVert *bmesh_kernel_unglue_region_make_vert_multi_isolated(BMesh *bm, BMLoop *l_sep); void BM_reassign_ids(BMesh *bm); +void BM_clear_ids(BMesh *bm); diff --git a/source/blender/bmesh/intern/bmesh_iterators.c b/source/blender/bmesh/intern/bmesh_iterators.c index bd28022de4b..951c1234362 100644 --- a/source/blender/bmesh/intern/bmesh_iterators.c +++ b/source/blender/bmesh/intern/bmesh_iterators.c @@ -25,6 +25,7 @@ #include "MEM_guardedalloc.h" #include "BLI_bitmap.h" +#include "BLI_compiler_attrs.h" #include "BLI_utildefines.h" #include "bmesh.h" diff --git a/source/blender/bmesh/intern/bmesh_iterators_inline.h b/source/blender/bmesh/intern/bmesh_iterators_inline.h index 81b6a58e58b..3589516bf6d 100644 --- a/source/blender/bmesh/intern/bmesh_iterators_inline.h +++ b/source/blender/bmesh/intern/bmesh_iterators_inline.h @@ -42,8 +42,7 @@ ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE void *BM_iter_step(BMIter *it * it with the appropriate function pointers based * upon its type. */ -ATTR_NONNULL(1) -BLI_INLINE bool BM_iter_init(BMIter *iter, BMesh *bm, const char itype, void *data) +ATTR_NONNULL(1) BLI_INLINE bool BM_iter_init(BMIter *iter, BMesh *bm, const char itype, void *data) { /* int argtype; */ iter->itype = itype; diff --git a/source/blender/bmesh/intern/bmesh_log.c b/source/blender/bmesh/intern/bmesh_log.c index 06d0b2bd07a..acba919e817 100644 --- a/source/blender/bmesh/intern/bmesh_log.c +++ b/source/blender/bmesh/intern/bmesh_log.c @@ -58,6 +58,14 @@ struct Mesh; +typedef enum { LOG_ENTRY_PARTIAL, LOG_ENTRY_FULL_MESH, LOG_ENTRY_MESH_IDS } BMLogEntryType; + +typedef struct BMLogIdMap { + int elemmask; + int elemtots[15]; + int *maps[15]; +} BMLogIdMap; + struct BMLogEntry { struct BMLogEntry *next, *prev; @@ -94,13 +102,15 @@ struct BMLogEntry { CustomData vdata, edata, ldata, pdata; struct BMLogEntry *combined_prev, *combined_next; - bool fully_copy; // has full copy + BMLogEntryType type; + struct Mesh *full_copy_mesh; // avoid excessive memory use by saving a Mesh instead of copying the bmesh + BMLogIdMap idmap; }; struct BMLog { - BMLogEntry *frozen_full_mesh; + // BMLogEntry *frozen_full_mesh; int refcount; @@ -167,8 +177,15 @@ typedef struct { #define logkey_hash BLI_ghashutil_inthash_p_simple #define logkey_cmp BLI_ghashutil_intcmp +static void log_idmap_load(BMesh *bm, BMLog *log, BMLogEntry *entry); +static void log_idmap_swap(BMesh *bm, BMLog *log, BMLogEntry *entry); +static void log_idmap_free(BMLogEntry *entry); + static void full_copy_swap(BMesh *bm, BMLog *log, BMLogEntry *entry); static void full_copy_load(BMesh *bm, BMLog *log, BMLogEntry *entry); + +BMLogEntry *bm_log_entry_add_ex( + BMesh *bm, BMLog *log, bool combine_with_last, BMLogEntryType type, BMLogEntry *last_entry); static void bm_log_entry_free(BMLogEntry *entry); static bool bm_log_free_direct(BMLog *log, bool safe_mode); @@ -780,20 +797,24 @@ static void bm_log_full_mesh_intern(BMesh *bm, BMLog *log, BMLogEntry *entry) } /* Allocate an empty log entry */ -static BMLogEntry *bm_log_entry_create(void) +static BMLogEntry *bm_log_entry_create(BMLogEntryType type) { BMLogEntry *entry = MEM_callocN(sizeof(BMLogEntry), __func__); - entry->deleted_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->deleted_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->added_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->added_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->modified_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->modified_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->type = type; + + if (type == LOG_ENTRY_PARTIAL) { + entry->deleted_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->deleted_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->added_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->added_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->modified_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); + entry->modified_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->pool_verts = BLI_mempool_create(sizeof(BMLogVert), 0, 64, BLI_MEMPOOL_NOP); - entry->pool_faces = BLI_mempool_create(sizeof(BMLogFace), 0, 64, BLI_MEMPOOL_NOP); - entry->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "bmlog arena"); + entry->pool_verts = BLI_mempool_create(sizeof(BMLogVert), 0, 64, BLI_MEMPOOL_NOP); + entry->pool_faces = BLI_mempool_create(sizeof(BMLogFace), 0, 64, BLI_MEMPOOL_NOP); + entry->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "bmlog arena"); + } return entry; } @@ -817,38 +838,45 @@ static void bm_log_entry_free(BMLogEntry *entry) kill_log = !log->refcount; } - if (entry->full_copy_mesh) { - BKE_mesh_free(entry->full_copy_mesh); - } - - BLI_ghash_free(entry->deleted_verts, NULL, NULL); - BLI_ghash_free(entry->deleted_faces, NULL, NULL); - BLI_ghash_free(entry->added_verts, NULL, NULL); - BLI_ghash_free(entry->added_faces, NULL, NULL); - BLI_ghash_free(entry->modified_verts, NULL, NULL); - BLI_ghash_free(entry->modified_faces, NULL, NULL); - - BLI_mempool_destroy(entry->pool_verts); - BLI_mempool_destroy(entry->pool_faces); - BLI_memarena_free(entry->arena); + switch (entry->type) { + case LOG_ENTRY_MESH_IDS: + log_idmap_free(entry); + break; + case LOG_ENTRY_FULL_MESH: + + BKE_mesh_free(entry->full_copy_mesh); + break; + case LOG_ENTRY_PARTIAL: + BLI_ghash_free(entry->deleted_verts, NULL, NULL); + BLI_ghash_free(entry->deleted_faces, NULL, NULL); + BLI_ghash_free(entry->added_verts, NULL, NULL); + BLI_ghash_free(entry->added_faces, NULL, NULL); + BLI_ghash_free(entry->modified_verts, NULL, NULL); + BLI_ghash_free(entry->modified_faces, NULL, NULL); + + BLI_mempool_destroy(entry->pool_verts); + BLI_mempool_destroy(entry->pool_faces); + BLI_memarena_free(entry->arena); + + if (entry->vdata.pool) { + BLI_mempool_destroy(entry->vdata.pool); + } + if (entry->edata.pool) { + BLI_mempool_destroy(entry->edata.pool); + } + if (entry->ldata.pool) { + BLI_mempool_destroy(entry->ldata.pool); + } + if (entry->pdata.pool) { + BLI_mempool_destroy(entry->pdata.pool); + } - if (entry->vdata.pool) { - BLI_mempool_destroy(entry->vdata.pool); + CustomData_free(&entry->vdata, 0); + CustomData_free(&entry->edata, 0); + CustomData_free(&entry->ldata, 0); + CustomData_free(&entry->pdata, 0); + break; } - if (entry->edata.pool) { - BLI_mempool_destroy(entry->edata.pool); - } - if (entry->ldata.pool) { - BLI_mempool_destroy(entry->ldata.pool); - } - if (entry->pdata.pool) { - BLI_mempool_destroy(entry->pdata.pool); - } - - CustomData_free(&entry->vdata, 0); - CustomData_free(&entry->edata, 0); - CustomData_free(&entry->ldata, 0); - CustomData_free(&entry->pdata, 0); if (kill_log) { bm_log_free_direct(log, true); @@ -924,35 +952,36 @@ void BM_log_cleanup_entry(BMLogEntry *entry) } } -BMLog *bm_log_from_existing_entries_create(BMesh *bm, - BMLog *log, - BMLogEntry *entry, - bool restore_ids) +BMLog *bm_log_from_existing_entries_create(BMesh *bm, BMLog *log, BMLogEntry *entry) { - if (entry->prev) { - log->current_entry = entry; - } - else { - log->current_entry = NULL; - } + log->current_entry = entry; /* Let BMLog manage the entry list again */ log->entries.first = log->entries.last = entry; - { - while (entry->prev) { - entry = entry->prev; - log->entries.first = entry; - } - entry = log->entries.last; - while (entry->next) { - entry = entry->next; - log->entries.last = entry; - } + while (entry->prev) { + entry = entry->prev; + log->entries.first = entry; + } + + entry = log->entries.last; + while (entry->next) { + entry = entry->next; + log->entries.last = entry; } for (entry = log->entries.first; entry; entry = entry->next) { + BMLogEntry *entry2 = entry->combined_prev; + + while (entry2) { + entry2->log = log; + entry2 = entry2->combined_prev; + + log->refcount++; + } + entry->log = log; + log->refcount++; } return log; @@ -969,7 +998,8 @@ BMLog *bm_log_from_existing_entries_create(BMesh *bm, BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry) { BMLog *log = BM_log_create(bm, -1); - bm_log_from_existing_entries_create(bm, log, entry, true); + + bm_log_from_existing_entries_create(bm, log, entry); return log; } @@ -980,17 +1010,20 @@ BMLog *BM_log_unfreeze(BMesh *bm, BMLogEntry *entry) return NULL; } +#if 0 BMLogEntry *frozen = entry->log->frozen_full_mesh; - if (!frozen && entry->fully_copy) { + if (!frozen && entry->type == LOG_ENTRY_FULL_MESH) { frozen = entry; } - if (!frozen) { + if (!frozen || frozen->type != LOG_ENTRY_FULL_MESH) { return entry->log->bm == bm ? entry->log : NULL; } +#endif entry->log->bm = bm; +#if 0 full_copy_load(bm, entry->log, frozen); if (entry->log->frozen_full_mesh) { @@ -998,7 +1031,7 @@ BMLog *BM_log_unfreeze(BMesh *bm, BMLogEntry *entry) bm_log_entry_free(entry->log->frozen_full_mesh); entry->log->frozen_full_mesh = NULL; } - +#endif return entry->log; } @@ -1010,14 +1043,15 @@ static bool bm_log_free_direct(BMLog *log, bool safe_mode) BMLogEntry *entry; if (safe_mode && log->refcount) { +#if 0 if (log->frozen_full_mesh) { log->frozen_full_mesh->log = NULL; bm_log_entry_free(log->frozen_full_mesh); } +#endif - log->frozen_full_mesh = bm_log_entry_create(); - - bm_log_full_mesh_intern(log->bm, log, log->frozen_full_mesh); + // log->frozen_full_mesh = bm_log_entry_create(LOG_ENTRY_FULL_MESH); + // bm_log_full_mesh_intern(log->bm, log, log->frozen_full_mesh); return false; } @@ -1071,22 +1105,28 @@ void BM_log_print_entry(BMLog *log, BMLogEntry *entry) printf("==bmlog step==\n"); while (first) { - if (first->fully_copy) { - printf(" ==full mesh copy==\n"); - } - else { - printf(" ==entry==\n"); - printf(" modified:\n"); - printf(" verts: %d\n", BLI_ghash_len(entry->modified_verts)); - printf(" faces: %d\n", BLI_ghash_len(entry->modified_faces)); - printf(" new:\n"); - printf(" verts: %d\n", BLI_ghash_len(entry->added_verts)); - printf(" faces: %d\n", BLI_ghash_len(entry->added_faces)); - printf(" deleted:\n"); - printf(" verts: %d\n", BLI_ghash_len(entry->deleted_verts)); - printf(" faces: %d\n", BLI_ghash_len(entry->deleted_faces)); - printf("\n"); + switch (first->type) { + case LOG_ENTRY_FULL_MESH: + printf(" ==full mesh copy==\n"); + break; + case LOG_ENTRY_MESH_IDS: + printf("==element IDs snapshot\n"); + break; + case LOG_ENTRY_PARTIAL: + printf(" ==entry==\n"); + printf(" modified:\n"); + printf(" verts: %d\n", BLI_ghash_len(first->modified_verts)); + printf(" faces: %d\n", BLI_ghash_len(first->modified_faces)); + printf(" new:\n"); + printf(" verts: %d\n", BLI_ghash_len(first->added_verts)); + printf(" faces: %d\n", BLI_ghash_len(first->added_faces)); + printf(" deleted:\n"); + printf(" verts: %d\n", BLI_ghash_len(first->deleted_verts)); + printf(" faces: %d\n", BLI_ghash_len(first->deleted_faces)); + printf("\n"); + break; } + printf("\n"); first = first->combined_next; } @@ -1156,6 +1196,10 @@ BMLogEntry *BM_log_entry_check_customdata(BMesh *bm, BMLog *log) return BM_log_entry_add_ex(bm, log, false); } + if (entry->type != LOG_ENTRY_PARTIAL) { + return BM_log_entry_add_ex(bm, log, true); + } + #ifndef CUSTOMDATA return entry; #else @@ -1190,7 +1234,8 @@ BMLogEntry *BM_log_entry_add(BMesh *bm, BMLog *log) return BM_log_entry_add_ex(bm, log, false); } -BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last) +BMLogEntry *bm_log_entry_add_ex( + BMesh *bm, BMLog *log, bool combine_with_last, BMLogEntryType type, BMLogEntry *last_entry) { if (log->dead) { fprintf(stderr, "BMLog Error: log is dead\n"); @@ -1217,31 +1262,43 @@ BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last) #endif /* Create and append the new entry */ - entry = bm_log_entry_create(); - BLI_addtail(&log->entries, entry); + entry = bm_log_entry_create(type); + + if (!last_entry || last_entry == log->current_entry) { + BLI_addtail(&log->entries, entry); + } + entry->log = log; log->refcount++; #ifdef CUSTOMDATA if (combine_with_last) { - if (log->current_entry) { - log->current_entry->combined_next = entry; - BLI_remlink(&log->entries, log->current_entry); - } + if (!last_entry || last_entry == log->current_entry) { + if (log->current_entry) { + log->current_entry->combined_next = entry; + BLI_remlink(&log->entries, log->current_entry); + } - entry->combined_prev = log->current_entry; + entry->combined_prev = log->current_entry; + } + else { + entry->combined_prev = last_entry; + last_entry->combined_next = entry; + } } - CustomData_copy_all_layout(&bm->vdata, &entry->vdata); - CustomData_copy_all_layout(&bm->edata, &entry->edata); - CustomData_copy_all_layout(&bm->ldata, &entry->ldata); - CustomData_copy_all_layout(&bm->pdata, &entry->pdata); + if (type == LOG_ENTRY_PARTIAL) { + CustomData_copy_all_layout(&bm->vdata, &entry->vdata); + CustomData_copy_all_layout(&bm->edata, &entry->edata); + CustomData_copy_all_layout(&bm->ldata, &entry->ldata); + CustomData_copy_all_layout(&bm->pdata, &entry->pdata); - CustomData_bmesh_init_pool(&entry->vdata, 0, BM_VERT); - CustomData_bmesh_init_pool(&entry->edata, 0, BM_EDGE); - CustomData_bmesh_init_pool(&entry->ldata, 0, BM_LOOP); - CustomData_bmesh_init_pool(&entry->pdata, 0, BM_FACE); + CustomData_bmesh_init_pool(&entry->vdata, 0, BM_VERT); + CustomData_bmesh_init_pool(&entry->edata, 0, BM_EDGE); + CustomData_bmesh_init_pool(&entry->ldata, 0, BM_LOOP); + CustomData_bmesh_init_pool(&entry->pdata, 0, BM_FACE); + } #endif log->current_entry = entry; @@ -1249,6 +1306,11 @@ BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last) return entry; } +BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last) +{ + return bm_log_entry_add_ex(bm, log, combine_with_last, LOG_ENTRY_PARTIAL, NULL); +} + /* Remove an entry from the log * * Uses entry->log as the log. If the log is NULL, the entry will be @@ -1272,54 +1334,41 @@ void BM_log_entry_drop(BMLogEntry *entry) entry->next->prev = NULL; } + BMLogEntry *entry2 = entry->combined_prev; + while (entry2) { + BMLogEntry *prev = entry2->combined_prev; + + bm_log_entry_free(entry2); + MEM_freeN(entry2); + + entry2 = prev; + } + bm_log_entry_free(entry); MEM_freeN(entry); return; } - if (!entry->prev) { - /* Release IDs of elements that are deleted by this - * entry. Since the entry is at the beginning of the undo - * stack, and it's being deleted, those elements can never be - * restored. Their IDs can go back into the pool. */ - - /* This would never happen usually since first entry of log is - * usually dyntopo enable, which, when reverted will free the log - * completely. However, it is possible have a stroke instead of - * dyntopo enable as first entry if nodes have been cleaned up - * after sculpting on a different object than A, B. - * - * The steps are: - * A dyntopo enable - sculpt - * B dyntopo enable - sculpt - undo (A objects operators get cleaned up) - * A sculpt (now A's log has a sculpt operator as first entry) - * - * Causing a cleanup at this point will call the code below, however - * this will invalidate the state of the log since the deleted vertices - * have been reclaimed already on step 2 (see BM_log_cleanup_entry) - * - * Also, design wise, a first entry should not have any deleted vertices since it - * should not have anything to delete them -from- - */ - // bm_log_id_ghash_release(log, entry->deleted_faces); - // bm_log_id_ghash_release(log, entry->deleted_verts); - } - else if (!entry->next) { - /* Release IDs of elements that are added by this entry. Since - * the entry is at the end of the undo stack, and it's being - * deleted, those elements can never be restored. Their IDs - * can go back into the pool. */ + if (log && log->current_entry == entry) { + log->current_entry = entry->prev; } - else { - BLI_assert_msg(0, "Cannot drop BMLogEntry from middle"); + + if (log) { + BLI_remlink(&log->entries, entry); } - if (log->current_entry == entry) { - log->current_entry = entry->prev; + // free subentries first + BMLogEntry *entry2 = entry->combined_prev; + while (entry2) { + BMLogEntry *prev = entry2->combined_prev; + + bm_log_entry_free(entry2); + MEM_freeN(entry2); + entry2 = prev; } bm_log_entry_free(entry); - BLI_freelinkN(&log->entries, entry); + MEM_freeN(entry); } static void full_copy_load(BMesh *bm, BMLog *log, BMLogEntry *entry) @@ -1348,6 +1397,254 @@ static void full_copy_load(BMesh *bm, BMLog *log, BMLogEntry *entry) bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; } +static void log_idmap_free(BMLogEntry *entry) +{ + for (int i = 0; i < 4; i++) { + int type = 1 << i; + + MEM_SAFE_FREE(entry->idmap.maps[type]); + entry->idmap.maps[type] = NULL; + entry->idmap.elemtots[type] = 0; + } +} + +static void log_idmap_save(BMesh *bm, BMLog *log, BMLogEntry *entry) +{ + log_idmap_free(entry); + + entry->type = LOG_ENTRY_MESH_IDS; + memset((void *)&entry->idmap, 0, sizeof(entry->idmap)); + + entry->idmap.elemmask = BM_VERT | BM_FACE; + BMLogIdMap *idmap = &entry->idmap; + + BMIter iter; + + int cd_id_offs[4] = {CustomData_get_offset(&bm->vdata, CD_MESH_ID), + CustomData_get_offset(&bm->edata, CD_MESH_ID), + CustomData_get_offset(&bm->ldata, CD_MESH_ID), + CustomData_get_offset(&bm->pdata, CD_MESH_ID)}; + + const char iters[] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, 0, BM_FACES_OF_MESH}; + int tots[] = {bm->totvert, bm->totedge, bm->totloop, bm->totface}; + + // enforce elemmask + for (int i = 0; i < 4; i++) { + int type = 1 << i; + + if (!(idmap->elemmask & type) || !tots[i]) { + tots[i] = 0; + cd_id_offs[i] = -1; + } + } + + // set up loop map which is handled specially + if (cd_id_offs[2] >= 0 && tots[2] > 0) { + idmap->maps[BM_LOOP] = MEM_malloc_arrayN((size_t)tots[2], sizeof(int), "idmap->maps[BM_LOOP]"); + } + + for (int i = 0; i < 4; i++) { + if (i == 2) { // loops are saved in face pass + continue; + } + + int type = 1 << i; + const int cd_off = cd_id_offs[i]; + const int tot = tots[i]; + + idmap->elemtots[type] = tot; + + if (cd_off < 0 || tot == 0) { + continue; + } + + int *map = idmap->maps[type] = MEM_malloc_arrayN( + (size_t)tot, sizeof(int), "idmap->maps entry"); + + BMElem *elem; + int j = 0; + int loopi = 0; + int cd_loop_off = cd_id_offs[2]; + int *lmap = idmap->maps[2]; + + BM_ITER_MESH_INDEX (elem, &iter, bm, iters[i], j) { + int id = BM_ELEM_CD_GET_INT(elem, cd_off); + + if (bm->idmap.map && bm->idmap.map[id] != elem) { + printf("Error\n"); + } + + map[j] = id; + + // deal with loops + if (type == BM_FACE && cd_loop_off >= 0 && lmap) { + BMFace *f = (BMFace *)elem; + BMLoop *l = f->l_first; + + do { + lmap[loopi++] = BM_ELEM_CD_GET_INT(l, cd_loop_off); + } while ((l = l->next) != f->l_first); + } + } + + if (type == BM_FACE) { + idmap->elemtots[BM_LOOP] = loopi; + } + } +} + +static void log_idmap_load(BMesh *bm, BMLog *log, BMLogEntry *entry) +{ + const int cd_id_offs[4] = {CustomData_get_offset(&bm->vdata, CD_MESH_ID), + CustomData_get_offset(&bm->edata, CD_MESH_ID), + CustomData_get_offset(&bm->ldata, CD_MESH_ID), + CustomData_get_offset(&bm->pdata, CD_MESH_ID)}; + + const char iters[] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, 0, BM_FACES_OF_MESH}; + const int tots[] = {bm->totvert, bm->totedge, bm->totloop, bm->totface}; + BMLogIdMap *idmap = &entry->idmap; + + BM_clear_ids(bm); + + for (int i = 0; i < 4; i++) { + int type = 1 << i; + + if (!(idmap->elemmask & type) || i == 2) { + continue; + } + + if (cd_id_offs[i] < 0) { + printf("mesh doesn't have ids for elem type %d\n", type); + continue; + } + + if (idmap->elemtots[type] != tots[i]) { + printf("idmap elem count mismatch error"); + continue; + } + + if (!idmap->elemtots[type]) { + continue; + } + + const int cd_loop_id = (idmap->elemmask & type) ? cd_id_offs[2] : -1; + + int j = 0; + BMElem *elem; + BMIter iter; + int *map = idmap->maps[type]; + int loopi = 0; + int *lmap = idmap->maps[BM_LOOP]; + + BM_ITER_MESH_INDEX (elem, &iter, bm, iters[i], j) { + bm_assign_id(bm, elem, (uint)map[j]); + + // deal with loops + if (type == BM_FACE && cd_loop_id >= 0) { + BMFace *f = (BMFace *)elem; + BMLoop *l = f->l_first; + + do { + bm_assign_id(bm, (BMElem *)l, (uint)lmap[loopi]); + + loopi++; + } while ((l = l->next) != f->l_first); + } + } + } +} + +static void log_idmap_swap(BMesh *bm, BMLog *log, BMLogEntry *entry) +{ + const int cd_id_offs[4] = {CustomData_get_offset(&bm->vdata, CD_MESH_ID), + CustomData_get_offset(&bm->edata, CD_MESH_ID), + CustomData_get_offset(&bm->ldata, CD_MESH_ID), + CustomData_get_offset(&bm->pdata, CD_MESH_ID)}; + + const char iters[] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, 0, BM_FACES_OF_MESH}; + const int tots[] = {bm->totvert, bm->totedge, bm->totloop, bm->totface}; + BMLogIdMap *idmap = &entry->idmap; + + BM_clear_ids(bm); + + for (int i = 0; i < 4; i++) { + int type = 1 << i; + + if (!(idmap->elemmask & type) || i == 2) { + continue; + } + + if (cd_id_offs[i] < 0) { + printf("mesh doesn't have ids for elem type %d\n", type); + continue; + } + + if (idmap->elemtots[type] != tots[i]) { + printf("idmap elem count mismatch error"); + continue; + } + + if (!idmap->elemtots[type]) { + continue; + } + + const int cd_loop_id = (idmap->elemmask & type) ? cd_id_offs[2] : -1; + + int cd_id = cd_id_offs[i]; + int j = 0; + BMElem *elem; + BMIter iter; + int *map = idmap->maps[type]; + int loopi = 0; + int *lmap = idmap->maps[BM_LOOP]; + + BM_ITER_MESH_INDEX (elem, &iter, bm, iters[i], j) { + int id = BM_ELEM_CD_GET_INT(elem, cd_id); + + bm_assign_id(bm, elem, (uint)map[j]); + map[j] = id; + + // deal with loops + if (type == BM_FACE && cd_loop_id >= 0) { + BMFace *f = (BMFace *)elem; + BMLoop *l = f->l_first; + + do { + int id2 = BM_ELEM_CD_GET_INT(l, cd_loop_id); + + bm_assign_id(bm, (BMElem *)l, (uint)lmap[loopi]); + lmap[loopi] = id2; + + loopi++; + } while ((l = l->next) != f->l_first); + } + } + } +} + +void BM_log_set_current_entry(BMLog *log, BMLogEntry *entry) +{ + // you cannot set the current entry to a sub-entry, so this should never happen. + while (entry && entry->combined_next) { + entry = entry->combined_next; + } + + log->current_entry = entry; +} + +BMLogEntry *BM_log_all_ids(BMesh *bm, BMLog *log, BMLogEntry *entry) +{ + if (!entry) { + entry = bm_log_entry_add_ex(bm, log, false, LOG_ENTRY_MESH_IDS, NULL); + } + else if (entry->type != LOG_ENTRY_MESH_IDS) { + entry = bm_log_entry_add_ex(bm, log, true, LOG_ENTRY_MESH_IDS, entry); + } + + log_idmap_save(bm, log, entry); + return entry; +} + static void full_copy_swap(BMesh *bm, BMLog *log, BMLogEntry *entry) { CustomData_MeshMasks cd_mask_extra = {CD_MASK_DYNTOPO_VERT, 0, 0, 0, 0}; @@ -1392,7 +1689,7 @@ static void bm_log_undo_intern( bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; - if (entry->fully_copy) { + if (entry->type == LOG_ENTRY_FULL_MESH) { full_copy_swap(bm, log, entry); if (callbacks) { @@ -1400,6 +1697,14 @@ static void bm_log_undo_intern( } return; } + else if (entry->type == LOG_ENTRY_MESH_IDS) { + log_idmap_load(bm, log, entry); + + if (callbacks && callbacks->on_mesh_id_restore) { + callbacks->on_mesh_id_restore(callbacks->userdata); + } + return; + } /* Delete added faces and verts */ bm_log_verts_unmake_pre(bm, log, entry->added_verts, entry, callbacks); @@ -1415,6 +1720,23 @@ static void bm_log_undo_intern( bm_log_face_values_swap(log, entry->modified_faces, entry, callbacks); } +void BM_log_undo_skip(BMesh *bm, BMLog *log) +{ + if (log->current_entry) { + log->current_entry = log->current_entry->prev; + } +} + +void BM_log_redo_skip(BMesh *bm, BMLog *log) +{ + if (log->current_entry) { + log->current_entry = log->current_entry->next; + } + else { + log->current_entry = log->entries.first; + } +} + void BM_log_undo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks, const char *node_layer_id) { BMLogEntry *entry = log->current_entry; @@ -1440,13 +1762,22 @@ void BM_log_undo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks, const char *n static void bm_log_redo_intern( BMesh *bm, BMLog *log, BMLogEntry *entry, BMLogCallbacks *callbacks, const char *node_layer_id) { - if (entry->fully_copy) { + if (entry->type == LOG_ENTRY_FULL_MESH) { // hrm, should we swap? full_copy_swap(bm, log, entry); if (callbacks) { callbacks->on_full_mesh_load(callbacks->userdata); } + + return; + } + else if (entry->type == LOG_ENTRY_MESH_IDS) { + log_idmap_load(bm, log, entry); + + if (callbacks && callbacks->on_mesh_id_restore) { + callbacks->on_mesh_id_restore(callbacks->userdata); + } return; } @@ -1467,6 +1798,16 @@ static void bm_log_redo_intern( bm_log_face_values_swap(log, entry->modified_faces, entry, callbacks); } +BMLogEntry *BM_log_entry_prev(BMLogEntry *entry) +{ + return entry->prev; +} + +BMLogEntry *BM_log_entry_next(BMLogEntry *entry) +{ + return entry->next; +} + void BM_log_redo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks, const char *node_layer_id) { BMLogEntry *entry = log->current_entry; @@ -1716,7 +2057,7 @@ void BM_log_full_mesh(BMesh *bm, BMLog *log) BMLogEntry *entry = log->current_entry; if (!entry) { - entry = BM_log_entry_add_ex(bm, log, false); + entry = bm_log_entry_add_ex(bm, log, false, LOG_ENTRY_FULL_MESH, NULL); } bool add = BLI_ghash_len(entry->added_faces) > 0; @@ -1726,11 +2067,9 @@ void BM_log_full_mesh(BMesh *bm, BMLog *log) add |= BLI_ghash_len(entry->deleted_faces) > 0; if (add) { - entry = BM_log_entry_add_ex(bm, log, true); + entry = bm_log_entry_add_ex(bm, log, true, LOG_ENTRY_FULL_MESH, NULL); } - entry->fully_copy = true; - bm_log_full_mesh_intern(bm, log, entry); // push a fresh entry diff --git a/source/blender/bmesh/intern/bmesh_log.h b/source/blender/bmesh/intern/bmesh_log.h index df41cb26d50..c7a1629a496 100644 --- a/source/blender/bmesh/intern/bmesh_log.h +++ b/source/blender/bmesh/intern/bmesh_log.h @@ -38,6 +38,7 @@ typedef struct BMLogCallbacks { void (*on_face_change)(struct BMFace *f, void *userdata, void *old_customdata); void (*on_full_mesh_load)(void *userdata); + void (*on_mesh_id_restore)(void *userdata); void *userdata; } BMLogCallbacks; @@ -64,6 +65,7 @@ void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log); /* Start a new log entry and update the log entry list */ BMLogEntry *BM_log_entry_add(BMesh *bm, BMLog *log); BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last); +BMLogEntry *BM_log_all_ids(BMesh *bm, BMLog *log, BMLogEntry *entry); BMLogEntry *BM_log_entry_check_customdata(BMesh *bm, BMLog *log); @@ -123,6 +125,9 @@ void BM_log_original_vert_data(BMLog *log, BMVert *v, const float **r_co, const /* For internal use only (unit testing) */ BMLogEntry *BM_log_current_entry(BMLog *log); +void BM_log_set_current_entry(BMLog *log, BMLogEntry *entry); +BMLogEntry *BM_log_entry_prev(BMLogEntry *entry); +BMLogEntry *BM_log_entry_next(BMLogEntry *entry); uint BM_log_vert_id_get(BMLog *log, BMVert *v); BMVert *BM_log_id_vert_get(BMLog *log, uint id); @@ -130,3 +135,5 @@ uint BM_log_face_id_get(BMLog *log, BMFace *f); BMFace *BM_log_id_face_get(BMLog *log, uint id); void BM_log_print_entry(BMLog *log, BMLogEntry *entry); +void BM_log_redo_skip(BMesh *bm, BMLog *log); +void BM_log_undo_skip(BMesh *bm, BMLog *log); diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c index fe91200f079..104ae6a349a 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -163,7 +163,9 @@ BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreate bm->idmap.flag |= params->use_id_elem_mask * BM_HAS_ID_MAP; bm->idmap.flag |= params->use_id_elem_mask; +#ifndef WITH_BM_ID_FREELIST bm->idmap.idtree = range_tree_uint_alloc(0, (uint)-1); +#endif } if (bm->idmap.flag & BM_HAS_ID_MAP) { @@ -221,9 +223,17 @@ void BM_mesh_data_free(BMesh *bm) BMIter iter; BMIter itersub; +#ifndef WITH_BM_ID_FREELIST if (bm->idmap.idtree) { range_tree_uint_free(bm->idmap.idtree); } +#else + if (bm->idmap.free_ids) { + BLI_gset_free(bm->idmap.free_ids, NULL); + } + + MEM_SAFE_FREE(bm->idmap.free_ids); +#endif MEM_SAFE_FREE(bm->idmap.map); @@ -337,7 +347,18 @@ void BM_mesh_clear(BMesh *bm) bm->idmap.flag = idmap_flags; if (bm->idmap.flag & BM_HAS_IDS) { +#ifndef WITH_BM_ID_FREELIST bm->idmap.idtree = range_tree_uint_alloc(0, (uint)-1); +#else + if (bm->idmap.free_ids) { + BLI_gset_free(bm->idmap.free_ids, NULL); + } + MEM_SAFE_FREE(bm->idmap.freelist); + + bm->idmap.freelist_len = bm->idmap.freelist_size = NULL; + bm->idmap.free_ids = NULL; + bm->idmap.freelist = NULL; +#endif bm_init_idmap_cdlayers(bm); } } diff --git a/source/blender/bmesh/intern/bmesh_mesh_normals.c b/source/blender/bmesh/intern/bmesh_mesh_normals.c index dea6561fe9a..6dfaa0ca688 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_normals.c +++ b/source/blender/bmesh/intern/bmesh_mesh_normals.c @@ -40,6 +40,16 @@ #include "intern/bmesh_private.h" +/* Smooth angle to use when tagging edges is disabled entirely. */ +#define EDGE_TAG_FROM_SPLIT_ANGLE_BYPASS -FLT_MAX + +static void bm_edge_tag_from_smooth_and_set_sharp(const float (*fnos)[3], + BMEdge *e, + const float split_angle_cos); +static void bm_edge_tag_from_smooth(const float (*fnos)[3], + BMEdge *e, + const float split_angle_cos); + /* -------------------------------------------------------------------- */ /** \name Update Vertex & Face Normals * \{ */ @@ -394,9 +404,7 @@ void BM_normals_loops_edges_tag(BMesh *bm, const bool do_edges) * Helpers for #BM_mesh_loop_normals_update and #BM_loops_calc_normal_vcos */ static void bm_mesh_edges_sharp_tag(BMesh *bm, - const float (*vnos)[3], const float (*fnos)[3], - float (*r_lnos)[3], const float split_angle, const bool do_sharp_edges_tag) { @@ -407,58 +415,23 @@ static void bm_mesh_edges_sharp_tag(BMesh *bm, const bool check_angle = (split_angle < (float)M_PI); const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f; - { - char htype = BM_VERT | BM_LOOP; - if (fnos) { - htype |= BM_FACE; - } - BM_mesh_elem_index_ensure(bm, htype); + if (fnos) { + BM_mesh_elem_index_ensure(bm, BM_FACE); } - /* This first loop checks which edges are actually smooth, - * and pre-populate lnos with vnos (as if they were all smooth). */ - BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) { - BMLoop *l_a, *l_b; - - BM_elem_index_set(e, i); /* set_inline */ - BM_elem_flag_disable(e, BM_ELEM_TAG); /* Clear tag (means edge is sharp). */ - - /* An edge with only two loops, might be smooth... */ - if (BM_edge_loop_pair(e, &l_a, &l_b)) { - bool is_angle_smooth = true; - if (check_angle) { - const float *no_a = fnos ? fnos[BM_elem_index_get(l_a->f)] : l_a->f->no; - const float *no_b = fnos ? fnos[BM_elem_index_get(l_b->f)] : l_b->f->no; - is_angle_smooth = (dot_v3v3(no_a, no_b) >= split_angle_cos); + if (do_sharp_edges_tag) { + BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) { + BM_elem_index_set(e, i); /* set_inline */ + if (e->l != NULL) { + bm_edge_tag_from_smooth_and_set_sharp(fnos, e, split_angle_cos); } - - /* We only tag edges that are *really* smooth: - * If the angle between both its polys' normals is below split_angle value, - * and it is tagged as such, - * and both its faces are smooth, - * and both its faces have compatible (non-flipped) normals, - * i.e. both loops on the same edge do not share the same vertex. - */ - if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) && BM_elem_flag_test(l_a->f, BM_ELEM_SMOOTH) && - BM_elem_flag_test(l_b->f, BM_ELEM_SMOOTH) && l_a->v != l_b->v) { - if (is_angle_smooth) { - const float *no; - BM_elem_flag_enable(e, BM_ELEM_TAG); - - /* linked vertices might be fully smooth, copy their normals to loop ones. */ - if (r_lnos) { - no = vnos ? vnos[BM_elem_index_get(l_a->v)] : l_a->v->no; - copy_v3_v3(r_lnos[BM_elem_index_get(l_a)], no); - no = vnos ? vnos[BM_elem_index_get(l_b->v)] : l_b->v->no; - copy_v3_v3(r_lnos[BM_elem_index_get(l_b)], no); - } - } - else if (do_sharp_edges_tag) { - /* Note that we do not care about the other sharp-edge cases - * (sharp poly, non-manifold edge, etc.), - * only tag edge as sharp when it is due to angle threshold. */ - BM_elem_flag_disable(e, BM_ELEM_SMOOTH); - } + } + } + else { + BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, i) { + BM_elem_index_set(e, i); /* set_inline */ + if (e->l != NULL) { + bm_edge_tag_from_smooth(fnos, e, split_angle_cos); } } } @@ -479,7 +452,7 @@ void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle) return; } - bm_mesh_edges_sharp_tag(bm, NULL, NULL, NULL, split_angle, true); + bm_mesh_edges_sharp_tag(bm, NULL, split_angle, true); } /** \} */ @@ -526,6 +499,600 @@ bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr) } /** + * Called for all faces loops. + * + * - All loops must have #BM_ELEM_TAG cleared. + * - Loop indices must be valid. + * + * \note When custom normals are present, the order of loops can be important. + * Loops with lower indices must be passed before loops with higher indices (for each vertex). + * This is needed since the first loop sets the reference point for the custom normal offsets. + * + * \return The number of loops that were handled (for early exit when all have been handled). + */ +static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm, + const float (*vcos)[3], + const float (*fnos)[3], + const short (*clnors_data)[2], + const int cd_loop_clnors_offset, + const bool has_clnors, + /* Cache. */ + BLI_Stack *edge_vectors, + /* Iterate. */ + BMLoop *l_curr, + /* Result. */ + float (*r_lnos)[3], + MLoopNorSpaceArray *r_lnors_spacearr) +{ + BLI_assert((bm->elem_index_dirty & (BM_FACE | BM_LOOP)) == 0); + BLI_assert((vcos == NULL) || ((bm->elem_index_dirty & BM_VERT) == 0)); + UNUSED_VARS_NDEBUG(bm); + + int handled = 0; + + /* Temp normal stack. */ + BLI_SMALLSTACK_DECLARE(normal, float *); + /* Temp clnors stack. */ + BLI_SMALLSTACK_DECLARE(clnors, short *); + /* Temp edge vectors stack, only used when computing lnor spacearr. */ + + /* A smooth edge, we have to check for cyclic smooth fan case. + * If we find a new, never-processed cyclic smooth fan, we can do it now using that loop/edge + * as 'entry point', otherwise we can skip it. */ + + /* NOTE: In theory, we could make bm_mesh_loop_check_cyclic_smooth_fan() store + * mlfan_pivot's in a stack, to avoid having to fan again around + * the vert during actual computation of clnor & clnorspace. However, this would complicate + * the code, add more memory usage, and + * BM_vert_step_fan_loop() is quite cheap in term of CPU cycles, + * so really think it's not worth it. */ + if (BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) && + (BM_elem_flag_test(l_curr, BM_ELEM_TAG) || !BM_loop_check_cyclic_smooth_fan(l_curr))) { + } + else if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) && + !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG)) { + /* Simple case (both edges around that vertex are sharp in related polygon), + * this vertex just takes its poly normal. + */ + const int l_curr_index = BM_elem_index_get(l_curr); + const float *no = fnos ? fnos[BM_elem_index_get(l_curr->f)] : l_curr->f->no; + copy_v3_v3(r_lnos[l_curr_index], no); + + /* If needed, generate this (simple!) lnor space. */ + if (r_lnors_spacearr) { + float vec_curr[3], vec_prev[3]; + MLoopNorSpace *lnor_space = BKE_lnor_space_create(r_lnors_spacearr); + + { + const BMVert *v_pivot = l_curr->v; + const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co; + const BMVert *v_1 = l_curr->next->v; + const float *co_1 = vcos ? vcos[BM_elem_index_get(v_1)] : v_1->co; + const BMVert *v_2 = l_curr->prev->v; + const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co; + + BLI_assert(v_1 == BM_edge_other_vert(l_curr->e, v_pivot)); + BLI_assert(v_2 == BM_edge_other_vert(l_curr->prev->e, v_pivot)); + + sub_v3_v3v3(vec_curr, co_1, co_pivot); + normalize_v3(vec_curr); + sub_v3_v3v3(vec_prev, co_2, co_pivot); + normalize_v3(vec_prev); + } + + BKE_lnor_space_define(lnor_space, r_lnos[l_curr_index], vec_curr, vec_prev, NULL); + /* We know there is only one loop in this space, + * no need to create a linklist in this case... */ + BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, l_curr_index, l_curr, true); + + if (has_clnors) { + const short(*clnor)[2] = clnors_data ? &clnors_data[l_curr_index] : + (const void *)BM_ELEM_CD_GET_VOID_P( + l_curr, cd_loop_clnors_offset); + BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor, r_lnos[l_curr_index]); + } + } + handled = 1; + } + /* We *do not need* to check/tag loops as already computed! + * Due to the fact a loop only links to one of its two edges, + * a same fan *will never be walked more than once!* + * Since we consider edges having neighbor faces with inverted (flipped) normals as sharp, + * we are sure that no fan will be skipped, even only considering the case + * (sharp curr_edge, smooth prev_edge), and not the alternative + * (smooth curr_edge, sharp prev_edge). + * All this due/thanks to link between normals and loop ordering. + */ + else { + /* We have to fan around current vertex, until we find the other non-smooth edge, + * and accumulate face normals into the vertex! + * Note in case this vertex has only one sharp edge, + * this is a waste because the normal is the same as the vertex normal, + * but I do not see any easy way to detect that (would need to count number of sharp edges + * per vertex, I doubt the additional memory usage would be worth it, especially as it + * should not be a common case in real-life meshes anyway). + */ + BMVert *v_pivot = l_curr->v; + BMEdge *e_next; + const BMEdge *e_org = l_curr->e; + BMLoop *lfan_pivot, *lfan_pivot_next; + int lfan_pivot_index; + float lnor[3] = {0.0f, 0.0f, 0.0f}; + float vec_curr[3], vec_next[3], vec_org[3]; + + /* We validate clnors data on the fly - cheapest way to do! */ + int clnors_avg[2] = {0, 0}; + const short(*clnor_ref)[2] = NULL; + int clnors_nbr = 0; + bool clnors_invalid = false; + + const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co; + + MLoopNorSpace *lnor_space = r_lnors_spacearr ? BKE_lnor_space_create(r_lnors_spacearr) : NULL; + + BLI_assert((edge_vectors == NULL) || BLI_stack_is_empty(edge_vectors)); + + lfan_pivot = l_curr; + lfan_pivot_index = BM_elem_index_get(lfan_pivot); + e_next = lfan_pivot->e; /* Current edge here, actually! */ + + /* Only need to compute previous edge's vector once, + * then we can just reuse old current one! */ + { + const BMVert *v_2 = lfan_pivot->next->v; + const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co; + + BLI_assert(v_2 == BM_edge_other_vert(e_next, v_pivot)); + + sub_v3_v3v3(vec_org, co_2, co_pivot); + normalize_v3(vec_org); + copy_v3_v3(vec_curr, vec_org); + + if (r_lnors_spacearr) { + BLI_stack_push(edge_vectors, vec_org); + } + } + + while (true) { + /* Much simpler than in sibling code with basic Mesh data! */ + lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next); + if (lfan_pivot_next) { + BLI_assert(lfan_pivot_next->v == v_pivot); + } + else { + /* next edge is non-manifold, we have to find it ourselves! */ + e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e; + } + + /* Compute edge vector. + * NOTE: We could pre-compute those into an array, in the first iteration, + * instead of computing them twice (or more) here. + * However, time gained is not worth memory and time lost, + * given the fact that this code should not be called that much in real-life meshes. + */ + { + const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot); + const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co; + + sub_v3_v3v3(vec_next, co_2, co_pivot); + normalize_v3(vec_next); + } + + { + /* Code similar to accumulate_vertex_normals_poly_v3. */ + /* Calculate angle between the two poly edges incident on this vertex. */ + const BMFace *f = lfan_pivot->f; + const float fac = saacos(dot_v3v3(vec_next, vec_curr)); + const float *no = fnos ? fnos[BM_elem_index_get(f)] : f->no; + /* Accumulate */ + madd_v3_v3fl(lnor, no, fac); + + if (has_clnors) { + /* Accumulate all clnors, if they are not all equal we have to fix that! */ + const short(*clnor)[2] = clnors_data ? &clnors_data[lfan_pivot_index] : + (const void *)BM_ELEM_CD_GET_VOID_P( + lfan_pivot, cd_loop_clnors_offset); + if (clnors_nbr) { + clnors_invalid |= ((*clnor_ref)[0] != (*clnor)[0] || (*clnor_ref)[1] != (*clnor)[1]); + } + else { + clnor_ref = clnor; + } + clnors_avg[0] += (*clnor)[0]; + clnors_avg[1] += (*clnor)[1]; + clnors_nbr++; + /* We store here a pointer to all custom lnors processed. */ + BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor); + } + } + + /* We store here a pointer to all loop-normals processed. */ + BLI_SMALLSTACK_PUSH(normal, (float *)r_lnos[lfan_pivot_index]); + + if (r_lnors_spacearr) { + /* Assign current lnor space to current 'vertex' loop. */ + BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, lfan_pivot_index, lfan_pivot, false); + if (e_next != e_org) { + /* We store here all edges-normalized vectors processed. */ + BLI_stack_push(edge_vectors, vec_next); + } + } + + handled += 1; + + if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) { + /* Next edge is sharp, we have finished with this fan of faces around this vert! */ + break; + } + + /* Copy next edge vector to current one. */ + copy_v3_v3(vec_curr, vec_next); + /* Next pivot loop to current one. */ + lfan_pivot = lfan_pivot_next; + lfan_pivot_index = BM_elem_index_get(lfan_pivot); + } + + { + float lnor_len = normalize_v3(lnor); + + /* If we are generating lnor spacearr, we can now define the one for this fan. */ + if (r_lnors_spacearr) { + if (UNLIKELY(lnor_len == 0.0f)) { + /* Use vertex normal as fallback! */ + copy_v3_v3(lnor, r_lnos[lfan_pivot_index]); + lnor_len = 1.0f; + } + + BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_next, edge_vectors); + + if (has_clnors) { + if (clnors_invalid) { + short *clnor; + + clnors_avg[0] /= clnors_nbr; + clnors_avg[1] /= clnors_nbr; + /* Fix/update all clnors of this fan with computed average value. */ + + /* Prints continuously when merge custom normals, so commenting. */ + /* printf("Invalid clnors in this fan!\n"); */ + + while ((clnor = BLI_SMALLSTACK_POP(clnors))) { + // print_v2("org clnor", clnor); + clnor[0] = (short)clnors_avg[0]; + clnor[1] = (short)clnors_avg[1]; + } + // print_v2("new clnors", clnors_avg); + } + else { + /* We still have to consume the stack! */ + while (BLI_SMALLSTACK_POP(clnors)) { + /* pass */ + } + } + BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor_ref, lnor); + } + } + + /* In case we get a zero normal here, just use vertex normal already set! */ + if (LIKELY(lnor_len != 0.0f)) { + /* Copy back the final computed normal into all related loop-normals. */ + float *nor; + + while ((nor = BLI_SMALLSTACK_POP(normal))) { + copy_v3_v3(nor, lnor); + } + } + else { + /* We still have to consume the stack! */ + while (BLI_SMALLSTACK_POP(normal)) { + /* pass */ + } + } + } + + /* Tag related vertex as sharp, to avoid fanning around it again + * (in case it was a smooth one). */ + if (r_lnors_spacearr) { + BM_elem_flag_enable(l_curr->v, BM_ELEM_TAG); + } + } + return handled; +} + +static int bm_loop_index_cmp(const void *a, const void *b) +{ + BLI_assert(BM_elem_index_get((BMLoop *)a) != BM_elem_index_get((BMLoop *)b)); + if (BM_elem_index_get((BMLoop *)a) < BM_elem_index_get((BMLoop *)b)) { + return -1; + } + return 1; +} + +/** + * We only tag edges that are *really* smooth when the following conditions are met: + * - The angle between both its polygons normals is below split_angle value. + * - The edge is tagged as smooth. + * - The faces of the edge are tagged as smooth. + * - The faces of the edge have compatible (non-flipped) topological normal (winding), + * i.e. both loops on the same edge do not share the same vertex. + */ +BLI_INLINE bool bm_edge_is_smooth_no_angle_test(const BMEdge *e, + const BMLoop *l_a, + const BMLoop *l_b) +{ + return ( + /* The face is manifold. */ + (l_a->radial_next == l_b) && + /* Faces have winding that faces the same way. */ + (l_a->v != l_b->v) && + /* The edge is smooth. */ + BM_elem_flag_test(e, BM_ELEM_SMOOTH) && + /* Both faces are smooth. */ + BM_elem_flag_test(l_a->f, BM_ELEM_SMOOTH) && BM_elem_flag_test(l_b->f, BM_ELEM_SMOOTH)); +} + +static void bm_edge_tag_from_smooth(const float (*fnos)[3], BMEdge *e, const float split_angle_cos) +{ + BLI_assert(e->l != NULL); + BMLoop *l_a = e->l, *l_b = l_a->radial_next; + bool is_smooth = false; + if (bm_edge_is_smooth_no_angle_test(e, l_a, l_b)) { + if (split_angle_cos != -1.0f) { + const float dot = (fnos == NULL) ? dot_v3v3(l_a->f->no, l_b->f->no) : + dot_v3v3(fnos[BM_elem_index_get(l_a->f)], + fnos[BM_elem_index_get(l_b->f)]); + if (dot >= split_angle_cos) { + is_smooth = true; + } + } + else { + is_smooth = true; + } + } + + /* Perform `BM_elem_flag_set(e, BM_ELEM_TAG, is_smooth)` + * NOTE: This will be set by multiple threads however it will be set to the same value. */ + + /* No need for atomics here as this is a single byte. */ + char *hflag_p = &e->head.hflag; + if (is_smooth) { + *hflag_p = *hflag_p | BM_ELEM_TAG; + } + else { + *hflag_p = *hflag_p & ~BM_ELEM_TAG; + } +} + +/** + * A version of #bm_edge_tag_from_smooth that sets sharp edges + * when they would be considered smooth but exceed the split angle . + * + * \note This doesn't have the same atomic requirement as #bm_edge_tag_from_smooth + * since it isn't run from multiple threads at once. + */ +static void bm_edge_tag_from_smooth_and_set_sharp(const float (*fnos)[3], + BMEdge *e, + const float split_angle_cos) +{ + BLI_assert(e->l != NULL); + BMLoop *l_a = e->l, *l_b = l_a->radial_next; + bool is_smooth = false; + if (bm_edge_is_smooth_no_angle_test(e, l_a, l_b)) { + if (split_angle_cos != -1.0f) { + const float dot = (fnos == NULL) ? dot_v3v3(l_a->f->no, l_b->f->no) : + dot_v3v3(fnos[BM_elem_index_get(l_a->f)], + fnos[BM_elem_index_get(l_b->f)]); + if (dot >= split_angle_cos) { + is_smooth = true; + } + else { + /* Note that we do not care about the other sharp-edge cases + * (sharp poly, non-manifold edge, etc.), + * only tag edge as sharp when it is due to angle threshold. */ + BM_elem_flag_disable(e, BM_ELEM_SMOOTH); + } + } + else { + is_smooth = true; + } + } + + BM_elem_flag_set(e, BM_ELEM_TAG, is_smooth); +} + +/** + * Operate on all vertices loops. + * operating on vertices this is needed for multi-threading + * so there is a guarantee that each thread has isolated loops. + */ +static void bm_mesh_loops_calc_normals_for_vert_with_clnors(BMesh *bm, + const float (*vcos)[3], + const float (*fnos)[3], + float (*r_lnos)[3], + const short (*clnors_data)[2], + const int cd_loop_clnors_offset, + const bool do_rebuild, + const float split_angle_cos, + /* TLS */ + MLoopNorSpaceArray *r_lnors_spacearr, + BLI_Stack *edge_vectors, + /* Iterate over. */ + BMVert *v) +{ + /* Respecting face order is necessary so the initial starting loop is consistent + * with looping over loops of all faces. + * + * Logically we could sort the loops by their index & loop over them + * however it's faster to use the lowest index of an un-ordered list + * since it's common that smooth vertices only ever need to pick one loop + * which then handles all the others. + * + * Sorting is only performed when multiple fans are found. */ + const bool has_clnors = true; + LinkNode *loops_of_vert = NULL; + int loops_of_vert_count = 0; + const bool do_edge_tag = (split_angle_cos != EDGE_TAG_FROM_SPLIT_ANGLE_BYPASS); + + /* The loop with the lowest index. */ + { + LinkNode *link_best; + uint index_best = UINT_MAX; + BMEdge *e_curr_iter = v->e; + do { /* Edges of vertex. */ + BMLoop *l_curr = e_curr_iter->l; + if (l_curr == NULL) { + continue; + } + + if (do_edge_tag) { + bm_edge_tag_from_smooth(fnos, e_curr_iter, split_angle_cos); + } + + do { /* Radial loops. */ + if (l_curr->v != v) { + continue; + } + if (do_rebuild && !BM_ELEM_API_FLAG_TEST(l_curr, BM_LNORSPACE_UPDATE) && + !(bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL)) { + continue; + } + BM_elem_flag_disable(l_curr, BM_ELEM_TAG); + BLI_linklist_prepend_alloca(&loops_of_vert, l_curr); + loops_of_vert_count += 1; + + const uint index_test = (uint)BM_elem_index_get(l_curr); + if (index_best > index_test) { + index_best = index_test; + link_best = loops_of_vert; + } + } while ((l_curr = l_curr->radial_next) != e_curr_iter->l); + } while ((e_curr_iter = BM_DISK_EDGE_NEXT(e_curr_iter, v)) != v->e); + + if (UNLIKELY(loops_of_vert == NULL)) { + return; + } + + /* Immediately pop the best element. + * The order doesn't matter, so swap the links as it's simpler than tracking + * reference to `link_best`. */ + if (link_best != loops_of_vert) { + SWAP(void *, link_best->link, loops_of_vert->link); + } + } + + bool loops_of_vert_is_sorted = false; + + /* Keep track of the number of loops that have been assigned. */ + int loops_of_vert_handled = 0; + + while (loops_of_vert != NULL) { + BMLoop *l_best = loops_of_vert->link; + loops_of_vert = loops_of_vert->next; + + BLI_assert(l_best->v == v); + loops_of_vert_handled += bm_mesh_loops_calc_normals_for_loop(bm, + vcos, + fnos, + clnors_data, + cd_loop_clnors_offset, + has_clnors, + edge_vectors, + l_best, + r_lnos, + r_lnors_spacearr); + + /* Check if an early exit is possible without an exhaustive inspection of every loop + * where 1 loop's fan extends out to all remaining loops. + * This is a common case for smooth vertices. */ + BLI_assert(loops_of_vert_handled <= loops_of_vert_count); + if (loops_of_vert_handled == loops_of_vert_count) { + break; + } + + /* Note on sorting, in some cases it will be faster to scan for the lowest index each time. + * However in the worst case this is `O(N^2)`, so use a single sort call instead. */ + if (!loops_of_vert_is_sorted) { + if (loops_of_vert && loops_of_vert->next) { + loops_of_vert = BLI_linklist_sort(loops_of_vert, bm_loop_index_cmp); + loops_of_vert_is_sorted = true; + } + } + } +} + +/** + * A simplified version of #bm_mesh_loops_calc_normals_for_vert_with_clnors + * that can operate on loops in any order. + */ +static void bm_mesh_loops_calc_normals_for_vert_without_clnors( + BMesh *bm, + const float (*vcos)[3], + const float (*fnos)[3], + float (*r_lnos)[3], + const bool do_rebuild, + const float split_angle_cos, + /* TLS */ + MLoopNorSpaceArray *r_lnors_spacearr, + BLI_Stack *edge_vectors, + /* Iterate over. */ + BMVert *v) +{ + const bool has_clnors = false; + const short(*clnors_data)[2] = NULL; + const bool do_edge_tag = (split_angle_cos != EDGE_TAG_FROM_SPLIT_ANGLE_BYPASS); + const int cd_loop_clnors_offset = -1; + + BMEdge *e_curr_iter; + + /* Unfortunately a loop is needed just to clear loop-tags. */ + e_curr_iter = v->e; + do { /* Edges of vertex. */ + BMLoop *l_curr = e_curr_iter->l; + if (l_curr == NULL) { + continue; + } + + if (do_edge_tag) { + bm_edge_tag_from_smooth(fnos, e_curr_iter, split_angle_cos); + } + + do { /* Radial loops. */ + if (l_curr->v != v) { + continue; + } + BM_elem_flag_disable(l_curr, BM_ELEM_TAG); + } while ((l_curr = l_curr->radial_next) != e_curr_iter->l); + } while ((e_curr_iter = BM_DISK_EDGE_NEXT(e_curr_iter, v)) != v->e); + + e_curr_iter = v->e; + do { /* Edges of vertex. */ + BMLoop *l_curr = e_curr_iter->l; + if (l_curr == NULL) { + continue; + } + do { /* Radial loops. */ + if (l_curr->v != v) { + continue; + } + if (do_rebuild && !BM_ELEM_API_FLAG_TEST(l_curr, BM_LNORSPACE_UPDATE) && + !(bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL)) { + continue; + } + bm_mesh_loops_calc_normals_for_loop(bm, + vcos, + fnos, + clnors_data, + cd_loop_clnors_offset, + has_clnors, + edge_vectors, + l_curr, + r_lnos, + r_lnors_spacearr); + } while ((l_curr = l_curr->radial_next) != e_curr_iter->l); + } while ((e_curr_iter = BM_DISK_EDGE_NEXT(e_curr_iter, v)) != v->e); +} + +/** * BMesh version of BKE_mesh_normals_loop_split() in `mesh_evaluate.cc` * Will use first clnors_data array, and fallback to cd_loop_clnors_offset * (use NULL and -1 to not use clnors). @@ -533,26 +1100,24 @@ bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr) * \note This sets #BM_ELEM_TAG which is used in tool code (e.g. T84426). * we could add a low-level API flag for this, see #BM_ELEM_API_FLAG_ENABLE and friends. */ -static void bm_mesh_loops_calc_normals(BMesh *bm, - const float (*vcos)[3], - const float (*fnos)[3], - float (*r_lnos)[3], - MLoopNorSpaceArray *r_lnors_spacearr, - const short (*clnors_data)[2], - const int cd_loop_clnors_offset, - const bool do_rebuild) +static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm, + const float (*vcos)[3], + const float (*fnos)[3], + float (*r_lnos)[3], + MLoopNorSpaceArray *r_lnors_spacearr, + const short (*clnors_data)[2], + const int cd_loop_clnors_offset, + const bool do_rebuild, + const float split_angle) { BMIter fiter; BMFace *f_curr; const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1); + const bool check_angle = (split_angle < (float)M_PI); + const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f; MLoopNorSpaceArray _lnors_spacearr = {NULL}; - /* Temp normal stack. */ - BLI_SMALLSTACK_DECLARE(normal, float *); - /* Temp clnors stack. */ - BLI_SMALLSTACK_DECLARE(clnors, short *); - /* Temp edge vectors stack, only used when computing lnor spacearr. */ BLI_Stack *edge_vectors = NULL; { @@ -573,6 +1138,10 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, edge_vectors = BLI_stack_new(sizeof(float[3]), __func__); } + if (split_angle_cos != -1.0f) { + bm_mesh_edges_sharp_tag(bm, fnos, has_clnors ? (float)M_PI : split_angle, false); + } + /* Clear all loops' tags (means none are to be skipped for now). */ int index_face, index_loop = 0; BM_ITER_MESH_INDEX (f_curr, &fiter, bm, BM_FACES_OF_MESH, index_face) { @@ -601,277 +1170,249 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, !(bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL)) { continue; } - /* A smooth edge, we have to check for cyclic smooth fan case. - * If we find a new, never-processed cyclic smooth fan, we can do it now using that loop/edge - * as 'entry point', otherwise we can skip it. */ - - /* NOTE: In theory, we could make bm_mesh_loop_check_cyclic_smooth_fan() store - * mlfan_pivot's in a stack, to avoid having to fan again around - * the vert during actual computation of clnor & clnorspace. However, this would complicate - * the code, add more memory usage, and - * BM_vert_step_fan_loop() is quite cheap in term of CPU cycles, - * so really think it's not worth it. */ - if (BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) && - (BM_elem_flag_test(l_curr, BM_ELEM_TAG) || !BM_loop_check_cyclic_smooth_fan(l_curr))) { - } - else if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) && - !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG)) { - /* Simple case (both edges around that vertex are sharp in related polygon), - * this vertex just takes its poly normal. - */ - const int l_curr_index = BM_elem_index_get(l_curr); - const float *no = fnos ? fnos[BM_elem_index_get(f_curr)] : f_curr->no; - copy_v3_v3(r_lnos[l_curr_index], no); - - /* If needed, generate this (simple!) lnor space. */ - if (r_lnors_spacearr) { - float vec_curr[3], vec_prev[3]; - MLoopNorSpace *lnor_space = BKE_lnor_space_create(r_lnors_spacearr); - - { - const BMVert *v_pivot = l_curr->v; - const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co; - const BMVert *v_1 = l_curr->next->v; - const float *co_1 = vcos ? vcos[BM_elem_index_get(v_1)] : v_1->co; - const BMVert *v_2 = l_curr->prev->v; - const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co; - - BLI_assert(v_1 == BM_edge_other_vert(l_curr->e, v_pivot)); - BLI_assert(v_2 == BM_edge_other_vert(l_curr->prev->e, v_pivot)); - - sub_v3_v3v3(vec_curr, co_1, co_pivot); - normalize_v3(vec_curr); - sub_v3_v3v3(vec_prev, co_2, co_pivot); - normalize_v3(vec_prev); - } - - BKE_lnor_space_define(lnor_space, r_lnos[l_curr_index], vec_curr, vec_prev, NULL); - /* We know there is only one loop in this space, - * no need to create a linklist in this case... */ - BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, l_curr_index, l_curr, true); - - if (has_clnors) { - const short(*clnor)[2] = clnors_data ? &clnors_data[l_curr_index] : - (const void *)BM_ELEM_CD_GET_VOID_P( - l_curr, cd_loop_clnors_offset); - BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor, r_lnos[l_curr_index]); - } - } - } - /* We *do not need* to check/tag loops as already computed! - * Due to the fact a loop only links to one of its two edges, - * a same fan *will never be walked more than once!* - * Since we consider edges having neighbor faces with inverted (flipped) normals as sharp, - * we are sure that no fan will be skipped, even only considering the case - * (sharp curr_edge, smooth prev_edge), and not the alternative - * (smooth curr_edge, sharp prev_edge). - * All this due/thanks to link between normals and loop ordering. - */ - else { - /* We have to fan around current vertex, until we find the other non-smooth edge, - * and accumulate face normals into the vertex! - * Note in case this vertex has only one sharp edge, - * this is a waste because the normal is the same as the vertex normal, - * but I do not see any easy way to detect that (would need to count number of sharp edges - * per vertex, I doubt the additional memory usage would be worth it, especially as it - * should not be a common case in real-life meshes anyway). - */ - BMVert *v_pivot = l_curr->v; - BMEdge *e_next; - const BMEdge *e_org = l_curr->e; - BMLoop *lfan_pivot, *lfan_pivot_next; - int lfan_pivot_index; - float lnor[3] = {0.0f, 0.0f, 0.0f}; - float vec_curr[3], vec_next[3], vec_org[3]; - - /* We validate clnors data on the fly - cheapest way to do! */ - int clnors_avg[2] = {0, 0}; - const short(*clnor_ref)[2] = NULL; - int clnors_nbr = 0; - bool clnors_invalid = false; - - const float *co_pivot = vcos ? vcos[BM_elem_index_get(v_pivot)] : v_pivot->co; - - MLoopNorSpace *lnor_space = r_lnors_spacearr ? BKE_lnor_space_create(r_lnors_spacearr) : - NULL; - - BLI_assert((edge_vectors == NULL) || BLI_stack_is_empty(edge_vectors)); - - lfan_pivot = l_curr; - lfan_pivot_index = BM_elem_index_get(lfan_pivot); - e_next = lfan_pivot->e; /* Current edge here, actually! */ - - /* Only need to compute previous edge's vector once, - * then we can just reuse old current one! */ - { - const BMVert *v_2 = lfan_pivot->next->v; - const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co; - - BLI_assert(v_2 == BM_edge_other_vert(e_next, v_pivot)); - - sub_v3_v3v3(vec_org, co_2, co_pivot); - normalize_v3(vec_org); - copy_v3_v3(vec_curr, vec_org); + bm_mesh_loops_calc_normals_for_loop(bm, + vcos, + fnos, + clnors_data, + cd_loop_clnors_offset, + has_clnors, + edge_vectors, + l_curr, + r_lnos, + r_lnors_spacearr); + } while ((l_curr = l_curr->next) != l_first); + } - if (r_lnors_spacearr) { - BLI_stack_push(edge_vectors, vec_org); - } - } + if (r_lnors_spacearr) { + BLI_stack_free(edge_vectors); + if (r_lnors_spacearr == &_lnors_spacearr) { + BKE_lnor_spacearr_free(r_lnors_spacearr); + } + } +} - while (true) { - /* Much simpler than in sibling code with basic Mesh data! */ - lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next); - if (lfan_pivot_next) { - BLI_assert(lfan_pivot_next->v == v_pivot); - } - else { - /* next edge is non-manifold, we have to find it ourselves! */ - e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e; - } +typedef struct BMLoopsCalcNormalsWithCoordsData { + /* Read-only data. */ + const float (*fnos)[3]; + const float (*vcos)[3]; + BMesh *bm; + const short (*clnors_data)[2]; + const int cd_loop_clnors_offset; + const bool do_rebuild; + const float split_angle_cos; + + /* Output. */ + float (*r_lnos)[3]; + MLoopNorSpaceArray *r_lnors_spacearr; +} BMLoopsCalcNormalsWithCoordsData; + +typedef struct BMLoopsCalcNormalsWithCoords_TLS { + BLI_Stack *edge_vectors; + + /** Copied from #BMLoopsCalcNormalsWithCoordsData.r_lnors_spacearr when it's not NULL. */ + MLoopNorSpaceArray *lnors_spacearr; + MLoopNorSpaceArray lnors_spacearr_buf; +} BMLoopsCalcNormalsWithCoords_TLS; + +static void bm_mesh_loops_calc_normals_for_vert_init_fn(const void *__restrict userdata, + void *__restrict chunk) +{ + const BMLoopsCalcNormalsWithCoordsData *data = userdata; + BMLoopsCalcNormalsWithCoords_TLS *tls_data = chunk; + if (data->r_lnors_spacearr) { + tls_data->edge_vectors = BLI_stack_new(sizeof(float[3]), __func__); + BKE_lnor_spacearr_tls_init(data->r_lnors_spacearr, &tls_data->lnors_spacearr_buf); + tls_data->lnors_spacearr = &tls_data->lnors_spacearr_buf; + } + else { + tls_data->lnors_spacearr = NULL; + } +} - /* Compute edge vector. - * NOTE: We could pre-compute those into an array, in the first iteration, - * instead of computing them twice (or more) here. - * However, time gained is not worth memory and time lost, - * given the fact that this code should not be called that much in real-life meshes. - */ - { - const BMVert *v_2 = BM_edge_other_vert(e_next, v_pivot); - const float *co_2 = vcos ? vcos[BM_elem_index_get(v_2)] : v_2->co; +static void bm_mesh_loops_calc_normals_for_vert_reduce_fn(const void *__restrict userdata, + void *__restrict UNUSED(chunk_join), + void *__restrict chunk) +{ + const BMLoopsCalcNormalsWithCoordsData *data = userdata; + BMLoopsCalcNormalsWithCoords_TLS *tls_data = chunk; - sub_v3_v3v3(vec_next, co_2, co_pivot); - normalize_v3(vec_next); - } + if (data->r_lnors_spacearr) { + BKE_lnor_spacearr_tls_join(data->r_lnors_spacearr, tls_data->lnors_spacearr); + } +} - { - /* Code similar to accumulate_vertex_normals_poly_v3. */ - /* Calculate angle between the two poly edges incident on this vertex. */ - const BMFace *f = lfan_pivot->f; - const float fac = saacos(dot_v3v3(vec_next, vec_curr)); - const float *no = fnos ? fnos[BM_elem_index_get(f)] : f->no; - /* Accumulate */ - madd_v3_v3fl(lnor, no, fac); - - if (has_clnors) { - /* Accumulate all clnors, if they are not all equal we have to fix that! */ - const short(*clnor)[2] = clnors_data ? &clnors_data[lfan_pivot_index] : - (const void *)BM_ELEM_CD_GET_VOID_P( - lfan_pivot, cd_loop_clnors_offset); - if (clnors_nbr) { - clnors_invalid |= ((*clnor_ref)[0] != (*clnor)[0] || - (*clnor_ref)[1] != (*clnor)[1]); - } - else { - clnor_ref = clnor; - } - clnors_avg[0] += (*clnor)[0]; - clnors_avg[1] += (*clnor)[1]; - clnors_nbr++; - /* We store here a pointer to all custom lnors processed. */ - BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor); - } - } +static void bm_mesh_loops_calc_normals_for_vert_free_fn(const void *__restrict userdata, + void *__restrict chunk) +{ + const BMLoopsCalcNormalsWithCoordsData *data = userdata; + BMLoopsCalcNormalsWithCoords_TLS *tls_data = chunk; - /* We store here a pointer to all loop-normals processed. */ - BLI_SMALLSTACK_PUSH(normal, (float *)r_lnos[lfan_pivot_index]); + if (data->r_lnors_spacearr) { + BLI_stack_free(tls_data->edge_vectors); + } +} - if (r_lnors_spacearr) { - /* Assign current lnor space to current 'vertex' loop. */ - BKE_lnor_space_add_loop( - r_lnors_spacearr, lnor_space, lfan_pivot_index, lfan_pivot, false); - if (e_next != e_org) { - /* We store here all edges-normalized vectors processed. */ - BLI_stack_push(edge_vectors, vec_next); - } - } +static void bm_mesh_loops_calc_normals_for_vert_with_clnors_fn( + void *userdata, MempoolIterData *mp_v, const TaskParallelTLS *__restrict tls) +{ + BMVert *v = (BMVert *)mp_v; + if (v->e == NULL) { + return; + } + BMLoopsCalcNormalsWithCoordsData *data = userdata; + BMLoopsCalcNormalsWithCoords_TLS *tls_data = tls->userdata_chunk; + bm_mesh_loops_calc_normals_for_vert_with_clnors(data->bm, + data->vcos, + data->fnos, + data->r_lnos, + + data->clnors_data, + data->cd_loop_clnors_offset, + data->do_rebuild, + data->split_angle_cos, + /* Thread local. */ + tls_data->lnors_spacearr, + tls_data->edge_vectors, + /* Iterate over. */ + v); +} - if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) { - /* Next edge is sharp, we have finished with this fan of faces around this vert! */ - break; - } +static void bm_mesh_loops_calc_normals_for_vert_without_clnors_fn( + void *userdata, MempoolIterData *mp_v, const TaskParallelTLS *__restrict tls) +{ + BMVert *v = (BMVert *)mp_v; + if (v->e == NULL) { + return; + } + BMLoopsCalcNormalsWithCoordsData *data = userdata; + BMLoopsCalcNormalsWithCoords_TLS *tls_data = tls->userdata_chunk; + bm_mesh_loops_calc_normals_for_vert_without_clnors(data->bm, + data->vcos, + data->fnos, + data->r_lnos, + + data->do_rebuild, + data->split_angle_cos, + /* Thread local. */ + tls_data->lnors_spacearr, + tls_data->edge_vectors, + /* Iterate over. */ + v); +} - /* Copy next edge vector to current one. */ - copy_v3_v3(vec_curr, vec_next); - /* Next pivot loop to current one. */ - lfan_pivot = lfan_pivot_next; - lfan_pivot_index = BM_elem_index_get(lfan_pivot); - } +static void bm_mesh_loops_calc_normals__multi_threaded(BMesh *bm, + const float (*vcos)[3], + const float (*fnos)[3], + float (*r_lnos)[3], + MLoopNorSpaceArray *r_lnors_spacearr, + const short (*clnors_data)[2], + const int cd_loop_clnors_offset, + const bool do_rebuild, + const float split_angle) +{ + const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1); + const bool check_angle = (split_angle < (float)M_PI); + const float split_angle_cos = check_angle ? cosf(split_angle) : -1.0f; - { - float lnor_len = normalize_v3(lnor); + MLoopNorSpaceArray _lnors_spacearr = {NULL}; - /* If we are generating lnor spacearr, we can now define the one for this fan. */ - if (r_lnors_spacearr) { - if (UNLIKELY(lnor_len == 0.0f)) { - /* Use vertex normal as fallback! */ - copy_v3_v3(lnor, r_lnos[lfan_pivot_index]); - lnor_len = 1.0f; - } + { + char htype = BM_LOOP; + if (vcos) { + htype |= BM_VERT; + } + if (fnos) { + htype |= BM_FACE; + } + /* Face/Loop indices are set inline below. */ + BM_mesh_elem_index_ensure(bm, htype); + } - BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_next, edge_vectors); - - if (has_clnors) { - if (clnors_invalid) { - short *clnor; - - clnors_avg[0] /= clnors_nbr; - clnors_avg[1] /= clnors_nbr; - /* Fix/update all clnors of this fan with computed average value. */ - - /* Prints continuously when merge custom normals, so commenting. */ - /* printf("Invalid clnors in this fan!\n"); */ - - while ((clnor = BLI_SMALLSTACK_POP(clnors))) { - // print_v2("org clnor", clnor); - clnor[0] = (short)clnors_avg[0]; - clnor[1] = (short)clnors_avg[1]; - } - // print_v2("new clnors", clnors_avg); - } - else { - /* We still have to consume the stack! */ - while (BLI_SMALLSTACK_POP(clnors)) { - /* pass */ - } - } - BKE_lnor_space_custom_data_to_normal(lnor_space, *clnor_ref, lnor); - } - } + if (!r_lnors_spacearr && has_clnors) { + /* We need to compute lnor spacearr if some custom lnor data are given to us! */ + r_lnors_spacearr = &_lnors_spacearr; + } + if (r_lnors_spacearr) { + BKE_lnor_spacearr_init(r_lnors_spacearr, bm->totloop, MLNOR_SPACEARR_BMLOOP_PTR); + } - /* In case we get a zero normal here, just use vertex normal already set! */ - if (LIKELY(lnor_len != 0.0f)) { - /* Copy back the final computed normal into all related loop-normals. */ - float *nor; + /* We now know edges that can be smoothed (they are tagged), + * and edges that will be hard (they aren't). + * Now, time to generate the normals. + */ - while ((nor = BLI_SMALLSTACK_POP(normal))) { - copy_v3_v3(nor, lnor); - } - } - else { - /* We still have to consume the stack! */ - while (BLI_SMALLSTACK_POP(normal)) { - /* pass */ - } - } - } + TaskParallelSettings settings; + BLI_parallel_mempool_settings_defaults(&settings); - /* Tag related vertex as sharp, to avoid fanning around it again - * (in case it was a smooth one). */ - if (r_lnors_spacearr) { - BM_elem_flag_enable(l_curr->v, BM_ELEM_TAG); - } - } - } while ((l_curr = l_curr->next) != l_first); - } + BMLoopsCalcNormalsWithCoords_TLS tls = {NULL}; + + settings.userdata_chunk = &tls; + settings.userdata_chunk_size = sizeof(tls); + + settings.func_init = bm_mesh_loops_calc_normals_for_vert_init_fn; + settings.func_reduce = bm_mesh_loops_calc_normals_for_vert_reduce_fn; + settings.func_free = bm_mesh_loops_calc_normals_for_vert_free_fn; + + BMLoopsCalcNormalsWithCoordsData data = { + .bm = bm, + .vcos = vcos, + .fnos = fnos, + .r_lnos = r_lnos, + .r_lnors_spacearr = r_lnors_spacearr, + .clnors_data = clnors_data, + .cd_loop_clnors_offset = cd_loop_clnors_offset, + .do_rebuild = do_rebuild, + .split_angle_cos = split_angle_cos, + }; + + BM_iter_parallel(bm, + BM_VERTS_OF_MESH, + has_clnors ? bm_mesh_loops_calc_normals_for_vert_with_clnors_fn : + bm_mesh_loops_calc_normals_for_vert_without_clnors_fn, + &data, + &settings); if (r_lnors_spacearr) { - BLI_stack_free(edge_vectors); if (r_lnors_spacearr == &_lnors_spacearr) { BKE_lnor_spacearr_free(r_lnors_spacearr); } } } +static void bm_mesh_loops_calc_normals(BMesh *bm, + const float (*vcos)[3], + const float (*fnos)[3], + float (*r_lnos)[3], + MLoopNorSpaceArray *r_lnors_spacearr, + const short (*clnors_data)[2], + const int cd_loop_clnors_offset, + const bool do_rebuild, + const float split_angle) +{ + if (bm->totloop < BM_OMP_LIMIT) { + bm_mesh_loops_calc_normals__single_threaded(bm, + vcos, + fnos, + r_lnos, + r_lnors_spacearr, + clnors_data, + cd_loop_clnors_offset, + do_rebuild, + split_angle); + } + else { + bm_mesh_loops_calc_normals__multi_threaded(bm, + vcos, + fnos, + r_lnos, + r_lnors_spacearr, + clnors_data, + cd_loop_clnors_offset, + do_rebuild, + split_angle); + } +} + /* This threshold is a bit touchy (usual float precision issue), this value seems OK. */ #define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-4f) @@ -1062,7 +1603,6 @@ static void bm_mesh_loops_assign_normal_data(BMesh *bm, */ static void bm_mesh_loops_custom_normals_set(BMesh *bm, const float (*vcos)[3], - const float (*vnos)[3], const float (*fnos)[3], MLoopNorSpaceArray *r_lnors_spacearr, short (*r_clnors_data)[2], @@ -1080,12 +1620,19 @@ static void bm_mesh_loops_custom_normals_set(BMesh *bm, /* Tag smooth edges and set lnos from vnos when they might be completely smooth... * When using custom loop normals, disable the angle feature! */ - bm_mesh_edges_sharp_tag(bm, vnos, fnos, cur_lnors, (float)M_PI, false); + bm_mesh_edges_sharp_tag(bm, fnos, (float)M_PI, false); /* Finish computing lnos by accumulating face normals * in each fan of faces defined by sharp edges. */ - bm_mesh_loops_calc_normals( - bm, vcos, fnos, cur_lnors, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, false); + bm_mesh_loops_calc_normals(bm, + vcos, + fnos, + cur_lnors, + r_lnors_spacearr, + r_clnors_data, + cd_loop_clnors_offset, + false, + EDGE_TAG_FROM_SPLIT_ANGLE_BYPASS); /* Extract new normals from the data layer if necessary. */ float(*custom_lnors)[3] = new_lnors; @@ -1118,8 +1665,15 @@ static void bm_mesh_loops_custom_normals_set(BMesh *bm, * spacearr/smooth fans matching the given custom lnors. */ BKE_lnor_spacearr_clear(r_lnors_spacearr); - bm_mesh_loops_calc_normals( - bm, vcos, fnos, cur_lnors, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, false); + bm_mesh_loops_calc_normals(bm, + vcos, + fnos, + cur_lnors, + r_lnors_spacearr, + r_clnors_data, + cd_loop_clnors_offset, + false, + EDGE_TAG_FROM_SPLIT_ANGLE_BYPASS); } /* And we just have to convert plain object-space custom normals to our @@ -1167,40 +1721,6 @@ static void bm_mesh_loops_calc_normals_no_autosmooth(BMesh *bm, } } -#if 0 /* Unused currently */ -/** - * \brief BMesh Compute Loop Normals - * - * Updates the loop normals of a mesh. - * Assumes vertex and face normals are valid (else call BM_mesh_normals_update() first)! - */ -void BM_mesh_loop_normals_update(BMesh *bm, - const bool use_split_normals, - const float split_angle, - float (*r_lnos)[3], - MLoopNorSpaceArray *r_lnors_spacearr, - const short (*clnors_data)[2], - const int cd_loop_clnors_offset) -{ - const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1); - - if (use_split_normals) { - /* Tag smooth edges and set lnos from vnos when they might be completely smooth... - * When using custom loop normals, disable the angle feature! */ - bm_mesh_edges_sharp_tag(bm, NULL, NULL, has_clnors ? (float)M_PI : split_angle, r_lnos); - - /* Finish computing lnos by accumulating face normals - * in each fan of faces defined by sharp edges. */ - bm_mesh_loops_calc_normals( - bm, NULL, NULL, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset); - } - else { - BLI_assert(!r_lnors_spacearr); - bm_mesh_loops_calc_normals_no_autosmooth(bm, NULL, NULL, r_lnos); - } -} -#endif - /** * \brief BMesh Compute Loop Normals from/to external data. * @@ -1223,14 +1743,15 @@ void BM_loops_calc_normal_vcos(BMesh *bm, const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1); if (use_split_normals) { - /* Tag smooth edges and set lnos from vnos when they might be completely smooth... - * When using custom loop normals, disable the angle feature! */ - bm_mesh_edges_sharp_tag(bm, vnos, fnos, r_lnos, has_clnors ? (float)M_PI : split_angle, false); - - /* Finish computing lnos by accumulating face normals - * in each fan of faces defined by sharp edges. */ - bm_mesh_loops_calc_normals( - bm, vcos, fnos, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset, do_rebuild); + bm_mesh_loops_calc_normals(bm, + vcos, + fnos, + r_lnos, + r_lnors_spacearr, + clnors_data, + cd_loop_clnors_offset, + do_rebuild, + has_clnors ? (float)M_PI : split_angle); } else { BLI_assert(!r_lnors_spacearr); @@ -1788,7 +2309,6 @@ void BM_custom_loop_normals_from_vector_layer(BMesh *bm, bool add_sharp_edges) bm_mesh_loops_custom_normals_set(bm, NULL, NULL, - NULL, bm->lnor_spacearr, NULL, cd_custom_normal_offset, diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index 8350c3910db..a25c644555c 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -245,7 +245,7 @@ static BMOpDefine bmo_region_extend_def = { * Edge Rotate. * * Rotates edges topologically. Also known as "spin edge" to some people. - * Simple example: ``[/] becomes [|] then [\]``. + * Simple example: `[/] becomes [|] then [\]`. */ static BMOpDefine bmo_rotate_edges_def = { "rotate_edges", @@ -1957,7 +1957,7 @@ static BMOpDefine bmo_inset_region_def = { }; /* - * Edgeloop Offset. + * Edge-loop Offset. * * Creates edge loops based on simple edge-outset method. */ diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c index 7d3f402a3d8..ecbdf316d90 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -317,7 +317,7 @@ float BM_face_calc_perimeter_with_mat3(const BMFace *f, const float mat3[3][3]) /** * Utility function to calculate the edge which is most different from the other two. * - * \return The first edge index, where the second vertex is ``(index + 1) % 3``. + * \return The first edge index, where the second vertex is `(index + 1) % 3`. */ static int bm_vert_tri_find_unique_edge(BMVert *verts[3]) { diff --git a/source/blender/bmesh/intern/bmesh_query.c b/source/blender/bmesh/intern/bmesh_query.c index bc881040e4e..cb5764b1c91 100644 --- a/source/blender/bmesh/intern/bmesh_query.c +++ b/source/blender/bmesh/intern/bmesh_query.c @@ -766,7 +766,7 @@ bool BM_edge_loop_pair(BMEdge *e, BMLoop **r_la, BMLoop **r_lb) } /** - * Fast alternative to ``(BM_vert_edge_count(v) == 2)`` + * Fast alternative to `(BM_vert_edge_count(v) == 2)`. */ bool BM_vert_is_edge_pair(const BMVert *v) { @@ -779,7 +779,7 @@ bool BM_vert_is_edge_pair(const BMVert *v) } /** - * Fast alternative to ``(BM_vert_edge_count(v) == 2)`` + * Fast alternative to `(BM_vert_edge_count(v) == 2)` * that checks both edges connect to the same faces. */ bool BM_vert_is_edge_pair_manifold(const BMVert *v) @@ -896,7 +896,7 @@ int BM_vert_face_count_at_most(const BMVert *v, int count_max) /** * Return true if the vertex is connected to _any_ faces. * - * same as ``BM_vert_face_count(v) != 0`` or ``BM_vert_find_first_loop(v) == NULL`` + * same as `BM_vert_face_count(v) != 0` or `BM_vert_find_first_loop(v) == NULL`. */ bool BM_vert_face_check(const BMVert *v) { diff --git a/source/blender/bmesh/intern/bmesh_query.h b/source/blender/bmesh/intern/bmesh_query.h index 5627f3820c2..021358f81ad 100644 --- a/source/blender/bmesh/intern/bmesh_query.h +++ b/source/blender/bmesh/intern/bmesh_query.h @@ -272,7 +272,7 @@ int BM_mesh_calc_edge_groups_as_arrays(BMesh *bm, int (**r_groups)[3]) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2, 3, 4, 5); -/* not really any good place to put this */ +/* Not really any good place to put this. */ float bmesh_subd_falloff_calc(const int falloff, float val) ATTR_WARN_UNUSED_RESULT; #include "bmesh_query_inline.h" diff --git a/source/blender/bmesh/intern/bmesh_structure.c b/source/blender/bmesh/intern/bmesh_structure.c index cfdce0b749b..d5d72cd4ba3 100644 --- a/source/blender/bmesh/intern/bmesh_structure.c +++ b/source/blender/bmesh/intern/bmesh_structure.c @@ -592,7 +592,7 @@ int bmesh_radial_facevert_count_at_most(const BMLoop *l, const BMVert *v, const /** * \brief RADIAL CHECK FACE VERT * - * Quicker check for ``bmesh_radial_facevert_count(...) != 0`` + * Quicker check for `bmesh_radial_facevert_count(...) != 0`. */ bool bmesh_radial_facevert_check(const BMLoop *l, const BMVert *v) { diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 1e8ef9737d3..0f99f04ad57 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -384,7 +384,7 @@ typedef struct BevelParams { // #pragma GCC diagnostic ignored "-Wpadded" -/* Only for debugging, this file shouldn't be in blender repo. */ +/* Only for debugging, this file shouldn't be in blender repository. */ // #include "bevdebug.c" /* Use the unused _BM_ELEM_TAG_ALT flag to flag the 'long' loops (parallel to beveled edge) @@ -1495,8 +1495,9 @@ static void offset_meet(BevelParams *bp, } } -/* Chosen so 1/sin(BEVEL_GOOD_ANGLE) is about 4, giving that expansion factor to bevel width. */ -#define BEVEL_GOOD_ANGLE 0.25f +/* This was changed from 0.25f to fix bug T86768. + * Original bug T44961 remains fixed with this value. */ +#define BEVEL_GOOD_ANGLE 0.0001f /** * Calculate the meeting point between e1 and e2 (one of which should have zero offsets), diff --git a/source/blender/bmesh/tools/bmesh_region_match.c b/source/blender/bmesh/tools/bmesh_region_match.c index 4ae5bfb7fb2..924538490ad 100644 --- a/source/blender/bmesh/tools/bmesh_region_match.c +++ b/source/blender/bmesh/tools/bmesh_region_match.c @@ -1336,7 +1336,7 @@ static void bm_vert_fasthash_destroy(UUIDFashMatch *fm) * Take a face-region and return a list of matching face-regions. * * \param faces_region: A single, contiguous face-region. - * \return A list of matching null-terminated face-region arrays. + * \return A list of matching null-terminated face-region arrays. */ int BM_mesh_region_match(BMesh *bm, BMFace **faces_region, diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 20b56ceb55f..830792a2a48 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -49,8 +49,11 @@ set(SRC COM_compositor.h COM_defines.h + intern/COM_BufferArea.h intern/COM_BufferOperation.cc intern/COM_BufferOperation.h + intern/COM_BufferRange.h + intern/COM_BuffersIterator.h intern/COM_CPUDevice.cc intern/COM_CPUDevice.h intern/COM_ChunkOrder.cc diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h index 9f8e6f10215..900f29db44c 100644 --- a/source/blender/compositor/COM_defines.h +++ b/source/blender/compositor/COM_defines.h @@ -18,6 +18,9 @@ #pragma once +#include "BLI_index_range.hh" +#include "BLI_rect.h" + namespace blender::compositor { enum class eExecutionModel { @@ -63,6 +66,7 @@ constexpr int COM_DATA_TYPE_VALUE_CHANNELS = COM_data_type_num_channels(DataType constexpr int COM_DATA_TYPE_VECTOR_CHANNELS = COM_data_type_num_channels(DataType::Vector); constexpr int COM_DATA_TYPE_COLOR_CHANNELS = COM_data_type_num_channels(DataType::Color); +constexpr float COM_COLOR_TRANSPARENT[4] = {0.0f, 0.0f, 0.0f, 0.0f}; constexpr float COM_VECTOR_ZERO[3] = {0.0f, 0.0f, 0.0f}; constexpr float COM_VALUE_ZERO[1] = {0.0f}; constexpr float COM_VALUE_ONE[1] = {1.0f}; @@ -109,4 +113,24 @@ constexpr float COM_PREVIEW_SIZE = 140.f; constexpr float COM_RULE_OF_THIRDS_DIVIDER = 100.0f; constexpr float COM_BLUR_BOKEH_PIXELS = 512; +constexpr IndexRange XRange(const rcti &area) +{ + return IndexRange(area.xmin, area.xmax - area.xmin); +} + +constexpr IndexRange YRange(const rcti &area) +{ + return IndexRange(area.ymin, area.ymax - area.ymin); +} + +constexpr IndexRange XRange(const rcti *area) +{ + return XRange(*area); +} + +constexpr IndexRange YRange(const rcti *area) +{ + return YRange(*area); +} + } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_BufferArea.h b/source/blender/compositor/intern/COM_BufferArea.h new file mode 100644 index 00000000000..6f7756ecbfc --- /dev/null +++ b/source/blender/compositor/intern/COM_BufferArea.h @@ -0,0 +1,215 @@ +/* + * 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. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "BLI_assert.h" +#include "BLI_rect.h" +#include <iterator> + +namespace blender::compositor { + +/* Forward declarations. */ +template<typename T> class BufferAreaIterator; + +/** + * A rectangle area of buffer elements. + */ +template<typename T> class BufferArea : rcti { + public: + using Iterator = BufferAreaIterator<T>; + using ConstIterator = BufferAreaIterator<const T>; + + private: + T *buffer_; + /* Number of elements in a buffer row. */ + int buffer_width_; + /* Buffer element stride. */ + int elem_stride_; + + public: + constexpr BufferArea() = default; + + /** + * Create a buffer area containing given rectangle area. + */ + constexpr BufferArea(T *buffer, int buffer_width, const rcti &area, int elem_stride = 1) + : rcti(area), buffer_(buffer), buffer_width_(buffer_width), elem_stride_(elem_stride) + { + } + + /** + * Create a buffer area containing whole buffer with no offsets. + */ + constexpr BufferArea(T *buffer, int buffer_width, int buffer_height, int elem_stride = 1) + : buffer_(buffer), buffer_width_(buffer_width), elem_stride_(elem_stride) + { + BLI_rcti_init(this, 0, buffer_width, 0, buffer_height); + } + + constexpr friend bool operator==(const BufferArea &a, const BufferArea &b) + { + return a.buffer_ == b.buffer_ && BLI_rcti_compare(&a, &b) && a.elem_stride_ == b.elem_stride_; + } + + constexpr const rcti &get_rect() const + { + return *this; + } + + /** + * Number of elements in a row. + */ + constexpr int width() const + { + return BLI_rcti_size_x(this); + } + + /** + * Number of elements in a column. + */ + constexpr int height() const + { + return BLI_rcti_size_y(this); + } + + constexpr Iterator begin() + { + return begin_iterator<Iterator>(); + } + + constexpr Iterator end() + { + return end_iterator<Iterator>(); + } + + constexpr ConstIterator begin() const + { + return begin_iterator<ConstIterator>(); + } + + constexpr ConstIterator end() const + { + return end_iterator<ConstIterator>(); + } + + private: + template<typename TIterator> constexpr TIterator begin_iterator() const + { + T *end_ptr = get_end_ptr(); + if (elem_stride_ == 0) { + /* Iterate a single element. */ + return TIterator(buffer_, end_ptr, 1, 1, 1); + } + + T *begin_ptr = buffer_ + (intptr_t)this->ymin * buffer_width_ * elem_stride_ + + (intptr_t)this->xmin * elem_stride_; + return TIterator(begin_ptr, end_ptr, buffer_width_, BLI_rcti_size_x(this), elem_stride_); + } + + template<typename TIterator> constexpr TIterator end_iterator() const + { + T *end_ptr = get_end_ptr(); + if (elem_stride_ == 0) { + /* Iterate a single element. */ + return TIterator(end_ptr, end_ptr, 1, 1, 1); + } + + return TIterator(end_ptr, end_ptr, buffer_width_, BLI_rcti_size_x(this), elem_stride_); + } + + T *get_end_ptr() const + { + if (elem_stride_ == 0) { + return buffer_ + 1; + } + return buffer_ + (intptr_t)(this->ymax - 1) * buffer_width_ * elem_stride_ + + (intptr_t)this->xmax * elem_stride_; + } +}; + +template<typename T> class BufferAreaIterator { + public: + using iterator_category = std::input_iterator_tag; + using value_type = T *; + using pointer = T *const *; + using reference = T *const &; + using difference_type = std::ptrdiff_t; + + private: + int elem_stride_; + int row_stride_; + /* Stride between a row end and the next row start. */ + int rows_gap_; + T *current_; + const T *row_end_; + const T *end_; + + public: + constexpr BufferAreaIterator() = default; + + constexpr BufferAreaIterator( + T *current, const T *end, int buffer_width, int area_width, int elem_stride = 1) + : elem_stride_(elem_stride), + row_stride_(buffer_width * elem_stride), + rows_gap_(row_stride_ - area_width * elem_stride), + current_(current), + row_end_(current + area_width * elem_stride), + end_(end) + { + } + + constexpr BufferAreaIterator &operator++() + { + current_ += elem_stride_; + BLI_assert(current_ <= row_end_); + if (current_ == row_end_) { + BLI_assert(current_ <= end_); + if (current_ == end_) { + return *this; + } + current_ += rows_gap_; + row_end_ += row_stride_; + } + return *this; + } + + constexpr BufferAreaIterator operator++(int) const + { + BufferAreaIterator copied_iterator = *this; + ++copied_iterator; + return copied_iterator; + } + + constexpr friend bool operator!=(const BufferAreaIterator &a, const BufferAreaIterator &b) + { + return a.current_ != b.current_; + } + + constexpr friend bool operator==(const BufferAreaIterator &a, const BufferAreaIterator &b) + { + return a.current_ == b.current_; + } + + constexpr T *operator*() const + { + return current_; + } +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_BufferRange.h b/source/blender/compositor/intern/COM_BufferRange.h new file mode 100644 index 00000000000..ffdf1f2f1e5 --- /dev/null +++ b/source/blender/compositor/intern/COM_BufferRange.h @@ -0,0 +1,171 @@ +/* + * 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. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "BLI_assert.h" +#include "BLI_rect.h" + +#include <iterator> + +namespace blender::compositor { + +/* Forward declarations. */ +template<typename T> class BufferRangeIterator; + +/** + * A range of buffer elements. + */ +template<typename T> class BufferRange { + public: + using Iterator = BufferRangeIterator<T>; + using ConstIterator = BufferRangeIterator<const T>; + + private: + T *start_; + /* Number of elements in the range. */ + int64_t size_; + /* Buffer element stride. */ + int elem_stride_; + + public: + constexpr BufferRange() = default; + + /** + * Create a buffer range of elements from a given element index. + */ + constexpr BufferRange(T *buffer, int64_t start_elem_index, int64_t size, int elem_stride = 1) + : start_(buffer + start_elem_index * elem_stride), size_(size), elem_stride_(elem_stride) + { + } + + constexpr friend bool operator==(const BufferRange &a, const BufferRange &b) + { + return a.start_ == b.start_ && a.size_ == b.size_ && a.elem_stride_ == b.elem_stride_; + } + + /** + * Access an element in the range. Index is relative to range start. + */ + constexpr T *operator[](int64_t index) const + { + BLI_assert(index >= 0); + BLI_assert(index < this->size()); + return start_ + index * elem_stride_; + } + + /** + * Get the number of elements in the range. + */ + constexpr int64_t size() const + { + return size_; + } + + constexpr Iterator begin() + { + return begin_iterator<Iterator>(); + } + + constexpr Iterator end() + { + return end_iterator<Iterator>(); + } + + constexpr ConstIterator begin() const + { + return begin_iterator<ConstIterator>(); + } + + constexpr ConstIterator end() const + { + return end_iterator<ConstIterator>(); + } + + private: + template<typename TIterator> constexpr TIterator begin_iterator() const + { + if (elem_stride_ == 0) { + /* Iterate a single element. */ + return TIterator(start_, 1); + } + + return TIterator(start_, elem_stride_); + } + + template<typename TIterator> constexpr TIterator end_iterator() const + { + if (elem_stride_ == 0) { + /* Iterate a single element. */ + return TIterator(start_ + 1, 1); + } + + return TIterator(start_ + size_ * elem_stride_, elem_stride_); + } +}; + +template<typename T> class BufferRangeIterator { + public: + using iterator_category = std::input_iterator_tag; + using value_type = T *; + using pointer = T *const *; + using reference = T *const &; + using difference_type = std::ptrdiff_t; + + private: + T *current_; + int elem_stride_; + + public: + constexpr BufferRangeIterator() = default; + + constexpr BufferRangeIterator(T *current, int elem_stride = 1) + : current_(current), elem_stride_(elem_stride) + { + } + + constexpr BufferRangeIterator &operator++() + { + current_ += elem_stride_; + return *this; + } + + constexpr BufferRangeIterator operator++(int) const + { + BufferRangeIterator copied_iterator = *this; + ++copied_iterator; + return copied_iterator; + } + + constexpr friend bool operator!=(const BufferRangeIterator &a, const BufferRangeIterator &b) + { + return a.current_ != b.current_; + } + + constexpr friend bool operator==(const BufferRangeIterator &a, const BufferRangeIterator &b) + { + return a.current_ == b.current_; + } + + constexpr T *operator*() const + { + return current_; + } +}; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_BuffersIterator.h b/source/blender/compositor/intern/COM_BuffersIterator.h new file mode 100644 index 00000000000..bfe0b7a3d45 --- /dev/null +++ b/source/blender/compositor/intern/COM_BuffersIterator.h @@ -0,0 +1,195 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2021, Blender Foundation. + */ + +#pragma once + +#include "BLI_rect.h" +#include "BLI_vector.hh" + +namespace blender::compositor { + +/** + * Builds an iterator for simultaneously iterating an area of elements in an output buffer and any + * number of input buffers. It's not a standard C++ iterator and it does not support neither + * deference, equality or postfix increment operators. + */ +template<typename T> class BuffersIteratorBuilder { + public: + class Iterator { + int x_start_; + int x_end_; + const T *out_end_; + int out_elem_stride_; + /* Stride between an output row end and the next row start. */ + int out_rows_gap_; + + struct In { + int elem_stride; + int rows_gap; + const T *in; + }; + Vector<In, 6> ins_; + + friend class BuffersIteratorBuilder; + + public: + int x; + int y; + /** Current output element. */ + T *out; + + public: + /** + * Get current element from an input. + */ + const T *in(int input_index) const + { + BLI_assert(input_index < ins_.size()); + return ins_[input_index].in; + } + + int get_num_inputs() const + { + return ins_.size(); + } + + /** + * Has the end of the area been reached. + */ + bool is_end() const + { + return out >= out_end_; + } + + /** + * Go to the next element in the area. + */ + void next() + { + out += out_elem_stride_; + for (In &in : ins_) { + in.in += in.elem_stride; + } + x++; + if (x == x_end_) { + x = x_start_; + y++; + out += out_rows_gap_; + for (In &in : ins_) { + in.in += in.rows_gap; + } + } + } + + Iterator &operator++() + { + this->next(); + return *this; + } + }; + + private: + Iterator iterator_; + rcti area_; + bool is_built_; + + public: + /** + * Create a buffers iterator builder to iterate given output buffer area. + * \param output: Output buffer. + * \param buffer_area: Whole output buffer area (may have offset position). + * \param iterated_area: Area to be iterated in all buffers. + * \param elem_stride: Output buffer element stride. + */ + BuffersIteratorBuilder(T *output, + const rcti &buffer_area, + const rcti &iterated_area, + int elem_stride = 1) + : area_(iterated_area), is_built_(false) + { + BLI_assert(BLI_rcti_inside_rcti(&buffer_area, &iterated_area)); + iterator_.x = iterated_area.xmin; + iterator_.y = iterated_area.ymin; + iterator_.x_start_ = iterated_area.xmin; + iterator_.x_end_ = iterated_area.xmax; + + iterator_.out_elem_stride_ = elem_stride; + const int buffer_width = BLI_rcti_size_x(&buffer_area); + intptr_t out_row_stride = buffer_width * elem_stride; + iterator_.out_rows_gap_ = out_row_stride - BLI_rcti_size_x(&iterated_area) * elem_stride; + const int out_start_x = iterated_area.xmin - buffer_area.xmin; + const int out_start_y = iterated_area.ymin - buffer_area.ymin; + iterator_.out = output + (intptr_t)out_start_y * out_row_stride + + (intptr_t)out_start_x * elem_stride; + const T *out_row_end_ = iterator_.out + + (intptr_t)BLI_rcti_size_x(&iterated_area) * elem_stride; + iterator_.out_end_ = out_row_end_ + + (intptr_t)out_row_stride * (BLI_rcti_size_y(&iterated_area) - 1); + } + + /** + * Create a buffers iterator builder to iterate given output buffer with no offsets. + */ + BuffersIteratorBuilder(T *output, int buffer_width, int buffer_height, int elem_stride = 1) + : BuffersIteratorBuilder(output, + {0, buffer_width, 0, buffer_height}, + {0, buffer_width, 0, buffer_height}, + elem_stride) + { + } + + /** + * Add an input buffer to be iterated. It must contain iterated area. + */ + void add_input(const T *input, const rcti &buffer_area, int elem_stride = 1) + { + BLI_assert(!is_built_); + BLI_assert(BLI_rcti_inside_rcti(&buffer_area, &area_)); + typename Iterator::In in; + in.elem_stride = elem_stride; + const int buffer_width = BLI_rcti_size_x(&buffer_area); + in.rows_gap = buffer_width * elem_stride - BLI_rcti_size_x(&area_) * elem_stride; + const int in_start_x = area_.xmin - buffer_area.xmin; + const int in_start_y = area_.ymin - buffer_area.ymin; + in.in = input + in_start_y * buffer_width * elem_stride + in_start_x * elem_stride; + iterator_.ins_.append(std::move(in)); + } + + /** + * Add an input buffer to be iterated with no offsets. It must contain iterated area. + */ + void add_input(const T *input, int buffer_width, int elem_stride = 1) + { + rcti buffer_area; + BLI_rcti_init(&buffer_area, 0, buffer_width, 0, area_.ymax); + add_input(input, buffer_area, elem_stride); + } + + /** + * Build the iterator. + */ + BuffersIteratorBuilder::Iterator build() + { + is_built_ = true; + return iterator_; + } +}; + +template<typename T> using BuffersIterator = typename BuffersIteratorBuilder<T>::Iterator; + +} // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_ConstantFolder.cc b/source/blender/compositor/intern/COM_ConstantFolder.cc index f20324de342..5b48ff8fc08 100644 --- a/source/blender/compositor/intern/COM_ConstantFolder.cc +++ b/source/blender/compositor/intern/COM_ConstantFolder.cc @@ -53,12 +53,12 @@ static bool is_constant_foldable(NodeOperation *operation) return false; } -static Vector<NodeOperation *> find_constant_foldable_operations(Span<NodeOperation *> operations) +static Set<NodeOperation *> find_constant_foldable_operations(Span<NodeOperation *> operations) { - Vector<NodeOperation *> foldable_ops; + Set<NodeOperation *> foldable_ops; for (NodeOperation *op : operations) { if (is_constant_foldable(op)) { - foldable_ops.append(op); + foldable_ops.add(op); } } return foldable_ops; @@ -94,6 +94,7 @@ ConstantOperation *ConstantFolder::fold_operation(NodeOperation *operation) const DataType data_type = operation->getOutputSocket()->getDataType(); MemoryBuffer fold_buf(data_type, first_elem_area_); Vector<MemoryBuffer *> input_bufs = get_constant_input_buffers(operation); + operation->init_data(); operation->render(&fold_buf, {first_elem_area_}, input_bufs); MemoryBuffer *constant_buf = create_constant_buffer(data_type); @@ -132,7 +133,7 @@ Vector<MemoryBuffer *> ConstantFolder::get_constant_input_buffers(NodeOperation /** Returns constant operations resulted from folded operations. */ Vector<ConstantOperation *> ConstantFolder::try_fold_operations(Span<NodeOperation *> operations) { - Vector<NodeOperation *> foldable_ops = find_constant_foldable_operations(operations); + Set<NodeOperation *> foldable_ops = find_constant_foldable_operations(operations); if (foldable_ops.size() == 0) { return Vector<ConstantOperation *>(); } diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc index 18973bb5a00..1983eb190e2 100644 --- a/source/blender/compositor/intern/COM_Converter.cc +++ b/source/blender/compositor/intern/COM_Converter.cc @@ -518,7 +518,7 @@ void COM_convert_resolution(NodeOperationBuilder &builder, NodeOperation *first = nullptr; ScaleOperation *scaleOperation = nullptr; if (doScale) { - scaleOperation = new ScaleOperation(fromSocket->getDataType()); + scaleOperation = new ScaleRelativeOperation(fromSocket->getDataType()); scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None); scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None); first = scaleOperation; diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc index abef4517b3e..5443974cbb0 100644 --- a/source/blender/compositor/intern/COM_Debug.cc +++ b/source/blender/compositor/intern/COM_Debug.cc @@ -31,6 +31,8 @@ extern "C" { #include "BKE_appdir.h" #include "BKE_node.h" #include "DNA_node_types.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" } #include "COM_ExecutionSystem.h" @@ -50,7 +52,7 @@ std::string DebugInfo::m_current_node_name; std::string DebugInfo::m_current_op_name; DebugInfo::GroupStateMap DebugInfo::m_group_states; -static std::string operation_class_name(NodeOperation *op) +static std::string operation_class_name(const NodeOperation *op) { std::string full_name = typeid(*op).name(); /* Remove name-spaces. */ @@ -452,4 +454,46 @@ void DebugInfo::graphviz(const ExecutionSystem *system, StringRefNull name) } } +static std::string get_operations_export_dir() +{ + return std::string(BKE_tempdir_session()) + "COM_operations" + SEP_STR; +} + +void DebugInfo::export_operation(const NodeOperation *op, MemoryBuffer *render) +{ + ImBuf *ibuf = IMB_allocFromBuffer(nullptr, + render->getBuffer(), + render->getWidth(), + render->getHeight(), + render->get_num_channels()); + + const std::string file_name = operation_class_name(op) + "_" + std::to_string(op->get_id()) + + ".png"; + const std::string path = get_operations_export_dir() + file_name; + BLI_make_existing_file(path.c_str()); + IMB_saveiff(ibuf, path.c_str(), ibuf->flags); + IMB_freeImBuf(ibuf); +} + +void DebugInfo::delete_operation_exports() +{ + const std::string dir = get_operations_export_dir(); + if (BLI_exists(dir.c_str())) { + struct direntry *file_list; + int num_files = BLI_filelist_dir_contents(dir.c_str(), &file_list); + for (int i = 0; i < num_files; i++) { + direntry *file = &file_list[i]; + const eFileAttributes file_attrs = BLI_file_attributes(file->path); + if (file_attrs & FILE_ATTR_ANY_LINK) { + continue; + } + + if (BLI_is_file(file->path) && BLI_path_extension_check(file->path, ".png")) { + BLI_delete(file->path, false, false); + } + } + BLI_filelist_free(file_list, num_files); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h index 53461e13f48..23d99c7e529 100644 --- a/source/blender/compositor/intern/COM_Debug.h +++ b/source/blender/compositor/intern/COM_Debug.h @@ -30,6 +30,9 @@ namespace blender::compositor { static constexpr bool COM_EXPORT_GRAPHVIZ = false; static constexpr bool COM_GRAPHVIZ_SHOW_NODE_NAME = false; +/* Saves operations results to image files. */ +static constexpr bool COM_EXPORT_OPERATION_BUFFERS = false; + class Node; class ExecutionSystem; class ExecutionGroup; @@ -75,6 +78,9 @@ class DebugInfo { m_group_states[execution_group] = EG_WAIT; } } + if (COM_EXPORT_OPERATION_BUFFERS) { + delete_operation_exports(); + } }; static void node_added(const Node *node) @@ -118,6 +124,14 @@ class DebugInfo { } }; + static void operation_rendered(const NodeOperation *op, MemoryBuffer *render) + { + /* Don't export constant operations as there are too many and it's rarely useful. */ + if (COM_EXPORT_OPERATION_BUFFERS && render && !render->is_a_single_elem()) { + export_operation(op, render); + } + } + static void graphviz(const ExecutionSystem *system, StringRefNull name = ""); protected: @@ -133,6 +147,9 @@ class DebugInfo { const char *name, const char *color, const char *style, char *str, int maxlen); static int graphviz_legend(char *str, int maxlen, bool has_execution_groups); static bool graphviz_system(const ExecutionSystem *system, char *str, int maxlen); + + static void export_operation(const NodeOperation *op, MemoryBuffer *render); + static void delete_operation_exports(); }; } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_Enums.cc b/source/blender/compositor/intern/COM_Enums.cc index d218de92544..2f20d2652ba 100644 --- a/source/blender/compositor/intern/COM_Enums.cc +++ b/source/blender/compositor/intern/COM_Enums.cc @@ -17,9 +17,28 @@ */ #include "COM_Enums.h" +#include "BLI_rect.h" namespace blender::compositor { +void expand_area_for_sampler(rcti &area, PixelSampler sampler) +{ + switch (sampler) { + case PixelSampler::Nearest: + break; + case PixelSampler::Bilinear: + area.xmax += 1; + area.ymax += 1; + break; + case PixelSampler::Bicubic: + area.xmin -= 1; + area.xmax += 2; + area.ymin -= 1; + area.ymax += 2; + break; + } +} + std::ostream &operator<<(std::ostream &os, const eCompositorPriority &priority) { switch (priority) { diff --git a/source/blender/compositor/intern/COM_Enums.h b/source/blender/compositor/intern/COM_Enums.h index 519e7df940e..7e5a1b73132 100644 --- a/source/blender/compositor/intern/COM_Enums.h +++ b/source/blender/compositor/intern/COM_Enums.h @@ -22,6 +22,8 @@ #include <ostream> +struct rcti; + namespace blender::compositor { /** @@ -85,6 +87,13 @@ enum class eWorkPackageType { CustomFunction = 1 }; +enum class PixelSampler { + Nearest = 0, + Bilinear = 1, + Bicubic = 2, +}; +void expand_area_for_sampler(rcti &area, PixelSampler sampler); + std::ostream &operator<<(std::ostream &os, const eCompositorPriority &priority); std::ostream &operator<<(std::ostream &os, const eWorkPackageState &execution_state); diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cc b/source/blender/compositor/intern/COM_ExecutionSystem.cc index 60caf22be1b..a449d86deb9 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.cc +++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc @@ -82,6 +82,7 @@ ExecutionSystem::ExecutionSystem(RenderData *rd, BLI_assert_msg(0, "Non implemented execution model"); break; } + num_work_threads_ = WorkScheduler::get_num_cpu_threads(); } ExecutionSystem::~ExecutionSystem() @@ -112,6 +113,9 @@ void ExecutionSystem::set_operations(const Vector<NodeOperation *> &operations, void ExecutionSystem::execute() { DebugInfo::execute_started(this); + for (NodeOperation *op : m_operations) { + op->init_data(); + } execution_model_->execute(*this); } @@ -127,7 +131,7 @@ void ExecutionSystem::execute_work(const rcti &work_rect, /* Split work vertically to maximize continuous memory. */ const int work_height = BLI_rcti_size_y(&work_rect); - const int num_sub_works = MIN2(WorkScheduler::get_num_cpu_threads(), work_height); + const int num_sub_works = MIN2(num_work_threads_, work_height); const int split_height = num_sub_works == 0 ? 0 : work_height / num_sub_works; int remaining_height = work_height - split_height * num_sub_works; diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h index 38c3432a8ec..bce96db52c7 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.h +++ b/source/blender/compositor/intern/COM_ExecutionSystem.h @@ -31,6 +31,7 @@ class ExecutionGroup; #include "DNA_node_types.h" #include "BLI_vector.hh" +#include "atomic_ops.h" namespace blender::compositor { @@ -150,6 +151,11 @@ class ExecutionSystem { */ ExecutionModel *execution_model_; + /** + * Number of cpu threads available for work execution. + */ + int num_work_threads_; + ThreadMutex work_mutex_; ThreadCondition work_finished_cond_; @@ -201,6 +207,27 @@ class ExecutionSystem { void execute_work(const rcti &work_rect, std::function<void(const rcti &split_rect)> work_func); + /** + * Multi-threaded execution of given work function passing work_rect splits as argument. + * Once finished, caller thread will call reduce_func for each thread result. + */ + template<typename TResult> + void execute_work(const rcti &work_rect, + std::function<TResult(const rcti &split_rect)> work_func, + TResult &join, + std::function<void(TResult &join, const TResult &chunk)> reduce_func) + { + Array<TResult> chunks(num_work_threads_); + int num_started = 0; + execute_work(work_rect, [&](const rcti &split_rect) { + const int current = atomic_fetch_and_add_int32(&num_started, 1); + chunks[current] = work_func(split_rect); + }); + for (const int i : IndexRange(num_started)) { + reduce_func(join, chunks[i]); + } + } + bool is_breaked() const; private: diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc index 3b0a9172871..bd3a481d691 100644 --- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc @@ -20,6 +20,7 @@ #include "COM_Debug.h" #include "COM_ExecutionGroup.h" #include "COM_ReadBufferOperation.h" +#include "COM_ViewerOperation.h" #include "COM_WorkScheduler.h" #include "BLT_translation.h" @@ -100,8 +101,13 @@ void FullFrameExecutionModel::render_operation(NodeOperation *op) const bool has_outputs = op->getNumberOfOutputSockets() > 0; MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op) : nullptr; - Span<rcti> areas = active_buffers_.get_areas_to_render(op); - op->render(op_buf, areas, input_bufs); + if (op->getWidth() > 0 && op->getHeight() > 0) { + Span<rcti> areas = active_buffers_.get_areas_to_render(op); + op->render(op_buf, areas, input_bufs); + DebugInfo::operation_rendered(op, op_buf); + } + /* Even if operation has no resolution set the empty buffer. It will be clipped with a + * TranslateOperation from convert resolutions if linked to an operation with resolution. */ active_buffers_.set_rendered_buffer(op, std::unique_ptr<MemoryBuffer>(op_buf)); operation_finished(op); @@ -117,10 +123,16 @@ void FullFrameExecutionModel::render_operations() WorkScheduler::start(this->context_); for (eCompositorPriority priority : priorities_) { for (NodeOperation *op : operations_) { - if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) { + const bool has_size = op->getWidth() > 0 && op->getHeight() > 0; + const bool is_priority_output = op->isOutputOperation(is_rendering) && + op->getRenderPriority() == priority; + if (is_priority_output && has_size) { render_output_dependencies(op); render_operation(op); } + else if (is_priority_output && !has_size && op->isActiveViewerOutput()) { + static_cast<ViewerOperation *>(op)->clear_display_buffer(); + } } } WorkScheduler::stop(); diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc index c7bddddd0e6..6b954072a9a 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.cc +++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc @@ -129,6 +129,20 @@ void MemoryBuffer::clear() memset(m_buffer, 0, buffer_len() * m_num_channels * sizeof(float)); } +BuffersIterator<float> MemoryBuffer::iterate_with(Span<MemoryBuffer *> inputs) +{ + return iterate_with(inputs, m_rect); +} + +BuffersIterator<float> MemoryBuffer::iterate_with(Span<MemoryBuffer *> inputs, const rcti &area) +{ + BuffersIteratorBuilder<float> builder(m_buffer, m_rect, area, elem_stride); + for (MemoryBuffer *input : inputs) { + builder.add_input(input->getBuffer(), input->get_rect(), input->elem_stride); + } + return builder.build(); +} + /** * Converts a single elem buffer to a full size buffer (allocates memory for all * elements in resolution). diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index 4ad0872b0b7..ae12c444dc1 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -18,6 +18,9 @@ #pragma once +#include "COM_BufferArea.h" +#include "COM_BufferRange.h" +#include "COM_BuffersIterator.h" #include "COM_ExecutionGroup.h" #include "COM_MemoryProxy.h" @@ -186,6 +189,25 @@ class MemoryBuffer { return m_buffer + get_coords_offset(x, y); } + void read_elem(int x, int y, float *out) const + { + memcpy(out, get_elem(x, y), m_num_channels * sizeof(float)); + } + + void read_elem_sampled(float x, float y, PixelSampler sampler, float *out) const + { + switch (sampler) { + case PixelSampler::Nearest: + this->read_elem(x, y, out); + break; + case PixelSampler::Bilinear: + case PixelSampler::Bicubic: + /* No bicubic. Current implementation produces fuzzy results. */ + this->readBilinear(out, x, y); + break; + } + } + /** * Get channel value at given coordinates. */ @@ -239,6 +261,32 @@ class MemoryBuffer { } /** + * Get all buffer elements as a range with no offsets. + */ + BufferRange<float> as_range() + { + return BufferRange<float>(m_buffer, 0, buffer_len(), elem_stride); + } + + BufferRange<const float> as_range() const + { + return BufferRange<const float>(m_buffer, 0, buffer_len(), elem_stride); + } + + BufferArea<float> get_buffer_area(const rcti &area) + { + return BufferArea<float>(m_buffer, getWidth(), area, elem_stride); + } + + BufferArea<const float> get_buffer_area(const rcti &area) const + { + return BufferArea<const float>(m_buffer, getWidth(), area, elem_stride); + } + + BuffersIterator<float> iterate_with(Span<MemoryBuffer *> inputs); + BuffersIterator<float> iterate_with(Span<MemoryBuffer *> inputs, const rcti &area); + + /** * \brief get the data of this MemoryBuffer * \note buffer should already be available in memory */ @@ -301,7 +349,7 @@ class MemoryBuffer { inline void wrap_pixel(float &x, float &y, MemoryBufferExtend extend_x, - MemoryBufferExtend extend_y) + MemoryBufferExtend extend_y) const { const float w = (float)getWidth(); const float h = (float)getHeight(); @@ -398,7 +446,7 @@ class MemoryBuffer { float x, float y, MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, - MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) + MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) const { float u = x; float v = y; diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index 4e115cb3f2f..575e8446abe 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -100,6 +100,11 @@ void NodeOperation::setResolutionInputSocketIndex(unsigned int index) { this->m_resolutionInputSocketIndex = index; } + +void NodeOperation::init_data() +{ + /* Pass. */ +} void NodeOperation::initExecution() { /* pass */ diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index fb9ec1e7a83..934007d25ce 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -75,12 +75,6 @@ enum class ResizeMode { Stretch = NS_CR_STRETCH, }; -enum class PixelSampler { - Nearest = 0, - Bilinear = 1, - Bicubic = 2, -}; - class NodeOperationInput { private: NodeOperation *m_operation; @@ -424,6 +418,12 @@ class NodeOperation { exec_system_ = system; } + /** + * Initializes operation data needed after operations are linked and resolutions determined. For + * rendering heap memory data use initExecution(). + */ + virtual void init_data(); + virtual void initExecution(); /** diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.cc b/source/blender/compositor/nodes/COM_RenderLayersNode.cc index 253ca542c04..6744e98ecdb 100644 --- a/source/blender/compositor/nodes/COM_RenderLayersNode.cc +++ b/source/blender/compositor/nodes/COM_RenderLayersNode.cc @@ -143,7 +143,7 @@ void RenderLayersNode::missingSocketLink(NodeConverter &converter, NodeOutput *o break; } default: { - BLI_assert("!Unexpected data type"); + BLI_assert_msg(0, "Unexpected data type"); return; } } diff --git a/source/blender/compositor/nodes/COM_ScaleNode.cc b/source/blender/compositor/nodes/COM_ScaleNode.cc index 50d2902f375..819d2e72f30 100644 --- a/source/blender/compositor/nodes/COM_ScaleNode.cc +++ b/source/blender/compositor/nodes/COM_ScaleNode.cc @@ -43,7 +43,7 @@ void ScaleNode::convertToOperations(NodeConverter &converter, switch (bnode->custom1) { case CMP_SCALE_RELATIVE: { - ScaleOperation *operation = new ScaleOperation(); + ScaleRelativeOperation *operation = new ScaleRelativeOperation(); converter.addOperation(operation); converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); @@ -59,7 +59,7 @@ void ScaleNode::convertToOperations(NodeConverter &converter, scaleFactorOperation->setValue(context.getRenderPercentageAsFactor()); converter.addOperation(scaleFactorOperation); - ScaleOperation *operation = new ScaleOperation(); + ScaleRelativeOperation *operation = new ScaleRelativeOperation(); converter.addOperation(operation); converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc index fc72b48eca2..0262f653d1a 100644 --- a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc +++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc @@ -43,7 +43,7 @@ void Stabilize2dNode::convertToOperations(NodeConverter &converter, MovieClip *clip = (MovieClip *)editorNode->id; bool invert = (editorNode->custom2 & CMP_NODEFLAG_STABILIZE_INVERSE) != 0; - ScaleOperation *scaleOperation = new ScaleOperation(); + ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation(); scaleOperation->setSampler((PixelSampler)editorNode->custom1); RotateOperation *rotateOperation = new RotateOperation(); rotateOperation->setDoDegree2RadConversion(false); diff --git a/source/blender/compositor/nodes/COM_TransformNode.cc b/source/blender/compositor/nodes/COM_TransformNode.cc index cd12939ab43..e1deaf616a4 100644 --- a/source/blender/compositor/nodes/COM_TransformNode.cc +++ b/source/blender/compositor/nodes/COM_TransformNode.cc @@ -40,7 +40,7 @@ void TransformNode::convertToOperations(NodeConverter &converter, NodeInput *angleInput = this->getInputSocket(3); NodeInput *scaleInput = this->getInputSocket(4); - ScaleOperation *scaleOperation = new ScaleOperation(); + ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation(); converter.addOperation(scaleOperation); RotateOperation *rotateOperation = new RotateOperation(); diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.cc b/source/blender/compositor/operations/COM_BoxMaskOperation.cc index 9938d4a85ed..15bb19660dc 100644 --- a/source/blender/compositor/operations/COM_BoxMaskOperation.cc +++ b/source/blender/compositor/operations/COM_BoxMaskOperation.cc @@ -105,6 +105,64 @@ void BoxMaskOperation::executePixelSampled(float output[4], float x, float y, Pi } } +void BoxMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + MaskFunc mask_func; + switch (m_maskType) { + case CMP_NODE_MASKTYPE_ADD: + mask_func = [](const bool is_inside, const float *mask, const float *value) { + return is_inside ? MAX2(mask[0], value[0]) : mask[0]; + }; + break; + case CMP_NODE_MASKTYPE_SUBTRACT: + mask_func = [](const bool is_inside, const float *mask, const float *value) { + return is_inside ? CLAMPIS(mask[0] - value[0], 0, 1) : mask[0]; + }; + break; + case CMP_NODE_MASKTYPE_MULTIPLY: + mask_func = [](const bool is_inside, const float *mask, const float *value) { + return is_inside ? mask[0] * value[0] : 0; + }; + break; + case CMP_NODE_MASKTYPE_NOT: + mask_func = [](const bool is_inside, const float *mask, const float *value) { + if (is_inside) { + return mask[0] > 0.0f ? 0.0f : value[0]; + } + return mask[0]; + }; + break; + } + apply_mask(output, area, inputs, mask_func); +} + +void BoxMaskOperation::apply_mask(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs, + MaskFunc mask_func) +{ + const float op_w = this->getWidth(); + const float op_h = this->getHeight(); + const float half_w = this->m_data->width / 2.0f; + const float half_h = this->m_data->height / 2.0f; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float op_ry = it.y / op_h; + const float dy = (op_ry - this->m_data->y) / m_aspectRatio; + const float op_rx = it.x / op_w; + const float dx = op_rx - this->m_data->x; + const float rx = this->m_data->x + (m_cosine * dx + m_sine * dy); + const float ry = this->m_data->y + (-m_sine * dx + m_cosine * dy); + + const bool inside = (rx > this->m_data->x - half_w && rx < this->m_data->x + half_w && + ry > this->m_data->y - half_h && ry < this->m_data->y + half_h); + const float *mask = it.in(0); + const float *value = it.in(1); + *it.out = mask_func(inside, mask, value); + } +} + void BoxMaskOperation::deinitExecution() { this->m_inputMask = nullptr; diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.h b/source/blender/compositor/operations/COM_BoxMaskOperation.h index fdec7bdd8ca..4c48dde844a 100644 --- a/source/blender/compositor/operations/COM_BoxMaskOperation.h +++ b/source/blender/compositor/operations/COM_BoxMaskOperation.h @@ -18,12 +18,14 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class BoxMaskOperation : public NodeOperation { +class BoxMaskOperation : public MultiThreadedOperation { private: + using MaskFunc = std::function<float(bool is_inside, const float *mask, const float *value)>; + /** * Cached reference to the inputProgram */ @@ -64,6 +66,16 @@ class BoxMaskOperation : public NodeOperation { { this->m_maskType = maskType; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + + private: + void apply_mask(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs, + MaskFunc mask_func); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.cc b/source/blender/compositor/operations/COM_BrightnessOperation.cc index 92cab47318a..7878eca2bbd 100644 --- a/source/blender/compositor/operations/COM_BrightnessOperation.cc +++ b/source/blender/compositor/operations/COM_BrightnessOperation.cc @@ -28,6 +28,7 @@ BrightnessOperation::BrightnessOperation() this->addOutputSocket(DataType::Color); this->m_inputProgram = nullptr; this->m_use_premultiply = false; + flags.can_be_constant = true; } void BrightnessOperation::setUsePremultiply(bool use_premultiply) @@ -85,6 +86,50 @@ void BrightnessOperation::executePixelSampled(float output[4], } } +void BrightnessOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + float tmp_color[4]; + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *in_color = it.in(0); + const float brightness = *it.in(1) / 100.0f; + const float contrast = *it.in(2); + float delta = contrast / 200.0f; + /* + * The algorithm is by Werner D. Streidt + * (http://visca.com/ffactory/archives/5-99/msg00021.html) + * Extracted of OpenCV demhist.c + */ + float a, b; + if (contrast > 0) { + a = 1.0f - delta * 2.0f; + a = 1.0f / max_ff(a, FLT_EPSILON); + b = a * (brightness - delta); + } + else { + delta *= -1; + a = max_ff(1.0f - delta * 2.0f, 0.0f); + b = a * brightness + delta; + } + const float *color; + if (this->m_use_premultiply) { + premul_to_straight_v4_v4(tmp_color, in_color); + color = tmp_color; + } + else { + color = in_color; + } + it.out[0] = a * color[0] + b; + it.out[1] = a * color[1] + b; + it.out[2] = a * color[2] + b; + it.out[3] = color[3]; + if (this->m_use_premultiply) { + straight_to_premul_v4(it.out); + } + } +} + void BrightnessOperation::deinitExecution() { this->m_inputProgram = nullptr; diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.h b/source/blender/compositor/operations/COM_BrightnessOperation.h index 7c33e0b35ec..64b4fa0dbe2 100644 --- a/source/blender/compositor/operations/COM_BrightnessOperation.h +++ b/source/blender/compositor/operations/COM_BrightnessOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class BrightnessOperation : public NodeOperation { +class BrightnessOperation : public MultiThreadedOperation { private: /** * Cached reference to the inputProgram @@ -52,6 +52,10 @@ class BrightnessOperation : public NodeOperation { void deinitExecution() override; void setUsePremultiply(bool use_premultiply); + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc index a7ea49aed8d..7457ac9e227 100644 --- a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc +++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc @@ -19,6 +19,7 @@ #include "COM_CalculateMeanOperation.h" #include "BLI_math.h" #include "BLI_utildefines.h" +#include "COM_ExecutionSystem.h" #include "IMB_colormanagement.h" @@ -128,4 +129,93 @@ void CalculateMeanOperation::calculateMean(MemoryBuffer *tile) this->m_result = sum / pixels; } +void CalculateMeanOperation::setSetting(int setting) +{ + this->m_setting = setting; + switch (setting) { + case 1: { + setting_func_ = IMB_colormanagement_get_luminance; + break; + } + case 2: { + setting_func_ = [](const float *elem) { return elem[0]; }; + break; + } + case 3: { + setting_func_ = [](const float *elem) { return elem[1]; }; + break; + } + case 4: { + setting_func_ = [](const float *elem) { return elem[2]; }; + break; + } + case 5: { + setting_func_ = [](const float *elem) { + float yuv[3]; + rgb_to_yuv(elem[0], elem[1], elem[2], &yuv[0], &yuv[1], &yuv[2], BLI_YUV_ITU_BT709); + return yuv[0]; + }; + break; + } + } +} + +void CalculateMeanOperation::get_area_of_interest(int input_idx, + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + NodeOperation *operation = getInputOperation(input_idx); + r_input_area.xmin = 0; + r_input_area.ymin = 0; + r_input_area.xmax = operation->getWidth(); + r_input_area.ymax = operation->getHeight(); +} + +void CalculateMeanOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), + const rcti &UNUSED(area), + Span<MemoryBuffer *> inputs) +{ + if (!this->m_iscalculated) { + MemoryBuffer *input = inputs[0]; + m_result = calc_mean(input); + this->m_iscalculated = true; + } +} + +void CalculateMeanOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs)) +{ + output->fill(area, &m_result); +} + +float CalculateMeanOperation::calc_mean(const MemoryBuffer *input) +{ + PixelsSum total = {0}; + exec_system_->execute_work<PixelsSum>( + input->get_rect(), + [=](const rcti &split) { return calc_area_sum(input, split); }, + total, + [](PixelsSum &join, const PixelsSum &chunk) { + join.sum += chunk.sum; + join.num_pixels += chunk.num_pixels; + }); + return total.num_pixels == 0 ? 0.0f : total.sum / total.num_pixels; +} + +using PixelsSum = CalculateMeanOperation::PixelsSum; +PixelsSum CalculateMeanOperation::calc_area_sum(const MemoryBuffer *input, const rcti &area) +{ + PixelsSum result = {0}; + for (const float *elem : input->get_buffer_area(area)) { + if (elem[3] <= 0.0f) { + continue; + } + result.sum += setting_func_(elem); + result.num_pixels++; + } + return result; +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.h b/source/blender/compositor/operations/COM_CalculateMeanOperation.h index 8b3bf281c93..779ca79b38a 100644 --- a/source/blender/compositor/operations/COM_CalculateMeanOperation.h +++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.h @@ -18,8 +18,9 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_node_types.h" +#include <functional> namespace blender::compositor { @@ -27,7 +28,13 @@ namespace blender::compositor { * \brief base class of CalculateMean, implementing the simple CalculateMean * \ingroup operation */ -class CalculateMeanOperation : public NodeOperation { +class CalculateMeanOperation : public MultiThreadedOperation { + public: + struct PixelsSum { + float sum; + int num_pixels; + }; + protected: /** * \brief Cached reference to the reader @@ -37,6 +44,7 @@ class CalculateMeanOperation : public NodeOperation { bool m_iscalculated; float m_result; int m_setting; + std::function<float(const float *elem)> setting_func_; public: CalculateMeanOperation(); @@ -61,13 +69,24 @@ class CalculateMeanOperation : public NodeOperation { bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; - void setSetting(int setting) - { - this->m_setting = setting; - } + void setSetting(int setting); + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + + virtual void update_memory_buffer_started(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + + virtual void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; protected: void calculateMean(MemoryBuffer *tile); + float calc_mean(const MemoryBuffer *input); + + private: + PixelsSum calc_area_sum(const MemoryBuffer *input, const rcti &area); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc index ed554b9ac06..494b66cb888 100644 --- a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc +++ b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc @@ -19,6 +19,7 @@ #include "COM_CalculateStandardDeviationOperation.h" #include "BLI_math.h" #include "BLI_utildefines.h" +#include "COM_ExecutionSystem.h" #include "IMB_colormanagement.h" @@ -96,4 +97,50 @@ void *CalculateStandardDeviationOperation::initializeTileData(rcti *rect) return nullptr; } +void CalculateStandardDeviationOperation::update_memory_buffer_started( + MemoryBuffer *UNUSED(output), const rcti &UNUSED(area), Span<MemoryBuffer *> inputs) +{ + if (!this->m_iscalculated) { + const MemoryBuffer *input = inputs[0]; + const float mean = CalculateMeanOperation::calc_mean(input); + + PixelsSum total = {0}; + exec_system_->execute_work<PixelsSum>( + input->get_rect(), + [=](const rcti &split) { return calc_area_sum(input, split, mean); }, + total, + [](PixelsSum &join, const PixelsSum &chunk) { + join.sum += chunk.sum; + join.num_pixels += chunk.num_pixels; + }); + this->m_standardDeviation = total.num_pixels <= 1 ? + 0.0f : + sqrt(total.sum / (float)(total.num_pixels - 1)); + this->m_iscalculated = true; + } +} + +void CalculateStandardDeviationOperation::update_memory_buffer_partial( + MemoryBuffer *output, const rcti &area, Span<MemoryBuffer *> UNUSED(inputs)) +{ + output->fill(area, &m_standardDeviation); +} + +using PixelsSum = CalculateMeanOperation::PixelsSum; +PixelsSum CalculateStandardDeviationOperation::calc_area_sum(const MemoryBuffer *input, + const rcti &area, + const float mean) +{ + PixelsSum result = {0}; + for (const float *elem : input->get_buffer_area(area)) { + if (elem[3] <= 0.0f) { + continue; + } + const float value = setting_func_(elem); + result.sum += (value - mean) * (value - mean); + result.num_pixels++; + } + return result; +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h index bc4aca69546..20de4cf4701 100644 --- a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h +++ b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h @@ -40,6 +40,17 @@ class CalculateStandardDeviationOperation : public CalculateMeanOperation { void executePixel(float output[4], int x, int y, void *data) override; void *initializeTileData(rcti *rect) override; + + void update_memory_buffer_started(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + + private: + PixelsSum calc_area_sum(const MemoryBuffer *input, const rcti &area, float mean); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc index c4099a6d33d..55ae19ad194 100644 --- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc +++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc @@ -25,7 +25,7 @@ namespace blender::compositor { -// this part has been copied from the double edge mask +/* This part has been copied from the double edge mask. */ static void do_adjacentKeepBorders(unsigned int t, unsigned int rw, const unsigned int *limask, @@ -35,163 +35,163 @@ static void do_adjacentKeepBorders(unsigned int t, unsigned int *rsize) { int x; - unsigned int isz = 0; // inner edge size - unsigned int osz = 0; // outer edge size - unsigned int gsz = 0; // gradient fill area size + unsigned int isz = 0; /* Inner edge size. */ + unsigned int osz = 0; /* Outer edge size. */ + unsigned int gsz = 0; /* Gradient fill area size. */ /* Test the four corners */ - /* upper left corner */ + /* Upper left corner. */ x = t - rw + 1; - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if pixel underneath, or to the right, are empty in the inner mask, - // but filled in the outer mask + /* Test if pixel underneath, or to the right, are empty in the inner mask, + * but filled in the outer mask. */ if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + 1] && lomask[x + 1])) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } - /* upper right corner */ + /* Upper right corner. */ x = t; - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if pixel underneath, or to the left, are empty in the inner mask, - // but filled in the outer mask + /* Test if pixel underneath, or to the left, are empty in the inner mask, + * but filled in the outer mask. */ if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x - 1] && lomask[x - 1])) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } - /* lower left corner */ + /* Lower left corner. */ x = 0; - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if pixel above, or to the right, are empty in the inner mask, - // but filled in the outer mask + /* Test if pixel above, or to the right, are empty in the inner mask, + * but filled in the outer mask. */ if ((!limask[x + rw] && lomask[x + rw]) || (!limask[x + 1] && lomask[x + 1])) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } - /* lower right corner */ + /* Lower right corner. */ x = rw - 1; - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if pixel above, or to the left, are empty in the inner mask, - // but filled in the outer mask + /* Test if pixel above, or to the left, are empty in the inner mask, + * but filled in the outer mask. */ if ((!limask[x + rw] && lomask[x + rw]) || (!limask[x - 1] && lomask[x - 1])) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } /* Test the TOP row of pixels in buffer, except corners */ for (x = t - 1; x >= (t - rw) + 2; x--) { - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if pixel to the right, or to the left, are empty in the inner mask, - // but filled in the outer mask + /* Test if pixel to the right, or to the left, are empty in the inner mask, + * but filled in the outer mask. */ if ((!limask[x - 1] && lomask[x - 1]) || (!limask[x + 1] && lomask[x + 1])) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } } /* Test the BOTTOM row of pixels in buffer, except corners */ for (x = rw - 2; x; x--) { - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if pixel to the right, or to the left, are empty in the inner mask, - // but filled in the outer mask + /* Test if pixel to the right, or to the left, are empty in the inner mask, + * but filled in the outer mask. */ if ((!limask[x - 1] && lomask[x - 1]) || (!limask[x + 1] && lomask[x + 1])) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } } /* Test the LEFT edge of pixels in buffer, except corners */ for (x = t - (rw << 1) + 1; x >= rw; x -= rw) { - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if pixel underneath, or above, are empty in the inner mask, - // but filled in the outer mask + /* Test if pixel underneath, or above, are empty in the inner mask, + * but filled in the outer mask. */ if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + rw] && lomask[x + rw])) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } } /* Test the RIGHT edge of pixels in buffer, except corners */ for (x = t - rw; x > rw; x -= rw) { - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if pixel underneath, or above, are empty in the inner mask, - // but filled in the outer mask + /* Test if pixel underneath, or above, are empty in the inner mask, + * but filled in the outer mask. */ if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + rw] && lomask[x + rw])) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } } - rsize[0] = isz; // fill in our return sizes for edges + fill + rsize[0] = isz; /* Fill in our return sizes for edges + fill. */ rsize[1] = osz; rsize[2] = gsz; } @@ -205,214 +205,218 @@ static void do_adjacentBleedBorders(unsigned int t, unsigned int *rsize) { int x; - unsigned int isz = 0; // inner edge size - unsigned int osz = 0; // outer edge size - unsigned int gsz = 0; // gradient fill area size + unsigned int isz = 0; /* Inner edge size. */ + unsigned int osz = 0; /* Outer edge size. */ + unsigned int gsz = 0; /* Gradient fill area size. */ /* Test the four corners */ - /* upper left corner */ + /* Upper left corner. */ x = t - rw + 1; - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if pixel underneath, or to the right, are empty in the inner mask, - // but filled in the outer mask + /* Test if pixel underneath, or to the right, are empty in the inner mask, + * but filled in the outer mask. */ if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + 1] && lomask[x + 1])) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ if (!lomask[x - rw] || - !lomask[x + 1]) { // test if outer mask is empty underneath or to the right - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + !lomask[x + 1]) { /* Test if outer mask is empty underneath or to the right. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } else { - gsz++; // increment the gradient pixel count - lres[x] = 2; // flag pixel as gradient + gsz++; /* Increment the gradient pixel count. */ + lres[x] = 2; /* Flag pixel as gradient. */ } } - /* upper right corner */ + /* Upper right corner. */ x = t; - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if pixel underneath, or to the left, are empty in the inner mask, - // but filled in the outer mask + /* Test if pixel underneath, or to the left, are empty in the inner mask, + * but filled in the outer mask. */ if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x - 1] && lomask[x - 1])) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ if (!lomask[x - rw] || - !lomask[x - 1]) { // test if outer mask is empty underneath or to the left - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + !lomask[x - 1]) { /* Test if outer mask is empty underneath or to the left. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } else { - gsz++; // increment the gradient pixel count - lres[x] = 2; // flag pixel as gradient + gsz++; /* Increment the gradient pixel count. */ + lres[x] = 2; /* Flag pixel as gradient. */ } } - /* lower left corner */ + /* Lower left corner. */ x = 0; - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if pixel above, or to the right, are empty in the inner mask, - // but filled in the outer mask + /* Test if pixel above, or to the right, are empty in the inner mask, + * But filled in the outer mask. */ if ((!limask[x + rw] && lomask[x + rw]) || (!limask[x + 1] && lomask[x + 1])) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - if (!lomask[x + rw] || !lomask[x + 1]) { // test if outer mask is empty above or to the right - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + if (!lomask[x + rw] || + !lomask[x + 1]) { /* Test if outer mask is empty above or to the right. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } else { - gsz++; // increment the gradient pixel count - lres[x] = 2; // flag pixel as gradient + gsz++; /* Increment the gradient pixel count. */ + lres[x] = 2; /* Flag pixel as gradient. */ } } - /* lower right corner */ + /* Lower right corner. */ x = rw - 1; - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if pixel above, or to the left, are empty in the inner mask, - // but filled in the outer mask + /* Test if pixel above, or to the left, are empty in the inner mask, + * but filled in the outer mask. */ if ((!limask[x + rw] && lomask[x + rw]) || (!limask[x - 1] && lomask[x - 1])) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - if (!lomask[x + rw] || !lomask[x - 1]) { // test if outer mask is empty above or to the left - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + if (!lomask[x + rw] || + !lomask[x - 1]) { /* Test if outer mask is empty above or to the left. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } else { - gsz++; // increment the gradient pixel count - lres[x] = 2; // flag pixel as gradient + gsz++; /* Increment the gradient pixel count. */ + lres[x] = 2; /* Flag pixel as gradient. */ } } /* Test the TOP row of pixels in buffer, except corners */ for (x = t - 1; x >= (t - rw) + 2; x--) { - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if pixel to the left, or to the right, are empty in the inner mask, - // but filled in the outer mask + /* Test if pixel to the left, or to the right, are empty in the inner mask, + * but filled in the outer mask. */ if ((!limask[x - 1] && lomask[x - 1]) || (!limask[x + 1] && lomask[x + 1])) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ if (!lomask[x - 1] || - !lomask[x + 1]) { // test if outer mask is empty to the left or to the right - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + !lomask[x + 1]) { /* Test if outer mask is empty to the left or to the right. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } else { - gsz++; // increment the gradient pixel count - lres[x] = 2; // flag pixel as gradient + gsz++; /* Increment the gradient pixel count. */ + lres[x] = 2; /* Flag pixel as gradient. */ } } } /* Test the BOTTOM row of pixels in buffer, except corners */ for (x = rw - 2; x; x--) { - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if pixel to the left, or to the right, are empty in the inner mask, - // but filled in the outer mask + /* Test if pixel to the left, or to the right, are empty in the inner mask, + * but filled in the outer mask. */ if ((!limask[x - 1] && lomask[x - 1]) || (!limask[x + 1] && lomask[x + 1])) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ if (!lomask[x - 1] || - !lomask[x + 1]) { // test if outer mask is empty to the left or to the right - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + !lomask[x + 1]) { /* Test if outer mask is empty to the left or to the right. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } else { - gsz++; // increment the gradient pixel count - lres[x] = 2; // flag pixel as gradient + gsz++; /* Increment the gradient pixel count. */ + lres[x] = 2; /* Flag pixel as gradient. */ } } } /* Test the LEFT edge of pixels in buffer, except corners */ for (x = t - (rw << 1) + 1; x >= rw; x -= rw) { - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if pixel underneath, or above, are empty in the inner mask, - // but filled in the outer mask + /* Test if pixel underneath, or above, are empty in the inner mask, + * but filled in the outer mask. */ if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + rw] && lomask[x + rw])) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - if (!lomask[x - rw] || !lomask[x + rw]) { // test if outer mask is empty underneath or above - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + if (!lomask[x - rw] || + !lomask[x + rw]) { /* Test if outer mask is empty underneath or above. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } else { - gsz++; // increment the gradient pixel count - lres[x] = 2; // flag pixel as gradient + gsz++; /* Increment the gradient pixel count. */ + lres[x] = 2; /* Flag pixel as gradient. */ } } } /* Test the RIGHT edge of pixels in buffer, except corners */ for (x = t - rw; x > rw; x -= rw) { - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if pixel underneath, or above, are empty in the inner mask, - // but filled in the outer mask + /* Test if pixel underneath, or above, are empty in the inner mask, + * But filled in the outer mask. */ if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + rw] && lomask[x + rw])) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - if (!lomask[x - rw] || !lomask[x + rw]) { // test if outer mask is empty underneath or above - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + if (!lomask[x - rw] || + !lomask[x + rw]) { /* Test if outer mask is empty underneath or above. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } else { - gsz++; // increment the gradient pixel count - lres[x] = 2; // flag pixel as gradient + gsz++; /* Increment the gradient pixel count. */ + lres[x] = 2; /* Flag pixel as gradient. */ } } } - rsize[0] = isz; // fill in our return sizes for edges + fill + rsize[0] = isz; /* Fill in our return sizes for edges + fill. */ rsize[1] = osz; rsize[2] = gsz; } @@ -426,155 +430,155 @@ static void do_allKeepBorders(unsigned int t, unsigned int *rsize) { int x; - unsigned int isz = 0; // inner edge size - unsigned int osz = 0; // outer edge size - unsigned int gsz = 0; // gradient fill area size - /* Test the four corners */ - /* upper left corner */ + unsigned int isz = 0; /* Inner edge size. */ + unsigned int osz = 0; /* Outer edge size. */ + unsigned int gsz = 0; /* Gradient fill area size. */ + /* Test the four corners. */ + /* Upper left corner. */ x = t - rw + 1; - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if the inner mask is empty underneath or to the right + /* Test if the inner mask is empty underneath or to the right. */ if (!limask[x - rw] || !limask[x + 1]) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } - /* upper right corner */ + /* Upper right corner. */ x = t; - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if the inner mask is empty underneath or to the left + /* Test if the inner mask is empty underneath or to the left. */ if (!limask[x - rw] || !limask[x - 1]) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } - /* lower left corner */ + /* Lower left corner. */ x = 0; - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if inner mask is empty above or to the right + /* Test if inner mask is empty above or to the right. */ if (!limask[x + rw] || !limask[x + 1]) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } - /* lower right corner */ + /* Lower right corner. */ x = rw - 1; - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if inner mask is empty above or to the left + /* Test if inner mask is empty above or to the left. */ if (!limask[x + rw] || !limask[x - 1]) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } /* Test the TOP row of pixels in buffer, except corners */ for (x = t - 1; x >= (t - rw) + 2; x--) { - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if inner mask is empty to the left or to the right + /* Test if inner mask is empty to the left or to the right. */ if (!limask[x - 1] || !limask[x + 1]) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } } /* Test the BOTTOM row of pixels in buffer, except corners */ for (x = rw - 2; x; x--) { - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if inner mask is empty to the left or to the right + /* Test if inner mask is empty to the left or to the right. */ if (!limask[x - 1] || !limask[x + 1]) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } } /* Test the LEFT edge of pixels in buffer, except corners */ for (x = t - (rw << 1) + 1; x >= rw; x -= rw) { - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if inner mask is empty underneath or above + /* Test if inner mask is empty underneath or above. */ if (!limask[x - rw] || !limask[x + rw]) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } } /* Test the RIGHT edge of pixels in buffer, except corners */ for (x = t - rw; x > rw; x -= rw) { - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if inner mask is empty underneath or above + /* Test if inner mask is empty underneath or above. */ if (!limask[x - rw] || !limask[x + rw]) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } } - rsize[0] = isz; // fill in our return sizes for edges + fill + rsize[0] = isz; /* Fill in our return sizes for edges + fill. */ rsize[1] = osz; rsize[2] = gsz; } @@ -588,207 +592,210 @@ static void do_allBleedBorders(unsigned int t, unsigned int *rsize) { int x; - unsigned int isz = 0; // inner edge size - unsigned int osz = 0; // outer edge size - unsigned int gsz = 0; // gradient fill area size + unsigned int isz = 0; /* Inner edge size. */ + unsigned int osz = 0; /* Outer edge size. */ + unsigned int gsz = 0; /* Gradient fill area size. */ /* Test the four corners */ - /* upper left corner */ + /* Upper left corner. */ x = t - rw + 1; - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if the inner mask is empty underneath or to the right + /* Test if the inner mask is empty underneath or to the right. */ if (!limask[x - rw] || !limask[x + 1]) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ if (!lomask[x - rw] || - !lomask[x + 1]) { // test if outer mask is empty underneath or to the right - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + !lomask[x + 1]) { /* Test if outer mask is empty underneath or to the right. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } else { - gsz++; // increment the gradient pixel count - lres[x] = 2; // flag pixel as gradient + gsz++; /* Increment the gradient pixel count. */ + lres[x] = 2; /* Flag pixel as gradient. */ } } - /* upper right corner */ + /* Upper right corner. */ x = t; - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if the inner mask is empty underneath or to the left + /* Test if the inner mask is empty underneath or to the left. */ if (!limask[x - rw] || !limask[x - 1]) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - if (!lomask[x - rw] || !lomask[x - 1]) { // test if outer mask is empty above or to the left - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + if (!lomask[x - rw] || + !lomask[x - 1]) { /* Test if outer mask is empty above or to the left. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } else { - gsz++; // increment the gradient pixel count - lres[x] = 2; // flag pixel as gradient + gsz++; /* Increment the gradient pixel count. */ + lres[x] = 2; /* Flag pixel as gradient. */ } } - /* lower left corner */ + /* Lower left corner. */ x = 0; - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if inner mask is empty above or to the right + /* Test if inner mask is empty above or to the right. */ if (!limask[x + rw] || !limask[x + 1]) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ if (!lomask[x + rw] || - !lomask[x + 1]) { // test if outer mask is empty underneath or to the right - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + !lomask[x + 1]) { /* Test if outer mask is empty underneath or to the right. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } else { - gsz++; // increment the gradient pixel count - lres[x] = 2; // flag pixel as gradient + gsz++; /* Increment the gradient pixel count. */ + lres[x] = 2; /* Flag pixel as gradient. */ } } - /* lower right corner */ + /* Lower right corner. */ x = rw - 1; - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if inner mask is empty above or to the left + /* Test if inner mask is empty above or to the left. */ if (!limask[x + rw] || !limask[x - 1]) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ if (!lomask[x + rw] || - !lomask[x - 1]) { // test if outer mask is empty underneath or to the left - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + !lomask[x - 1]) { /* Test if outer mask is empty underneath or to the left. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } else { - gsz++; // increment the gradient pixel count - lres[x] = 2; // flag pixel as gradient + gsz++; /* Increment the gradient pixel count. */ + lres[x] = 2; /* Flag pixel as gradient. */ } } /* Test the TOP row of pixels in buffer, except corners */ for (x = t - 1; x >= (t - rw) + 2; x--) { - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if inner mask is empty to the left or to the right + /* Test if inner mask is empty to the left or to the right. */ if (!limask[x - 1] || !limask[x + 1]) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ if (!lomask[x - 1] || - !lomask[x + 1]) { // test if outer mask is empty to the left or to the right - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + !lomask[x + 1]) { /* Test if outer mask is empty to the left or to the right. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } else { - gsz++; // increment the gradient pixel count - lres[x] = 2; // flag pixel as gradient + gsz++; /* Increment the gradient pixel count. */ + lres[x] = 2; /* Flag pixel as gradient. */ } } } /* Test the BOTTOM row of pixels in buffer, except corners */ for (x = rw - 2; x; x--) { - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if inner mask is empty to the left or to the right + /* Test if inner mask is empty to the left or to the right. */ if (!limask[x - 1] || !limask[x + 1]) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ if (!lomask[x - 1] || - !lomask[x + 1]) { // test if outer mask is empty to the left or to the right - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + !lomask[x + 1]) { /* Test if outer mask is empty to the left or to the right. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } else { - gsz++; // increment the gradient pixel count - lres[x] = 2; // flag pixel as gradient + gsz++; /* Increment the gradient pixel count. */ + lres[x] = 2; /* Flag pixel as gradient. */ } } } /* Test the LEFT edge of pixels in buffer, except corners */ for (x = t - (rw << 1) + 1; x >= rw; x -= rw) { - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if inner mask is empty underneath or above + /* Test if inner mask is empty underneath or above. */ if (!limask[x - rw] || !limask[x + rw]) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - if (!lomask[x - rw] || !lomask[x + rw]) { // test if outer mask is empty underneath or above - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + if (!lomask[x - rw] || + !lomask[x + rw]) { /* Test if outer mask is empty underneath or above. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } else { - gsz++; // increment the gradient pixel count - lres[x] = 2; // flag pixel as gradient + gsz++; /* Increment the gradient pixel count. */ + lres[x] = 2; /* Flag pixel as gradient. */ } } } /* Test the RIGHT edge of pixels in buffer, except corners */ for (x = t - rw; x > rw; x -= rw) { - // test if inner mask is filled + /* Test if inner mask is filled. */ if (limask[x]) { - // test if inner mask is empty underneath or above + /* Test if inner mask is empty underneath or above. */ if (!limask[x - rw] || !limask[x + rw]) { - isz++; // increment inner edge size - lres[x] = 4; // flag pixel as inner edge + isz++; /* Increment inner edge size. */ + lres[x] = 4; /* Flag pixel as inner edge. */ } else { - res[x] = 1.0f; // pixel is just part of inner mask, and it's not an edge + res[x] = 1.0f; /* Pixel is just part of inner mask, and it's not an edge. */ } } - else if (lomask[x]) { // inner mask was empty, test if outer mask is filled - if (!lomask[x - rw] || !lomask[x + rw]) { // test if outer mask is empty underneath or above - osz++; // increment outer edge size - lres[x] = 3; // flag pixel as outer edge + else if (lomask[x]) { /* Inner mask was empty, test if outer mask is filled. */ + if (!lomask[x - rw] || + !lomask[x + rw]) { /* Test if outer mask is empty underneath or above. */ + osz++; /* Increment outer edge size. */ + lres[x] = 3; /* Flag pixel as outer edge. */ } else { - gsz++; // increment the gradient pixel count - lres[x] = 2; // flag pixel as gradient + gsz++; /* Increment the gradient pixel count. */ + lres[x] = 2; /* Flag pixel as gradient. */ } } } - rsize[0] = isz; // fill in our return sizes for edges + fill + rsize[0] = isz; /* Fill in our return sizes for edges + fill. */ rsize[1] = osz; rsize[2] = gsz; } @@ -804,13 +811,14 @@ static void do_allEdgeDetection(unsigned int t, unsigned int in_osz, unsigned int in_gsz) { - int x; // x = pixel loop counter - int a; // a = pixel loop counter - int dx; // dx = delta x - int pix_prevRow; // pix_prevRow = pixel one row behind the one we are testing in a loop - int pix_nextRow; // pix_nextRow = pixel one row in front of the one we are testing in a loop - int pix_prevCol; // pix_prevCol = pixel one column behind the one we are testing in a loop - int pix_nextCol; // pix_nextCol = pixel one column in front of the one we are testing in a loop + int x; /* Pixel loop counter. */ + int a; /* Pixel loop counter. */ + int dx; /* Delta x. */ + int pix_prevRow; /* Pixel one row behind the one we are testing in a loop. */ + int pix_nextRow; /* Pixel one row in front of the one we are testing in a loop. */ + int pix_prevCol; /* Pixel one column behind the one we are testing in a loop. */ + int pix_nextCol; /* Pixel one column in front of the one we are testing in a loop. */ + /* Test all rows between the FIRST and LAST rows, excluding left and right edges */ for (x = (t - rw) + 1, dx = x - (rw - 2); dx > rw; x -= rw, dx -= rw) { a = x - 2; @@ -819,8 +827,8 @@ static void do_allEdgeDetection(unsigned int t, pix_prevCol = a + 1; pix_nextCol = a - 1; while (a > dx - 2) { - if (!limask[a]) { // if the inner mask is empty - if (lomask[a]) { // if the outer mask is full + if (!limask[a]) { /* If the inner mask is empty. */ + if (lomask[a]) { /* If the outer mask is full. */ /* * Next we test all 4 directions around the current pixel: next/prev/up/down * The test ensures that the outer mask is empty and that the inner mask @@ -831,23 +839,23 @@ static void do_allEdgeDetection(unsigned int t, (!lomask[pix_prevCol] && !limask[pix_prevCol]) || (!lomask[pix_nextRow] && !limask[pix_nextRow]) || (!lomask[pix_prevRow] && !limask[pix_prevRow])) { - in_osz++; // increment the outer boundary pixel count - lres[a] = 3; // flag pixel as part of outer edge + in_osz++; /* Increment the outer boundary pixel count. */ + lres[a] = 3; /* Flag pixel as part of outer edge. */ } - else { // it's not a boundary pixel, but it is a gradient pixel - in_gsz++; // increment the gradient pixel count - lres[a] = 2; // flag pixel as gradient + else { /* It's not a boundary pixel, but it is a gradient pixel. */ + in_gsz++; /* Increment the gradient pixel count. */ + lres[a] = 2; /* Flag pixel as gradient. */ } } } else { if (!limask[pix_nextCol] || !limask[pix_prevCol] || !limask[pix_nextRow] || !limask[pix_prevRow]) { - in_isz++; // increment the inner boundary pixel count - lres[a] = 4; // flag pixel as part of inner edge + in_isz++; /* Increment the inner boundary pixel count. */ + lres[a] = 4; /* Flag pixel as part of inner edge. */ } else { - res[a] = 1.0f; // pixel is part of inner mask, but not at an edge + res[a] = 1.0f; /* Pixel is part of inner mask, but not at an edge. */ } } a--; @@ -858,7 +866,7 @@ static void do_allEdgeDetection(unsigned int t, } } - rsize[0] = in_isz; // fill in our return sizes for edges + fill + rsize[0] = in_isz; /* Fill in our return sizes for edges + fill. */ rsize[1] = in_osz; rsize[2] = in_gsz; } @@ -874,13 +882,13 @@ static void do_adjacentEdgeDetection(unsigned int t, unsigned int in_osz, unsigned int in_gsz) { - int x; // x = pixel loop counter - int a; // a = pixel loop counter - int dx; // dx = delta x - int pix_prevRow; // pix_prevRow = pixel one row behind the one we are testing in a loop - int pix_nextRow; // pix_nextRow = pixel one row in front of the one we are testing in a loop - int pix_prevCol; // pix_prevCol = pixel one column behind the one we are testing in a loop - int pix_nextCol; // pix_nextCol = pixel one column in front of the one we are testing in a loop + int x; /* Pixel loop counter. */ + int a; /* Pixel loop counter. */ + int dx; /* Delta x. */ + int pix_prevRow; /* Pixel one row behind the one we are testing in a loop. */ + int pix_nextRow; /* Pixel one row in front of the one we are testing in a loop. */ + int pix_prevCol; /* Pixel one column behind the one we are testing in a loop. */ + int pix_nextCol; /* Pixel one column in front of the one we are testing in a loop. */ /* Test all rows between the FIRST and LAST rows, excluding left and right edges */ for (x = (t - rw) + 1, dx = x - (rw - 2); dx > rw; x -= rw, dx -= rw) { a = x - 2; @@ -889,8 +897,8 @@ static void do_adjacentEdgeDetection(unsigned int t, pix_prevCol = a + 1; pix_nextCol = a - 1; while (a > dx - 2) { - if (!limask[a]) { // if the inner mask is empty - if (lomask[a]) { // if the outer mask is full + if (!limask[a]) { /* If the inner mask is empty. */ + if (lomask[a]) { /* If the outer mask is full. */ /* * Next we test all 4 directions around the current pixel: next/prev/up/down * The test ensures that the outer mask is empty and that the inner mask @@ -901,12 +909,12 @@ static void do_adjacentEdgeDetection(unsigned int t, (!lomask[pix_prevCol] && !limask[pix_prevCol]) || (!lomask[pix_nextRow] && !limask[pix_nextRow]) || (!lomask[pix_prevRow] && !limask[pix_prevRow])) { - in_osz++; // increment the outer boundary pixel count - lres[a] = 3; // flag pixel as part of outer edge + in_osz++; /* Increment the outer boundary pixel count. */ + lres[a] = 3; /* Flag pixel as part of outer edge. */ } - else { // it's not a boundary pixel, but it is a gradient pixel - in_gsz++; // increment the gradient pixel count - lres[a] = 2; // flag pixel as gradient + else { /* It's not a boundary pixel, but it is a gradient pixel. */ + in_gsz++; /* Increment the gradient pixel count. */ + lres[a] = 2; /* Flag pixel as gradient. */ } } } @@ -915,22 +923,22 @@ static void do_adjacentEdgeDetection(unsigned int t, (!limask[pix_prevCol] && lomask[pix_prevCol]) || (!limask[pix_nextRow] && lomask[pix_nextRow]) || (!limask[pix_prevRow] && lomask[pix_prevRow])) { - in_isz++; // increment the inner boundary pixel count - lres[a] = 4; // flag pixel as part of inner edge + in_isz++; /* Increment the inner boundary pixel count. */ + lres[a] = 4; /* Flag pixel as part of inner edge. */ } else { - res[a] = 1.0f; // pixel is part of inner mask, but not at an edge + res[a] = 1.0f; /* Pixel is part of inner mask, but not at an edge. */ } } a--; - pix_prevRow--; // advance all four "surrounding" pixel pointers + pix_prevRow--; /* Advance all four "surrounding" pixel pointers. */ pix_nextRow--; pix_prevCol--; pix_nextCol--; } } - rsize[0] = in_isz; // fill in our return sizes for edges + fill + rsize[0] = in_isz; /* Fill in our return sizes for edges + fill. */ rsize[1] = in_osz; rsize[2] = in_gsz; } @@ -945,12 +953,12 @@ static void do_createEdgeLocationBuffer(unsigned int t, unsigned int isz, unsigned int gsz) { - int x; // x = pixel loop counter - int a; // a = temporary pixel index buffer loop counter - unsigned int ud; // ud = unscaled edge distance - unsigned int dmin; // dmin = minimum edge distance + int x; /* Pixel loop counter. */ + int a; /* Temporary pixel index buffer loop counter. */ + unsigned int ud; /* Unscaled edge distance. */ + unsigned int dmin; /* Minimum edge distance. */ - unsigned int rsl; // long used for finding fast 1.0/sqrt + unsigned int rsl; /* Long used for finding fast `1.0/sqrt`. */ unsigned int gradientFillOffset; /* For looping inner edge pixel indexes, represents current position from offset. */ @@ -980,11 +988,11 @@ static void do_createEdgeLocationBuffer(unsigned int t, * * Example: 9 by 9 pixel block * - * . = pixel non-white in both outer and inner mask - * o = pixel white in outer, but not inner mask, adjacent to "." pixel - * g = pixel white in outer, but not inner mask, not adjacent to "." pixel - * i = pixel white in inner mask, adjacent to "g" or "." pixel - * F = pixel white in inner mask, only adjacent to other pixels white in the inner mask + * `.` = Pixel non-white in both outer and inner mask. + * `o` = Pixel white in outer, but not inner mask, adjacent to "." pixel. + * `g` = Pixel white in outer, but not inner mask, not adjacent to "." pixel. + * `i` = Pixel white in inner mask, adjacent to "g" or "." pixel. + * `F` = Pixel white in inner mask, only adjacent to other pixels white in the inner mask. * * * ......... <----- pixel #80 @@ -1025,36 +1033,37 @@ static void do_createEdgeLocationBuffer(unsigned int t, */ /* clang-format on */ - gradientFillOffset = 0; // since there are likely "more" of these, put it first. :) - *innerEdgeOffset = gradientFillOffset + gsz; // set start of inner edge indexes - *outerEdgeOffset = (*innerEdgeOffset) + isz; // set start of outer edge indexes - /* set the accumulators to correct positions */ // set up some accumulator variables for loops - gradientAccum = gradientFillOffset; // each accumulator variable starts at its respective - innerAccum = *innerEdgeOffset; // section's offset so when we start filling, each - outerAccum = *outerEdgeOffset; // section fills up its allocated space in gbuf - // uses dmin=row, rsl=col + gradientFillOffset = 0; /* Since there are likely "more" of these, put it first. :). */ + *innerEdgeOffset = gradientFillOffset + gsz; /* Set start of inner edge indexes. */ + *outerEdgeOffset = (*innerEdgeOffset) + isz; /* Set start of outer edge indexes. */ + /* Set the accumulators to correct positions */ /* Set up some accumulator variables for loops. + */ + gradientAccum = gradientFillOffset; /* Each accumulator variable starts at its respective. */ + innerAccum = *innerEdgeOffset; /* Section's offset so when we start filling, each. */ + outerAccum = *outerEdgeOffset; /* Section fills up its allocated space in gbuf. */ + /* Uses `dmin=row`, `rsl=col`. */ for (x = 0, dmin = 0; x < t; x += rw, dmin++) { for (rsl = 0; rsl < rw; rsl++) { a = x + rsl; - if (lres[a] == 2) { // it is a gradient pixel flagged by 2 - ud = gradientAccum << 1; // double the index to reach correct unsigned short location - gbuf[ud] = dmin; // insert pixel's row into gradient pixel location buffer - gbuf[ud + 1] = rsl; // insert pixel's column into gradient pixel location buffer - gradientAccum++; // increment gradient index buffer pointer + if (lres[a] == 2) { /* It is a gradient pixel flagged by 2. */ + ud = gradientAccum << 1; /* Double the index to reach correct unsigned short location. */ + gbuf[ud] = dmin; /* Insert pixel's row into gradient pixel location buffer. */ + gbuf[ud + 1] = rsl; /* Insert pixel's column into gradient pixel location buffer. */ + gradientAccum++; /* Increment gradient index buffer pointer. */ } - else if (lres[a] == 3) { // it is an outer edge pixel flagged by 3 - ud = outerAccum << 1; // double the index to reach correct unsigned short location - gbuf[ud] = dmin; // insert pixel's row into outer edge pixel location buffer - gbuf[ud + 1] = rsl; // insert pixel's column into outer edge pixel location buffer - outerAccum++; // increment outer edge index buffer pointer - res[a] = 0.0f; // set output pixel intensity now since it won't change later + else if (lres[a] == 3) { /* It is an outer edge pixel flagged by 3. */ + ud = outerAccum << 1; /* Double the index to reach correct unsigned short location. */ + gbuf[ud] = dmin; /* Insert pixel's row into outer edge pixel location buffer. */ + gbuf[ud + 1] = rsl; /* Insert pixel's column into outer edge pixel location buffer. */ + outerAccum++; /* Increment outer edge index buffer pointer. */ + res[a] = 0.0f; /* Set output pixel intensity now since it won't change later. */ } - else if (lres[a] == 4) { // it is an inner edge pixel flagged by 4 - ud = innerAccum << 1; // double int index to reach correct unsigned short location - gbuf[ud] = dmin; // insert pixel's row into inner edge pixel location buffer - gbuf[ud + 1] = rsl; // insert pixel's column into inner edge pixel location buffer - innerAccum++; // increment inner edge index buffer pointer - res[a] = 1.0f; // set output pixel intensity now since it won't change later + else if (lres[a] == 4) { /* It is an inner edge pixel flagged by 4. */ + ud = innerAccum << 1; /* Double int index to reach correct unsigned short location. */ + gbuf[ud] = dmin; /* Insert pixel's row into inner edge pixel location buffer. */ + gbuf[ud + 1] = rsl; /* Insert pixel's column into inner edge pixel location buffer. */ + innerAccum++; /* Increment inner edge index buffer pointer. */ + res[a] = 1.0f; /* Set output pixel intensity now since it won't change later. */ } } } @@ -1069,21 +1078,21 @@ static void do_fillGradientBuffer(unsigned int rw, unsigned int innerEdgeOffset, unsigned int outerEdgeOffset) { - int x; // x = pixel loop counter - int a; // a = temporary pixel index buffer loop counter - int fsz; // size of the frame - unsigned int rsl; // long used for finding fast 1.0/sqrt - float rsf; // float used for finding fast 1.0/sqrt - const float rsopf = 1.5f; // constant float used for finding fast 1.0/sqrt + int x; /* Pixel loop counter. */ + int a; /* Temporary pixel index buffer loop counter. */ + int fsz; /* Size of the frame. */ + unsigned int rsl; /* Long used for finding fast `1.0/sqrt`. */ + float rsf; /* Float used for finding fast `1.0/sqrt`. */ + const float rsopf = 1.5f; /* Constant float used for finding fast `1.0/sqrt`. */ unsigned int gradientFillOffset; unsigned int t; - unsigned int ud; // ud = unscaled edge distance - unsigned int dmin; // dmin = minimum edge distance - float odist; // odist = current outer edge distance - float idist; // idist = current inner edge distance - int dx; // dx = X-delta (used for distance proportion calculation) - int dy; // dy = Y-delta (used for distance proportion calculation) + unsigned int ud; /* Unscaled edge distance. */ + unsigned int dmin; /* Minimum edge distance. */ + float odist; /* Current outer edge distance. */ + float idist; /* Current inner edge distance. */ + int dx; /* X-delta (used for distance proportion calculation) */ + int dy; /* Y-delta (used for distance proportion calculation) */ /* * The general algorithm used to color each gradient pixel is: @@ -1099,9 +1108,9 @@ static void do_fillGradientBuffer(unsigned int rw, * outside edge. * * In an image where: - * . = blank (black) pixels, not covered by inner mask or outer mask - * + = desired gradient pixels, covered only by outer mask - * * = white full mask pixels, covered by at least inner mask + * `.` = Blank (black) pixels, not covered by inner mask or outer mask. + * `+` = Desired gradient pixels, covered only by outer mask. + * `*` = White full mask pixels, covered by at least inner mask. * * ............................... * ...............+++++++++++..... @@ -1146,92 +1155,95 @@ static void do_fillGradientBuffer(unsigned int rw, for (x = gsz - 1; x >= 0; x--) { gradientFillOffset = x << 1; - t = gbuf[gradientFillOffset]; // calculate column of pixel indexed by gbuf[x] - fsz = gbuf[gradientFillOffset + 1]; // calculate row of pixel indexed by gbuf[x] - dmin = 0xffffffff; // reset min distance to edge pixel + t = gbuf[gradientFillOffset]; /* Calculate column of pixel indexed by `gbuf[x]`. */ + fsz = gbuf[gradientFillOffset + 1]; /* Calculate row of pixel indexed by `gbuf[x]`. */ + dmin = 0xffffffff; /* Reset min distance to edge pixel. */ for (a = outerEdgeOffset + osz - 1; a >= outerEdgeOffset; - a--) { // loop through all outer edge buffer pixels + a--) { /* Loop through all outer edge buffer pixels. */ ud = a << 1; - dy = t - gbuf[ud]; // set dx to gradient pixel column - outer edge pixel row - dx = fsz - gbuf[ud + 1]; // set dy to gradient pixel row - outer edge pixel column - ud = dx * dx + dy * dy; // compute sum of squares - if (ud < dmin) { // if our new sum of squares is less than the current minimum - dmin = ud; // set a new minimum equal to the new lower value + dy = t - gbuf[ud]; /* Set dx to gradient pixel column - outer edge pixel row. */ + dx = fsz - gbuf[ud + 1]; /* Set dy to gradient pixel row - outer edge pixel column. */ + ud = dx * dx + dy * dy; /* Compute sum of squares. */ + if (ud < dmin) { /* If our new sum of squares is less than the current minimum. */ + dmin = ud; /* Set a new minimum equal to the new lower value. */ } } - odist = (float)(dmin); // cast outer min to a float - rsf = odist * 0.5f; // - rsl = *(unsigned int *)&odist; // use some peculiar properties of the way bits are stored - rsl = 0x5f3759df - (rsl >> 1); // in floats vs. unsigned ints to compute an approximate - odist = *(float *)&rsl; // reciprocal square root + odist = (float)(dmin); /* Cast outer min to a float. */ + rsf = odist * 0.5f; + rsl = *(unsigned int *)&odist; /* Use some peculiar properties of the way bits are stored. */ + rsl = 0x5f3759df - (rsl >> 1); /* In floats vs. unsigned ints to compute an approximate. */ + odist = *(float *)&rsl; /* Reciprocal square root. */ odist = odist * (rsopf - (rsf * odist * - odist)); // -- ** this line can be iterated for more accuracy ** -- - dmin = 0xffffffff; // reset min distance to edge pixel + odist)); /* -- This line can be iterated for more accuracy. -- */ + dmin = 0xffffffff; /* Reset min distance to edge pixel. */ for (a = innerEdgeOffset + isz - 1; a >= innerEdgeOffset; - a--) { // loop through all inside edge pixels + a--) { /* Loop through all inside edge pixels. */ ud = a << 1; - dy = t - gbuf[ud]; // compute delta in Y from gradient pixel to inside edge pixel - dx = fsz - gbuf[ud + 1]; // compute delta in X from gradient pixel to inside edge pixel - ud = dx * dx + dy * dy; // compute sum of squares - if (ud < dmin) { // if our new sum of squares is less than the current minimum we've found - dmin = ud; // set a new minimum equal to the new lower value + dy = t - gbuf[ud]; /* Compute delta in Y from gradient pixel to inside edge pixel. */ + dx = fsz - gbuf[ud + 1]; /* Compute delta in X from gradient pixel to inside edge pixel. */ + ud = dx * dx + dy * dy; /* Compute sum of squares. */ + if (ud < + dmin) { /* If our new sum of squares is less than the current minimum we've found. */ + dmin = ud; /* Set a new minimum equal to the new lower value. */ } } - idist = (float)(dmin); // cast inner min to a float - rsf = idist * 0.5f; // - rsl = *(unsigned int *)&idist; // - rsl = 0x5f3759df - (rsl >> 1); // see notes above - idist = *(float *)&rsl; // - idist = idist * (rsopf - (rsf * idist * idist)); // - /* - * Note once again that since we are using reciprocals of distance values our + + /* Cast inner min to a float. */ + idist = (float)(dmin); + rsf = idist * 0.5f; + rsl = *(unsigned int *)&idist; + + /* See notes above. */ + rsl = 0x5f3759df - (rsl >> 1); + idist = *(float *)&rsl; + idist = idist * (rsopf - (rsf * idist * idist)); + + /* NOTE: once again that since we are using reciprocals of distance values our * proportion is already the correct intensity, and does not need to be - * subtracted from 1.0 like it would have if we used real distances. - */ + * subtracted from 1.0 like it would have if we used real distances. */ - /* - * Here we reconstruct the pixel's memory location in the CompBuf by - * Pixel Index = Pixel Column + ( Pixel Row * Row Width ) - */ + /* Here we reconstruct the pixel's memory location in the CompBuf by + * `Pixel Index = Pixel Column + ( Pixel Row * Row Width )`. */ res[gbuf[gradientFillOffset + 1] + (gbuf[gradientFillOffset] * rw)] = - (idist / (idist + odist)); // set intensity + (idist / (idist + odist)); /* Set intensity. */ } } -// end of copy +/* End of copy. */ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float *res) { - unsigned int *lres; // lres = unsigned int pointer to output pixel buffer (for bit operations) - unsigned int *limask; // limask = unsigned int pointer to inner mask (for bit operations) - unsigned int *lomask; // lomask = unsigned int pointer to outer mask (for bit operations) - - int rw; // rw = pixel row width - int t; // t = total number of pixels in buffer - 1 (used for loop starts) - int fsz; // size of the frame - - unsigned int isz = 0; // size (in pixels) of inside edge pixel index buffer - unsigned int osz = 0; // size (in pixels) of outside edge pixel index buffer - unsigned int gsz = 0; // size (in pixels) of gradient pixel index buffer - unsigned int rsize[3]; // size storage to pass to helper functions + unsigned int *lres; /* Pointer to output pixel buffer (for bit operations). */ + unsigned int *limask; /* Pointer to inner mask (for bit operations). */ + unsigned int *lomask; /* Pointer to outer mask (for bit operations). */ + + int rw; /* Pixel row width. */ + int t; /* Total number of pixels in buffer - 1 (used for loop starts). */ + int fsz; /* Size of the frame. */ + + unsigned int isz = 0; /* Size (in pixels) of inside edge pixel index buffer. */ + unsigned int osz = 0; /* Size (in pixels) of outside edge pixel index buffer. */ + unsigned int gsz = 0; /* Size (in pixels) of gradient pixel index buffer. */ + unsigned int rsize[3]; /* Size storage to pass to helper functions. */ unsigned int innerEdgeOffset = - 0; // offset into final buffer where inner edge pixel indexes start + 0; /* Offset into final buffer where inner edge pixel indexes start. */ unsigned int outerEdgeOffset = - 0; // offset into final buffer where outer edge pixel indexes start + 0; /* Offset into final buffer where outer edge pixel indexes start. */ - unsigned short *gbuf; // gradient/inner/outer pixel location index buffer + unsigned short *gbuf; /* Gradient/inner/outer pixel location index buffer. */ - if (true) { // if both input sockets have some data coming in... + if (true) { /* If both input sockets have some data coming in... */ - rw = this->getWidth(); // width of a row of pixels - t = (rw * this->getHeight()) - 1; // determine size of the frame + rw = this->getWidth(); /* Width of a row of pixels. */ + t = (rw * this->getHeight()) - 1; /* Determine size of the frame. */ memset(res, 0, - sizeof(float) * (t + 1)); // clear output buffer (not all pixels will be written later) + sizeof(float) * + (t + 1)); /* Clear output buffer (not all pixels will be written later). */ - lres = (unsigned int *)res; // unsigned int pointer to output buffer (for bit level ops) - limask = (unsigned int *)imask; // unsigned int pointer to input mask (for bit level ops) - lomask = (unsigned int *)omask; // unsigned int pointer to output mask (for bit level ops) + lres = (unsigned int *)res; /* Pointer to output buffer (for bit level ops).. */ + limask = (unsigned int *)imask; /* Pointer to input mask (for bit level ops).. */ + lomask = (unsigned int *)omask; /* Pointer to output mask (for bit level ops).. */ /* * The whole buffer is broken up into 4 parts. The four CORNERS, the FIRST and LAST rows, the @@ -1258,52 +1270,52 @@ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float * * Each version has slightly different criteria for detecting an edge pixel. */ - if (this->m_adjacentOnly) { // if "adjacent only" inner edge mode is turned on - if (this->m_keepInside) { // if "keep inside" buffer edge mode is turned on + if (this->m_adjacentOnly) { /* If "adjacent only" inner edge mode is turned on. */ + if (this->m_keepInside) { /* If "keep inside" buffer edge mode is turned on. */ do_adjacentKeepBorders(t, rw, limask, lomask, lres, res, rsize); } - else { // "bleed out" buffer edge mode is turned on + else { /* "bleed out" buffer edge mode is turned on. */ do_adjacentBleedBorders(t, rw, limask, lomask, lres, res, rsize); } - // set up inner edge, outer edge, and gradient buffer sizes after border pass + /* Set up inner edge, outer edge, and gradient buffer sizes after border pass. */ isz = rsize[0]; osz = rsize[1]; gsz = rsize[2]; - // detect edges in all non-border pixels in the buffer + /* Detect edges in all non-border pixels in the buffer. */ do_adjacentEdgeDetection(t, rw, limask, lomask, lres, res, rsize, isz, osz, gsz); } - else { // "all" inner edge mode is turned on - if (this->m_keepInside) { // if "keep inside" buffer edge mode is turned on + else { /* "all" inner edge mode is turned on. */ + if (this->m_keepInside) { /* If "keep inside" buffer edge mode is turned on. */ do_allKeepBorders(t, rw, limask, lomask, lres, res, rsize); } - else { // "bleed out" buffer edge mode is turned on + else { /* "bleed out" buffer edge mode is turned on. */ do_allBleedBorders(t, rw, limask, lomask, lres, res, rsize); } - // set up inner edge, outer edge, and gradient buffer sizes after border pass + /* Set up inner edge, outer edge, and gradient buffer sizes after border pass. */ isz = rsize[0]; osz = rsize[1]; gsz = rsize[2]; - // detect edges in all non-border pixels in the buffer + /* Detect edges in all non-border pixels in the buffer. */ do_allEdgeDetection(t, rw, limask, lomask, lres, res, rsize, isz, osz, gsz); } - // set edge and gradient buffer sizes once again... - // the sizes in rsize[] may have been modified - // by the do_*EdgeDetection() function. + /* Set edge and gradient buffer sizes once again... + * the sizes in rsize[] may have been modified + * by the `do_*EdgeDetection()` function. */ isz = rsize[0]; osz = rsize[1]; gsz = rsize[2]; - // calculate size of pixel index buffer needed + /* Calculate size of pixel index buffer needed. */ fsz = gsz + isz + osz; - // allocate edge/gradient pixel index buffer + /* Allocate edge/gradient pixel index buffer. */ gbuf = (unsigned short *)MEM_callocN(sizeof(unsigned short) * fsz * 2, "DEM"); do_createEdgeLocationBuffer( t, rw, lres, res, gbuf, &innerEdgeOffset, &outerEdgeOffset, isz, gsz); do_fillGradientBuffer(rw, res, gbuf, isz, osz, gsz, innerEdgeOffset, outerEdgeOffset); - // free the gradient index buffer + /* Free the gradient index buffer. */ MEM_freeN(gbuf); } } @@ -1318,6 +1330,7 @@ DoubleEdgeMaskOperation::DoubleEdgeMaskOperation() this->m_adjacentOnly = false; this->m_keepInside = false; this->flags.complex = true; + is_output_rendered_ = false; } bool DoubleEdgeMaskOperation::determineDependingAreaOfInterest(rcti * /*input*/, @@ -1382,4 +1395,43 @@ void DoubleEdgeMaskOperation::deinitExecution() } } +void DoubleEdgeMaskOperation::get_area_of_interest(int UNUSED(input_idx), + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + r_input_area.xmax = this->getWidth(); + r_input_area.xmin = 0; + r_input_area.ymax = this->getHeight(); + r_input_area.ymin = 0; +} + +void DoubleEdgeMaskOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &UNUSED(area), + Span<MemoryBuffer *> inputs) +{ + if (!is_output_rendered_) { + /* Ensure full buffers to work with no strides. */ + MemoryBuffer *input_inner_mask = inputs[0]; + MemoryBuffer *inner_mask = input_inner_mask->is_a_single_elem() ? input_inner_mask->inflate() : + input_inner_mask; + MemoryBuffer *input_outer_mask = inputs[1]; + MemoryBuffer *outer_mask = input_outer_mask->is_a_single_elem() ? input_outer_mask->inflate() : + input_outer_mask; + + BLI_assert(output->getWidth() == this->getWidth()); + BLI_assert(output->getHeight() == this->getHeight()); + /* TODO(manzanilla): Once tiled implementation is removed, use execution system to run + * multi-threaded where possible. */ + doDoubleEdgeMask(inner_mask->getBuffer(), outer_mask->getBuffer(), output->getBuffer()); + is_output_rendered_ = true; + + if (inner_mask != input_inner_mask) { + delete inner_mask; + } + if (outer_mask != input_outer_mask) { + delete outer_mask; + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h index e956e8edc3e..45a80bbbbf0 100644 --- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h +++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { @@ -31,8 +31,12 @@ class DoubleEdgeMaskOperation : public NodeOperation { SocketReader *m_inputInnerMask; bool m_adjacentOnly; bool m_keepInside; + + /* TODO(manzanilla): To be removed with tiled implementation. */ float *m_cachedInstance; + bool is_output_rendered_; + public: DoubleEdgeMaskOperation(); @@ -66,6 +70,12 @@ class DoubleEdgeMaskOperation : public NodeOperation { { this->m_keepInside = keepInside; } + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + + void update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc index 5a4503fecec..eb1fd98a590 100644 --- a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc +++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc @@ -20,6 +20,8 @@ #include "BLI_math.h" #include "DNA_node_types.h" +#include <functional> + namespace blender::compositor { EllipseMaskOperation::EllipseMaskOperation() @@ -114,6 +116,77 @@ void EllipseMaskOperation::executePixelSampled(float output[4], } } +void EllipseMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + MaskFunc mask_func; + switch (m_maskType) { + case CMP_NODE_MASKTYPE_ADD: + mask_func = [](const bool is_inside, const float *mask, const float *value) { + return is_inside ? MAX2(mask[0], value[0]) : mask[0]; + }; + break; + case CMP_NODE_MASKTYPE_SUBTRACT: + mask_func = [](const bool is_inside, const float *mask, const float *value) { + return is_inside ? CLAMPIS(mask[0] - value[0], 0, 1) : mask[0]; + }; + break; + case CMP_NODE_MASKTYPE_MULTIPLY: + mask_func = [](const bool is_inside, const float *mask, const float *value) { + return is_inside ? mask[0] * value[0] : 0; + }; + break; + case CMP_NODE_MASKTYPE_NOT: + mask_func = [](const bool is_inside, const float *mask, const float *value) { + if (is_inside) { + return mask[0] > 0.0f ? 0.0f : value[0]; + } + return mask[0]; + }; + break; + } + apply_mask(output, area, inputs, mask_func); +} + +void EllipseMaskOperation::apply_mask(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs, + MaskFunc mask_func) +{ + const MemoryBuffer *input_mask = inputs[0]; + const MemoryBuffer *input_value = inputs[1]; + const float op_w = this->getWidth(); + const float op_h = this->getHeight(); + const float half_w = this->m_data->width / 2.0f; + const float half_h = this->m_data->height / 2.0f; + const float tx = half_w * half_w; + const float ty = half_h * half_h; + for (const int y : YRange(area)) { + const float op_ry = y / op_h; + const float dy = (op_ry - this->m_data->y) / m_aspectRatio; + float *out = output->get_elem(area.xmin, y); + const float *mask = input_mask->get_elem(area.xmin, y); + const float *value = input_value->get_elem(area.xmin, y); + for (const int x : XRange(area)) { + const float op_rx = x / op_w; + const float dx = op_rx - this->m_data->x; + const float rx = this->m_data->x + (m_cosine * dx + m_sine * dy); + const float ry = this->m_data->y + (-m_sine * dx + m_cosine * dy); + float sx = rx - this->m_data->x; + sx *= sx; + float sy = ry - this->m_data->y; + sy *= sy; + const bool inside = ((sx / tx) + (sy / ty)) < 1.0f; + out[0] = mask_func(inside, mask, value); + + mask += input_mask->elem_stride; + value += input_value->elem_stride; + out += output->elem_stride; + } + } +} + void EllipseMaskOperation::deinitExecution() { this->m_inputMask = nullptr; diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.h b/source/blender/compositor/operations/COM_EllipseMaskOperation.h index 64afe0145cf..fba3f979d26 100644 --- a/source/blender/compositor/operations/COM_EllipseMaskOperation.h +++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.h @@ -18,12 +18,14 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class EllipseMaskOperation : public NodeOperation { +class EllipseMaskOperation : public MultiThreadedOperation { private: + using MaskFunc = std::function<float(bool is_inside, const float *mask, const float *value)>; + /** * Cached reference to the inputProgram */ @@ -64,6 +66,16 @@ class EllipseMaskOperation : public NodeOperation { { this->m_maskType = maskType; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + + private: + void apply_mask(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs, + MaskFunc mask_func); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc index 2be6e4d1be7..3804e6ec646 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc @@ -126,7 +126,7 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, float *buffer = src->getBuffer(); const uint8_t num_channels = src->get_num_channels(); - // <0.5 not valid, though can have a possibly useful sort of sharpening effect + /* <0.5 not valid, though can have a possibly useful sort of sharpening effect. */ if (sigma < 0.5f) { return; } @@ -135,8 +135,8 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, xy = 3; } - // XXX The YVV macro defined below explicitly expects sources of at least 3x3 pixels, - // so just skipping blur along faulty direction if src's def is below that limit! + /* XXX The YVV macro defined below explicitly expects sources of at least 3x3 pixels, + * so just skipping blur along faulty direction if src's def is below that limit! */ if (src_width < 3) { xy &= ~1; } @@ -147,32 +147,32 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, return; } - // see "Recursive Gabor Filtering" by Young/VanVliet - // all factors here in double.prec. - // Required, because for single.prec it seems to blow up if sigma > ~200 + /* See "Recursive Gabor Filtering" by Young/VanVliet + * all factors here in double-precision. + * Required, because for single-precision floating point seems to blow up if `sigma > ~200`. */ if (sigma >= 3.556f) { q = 0.9804f * (sigma - 3.556f) + 2.5091f; } - else { // sigma >= 0.5 + else { /* `sigma >= 0.5`. */ q = (0.0561f * sigma + 0.5784f) * sigma - 0.2568f; } q2 = q * q; sc = (1.1668 + q) * (3.203729649 + (2.21566 + q) * q); - // no gabor filtering here, so no complex multiplies, just the regular coefs. - // all negated here, so as not to have to recalc Triggs/Sdika matrix + /* No gabor filtering here, so no complex multiplies, just the regular coefficients. + * all negated here, so as not to have to recalc Triggs/Sdika matrix. */ cf[1] = q * (5.788961737 + (6.76492 + 3.0 * q) * q) / sc; cf[2] = -q2 * (3.38246 + 3.0 * q) / sc; - // 0 & 3 unchanged + /* 0 & 3 unchanged. */ cf[3] = q2 * q / sc; cf[0] = 1.0 - cf[1] - cf[2] - cf[3]; - // Triggs/Sdika border corrections, - // it seems to work, not entirely sure if it is actually totally correct, - // Besides J.M.Geusebroek's anigauss.c (see http://www.science.uva.nl/~mark), - // found one other implementation by Cristoph Lampert, - // but neither seem to be quite the same, result seems to be ok so far anyway. - // Extra scale factor here to not have to do it in filter, - // though maybe this had something to with the precision errors + /* Triggs/Sdika border corrections, + * it seems to work, not entirely sure if it is actually totally correct, + * Besides J.M.Geusebroek's `anigauss.c` (see http://www.science.uva.nl/~mark), + * found one other implementation by Cristoph Lampert, + * but neither seem to be quite the same, result seems to be ok so far anyway. + * Extra scale factor here to not have to do it in filter, + * though maybe this had something to with the precision errors */ sc = cf[0] / ((1.0 + cf[1] - cf[2] + cf[3]) * (1.0 - cf[1] - cf[2] - cf[3]) * (1.0 + cf[2] + (cf[1] - cf[3]) * cf[3])); tsM[0] = sc * (-cf[3] * cf[1] + 1.0 - cf[3] * cf[3] - cf[2]); @@ -210,12 +210,12 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, } \ (void)0 - // intermediate buffers + /* Intermediate buffers. */ sz = MAX2(src_width, src_height); X = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss X buf"); Y = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss Y buf"); W = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss W buf"); - if (xy & 1) { // H + if (xy & 1) { /* H. */ int offset; for (y = 0; y < src_height; y++) { const int yx = y * src_width; @@ -232,7 +232,7 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, } } } - if (xy & 2) { // V + if (xy & 2) { /* V. */ int offset; const int add = src_width * num_channels; @@ -257,7 +257,6 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, #undef YVV } -/// FastGaussianBlurValueOperation::FastGaussianBlurValueOperation() { this->addInputSocket(DataType::Value); @@ -336,8 +335,6 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect) } } - // newBuf-> - this->m_iirgaus = copy; } unlockMutex(); diff --git a/source/blender/compositor/operations/COM_MixOperation.cc b/source/blender/compositor/operations/COM_MixOperation.cc index 58fa09fa2a8..77ecbf60356 100644 --- a/source/blender/compositor/operations/COM_MixOperation.cc +++ b/source/blender/compositor/operations/COM_MixOperation.cc @@ -35,6 +35,7 @@ MixBaseOperation::MixBaseOperation() this->m_inputColor2Operation = nullptr; this->setUseValueAlphaMultiply(false); this->setUseClamp(false); + flags.can_be_constant = true; } void MixBaseOperation::initExecution() @@ -97,6 +98,45 @@ void MixBaseOperation::deinitExecution() this->m_inputColor2Operation = nullptr; } +void MixBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_value = inputs[0]; + const MemoryBuffer *input_color1 = inputs[1]; + const MemoryBuffer *input_color2 = inputs[2]; + const int width = BLI_rcti_size_x(&area); + PixelCursor p; + p.out_stride = output->elem_stride; + p.value_stride = input_value->elem_stride; + p.color1_stride = input_color1->elem_stride; + p.color2_stride = input_color2->elem_stride; + for (const int y : YRange(area)) { + p.out = output->get_elem(area.xmin, y); + p.row_end = p.out + width * output->elem_stride; + p.value = input_value->get_elem(area.xmin, y); + p.color1 = input_color1->get_elem(area.xmin, y); + p.color2 = input_color2->get_elem(area.xmin, y); + update_memory_buffer_row(p); + } +} + +void MixBaseOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + const float value_m = 1.0f - value; + p.out[0] = value_m * p.color1[0] + value * p.color2[0]; + p.out[1] = value_m * p.color1[1] + value * p.color2[1]; + p.out[2] = value_m * p.color1[2] + value * p.color2[2]; + p.out[3] = p.color1[3]; + p.next(); + } +} + /* ******** Mix Add Operation ******** */ void MixAddOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) @@ -121,6 +161,23 @@ void MixAddOperation::executePixelSampled(float output[4], float x, float y, Pix clampIfNeeded(output); } +void MixAddOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + p.out[0] = p.color1[0] + value * p.color2[0]; + p.out[1] = p.color1[1] + value * p.color2[1]; + p.out[2] = p.color1[2] + value * p.color2[2]; + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Blend Operation ******** */ void MixBlendOperation::executePixelSampled(float output[4], @@ -150,6 +207,24 @@ void MixBlendOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MixBlendOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + float value_m = 1.0f - value; + p.out[0] = value_m * p.color1[0] + value * p.color2[0]; + p.out[1] = value_m * p.color1[1] + value * p.color2[1]; + p.out[2] = value_m * p.color1[2] + value * p.color2[2]; + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Burn Operation ******** */ void MixColorBurnOperation::executePixelSampled(float output[4], @@ -228,6 +303,48 @@ void MixColorBurnOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MixColorBurnOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + const float value_m = 1.0f - value; + + float tmp = value_m + value * p.color2[0]; + if (tmp <= 0.0f) { + p.out[0] = 0.0f; + } + else { + tmp = 1.0f - (1.0f - p.color1[0]) / tmp; + p.out[0] = CLAMPIS(tmp, 0.0f, 1.0f); + } + + tmp = value_m + value * p.color2[1]; + if (tmp <= 0.0f) { + p.out[1] = 0.0f; + } + else { + tmp = 1.0f - (1.0f - p.color1[1]) / tmp; + p.out[1] = CLAMPIS(tmp, 0.0f, 1.0f); + } + + tmp = value_m + value * p.color2[2]; + if (tmp <= 0.0f) { + p.out[2] = 0.0f; + } + else { + tmp = 1.0f - (1.0f - p.color1[2]) / tmp; + p.out[2] = CLAMPIS(tmp, 0.0f, 1.0f); + } + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Color Operation ******** */ void MixColorOperation::executePixelSampled(float output[4], @@ -268,6 +385,36 @@ void MixColorOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MixColorOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + const float value_m = 1.0f - value; + + float colH, colS, colV; + rgb_to_hsv(p.color2[0], p.color2[1], p.color2[2], &colH, &colS, &colV); + if (colS != 0.0f) { + float rH, rS, rV; + float tmpr, tmpg, tmpb; + rgb_to_hsv(p.color1[0], p.color1[1], p.color1[2], &rH, &rS, &rV); + hsv_to_rgb(colH, colS, rV, &tmpr, &tmpg, &tmpb); + p.out[0] = (value_m * p.color1[0]) + (value * tmpr); + p.out[1] = (value_m * p.color1[1]) + (value * tmpg); + p.out[2] = (value_m * p.color1[2]) + (value * tmpb); + } + else { + copy_v3_v3(p.out, p.color1); + } + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Darken Operation ******** */ void MixDarkenOperation::executePixelSampled(float output[4], @@ -296,6 +443,24 @@ void MixDarkenOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MixDarkenOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + float value_m = 1.0f - value; + p.out[0] = min_ff(p.color1[0], p.color2[0]) * value + p.color1[0] * value_m; + p.out[1] = min_ff(p.color1[1], p.color2[1]) * value + p.color1[1] * value_m; + p.out[2] = min_ff(p.color1[2], p.color2[2]) * value + p.color1[2] * value_m; + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Difference Operation ******** */ void MixDifferenceOperation::executePixelSampled(float output[4], @@ -324,6 +489,24 @@ void MixDifferenceOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MixDifferenceOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + const float value_m = 1.0f - value; + p.out[0] = value_m * p.color1[0] + value * fabsf(p.color1[0] - p.color2[0]); + p.out[1] = value_m * p.color1[1] + value * fabsf(p.color1[1] - p.color2[1]); + p.out[2] = value_m * p.color1[2] + value * fabsf(p.color1[2] - p.color2[2]); + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Difference Operation ******** */ void MixDivideOperation::executePixelSampled(float output[4], @@ -369,6 +552,41 @@ void MixDivideOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MixDivideOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + const float value_m = 1.0f - value; + + if (p.color2[0] != 0.0f) { + p.out[0] = value_m * (p.color1[0]) + value * (p.color1[0]) / p.color2[0]; + } + else { + p.out[0] = 0.0f; + } + if (p.color2[1] != 0.0f) { + p.out[1] = value_m * (p.color1[1]) + value * (p.color1[1]) / p.color2[1]; + } + else { + p.out[1] = 0.0f; + } + if (p.color2[2] != 0.0f) { + p.out[2] = value_m * (p.color1[2]) + value * (p.color1[2]) / p.color2[2]; + } + else { + p.out[2] = 0.0f; + } + + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Dodge Operation ******** */ void MixDodgeOperation::executePixelSampled(float output[4], @@ -452,6 +670,64 @@ void MixDodgeOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MixDodgeOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + + float tmp; + if (p.color1[0] != 0.0f) { + tmp = 1.0f - value * p.color2[0]; + if (tmp <= 0.0f) { + p.out[0] = 1.0f; + } + else { + p.out[0] = p.color1[0] / tmp; + CLAMP_MAX(p.out[0], 1.0f); + } + } + else { + p.out[0] = 0.0f; + } + + if (p.color1[1] != 0.0f) { + tmp = 1.0f - value * p.color2[1]; + if (tmp <= 0.0f) { + p.out[1] = 1.0f; + } + else { + p.out[1] = p.color1[1] / tmp; + CLAMP_MAX(p.out[1], 1.0f); + } + } + else { + p.out[1] = 0.0f; + } + + if (p.color1[2] != 0.0f) { + tmp = 1.0f - value * p.color2[2]; + if (tmp <= 0.0f) { + p.out[2] = 1.0f; + } + else { + p.out[2] = p.color1[2] / tmp; + CLAMP_MAX(p.out[2], 1.0f); + } + } + else { + p.out[2] = 0.0f; + } + + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Glare Operation ******** */ void MixGlareOperation::executePixelSampled(float output[4], @@ -487,6 +763,33 @@ void MixGlareOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MixGlareOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + const float value = p.value[0]; + /* Linear interpolation between 3 cases: + * value=-1:output=input value=0:output=input+glare value=1:output=glare + */ + float input_weight; + float glare_weight; + if (value < 0.0f) { + input_weight = 1.0f; + glare_weight = 1.0f + value; + } + else { + input_weight = 1.0f - value; + glare_weight = 1.0f; + } + p.out[0] = input_weight * MAX2(p.color1[0], 0.0f) + glare_weight * p.color2[0]; + p.out[1] = input_weight * MAX2(p.color1[1], 0.0f) + glare_weight * p.color2[1]; + p.out[2] = input_weight * MAX2(p.color1[2], 0.0f) + glare_weight * p.color2[2]; + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Hue Operation ******** */ void MixHueOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) @@ -524,6 +827,36 @@ void MixHueOperation::executePixelSampled(float output[4], float x, float y, Pix clampIfNeeded(output); } +void MixHueOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + const float value_m = 1.0f - value; + + float colH, colS, colV; + rgb_to_hsv(p.color2[0], p.color2[1], p.color2[2], &colH, &colS, &colV); + if (colS != 0.0f) { + float rH, rS, rV; + float tmpr, tmpg, tmpb; + rgb_to_hsv(p.color1[0], p.color1[1], p.color1[2], &rH, &rS, &rV); + hsv_to_rgb(colH, rS, rV, &tmpr, &tmpg, &tmpb); + p.out[0] = value_m * p.color1[0] + value * tmpr; + p.out[1] = value_m * p.color1[1] + value * tmpg; + p.out[2] = value_m * p.color1[2] + value * tmpb; + } + else { + copy_v3_v3(p.out, p.color1); + } + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Lighten Operation ******** */ void MixLightenOperation::executePixelSampled(float output[4], @@ -570,6 +903,30 @@ void MixLightenOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MixLightenOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + + float tmp = value * p.color2[0]; + p.out[0] = MAX2(tmp, p.color1[0]); + + tmp = value * p.color2[1]; + p.out[1] = MAX2(tmp, p.color1[1]); + + tmp = value * p.color2[2]; + p.out[2] = MAX2(tmp, p.color1[2]); + + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Linear Light Operation ******** */ void MixLinearLightOperation::executePixelSampled(float output[4], @@ -613,6 +970,39 @@ void MixLinearLightOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MixLinearLightOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + if (p.color2[0] > 0.5f) { + p.out[0] = p.color1[0] + value * (2.0f * (p.color2[0] - 0.5f)); + } + else { + p.out[0] = p.color1[0] + value * (2.0f * (p.color2[0]) - 1.0f); + } + if (p.color2[1] > 0.5f) { + p.out[1] = p.color1[1] + value * (2.0f * (p.color2[1] - 0.5f)); + } + else { + p.out[1] = p.color1[1] + value * (2.0f * (p.color2[1]) - 1.0f); + } + if (p.color2[2] > 0.5f) { + p.out[2] = p.color1[2] + value * (2.0f * (p.color2[2] - 0.5f)); + } + else { + p.out[2] = p.color1[2] + value * (2.0f * (p.color2[2]) - 1.0f); + } + + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Multiply Operation ******** */ void MixMultiplyOperation::executePixelSampled(float output[4], @@ -641,6 +1031,25 @@ void MixMultiplyOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MixMultiplyOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + const float value_m = 1.0f - value; + p.out[0] = p.color1[0] * (value_m + value * p.color2[0]); + p.out[1] = p.color1[1] * (value_m + value * p.color2[1]); + p.out[2] = p.color1[2] * (value_m + value * p.color2[2]); + + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Overlay Operation ******** */ void MixOverlayOperation::executePixelSampled(float output[4], @@ -686,6 +1095,40 @@ void MixOverlayOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MixOverlayOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + const float value_m = 1.0f - value; + if (p.color1[0] < 0.5f) { + p.out[0] = p.color1[0] * (value_m + 2.0f * value * p.color2[0]); + } + else { + p.out[0] = 1.0f - (value_m + 2.0f * value * (1.0f - p.color2[0])) * (1.0f - p.color1[0]); + } + if (p.color1[1] < 0.5f) { + p.out[1] = p.color1[1] * (value_m + 2.0f * value * p.color2[1]); + } + else { + p.out[1] = 1.0f - (value_m + 2.0f * value * (1.0f - p.color2[1])) * (1.0f - p.color1[1]); + } + if (p.color1[2] < 0.5f) { + p.out[2] = p.color1[2] * (value_m + 2.0f * value * p.color2[2]); + } + else { + p.out[2] = 1.0f - (value_m + 2.0f * value * (1.0f - p.color2[2])) * (1.0f - p.color1[2]); + } + + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Saturation Operation ******** */ void MixSaturationOperation::executePixelSampled(float output[4], @@ -723,6 +1166,33 @@ void MixSaturationOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MixSaturationOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + const float value_m = 1.0f - value; + + float rH, rS, rV; + rgb_to_hsv(p.color1[0], p.color1[1], p.color1[2], &rH, &rS, &rV); + if (rS != 0.0f) { + float colH, colS, colV; + rgb_to_hsv(p.color2[0], p.color2[1], p.color2[2], &colH, &colS, &colV); + hsv_to_rgb(rH, (value_m * rS + value * colS), rV, &p.out[0], &p.out[1], &p.out[2]); + } + else { + copy_v3_v3(p.out, p.color1); + } + + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Screen Operation ******** */ void MixScreenOperation::executePixelSampled(float output[4], @@ -752,6 +1222,25 @@ void MixScreenOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MixScreenOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + const float value_m = 1.0f - value; + + p.out[0] = 1.0f - (value_m + value * (1.0f - p.color2[0])) * (1.0f - p.color1[0]); + p.out[1] = 1.0f - (value_m + value * (1.0f - p.color2[1])) * (1.0f - p.color1[1]); + p.out[2] = 1.0f - (value_m + value * (1.0f - p.color2[2])) * (1.0f - p.color1[2]); + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Soft Light Operation ******** */ void MixSoftLightOperation::executePixelSampled(float output[4], @@ -793,6 +1282,34 @@ void MixSoftLightOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MixSoftLightOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + const float value_m = 1.0f - value; + float scr, scg, scb; + + /* First calculate non-fac based Screen mix. */ + scr = 1.0f - (1.0f - p.color2[0]) * (1.0f - p.color1[0]); + scg = 1.0f - (1.0f - p.color2[1]) * (1.0f - p.color1[1]); + scb = 1.0f - (1.0f - p.color2[2]) * (1.0f - p.color1[2]); + + p.out[0] = value_m * p.color1[0] + + value * ((1.0f - p.color1[0]) * p.color2[0] * p.color1[0] + p.color1[0] * scr); + p.out[1] = value_m * p.color1[1] + + value * ((1.0f - p.color1[1]) * p.color2[1] * p.color1[1] + p.color1[1] * scg); + p.out[2] = value_m * p.color1[2] + + value * ((1.0f - p.color1[2]) * p.color2[2] * p.color1[2] + p.color1[2] * scb); + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Subtract Operation ******** */ void MixSubtractOperation::executePixelSampled(float output[4], @@ -820,6 +1337,23 @@ void MixSubtractOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MixSubtractOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + p.out[0] = p.color1[0] - value * p.color2[0]; + p.out[1] = p.color1[1] - value * p.color2[1]; + p.out[2] = p.color1[2] - value * p.color2[2]; + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + /* ******** Mix Value Operation ******** */ void MixValueOperation::executePixelSampled(float output[4], @@ -851,4 +1385,25 @@ void MixValueOperation::executePixelSampled(float output[4], clampIfNeeded(output); } +void MixValueOperation::update_memory_buffer_row(PixelCursor &p) +{ + while (p.out < p.row_end) { + float value = p.value[0]; + if (this->useValueAlphaMultiply()) { + value *= p.color2[3]; + } + float value_m = 1.0f - value; + + float rH, rS, rV; + float colH, colS, colV; + rgb_to_hsv(p.color1[0], p.color1[1], p.color1[2], &rH, &rS, &rV); + rgb_to_hsv(p.color2[0], p.color2[1], p.color2[2], &colH, &colS, &colV); + hsv_to_rgb(rH, rS, (value_m * rV + value * colV), &p.out[0], &p.out[1], &p.out[2]); + p.out[3] = p.color1[3]; + + clampIfNeeded(p.out); + p.next(); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MixOperation.h b/source/blender/compositor/operations/COM_MixOperation.h index 6c241bc5762..7ef9d78d58f 100644 --- a/source/blender/compositor/operations/COM_MixOperation.h +++ b/source/blender/compositor/operations/COM_MixOperation.h @@ -18,7 +18,7 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { @@ -27,8 +27,29 @@ namespace blender::compositor { * it assumes we are in sRGB color space. */ -class MixBaseOperation : public NodeOperation { +class MixBaseOperation : public MultiThreadedOperation { protected: + struct PixelCursor { + float *out; + const float *row_end; + const float *value; + const float *color1; + const float *color2; + int out_stride; + int value_stride; + int color1_stride; + int color2_stride; + + void next() + { + BLI_assert(out < row_end); + out += out_stride; + value += value_stride; + color1 += color1_stride; + color2 += color2_stride; + } + }; + /** * Prefetched reference to the inputProgram */ @@ -81,101 +102,165 @@ class MixBaseOperation : public NodeOperation { { this->m_useClamp = value; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) final; + + protected: + virtual void update_memory_buffer_row(PixelCursor &p); }; class MixAddOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixBlendOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixColorBurnOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixColorOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixDarkenOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixDifferenceOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixDivideOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixDodgeOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixGlareOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixHueOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixLightenOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixLinearLightOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixMultiplyOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixOverlayOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixSaturationOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixScreenOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixSoftLightOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixSubtractOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; class MixValueOperation : public MixBaseOperation { public: void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + protected: + void update_memory_buffer_row(PixelCursor &p) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.cc b/source/blender/compositor/operations/COM_MovieClipOperation.cc index d93a75407c4..e520b928edf 100644 --- a/source/blender/compositor/operations/COM_MovieClipOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipOperation.cc @@ -116,6 +116,18 @@ void MovieClipBaseOperation::executePixelSampled(float output[4], } } +void MovieClipBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs)) +{ + if (m_movieClipBuffer) { + output->copy_from(m_movieClipBuffer, area); + } + else { + output->fill(area, COM_COLOR_TRANSPARENT); + } +} + MovieClipOperation::MovieClipOperation() : MovieClipBaseOperation() { this->addOutputSocket(DataType::Color); @@ -136,4 +148,16 @@ void MovieClipAlphaOperation::executePixelSampled(float output[4], output[0] = result[3]; } +void MovieClipAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> UNUSED(inputs)) +{ + if (m_movieClipBuffer) { + output->copy_from(m_movieClipBuffer, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0); + } + else { + output->fill(area, COM_VALUE_ZERO); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.h b/source/blender/compositor/operations/COM_MovieClipOperation.h index c853ea43762..0a0c4c00f81 100644 --- a/source/blender/compositor/operations/COM_MovieClipOperation.h +++ b/source/blender/compositor/operations/COM_MovieClipOperation.h @@ -19,7 +19,7 @@ #pragma once #include "BLI_listbase.h" -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_movieclip_types.h" #include "IMB_imbuf_types.h" @@ -28,7 +28,7 @@ namespace blender::compositor { /** * Base class for movie clip */ -class MovieClipBaseOperation : public NodeOperation { +class MovieClipBaseOperation : public MultiThreadedOperation { protected: MovieClip *m_movieClip; MovieClipUser *m_movieClipUser; @@ -67,6 +67,10 @@ class MovieClipBaseOperation : public NodeOperation { this->m_framenumber = framenumber; } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; class MovieClipOperation : public MovieClipBaseOperation { @@ -78,6 +82,10 @@ class MovieClipAlphaOperation : public MovieClipBaseOperation { public: MovieClipAlphaOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc index 3c753591ced..b078d85372d 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.cc +++ b/source/blender/compositor/operations/COM_SMAAOperation.cc @@ -19,9 +19,9 @@ */ #include "COM_SMAAOperation.h" +#include "BKE_node.h" #include "BLI_math.h" #include "COM_SMAAAreaTexture.h" -#include "BKE_node.h" extern "C" { #include "IMB_colormanagement.h" diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc index f03b9fcf34d..5410b2c832a 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.cc +++ b/source/blender/compositor/operations/COM_ScaleOperation.cc @@ -17,6 +17,7 @@ */ #include "COM_ScaleOperation.h" +#include "COM_ConstantOperation.h" namespace blender::compositor { @@ -52,13 +53,51 @@ ScaleOperation::ScaleOperation(DataType data_type) : BaseScaleOperation() this->m_inputXOperation = nullptr; this->m_inputYOperation = nullptr; } + +float ScaleOperation::get_constant_scale(const int input_op_idx, const float factor) +{ + const bool is_constant = getInputOperation(input_op_idx)->get_flags().is_constant_operation; + if (is_constant) { + return ((ConstantOperation *)getInputOperation(input_op_idx))->get_constant_elem()[0] * factor; + } + + return 1.0f; +} + +float ScaleOperation::get_constant_scale_x() +{ + return get_constant_scale(1, get_relative_scale_x_factor()); +} + +float ScaleOperation::get_constant_scale_y() +{ + return get_constant_scale(2, get_relative_scale_y_factor()); +} + +BLI_INLINE float scale_coord(const int coord, const float center, const float relative_scale) +{ + return center + (coord - center) / relative_scale; +} + +void ScaleOperation::scale_area(rcti &rect, float scale_x, float scale_y) +{ + rect.xmin = scale_coord(rect.xmin, m_centerX, scale_x); + rect.xmax = scale_coord(rect.xmax, m_centerX, scale_x); + rect.ymin = scale_coord(rect.ymin, m_centerY, scale_y); + rect.ymax = scale_coord(rect.ymax, m_centerY, scale_y); +} + +void ScaleOperation::init_data() +{ + m_centerX = getWidth() / 2.0f; + m_centerY = getHeight() / 2.0f; +} + void ScaleOperation::initExecution() { this->m_inputOperation = this->getInputSocketReader(0); this->m_inputXOperation = this->getInputSocketReader(1); this->m_inputYOperation = this->getInputSocketReader(2); - this->m_centerX = this->getWidth() / 2.0; - this->m_centerY = this->getHeight() / 2.0; } void ScaleOperation::deinitExecution() @@ -68,7 +107,52 @@ void ScaleOperation::deinitExecution() this->m_inputYOperation = nullptr; } -void ScaleOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void ScaleOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + r_input_area = output_area; + if (input_idx != 0 || m_variable_size) { + return; + } + + float scale_x = get_constant_scale_x(); + float scale_y = get_constant_scale_y(); + scale_area(r_input_area, scale_x, scale_y); + expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler); +} + +void ScaleOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_img = inputs[0]; + MemoryBuffer *input_x = inputs[1]; + MemoryBuffer *input_y = inputs[2]; + const float scale_x_factor = get_relative_scale_x_factor(); + const float scale_y_factor = get_relative_scale_y_factor(); + BuffersIterator<float> it = output->iterate_with({input_x, input_y}, area); + for (; !it.is_end(); ++it) { + const float rel_scale_x = *it.in(0) * scale_x_factor; + const float rel_scale_y = *it.in(1) * scale_y_factor; + const float scaled_x = scale_coord(it.x, m_centerX, rel_scale_x); + const float scaled_y = scale_coord(it.y, m_centerY, rel_scale_y); + input_img->read_elem_sampled(scaled_x, scaled_y, (PixelSampler)m_sampler, it.out); + } +} + +ScaleRelativeOperation::ScaleRelativeOperation() : ScaleOperation() +{ +} + +ScaleRelativeOperation::ScaleRelativeOperation(DataType data_type) : ScaleOperation(data_type) +{ +} + +void ScaleRelativeOperation::executePixelSampled(float output[4], + float x, + float y, + PixelSampler sampler) { PixelSampler effective_sampler = getEffectiveSampler(sampler); @@ -86,9 +170,9 @@ void ScaleOperation::executePixelSampled(float output[4], float x, float y, Pixe this->m_inputOperation->readSampled(output, nx, ny, effective_sampler); } -bool ScaleOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool ScaleRelativeOperation::determineDependingAreaOfInterest(rcti *input, + ReadBufferOperation *readOperation, + rcti *output) { rcti newInput; if (!m_variable_size) { @@ -115,34 +199,6 @@ bool ScaleOperation::determineDependingAreaOfInterest(rcti *input, return BaseScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } -// SCALE ABSOLUTE -ScaleAbsoluteOperation::ScaleAbsoluteOperation() : BaseScaleOperation() -{ - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - this->setResolutionInputSocketIndex(0); - this->m_inputOperation = nullptr; - this->m_inputXOperation = nullptr; - this->m_inputYOperation = nullptr; -} -void ScaleAbsoluteOperation::initExecution() -{ - this->m_inputOperation = this->getInputSocketReader(0); - this->m_inputXOperation = this->getInputSocketReader(1); - this->m_inputYOperation = this->getInputSocketReader(2); - this->m_centerX = this->getWidth() / 2.0; - this->m_centerY = this->getHeight() / 2.0; -} - -void ScaleAbsoluteOperation::deinitExecution() -{ - this->m_inputOperation = nullptr; - this->m_inputXOperation = nullptr; - this->m_inputYOperation = nullptr; -} - void ScaleAbsoluteOperation::executePixelSampled(float output[4], float x, float y, @@ -202,8 +258,7 @@ bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input, newInput.ymax = this->getHeight(); newInput.ymin = 0; } - - return BaseScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return ScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); } // Absolute fixed size @@ -215,11 +270,12 @@ ScaleFixedSizeOperation::ScaleFixedSizeOperation() : BaseScaleOperation() this->m_inputOperation = nullptr; this->m_is_offset = false; } -void ScaleFixedSizeOperation::initExecution() + +void ScaleFixedSizeOperation::init_data() { - this->m_inputOperation = this->getInputSocketReader(0); - this->m_relX = this->m_inputOperation->getWidth() / (float)this->m_newWidth; - this->m_relY = this->m_inputOperation->getHeight() / (float)this->m_newHeight; + const NodeOperation *input_op = getInputOperation(0); + this->m_relX = input_op->getWidth() / (float)this->m_newWidth; + this->m_relY = input_op->getHeight() / (float)this->m_newHeight; /* *** all the options below are for a fairly special case - camera framing *** */ if (this->m_offsetX != 0.0f || this->m_offsetY != 0.0f) { @@ -237,8 +293,8 @@ void ScaleFixedSizeOperation::initExecution() if (this->m_is_aspect) { /* apply aspect from clip */ - const float w_src = this->m_inputOperation->getWidth(); - const float h_src = this->m_inputOperation->getHeight(); + const float w_src = input_op->getWidth(); + const float h_src = input_op->getHeight(); /* destination aspect is already applied from the camera frame */ const float w_dst = this->m_newWidth; @@ -267,6 +323,11 @@ void ScaleFixedSizeOperation::initExecution() /* *** end framing options *** */ } +void ScaleFixedSizeOperation::initExecution() +{ + this->m_inputOperation = this->getInputSocketReader(0); +} + void ScaleFixedSizeOperation::deinitExecution() { this->m_inputOperation = nullptr; @@ -315,4 +376,38 @@ void ScaleFixedSizeOperation::determineResolution(unsigned int resolution[2], resolution[1] = this->m_newHeight; } +void ScaleFixedSizeOperation::get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmax = (output_area.xmax - m_offsetX) * this->m_relX; + r_input_area.xmin = (output_area.xmin - m_offsetX) * this->m_relX; + r_input_area.ymax = (output_area.ymax - m_offsetY) * this->m_relY; + r_input_area.ymin = (output_area.ymin - m_offsetY) * this->m_relY; + expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler); +} + +void ScaleFixedSizeOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const MemoryBuffer *input_img = inputs[0]; + PixelSampler sampler = (PixelSampler)m_sampler; + BuffersIterator<float> it = output->iterate_with({}, area); + if (this->m_is_offset) { + for (; !it.is_end(); ++it) { + const float nx = (it.x - this->m_offsetX) * this->m_relX; + const float ny = (it.y - this->m_offsetY) * this->m_relY; + input_img->read_elem_sampled(nx, ny, sampler, it.out); + } + } + else { + for (; !it.is_end(); ++it) { + input_img->read_elem_sampled(it.x * this->m_relX, it.y * this->m_relY, sampler, it.out); + } + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ScaleOperation.h b/source/blender/compositor/operations/COM_ScaleOperation.h index 2f9a7be92e6..62a2cabc8e6 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.h +++ b/source/blender/compositor/operations/COM_ScaleOperation.h @@ -18,11 +18,11 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" namespace blender::compositor { -class BaseScaleOperation : public NodeOperation { +class BaseScaleOperation : public MultiThreadedOperation { public: void setSampler(PixelSampler sampler) { @@ -46,7 +46,7 @@ class BaseScaleOperation : public NodeOperation { }; class ScaleOperation : public BaseScaleOperation { - private: + protected: SocketReader *m_inputOperation; SocketReader *m_inputXOperation; SocketReader *m_inputYOperation; @@ -56,31 +56,59 @@ class ScaleOperation : public BaseScaleOperation { public: ScaleOperation(); ScaleOperation(DataType data_type); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void init_data() override; void initExecution() override; void deinitExecution() override; -}; -class ScaleAbsoluteOperation : public BaseScaleOperation { - SocketReader *m_inputOperation; - SocketReader *m_inputXOperation; - SocketReader *m_inputYOperation; - float m_centerX; - float m_centerY; + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + + protected: + virtual float get_relative_scale_x_factor() = 0; + virtual float get_relative_scale_y_factor() = 0; + + private: + float get_constant_scale(int input_op_idx, float factor); + float get_constant_scale_x(); + float get_constant_scale_y(); + void scale_area(rcti &rect, float scale_x, float scale_y); +}; +class ScaleRelativeOperation : public ScaleOperation { public: - ScaleAbsoluteOperation(); + ScaleRelativeOperation(); + ScaleRelativeOperation(DataType data_type); bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + float get_relative_scale_x_factor() override + { + return 1.0f; + } + float get_relative_scale_y_factor() override + { + return 1.0f; + } +}; - void initExecution() override; - void deinitExecution() override; +class ScaleAbsoluteOperation : public ScaleOperation { + public: + bool determineDependingAreaOfInterest(rcti *input, + ReadBufferOperation *readOperation, + rcti *output) override; + void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + float get_relative_scale_x_factor() override + { + return 1.0f / getWidth(); + } + float get_relative_scale_y_factor() override + { + return 1.0f / getHeight(); + } }; class ScaleFixedSizeOperation : public BaseScaleOperation { @@ -108,6 +136,7 @@ class ScaleFixedSizeOperation : public BaseScaleOperation { unsigned int preferredResolution[2]) override; void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void init_data() override; void initExecution() override; void deinitExecution() override; void setNewWidth(int width) @@ -131,6 +160,11 @@ class ScaleFixedSizeOperation : public BaseScaleOperation { this->m_offsetX = x; this->m_offsetY = y; } + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cc b/source/blender/compositor/operations/COM_SunBeamsOperation.cc index ff117841e8e..bd82b6397ad 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.cc +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cc @@ -46,14 +46,14 @@ void SunBeamsOperation::initExecution() * (u,v) is used to designate sector space coordinates * * For a target point (x,y) the sector should be chosen such that - * ``u >= v >= 0`` + * `u >= v >= 0` * This removes the need to handle all sorts of special cases. * * Template parameters: - * fxu : buffer increment in x for sector u+1 - * fxv : buffer increment in x for sector v+1 - * fyu : buffer increment in y for sector u+1 - * fyv : buffer increment in y for sector v+1 + * \param fxu: buffer increment in x for sector `u + 1`. + * \param fxv: buffer increment in x for sector `v + 1`. + * \param fyu: buffer increment in y for sector `u + 1`. + * \param fyv: buffer increment in y for sector `v + 1`. */ template<int fxu, int fxv, int fyu, int fyv> struct BufferLineAccumulator { diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc index 059a289ae4d..c8e0844d35f 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cc +++ b/source/blender/compositor/operations/COM_TextureOperation.cc @@ -157,14 +157,8 @@ void TextureBaseOperation::executePixelSampled(float output[4], m_sceneColorManage, false); - if (texres.talpha) { - output[3] = texres.ta; - } - else { - output[3] = texres.tin; - } - - if ((retval & TEX_RGB)) { + output[3] = texres.talpha ? texres.ta : texres.tin; + if (retval & TEX_RGB) { output[0] = texres.tr; output[1] = texres.tg; output[2] = texres.tb; @@ -174,4 +168,67 @@ void TextureBaseOperation::executePixelSampled(float output[4], } } +void TextureBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + const int op_width = this->getWidth(); + const int op_height = this->getHeight(); + const float center_x = op_width / 2; + const float center_y = op_height / 2; + TexResult tex_result = {0}; + float vec[3]; + const int thread_id = WorkScheduler::current_thread_id(); + for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *tex_offset = it.in(0); + const float *tex_size = it.in(1); + float u = (it.x - center_x) / op_width * 2; + float v = (it.y - center_y) / op_height * 2; + + /* When no interpolation/filtering happens in multitex() force nearest interpolation. + * We do it here because (a) we can't easily say multitex() that we want nearest + * interpolation and (b) in such configuration multitex() simply floor's the value + * which often produces artifacts. + */ + if (m_texture != nullptr && (m_texture->imaflag & TEX_INTERPOL) == 0) { + u += 0.5f / center_x; + v += 0.5f / center_y; + } + + vec[0] = tex_size[0] * (u + tex_offset[0]); + vec[1] = tex_size[1] * (v + tex_offset[1]); + vec[2] = tex_size[2] * tex_offset[2]; + + const int retval = multitex_ext(this->m_texture, + vec, + nullptr, + nullptr, + 0, + &tex_result, + thread_id, + m_pool, + m_sceneColorManage, + false); + + it.out[3] = tex_result.talpha ? tex_result.ta : tex_result.tin; + if (retval & TEX_RGB) { + it.out[0] = tex_result.tr; + it.out[1] = tex_result.tg; + it.out[2] = tex_result.tb; + } + else { + it.out[0] = it.out[1] = it.out[2] = it.out[3]; + } + } +} + +void TextureAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + MemoryBuffer texture(DataType::Color, area); + TextureBaseOperation::update_memory_buffer_partial(&texture, area, inputs); + output->copy_from(&texture, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0); +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TextureOperation.h b/source/blender/compositor/operations/COM_TextureOperation.h index 6fec9ab8f33..1e95cb270d0 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.h +++ b/source/blender/compositor/operations/COM_TextureOperation.h @@ -19,7 +19,7 @@ #pragma once #include "BLI_listbase.h" -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_texture_types.h" #include "MEM_guardedalloc.h" @@ -33,7 +33,7 @@ namespace blender::compositor { * * \todo Rename to operation. */ -class TextureBaseOperation : public NodeOperation { +class TextureBaseOperation : public MultiThreadedOperation { private: Tex *m_texture; const RenderData *m_rd; @@ -71,6 +71,10 @@ class TextureBaseOperation : public NodeOperation { { this->m_sceneColorManage = sceneColorManage; } + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; class TextureOperation : public TextureBaseOperation { @@ -81,6 +85,10 @@ class TextureAlphaOperation : public TextureBaseOperation { public: TextureAlphaOperation(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc index 860f56e23fa..37a45ac32cb 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cc +++ b/source/blender/compositor/operations/COM_ViewerOperation.cc @@ -191,10 +191,11 @@ void ViewerOperation::initImage() BLI_thread_unlock(LOCK_DRAW_IMAGE); } -void ViewerOperation::updateImage(rcti *rect) +void ViewerOperation::updateImage(const rcti *rect) { + float *buffer = m_outputBuffer; IMB_partial_display_buffer_update(this->m_ibuf, - this->m_outputBuffer, + buffer, nullptr, getWidth(), 0, @@ -218,4 +219,44 @@ eCompositorPriority ViewerOperation::getRenderPriority() const return eCompositorPriority::Low; } +void ViewerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), + const rcti &area, + Span<MemoryBuffer *> inputs) +{ + if (!m_outputBuffer) { + return; + } + + MemoryBuffer output_buffer( + m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, getWidth(), getHeight()); + const MemoryBuffer *input_image = inputs[0]; + output_buffer.copy_from(input_image, area); + if (this->m_useAlphaInput) { + const MemoryBuffer *input_alpha = inputs[1]; + output_buffer.copy_from(input_alpha, area, 0, COM_DATA_TYPE_VALUE_CHANNELS, 3); + } + + if (m_depthBuffer) { + MemoryBuffer depth_buffer( + m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, getWidth(), getHeight()); + const MemoryBuffer *input_depth = inputs[2]; + depth_buffer.copy_from(input_depth, area); + } + + updateImage(&area); +} + +void ViewerOperation::clear_display_buffer() +{ + BLI_assert(isActiveViewerOutput()); + initImage(); + size_t buf_bytes = (size_t)m_ibuf->y * m_ibuf->x * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float); + if (buf_bytes > 0) { + memset(m_outputBuffer, 0, buf_bytes); + rcti display_area; + BLI_rcti_init(&display_area, 0, m_ibuf->x, 0, m_ibuf->y); + updateImage(&display_area); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ViewerOperation.h b/source/blender/compositor/operations/COM_ViewerOperation.h index c0f13ff79fc..06ac501a535 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.h +++ b/source/blender/compositor/operations/COM_ViewerOperation.h @@ -20,15 +20,17 @@ #include "BKE_global.h" #include "BLI_rect.h" -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_image_types.h" namespace blender::compositor { -class ViewerOperation : public NodeOperation { +class ViewerOperation : public MultiThreadedOperation { private: + /* TODO(manzanilla): To be removed together with tiled implementation. */ float *m_outputBuffer; float *m_depthBuffer; + Image *m_image; ImageUser *m_imageUser; bool m_active; @@ -125,8 +127,14 @@ class ViewerOperation : public NodeOperation { this->m_displaySettings = displaySettings; } + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span<MemoryBuffer *> inputs) override; + + void clear_display_buffer(); + private: - void updateImage(rcti *rect); + void updateImage(const rcti *rect); void initImage(); }; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index e561d0b653c..8d1074d912f 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1496,7 +1496,7 @@ void DepsgraphNodeBuilder::build_object_data_geometry(Object *object, bool is_ob add_operation_node( &object->id, NodeType::BATCH_CACHE, - OperationCode::BATCH_UPDATE_SELECT, + OperationCode::GEOMETRY_SELECT_UPDATE, [object_cow](::Depsgraph *depsgraph) { BKE_object_select_update(depsgraph, object_cow); }); } @@ -1517,37 +1517,33 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata, bool if (key) { build_shapekeys(key); } - - /* Geometry evaluation. */ - /* Entry operation, takes care of initialization, and some other - * relations which needs to be run prior to actual geometry evaluation. */ - op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT); - op_node->set_as_entry(); - - add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DEFORM); - + /* Nodes for result of obdata's evaluation, and geometry + * evaluation on object. */ const ID_Type id_type = GS(obdata->name); switch (id_type) { case ID_ME: { - add_operation_node(obdata, - NodeType::GEOMETRY, - OperationCode::GEOMETRY_EVAL, - [obdata_cow](::Depsgraph *depsgraph) { - BKE_mesh_eval_geometry(depsgraph, (Mesh *)obdata_cow); - }); + op_node = add_operation_node(obdata, + NodeType::GEOMETRY, + OperationCode::GEOMETRY_EVAL, + [obdata_cow](::Depsgraph *depsgraph) { + BKE_mesh_eval_geometry(depsgraph, (Mesh *)obdata_cow); + }); + op_node->set_as_entry(); break; } case ID_MB: { - add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); + op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); + op_node->set_as_entry(); break; } case ID_CU: { - add_operation_node(obdata, - NodeType::GEOMETRY, - OperationCode::GEOMETRY_EVAL, - [obdata_cow](::Depsgraph *depsgraph) { - BKE_curve_eval_geometry(depsgraph, (Curve *)obdata_cow); - }); + op_node = add_operation_node(obdata, + NodeType::GEOMETRY, + OperationCode::GEOMETRY_EVAL, + [obdata_cow](::Depsgraph *depsgraph) { + BKE_curve_eval_geometry(depsgraph, (Curve *)obdata_cow); + }); + op_node->set_as_entry(); /* Make sure objects used for bevel.taper are in the graph. * NOTE: This objects might be not linked to the scene. */ Curve *cu = (Curve *)obdata; @@ -1563,41 +1559,47 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata, bool break; } case ID_LT: { - add_operation_node(obdata, - NodeType::GEOMETRY, - OperationCode::GEOMETRY_EVAL, - [obdata_cow](::Depsgraph *depsgraph) { - BKE_lattice_eval_geometry(depsgraph, (Lattice *)obdata_cow); - }); + op_node = add_operation_node(obdata, + NodeType::GEOMETRY, + OperationCode::GEOMETRY_EVAL, + [obdata_cow](::Depsgraph *depsgraph) { + BKE_lattice_eval_geometry(depsgraph, (Lattice *)obdata_cow); + }); + op_node->set_as_entry(); break; } case ID_GD: { /* GPencil evaluation operations. */ - add_operation_node(obdata, - NodeType::GEOMETRY, - OperationCode::GEOMETRY_EVAL, - [obdata_cow](::Depsgraph *depsgraph) { - BKE_gpencil_frame_active_set(depsgraph, (bGPdata *)obdata_cow); - }); + op_node = add_operation_node(obdata, + NodeType::GEOMETRY, + OperationCode::GEOMETRY_EVAL, + [obdata_cow](::Depsgraph *depsgraph) { + BKE_gpencil_frame_active_set(depsgraph, + (bGPdata *)obdata_cow); + }); + op_node->set_as_entry(); break; } case ID_HA: { - add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); + op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); + op_node->set_as_entry(); break; } case ID_PT: { - add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); + op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); + op_node->set_as_entry(); break; } case ID_VO: { /* Volume frame update. */ - add_operation_node(obdata, - NodeType::GEOMETRY, - OperationCode::GEOMETRY_EVAL, - [obdata_cow](::Depsgraph *depsgraph) { - BKE_volume_eval_geometry(depsgraph, (Volume *)obdata_cow); - }); + op_node = add_operation_node(obdata, + NodeType::GEOMETRY, + OperationCode::GEOMETRY_EVAL, + [obdata_cow](::Depsgraph *depsgraph) { + BKE_volume_eval_geometry(depsgraph, (Volume *)obdata_cow); + }); + op_node->set_as_entry(); break; } default: @@ -1611,22 +1613,10 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata, bool /* Batch cache. */ add_operation_node(obdata, NodeType::BATCH_CACHE, - OperationCode::BATCH_UPDATE_SELECT, + OperationCode::GEOMETRY_SELECT_UPDATE, [obdata_cow](::Depsgraph *depsgraph) { BKE_object_data_select_update(depsgraph, obdata_cow); }); - add_operation_node(obdata, - NodeType::BATCH_CACHE, - OperationCode::BATCH_UPDATE_DEFORM, - [obdata_cow](::Depsgraph *depsgraph) { - BKE_object_data_eval_batch_cache_deform_tag(depsgraph, obdata_cow); - }); - add_operation_node(obdata, - NodeType::BATCH_CACHE, - OperationCode::BATCH_UPDATE_ALL, - [obdata_cow](::Depsgraph *depsgraph) { - BKE_object_data_eval_batch_cache_dirty_tag(depsgraph, obdata_cow); - }); } void DepsgraphNodeBuilder::build_armature(bArmature *armature) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index c63b3d825a0..c7c6fafa512 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -647,7 +647,7 @@ void DepsgraphRelationBuilder::build_collection(LayerCollection *from_layer_coll /* Only create geometry relations to child objects, if they have a geometry component. */ OperationKey object_geometry_key{ - &cob->ob->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT}; + &cob->ob->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL}; if (find_node(object_geometry_key) != nullptr) { add_relation(object_geometry_key, collection_geometry_key, "Collection Geometry"); } @@ -1099,14 +1099,7 @@ void DepsgraphRelationBuilder::build_object_pointcache(Object *object) else { flag = FLAG_GEOMETRY; OperationKey geometry_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); - add_relation(point_cache_key, geometry_key, "Point Cache -> Geometry Eval"); - if (object->data) { - /* Geometry may change, so rebuild the Drawing Cache. */ - OperationKey object_data_batch_all_key( - (ID *)object->data, NodeType::BATCH_CACHE, OperationCode::BATCH_UPDATE_ALL); - add_relation( - point_cache_key, object_data_batch_all_key, "Point Cache -> Batch Update All"); - } + add_relation(point_cache_key, geometry_key, "Point Cache -> Geometry"); } BLI_assert(flag != -1); /* Tag that we did handle that component. */ @@ -1875,8 +1868,7 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) void DepsgraphRelationBuilder::build_particle_systems(Object *object) { TimeSourceKey time_src_key; - OperationKey obdata_ubereval_key( - &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT); + OperationKey obdata_ubereval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); OperationKey eval_init_key( &object->id, NodeType::PARTICLE_SYSTEM, OperationCode::PARTICLE_SYSTEM_INIT); OperationKey eval_done_key( @@ -2024,8 +2016,7 @@ void DepsgraphRelationBuilder::build_particle_system_visualization_object(Object { OperationKey psys_key( &object->id, NodeType::PARTICLE_SYSTEM, OperationCode::PARTICLE_SYSTEM_EVAL, psys->name); - OperationKey obdata_ubereval_key( - &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT); + OperationKey obdata_ubereval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); ComponentKey dup_ob_key(&draw_object->id, NodeType::TRANSFORM); add_relation(dup_ob_key, psys_key, "Particle Object Visualization"); if (draw_object->type == OB_MBALL) { @@ -2082,15 +2073,15 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) /* Get nodes for result of obdata's evaluation, and geometry evaluation * on object. */ ComponentKey obdata_geom_key(obdata, NodeType::GEOMETRY); - OperationKey obdata_ubereval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); + ComponentKey geom_key(&object->id, NodeType::GEOMETRY); /* Link components to each other. */ - add_relation(obdata_geom_key, obdata_ubereval_key, "Object Geometry Base Data"); - + add_relation(obdata_geom_key, geom_key, "Object Geometry Base Data"); + OperationKey obdata_ubereval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); /* Special case: modifiers evaluation queries scene for various things like * data mask to be used. We add relation here to ensure object is never * evaluated prior to Scene's CoW is ready. */ OperationKey scene_key(&scene_->id, NodeType::PARAMETERS, OperationCode::SCENE_EVAL); - Relation *rel = add_relation(scene_key, geom_init_key, "CoW Relation"); + Relation *rel = add_relation(scene_key, obdata_ubereval_key, "CoW Relation"); rel->flag |= RELATION_FLAG_NO_FLUSH; /* Modifiers */ if (object->modifiers.first != nullptr) { @@ -2100,13 +2091,13 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); if (mti->updateDepsgraph) { - DepsNodeHandle handle = create_node_handle(geom_init_key); + DepsNodeHandle handle = create_node_handle(obdata_ubereval_key); ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle); mti->updateDepsgraph(md, &ctx); } if (BKE_object_modifier_use_time(object, md)) { TimeSourceKey time_src_key; - add_relation(time_src_key, geom_init_key, "Time Source"); + add_relation(time_src_key, obdata_ubereval_key, "Time Source"); } } } @@ -2119,13 +2110,13 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info( (GpencilModifierType)md->type); if (mti->updateDepsgraph) { - DepsNodeHandle handle = create_node_handle(geom_init_key); + DepsNodeHandle handle = create_node_handle(obdata_ubereval_key); ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle); mti->updateDepsgraph(md, &ctx, graph_->mode); } if (BKE_object_modifier_gpencil_use_time(object, md)) { TimeSourceKey time_src_key; - add_relation(time_src_key, geom_init_key, "Time Source"); + add_relation(time_src_key, obdata_ubereval_key, "Time Source"); } } } @@ -2137,13 +2128,13 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) LISTBASE_FOREACH (ShaderFxData *, fx, &object->shader_fx) { const ShaderFxTypeInfo *fxi = BKE_shaderfx_get_info((ShaderFxType)fx->type); if (fxi->updateDepsgraph) { - DepsNodeHandle handle = create_node_handle(geom_init_key); + DepsNodeHandle handle = create_node_handle(obdata_ubereval_key); ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle); fxi->updateDepsgraph(fx, &ctx); } if (BKE_object_shaderfx_use_time(object, fx)) { TimeSourceKey time_src_key; - add_relation(time_src_key, geom_init_key, "Time Source"); + add_relation(time_src_key, obdata_ubereval_key, "Time Source"); } } } @@ -2169,7 +2160,6 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) add_relation(mom_transform_key, mom_geom_key, "Metaball Motherball Transform -> Geometry"); } else { - ComponentKey geom_key(&object->id, NodeType::GEOMETRY); ComponentKey transform_key(&object->id, NodeType::TRANSFORM); add_relation(geom_key, mom_geom_key, "Metaball Motherball"); add_relation(transform_key, mom_geom_key, "Metaball Motherball"); @@ -2184,7 +2174,9 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) * Ideally we need to get rid of this relation. */ if (object_particles_depends_on_time(object)) { TimeSourceKey time_key; - add_relation(time_key, geom_init_key, "Legacy particle time"); + OperationKey obdata_ubereval_key( + &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); + add_relation(time_key, obdata_ubereval_key, "Legacy particle time"); } /* Object data data-block. */ build_object_data_geometry_datablock((ID *)object->data); @@ -2206,33 +2198,12 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) add_relation(final_geometry_key, synchronize_key, "Synchronize to Original"); /* Batch cache. */ OperationKey object_data_select_key( - obdata, NodeType::BATCH_CACHE, OperationCode::BATCH_UPDATE_SELECT); + obdata, NodeType::BATCH_CACHE, OperationCode::GEOMETRY_SELECT_UPDATE); OperationKey object_select_key( - &object->id, NodeType::BATCH_CACHE, OperationCode::BATCH_UPDATE_SELECT); - + &object->id, NodeType::BATCH_CACHE, OperationCode::GEOMETRY_SELECT_UPDATE); add_relation(object_data_select_key, object_select_key, "Data Selection -> Object Selection"); - add_relation(final_geometry_key, - object_select_key, - "Object Geometry -> Select Update", - RELATION_FLAG_NO_FLUSH); - - OperationKey object_data_geom_deform_key( - obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DEFORM); - OperationKey object_data_geom_init_key( - obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT); - - OperationKey object_data_batch_deform_key( - obdata, NodeType::BATCH_CACHE, OperationCode::BATCH_UPDATE_DEFORM); - OperationKey object_data_batch_all_key( - obdata, NodeType::BATCH_CACHE, OperationCode::BATCH_UPDATE_ALL); - - add_relation(geom_init_key, object_data_batch_all_key, "Object Geometry -> Batch Update All"); - add_relation( - object_data_geom_init_key, object_data_batch_all_key, "Data Init -> Batch Update All"); - add_relation(object_data_geom_deform_key, - object_data_batch_deform_key, - "Data Deform -> Batch Update Deform"); + geom_key, object_select_key, "Object Geometry -> Select Update", RELATION_FLAG_NO_FLUSH); } void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) @@ -2250,13 +2221,8 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) build_shapekeys(key); } /* Link object data evaluation node to exit operation. */ - OperationKey obdata_geom_deform_key( - obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DEFORM); - OperationKey obdata_geom_init_key(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT); OperationKey obdata_geom_eval_key(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); OperationKey obdata_geom_done_key(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE); - add_relation(obdata_geom_init_key, obdata_geom_eval_key, "ObData Init -> Geom Eval"); - add_relation(obdata_geom_deform_key, obdata_geom_eval_key, "ObData Deform -> Geom Eval"); add_relation(obdata_geom_eval_key, obdata_geom_done_key, "ObData Geom Eval Done"); /* Type-specific links. */ const ID_Type id_type = GS(obdata->name); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc index 8e3960e1a15..bdabd67cc07 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc @@ -153,8 +153,8 @@ bool RNANodeQuery::contains(const char *prop_identifier, const char *rna_path_co return false; } - // If substr != prop_identifier, it means that the substring is found further in prop_identifier, - // and that thus index -1 is a valid memory location. + /* If substr != prop_identifier, it means that the substring is found further in prop_identifier, + * and that thus index -1 is a valid memory location. */ const bool start_ok = substr == prop_identifier || substr[-1] == '.'; if (!start_ok) { return false; diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 34b33e9a6c0..ab93464d09a 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -117,7 +117,7 @@ void depsgraph_select_tag_to_component_opcode(const ID *id, } else if (is_selectable_data_id_type(id_type)) { *component_type = NodeType::BATCH_CACHE; - *operation_code = OperationCode::BATCH_UPDATE_SELECT; + *operation_code = OperationCode::GEOMETRY_SELECT_UPDATE; } else { *component_type = NodeType::COPY_ON_WRITE; @@ -168,11 +168,6 @@ void depsgraph_tag_to_component_opcode(const ID *id, break; case ID_RECALC_GEOMETRY: depsgraph_geometry_tag_to_component(id, component_type); - *operation_code = OperationCode::GEOMETRY_EVAL_INIT; - break; - case ID_RECALC_GEOMETRY_DEFORM: - depsgraph_geometry_tag_to_component(id, component_type); - *operation_code = OperationCode::GEOMETRY_EVAL_DEFORM; break; case ID_RECALC_ANIMATION: *component_type = NodeType::ANIMATION; @@ -713,8 +708,6 @@ const char *DEG_update_tag_as_string(IDRecalcFlag flag) return "GEOMETRY"; case ID_RECALC_GEOMETRY_ALL_MODES: return "GEOMETRY_ALL_MODES"; - case ID_RECALC_GEOMETRY_DEFORM: - return "GEOMETRY_DEFORM"; case ID_RECALC_ANIMATION: return "ANIMATION"; case ID_RECALC_PSYS_REDO: diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index 915b9fedcec..ad88cf656ad 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -103,7 +103,7 @@ void evaluate_node(const DepsgraphEvalState *state, OperationNode *operation_nod ::Depsgraph *depsgraph = reinterpret_cast<::Depsgraph *>(state->graph); /* Sanity checks. */ - BLI_assert(!operation_node->is_noop() && "NOOP nodes should not actually be scheduled"); + BLI_assert_msg(!operation_node->is_noop(), "NOOP nodes should not actually be scheduled"); /* Perform operation. */ if (state->do_stats) { const double start_time = PIL_check_seconds_timer(); 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 346eba5bbc2..a844d23b558 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 @@ -348,7 +348,7 @@ bool scene_copy_inplace_no_main(const Scene *scene, Scene *new_scene) /* For the given scene get view layer which corresponds to an original for the * scene's evaluated one. This depends on how the scene is pulled into the - * dependency graph. */ + * dependency graph. */ ViewLayer *get_original_view_layer(const Depsgraph *depsgraph, const IDNode *id_node) { if (id_node->linked_state == DEG_ID_LINKED_DIRECTLY) { diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc index 2cbb0b52e34..a015491e2d7 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -58,15 +58,15 @@ #include "intern/eval/deg_eval_copy_on_write.h" -// Invalidate data-block data when update is flushed on it. -// -// The idea of this is to help catching cases when area is accessing data which -// is not yet evaluated, which could happen due to missing relations. The issue -// is that usually that data will be kept from previous frame, and it looks to -// be plausible. -// -// This ensures that data does not look plausible, making it much easier to -// catch usage of invalid state. +/* Invalidate data-block data when update is flushed on it. + * + * The idea of this is to help catching cases when area is accessing data which + * is not yet evaluated, which could happen due to missing relations. The issue + * is that usually that data will be kept from previous frame, and it looks to + * be plausible. + * + * This ensures that data does not look plausible, making it much easier to + * catch usage of invalid state. */ #undef INVALIDATE_ON_FLUSH namespace blender::deg { @@ -144,10 +144,7 @@ inline void flush_handle_component_node(IDNode *id_node, * special component where we don't want all operations to be tagged. * * TODO(sergey): Make this a more generic solution. */ - if (!ELEM(comp_node->type, - NodeType::PARTICLE_SETTINGS, - NodeType::PARTICLE_SYSTEM, - NodeType::BATCH_CACHE)) { + if (!ELEM(comp_node->type, NodeType::PARTICLE_SETTINGS, NodeType::PARTICLE_SYSTEM)) { for (OperationNode *op : comp_node->operations) { op->flag |= DEPSOP_FLAG_NEEDS_UPDATE; } diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc index d98486b83a8..c25dc6fc8d5 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.cc +++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc @@ -98,8 +98,6 @@ const char *operationCodeAsString(OperationCode opcode) /* Geometry. */ case OperationCode::GEOMETRY_EVAL_INIT: return "GEOMETRY_EVAL_INIT"; - case OperationCode::GEOMETRY_EVAL_DEFORM: - return "GEOMETRY_EVAL_DEFORM"; case OperationCode::GEOMETRY_EVAL: return "GEOMETRY_EVAL"; case OperationCode::GEOMETRY_EVAL_DONE: @@ -162,12 +160,8 @@ const char *operationCodeAsString(OperationCode opcode) case OperationCode::FILE_CACHE_UPDATE: return "FILE_CACHE_UPDATE"; /* Batch cache. */ - case OperationCode::BATCH_UPDATE_SELECT: - return "BATCH_UPDATE_SELECT"; - case OperationCode::BATCH_UPDATE_DEFORM: - return "BATCH_UPDATE_DEFORM"; - case OperationCode::BATCH_UPDATE_ALL: - return "BATCH_UPDATE_ALL"; + case OperationCode::GEOMETRY_SELECT_UPDATE: + return "GEOMETRY_SELECT_UPDATE"; /* Masks. */ case OperationCode::MASK_ANIMATION: return "MASK_ANIMATION"; diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h index b0130d03c69..a17186da941 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.h +++ b/source/blender/depsgraph/intern/node/deg_node_operation.h @@ -100,11 +100,7 @@ enum class OperationCode { /* Initialize evaluation of the geometry. Is an entry operation of geometry * component. */ GEOMETRY_EVAL_INIT, - /* Evaluate the geometry, including modifiers, and update only batches that - * are affected by deform operations. */ - GEOMETRY_EVAL_DEFORM, - /* Evaluate the geometry, including modifiers, but don't update the batch - * cache. */ + /* Evaluate the whole geometry, including modifiers. */ GEOMETRY_EVAL, /* Evaluation of geometry is completely done. */ GEOMETRY_EVAL_DONE, @@ -182,9 +178,7 @@ enum class OperationCode { WORLD_UPDATE, /* Batch caches. -------------------------------------------------------- */ - BATCH_UPDATE_SELECT, - BATCH_UPDATE_DEFORM, - BATCH_UPDATE_ALL, + GEOMETRY_SELECT_UPDATE, /* Masks. --------------------------------------------------------------- */ MASK_ANIMATION, diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index ad154704d2c..930d82fa225 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -52,8 +52,8 @@ set(INC set(SRC intern/draw_cache.c intern/draw_cache_extract_mesh.cc - intern/draw_cache_extract_mesh_extractors.c intern/draw_cache_extract_mesh_render_data.c + intern/mesh_extractors/extract_mesh.c intern/mesh_extractors/extract_mesh_ibo_edituv.cc intern/mesh_extractors/extract_mesh_ibo_fdots.cc intern/mesh_extractors/extract_mesh_ibo_lines.cc @@ -193,7 +193,6 @@ set(SRC intern/DRW_render.h intern/draw_cache.h intern/draw_cache_extract.h - intern/draw_cache_extract_mesh_private.h intern/draw_cache_impl.h intern/draw_cache_inline.h intern/draw_color_management.h @@ -207,6 +206,7 @@ set(SRC intern/draw_manager_text.h intern/draw_shader.h intern/draw_view.h + intern/mesh_extractors/extract_mesh.h intern/smaa_textures.h engines/basic/basic_engine.h engines/eevee/eevee_engine.h diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index 9d4f7865c32..6a66e8b1a58 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -178,19 +178,19 @@ static void eevee_cache_finish(void *vedata) } EEVEE_renderpasses_output_init(sldata, vedata, tot_samples); - /* Restart taa if a shader has finish compiling. */ - /* HACK We should use notification of some sort from the compilation job instead. */ + /* Restart TAA if a shader has finish compiling. */ + /* HACK: We should use notification of some sort from the compilation job instead. */ if (g_data->queued_shaders_count != g_data->queued_shaders_count_prev) { g_data->queued_shaders_count_prev = g_data->queued_shaders_count; EEVEE_temporal_sampling_reset(vedata); } } -/* As renders in an HDR offscreen buffer, we need draw everything once +/* As renders in an HDR off-screen buffer, we need draw everything once * during the background pass. This way the other drawing callback between * the background and the scene pass are visible. * NOTE: we could break it up in two passes using some depth test - * to reduce the fillrate */ + * to reduce the fill-rate. */ static void eevee_draw_scene(void *vedata) { EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.c b/source/blender/draw/engines/eevee/eevee_subsurface.c index b7bcd127859..48c24d138e6 100644 --- a/source/blender/draw/engines/eevee/eevee_subsurface.c +++ b/source/blender/draw/engines/eevee/eevee_subsurface.c @@ -192,7 +192,7 @@ void EEVEE_subsurface_add_pass(EEVEE_ViewLayerData *sldata, gpumat, stl->effects->sss_sample_count, &sss_tex_profile); if (!sss_profile) { - BLI_assert(0 && "SSS pass requested but no SSS data was found"); + BLI_assert_msg(0, "SSS pass requested but no SSS data was found"); return; } diff --git a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c index 8a2aebc95ab..fc6ad68d753 100644 --- a/source/blender/draw/engines/gpencil/gpencil_cache_utils.c +++ b/source/blender/draw/engines/gpencil/gpencil_cache_utils.c @@ -287,7 +287,7 @@ GPENCIL_tLayer *gpencil_layer_cache_add(GPENCIL_PrivateData *pd, pd->is_render ? gpl->vertex_paint_opacity : pd->vertex_paint_opacity; /* Negate thickness sign to tag that strokes are in screen space. - * Convert to world units (by default, 1 meter = 2000 px). */ + * Convert to world units (by default, 1 meter = 2000 pixels). */ float thickness_scale = (is_screenspace) ? -1.0f : (gpd->pixfactor / GPENCIL_PIXEL_FACTOR); float layer_opacity = gpencil_layer_final_opacity_get(pd, ob, gpl); float layer_tint[4]; diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl index b1368f90846..36a52e05a4a 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl @@ -327,7 +327,7 @@ vec2 safe_normalize_len(vec2 v, out float len) float stroke_thickness_modulate(float thickness) { - /* Modify stroke thickness by object and layer factors.-*/ + /* Modify stroke thickness by object and layer factors. */ thickness *= thicknessScale; thickness += thicknessOffset; thickness = max(1.0, thickness); diff --git a/source/blender/draw/engines/overlay/overlay_outline.c b/source/blender/draw/engines/overlay/overlay_outline.c index e3f01d968ae..03d70a37519 100644 --- a/source/blender/draw/engines/overlay/overlay_outline.c +++ b/source/blender/draw/engines/overlay/overlay_outline.c @@ -196,7 +196,7 @@ static void gpencil_layer_cache_populate(bGPDlayer *gpl, float object_scale = mat4_to_scale(iter->ob->obmat); /* Negate thickness sign to tag that strokes are in screen space. - * Convert to world units (by default, 1 meter = 2000 px). */ + * Convert to world units (by default, 1 meter = 2000 pixels). */ float thickness_scale = (is_screenspace) ? -1.0f : (gpd->pixfactor / 2000.0f); DRWShadingGroup *grp = iter->stroke_grp = DRW_shgroup_create_sub(iter->stroke_grp); @@ -340,7 +340,7 @@ void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata, if (shgroup && geom) { if (ob->type == OB_POINTCLOUD) { - /* Draw range to avoid drawcall batching messing up the instance attrib. */ + /* Draw range to avoid drawcall batching messing up the instance attribute. */ DRW_shgroup_call_instance_range(shgroup, ob, geom, 0, 0); } else { diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index e9d6763fbe9..f09c019ef8d 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -128,7 +128,7 @@ static void workbench_cache_sculpt_populate(WORKBENCH_PrivateData *wpd, BLI_INLINE void workbench_object_drawcall(DRWShadingGroup *grp, struct GPUBatch *geom, Object *ob) { if (ob->type == OB_POINTCLOUD) { - /* Draw range to avoid drawcall batching messing up the instance attrib. */ + /* Draw range to avoid drawcall batching messing up the instance attribute. */ DRW_shgroup_call_instance_range(grp, ob, geom, 0, 0); } else { diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index f5b95ac97ff..cd8b6531037 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -327,7 +327,7 @@ typedef enum { /** Culling test */ DRW_STATE_CULL_BACK = (1 << 7), DRW_STATE_CULL_FRONT = (1 << 8), - /** Stencil test . These options are mutually exclusive and packed into 2 bits. */ + /** Stencil test. These options are mutually exclusive and packed into 2 bits. */ DRW_STATE_STENCIL_ALWAYS = (1 << 9), DRW_STATE_STENCIL_EQUAL = (2 << 9), DRW_STATE_STENCIL_NEQUAL = (3 << 9), diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index e2fdfbf5e1c..734b312bbd9 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -808,9 +808,9 @@ GPUBatch *DRW_cache_normal_arrow_get(void) } /* -------------------------------------------------------------------- */ -/** \name Dummy vbos +/** \name Dummy VBO's * - * We need a dummy vbo containing the vertex count to draw instances ranges. + * We need a dummy VBO containing the vertex count to draw instances ranges. * * \{ */ diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index a0694a08f0b..7dc468d1a73 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -81,11 +81,12 @@ typedef enum eMRDataType { MR_DATA_POLY_NOR = 1 << 1, MR_DATA_LOOP_NOR = 1 << 2, MR_DATA_LOOPTRI = 1 << 3, + MR_DATA_LOOSE_GEOM = 1 << 4, /** Force loop normals calculation. */ - MR_DATA_TAN_LOOP_NOR = 1 << 4, - MR_DATA_MAT_OFFSETS = 1 << 5, + MR_DATA_TAN_LOOP_NOR = 1 << 5, + MR_DATA_POLYS_SORTED = 1 << 6, } eMRDataType; -ENUM_OPERATORS(eMRDataType, MR_DATA_MAT_OFFSETS) +ENUM_OPERATORS(eMRDataType, MR_DATA_POLYS_SORTED) #ifdef __cplusplus extern "C" { @@ -169,10 +170,10 @@ typedef struct MeshBufferExtractionCache { } loose_geom; struct { - int *tri; + int *tri_first_index; + int *mat_tri_len; int visible_tri_len; - } mat_offsets; - + } poly_sorted; } MeshBufferExtractionCache; #define FOREACH_MESH_BUFFER_CACHE(batch_cache, mbc) \ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 6d71b01b7e0..1bc2b8a9def 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -41,9 +41,10 @@ #include "GPU_capabilities.h" #include "draw_cache_extract.h" -#include "draw_cache_extract_mesh_private.h" #include "draw_cache_inline.h" +#include "mesh_extractors/extract_mesh.h" + // #define DEBUG_TIME #ifdef DEBUG_TIME @@ -531,7 +532,8 @@ static void mesh_extract_render_data_node_exec(void *__restrict task_data) mesh_render_data_update_normals(mr, data_flag); mesh_render_data_update_looptris(mr, iter_type, data_flag); - mesh_render_data_update_mat_offsets(mr, update_task_data->cache, data_flag); + mesh_render_data_update_loose_geom(mr, update_task_data->cache, iter_type, data_flag); + mesh_render_data_update_polys_sorted(mr, update_task_data->cache, data_flag); } static struct TaskNode *mesh_extract_render_data_node_create(struct TaskGraph *task_graph, @@ -685,19 +687,8 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, double rdata_start = PIL_check_seconds_timer(); #endif - eMRIterType iter_type = extractors.iter_types(); - eMRDataType data_flag = extractors.data_types(); - - MeshRenderData *mr = mesh_render_data_create(me, - extraction_cache, - is_editmode, - is_paint_mode, - is_mode_active, - obmat, - do_final, - do_uvedit, - ts, - iter_type); + MeshRenderData *mr = mesh_render_data_create( + me, is_editmode, is_paint_mode, is_mode_active, obmat, do_final, do_uvedit, ts); mr->use_hide = use_hide; mr->use_subsurf_fdots = use_subsurf_fdots; mr->use_final_mesh = do_final; @@ -706,6 +697,9 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, double rdata_end = PIL_check_seconds_timer(); #endif + eMRIterType iter_type = extractors.iter_types(); + eMRDataType data_flag = extractors.data_types(); + struct TaskNode *task_node_mesh_render_data = mesh_extract_render_data_node_create( task_graph, mr, extraction_cache, iter_type, data_flag); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c index bccf894cc53..27fd6ca9134 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.c @@ -25,6 +25,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_alloca.h" #include "BLI_bitmap.h" #include "BLI_math.h" #include "BLI_task.h" @@ -37,7 +38,7 @@ #include "ED_mesh.h" -#include "draw_cache_extract_mesh_private.h" +#include "mesh_extractors/extract_mesh.h" /* ---------------------------------------------------------------------- */ /** \name Update Loose Geometry @@ -165,119 +166,118 @@ static void mesh_render_data_ledges_bm(const MeshRenderData *mr, } } +void mesh_render_data_update_loose_geom(MeshRenderData *mr, + MeshBufferExtractionCache *cache, + const eMRIterType iter_type, + const eMRDataType data_flag) +{ + if ((iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) || (data_flag & MR_DATA_LOOSE_GEOM)) { + mesh_render_data_loose_geom_ensure(mr, cache); + mesh_render_data_loose_geom_load(mr, cache); + } +} + /** \} */ /* ---------------------------------------------------------------------- */ -/** \name Material Offsets +/** \name Polygons sorted per material * - * Material offsets contains the offset of a material after sorting tris based on their material. + * Contains polygon indices sorted based on their material. * * \{ */ -static void mesh_render_data_mat_offset_load(MeshRenderData *mr, - const MeshBufferExtractionCache *cache); -static void mesh_render_data_mat_offset_ensure(MeshRenderData *mr, - MeshBufferExtractionCache *cache); -static void mesh_render_data_mat_offset_build(MeshRenderData *mr, - MeshBufferExtractionCache *cache); -static void mesh_render_data_mat_offset_build_bm(MeshRenderData *mr, +static void mesh_render_data_polys_sorted_load(MeshRenderData *mr, + const MeshBufferExtractionCache *cache); +static void mesh_render_data_polys_sorted_ensure(MeshRenderData *mr, MeshBufferExtractionCache *cache); -static void mesh_render_data_mat_offset_build_mesh(MeshRenderData *mr, - MeshBufferExtractionCache *cache); -static void mesh_render_data_mat_offset_apply_offset(MeshRenderData *mr, - MeshBufferExtractionCache *cache); - -void mesh_render_data_update_mat_offsets(MeshRenderData *mr, - MeshBufferExtractionCache *cache, - const eMRDataType data_flag) +static void mesh_render_data_polys_sorted_build(MeshRenderData *mr, + MeshBufferExtractionCache *cache); +static int *mesh_render_data_mat_tri_len_build(MeshRenderData *mr); + +void mesh_render_data_update_polys_sorted(MeshRenderData *mr, + MeshBufferExtractionCache *cache, + const eMRDataType data_flag) { - if (data_flag & MR_DATA_MAT_OFFSETS) { - mesh_render_data_mat_offset_ensure(mr, cache); - mesh_render_data_mat_offset_load(mr, cache); + if (data_flag & MR_DATA_POLYS_SORTED) { + mesh_render_data_polys_sorted_ensure(mr, cache); + mesh_render_data_polys_sorted_load(mr, cache); } } -static void mesh_render_data_mat_offset_load(MeshRenderData *mr, - const MeshBufferExtractionCache *cache) +static void mesh_render_data_polys_sorted_load(MeshRenderData *mr, + const MeshBufferExtractionCache *cache) { - mr->mat_offsets.tri = cache->mat_offsets.tri; - mr->mat_offsets.visible_tri_len = cache->mat_offsets.visible_tri_len; + mr->poly_sorted.tri_first_index = cache->poly_sorted.tri_first_index; + mr->poly_sorted.mat_tri_len = cache->poly_sorted.mat_tri_len; + mr->poly_sorted.visible_tri_len = cache->poly_sorted.visible_tri_len; } -static void mesh_render_data_mat_offset_ensure(MeshRenderData *mr, - MeshBufferExtractionCache *cache) +static void mesh_render_data_polys_sorted_ensure(MeshRenderData *mr, + MeshBufferExtractionCache *cache) { - if (cache->mat_offsets.tri) { + if (cache->poly_sorted.tri_first_index) { return; } - mesh_render_data_mat_offset_build(mr, cache); + mesh_render_data_polys_sorted_build(mr, cache); } -static void mesh_render_data_mat_offset_build(MeshRenderData *mr, MeshBufferExtractionCache *cache) +static void mesh_render_data_polys_sorted_build(MeshRenderData *mr, + MeshBufferExtractionCache *cache) { - size_t mat_tri_idx_size = sizeof(int) * mr->mat_len; - cache->mat_offsets.tri = MEM_callocN(mat_tri_idx_size, __func__); + int *tri_first_index = MEM_mallocN(sizeof(*tri_first_index) * mr->poly_len, __func__); + int *mat_tri_len = mesh_render_data_mat_tri_len_build(mr); + + /* Apply offset. */ + int visible_tri_len = 0; + int *mat_tri_offs = BLI_array_alloca(mat_tri_offs, mr->mat_len); + { + for (int i = 0; i < mr->mat_len; i++) { + mat_tri_offs[i] = visible_tri_len; + visible_tri_len += mat_tri_len[i]; + } + } - /* Count how many triangles for each material. */ + /* Sort per material. */ + int mat_last = mr->mat_len - 1; if (mr->extract_type == MR_EXTRACT_BMESH) { - mesh_render_data_mat_offset_build_bm(mr, cache); + BMIter iter; + BMFace *f; + int i; + BM_ITER_MESH_INDEX (f, &iter, mr->bm, BM_FACES_OF_MESH, i) { + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + const int mat = min_ii(f->mat_nr, mat_last); + tri_first_index[i] = mat_tri_offs[mat]; + mat_tri_offs[mat] += f->len - 2; + } + else { + tri_first_index[i] = -1; + } + } } else { - mesh_render_data_mat_offset_build_mesh(mr, cache); - } - - mesh_render_data_mat_offset_apply_offset(mr, cache); -} - -typedef struct MatOffsetUserData { - MeshRenderData *mr; - /** This struct is extended during allocation to hold mat_tri_len for each material. */ - int mat_tri_len[0]; -} MatOffsetUserData; - -static void mesh_render_data_mat_offset_reduce(const void *__restrict UNUSED(userdata), - void *__restrict chunk_join, - void *__restrict chunk) -{ - MatOffsetUserData *dst = chunk_join; - MatOffsetUserData *src = chunk; - int *dst_mat_len = dst->mat_tri_len; - int *src_mat_len = src->mat_tri_len; - for (int i = 0; i < dst->mr->mat_len; i++) { - dst_mat_len[i] += src_mat_len[i]; + const MPoly *mp = &mr->mpoly[0]; + for (int i = 0; i < mr->poly_len; i++, mp++) { + if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + const int mat = min_ii(mp->mat_nr, mat_last); + tri_first_index[i] = mat_tri_offs[mat]; + mat_tri_offs[mat] += mp->totloop - 2; + } + else { + tri_first_index[i] = -1; + } + } } -} -static void mesh_render_data_mat_offset_build_threaded(MeshRenderData *mr, - MeshBufferExtractionCache *cache, - int face_len, - TaskParallelRangeFunc range_func) -{ - /* Extending the #MatOffsetUserData with an int per material slot. */ - size_t userdata_size = sizeof(MatOffsetUserData) + - (mr->mat_len) * sizeof(*cache->mat_offsets.tri); - MatOffsetUserData *userdata = MEM_callocN(userdata_size, __func__); - userdata->mr = mr; - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.userdata_chunk = userdata; - settings.userdata_chunk_size = userdata_size; - settings.min_iter_per_thread = MIN_RANGE_LEN; - settings.func_reduce = mesh_render_data_mat_offset_reduce; - BLI_task_parallel_range(0, face_len, NULL, range_func, &settings); - - memcpy(cache->mat_offsets.tri, - &userdata->mat_tri_len, - (mr->mat_len) * sizeof(*cache->mat_offsets.tri)); - MEM_freeN(userdata); + cache->poly_sorted.tri_first_index = tri_first_index; + cache->poly_sorted.mat_tri_len = mat_tri_len; + cache->poly_sorted.visible_tri_len = visible_tri_len; } -static void mesh_render_data_mat_offset_bm_range(void *__restrict UNUSED(userdata), - const int iter, - const TaskParallelTLS *__restrict tls) +static void mesh_render_data_mat_tri_len_bm_range_fn(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict tls) { - MatOffsetUserData *mat_offset_userdata = tls->userdata_chunk; - MeshRenderData *mr = mat_offset_userdata->mr; - int *mat_tri_len = mat_offset_userdata->mat_tri_len; + MeshRenderData *mr = userdata; + int *mat_tri_len = tls->userdata_chunk; BMesh *bm = mr->bm; BMFace *efa = BM_face_at_index(bm, iter); @@ -287,21 +287,12 @@ static void mesh_render_data_mat_offset_bm_range(void *__restrict UNUSED(userdat } } -static void mesh_render_data_mat_offset_build_bm(MeshRenderData *mr, - MeshBufferExtractionCache *cache) +static void mesh_render_data_mat_tri_len_mesh_range_fn(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict tls) { - BMesh *bm = mr->bm; - mesh_render_data_mat_offset_build_threaded( - mr, cache, bm->totface, mesh_render_data_mat_offset_bm_range); -} - -static void mesh_render_data_mat_offset_mesh_range(void *__restrict UNUSED(userdata), - const int iter, - const TaskParallelTLS *__restrict tls) -{ - MatOffsetUserData *mat_offset_userdata = tls->userdata_chunk; - const MeshRenderData *mr = mat_offset_userdata->mr; - int *mat_tri_len = mat_offset_userdata->mat_tri_len; + MeshRenderData *mr = userdata; + int *mat_tri_len = tls->userdata_chunk; const MPoly *mp = &mr->mpoly[iter]; if (!(mr->use_hide && (mp->flag & ME_HIDE))) { @@ -310,25 +301,47 @@ static void mesh_render_data_mat_offset_mesh_range(void *__restrict UNUSED(userd } } -static void mesh_render_data_mat_offset_build_mesh(MeshRenderData *mr, - MeshBufferExtractionCache *cache) +static void mesh_render_data_mat_tri_len_reduce_fn(const void *__restrict userdata, + void *__restrict chunk_join, + void *__restrict chunk) { - mesh_render_data_mat_offset_build_threaded( - mr, cache, mr->poly_len, mesh_render_data_mat_offset_mesh_range); + const MeshRenderData *mr = userdata; + int *dst_mat_len = chunk_join; + int *src_mat_len = chunk; + for (int i = 0; i < mr->mat_len; i++) { + dst_mat_len[i] += src_mat_len[i]; + } } -static void mesh_render_data_mat_offset_apply_offset(MeshRenderData *mr, - MeshBufferExtractionCache *cache) +static int *mesh_render_data_mat_tri_len_build_threaded(MeshRenderData *mr, + int face_len, + TaskParallelRangeFunc range_func) { - int *mat_tri_len = cache->mat_offsets.tri; - int ofs = mat_tri_len[0]; - mat_tri_len[0] = 0; - for (int i = 1; i < mr->mat_len; i++) { - int tmp = mat_tri_len[i]; - mat_tri_len[i] = ofs; - ofs += tmp; + /* Extending the #MatOffsetUserData with an int per material slot. */ + size_t mat_tri_len_size = sizeof(int) * mr->mat_len; + int *mat_tri_len = MEM_callocN(mat_tri_len_size, __func__); + + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.userdata_chunk = mat_tri_len; + settings.userdata_chunk_size = mat_tri_len_size; + settings.min_iter_per_thread = MIN_RANGE_LEN; + settings.func_reduce = mesh_render_data_mat_tri_len_reduce_fn; + BLI_task_parallel_range(0, face_len, mr, range_func, &settings); + + return mat_tri_len; +} + +/* Count how many triangles for each material. */ +static int *mesh_render_data_mat_tri_len_build(MeshRenderData *mr) +{ + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMesh *bm = mr->bm; + return mesh_render_data_mat_tri_len_build_threaded( + mr, bm->totface, mesh_render_data_mat_tri_len_bm_range_fn); } - cache->mat_offsets.visible_tri_len = ofs; + return mesh_render_data_mat_tri_len_build_threaded( + mr, mr->poly_len, mesh_render_data_mat_tri_len_mesh_range_fn); } /** \} */ @@ -447,15 +460,13 @@ void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_ * otherwise don't use modifiers as they are not from this object. */ MeshRenderData *mesh_render_data_create(Mesh *me, - MeshBufferExtractionCache *cache, const bool is_editmode, const bool is_paint_mode, const bool is_mode_active, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const ToolSettings *ts, - const eMRIterType iter_type) + const ToolSettings *ts) { MeshRenderData *mr = MEM_callocN(sizeof(*mr), __func__); mr->toolsettings = ts; @@ -565,11 +576,6 @@ MeshRenderData *mesh_render_data_create(Mesh *me, mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len); } - if (iter_type & (MR_ITER_LEDGE | MR_ITER_LVERT)) { - mesh_render_data_loose_geom_ensure(mr, cache); - mesh_render_data_loose_geom_load(mr, cache); - } - return mr; } diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index 51bd4c535cd..1efe0c080be 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -663,7 +663,7 @@ static void curve_create_curves_lines(CurveRenderData *rdata, GPUIndexBuf *ibo_c for (const int i_spline : splines.index_range()) { const int eval_size = splines[i_spline]->evaluated_points_size(); - if (splines[i_spline]->is_cyclic()) { + if (splines[i_spline]->is_cyclic() && splines[i_spline]->evaluated_edges_size() > 1) { GPU_indexbuf_add_generic_vert(&elb, offsets[i_spline] + eval_size - 1); } for (const int i_point : IndexRange(eval_size)) { diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 0c002ff09f2..359788545e4 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -67,11 +67,12 @@ #include "ED_uvedit.h" #include "draw_cache_extract.h" -#include "draw_cache_extract_mesh_private.h" #include "draw_cache_inline.h" #include "draw_cache_impl.h" /* own include */ +#include "mesh_extractors/extract_mesh.h" + /* ---------------------------------------------------------------------- */ /** \name Dependencies between buffer and batch * \{ */ @@ -821,17 +822,6 @@ void DRW_mesh_batch_cache_dirty_tag(Mesh *me, eMeshBatchDirtyMode mode) mesh_batch_cache_discard_shaded_tri(cache); mesh_batch_cache_discard_uvedit(cache); break; - case BKE_MESH_BATCH_DIRTY_DEFORM: - FOREACH_MESH_BUFFER_CACHE (cache, mbufcache) { - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.pos_nor); - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.lnor); - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_pos); - GPU_VERTBUF_DISCARD_SAFE(mbufcache->vbo.fdots_nor); - GPU_INDEXBUF_DISCARD_SAFE(mbufcache->ibo.tris); - } - batch_map = MDEPS_CREATE_MAP(vbo.pos_nor, vbo.lnor, vbo.fdots_pos, vbo.fdots_nor, ibo.tris); - mesh_batch_cache_discard_batch(cache, batch_map); - break; case BKE_MESH_BATCH_DIRTY_UVEDIT_ALL: mesh_batch_cache_discard_uvedit(cache); break; @@ -867,7 +857,9 @@ static void mesh_buffer_extraction_cache_clear(MeshBufferExtractionCache *extrac extraction_cache->loose_geom.edge_len = 0; extraction_cache->loose_geom.vert_len = 0; - MEM_SAFE_FREE(extraction_cache->mat_offsets.tri); + MEM_SAFE_FREE(extraction_cache->poly_sorted.tri_first_index); + MEM_SAFE_FREE(extraction_cache->poly_sorted.mat_tri_len); + extraction_cache->poly_sorted.visible_tri_len = 0; } static void mesh_batch_cache_clear(Mesh *me) diff --git a/source/blender/draw/intern/draw_cache_impl_volume.c b/source/blender/draw/intern/draw_cache_impl_volume.c index a91a1391c31..cba063ed5ef 100644 --- a/source/blender/draw/intern/draw_cache_impl_volume.c +++ b/source/blender/draw/intern/draw_cache_impl_volume.c @@ -304,7 +304,7 @@ static DRWVolumeGrid *volume_grid_cache_get(const Volume *volume, BLI_addtail(&cache->grids, cache_grid); /* TODO: can we load this earlier, avoid accessing the global and take - * advantage of dependency graph multithreading? */ + * advantage of dependency graph multi-threading? */ BKE_volume_load(volume, G.main); /* Test if we support textures with the number of channels. */ diff --git a/source/blender/draw/intern/draw_cache_inline.h b/source/blender/draw/intern/draw_cache_inline.h index b977d0cdda2..5e349d43538 100644 --- a/source/blender/draw/intern/draw_cache_inline.h +++ b/source/blender/draw/intern/draw_cache_inline.h @@ -74,8 +74,8 @@ BLI_INLINE void DRW_ibo_request(GPUBatch *batch, GPUIndexBuf **ibo) BLI_INLINE bool DRW_ibo_requested(GPUIndexBuf *ibo) { - /* TODO: do not rely on data uploaded. This prevents multithreading. - * (need access to a gl context) */ + /* TODO: do not rely on data uploaded. This prevents multi-threading. + * (need access to a OpenGL context). */ return (ibo != NULL && !GPU_indexbuf_is_init(ibo)); } @@ -85,7 +85,7 @@ BLI_INLINE void DRW_vbo_request(GPUBatch *batch, GPUVertBuf **vbo) *vbo = GPU_vertbuf_calloc(); } if (batch != NULL) { - /* HACK we set vbos that may not yet be valid. */ + /* HACK we set VBO's that may not yet be valid. */ GPU_batch_vertbuf_add(batch, *vbo); } } diff --git a/source/blender/draw/intern/draw_instance_data.c b/source/blender/draw/intern/draw_instance_data.c index 449f2cd9606..e055192eb21 100644 --- a/source/blender/draw/intern/draw_instance_data.c +++ b/source/blender/draw/intern/draw_instance_data.c @@ -80,7 +80,7 @@ typedef struct DRWTempInstancingHandle { GPUBatch *batch; /** Batch containing instancing attributes. */ GPUBatch *instancer; - /** Callbuffer to be used instead of instancer . */ + /** Callbuffer to be used instead of instancer. */ GPUVertBuf *buf; /** Original non-instanced batch pointer. */ GPUBatch *geom; diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 6f5e041fa79..35072518b66 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -147,7 +147,7 @@ static bool drw_draw_show_annotation(void) * the draw manager is only used to draw the background. */ return false; default: - BLI_assert(""); + BLI_assert(0); return false; } } @@ -2253,7 +2253,7 @@ static void draw_select_framebuffer_depth_only_setup(const int size[2]) /* Must run after all instance datas have been added. */ void DRW_render_instance_buffer_finish(void) { - BLI_assert(!DST.buffer_finish_called && "DRW_render_instance_buffer_finish called twice!"); + BLI_assert_msg(!DST.buffer_finish_called, "DRW_render_instance_buffer_finish called twice!"); DST.buffer_finish_called = true; DRW_instance_buffer_finish(DST.idatalist); drw_resource_buffer_finish(DST.vmempool); diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index c4e8d0a980d..512c2775850 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -110,8 +110,8 @@ typedef struct DRWCullingState { /* Minimum max UBO size is 64KiB. We take the largest * UBO struct and alloc the max number. - * ((1 << 16) / sizeof(DRWObjectMatrix)) = 512 - * Keep in sync with common_view_lib.glsl */ + * `((1 << 16) / sizeof(DRWObjectMatrix)) = 512` + * Keep in sync with `common_view_lib.glsl`. */ #define DRW_RESOURCE_CHUNK_LEN 512 /** diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 90575f96059..86292c43d95 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -74,7 +74,7 @@ static void draw_call_sort(DRWCommand *array, DRWCommand *array_tmp, int array_l return; } } - /* Cumulate batch indices */ + /* Accumulate batch indices */ for (int i = 1; i < ARRAY_SIZE(idx); i++) { idx[i] += idx[i - 1]; } @@ -453,7 +453,7 @@ void DRW_shgroup_vertex_buffer(DRWShadingGroup *shgroup, { int location = GPU_shader_get_ssbo(shgroup->shader, name); if (location == -1) { - BLI_assert(false && "Unable to locate binding of shader storage buffer objects."); + BLI_assert_msg(0, "Unable to locate binding of shader storage buffer objects."); return; } drw_shgroup_uniform_create_ex( diff --git a/source/blender/draw/intern/draw_manager_text.c b/source/blender/draw/intern/draw_manager_text.c index 6c63838201e..265fdba66fd 100644 --- a/source/blender/draw/intern/draw_manager_text.c +++ b/source/blender/draw/intern/draw_manager_text.c @@ -266,7 +266,7 @@ void DRW_text_edit_mesh_measure_stats(ARegion *region, } const short edge_tex_sep = (short)((edge_tex_count - 1) * 5.0f * U.dpi_fac); - /* make the precision of the display value proportionate to the gridsize */ + /* Make the precision of the display value proportionate to the grid-size. */ if (grid <= 0.01f) { conv_float = "%.6g"; diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c b/source/blender/draw/intern/mesh_extractors/extract_mesh.c index e813f006351..53827dcc7d9 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_extractors.c +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.c @@ -29,7 +29,8 @@ #include "ED_uvedit.h" -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" + #include "draw_cache_impl.h" void *mesh_extract_buffer_get(const MeshExtract *extractor, MeshBufferCache *mbc) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_private.h b/source/blender/draw/intern/mesh_extractors/extract_mesh.h index 5f670bdc5ec..f24ccf1a028 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_private.h +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.h @@ -101,10 +101,12 @@ typedef struct MeshRenderData { float (*loop_normals)[3]; float (*poly_normals)[3]; int *lverts, *ledges; + struct { - int *tri; + int *tri_first_index; + int *mat_tri_len; int visible_tri_len; - } mat_offsets; + } poly_sorted; } MeshRenderData; BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx) @@ -240,20 +242,22 @@ typedef struct MeshExtract { /* draw_cache_extract_mesh_render_data.c */ MeshRenderData *mesh_render_data_create(Mesh *me, - MeshBufferExtractionCache *cache, const bool is_editmode, const bool is_paint_mode, const bool is_mode_active, const float obmat[4][4], const bool do_final, const bool do_uvedit, - const ToolSettings *ts, - const eMRIterType iter_type); + const ToolSettings *ts); void mesh_render_data_free(MeshRenderData *mr); void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_flag); -void mesh_render_data_update_mat_offsets(MeshRenderData *mr, - MeshBufferExtractionCache *cache, - const eMRDataType data_flag); +void mesh_render_data_update_loose_geom(MeshRenderData *mr, + MeshBufferExtractionCache *cache, + const eMRIterType iter_type, + const eMRDataType data_flag); +void mesh_render_data_update_polys_sorted(MeshRenderData *mr, + MeshBufferExtractionCache *cache, + const eMRDataType data_flag); void mesh_render_data_update_looptris(MeshRenderData *mr, const eMRIterType iter_type, const eMRDataType data_flag); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc index 2dff101c71f..5bd5f7adaa8 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc @@ -21,12 +21,12 @@ * \ingroup draw */ -#include "draw_cache_extract_mesh_private.h" - #include "BLI_vector.hh" #include "MEM_guardedalloc.h" +#include "extract_mesh.h" + namespace blender::draw { /* ---------------------------------------------------------------------- */ /** \name Extract Edit UV Triangles Indices diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc index c21725ffa32..0f41702a22c 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc @@ -21,12 +21,12 @@ * \ingroup draw */ -#include "draw_cache_extract_mesh_private.h" - #include "BLI_vector.hh" #include "MEM_guardedalloc.h" +#include "extract_mesh.h" + namespace blender::draw { /* ---------------------------------------------------------------------- */ /** \name Extract Face-dots Indices diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc index 2c2603af1b2..0096da9e56f 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc @@ -21,10 +21,10 @@ * \ingroup draw */ -#include "draw_cache_extract_mesh_private.h" - #include "MEM_guardedalloc.h" +#include "extract_mesh.h" + namespace blender::draw { /* ---------------------------------------------------------------------- */ @@ -237,7 +237,7 @@ constexpr MeshExtract create_extractor_lines_loose_only() { MeshExtract extractor = {nullptr}; extractor.init = extract_lines_loose_only_init; - extractor.data_type = MR_DATA_NONE; + extractor.data_type = MR_DATA_LOOSE_GEOM; extractor.data_size = 0; extractor.use_threading = false; extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.lines_loose); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc index bdb9af1faf3..7a37cf50264 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc @@ -21,13 +21,13 @@ * \ingroup draw */ -#include "draw_cache_extract_mesh_private.h" - #include "BLI_edgehash.h" #include "BLI_vector.hh" #include "MEM_guardedalloc.h" +#include "extract_mesh.h" + namespace blender::draw { /* ---------------------------------------------------------------------- */ diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc index 277f9d69c2f..5def7edb75a 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc @@ -21,14 +21,14 @@ * \ingroup draw */ -#include "draw_cache_extract_mesh_private.h" - #include "BLI_bitmap.h" #include "BLI_vector.hh" #include "atomic_ops.h" #include "MEM_guardedalloc.h" +#include "extract_mesh.h" + namespace blender::draw { /* ---------------------------------------------------------------------- */ /** \name Extract Paint Mask Line Indices diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc index cee0c224aab..cc1a19b8d26 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc @@ -21,12 +21,12 @@ * \ingroup draw */ -#include "draw_cache_extract_mesh_private.h" - #include "BLI_vector.hh" #include "MEM_guardedalloc.h" +#include "extract_mesh.h" + namespace blender::draw { /* ---------------------------------------------------------------------- */ diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc index 93f71f920eb..e346177af52 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc @@ -21,65 +21,76 @@ * \ingroup draw */ -#include "draw_cache_extract_mesh_private.h" - #include "MEM_guardedalloc.h" +#include "extract_mesh.h" + namespace blender::draw { +static void extract_tris_mat_task_reduce(void *_userdata_to, void *_userdata_from) +{ + GPUIndexBufBuilder *elb_to = static_cast<GPUIndexBufBuilder *>(_userdata_to); + GPUIndexBufBuilder *elb_from = static_cast<GPUIndexBufBuilder *>(_userdata_from); + GPU_indexbuf_join(elb_to, elb_from); +} + /* ---------------------------------------------------------------------- */ /** \name Extract Triangles Indices (multi material) * \{ */ -struct MeshExtract_Tri_Data { - GPUIndexBufBuilder elb; - const int *tri_mat_start; - int *tri_mat_end; -}; - static void extract_tris_init(const MeshRenderData *mr, struct MeshBatchCache *UNUSED(cache), void *UNUSED(ibo), void *tls_data) { - MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(tls_data); - data->tri_mat_start = mr->mat_offsets.tri; - data->tri_mat_end = static_cast<int *>(MEM_dupallocN(data->tri_mat_start)); - GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->mat_offsets.visible_tri_len, mr->loop_len); + GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(tls_data); + GPU_indexbuf_init(elb, GPU_PRIM_TRIS, mr->poly_sorted.visible_tri_len, mr->loop_len); } -static void extract_tris_iter_looptri_bm(const MeshRenderData *mr, - BMLoop **elt, - const int UNUSED(elt_index), - void *_data) +static void extract_tris_iter_poly_bm(const MeshRenderData *mr, + const BMFace *f, + const int f_index, + void *_data) { - MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(_data); - const int mat_last = mr->mat_len - 1; + int tri_first_index = mr->poly_sorted.tri_first_index[f_index]; + if (tri_first_index == -1) { + return; + } - if (!BM_elem_flag_test(elt[0]->f, BM_ELEM_HIDDEN)) { - int *mat_tri_ofs = data->tri_mat_end; - const int mat = min_ii(elt[0]->f->mat_nr, mat_last); - GPU_indexbuf_set_tri_verts(&data->elb, - mat_tri_ofs[mat]++, + GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data); + int tri_first_index_real = poly_to_tri_count(f_index, BM_elem_index_get(f->l_first)); + + struct BMLoop *(*looptris)[3] = mr->edit_bmesh->looptris; + int tri_len = f->len - 2; + for (int offs = 0; offs < tri_len; offs++) { + BMLoop **elt = looptris[tri_first_index_real + offs]; + int tri_index = tri_first_index + offs; + GPU_indexbuf_set_tri_verts(elb, + tri_index, BM_elem_index_get(elt[0]), BM_elem_index_get(elt[1]), BM_elem_index_get(elt[2])); } } -static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr, - const MLoopTri *mlt, - const int UNUSED(elt_index), - void *_data) +static void extract_tris_iter_poly_mesh(const MeshRenderData *mr, + const MPoly *mp, + const int mp_index, + void *_data) { - MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(_data); - const int mat_last = mr->mat_len - 1; - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - int *mat_tri_ofs = data->tri_mat_end; - const int mat = min_ii(mp->mat_nr, mat_last); - GPU_indexbuf_set_tri_verts( - &data->elb, mat_tri_ofs[mat]++, mlt->tri[0], mlt->tri[1], mlt->tri[2]); + int tri_first_index = mr->poly_sorted.tri_first_index[mp_index]; + if (tri_first_index == -1) { + return; + } + + GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data); + int tri_first_index_real = poly_to_tri_count(mp_index, mp->loopstart); + + int tri_len = mp->totloop - 2; + for (int offs = 0; offs < tri_len; offs++) { + const MLoopTri *mlt = &mr->mlooptri[tri_first_index_real + offs]; + int tri_index = tri_first_index + offs; + GPU_indexbuf_set_tri_verts(elb, tri_index, mlt->tri[0], mlt->tri[1], mlt->tri[2]); } } @@ -89,40 +100,41 @@ static void extract_tris_finish(const MeshRenderData *mr, void *_data) { GPUIndexBuf *ibo = static_cast<GPUIndexBuf *>(buf); - MeshExtract_Tri_Data *data = static_cast<MeshExtract_Tri_Data *>(_data); - GPU_indexbuf_build_in_place(&data->elb, ibo); + GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data); + GPU_indexbuf_build_in_place(elb, ibo); /* Create ibo sub-ranges. Always do this to avoid error when the standard surface batch * is created before the surfaces-per-material. */ if (mr->use_final_mesh && cache->final.tris_per_mat) { MeshBufferCache *mbc_final = &cache->final; + int mat_start = 0; for (int i = 0; i < mr->mat_len; i++) { /* These IBOs have not been queried yet but we create them just in case they are needed * later since they are not tracked by mesh_buffer_cache_create_requested(). */ if (mbc_final->tris_per_mat[i] == nullptr) { mbc_final->tris_per_mat[i] = GPU_indexbuf_calloc(); } + const int mat_tri_len = mr->poly_sorted.mat_tri_len[i]; /* Multiply by 3 because these are triangle indices. */ - const int mat_start = data->tri_mat_start[i]; - const int mat_end = data->tri_mat_end[i]; const int start = mat_start * 3; - const int len = (mat_end - mat_start) * 3; + const int len = mat_tri_len * 3; GPU_indexbuf_create_subrange_in_place(mbc_final->tris_per_mat[i], ibo, start, len); + mat_start += mat_tri_len; } } - MEM_freeN(data->tri_mat_end); } constexpr MeshExtract create_extractor_tris() { MeshExtract extractor = {nullptr}; extractor.init = extract_tris_init; - extractor.iter_looptri_bm = extract_tris_iter_looptri_bm; - extractor.iter_looptri_mesh = extract_tris_iter_looptri_mesh; + extractor.iter_poly_bm = extract_tris_iter_poly_bm; + extractor.iter_poly_mesh = extract_tris_iter_poly_mesh; + extractor.task_reduce = extract_tris_mat_task_reduce; extractor.finish = extract_tris_finish; - extractor.data_type = MR_DATA_MAT_OFFSETS; - extractor.data_size = sizeof(MeshExtract_Tri_Data); - extractor.use_threading = false; + extractor.data_type = MR_DATA_LOOPTRI | MR_DATA_POLYS_SORTED; + extractor.data_size = sizeof(GPUIndexBufBuilder); + extractor.use_threading = true; extractor.mesh_buffer_offset = offsetof(MeshBufferCache, ibo.tris); return extractor; } @@ -174,13 +186,6 @@ static void extract_tris_single_mat_iter_looptri_mesh(const MeshRenderData *mr, } } -static void extract_tris_single_mat_task_reduce(void *_userdata_to, void *_userdata_from) -{ - GPUIndexBufBuilder *elb_to = static_cast<GPUIndexBufBuilder *>(_userdata_to); - GPUIndexBufBuilder *elb_from = static_cast<GPUIndexBufBuilder *>(_userdata_from); - GPU_indexbuf_join(elb_to, elb_from); -} - static void extract_tris_single_mat_finish(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf, @@ -213,7 +218,7 @@ constexpr MeshExtract create_extractor_tris_single_mat() extractor.init = extract_tris_single_mat_init; extractor.iter_looptri_bm = extract_tris_single_mat_iter_looptri_bm; extractor.iter_looptri_mesh = extract_tris_single_mat_iter_looptri_mesh; - extractor.task_reduce = extract_tris_single_mat_task_reduce; + extractor.task_reduce = extract_tris_mat_task_reduce; extractor.finish = extract_tris_single_mat_finish; extractor.data_type = MR_DATA_NONE; extractor.data_size = sizeof(GPUIndexBufBuilder); diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc index 1bc4c7e330f..302616d4da9 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc @@ -25,7 +25,7 @@ #include "GPU_capabilities.h" -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edit_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edit_data.cc index ff250a30ec4..a7efb9c8a1b 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edit_data.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edit_data.cc @@ -21,7 +21,8 @@ * \ingroup draw */ -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" + #include "draw_cache_impl.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc index aa58266d56b..0378aadabd0 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc @@ -21,7 +21,8 @@ * \ingroup draw */ -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" + #include "draw_cache_impl.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc index 1d62637d172..a60c0182e89 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc @@ -25,7 +25,7 @@ #include "BKE_mesh.h" -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc index 16814653408..d79ac493c33 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc @@ -25,7 +25,7 @@ #include "BKE_mesh.h" -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc index 5a988c73a7e..b7182d1b60f 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_edituv_data.cc @@ -21,7 +21,8 @@ * \ingroup draw */ -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" + #include "draw_cache_impl.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc index fb9d34e7733..5e4ad54f7b6 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc @@ -21,7 +21,7 @@ * \ingroup draw */ -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc index c4706c412c6..e765fb8a8bf 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_pos.cc @@ -21,7 +21,7 @@ * \ingroup draw */ -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc index 0289fd63a30..042a0d2debe 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_uv.cc @@ -21,7 +21,7 @@ * \ingroup draw */ -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc index b942068352b..8344a615cbe 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc @@ -21,7 +21,7 @@ * \ingroup draw */ -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc index b734061b76a..075d54e268e 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc @@ -30,7 +30,7 @@ #include "BKE_editmesh_bvh.h" #include "BKE_editmesh_cache.h" -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc index 80b73cac678..269c0343e32 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_orco.cc @@ -21,7 +21,7 @@ * \ingroup draw */ -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc index 2ac926dd257..b8e5dcb613f 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc @@ -23,7 +23,7 @@ #include "MEM_guardedalloc.h" -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc index 7b36a009419..dbb1e1f880b 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc @@ -27,7 +27,7 @@ #include "BKE_paint.h" -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc index ac44e97f229..cd8d46901c5 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_select_idx.cc @@ -21,7 +21,7 @@ * \ingroup draw */ -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc index d7a01ee607f..ffca01d00dd 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc @@ -21,7 +21,7 @@ * \ingroup draw */ -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc index f251141c442..8f36bfdf1ef 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_tan.cc @@ -30,7 +30,7 @@ #include "BKE_mesh.h" #include "BKE_mesh_tangent.h" -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc index 0f3c2483296..013da7d674a 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc @@ -23,7 +23,7 @@ #include "BLI_string.h" -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc index 2f1cff08796..d810acfb617 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc @@ -25,7 +25,7 @@ #include "BLI_string.h" -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" namespace blender::draw { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc index aae266eadce..c547a453aeb 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc @@ -25,7 +25,7 @@ #include "BKE_deform.h" -#include "draw_cache_extract_mesh_private.h" +#include "extract_mesh.h" namespace blender::draw { diff --git a/source/blender/editors/animation/CMakeLists.txt b/source/blender/editors/animation/CMakeLists.txt index f50a5ffbb5e..7a53b54b5a4 100644 --- a/source/blender/editors/animation/CMakeLists.txt +++ b/source/blender/editors/animation/CMakeLists.txt @@ -47,6 +47,7 @@ set(SRC keyframes_draw.c keyframes_edit.c keyframes_general.c + keyframes_keylist.c keyframing.c keyingsets.c time_scrub_ui.c diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 8f8c1c067d4..afbd9b2c92d 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -2026,7 +2026,7 @@ static const EnumPropertyItem prop_animchannel_settings_types[] = { * \param mode: eAnimChannels_SetFlag. * \param onlysel: only selected channels get the flag set. * - * TODO: enable a setting which turns flushing on/off?. + * TODO: enable a setting which turns flushing on/off? */ static void setflag_anim_channels(bAnimContext *ac, eAnimChannel_Settings setting, @@ -2683,8 +2683,9 @@ static void box_select_anim_channels(bAnimContext *ac, rcti *rect, short selectm /* loop over data, doing box select */ for (ale = anim_data.first; ale; ale = ale->next) { float ymin; - /* Skip grease pencil datablock. Only use grease pencil layers. */ + if (ale->type == ANIMTYPE_GPDATABLOCK) { + ymax -= ACHANNEL_STEP(ac); continue; } diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index 2fcd59a1bbe..baf8adf28d0 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -48,6 +48,7 @@ #include "ED_anim_api.h" #include "ED_keyframes_draw.h" #include "ED_keyframes_edit.h" +#include "ED_keyframes_keylist.h" #include "RNA_access.h" diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 9d998326b4d..227af598f53 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -123,7 +123,7 @@ ListBase *ED_animcontext_get_markers(const bAnimContext *ac) * so don't assume anything. * \param scene: Current scene (for getting current frame) * \param mode: (TfmMode) transform mode that this transform is for - * \param value: From the transform code, this is ``t->vec[0]`` + * \param value: From the transform code, this is `t->vec[0]` * (which is delta transform for grab/extend, and scale factor for scale) * \param side: (B/L/R) for 'extend' functionality, which side of current frame to use */ diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c index 51a897600e1..bddd5dbff55 100644 --- a/source/blender/editors/animation/anim_motion_paths.c +++ b/source/blender/editors/animation/anim_motion_paths.c @@ -43,7 +43,7 @@ #include "GPU_vertex_buffer.h" #include "ED_anim_api.h" -#include "ED_keyframes_draw.h" +#include "ED_keyframes_keylist.h" #include "CLG_log.h" diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 06107b6fee6..4f512c9d7ca 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -24,28 +24,17 @@ /* System includes ----------------------------------------------------- */ #include <float.h> -#include <math.h> -#include <stdlib.h> -#include <string.h> - -#include "MEM_guardedalloc.h" #include "BLI_dlrbTree.h" #include "BLI_listbase.h" -#include "BLI_math.h" #include "BLI_rect.h" -#include "BLI_utildefines.h" #include "DNA_anim_types.h" -#include "DNA_brush_types.h" -#include "DNA_cachefile_types.h" #include "DNA_gpencil_types.h" #include "DNA_mask_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" -#include "BKE_fcurve.h" - #include "GPU_immediate.h" #include "GPU_state.h" @@ -55,498 +44,7 @@ #include "ED_anim_api.h" #include "ED_keyframes_draw.h" - -/* *************************** Keyframe Processing *************************** */ - -/* ActKeyColumns (Keyframe Columns) ------------------------------------------ */ - -BLI_INLINE bool is_cfra_eq(float a, float b) -{ - return IS_EQT(a, b, BEZT_BINARYSEARCH_THRESH); -} - -BLI_INLINE bool is_cfra_lt(float a, float b) -{ - return (b - a) > BEZT_BINARYSEARCH_THRESH; -} - -/* Comparator callback used for ActKeyColumns and cframe float-value pointer */ -/* NOTE: this is exported to other modules that use the ActKeyColumns for finding keyframes */ -short compare_ak_cfraPtr(void *node, void *data) -{ - ActKeyColumn *ak = (ActKeyColumn *)node; - const float *cframe = data; - float val = *cframe; - - if (is_cfra_eq(val, ak->cfra)) { - return 0; - } - - if (val < ak->cfra) { - return -1; - } - return 1; -} - -/* --------------- */ - -/* Set of references to three logically adjacent keys. */ -typedef struct BezTripleChain { - /* Current keyframe. */ - BezTriple *cur; - - /* Logical neighbors. May be NULL. */ - BezTriple *prev, *next; -} BezTripleChain; - -/* Categorize the interpolation & handle type of the keyframe. */ -static eKeyframeHandleDrawOpts bezt_handle_type(BezTriple *bezt) -{ - if (bezt->h1 == HD_AUTO_ANIM && bezt->h2 == HD_AUTO_ANIM) { - return KEYFRAME_HANDLE_AUTO_CLAMP; - } - if (ELEM(bezt->h1, HD_AUTO_ANIM, HD_AUTO) && ELEM(bezt->h2, HD_AUTO_ANIM, HD_AUTO)) { - return KEYFRAME_HANDLE_AUTO; - } - if (bezt->h1 == HD_VECT && bezt->h2 == HD_VECT) { - return KEYFRAME_HANDLE_VECTOR; - } - if (ELEM(HD_FREE, bezt->h1, bezt->h2)) { - return KEYFRAME_HANDLE_FREE; - } - return KEYFRAME_HANDLE_ALIGNED; -} - -/* Determine if the keyframe is an extreme by comparing with neighbors. - * Ends of fixed-value sections and of the whole curve are also marked. - */ -static eKeyframeExtremeDrawOpts bezt_extreme_type(BezTripleChain *chain) -{ - if (chain->prev == NULL && chain->next == NULL) { - return KEYFRAME_EXTREME_NONE; - } - - /* Keyframe values for the current one and neighbors. */ - float cur_y = chain->cur->vec[1][1]; - float prev_y = cur_y, next_y = cur_y; - - if (chain->prev && !IS_EQF(cur_y, chain->prev->vec[1][1])) { - prev_y = chain->prev->vec[1][1]; - } - if (chain->next && !IS_EQF(cur_y, chain->next->vec[1][1])) { - next_y = chain->next->vec[1][1]; - } - - /* Static hold. */ - if (prev_y == cur_y && next_y == cur_y) { - return KEYFRAME_EXTREME_FLAT; - } - - /* Middle of an incline. */ - if ((prev_y < cur_y && next_y > cur_y) || (prev_y > cur_y && next_y < cur_y)) { - return KEYFRAME_EXTREME_NONE; - } - - /* Bezier handle values for the overshoot check. */ - bool l_bezier = chain->prev && chain->prev->ipo == BEZT_IPO_BEZ; - bool r_bezier = chain->next && chain->cur->ipo == BEZT_IPO_BEZ; - float handle_l = l_bezier ? chain->cur->vec[0][1] : cur_y; - float handle_r = r_bezier ? chain->cur->vec[2][1] : cur_y; - - /* Detect extremes. One of the neighbors is allowed to be equal to current. */ - if (prev_y < cur_y || next_y < cur_y) { - bool is_overshoot = (handle_l > cur_y || handle_r > cur_y); - - return KEYFRAME_EXTREME_MAX | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0); - } - - if (prev_y > cur_y || next_y > cur_y) { - bool is_overshoot = (handle_l < cur_y || handle_r < cur_y); - - return KEYFRAME_EXTREME_MIN | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0); - } - - return KEYFRAME_EXTREME_NONE; -} - -/* Comparator callback used for ActKeyColumns and BezTripleChain */ -static short compare_ak_bezt(void *node, void *data) -{ - BezTripleChain *chain = data; - - return compare_ak_cfraPtr(node, &chain->cur->vec[1][0]); -} - -/* New node callback used for building ActKeyColumns from BezTripleChain */ -static DLRBT_Node *nalloc_ak_bezt(void *data) -{ - ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"); - BezTripleChain *chain = data; - BezTriple *bezt = chain->cur; - - /* store settings based on state of BezTriple */ - ak->cfra = bezt->vec[1][0]; - ak->sel = BEZT_ISSEL_ANY(bezt) ? SELECT : 0; - ak->key_type = BEZKEYTYPE(bezt); - ak->handle_type = bezt_handle_type(bezt); - ak->extreme_type = bezt_extreme_type(chain); - - /* count keyframes in this column */ - ak->totkey = 1; - - return (DLRBT_Node *)ak; -} - -/* Node updater callback used for building ActKeyColumns from BezTripleChain */ -static void nupdate_ak_bezt(void *node, void *data) -{ - ActKeyColumn *ak = node; - BezTripleChain *chain = data; - BezTriple *bezt = chain->cur; - - /* set selection status and 'touched' status */ - if (BEZT_ISSEL_ANY(bezt)) { - ak->sel = SELECT; - } - - /* count keyframes in this column */ - ak->totkey++; - - /* For keyframe type, 'proper' keyframes have priority over breakdowns - * (and other types for now). */ - if (BEZKEYTYPE(bezt) == BEZT_KEYTYPE_KEYFRAME) { - ak->key_type = BEZT_KEYTYPE_KEYFRAME; - } - - /* For interpolation type, select the highest value (enum is sorted). */ - ak->handle_type = MAX2(ak->handle_type, bezt_handle_type(bezt)); - - /* For extremes, detect when combining different states. */ - char new_extreme = bezt_extreme_type(chain); - - if (new_extreme != ak->extreme_type) { - /* Replace the flat status without adding mixed. */ - if (ak->extreme_type == KEYFRAME_EXTREME_FLAT) { - ak->extreme_type = new_extreme; - } - else if (new_extreme != KEYFRAME_EXTREME_FLAT) { - ak->extreme_type |= (new_extreme | KEYFRAME_EXTREME_MIXED); - } - } -} - -/* ......... */ - -/* Comparator callback used for ActKeyColumns and GPencil frame */ -static short compare_ak_gpframe(void *node, void *data) -{ - bGPDframe *gpf = (bGPDframe *)data; - - float frame = gpf->framenum; - return compare_ak_cfraPtr(node, &frame); -} - -/* New node callback used for building ActKeyColumns from GPencil frames */ -static DLRBT_Node *nalloc_ak_gpframe(void *data) -{ - ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF"); - bGPDframe *gpf = (bGPDframe *)data; - - /* store settings based on state of BezTriple */ - ak->cfra = gpf->framenum; - ak->sel = (gpf->flag & GP_FRAME_SELECT) ? SELECT : 0; - ak->key_type = gpf->key_type; - - /* count keyframes in this column */ - ak->totkey = 1; - /* Set as visible block. */ - ak->totblock = 1; - ak->block.sel = ak->sel; - ak->block.flag |= ACTKEYBLOCK_FLAG_GPENCIL; - - return (DLRBT_Node *)ak; -} - -/* Node updater callback used for building ActKeyColumns from GPencil frames */ -static void nupdate_ak_gpframe(void *node, void *data) -{ - ActKeyColumn *ak = (ActKeyColumn *)node; - bGPDframe *gpf = (bGPDframe *)data; - - /* set selection status and 'touched' status */ - if (gpf->flag & GP_FRAME_SELECT) { - ak->sel = SELECT; - } - - /* count keyframes in this column */ - ak->totkey++; - - /* for keyframe type, 'proper' keyframes have priority over breakdowns - * (and other types for now). */ - if (gpf->key_type == BEZT_KEYTYPE_KEYFRAME) { - ak->key_type = BEZT_KEYTYPE_KEYFRAME; - } -} - -/* ......... */ - -/* Comparator callback used for ActKeyColumns and GPencil frame */ -static short compare_ak_masklayshape(void *node, void *data) -{ - MaskLayerShape *masklay_shape = (MaskLayerShape *)data; - - float frame = masklay_shape->frame; - return compare_ak_cfraPtr(node, &frame); -} - -/* New node callback used for building ActKeyColumns from GPencil frames */ -static DLRBT_Node *nalloc_ak_masklayshape(void *data) -{ - ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF"); - MaskLayerShape *masklay_shape = (MaskLayerShape *)data; - - /* store settings based on state of BezTriple */ - ak->cfra = masklay_shape->frame; - ak->sel = (masklay_shape->flag & MASK_SHAPE_SELECT) ? SELECT : 0; - - /* count keyframes in this column */ - ak->totkey = 1; - - return (DLRBT_Node *)ak; -} - -/* Node updater callback used for building ActKeyColumns from GPencil frames */ -static void nupdate_ak_masklayshape(void *node, void *data) -{ - ActKeyColumn *ak = (ActKeyColumn *)node; - MaskLayerShape *masklay_shape = (MaskLayerShape *)data; - - /* set selection status and 'touched' status */ - if (masklay_shape->flag & MASK_SHAPE_SELECT) { - ak->sel = SELECT; - } - - /* count keyframes in this column */ - ak->totkey++; -} - -/* --------------- */ - -/* Add the given BezTriple to the given 'list' of Keyframes */ -static void add_bezt_to_keycolumns_list(DLRBT_Tree *keys, BezTripleChain *bezt) -{ - if (ELEM(NULL, keys, bezt)) { - return; - } - - BLI_dlrbTree_add(keys, compare_ak_bezt, nalloc_ak_bezt, nupdate_ak_bezt, bezt); -} - -/* Add the given GPencil Frame to the given 'list' of Keyframes */ -static void add_gpframe_to_keycolumns_list(DLRBT_Tree *keys, bGPDframe *gpf) -{ - if (ELEM(NULL, keys, gpf)) { - return; - } - - BLI_dlrbTree_add(keys, compare_ak_gpframe, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf); -} - -/* Add the given MaskLayerShape Frame to the given 'list' of Keyframes */ -static void add_masklay_to_keycolumns_list(DLRBT_Tree *keys, MaskLayerShape *masklay_shape) -{ - if (ELEM(NULL, keys, masklay_shape)) { - return; - } - - BLI_dlrbTree_add(keys, - compare_ak_masklayshape, - nalloc_ak_masklayshape, - nupdate_ak_masklayshape, - masklay_shape); -} - -/* ActKeyBlocks (Long Keyframes) ------------------------------------------ */ - -static const ActKeyBlockInfo dummy_keyblock = {0}; - -static void compute_keyblock_data(ActKeyBlockInfo *info, BezTriple *prev, BezTriple *beztn) -{ - memset(info, 0, sizeof(ActKeyBlockInfo)); - - if (BEZKEYTYPE(beztn) == BEZT_KEYTYPE_MOVEHOLD) { - /* Animator tagged a "moving hold" - * - Previous key must also be tagged as a moving hold, otherwise - * we're just dealing with the first of a pair, and we don't - * want to be creating any phantom holds... - */ - if (BEZKEYTYPE(prev) == BEZT_KEYTYPE_MOVEHOLD) { - info->flag |= ACTKEYBLOCK_FLAG_MOVING_HOLD | ACTKEYBLOCK_FLAG_ANY_HOLD; - } - } - - /* Check for same values... - * - Handles must have same central value as each other - * - Handles which control that section of the curve must be constant - */ - if (IS_EQF(beztn->vec[1][1], prev->vec[1][1])) { - bool hold; - - /* Only check handles in case of actual bezier interpolation. */ - if (prev->ipo == BEZT_IPO_BEZ) { - hold = IS_EQF(beztn->vec[1][1], beztn->vec[0][1]) && - IS_EQF(prev->vec[1][1], prev->vec[2][1]); - } - /* This interpolation type induces movement even between identical keys. */ - else { - hold = !ELEM(prev->ipo, BEZT_IPO_ELASTIC); - } - - if (hold) { - info->flag |= ACTKEYBLOCK_FLAG_STATIC_HOLD | ACTKEYBLOCK_FLAG_ANY_HOLD; - } - } - - /* Remember non-bezier interpolation info. */ - if (prev->ipo != BEZT_IPO_BEZ) { - info->flag |= ACTKEYBLOCK_FLAG_NON_BEZIER; - } - - info->sel = BEZT_ISSEL_ANY(prev) || BEZT_ISSEL_ANY(beztn); -} - -static void add_keyblock_info(ActKeyColumn *col, const ActKeyBlockInfo *block) -{ - /* New curve and block. */ - if (col->totcurve <= 1 && col->totblock == 0) { - memcpy(&col->block, block, sizeof(ActKeyBlockInfo)); - } - /* Existing curve. */ - else { - col->block.conflict |= (col->block.flag ^ block->flag); - col->block.flag |= block->flag; - col->block.sel |= block->sel; - } - - if (block->flag) { - col->totblock++; - } -} - -static void add_bezt_to_keyblocks_list(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len) -{ - ActKeyColumn *col = keys->first; - - if (bezt && bezt_len >= 2) { - ActKeyBlockInfo block; - - /* Find the first key column while inserting dummy blocks. */ - for (; col != NULL && is_cfra_lt(col->cfra, bezt[0].vec[1][0]); col = col->next) { - add_keyblock_info(col, &dummy_keyblock); - } - - BLI_assert(col != NULL); - - /* Insert real blocks. */ - for (int v = 1; col != NULL && v < bezt_len; v++, bezt++) { - /* Wrong order of bezier keys: resync position. */ - if (is_cfra_lt(bezt[1].vec[1][0], bezt[0].vec[1][0])) { - /* Backtrack to find the right location. */ - if (is_cfra_lt(bezt[1].vec[1][0], col->cfra)) { - ActKeyColumn *newcol = (ActKeyColumn *)BLI_dlrbTree_search_exact( - keys, compare_ak_cfraPtr, &bezt[1].vec[1][0]); - - if (newcol != NULL) { - col = newcol; - - /* The previous keyblock is garbage too. */ - if (col->prev != NULL) { - add_keyblock_info(col->prev, &dummy_keyblock); - } - } - else { - BLI_assert(false); - } - } - - continue; - } - - /* Normal sequence */ - BLI_assert(is_cfra_eq(col->cfra, bezt[0].vec[1][0])); - - compute_keyblock_data(&block, bezt, bezt + 1); - - for (; col != NULL && is_cfra_lt(col->cfra, bezt[1].vec[1][0]); col = col->next) { - add_keyblock_info(col, &block); - } - - BLI_assert(col != NULL); - } - } - - /* Insert dummy blocks at the end. */ - for (; col != NULL; col = col->next) { - add_keyblock_info(col, &dummy_keyblock); - } -} - -/* Walk through columns and propagate blocks and totcurve. - * - * This must be called even by animation sources that don't generate - * keyblocks to keep the data structure consistent after adding columns. - */ -static void update_keyblocks(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len) -{ - /* Recompute the prev/next linked list. */ - BLI_dlrbTree_linkedlist_sync(keys); - - /* Find the curve count */ - int max_curve = 0; - - LISTBASE_FOREACH (ActKeyColumn *, col, keys) { - max_curve = MAX2(max_curve, col->totcurve); - } - - /* Propagate blocks to inserted keys */ - ActKeyColumn *prev_ready = NULL; - - LISTBASE_FOREACH (ActKeyColumn *, col, keys) { - /* Pre-existing column. */ - if (col->totcurve > 0) { - prev_ready = col; - } - /* Newly inserted column, so copy block data from previous. */ - else if (prev_ready != NULL) { - col->totblock = prev_ready->totblock; - memcpy(&col->block, &prev_ready->block, sizeof(ActKeyBlockInfo)); - } - - col->totcurve = max_curve + 1; - } - - /* Add blocks on top */ - add_bezt_to_keyblocks_list(keys, bezt, bezt_len); -} - -/* --------- */ - -bool actkeyblock_is_valid(ActKeyColumn *ac) -{ - return ac != NULL && ac->next != NULL && ac->totblock > 0; -} - -/* Checks if ActKeyBlock should exist... */ -int actkeyblock_get_valid_hold(ActKeyColumn *ac) -{ - /* check that block is valid */ - if (!actkeyblock_is_valid(ac)) { - return 0; - } - - const int hold_mask = (ACTKEYBLOCK_FLAG_ANY_HOLD | ACTKEYBLOCK_FLAG_STATIC_HOLD); - return (ac->block.flag & ~ac->block.conflict) & hold_mask; -} +#include "ED_keyframes_keylist.h" /* *************************** Keyframe Drawing *************************** */ @@ -1029,257 +527,3 @@ void draw_masklay_channel(View2D *v2d, BLI_dlrbTree_free(&keys); } - -/* *************************** Keyframe List Conversions *************************** */ - -void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys, int saction_flag) -{ - if (ac) { - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - int filter; - - /* get F-Curves to take keyframes from */ - filter = ANIMFILTER_DATA_VISIBLE; - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - - /* loop through each F-Curve, grabbing the keyframes */ - for (ale = anim_data.first; ale; ale = ale->next) { - /* Why not use all #eAnim_KeyType here? - * All of the other key types are actually "summaries" themselves, - * and will just end up duplicating stuff that comes up through - * standard filtering of just F-Curves. Given the way that these work, - * there isn't really any benefit at all from including them. - Aligorith */ - - switch (ale->datatype) { - case ALE_FCURVE: - fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); - break; - case ALE_MASKLAY: - mask_to_keylist(ac->ads, ale->data, keys); - break; - case ALE_GPFRAME: - gpl_to_keylist(ac->ads, ale->data, keys); - break; - default: - // printf("%s: datatype %d unhandled\n", __func__, ale->datatype); - break; - } - } - - ANIM_animdata_freelist(&anim_data); - } -} - -void scene_to_keylist(bDopeSheet *ads, Scene *sce, DLRBT_Tree *keys, int saction_flag) -{ - bAnimContext ac = {NULL}; - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - int filter; - - bAnimListElem dummychan = {NULL}; - - if (sce == NULL) { - return; - } - - /* create a dummy wrapper data to work with */ - dummychan.type = ANIMTYPE_SCENE; - dummychan.data = sce; - dummychan.id = &sce->id; - dummychan.adt = sce->adt; - - ac.ads = ads; - ac.data = &dummychan; - ac.datatype = ANIMCONT_CHANNEL; - - /* get F-Curves to take keyframes from */ - filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ - ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - - /* loop through each F-Curve, grabbing the keyframes */ - for (ale = anim_data.first; ale; ale = ale->next) { - fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); - } - - ANIM_animdata_freelist(&anim_data); -} - -void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, int saction_flag) -{ - bAnimContext ac = {NULL}; - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - int filter; - - bAnimListElem dummychan = {NULL}; - Base dummybase = {NULL}; - - if (ob == NULL) { - return; - } - - /* create a dummy wrapper data to work with */ - dummybase.object = ob; - - dummychan.type = ANIMTYPE_OBJECT; - dummychan.data = &dummybase; - dummychan.id = &ob->id; - dummychan.adt = ob->adt; - - ac.ads = ads; - ac.data = &dummychan; - ac.datatype = ANIMCONT_CHANNEL; - - /* get F-Curves to take keyframes from */ - filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ - ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - - /* loop through each F-Curve, grabbing the keyframes */ - for (ale = anim_data.first; ale; ale = ale->next) { - fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); - } - - ANIM_animdata_freelist(&anim_data); -} - -void cachefile_to_keylist(bDopeSheet *ads, - CacheFile *cache_file, - DLRBT_Tree *keys, - int saction_flag) -{ - if (cache_file == NULL) { - return; - } - - /* create a dummy wrapper data to work with */ - bAnimListElem dummychan = {NULL}; - dummychan.type = ANIMTYPE_DSCACHEFILE; - dummychan.data = cache_file; - dummychan.id = &cache_file->id; - dummychan.adt = cache_file->adt; - - bAnimContext ac = {NULL}; - ac.ads = ads; - ac.data = &dummychan; - ac.datatype = ANIMCONT_CHANNEL; - - /* get F-Curves to take keyframes from */ - ListBase anim_data = {NULL, NULL}; - int filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ - ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); - - /* loop through each F-Curve, grabbing the keyframes */ - LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { - fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); - } - - ANIM_animdata_freelist(&anim_data); -} - -void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, int saction_flag) -{ - if (fcu && fcu->totvert && fcu->bezt) { - /* apply NLA-mapping (if applicable) */ - if (adt) { - ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0); - } - - /* Check if the curve is cyclic. */ - bool is_cyclic = BKE_fcurve_is_cyclic(fcu) && (fcu->totvert >= 2); - bool do_extremes = (saction_flag & SACTION_SHOW_EXTREMES) != 0; - - /* loop through beztriples, making ActKeysColumns */ - BezTripleChain chain = {0}; - - for (int v = 0; v < fcu->totvert; v++) { - chain.cur = &fcu->bezt[v]; - - /* Neighbor keys, accounting for being cyclic. */ - if (do_extremes) { - chain.prev = (v > 0) ? &fcu->bezt[v - 1] : is_cyclic ? &fcu->bezt[fcu->totvert - 2] : NULL; - chain.next = (v + 1 < fcu->totvert) ? &fcu->bezt[v + 1] : is_cyclic ? &fcu->bezt[1] : NULL; - } - - add_bezt_to_keycolumns_list(keys, &chain); - } - - /* Update keyblocks. */ - update_keyblocks(keys, fcu->bezt, fcu->totvert); - - /* unapply NLA-mapping if applicable */ - if (adt) { - ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0); - } - } -} - -void agroup_to_keylist(AnimData *adt, bActionGroup *agrp, DLRBT_Tree *keys, int saction_flag) -{ - FCurve *fcu; - - if (agrp) { - /* loop through F-Curves */ - for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next) { - fcurve_to_keylist(adt, fcu, keys, saction_flag); - } - } -} - -void action_to_keylist(AnimData *adt, bAction *act, DLRBT_Tree *keys, int saction_flag) -{ - FCurve *fcu; - - if (act) { - /* loop through F-Curves */ - for (fcu = act->curves.first; fcu; fcu = fcu->next) { - fcurve_to_keylist(adt, fcu, keys, saction_flag); - } - } -} - -void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, DLRBT_Tree *keys, const bool active) -{ - bGPDlayer *gpl; - - if (gpd && keys) { - /* for now, just aggregate out all the frames, but only for visible layers */ - for (gpl = gpd->layers.last; gpl; gpl = gpl->prev) { - if ((gpl->flag & GP_LAYER_HIDE) == 0) { - if ((!active) || ((active) && (gpl->flag & GP_LAYER_SELECT))) { - gpl_to_keylist(ads, gpl, keys); - } - } - } - } -} - -void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, DLRBT_Tree *keys) -{ - bGPDframe *gpf; - - if (gpl && keys) { - /* Although the frames should already be in an ordered list, - * they are not suitable for displaying yet. */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - add_gpframe_to_keycolumns_list(keys, gpf); - } - - update_keyblocks(keys, NULL, 0); - } -} - -void mask_to_keylist(bDopeSheet *UNUSED(ads), MaskLayer *masklay, DLRBT_Tree *keys) -{ - MaskLayerShape *masklay_shape; - - if (masklay && keys) { - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape->next) { - add_masklay_to_keycolumns_list(keys, masklay_shape); - } - - update_keyblocks(keys, NULL, 0); - } -} diff --git a/source/blender/editors/animation/keyframes_keylist.c b/source/blender/editors/animation/keyframes_keylist.c new file mode 100644 index 00000000000..47ed2b56300 --- /dev/null +++ b/source/blender/editors/animation/keyframes_keylist.c @@ -0,0 +1,793 @@ +/* + * 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) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + */ + +/** \file + * \ingroup edanimation + */ + +/* System includes ----------------------------------------------------- */ + +#include <float.h> +#include <math.h> +#include <stdlib.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_dlrbTree.h" +#include "BLI_listbase.h" +#include "BLI_utildefines.h" + +#include "DNA_anim_types.h" +#include "DNA_cachefile_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_mask_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_fcurve.h" + +#include "ED_anim_api.h" +#include "ED_keyframes_keylist.h" + +/* *************************** Keyframe Processing *************************** */ + +/* ActKeyColumns (Keyframe Columns) ------------------------------------------ */ + +BLI_INLINE bool is_cfra_eq(float a, float b) +{ + return IS_EQT(a, b, BEZT_BINARYSEARCH_THRESH); +} + +BLI_INLINE bool is_cfra_lt(float a, float b) +{ + return (b - a) > BEZT_BINARYSEARCH_THRESH; +} + +/* Comparator callback used for ActKeyColumns and cframe float-value pointer */ +/* NOTE: this is exported to other modules that use the ActKeyColumns for finding keyframes */ +short compare_ak_cfraPtr(void *node, void *data) +{ + ActKeyColumn *ak = (ActKeyColumn *)node; + const float *cframe = data; + float val = *cframe; + + if (is_cfra_eq(val, ak->cfra)) { + return 0; + } + + if (val < ak->cfra) { + return -1; + } + return 1; +} + +/* --------------- */ + +/* Set of references to three logically adjacent keys. */ +typedef struct BezTripleChain { + /* Current keyframe. */ + BezTriple *cur; + + /* Logical neighbors. May be NULL. */ + BezTriple *prev, *next; +} BezTripleChain; + +/* Categorize the interpolation & handle type of the keyframe. */ +static eKeyframeHandleDrawOpts bezt_handle_type(BezTriple *bezt) +{ + if (bezt->h1 == HD_AUTO_ANIM && bezt->h2 == HD_AUTO_ANIM) { + return KEYFRAME_HANDLE_AUTO_CLAMP; + } + if (ELEM(bezt->h1, HD_AUTO_ANIM, HD_AUTO) && ELEM(bezt->h2, HD_AUTO_ANIM, HD_AUTO)) { + return KEYFRAME_HANDLE_AUTO; + } + if (bezt->h1 == HD_VECT && bezt->h2 == HD_VECT) { + return KEYFRAME_HANDLE_VECTOR; + } + if (ELEM(HD_FREE, bezt->h1, bezt->h2)) { + return KEYFRAME_HANDLE_FREE; + } + return KEYFRAME_HANDLE_ALIGNED; +} + +/* Determine if the keyframe is an extreme by comparing with neighbors. + * Ends of fixed-value sections and of the whole curve are also marked. + */ +static eKeyframeExtremeDrawOpts bezt_extreme_type(BezTripleChain *chain) +{ + if (chain->prev == NULL && chain->next == NULL) { + return KEYFRAME_EXTREME_NONE; + } + + /* Keyframe values for the current one and neighbors. */ + float cur_y = chain->cur->vec[1][1]; + float prev_y = cur_y, next_y = cur_y; + + if (chain->prev && !IS_EQF(cur_y, chain->prev->vec[1][1])) { + prev_y = chain->prev->vec[1][1]; + } + if (chain->next && !IS_EQF(cur_y, chain->next->vec[1][1])) { + next_y = chain->next->vec[1][1]; + } + + /* Static hold. */ + if (prev_y == cur_y && next_y == cur_y) { + return KEYFRAME_EXTREME_FLAT; + } + + /* Middle of an incline. */ + if ((prev_y < cur_y && next_y > cur_y) || (prev_y > cur_y && next_y < cur_y)) { + return KEYFRAME_EXTREME_NONE; + } + + /* Bezier handle values for the overshoot check. */ + bool l_bezier = chain->prev && chain->prev->ipo == BEZT_IPO_BEZ; + bool r_bezier = chain->next && chain->cur->ipo == BEZT_IPO_BEZ; + float handle_l = l_bezier ? chain->cur->vec[0][1] : cur_y; + float handle_r = r_bezier ? chain->cur->vec[2][1] : cur_y; + + /* Detect extremes. One of the neighbors is allowed to be equal to current. */ + if (prev_y < cur_y || next_y < cur_y) { + bool is_overshoot = (handle_l > cur_y || handle_r > cur_y); + + return KEYFRAME_EXTREME_MAX | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0); + } + + if (prev_y > cur_y || next_y > cur_y) { + bool is_overshoot = (handle_l < cur_y || handle_r < cur_y); + + return KEYFRAME_EXTREME_MIN | (is_overshoot ? KEYFRAME_EXTREME_MIXED : 0); + } + + return KEYFRAME_EXTREME_NONE; +} + +/* Comparator callback used for ActKeyColumns and BezTripleChain */ +static short compare_ak_bezt(void *node, void *data) +{ + BezTripleChain *chain = data; + + return compare_ak_cfraPtr(node, &chain->cur->vec[1][0]); +} + +/* New node callback used for building ActKeyColumns from BezTripleChain */ +static DLRBT_Node *nalloc_ak_bezt(void *data) +{ + ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"); + BezTripleChain *chain = data; + BezTriple *bezt = chain->cur; + + /* store settings based on state of BezTriple */ + ak->cfra = bezt->vec[1][0]; + ak->sel = BEZT_ISSEL_ANY(bezt) ? SELECT : 0; + ak->key_type = BEZKEYTYPE(bezt); + ak->handle_type = bezt_handle_type(bezt); + ak->extreme_type = bezt_extreme_type(chain); + + /* count keyframes in this column */ + ak->totkey = 1; + + return (DLRBT_Node *)ak; +} + +/* Node updater callback used for building ActKeyColumns from BezTripleChain */ +static void nupdate_ak_bezt(void *node, void *data) +{ + ActKeyColumn *ak = node; + BezTripleChain *chain = data; + BezTriple *bezt = chain->cur; + + /* set selection status and 'touched' status */ + if (BEZT_ISSEL_ANY(bezt)) { + ak->sel = SELECT; + } + + /* count keyframes in this column */ + ak->totkey++; + + /* For keyframe type, 'proper' keyframes have priority over breakdowns + * (and other types for now). */ + if (BEZKEYTYPE(bezt) == BEZT_KEYTYPE_KEYFRAME) { + ak->key_type = BEZT_KEYTYPE_KEYFRAME; + } + + /* For interpolation type, select the highest value (enum is sorted). */ + ak->handle_type = MAX2(ak->handle_type, bezt_handle_type(bezt)); + + /* For extremes, detect when combining different states. */ + char new_extreme = bezt_extreme_type(chain); + + if (new_extreme != ak->extreme_type) { + /* Replace the flat status without adding mixed. */ + if (ak->extreme_type == KEYFRAME_EXTREME_FLAT) { + ak->extreme_type = new_extreme; + } + else if (new_extreme != KEYFRAME_EXTREME_FLAT) { + ak->extreme_type |= (new_extreme | KEYFRAME_EXTREME_MIXED); + } + } +} + +/* ......... */ + +/* Comparator callback used for ActKeyColumns and GPencil frame */ +static short compare_ak_gpframe(void *node, void *data) +{ + bGPDframe *gpf = (bGPDframe *)data; + + float frame = gpf->framenum; + return compare_ak_cfraPtr(node, &frame); +} + +/* New node callback used for building ActKeyColumns from GPencil frames */ +static DLRBT_Node *nalloc_ak_gpframe(void *data) +{ + ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF"); + bGPDframe *gpf = (bGPDframe *)data; + + /* store settings based on state of BezTriple */ + ak->cfra = gpf->framenum; + ak->sel = (gpf->flag & GP_FRAME_SELECT) ? SELECT : 0; + ak->key_type = gpf->key_type; + + /* count keyframes in this column */ + ak->totkey = 1; + /* Set as visible block. */ + ak->totblock = 1; + ak->block.sel = ak->sel; + ak->block.flag |= ACTKEYBLOCK_FLAG_GPENCIL; + + return (DLRBT_Node *)ak; +} + +/* Node updater callback used for building ActKeyColumns from GPencil frames */ +static void nupdate_ak_gpframe(void *node, void *data) +{ + ActKeyColumn *ak = (ActKeyColumn *)node; + bGPDframe *gpf = (bGPDframe *)data; + + /* set selection status and 'touched' status */ + if (gpf->flag & GP_FRAME_SELECT) { + ak->sel = SELECT; + } + + /* count keyframes in this column */ + ak->totkey++; + + /* for keyframe type, 'proper' keyframes have priority over breakdowns + * (and other types for now). */ + if (gpf->key_type == BEZT_KEYTYPE_KEYFRAME) { + ak->key_type = BEZT_KEYTYPE_KEYFRAME; + } +} + +/* ......... */ + +/* Comparator callback used for ActKeyColumns and GPencil frame */ +static short compare_ak_masklayshape(void *node, void *data) +{ + MaskLayerShape *masklay_shape = (MaskLayerShape *)data; + + float frame = masklay_shape->frame; + return compare_ak_cfraPtr(node, &frame); +} + +/* New node callback used for building ActKeyColumns from GPencil frames */ +static DLRBT_Node *nalloc_ak_masklayshape(void *data) +{ + ActKeyColumn *ak = MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumnGPF"); + MaskLayerShape *masklay_shape = (MaskLayerShape *)data; + + /* store settings based on state of BezTriple */ + ak->cfra = masklay_shape->frame; + ak->sel = (masklay_shape->flag & MASK_SHAPE_SELECT) ? SELECT : 0; + + /* count keyframes in this column */ + ak->totkey = 1; + + return (DLRBT_Node *)ak; +} + +/* Node updater callback used for building ActKeyColumns from GPencil frames */ +static void nupdate_ak_masklayshape(void *node, void *data) +{ + ActKeyColumn *ak = (ActKeyColumn *)node; + MaskLayerShape *masklay_shape = (MaskLayerShape *)data; + + /* set selection status and 'touched' status */ + if (masklay_shape->flag & MASK_SHAPE_SELECT) { + ak->sel = SELECT; + } + + /* count keyframes in this column */ + ak->totkey++; +} + +/* --------------- */ + +/* Add the given BezTriple to the given 'list' of Keyframes */ +static void add_bezt_to_keycolumns_list(DLRBT_Tree *keys, BezTripleChain *bezt) +{ + if (ELEM(NULL, keys, bezt)) { + return; + } + + BLI_dlrbTree_add(keys, compare_ak_bezt, nalloc_ak_bezt, nupdate_ak_bezt, bezt); +} + +/* Add the given GPencil Frame to the given 'list' of Keyframes */ +static void add_gpframe_to_keycolumns_list(DLRBT_Tree *keys, bGPDframe *gpf) +{ + if (ELEM(NULL, keys, gpf)) { + return; + } + + BLI_dlrbTree_add(keys, compare_ak_gpframe, nalloc_ak_gpframe, nupdate_ak_gpframe, gpf); +} + +/* Add the given MaskLayerShape Frame to the given 'list' of Keyframes */ +static void add_masklay_to_keycolumns_list(DLRBT_Tree *keys, MaskLayerShape *masklay_shape) +{ + if (ELEM(NULL, keys, masklay_shape)) { + return; + } + + BLI_dlrbTree_add(keys, + compare_ak_masklayshape, + nalloc_ak_masklayshape, + nupdate_ak_masklayshape, + masklay_shape); +} + +/* ActKeyBlocks (Long Keyframes) ------------------------------------------ */ + +static const ActKeyBlockInfo dummy_keyblock = {0}; + +static void compute_keyblock_data(ActKeyBlockInfo *info, BezTriple *prev, BezTriple *beztn) +{ + memset(info, 0, sizeof(ActKeyBlockInfo)); + + if (BEZKEYTYPE(beztn) == BEZT_KEYTYPE_MOVEHOLD) { + /* Animator tagged a "moving hold" + * - Previous key must also be tagged as a moving hold, otherwise + * we're just dealing with the first of a pair, and we don't + * want to be creating any phantom holds... + */ + if (BEZKEYTYPE(prev) == BEZT_KEYTYPE_MOVEHOLD) { + info->flag |= ACTKEYBLOCK_FLAG_MOVING_HOLD | ACTKEYBLOCK_FLAG_ANY_HOLD; + } + } + + /* Check for same values... + * - Handles must have same central value as each other + * - Handles which control that section of the curve must be constant + */ + if (IS_EQF(beztn->vec[1][1], prev->vec[1][1])) { + bool hold; + + /* Only check handles in case of actual bezier interpolation. */ + if (prev->ipo == BEZT_IPO_BEZ) { + hold = IS_EQF(beztn->vec[1][1], beztn->vec[0][1]) && + IS_EQF(prev->vec[1][1], prev->vec[2][1]); + } + /* This interpolation type induces movement even between identical keys. */ + else { + hold = !ELEM(prev->ipo, BEZT_IPO_ELASTIC); + } + + if (hold) { + info->flag |= ACTKEYBLOCK_FLAG_STATIC_HOLD | ACTKEYBLOCK_FLAG_ANY_HOLD; + } + } + + /* Remember non-bezier interpolation info. */ + if (prev->ipo != BEZT_IPO_BEZ) { + info->flag |= ACTKEYBLOCK_FLAG_NON_BEZIER; + } + + info->sel = BEZT_ISSEL_ANY(prev) || BEZT_ISSEL_ANY(beztn); +} + +static void add_keyblock_info(ActKeyColumn *col, const ActKeyBlockInfo *block) +{ + /* New curve and block. */ + if (col->totcurve <= 1 && col->totblock == 0) { + memcpy(&col->block, block, sizeof(ActKeyBlockInfo)); + } + /* Existing curve. */ + else { + col->block.conflict |= (col->block.flag ^ block->flag); + col->block.flag |= block->flag; + col->block.sel |= block->sel; + } + + if (block->flag) { + col->totblock++; + } +} + +static void add_bezt_to_keyblocks_list(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len) +{ + ActKeyColumn *col = keys->first; + + if (bezt && bezt_len >= 2) { + ActKeyBlockInfo block; + + /* Find the first key column while inserting dummy blocks. */ + for (; col != NULL && is_cfra_lt(col->cfra, bezt[0].vec[1][0]); col = col->next) { + add_keyblock_info(col, &dummy_keyblock); + } + + BLI_assert(col != NULL); + + /* Insert real blocks. */ + for (int v = 1; col != NULL && v < bezt_len; v++, bezt++) { + /* Wrong order of bezier keys: resync position. */ + if (is_cfra_lt(bezt[1].vec[1][0], bezt[0].vec[1][0])) { + /* Backtrack to find the right location. */ + if (is_cfra_lt(bezt[1].vec[1][0], col->cfra)) { + ActKeyColumn *newcol = (ActKeyColumn *)BLI_dlrbTree_search_exact( + keys, compare_ak_cfraPtr, &bezt[1].vec[1][0]); + + if (newcol != NULL) { + col = newcol; + + /* The previous keyblock is garbage too. */ + if (col->prev != NULL) { + add_keyblock_info(col->prev, &dummy_keyblock); + } + } + else { + BLI_assert(false); + } + } + + continue; + } + + /* Normal sequence */ + BLI_assert(is_cfra_eq(col->cfra, bezt[0].vec[1][0])); + + compute_keyblock_data(&block, bezt, bezt + 1); + + for (; col != NULL && is_cfra_lt(col->cfra, bezt[1].vec[1][0]); col = col->next) { + add_keyblock_info(col, &block); + } + + BLI_assert(col != NULL); + } + } + + /* Insert dummy blocks at the end. */ + for (; col != NULL; col = col->next) { + add_keyblock_info(col, &dummy_keyblock); + } +} + +/* Walk through columns and propagate blocks and totcurve. + * + * This must be called even by animation sources that don't generate + * keyblocks to keep the data structure consistent after adding columns. + */ +static void update_keyblocks(DLRBT_Tree *keys, BezTriple *bezt, int bezt_len) +{ + /* Recompute the prev/next linked list. */ + BLI_dlrbTree_linkedlist_sync(keys); + + /* Find the curve count */ + int max_curve = 0; + + LISTBASE_FOREACH (ActKeyColumn *, col, keys) { + max_curve = MAX2(max_curve, col->totcurve); + } + + /* Propagate blocks to inserted keys */ + ActKeyColumn *prev_ready = NULL; + + LISTBASE_FOREACH (ActKeyColumn *, col, keys) { + /* Pre-existing column. */ + if (col->totcurve > 0) { + prev_ready = col; + } + /* Newly inserted column, so copy block data from previous. */ + else if (prev_ready != NULL) { + col->totblock = prev_ready->totblock; + memcpy(&col->block, &prev_ready->block, sizeof(ActKeyBlockInfo)); + } + + col->totcurve = max_curve + 1; + } + + /* Add blocks on top */ + add_bezt_to_keyblocks_list(keys, bezt, bezt_len); +} + +/* --------- */ + +bool actkeyblock_is_valid(ActKeyColumn *ac) +{ + return ac != NULL && ac->next != NULL && ac->totblock > 0; +} + +/* Checks if ActKeyBlock should exist... */ +int actkeyblock_get_valid_hold(ActKeyColumn *ac) +{ + /* check that block is valid */ + if (!actkeyblock_is_valid(ac)) { + return 0; + } + + const int hold_mask = (ACTKEYBLOCK_FLAG_ANY_HOLD | ACTKEYBLOCK_FLAG_STATIC_HOLD); + return (ac->block.flag & ~ac->block.conflict) & hold_mask; +} + +/* *************************** Keyframe List Conversions *************************** */ + +void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys, int saction_flag) +{ + if (ac) { + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + /* get F-Curves to take keyframes from */ + filter = ANIMFILTER_DATA_VISIBLE; + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* loop through each F-Curve, grabbing the keyframes */ + for (ale = anim_data.first; ale; ale = ale->next) { + /* Why not use all #eAnim_KeyType here? + * All of the other key types are actually "summaries" themselves, + * and will just end up duplicating stuff that comes up through + * standard filtering of just F-Curves. Given the way that these work, + * there isn't really any benefit at all from including them. - Aligorith */ + + switch (ale->datatype) { + case ALE_FCURVE: + fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); + break; + case ALE_MASKLAY: + mask_to_keylist(ac->ads, ale->data, keys); + break; + case ALE_GPFRAME: + gpl_to_keylist(ac->ads, ale->data, keys); + break; + default: + // printf("%s: datatype %d unhandled\n", __func__, ale->datatype); + break; + } + } + + ANIM_animdata_freelist(&anim_data); + } +} + +void scene_to_keylist(bDopeSheet *ads, Scene *sce, DLRBT_Tree *keys, int saction_flag) +{ + bAnimContext ac = {NULL}; + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + bAnimListElem dummychan = {NULL}; + + if (sce == NULL) { + return; + } + + /* create a dummy wrapper data to work with */ + dummychan.type = ANIMTYPE_SCENE; + dummychan.data = sce; + dummychan.id = &sce->id; + dummychan.adt = sce->adt; + + ac.ads = ads; + ac.data = &dummychan; + ac.datatype = ANIMCONT_CHANNEL; + + /* get F-Curves to take keyframes from */ + filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* loop through each F-Curve, grabbing the keyframes */ + for (ale = anim_data.first; ale; ale = ale->next) { + fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); + } + + ANIM_animdata_freelist(&anim_data); +} + +void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, int saction_flag) +{ + bAnimContext ac = {NULL}; + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + bAnimListElem dummychan = {NULL}; + Base dummybase = {NULL}; + + if (ob == NULL) { + return; + } + + /* create a dummy wrapper data to work with */ + dummybase.object = ob; + + dummychan.type = ANIMTYPE_OBJECT; + dummychan.data = &dummybase; + dummychan.id = &ob->id; + dummychan.adt = ob->adt; + + ac.ads = ads; + ac.data = &dummychan; + ac.datatype = ANIMCONT_CHANNEL; + + /* get F-Curves to take keyframes from */ + filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* loop through each F-Curve, grabbing the keyframes */ + for (ale = anim_data.first; ale; ale = ale->next) { + fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); + } + + ANIM_animdata_freelist(&anim_data); +} + +void cachefile_to_keylist(bDopeSheet *ads, + CacheFile *cache_file, + DLRBT_Tree *keys, + int saction_flag) +{ + if (cache_file == NULL) { + return; + } + + /* create a dummy wrapper data to work with */ + bAnimListElem dummychan = {NULL}; + dummychan.type = ANIMTYPE_DSCACHEFILE; + dummychan.data = cache_file; + dummychan.id = &cache_file->id; + dummychan.adt = cache_file->adt; + + bAnimContext ac = {NULL}; + ac.ads = ads; + ac.data = &dummychan; + ac.datatype = ANIMCONT_CHANNEL; + + /* get F-Curves to take keyframes from */ + ListBase anim_data = {NULL, NULL}; + int filter = ANIMFILTER_DATA_VISIBLE; /* curves only */ + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* loop through each F-Curve, grabbing the keyframes */ + LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { + fcurve_to_keylist(ale->adt, ale->data, keys, saction_flag); + } + + ANIM_animdata_freelist(&anim_data); +} + +void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, int saction_flag) +{ + if (fcu && fcu->totvert && fcu->bezt) { + /* apply NLA-mapping (if applicable) */ + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 0); + } + + /* Check if the curve is cyclic. */ + bool is_cyclic = BKE_fcurve_is_cyclic(fcu) && (fcu->totvert >= 2); + bool do_extremes = (saction_flag & SACTION_SHOW_EXTREMES) != 0; + + /* loop through beztriples, making ActKeysColumns */ + BezTripleChain chain = {0}; + + for (int v = 0; v < fcu->totvert; v++) { + chain.cur = &fcu->bezt[v]; + + /* Neighbor keys, accounting for being cyclic. */ + if (do_extremes) { + chain.prev = (v > 0) ? &fcu->bezt[v - 1] : is_cyclic ? &fcu->bezt[fcu->totvert - 2] : NULL; + chain.next = (v + 1 < fcu->totvert) ? &fcu->bezt[v + 1] : is_cyclic ? &fcu->bezt[1] : NULL; + } + + add_bezt_to_keycolumns_list(keys, &chain); + } + + /* Update keyblocks. */ + update_keyblocks(keys, fcu->bezt, fcu->totvert); + + /* unapply NLA-mapping if applicable */ + if (adt) { + ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 0); + } + } +} + +void agroup_to_keylist(AnimData *adt, bActionGroup *agrp, DLRBT_Tree *keys, int saction_flag) +{ + FCurve *fcu; + + if (agrp) { + /* loop through F-Curves */ + for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next) { + fcurve_to_keylist(adt, fcu, keys, saction_flag); + } + } +} + +void action_to_keylist(AnimData *adt, bAction *act, DLRBT_Tree *keys, int saction_flag) +{ + FCurve *fcu; + + if (act) { + /* loop through F-Curves */ + for (fcu = act->curves.first; fcu; fcu = fcu->next) { + fcurve_to_keylist(adt, fcu, keys, saction_flag); + } + } +} + +void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, DLRBT_Tree *keys, const bool active) +{ + bGPDlayer *gpl; + + if (gpd && keys) { + /* for now, just aggregate out all the frames, but only for visible layers */ + for (gpl = gpd->layers.last; gpl; gpl = gpl->prev) { + if ((gpl->flag & GP_LAYER_HIDE) == 0) { + if ((!active) || ((active) && (gpl->flag & GP_LAYER_SELECT))) { + gpl_to_keylist(ads, gpl, keys); + } + } + } + } +} + +void gpl_to_keylist(bDopeSheet *UNUSED(ads), bGPDlayer *gpl, DLRBT_Tree *keys) +{ + bGPDframe *gpf; + + if (gpl && keys) { + /* Although the frames should already be in an ordered list, + * they are not suitable for displaying yet. */ + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + add_gpframe_to_keycolumns_list(keys, gpf); + } + + update_keyblocks(keys, NULL, 0); + } +} + +void mask_to_keylist(bDopeSheet *UNUSED(ads), MaskLayer *masklay, DLRBT_Tree *keys) +{ + MaskLayerShape *masklay_shape; + + if (masklay && keys) { + for (masklay_shape = masklay->splines_shapes.first; masklay_shape; + masklay_shape = masklay_shape->next) { + add_masklay_to_keycolumns_list(keys, masklay_shape); + } + + update_keyblocks(keys, NULL, 0); + } +} diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt index e942bcf2902..aff5803f037 100644 --- a/source/blender/editors/armature/CMakeLists.txt +++ b/source/blender/editors/armature/CMakeLists.txt @@ -44,7 +44,7 @@ set(SRC armature_utils.c editarmature_undo.c meshlaplacian.c - pose_backup.c + pose_backup.cc pose_edit.c pose_group.c pose_lib.c diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c index 874f1b49451..4fe4422e4e0 100644 --- a/source/blender/editors/armature/armature_utils.c +++ b/source/blender/editors/armature/armature_utils.c @@ -107,7 +107,7 @@ int bone_looper(Object *ob, Bone *bone, void *data, int (*bone_func)(Object *, B { /* We want to apply the function bone_func to every bone * in an armature -- feed bone_looper the first bone and - * a pointer to the bone_func and watch it go!. The int count + * a pointer to the bone_func and watch it go! The int count * can be useful for counting bones with a certain property * (e.g. skinnable) */ diff --git a/source/blender/editors/armature/pose_backup.c b/source/blender/editors/armature/pose_backup.cc index dffcd9bdc5a..9c9347309b2 100644 --- a/source/blender/editors/armature/pose_backup.c +++ b/source/blender/editors/armature/pose_backup.cc @@ -20,7 +20,7 @@ #include "ED_armature.h" -#include <string.h> +#include <cstring> #include "BLI_listbase.h" @@ -31,44 +31,54 @@ #include "DNA_object_types.h" #include "BKE_action.h" -#include "BKE_armature.h" +#include "BKE_action.hh" +#include "BKE_armature.hh" #include "BKE_idprop.h" +using namespace blender::bke; + /* simple struct for storing backup info for one pose channel */ -typedef struct PoseChannelBackup { +struct PoseChannelBackup { struct PoseChannelBackup *next, *prev; struct bPoseChannel *pchan; /* Pose channel this backup is for. */ struct bPoseChannel olddata; /* Backup of pose channel. */ struct IDProperty *oldprops; /* Backup copy (needs freeing) of pose channel's ID properties. */ -} PoseChannelBackup; +}; -typedef struct PoseBackup { +struct PoseBackup { bool is_bone_selection_relevant; ListBase /* PoseChannelBackup* */ backups; -} PoseBackup; +}; static PoseBackup *pose_backup_create(const Object *ob, const bAction *action, - const bool is_bone_selection_relevant) + const BoneNameSet &selected_bone_names) { - ListBase backups = {NULL, NULL}; - const bArmature *armature = ob->data; - - /* TODO(Sybren): reuse same approach as in `armature_pose.cc` in this function, as that doesn't - * have the assumption that action group names are bone names. */ - LISTBASE_FOREACH (bActionGroup *, agrp, &action->groups) { - bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, agrp->name); - if (pchan == NULL) { - continue; + ListBase backups = {nullptr, nullptr}; + const bool is_bone_selection_relevant = !selected_bone_names.is_empty(); + + BoneNameSet backed_up_bone_names; + /* Make a backup of the given pose channel. */ + auto store_animated_pchans = [&](FCurve * /* unused */, const char *bone_name) { + if (backed_up_bone_names.contains(bone_name)) { + /* Only backup each bone once. */ + return; + } + + bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, bone_name); + if (pchan == nullptr) { + /* FCurve targets non-existent bone. */ + return; } - if (is_bone_selection_relevant && !PBONE_SELECTED(armature, pchan->bone)) { - continue; + if (is_bone_selection_relevant && !selected_bone_names.contains(bone_name)) { + return; } - PoseChannelBackup *chan_bak = MEM_callocN(sizeof(*chan_bak), "PoseChannelBackup"); + PoseChannelBackup *chan_bak = static_cast<PoseChannelBackup *>( + MEM_callocN(sizeof(*chan_bak), "PoseChannelBackup")); chan_bak->pchan = pchan; memcpy(&chan_bak->olddata, chan_bak->pchan, sizeof(chan_bak->olddata)); @@ -77,10 +87,14 @@ static PoseBackup *pose_backup_create(const Object *ob, } BLI_addtail(&backups, chan_bak); - } + backed_up_bone_names.add_new(bone_name); + }; + + /* Call `store_animated_pchans()` for each FCurve that targets a bone. */ + BKE_action_find_fcurves_with_bones(action, store_animated_pchans); /* PoseBackup is constructed late, so that the above loop can use stack variables. */ - PoseBackup *pose_backup = MEM_callocN(sizeof(*pose_backup), __func__); + PoseBackup *pose_backup = static_cast<PoseBackup *>(MEM_callocN(sizeof(*pose_backup), __func__)); pose_backup->is_bone_selection_relevant = is_bone_selection_relevant; pose_backup->backups = backups; return pose_backup; @@ -88,24 +102,14 @@ static PoseBackup *pose_backup_create(const Object *ob, PoseBackup *ED_pose_backup_create_all_bones(const Object *ob, const bAction *action) { - return pose_backup_create(ob, action, false); + return pose_backup_create(ob, action, BoneNameSet()); } PoseBackup *ED_pose_backup_create_selected_bones(const Object *ob, const bAction *action) { - /* See if bone selection is relevant. */ - bool all_bones_selected = true; - bool no_bones_selected = true; - const bArmature *armature = ob->data; - LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { - const bool is_selected = PBONE_SELECTED(armature, pchan->bone); - all_bones_selected &= is_selected; - no_bones_selected &= !is_selected; - } - - /* If no bones are selected, act as if all are. */ - const bool is_bone_selection_relevant = !all_bones_selected && !no_bones_selected; - return pose_backup_create(ob, action, is_bone_selection_relevant); + const bArmature *armature = static_cast<const bArmature *>(ob->data); + const BoneNameSet selected_bone_names = BKE_armature_find_selected_bone_names(armature); + return pose_backup_create(ob, action, selected_bone_names); } bool ED_pose_backup_is_selection_relevant(const struct PoseBackup *pose_backup) diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index df3550d5db6..cb70b2810d1 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -62,8 +62,8 @@ #include "ED_anim_api.h" #include "ED_armature.h" -#include "ED_keyframes_draw.h" #include "ED_keyframes_edit.h" +#include "ED_keyframes_keylist.h" #include "ED_keyframing.h" #include "ED_object.h" #include "ED_screen.h" diff --git a/source/blender/editors/armature/pose_lib_2.c b/source/blender/editors/armature/pose_lib_2.c index eb091296282..32440f941ba 100644 --- a/source/blender/editors/armature/pose_lib_2.c +++ b/source/blender/editors/armature/pose_lib_2.c @@ -130,7 +130,7 @@ static void poselib_keytag_pose(bContext *C, Scene *scene, PoseBlendData *pbd) /* start tagging/keying */ const bArmature *armature = pbd->ob->data; LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) { - /* only for selected bones unless there aren't any selected, in which case all are included */ + /* Only for selected bones unless there aren't any selected, in which case all are included. */ bPoseChannel *pchan = BKE_pose_channel_find_name(pose, agrp->name); if (pchan == NULL) { continue; @@ -422,7 +422,7 @@ static void poselib_blend_cleanup(bContext *C, wmOperator *op) case POSE_BLEND_BLENDING: case POSE_BLEND_ORIGINAL: /* Cleanup should not be called directly from these states. */ - BLI_assert(!"poselib_blend_cleanup: unexpected pose blend state"); + BLI_assert_msg(0, "poselib_blend_cleanup: unexpected pose blend state"); BKE_report(op->reports, RPT_ERROR, "Internal pose library error, cancelling operator"); ATTR_FALLTHROUGH; case POSE_BLEND_CANCEL: @@ -544,7 +544,7 @@ static bool poselib_asset_in_context(bContext *C) AssetHandle asset_handle = CTX_wm_asset_handle(C, &asset_handle_valid); return (asset_library != NULL) && asset_handle_valid && - (asset_handle.file_data->blentype == ID_AC); + (ED_asset_handle_get_id_type(&asset_handle) == ID_AC); } /* Poll callback for operators that require existing PoseLib data (with poses) to work. */ diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 1a1685e4a01..38f562ebf25 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -77,11 +77,12 @@ #include "UI_resources.h" #include "ED_armature.h" -#include "ED_keyframes_draw.h" +#include "ED_keyframes_keylist.h" #include "ED_markers.h" #include "ED_numinput.h" #include "ED_screen.h" #include "ED_space_api.h" +#include "ED_util.h" #include "GPU_immediate.h" #include "GPU_immediate_util.h" @@ -137,8 +138,8 @@ typedef struct tPoseSlideOp { Scene *scene; /** area that we're operating in (needed for modal()) */ ScrArea *area; - /** Header of the region used for drawing the slider. */ - ARegion *region_header; + /** Region we're operating in (needed for modal()). */ + ARegion *region; /** len of the PoseSlideObject array. */ uint objects_len; @@ -168,26 +169,7 @@ typedef struct tPoseSlideOp { /** Axis-limits for transforms. */ ePoseSlide_AxisLock axislock; - /** Allow overshoot or clamp between 0% and 100%. */ - bool overshoot; - - /** Reduces factor delta from mouse movement. */ - bool precision; - - /** Move factor in 10% steps. */ - bool increments; - - /** Draw callback handler. */ - void *draw_handle; - - /** Accumulative, unclamped and unrounded factor. */ - float raw_factor; - - /** 0-1 value for determining the influence of whatever is relevant. */ - float factor; - - /** Last cursor position in screen space used for mouse movement delta calculation. */ - int last_cursor_x; + struct tSlider *slider; /** Numeric input. */ NumInput num; @@ -232,256 +214,6 @@ static const EnumPropertyItem prop_axis_lock_types[] = { /* ------------------------------------ */ -static void draw_overshoot_triangle(const uint8_t color[4], - const bool facing_right, - const float x, - const float y) -{ - const uint shdr_pos_2d = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_blend(GPU_BLEND_ALPHA); - GPU_polygon_smooth(true); - immUniformColor3ubvAlpha(color, 225); - const float triangle_side_length = facing_right ? 6 * U.pixelsize : -6 * U.pixelsize; - const float triangle_offset = facing_right ? 2 * U.pixelsize : -2 * U.pixelsize; - - immBegin(GPU_PRIM_TRIS, 3); - immVertex2f(shdr_pos_2d, x + triangle_offset + triangle_side_length, y); - immVertex2f(shdr_pos_2d, x + triangle_offset, y + triangle_side_length / 2); - immVertex2f(shdr_pos_2d, x + triangle_offset, y - triangle_side_length / 2); - immEnd(); - - GPU_polygon_smooth(false); - GPU_blend(GPU_BLEND_NONE); - immUnbindProgram(); -} - -static void draw_ticks(const float start_factor, - const float end_factor, - const float line_start[2], - const float base_tick_height, - const float line_width, - const uint8_t color_overshoot[4], - const uint8_t color_line[4]) -{ - /* Use factor represented as 0-100 int to avoid floating point precision problems. */ - const int tick_increment = 10; - - /* Round initial_tick_factor up to the next tick_increment. */ - int tick_percentage = ceil((start_factor * 100) / tick_increment) * tick_increment; - - while (tick_percentage <= (int)(end_factor * 100)) { - float tick_height; - /* Different ticks have different heights. Multiples of 100% are the tallest, 50% is a bit - * smaller and the rest is the minimum size. */ - if (tick_percentage % 100 == 0) { - tick_height = base_tick_height; - } - else if (tick_percentage % 50 == 0) { - tick_height = base_tick_height * 0.8; - } - else { - tick_height = base_tick_height * 0.5; - } - - const float x = line_start[0] + - (((float)tick_percentage / 100) - start_factor) * SLIDE_PIXEL_DISTANCE; - const rctf tick_rect = { - .xmin = x - (line_width / 2), - .xmax = x + (line_width / 2), - .ymin = line_start[1] - (tick_height / 2), - .ymax = line_start[1] + (tick_height / 2), - }; - - if (tick_percentage < 0 || tick_percentage > 100) { - UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_overshoot, 255); - } - else { - UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_line, 255); - } - tick_percentage += tick_increment; - } -} - -static void draw_main_line(const rctf *main_line_rect, - const float factor, - const bool overshoot, - const uint8_t color_overshoot[4], - const uint8_t color_line[4]) -{ - if (overshoot) { - /* In overshoot mode, draw the 0-100% range differently to provide a visual reference. */ - const float line_zero_percent = main_line_rect->xmin - - ((factor - 0.5f - OVERSHOOT_RANGE_DELTA) * - SLIDE_PIXEL_DISTANCE); - - const float clamped_line_zero_percent = clamp_f( - line_zero_percent, main_line_rect->xmin, main_line_rect->xmax); - const float clamped_line_hundred_percent = clamp_f( - line_zero_percent + SLIDE_PIXEL_DISTANCE, main_line_rect->xmin, main_line_rect->xmax); - - const rctf left_overshoot_line_rect = { - .xmin = main_line_rect->xmin, - .xmax = clamped_line_zero_percent, - .ymin = main_line_rect->ymin, - .ymax = main_line_rect->ymax, - }; - const rctf right_overshoot_line_rect = { - .xmin = clamped_line_hundred_percent, - .xmax = main_line_rect->xmax, - .ymin = main_line_rect->ymin, - .ymax = main_line_rect->ymax, - }; - UI_draw_roundbox_3ub_alpha(&left_overshoot_line_rect, true, 0, color_overshoot, 255); - UI_draw_roundbox_3ub_alpha(&right_overshoot_line_rect, true, 0, color_overshoot, 255); - - const rctf non_overshoot_line_rect = { - .xmin = clamped_line_zero_percent, - .xmax = clamped_line_hundred_percent, - .ymin = main_line_rect->ymin, - .ymax = main_line_rect->ymax, - }; - UI_draw_roundbox_3ub_alpha(&non_overshoot_line_rect, true, 0, color_line, 255); - } - else { - UI_draw_roundbox_3ub_alpha(main_line_rect, true, 0, color_line, 255); - } -} - -static void draw_backdrop(const int fontid, - const rctf *main_line_rect, - const float color_bg[4], - const short region_y_size, - const float base_tick_height) -{ - float string_pixel_size[2]; - const char *percentage_string_placeholder = "000%%"; - BLF_width_and_height(fontid, - percentage_string_placeholder, - sizeof(percentage_string_placeholder), - &string_pixel_size[0], - &string_pixel_size[1]); - const float pad[2] = {(region_y_size - base_tick_height) / 2, 2.0f * U.pixelsize}; - const rctf backdrop_rect = { - .xmin = main_line_rect->xmin - string_pixel_size[0] - pad[0], - .xmax = main_line_rect->xmax + pad[0], - .ymin = pad[1], - .ymax = region_y_size - pad[1], - }; - UI_draw_roundbox_aa(&backdrop_rect, true, 4.0f, color_bg); -} - -/** - * Draw an on screen Slider for a Pose Slide Operator. - */ -static void pose_slide_draw_2d_slider(const struct bContext *UNUSED(C), ARegion *region, void *arg) -{ - tPoseSlideOp *pso = arg; - - /* Only draw in region from which the Operator was started. */ - if (region != pso->region_header) { - return; - } - - uint8_t color_text[4]; - uint8_t color_line[4]; - uint8_t color_handle[4]; - uint8_t color_overshoot[4]; - float color_bg[4]; - - /* Get theme colors. */ - UI_GetThemeColor4ubv(TH_TEXT, color_text); - UI_GetThemeColor4ubv(TH_TEXT, color_line); - UI_GetThemeColor4ubv(TH_TEXT, color_overshoot); - UI_GetThemeColor4ubv(TH_ACTIVE, color_handle); - UI_GetThemeColor3fv(TH_BACK, color_bg); - - color_bg[3] = 0.5f; - color_overshoot[0] = color_overshoot[0] * 0.7; - color_overshoot[1] = color_overshoot[1] * 0.7; - color_overshoot[2] = color_overshoot[2] * 0.7; - - /* Get the default font. */ - const uiStyle *style = UI_style_get(); - const uiFontStyle *fstyle = &style->widget; - const int fontid = fstyle->uifont_id; - BLF_color3ubv(fontid, color_text); - BLF_rotation(fontid, 0.0f); - - const float line_width = 1.5 * U.pixelsize; - const float base_tick_height = 12.0 * U.pixelsize; - const float line_y = region->winy / 2; - - rctf main_line_rect = { - .xmin = (region->winx / 2) - (SLIDE_PIXEL_DISTANCE / 2), - .xmax = (region->winx / 2) + (SLIDE_PIXEL_DISTANCE / 2), - .ymin = line_y - line_width / 2, - .ymax = line_y + line_width / 2, - }; - float line_start_factor = 0; - int handle_pos_x = main_line_rect.xmin + SLIDE_PIXEL_DISTANCE * pso->factor; - - if (pso->overshoot) { - main_line_rect.xmin = main_line_rect.xmin - SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA; - main_line_rect.xmax = main_line_rect.xmax + SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA; - line_start_factor = pso->factor - 0.5f - OVERSHOOT_RANGE_DELTA; - handle_pos_x = region->winx / 2; - } - - draw_backdrop(fontid, &main_line_rect, color_bg, pso->region_header->winy, base_tick_height); - - draw_main_line(&main_line_rect, pso->factor, pso->overshoot, color_overshoot, color_line); - - const float factor_range = pso->overshoot ? 1 + OVERSHOOT_RANGE_DELTA * 2 : 1; - const float line_start_position[2] = {main_line_rect.xmin, line_y}; - draw_ticks(line_start_factor, - line_start_factor + factor_range, - line_start_position, - base_tick_height, - line_width, - color_overshoot, - color_line); - - /* Draw triangles at the ends of the line in overshoot mode to indicate direction of 0-100% - * range. */ - if (pso->overshoot) { - if (pso->factor > 1 + OVERSHOOT_RANGE_DELTA + 0.5) { - draw_overshoot_triangle(color_line, false, main_line_rect.xmin, line_y); - } - if (pso->factor < 0 - OVERSHOOT_RANGE_DELTA - 0.5) { - draw_overshoot_triangle(color_line, true, main_line_rect.xmax, line_y); - } - } - - char percentage_string[256]; - - /* Draw handle indicating current factor. */ - const rctf handle_rect = { - .xmin = handle_pos_x - (line_width), - .xmax = handle_pos_x + (line_width), - .ymin = line_y - (base_tick_height / 2), - .ymax = line_y + (base_tick_height / 2), - }; - - UI_draw_roundbox_3ub_alpha(&handle_rect, true, 1, color_handle, 255); - BLI_snprintf(percentage_string, sizeof(percentage_string), "%.0f%%", pso->factor * 100); - - /* Draw percentage string. */ - float percentage_string_pixel_size[2]; - BLF_width_and_height(fontid, - percentage_string, - sizeof(percentage_string), - &percentage_string_pixel_size[0], - &percentage_string_pixel_size[1]); - - BLF_position(fontid, - main_line_rect.xmin - 24.0 * U.pixelsize - percentage_string_pixel_size[0] / 2, - (region->winy / 2) - percentage_string_pixel_size[1] / 2, - 0.0f); - BLF_draw(fontid, percentage_string, sizeof(percentage_string)); -} - /** Operator custom-data initialization. */ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) { @@ -492,15 +224,13 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) /* Get info from context. */ pso->scene = CTX_data_scene(C); - pso->area = CTX_wm_area(C); /* Only really needed when doing modal(). */ - pso->region_header = CTX_wm_region(C); /* Only really needed when doing modal(). */ + pso->area = CTX_wm_area(C); /* Only really needed when doing modal(). */ + pso->region = CTX_wm_region(C); /* Only really needed when doing modal(). */ pso->cframe = pso->scene->r.cfra; pso->mode = mode; /* Set range info from property values - these may get overridden for the invoke(). */ - pso->factor = RNA_float_get(op->ptr, "factor"); - pso->raw_factor = pso->factor; pso->prevFrame = RNA_int_get(op->ptr, "prev_frame"); pso->nextFrame = RNA_int_get(op->ptr, "next_frame"); @@ -508,6 +238,9 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) pso->channels = RNA_enum_get(op->ptr, "channels"); pso->axislock = RNA_enum_get(op->ptr, "axis_lock"); + pso->slider = ED_slider_create(C); + ED_slider_factor_set(pso->slider, RNA_float_get(op->ptr, "factor")); + /* For each Pose-Channel which gets affected, get the F-Curves for that channel * and set the relevant transform flags. */ poseAnim_mapping_get(C, &pso->pfLinks); @@ -552,14 +285,6 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) pso->num.val_flag[0] |= NUM_NO_NEGATIVE; pso->num.unit_type[0] = B_UNIT_NONE; /* Percentages don't have any units. */ - /* Register UI drawing callback. */ - ARegion *region_header = BKE_area_find_region_type(pso->area, RGN_TYPE_HEADER); - if (region_header != NULL) { - pso->region_header = region_header; - pso->draw_handle = ED_region_draw_cb_activate( - region_header->type, pose_slide_draw_2d_slider, pso, REGION_DRAW_POST_PIXEL); - } - /* Return status is whether we've got all the data we were requested to get. */ return 1; } @@ -567,17 +292,16 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) /** * Exiting the operator (free data). */ -static void pose_slide_exit(wmOperator *op) +static void pose_slide_exit(bContext *C, wmOperator *op) { tPoseSlideOp *pso = op->customdata; + ED_slider_destroy(C, pso->slider); + /* Hide Bone Overlay. */ View3D *v3d = pso->area->spacedata.first; v3d->overlay.flag = pso->overlay_flag; - /* Remove UI drawing callback. */ - ED_region_draw_cb_exit(pso->region_header->type, pso->draw_handle); - /* Free the temp pchan links and their data. */ poseAnim_mapping_free(&pso->pfLinks); @@ -655,8 +379,8 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, flo /* Calculate the relative weights of the endpoints. */ if (pso->mode == POSESLIDE_BREAKDOWN) { /* Get weights from the factor control. */ - w1 = pso->factor; /* This must come second. */ - w2 = 1.0f - w1; /* This must come first. */ + w1 = ED_slider_factor_get(pso->slider); /* This must come second. */ + w2 = 1.0f - w1; /* This must come first. */ } else { /* - these weights are derived from the relative distance of these @@ -682,13 +406,13 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, flo case POSESLIDE_PUSH: /* Make the current pose more pronounced. */ { /* Slide the pose away from the breakdown pose in the timeline */ - (*val) -= ((sVal * w2) + (eVal * w1) - (*val)) * pso->factor; + (*val) -= ((sVal * w2) + (eVal * w1) - (*val)) * ED_slider_factor_get(pso->slider); break; } case POSESLIDE_RELAX: /* Make the current pose more like its surrounding ones. */ { /* Slide the pose towards the breakdown pose in the timeline */ - (*val) += ((sVal * w2) + (eVal * w1) - (*val)) * pso->factor; + (*val) += ((sVal * w2) + (eVal * w1) - (*val)) * ED_slider_factor_get(pso->slider); break; } case POSESLIDE_BREAKDOWN: /* Make the current pose slide around between the endpoints. */ @@ -888,7 +612,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) normalize_qt(quat_prev); normalize_qt(quat_next); - interp_qt_qtqt(quat_final, quat_prev, quat_next, pso->factor); + interp_qt_qtqt(quat_final, quat_prev, quat_next, ED_slider_factor_get(pso->slider)); } else { /* POSESLIDE_PUSH and POSESLIDE_RELAX. */ @@ -906,11 +630,12 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) normalize_qt(quat_curr); if (pso->mode == POSESLIDE_PUSH) { - interp_qt_qtqt(quat_final, quat_breakdown, quat_curr, 1.0f + pso->factor); + interp_qt_qtqt( + quat_final, quat_breakdown, quat_curr, 1.0f + ED_slider_factor_get(pso->slider)); } else { BLI_assert(pso->mode == POSESLIDE_RELAX); - interp_qt_qtqt(quat_final, quat_curr, quat_breakdown, pso->factor); + interp_qt_qtqt(quat_final, quat_curr, quat_breakdown, ED_slider_factor_get(pso->slider)); } } @@ -931,11 +656,11 @@ static void pose_slide_rest_pose_apply_vec3(tPoseSlideOp *pso, float vec[3], flo ((lock & PS_LOCK_Z) && (idx == 2))) { float diff_val = default_value - vec[idx]; if (pso->mode == POSESLIDE_RELAX_REST) { - vec[idx] += pso->factor * diff_val; + vec[idx] += ED_slider_factor_get(pso->slider) * diff_val; } else { /* Push */ - vec[idx] -= pso->factor * diff_val; + vec[idx] -= ED_slider_factor_get(pso->slider) * diff_val; } } } @@ -953,11 +678,11 @@ static void pose_slide_rest_pose_apply_other_rot(tPoseSlideOp *pso, float vec[4] for (int idx = 0; idx < 4; idx++) { float diff_val = default_values[idx] - vec[idx]; if (pso->mode == POSESLIDE_RELAX_REST) { - vec[idx] += pso->factor * diff_val; + vec[idx] += ED_slider_factor_get(pso->slider) * diff_val; } else { /* Push */ - vec[idx] -= pso->factor * diff_val; + vec[idx] -= ED_slider_factor_get(pso->slider) * diff_val; } } } @@ -1130,9 +855,7 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) char limits_str[UI_MAX_DRAW_STR]; char axis_str[50]; char mode_str[32]; - char overshoot_str[50]; - char precision_str[50]; - char increments_str[50]; + char slider_str[UI_MAX_DRAW_STR]; char bone_vis_str[50]; switch (pso->mode) { @@ -1203,29 +926,10 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) break; } - if (pso->overshoot) { - STRNCPY(overshoot_str, TIP_("[E] - Disable overshoot")); - } - else { - STRNCPY(overshoot_str, TIP_("[E] - Enable overshoot")); - } - - if (pso->precision) { - STRNCPY(precision_str, TIP_("[Shift] - Precision active")); - } - else { - STRNCPY(precision_str, TIP_("Shift - Hold for precision")); - } - - if (pso->increments) { - STRNCPY(increments_str, TIP_("[Ctrl] - Increments active")); - } - else { - STRNCPY(increments_str, TIP_("Ctrl - Hold for 10% increments")); - } - STRNCPY(bone_vis_str, TIP_("[H] - Toggle bone visibility")); + ED_slider_status_string_get(pso->slider, slider_str, sizeof(slider_str)); + if (hasNumInput(&pso->num)) { Scene *scene = pso->scene; char str_offs[NUM_STR_REP_LEN]; @@ -1237,12 +941,10 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) else { BLI_snprintf(status_str, sizeof(status_str), - "%s: %s | %s | %s | %s | %s", + "%s: %s | %s | %s", mode_str, limits_str, - overshoot_str, - precision_str, - increments_str, + slider_str, bone_vis_str); } @@ -1253,11 +955,15 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) /** * Common code for invoke() methods. */ -static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *pso) +static int pose_slide_invoke_common(bContext *C, wmOperator *op, const wmEvent *event) { tPChanFCurveLink *pfl; wmWindow *win = CTX_wm_window(C); + tPoseSlideOp *pso = op->customdata; + + ED_slider_init(pso->slider, event); + /* For each link, add all its keyframes to the search tree. */ for (pfl = pso->pfLinks.first; pfl; pfl = pfl->next) { LinkData *ld; @@ -1315,7 +1021,7 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p } else { BKE_report(op->reports, RPT_ERROR, "No keyframes to slide between"); - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } @@ -1340,38 +1046,14 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p /* Add a modal handler for this operator. */ WM_event_add_modal_handler(C, op); - /* Hide Bone Overlay. */ + /* Save current bone visibility. */ View3D *v3d = pso->area->spacedata.first; pso->overlay_flag = v3d->overlay.flag; - v3d->overlay.flag |= V3D_OVERLAY_HIDE_BONES; return OPERATOR_RUNNING_MODAL; } /** - * Calculate factor based on mouse movement, clamp or round to increments if - * enabled by the user. Store the new factor value. - */ -static void pose_slide_mouse_update_factor(tPoseSlideOp *pso, wmOperator *op, const wmEvent *event) -{ - const float factor_delta = (event->x - pso->last_cursor_x) / ((float)(SLIDE_PIXEL_DISTANCE)); - /* Reduced factor delta in precision mode (shift held). */ - pso->raw_factor += pso->precision ? (factor_delta / 8) : factor_delta; - pso->factor = pso->raw_factor; - pso->last_cursor_x = event->x; - - if (!pso->overshoot) { - pso->factor = clamp_f(pso->factor, 0, 1); - } - - if (pso->increments) { - pso->factor = round(pso->factor * 10) / 10; - } - - RNA_float_set(op->ptr, "factor", pso->factor); -} - -/** * Handle an event to toggle channels mode. */ static void pose_slide_toggle_channels_mode(wmOperator *op, @@ -1434,6 +1116,8 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) const bool has_numinput = hasNumInput(&pso->num); + do_pose_update = ED_slider_modal(pso->slider, event); + switch (event->type) { case LEFTMOUSE: /* Confirm. */ case EVT_RETKEY: @@ -1449,7 +1133,7 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Insert keyframes as required. */ pose_slide_autoKeyframe(C, pso); - pose_slide_exit(op); + pose_slide_exit(C, op); /* Done! */ return OPERATOR_FINISHED; @@ -1472,7 +1156,7 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) pose_slide_refresh(C, pso); /* Clean up temp data. */ - pose_slide_exit(op); + pose_slide_exit(C, op); /* Canceled! */ return OPERATOR_CANCELLED; @@ -1485,9 +1169,6 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) { /* Only handle mouse-move if not doing numinput. */ if (has_numinput == false) { - /* Update factor based on position of mouse. */ - pose_slide_mouse_update_factor(pso, op, event); - /* Update pose to reflect the new values (see below). */ do_pose_update = true; } @@ -1500,12 +1181,13 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Grab percentage from numeric input, and store this new value for redo * NOTE: users see ints, while internally we use a 0-1 float */ - value = pso->factor * 100.0f; + value = ED_slider_factor_get(pso->slider) * 100.0f; applyNumInput(&pso->num, &value); - pso->factor = value / 100.0f; - CLAMP(pso->factor, 0.0f, 1.0f); - RNA_float_set(op->ptr, "factor", pso->factor); + float factor = value / 100; + CLAMP(factor, 0.0f, 1.0f); + ED_slider_factor_set(pso->slider, factor); + RNA_float_set(op->ptr, "factor", ED_slider_factor_get(pso->slider)); /* Update pose to reflect the new values (see below) */ do_pose_update = true; @@ -1567,58 +1249,17 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) break; } - /* Overshoot. */ - case EVT_EKEY: { - pso->overshoot = !pso->overshoot; - do_pose_update = true; - break; - } - - /* Precision mode. */ - case EVT_LEFTSHIFTKEY: - case EVT_RIGHTSHIFTKEY: { - pso->precision = true; - do_pose_update = true; - break; - } - - /* Increments mode. */ - case EVT_LEFTCTRLKEY: - case EVT_RIGHTCTRLKEY: { - pso->increments = true; - do_pose_update = true; - break; - } - /* Toggle Bone visibility. */ case EVT_HKEY: { View3D *v3d = pso->area->spacedata.first; v3d->overlay.flag ^= V3D_OVERLAY_HIDE_BONES; + ED_region_tag_redraw(pso->region); } default: /* Some other unhandled key... */ break; } } - /* Precision and stepping only active while button is held. */ - else if (event->val == KM_RELEASE) { - switch (event->type) { - case EVT_LEFTSHIFTKEY: - case EVT_RIGHTSHIFTKEY: { - pso->precision = false; - do_pose_update = true; - break; - } - case EVT_LEFTCTRLKEY: - case EVT_RIGHTCTRLKEY: { - pso->increments = false; - do_pose_update = true; - break; - } - default: - break; - } - } else { /* Unhandled event - maybe it was some view manipulation? */ /* Allow to pass through. */ @@ -1652,10 +1293,10 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) /** * Common code for cancel() */ -static void pose_slide_cancel(bContext *UNUSED(C), wmOperator *op) +static void pose_slide_cancel(bContext *C, wmOperator *op) { /* Cleanup and done. */ - pose_slide_exit(op); + pose_slide_exit(C, op); } /** @@ -1675,7 +1316,7 @@ static int pose_slide_exec_common(bContext *C, wmOperator *op, tPoseSlideOp *pso pose_slide_autoKeyframe(C, pso); /* Cleanup and done. */ - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_FINISHED; } @@ -1743,23 +1384,14 @@ static void pose_slide_opdef_properties(wmOperatorType *ot) */ static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - tPoseSlideOp *pso; - /* Initialize data. */ if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } - pso = op->customdata; - - pso->last_cursor_x = event->x; - - /* Initialize factor so that it won't pop on first mouse move. */ - pose_slide_mouse_update_factor(pso, op, event); - /* Do common setup work. */ - return pose_slide_invoke_common(C, op, pso); + return pose_slide_invoke_common(C, op, event); } /** @@ -1771,7 +1403,7 @@ static int pose_slide_push_exec(bContext *C, wmOperator *op) /* Initialize data (from RNA-props). */ if (pose_slide_init(C, op, POSESLIDE_PUSH) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } @@ -1809,23 +1441,14 @@ void POSE_OT_push(wmOperatorType *ot) */ static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - tPoseSlideOp *pso; - /* Initialize data. */ if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } - pso = op->customdata; - - pso->last_cursor_x = event->x; - - /* Initialize factor so that it won't pop on first mouse move. */ - pose_slide_mouse_update_factor(pso, op, event); - /* Do common setup work. */ - return pose_slide_invoke_common(C, op, pso); + return pose_slide_invoke_common(C, op, event); } /** @@ -1837,7 +1460,7 @@ static int pose_slide_relax_exec(bContext *C, wmOperator *op) /* Initialize data (from RNA-props). */ if (pose_slide_init(C, op, POSESLIDE_RELAX) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } @@ -1874,23 +1497,14 @@ void POSE_OT_relax(wmOperatorType *ot) */ static int pose_slide_push_rest_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - tPoseSlideOp *pso; - /* Initialize data. */ if (pose_slide_init(C, op, POSESLIDE_PUSH_REST) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } - pso = op->customdata; - - pso->last_cursor_x = event->x; - - /* Initialize factor so that it won't pop on first mouse move. */ - pose_slide_mouse_update_factor(pso, op, event); - /* do common setup work */ - return pose_slide_invoke_common(C, op, pso); + return pose_slide_invoke_common(C, op, event); } /** @@ -1902,7 +1516,7 @@ static int pose_slide_push_rest_exec(bContext *C, wmOperator *op) /* Initialize data (from RNA-props). */ if (pose_slide_init(C, op, POSESLIDE_PUSH_REST) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } @@ -1940,23 +1554,14 @@ void POSE_OT_push_rest(wmOperatorType *ot) */ static int pose_slide_relax_rest_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - tPoseSlideOp *pso; - /* Initialize data. */ if (pose_slide_init(C, op, POSESLIDE_RELAX_REST) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } - pso = op->customdata; - - pso->last_cursor_x = event->x; - - /* Initialize factor so that it won't pop on first mouse move. */ - pose_slide_mouse_update_factor(pso, op, event); - /* Do common setup work. */ - return pose_slide_invoke_common(C, op, pso); + return pose_slide_invoke_common(C, op, event); } /** @@ -1968,7 +1573,7 @@ static int pose_slide_relax_rest_exec(bContext *C, wmOperator *op) /* Initialize data (from RNA-props). */ if (pose_slide_init(C, op, POSESLIDE_RELAX_REST) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } @@ -2006,23 +1611,14 @@ void POSE_OT_relax_rest(wmOperatorType *ot) */ static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - tPoseSlideOp *pso; - /* Initialize data. */ if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } - pso = op->customdata; - - pso->last_cursor_x = event->x; - - /* Initialize factor so that it won't pop on first mouse move. */ - pose_slide_mouse_update_factor(pso, op, event); - /* Do common setup work. */ - return pose_slide_invoke_common(C, op, pso); + return pose_slide_invoke_common(C, op, event); } /** @@ -2034,7 +1630,7 @@ static int pose_slide_breakdown_exec(bContext *C, wmOperator *op) /* Initialize data (from RNA-props). */ if (pose_slide_init(C, op, POSESLIDE_BREAKDOWN) == 0) { - pose_slide_exit(op); + pose_slide_exit(C, op); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt index a27975bc37b..d56edd43d7d 100644 --- a/source/blender/editors/asset/CMakeLists.txt +++ b/source/blender/editors/asset/CMakeLists.txt @@ -16,6 +16,7 @@ # ***** END GPL LICENSE BLOCK ***** set(INC + . ../include ../../blenkernel ../../blenlib @@ -30,10 +31,21 @@ set(INC_SYS ) set(SRC - asset_edit.cc - asset_list.cc - asset_ops.cc - asset_temp_id_consumer.cc + intern/asset_handle.cc + intern/asset_library_reference.cc + intern/asset_library_reference_enum.cc + intern/asset_list.cc + intern/asset_mark_clear.cc + intern/asset_ops.cc + intern/asset_temp_id_consumer.cc + + ED_asset_handle.h + ED_asset_library.h + ED_asset_list.h + ED_asset_list.hh + ED_asset_mark_clear.h + ED_asset_temp_id_consumer.h + intern/asset_library_reference.hh ) set(LIB diff --git a/source/blender/editors/asset/ED_asset_handle.h b/source/blender/editors/asset/ED_asset_handle.h new file mode 100644 index 00000000000..c51ce422c25 --- /dev/null +++ b/source/blender/editors/asset/ED_asset_handle.h @@ -0,0 +1,45 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + */ + +#pragma once + +#include "DNA_ID_enums.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct AssetHandle; +struct AssetLibraryReference; +struct bContext; + +const char *ED_asset_handle_get_name(const struct AssetHandle *asset); +struct AssetMetaData *ED_asset_handle_get_metadata(const struct AssetHandle *asset); +struct ID *ED_asset_handle_get_local_id(const struct AssetHandle *asset); +ID_Type ED_asset_handle_get_id_type(const struct AssetHandle *asset); +int ED_asset_handle_get_preview_icon_id(const struct AssetHandle *asset); +void ED_asset_handle_get_full_library_path(const struct bContext *C, + const struct AssetLibraryReference *asset_library, + const struct AssetHandle *asset, + char r_full_lib_path[]); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/asset/ED_asset_library.h b/source/blender/editors/asset/ED_asset_library.h new file mode 100644 index 00000000000..905d097d223 --- /dev/null +++ b/source/blender/editors/asset/ED_asset_library.h @@ -0,0 +1,35 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + */ + +#pragma once + +#include "DNA_asset_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int ED_asset_library_reference_to_enum_value(const AssetLibraryReference *library); +AssetLibraryReference ED_asset_library_reference_from_enum_value(int value); +const struct EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/asset/ED_asset_list.h b/source/blender/editors/asset/ED_asset_list.h new file mode 100644 index 00000000000..1e7f0f0de55 --- /dev/null +++ b/source/blender/editors/asset/ED_asset_list.h @@ -0,0 +1,54 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct AssetFilterSettings; +struct AssetHandle; +struct AssetLibraryReference; +struct bContext; +struct ID; +struct wmNotifier; + +void ED_assetlist_storage_fetch(const struct AssetLibraryReference *library_reference, + const struct AssetFilterSettings *filter_settings, + const struct bContext *C); +void ED_assetlist_ensure_previews_job(const struct AssetLibraryReference *library_reference, + struct bContext *C); +void ED_assetlist_clear(const struct AssetLibraryReference *library_reference, struct bContext *C); +bool ED_assetlist_storage_has_list_for_library(const AssetLibraryReference *library_reference); +void ED_assetlist_storage_tag_main_data_dirty(void); +void ED_assetlist_storage_id_remap(struct ID *id_old, struct ID *id_new); +void ED_assetlist_storage_exit(void); + +struct ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle); +const char *ED_assetlist_library_path(const struct AssetLibraryReference *library_reference); + +bool ED_assetlist_listen(const struct AssetLibraryReference *library_reference, + const struct wmNotifier *notifier); +int ED_assetlist_size(const struct AssetLibraryReference *library_reference); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/asset/ED_asset_list.hh b/source/blender/editors/asset/ED_asset_list.hh new file mode 100644 index 00000000000..7f41fba3457 --- /dev/null +++ b/source/blender/editors/asset/ED_asset_list.hh @@ -0,0 +1,38 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + */ + +#pragma once + +#include <string> + +#include "BLI_function_ref.hh" + +struct AssetLibraryReference; +struct AssetHandle; +struct bContext; +struct FileDirEntry; + +std::string ED_assetlist_asset_filepath_get(const bContext *C, + const AssetLibraryReference &library_reference, + const AssetHandle &asset_handle); + +/* Can return false to stop iterating. */ +using AssetListIterFn = blender::FunctionRef<bool(FileDirEntry &)>; +void ED_assetlist_iterate(const AssetLibraryReference *library_reference, AssetListIterFn fn); diff --git a/source/blender/editors/asset/ED_asset_mark_clear.h b/source/blender/editors/asset/ED_asset_mark_clear.h new file mode 100644 index 00000000000..cdd1f0d080b --- /dev/null +++ b/source/blender/editors/asset/ED_asset_mark_clear.h @@ -0,0 +1,37 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct bContext; +struct ID; + +bool ED_asset_mark_id(const struct bContext *C, struct ID *id); +bool ED_asset_clear_id(struct ID *id); + +bool ED_asset_can_mark_single_from_context(const struct bContext *C); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/asset/ED_asset_temp_id_consumer.h b/source/blender/editors/asset/ED_asset_temp_id_consumer.h new file mode 100644 index 00000000000..9af08c5c52b --- /dev/null +++ b/source/blender/editors/asset/ED_asset_temp_id_consumer.h @@ -0,0 +1,49 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + */ + +#pragma once + +#include "DNA_ID_enums.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct AssetTempIDConsumer AssetTempIDConsumer; + +struct AssetHandle; +struct AssetLibraryReference; +struct bContext; +struct Main; +struct ReportList; + +AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const struct AssetHandle *handle); +void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer); +struct ID *ED_asset_temp_id_consumer_ensure_local_id( + AssetTempIDConsumer *consumer, + const struct bContext *C, + const struct AssetLibraryReference *asset_library, + ID_Type id_type, + struct Main *bmain, + struct ReportList *reports); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/asset/asset_edit.cc b/source/blender/editors/asset/asset_edit.cc deleted file mode 100644 index f4860737193..00000000000 --- a/source/blender/editors/asset/asset_edit.cc +++ /dev/null @@ -1,155 +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. - */ - -/** \file - * \ingroup edasset - */ - -#include <memory> -#include <string> - -#include "BKE_asset.h" -#include "BKE_context.h" -#include "BKE_lib_id.h" - -#include "BLO_readfile.h" - -#include "DNA_ID.h" -#include "DNA_asset_types.h" -#include "DNA_space_types.h" - -#include "UI_interface_icons.h" - -#include "RNA_access.h" - -#include "ED_asset.h" - -using namespace blender; - -bool ED_asset_mark_id(const bContext *C, ID *id) -{ - if (id->asset_data) { - return false; - } - if (!BKE_id_can_be_asset(id)) { - return false; - } - - id_fake_user_set(id); - - id->asset_data = BKE_asset_metadata_create(); - - UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true); - - /* Important for asset storage to update properly! */ - ED_assetlist_storage_tag_main_data_dirty(); - - return true; -} - -bool ED_asset_clear_id(ID *id) -{ - if (!id->asset_data) { - return false; - } - BKE_asset_metadata_free(&id->asset_data); - /* Don't clear fake user here, there's no guarantee that it was actually set by - * #ED_asset_mark_id(), it might have been something/someone else. */ - - /* Important for asset storage to update properly! */ - ED_assetlist_storage_tag_main_data_dirty(); - - return true; -} - -bool ED_asset_can_make_single_from_context(const bContext *C) -{ - /* Context needs a "id" pointer to be set for #ASSET_OT_mark()/#ASSET_OT_clear() to use. */ - return CTX_data_pointer_get_type_silent(C, "id", &RNA_ID).data != nullptr; -} - -/* TODO better place? */ -/* TODO What about the setter and the `itemf` callback? */ -#include "BKE_preferences.h" -#include "DNA_asset_types.h" -#include "DNA_userdef_types.h" -int ED_asset_library_reference_to_enum_value(const AssetLibraryReference *library) -{ - /* Simple case: Predefined repository, just set the value. */ - if (library->type < ASSET_LIBRARY_CUSTOM) { - return library->type; - } - - /* Note that the path isn't checked for validity here. If an invalid library path is used, the - * Asset Browser can give a nice hint on what's wrong. */ - const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index( - &U, library->custom_library_index); - if (user_library) { - return ASSET_LIBRARY_CUSTOM + library->custom_library_index; - } - - BLI_assert(0); - return ASSET_LIBRARY_LOCAL; -} - -AssetLibraryReference ED_asset_library_reference_from_enum_value(int value) -{ - AssetLibraryReference library; - - /* Simple case: Predefined repository, just set the value. */ - if (value < ASSET_LIBRARY_CUSTOM) { - library.type = value; - library.custom_library_index = -1; - BLI_assert(ELEM(value, ASSET_LIBRARY_LOCAL)); - return library; - } - - const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index( - &U, value - ASSET_LIBRARY_CUSTOM); - - /* Note that the path isn't checked for validity here. If an invalid library path is used, the - * Asset Browser can give a nice hint on what's wrong. */ - const bool is_valid = (user_library->name[0] && user_library->path[0]); - if (!user_library) { - library.type = ASSET_LIBRARY_LOCAL; - library.custom_library_index = -1; - } - else if (user_library && is_valid) { - library.custom_library_index = value - ASSET_LIBRARY_CUSTOM; - library.type = ASSET_LIBRARY_CUSTOM; - } - return library; -} - -const char *ED_asset_handle_get_name(const AssetHandle *asset) -{ - return asset->file_data->name; -} - -void ED_asset_handle_get_full_library_path(const bContext *C, - const AssetLibraryReference *asset_library, - const AssetHandle *asset, - char r_full_lib_path[FILE_MAX_LIBEXTRA]) -{ - *r_full_lib_path = '\0'; - - std::string asset_path = ED_assetlist_asset_filepath_get(C, *asset_library, *asset); - if (asset_path.empty()) { - return; - } - - BLO_library_path_explode(asset_path.c_str(), r_full_lib_path, nullptr, nullptr); -} diff --git a/source/blender/editors/asset/intern/asset_handle.cc b/source/blender/editors/asset/intern/asset_handle.cc new file mode 100644 index 00000000000..aae85e61372 --- /dev/null +++ b/source/blender/editors/asset/intern/asset_handle.cc @@ -0,0 +1,75 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + * + * Asset-handle is a temporary design, not part of the core asset system design. + * + * Currently asset-list items are just file directory items (#FileDirEntry). So an asset-handle is + * just wraps a pointer to this. We try to abstract away the fact that it's just a file entry, + * although that doesn't always work (see #rna_def_asset_handle()). + */ + +#include <string> + +#include "DNA_asset_types.h" +#include "DNA_space_types.h" + +#include "BLO_readfile.h" + +#include "ED_asset_handle.h" +#include "ED_asset_list.hh" + +const char *ED_asset_handle_get_name(const AssetHandle *asset) +{ + return asset->file_data->name; +} + +AssetMetaData *ED_asset_handle_get_metadata(const AssetHandle *asset) +{ + return asset->file_data->asset_data; +} + +ID *ED_asset_handle_get_local_id(const AssetHandle *asset) +{ + return asset->file_data->id; +} + +ID_Type ED_asset_handle_get_id_type(const AssetHandle *asset) +{ + return static_cast<ID_Type>(asset->file_data->blentype); +} + +int ED_asset_handle_get_preview_icon_id(const AssetHandle *asset) +{ + return asset->file_data->preview_icon_id; +} + +void ED_asset_handle_get_full_library_path(const bContext *C, + const AssetLibraryReference *asset_library, + const AssetHandle *asset, + char r_full_lib_path[FILE_MAX_LIBEXTRA]) +{ + *r_full_lib_path = '\0'; + + std::string asset_path = ED_assetlist_asset_filepath_get(C, *asset_library, *asset); + if (asset_path.empty()) { + return; + } + + BLO_library_path_explode(asset_path.c_str(), r_full_lib_path, nullptr, nullptr); +} diff --git a/source/blender/editors/asset/intern/asset_library_reference.cc b/source/blender/editors/asset/intern/asset_library_reference.cc new file mode 100644 index 00000000000..df1d58df5f9 --- /dev/null +++ b/source/blender/editors/asset/intern/asset_library_reference.cc @@ -0,0 +1,50 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + */ + +#include "BLI_hash.hh" + +#include "asset_library_reference.hh" + +namespace blender::ed::asset { + +AssetLibraryReferenceWrapper::AssetLibraryReferenceWrapper(const AssetLibraryReference &reference) + : AssetLibraryReference(reference) +{ +} + +bool operator==(const AssetLibraryReferenceWrapper &a, const AssetLibraryReferenceWrapper &b) +{ + return (a.type == b.type) && (a.type == ASSET_LIBRARY_CUSTOM) ? + (a.custom_library_index == b.custom_library_index) : + true; +} + +uint64_t AssetLibraryReferenceWrapper::hash() const +{ + uint64_t hash1 = DefaultHash<decltype(type)>{}(type); + if (type != ASSET_LIBRARY_CUSTOM) { + return hash1; + } + + uint64_t hash2 = DefaultHash<decltype(custom_library_index)>{}(custom_library_index); + return hash1 ^ (hash2 * 33); /* Copied from DefaultHash for std::pair. */ +} + +} // namespace blender::ed::asset diff --git a/source/blender/editors/asset/intern/asset_library_reference.hh b/source/blender/editors/asset/intern/asset_library_reference.hh new file mode 100644 index 00000000000..7e8cb4a3472 --- /dev/null +++ b/source/blender/editors/asset/intern/asset_library_reference.hh @@ -0,0 +1,46 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + * + * Utility to extend #AssetLibraryReference with C++ functionality (operators, hash function, etc). + */ + +#pragma once + +#include <cstdint> + +#include "DNA_asset_types.h" + +namespace blender::ed::asset { + +/** + * Wrapper to add logic to the AssetLibraryReference DNA struct. + */ +class AssetLibraryReferenceWrapper : public AssetLibraryReference { + public: + /* Intentionally not `explicit`, allow implicit conversion for convenience. Might have to be + * NOLINT */ + AssetLibraryReferenceWrapper(const AssetLibraryReference &reference); + ~AssetLibraryReferenceWrapper() = default; + + friend bool operator==(const AssetLibraryReferenceWrapper &a, + const AssetLibraryReferenceWrapper &b); + uint64_t hash() const; +}; + +} // namespace blender::ed::asset diff --git a/source/blender/editors/asset/intern/asset_library_reference_enum.cc b/source/blender/editors/asset/intern/asset_library_reference_enum.cc new file mode 100644 index 00000000000..5a8fed6fea1 --- /dev/null +++ b/source/blender/editors/asset/intern/asset_library_reference_enum.cc @@ -0,0 +1,156 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + * + * Helpers to convert asset library references from and to enum values and RNA enums. + * In some cases it's simply not possible to reference an asset library with + * #AssetLibraryReferences. This API guarantees a safe translation to indices/enum values for as + * long as there is no change in the order of registered custom asset libraries. + */ + +#include "BLI_listbase.h" + +#include "BKE_preferences.h" + +#include "DNA_asset_types.h" +#include "DNA_userdef_types.h" + +#include "UI_resources.h" + +#include "RNA_define.h" + +#include "ED_asset_library.h" + +/** + * Return an index that can be used to uniquely identify \a library, assuming + * that all relevant indices were created with this function. + */ +int ED_asset_library_reference_to_enum_value(const AssetLibraryReference *library) +{ + /* Simple case: Predefined repository, just set the value. */ + if (library->type < ASSET_LIBRARY_CUSTOM) { + return library->type; + } + + /* Note that the path isn't checked for validity here. If an invalid library path is used, the + * Asset Browser can give a nice hint on what's wrong. */ + const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index( + &U, library->custom_library_index); + if (user_library) { + return ASSET_LIBRARY_CUSTOM + library->custom_library_index; + } + + BLI_assert_unreachable(); + return ASSET_LIBRARY_LOCAL; +} + +/** + * Return an asset library reference matching the index returned by + * #ED_asset_library_reference_to_enum_value(). + */ +AssetLibraryReference ED_asset_library_reference_from_enum_value(int value) +{ + AssetLibraryReference library; + + /* Simple case: Predefined repository, just set the value. */ + if (value < ASSET_LIBRARY_CUSTOM) { + library.type = value; + library.custom_library_index = -1; + BLI_assert(ELEM(value, ASSET_LIBRARY_LOCAL)); + return library; + } + + const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index( + &U, value - ASSET_LIBRARY_CUSTOM); + + /* Note that there is no check if the path exists here. If an invalid library path is used, the + * Asset Browser can give a nice hint on what's wrong. */ + const bool is_valid = (user_library->name[0] && user_library->path[0]); + if (!user_library) { + library.type = ASSET_LIBRARY_LOCAL; + library.custom_library_index = -1; + } + else if (user_library && is_valid) { + library.custom_library_index = value - ASSET_LIBRARY_CUSTOM; + library.type = ASSET_LIBRARY_CUSTOM; + } + return library; +} + +/** + * Translate all available asset libraries to an RNA enum, whereby the enum values match the result + * of #ED_asset_library_reference_to_enum_value() for any given library. + * + * Since this is meant for UI display, skips non-displayable libraries, that is, libraries with an + * empty name or path. + */ +const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf() +{ + const EnumPropertyItem predefined_items[] = { + /* For the future. */ + // {ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"}, + {ASSET_LIBRARY_LOCAL, + "LOCAL", + ICON_BLENDER, + "Current File", + "Show the assets currently available in this Blender session"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + EnumPropertyItem *item = nullptr; + int totitem = 0; + + /* Add separator if needed. */ + if (!BLI_listbase_is_empty(&U.asset_libraries)) { + const EnumPropertyItem sepr = {0, "", 0, "Custom", nullptr}; + RNA_enum_item_add(&item, &totitem, &sepr); + } + + int i = 0; + for (bUserAssetLibrary *user_library = (bUserAssetLibrary *)U.asset_libraries.first; + user_library; + user_library = user_library->next, i++) { + /* Note that the path itself isn't checked for validity here. If an invalid library path is + * used, the Asset Browser can give a nice hint on what's wrong. */ + const bool is_valid = (user_library->name[0] && user_library->path[0]); + if (!is_valid) { + continue; + } + + AssetLibraryReference library_reference; + library_reference.type = ASSET_LIBRARY_CUSTOM; + library_reference.custom_library_index = i; + + const int enum_value = ED_asset_library_reference_to_enum_value(&library_reference); + /* Use library path as description, it's a nice hint for users. */ + EnumPropertyItem tmp = { + enum_value, user_library->name, ICON_NONE, user_library->name, user_library->path}; + RNA_enum_item_add(&item, &totitem, &tmp); + } + + if (totitem) { + const EnumPropertyItem sepr = {0, "", 0, "Built-in", nullptr}; + RNA_enum_item_add(&item, &totitem, &sepr); + } + + /* Add predefined items. */ + RNA_enum_items_add(&item, &totitem, predefined_items); + + RNA_enum_item_end(&item, &totitem); + return item; +} diff --git a/source/blender/editors/asset/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc index dd1c5f360a0..445269d563e 100644 --- a/source/blender/editors/asset/asset_list.cc +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -26,12 +26,8 @@ #include <optional> #include <string> -#include "BKE_asset.h" #include "BKE_context.h" -#include "BKE_screen.h" -#include "BLI_function_ref.hh" -#include "BLI_hash.hh" #include "BLI_map.hh" #include "BLI_path_util.h" #include "BLI_utility_mixins.hh" @@ -41,9 +37,7 @@ #include "BKE_preferences.h" -#include "ED_asset.h" #include "ED_fileselect.h" -#include "ED_screen.h" #include "WM_api.h" #include "WM_types.h" @@ -51,48 +45,12 @@ /* XXX uses private header of file-space. */ #include "../space_file/filelist.h" -using namespace blender; +#include "ED_asset_handle.h" +#include "ED_asset_list.h" +#include "ED_asset_list.hh" +#include "asset_library_reference.hh" -/** - * Wrapper to add logic to the AssetLibraryReference DNA struct. - */ -class AssetLibraryReferenceWrapper { - const AssetLibraryReference reference_; - - public: - /* Intentionally not `explicit`, allow implicit conversion for convenience. Might have to be - * NOLINT */ - AssetLibraryReferenceWrapper(const AssetLibraryReference &reference); - ~AssetLibraryReferenceWrapper() = default; - - friend bool operator==(const AssetLibraryReferenceWrapper &a, - const AssetLibraryReferenceWrapper &b); - uint64_t hash() const; -}; - -AssetLibraryReferenceWrapper::AssetLibraryReferenceWrapper(const AssetLibraryReference &reference) - : reference_(reference) -{ -} - -bool operator==(const AssetLibraryReferenceWrapper &a, const AssetLibraryReferenceWrapper &b) -{ - return (a.reference_.type == b.reference_.type) && (a.reference_.type == ASSET_LIBRARY_CUSTOM) ? - (a.reference_.custom_library_index == b.reference_.custom_library_index) : - true; -} - -uint64_t AssetLibraryReferenceWrapper::hash() const -{ - uint64_t hash1 = DefaultHash<decltype(reference_.type)>{}(reference_.type); - if (reference_.type != ASSET_LIBRARY_CUSTOM) { - return hash1; - } - - uint64_t hash2 = DefaultHash<decltype(reference_.custom_library_index)>{}( - reference_.custom_library_index); - return hash1 ^ (hash2 * 33); /* Copied from DefaultHash for std::pair. */ -} +namespace blender::ed::asset { /* -------------------------------------------------------------------- */ /** \name Asset list API @@ -187,11 +145,6 @@ void AssetList::setup(const AssetFilterSettings *filter_settings) { FileList *files = filelist_; - /* TODO there should only be one (FileSelectAssetLibraryUID vs. AssetLibraryReference). */ - FileSelectAssetLibraryUID file_asset_lib_ref; - file_asset_lib_ref.type = library_ref_.type; - file_asset_lib_ref.custom_library_index = library_ref_.custom_library_index; - bUserAssetLibrary *user_library = nullptr; /* Ensure valid repository, or fall-back to local one. */ @@ -206,7 +159,7 @@ void AssetList::setup(const AssetFilterSettings *filter_settings) /* TODO pass options properly. */ filelist_setrecursion(files, 1); filelist_setsorting(files, FILE_SORT_ALPHA, false); - filelist_setlibrary(files, &file_asset_lib_ref); + filelist_setlibrary(files, &library_ref_); /* TODO different filtering settings require the list to be reread. That's a no-go for when we * want to allow showing the same asset library with different filter settings (as in, * different ID types). The filelist needs to be made smarter somehow, maybe goes together with @@ -469,10 +422,14 @@ AssetListStorage::AssetListMap &AssetListStorage::global_storage() /** \} */ +} // namespace blender::ed::asset + /* -------------------------------------------------------------------- */ /** \name C-API * \{ */ +using namespace blender::ed::asset; + /** * Invoke asset list reading, potentially in a parallel job. Won't wait until the job is done, * and may return earlier. @@ -506,7 +463,6 @@ bool ED_assetlist_storage_has_list_for_library(const AssetLibraryReference *libr return AssetListStorage::lookup_list(*library_reference) != nullptr; } -/* TODO expose AssetList with an iterator? */ void ED_assetlist_iterate(const AssetLibraryReference *library_reference, AssetListIterFn fn) { AssetList *list = AssetListStorage::lookup_list(*library_reference); @@ -536,11 +492,12 @@ std::string ED_assetlist_asset_filepath_get(const bContext *C, const AssetLibraryReference &library_reference, const AssetHandle &asset_handle) { - if (asset_handle.file_data->id || !asset_handle.file_data->asset_data) { + if (ED_asset_handle_get_local_id(&asset_handle) || + !ED_asset_handle_get_metadata(&asset_handle)) { return {}; } const char *library_path = ED_assetlist_library_path(&library_reference); - if (!library_path) { + if (!library_path && C) { library_path = assetlist_library_path_from_sfile_get_hack(C); } if (!library_path) { @@ -554,11 +511,6 @@ std::string ED_assetlist_asset_filepath_get(const bContext *C, return path; } -ID *ED_assetlist_asset_local_id_get(const AssetHandle *asset_handle) -{ - return asset_handle->file_data->asset_data ? asset_handle->file_data->id : nullptr; -} - ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle) { ImBuf *imbuf = filelist_file_getimage(asset_handle->file_data); diff --git a/source/blender/editors/asset/intern/asset_mark_clear.cc b/source/blender/editors/asset/intern/asset_mark_clear.cc new file mode 100644 index 00000000000..ba348e38823 --- /dev/null +++ b/source/blender/editors/asset/intern/asset_mark_clear.cc @@ -0,0 +1,83 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup edasset + * + * Functions for marking and clearing assets. + */ + +#include <memory> +#include <string> + +#include "BKE_asset.h" +#include "BKE_context.h" +#include "BKE_lib_id.h" + +#include "BLO_readfile.h" + +#include "DNA_ID.h" +#include "DNA_asset_types.h" +#include "DNA_space_types.h" + +#include "UI_interface_icons.h" + +#include "RNA_access.h" + +#include "ED_asset_list.h" +#include "ED_asset_mark_clear.h" + +bool ED_asset_mark_id(const bContext *C, ID *id) +{ + if (id->asset_data) { + return false; + } + if (!BKE_id_can_be_asset(id)) { + return false; + } + + id_fake_user_set(id); + + id->asset_data = BKE_asset_metadata_create(); + + UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true); + + /* Important for asset storage to update properly! */ + ED_assetlist_storage_tag_main_data_dirty(); + + return true; +} + +bool ED_asset_clear_id(ID *id) +{ + if (!id->asset_data) { + return false; + } + BKE_asset_metadata_free(&id->asset_data); + /* Don't clear fake user here, there's no guarantee that it was actually set by + * #ED_asset_mark_id(), it might have been something/someone else. */ + + /* Important for asset storage to update properly! */ + ED_assetlist_storage_tag_main_data_dirty(); + + return true; +} + +bool ED_asset_can_mark_single_from_context(const bContext *C) +{ + /* Context needs a "id" pointer to be set for #ASSET_OT_mark()/#ASSET_OT_clear() to use. */ + return CTX_data_pointer_get_type_silent(C, "id", &RNA_ID).data != nullptr; +} diff --git a/source/blender/editors/asset/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index 79edd1f8a6a..30f8bfe554a 100644 --- a/source/blender/editors/asset/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -169,7 +169,7 @@ class AssetClearHelper { public: void operator()(PointerRNAVec &ids); - void reportResults(ReportList &reports) const; + void reportResults(const bContext *C, ReportList &reports) const; bool wasSuccessful() const; private: @@ -198,10 +198,22 @@ void AssetClearHelper::operator()(PointerRNAVec &ids) } } -void AssetClearHelper::reportResults(ReportList &reports) const +void AssetClearHelper::reportResults(const bContext *C, ReportList &reports) const { if (!wasSuccessful()) { - BKE_report(&reports, RPT_ERROR, "No asset data-blocks selected/focused"); + bool is_valid; + /* Dedicated error message for when there is an active asset detected, but it's not an ID local + * to this file. Helps users better understanding what's going on. */ + if (AssetHandle active_asset = CTX_wm_asset_handle(C, &is_valid); + is_valid && !ED_asset_handle_get_local_id(&active_asset)) { + BKE_report(&reports, + RPT_ERROR, + "No asset data-blocks from the current file selected (assets must be stored in " + "the current file to be able to edit or clear them)"); + } + else { + BKE_report(&reports, RPT_ERROR, "No asset data-blocks selected/focused"); + } } else if (stats.tot_cleared == 1) { /* If only one data-block: Give more useful message by printing asset name. */ @@ -224,7 +236,7 @@ static int asset_clear_exec(bContext *C, wmOperator *op) AssetClearHelper clear_helper; clear_helper(ids); - clear_helper.reportResults(*op->reports); + clear_helper.reportResults(C, *op->reports); if (!clear_helper.wasSuccessful()) { return OPERATOR_CANCELLED; diff --git a/source/blender/editors/asset/asset_temp_id_consumer.cc b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc index 24e1fc86fef..bed35fdeeb5 100644 --- a/source/blender/editors/asset/asset_temp_id_consumer.cc +++ b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc @@ -21,6 +21,8 @@ * Uses the `BLO_library_temp_xxx()` API internally. */ +#include <new> + #include "DNA_asset_types.h" #include "DNA_space_types.h" @@ -32,7 +34,8 @@ #include "MEM_guardedalloc.h" -#include "ED_asset.h" +#include "ED_asset_handle.h" +#include "ED_asset_temp_id_consumer.h" using namespace blender; @@ -53,7 +56,7 @@ class AssetTemporaryIDConsumer : NonCopyable, NonMovable { ID *get_local_id() { - return ED_assetlist_asset_local_id_get(&handle_); + return ED_asset_handle_get_local_id(&handle_); } ID *import_id(const bContext *C, diff --git a/source/blender/editors/geometry/geometry_attributes.c b/source/blender/editors/geometry/geometry_attributes.c index 9b034d82a51..5cb491f116a 100644 --- a/source/blender/editors/geometry/geometry_attributes.c +++ b/source/blender/editors/geometry/geometry_attributes.c @@ -47,6 +47,21 @@ static bool geometry_attributes_poll(bContext *C) BKE_id_attributes_supported(data); } +static bool geometry_attributes_remove_poll(bContext *C) +{ + if (!geometry_attributes_poll(C)) { + return false; + } + + Object *ob = ED_object_context(C); + ID *data = (ob) ? ob->data : NULL; + if (BKE_id_attributes_active_get(data) != NULL) { + return true; + } + + return false; +} + static const EnumPropertyItem *geometry_attribute_domain_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), @@ -160,7 +175,7 @@ void GEOMETRY_OT_attribute_remove(wmOperatorType *ot) /* api callbacks */ ot->exec = geometry_attribute_remove_exec; - ot->poll = geometry_attributes_poll; + ot->poll = geometry_attributes_remove_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c index 196fb88ea55..7795eed7c21 100644 --- a/source/blender/editors/gpencil/annotate_draw.c +++ b/source/blender/editors/gpencil/annotate_draw.c @@ -918,7 +918,7 @@ void ED_annotation_draw_view3d( return; } - /* when rendering to the offscreen buffer we don't want to + /* When rendering to the off-screen buffer we don't want to * deal with the camera border, otherwise map the coords to the camera border. */ if ((rv3d->persp == RV3D_CAMOB) && !(G.f & G_FLAG_RENDER_VIEWPORT)) { rctf rectf; diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 4b0c5ccd285..9bf44370c80 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -1320,7 +1320,7 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p) p->area = curarea; p->region = region; p->v2d = ®ion->v2d; - p->align_flag = &ts->gpencil_seq_align; + p->align_flag = &ts->gpencil_v2d_align; /* check that gpencil data is allowed to be drawn */ if (sseq->mainb == SEQ_DRAW_SEQUENCE) { @@ -1339,7 +1339,7 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p) p->area = curarea; p->region = region; p->v2d = ®ion->v2d; - p->align_flag = &ts->gpencil_ima_align; + p->align_flag = &ts->gpencil_v2d_align; break; } case SPACE_CLIP: { diff --git a/source/blender/editors/gpencil/gpencil_armature.c b/source/blender/editors/gpencil/gpencil_armature.c index c800851bb08..86ec6c53fe6 100644 --- a/source/blender/editors/gpencil/gpencil_armature.c +++ b/source/blender/editors/gpencil/gpencil_armature.c @@ -80,7 +80,7 @@ static int gpencil_bone_looper(Object *ob, { /* We want to apply the function bone_func to every bone * in an armature -- feed bone_looper the first bone and - * a pointer to the bone_func and watch it go!. The int count + * a pointer to the bone_func and watch it go! The int count * can be useful for counting bones with a certain property * (e.g. skinnable) */ diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index b1e57079d28..8baac26bed3 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -1355,6 +1355,14 @@ static int gpencil_merge_layer_exec(bContext *C, wmOperator *op) LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) { /* Try to find frame in destination layer hash table. */ bGPDframe *gpf_dst = BLI_ghash_lookup(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum)); + /* Apply layer transformation. */ + LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) { + for (int p = 0; p < gps_src->totpoints; p++) { + bGPDspoint *pt = &gps_src->points[p]; + mul_v3_m4v3(&pt->x, gpl_src->layer_mat, &pt->x); + } + } + /* Add to tail all strokes. */ if (gpf_dst) { BLI_movelisttolist(&gpf_dst->strokes, &gpf_src->strokes); diff --git a/source/blender/editors/include/BIF_glutil.h b/source/blender/editors/include/BIF_glutil.h index 1d688b2ad68..0a1cf643f37 100644 --- a/source/blender/editors/include/BIF_glutil.h +++ b/source/blender/editors/include/BIF_glutil.h @@ -49,17 +49,17 @@ typedef struct IMMDrawPixelsTexState { IMMDrawPixelsTexState immDrawPixelsTexSetup(int builtin); /** - * immDrawPixelsTex - Functions like a limited glDrawPixels, but actually draws the + * #immDrawPixelsTex - Functions like a limited #glDrawPixels, but actually draws the * image using textures, which can be tremendously faster on low-end * cards, and also avoids problems with the raster position being - * clipped when offscreen. Pixel unpacking parameters and - * the glPixelZoom values are _not_ respected. + * clipped when off-screen. Pixel unpacking parameters and + * the #glPixelZoom values are _not_ respected. * - * \attention Use immDrawPixelsTexSetup before calling this function. + * \attention Use #immDrawPixelsTexSetup before calling this function. * - * \attention This routine makes many assumptions: the rect data + * \attention This routine makes many assumptions: the `rect` data * is expected to be in RGBA byte or float format, and the - * modelview and projection matrices are assumed to define a + * model-view and projection matrices are assumed to define a * 1-to-1 mapping to screen space. */ void immDrawPixelsTex(IMMDrawPixelsTexState *state, diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 50e53acb376..75c02082bd3 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -290,7 +290,7 @@ typedef enum eAnimFilter_Flags { * (i.e. scene visibility criteria). * * XXX: it's hard to think of any examples where this *ISN'T* the case... - * perhaps becomes implicit?. + * perhaps becomes implicit? */ ANIMFILTER_DATA_VISIBLE = (1 << 0), /** channel is visible within the channel-list hierarchy diff --git a/source/blender/editors/include/ED_asset.h b/source/blender/editors/include/ED_asset.h index 0058c0615c3..42faf716560 100644 --- a/source/blender/editors/include/ED_asset.h +++ b/source/blender/editors/include/ED_asset.h @@ -16,66 +16,19 @@ /** \file * \ingroup editors + * + * The public API for assets is defined in dedicated headers. This is a utility file that just + * includes all of these. */ #pragma once -#include "DNA_ID_enums.h" - #ifdef __cplusplus extern "C" { #endif -struct AssetFilterSettings; -struct AssetLibraryReference; -struct Main; -struct ReportList; -struct bContext; -struct wmNotifier; - -typedef struct AssetTempIDConsumer AssetTempIDConsumer; - -bool ED_asset_mark_id(const struct bContext *C, struct ID *id); -bool ED_asset_clear_id(struct ID *id); - -bool ED_asset_can_make_single_from_context(const struct bContext *C); - -int ED_asset_library_reference_to_enum_value(const struct AssetLibraryReference *library); -struct AssetLibraryReference ED_asset_library_reference_from_enum_value(int value); - -const char *ED_asset_handle_get_name(const AssetHandle *asset); -void ED_asset_handle_get_full_library_path(const struct bContext *C, - const AssetLibraryReference *asset_library, - const AssetHandle *asset, - char r_full_lib_path[]); - -AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const AssetHandle *handle); -void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer); -struct ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer, - const struct bContext *C, - const AssetLibraryReference *asset_library, - ID_Type id_type, - struct Main *bmain, - struct ReportList *reports); - -void ED_assetlist_storage_fetch(const struct AssetLibraryReference *library_reference, - const struct AssetFilterSettings *filter_settings, - const struct bContext *C); -void ED_assetlist_ensure_previews_job(const struct AssetLibraryReference *library_reference, - struct bContext *C); -void ED_assetlist_clear(const struct AssetLibraryReference *library_reference, struct bContext *C); -bool ED_assetlist_storage_has_list_for_library(const AssetLibraryReference *library_reference); -void ED_assetlist_storage_tag_main_data_dirty(void); -void ED_assetlist_storage_id_remap(struct ID *id_old, struct ID *id_new); -void ED_assetlist_storage_exit(void); - -ID *ED_assetlist_asset_local_id_get(const AssetHandle *asset_handle); -struct ImBuf *ED_assetlist_asset_image_get(const AssetHandle *asset_handle); -const char *ED_assetlist_library_path(const struct AssetLibraryReference *library_reference); - -bool ED_assetlist_listen(const struct AssetLibraryReference *library_reference, - const struct wmNotifier *notifier); -int ED_assetlist_size(const struct AssetLibraryReference *library_reference); +/* Barely anything here. Just general editor level functions. Actual asset level code is in + * dedicated headers. */ void ED_operatortypes_asset(void); @@ -83,17 +36,13 @@ void ED_operatortypes_asset(void); } #endif -/* TODO move to C++ asset-list header? */ -#ifdef __cplusplus +#include "../asset/ED_asset_handle.h" +#include "../asset/ED_asset_library.h" +#include "../asset/ED_asset_list.h" +#include "../asset/ED_asset_mark_clear.h" +#include "../asset/ED_asset_temp_id_consumer.h" -# include <string> - -std::string ED_assetlist_asset_filepath_get(const bContext *C, - const AssetLibraryReference &library_reference, - const AssetHandle &asset_handle); - -# include "BLI_function_ref.hh" -/* Can return false to stop iterating. */ -using AssetListIterFn = blender::FunctionRef<bool(FileDirEntry &)>; -void ED_assetlist_iterate(const AssetLibraryReference *library_reference, AssetListIterFn fn); +/* C++ only headers. */ +#ifdef __cplusplus +# include "../asset/ED_asset_list.hh" #endif diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h index 2f8faf1b2bd..d2d22dd38dc 100644 --- a/source/blender/editors/include/ED_keyframes_draw.h +++ b/source/blender/editors/include/ED_keyframes_draw.h @@ -28,10 +28,7 @@ extern "C" { #endif struct AnimData; -struct CacheFile; -struct DLRBT_Tree; struct FCurve; -struct ListBase; struct MaskLayer; struct Object; struct Scene; @@ -42,99 +39,6 @@ struct bAnimContext; struct bDopeSheet; struct bGPDlayer; -/* ****************************** Base Structs ****************************** */ - -/* Information about the stretch of time from current to the next column */ -typedef struct ActKeyBlockInfo { - /* Combination of flags from all curves. */ - short flag; - /* Mask of flags that differ between curves. */ - short conflict; - - /* Selection flag. */ - char sel; -} ActKeyBlockInfo; - -/* Keyframe Column Struct */ -typedef struct ActKeyColumn { - /* ListBase linkage */ - struct ActKeyColumn *next, *prev; - - /* sorting-tree linkage */ - /** 'children' of this node, less than and greater than it (respectively) */ - struct ActKeyColumn *left, *right; - /** parent of this node in the tree */ - struct ActKeyColumn *parent; - /** DLRB_BLACK or DLRB_RED */ - char tree_col; - - /* keyframe info */ - /** eBezTripe_KeyframeType */ - char key_type; - /** eKeyframeHandleDrawOpts */ - char handle_type; - /** eKeyframeExtremeDrawOpts */ - char extreme_type; - short sel; - float cfra; - - /* key-block info */ - ActKeyBlockInfo block; - - /* number of curves and keys in this column */ - short totcurve, totkey, totblock; -} ActKeyColumn; - -/* ActKeyBlockInfo - Flag */ -typedef enum eActKeyBlock_Hold { - /* Key block represents a moving hold */ - ACTKEYBLOCK_FLAG_MOVING_HOLD = (1 << 0), - /* Key block represents a static hold */ - ACTKEYBLOCK_FLAG_STATIC_HOLD = (1 << 1), - /* Key block represents any kind of hold */ - ACTKEYBLOCK_FLAG_ANY_HOLD = (1 << 2), - /* The curve segment uses non-bezier interpolation */ - ACTKEYBLOCK_FLAG_NON_BEZIER = (1 << 3), - /* The block is grease pencil */ - ACTKEYBLOCK_FLAG_GPENCIL = (1 << 4), -} eActKeyBlock_Flag; - -/* *********************** Keyframe Drawing ****************************** */ - -/* options for keyframe shape drawing */ -typedef enum eKeyframeShapeDrawOpts { - /* only the border */ - KEYFRAME_SHAPE_FRAME = 0, - /* only the inside filling */ - KEYFRAME_SHAPE_INSIDE, - /* the whole thing */ - KEYFRAME_SHAPE_BOTH, -} eKeyframeShapeDrawOpts; - -/* Handle type. */ -typedef enum eKeyframeHandleDrawOpts { - /* Don't draw */ - KEYFRAME_HANDLE_NONE = 0, - /* Various marks in order of increasing display priority. */ - KEYFRAME_HANDLE_AUTO_CLAMP, - KEYFRAME_HANDLE_AUTO, - KEYFRAME_HANDLE_VECTOR, - KEYFRAME_HANDLE_ALIGNED, - KEYFRAME_HANDLE_FREE, -} eKeyframeHandleDrawOpts; - -/* Extreme type. */ -typedef enum eKeyframeExtremeDrawOpts { - KEYFRAME_EXTREME_NONE = 0, - /* Minimum/maximum present. */ - KEYFRAME_EXTREME_MIN = (1 << 0), - KEYFRAME_EXTREME_MAX = (1 << 1), - /* Grouped keys have different states. */ - KEYFRAME_EXTREME_MIXED = (1 << 2), - /* Both neighbors are equal to this key. */ - KEYFRAME_EXTREME_FLAT = (1 << 3), -} eKeyframeExtremeDrawOpts; - /* draw simple diamond-shape keyframe */ /* caller should set up vertex format, bind GPU_SHADER_KEYFRAME_DIAMOND, * immBegin(GPU_PRIM_POINTS, n), then call this n times */ @@ -216,59 +120,6 @@ void draw_masklay_channel(struct View2D *v2d, float yscale_fac, int saction_flag); -/* Keydata Generation --------------- */ -/* F-Curve */ -void fcurve_to_keylist(struct AnimData *adt, - struct FCurve *fcu, - struct DLRBT_Tree *keys, - int saction_flag); -/* Action Group */ -void agroup_to_keylist(struct AnimData *adt, - struct bActionGroup *agrp, - struct DLRBT_Tree *keys, - int saction_flag); -/* Action */ -void action_to_keylist(struct AnimData *adt, - struct bAction *act, - struct DLRBT_Tree *keys, - int saction_flag); -/* Object */ -void ob_to_keylist(struct bDopeSheet *ads, - struct Object *ob, - struct DLRBT_Tree *keys, - int saction_flag); -/* Cache File */ -void cachefile_to_keylist(struct bDopeSheet *ads, - struct CacheFile *cache_file, - struct DLRBT_Tree *keys, - int saction_flag); -/* Scene */ -void scene_to_keylist(struct bDopeSheet *ads, - struct Scene *sce, - struct DLRBT_Tree *keys, - int saction_flag); -/* DopeSheet Summary */ -void summary_to_keylist(struct bAnimContext *ac, struct DLRBT_Tree *keys, int saction_flag); -/* Grease Pencil datablock summary */ -void gpencil_to_keylist(struct bDopeSheet *ads, - struct bGPdata *gpd, - struct DLRBT_Tree *keys, - const bool active); -/* Grease Pencil Layer */ -void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, struct DLRBT_Tree *keys); -/* Mask */ -void mask_to_keylist(struct bDopeSheet *ads, struct MaskLayer *masklay, struct DLRBT_Tree *keys); - -/* ActKeyColumn API ---------------- */ -/* Comparator callback used for ActKeyColumns and cframe float-value pointer */ -short compare_ak_cfraPtr(void *node, void *data); - -/* Checks if ActKeyColumn has any block data */ -bool actkeyblock_is_valid(ActKeyColumn *ac); - -/* Checks if ActKeyColumn can be used as a block (i.e. drawn/used to detect "holds") */ -int actkeyblock_get_valid_hold(ActKeyColumn *ac); - #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/ED_keyframes_keylist.h b/source/blender/editors/include/ED_keyframes_keylist.h new file mode 100644 index 00000000000..be3eac66771 --- /dev/null +++ b/source/blender/editors/include/ED_keyframes_keylist.h @@ -0,0 +1,192 @@ +/* + * 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) (C) 2009 Blender Foundation, Joshua Leung + * All rights reserved. + */ + +/** \file + * \ingroup editors + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct AnimData; +struct CacheFile; +struct DLRBT_Tree; +struct FCurve; +struct MaskLayer; +struct Object; +struct Scene; +struct bAnimContext; +struct bDopeSheet; +struct bGPDlayer; + +/* ****************************** Base Structs ****************************** */ + +/* Information about the stretch of time from current to the next column */ +typedef struct ActKeyBlockInfo { + /* Combination of flags from all curves. */ + short flag; + /* Mask of flags that differ between curves. */ + short conflict; + + /* Selection flag. */ + char sel; +} ActKeyBlockInfo; + +/* Keyframe Column Struct */ +typedef struct ActKeyColumn { + /* ListBase linkage */ + struct ActKeyColumn *next, *prev; + + /* sorting-tree linkage */ + /** 'children' of this node, less than and greater than it (respectively) */ + struct ActKeyColumn *left, *right; + /** parent of this node in the tree */ + struct ActKeyColumn *parent; + /** DLRB_BLACK or DLRB_RED */ + char tree_col; + + /* keyframe info */ + /** eBezTripe_KeyframeType */ + char key_type; + /** eKeyframeHandleDrawOpts */ + char handle_type; + /** eKeyframeExtremeDrawOpts */ + char extreme_type; + short sel; + float cfra; + + /* key-block info */ + ActKeyBlockInfo block; + + /* number of curves and keys in this column */ + short totcurve, totkey, totblock; +} ActKeyColumn; + +/* ActKeyBlockInfo - Flag */ +typedef enum eActKeyBlock_Hold { + /* Key block represents a moving hold */ + ACTKEYBLOCK_FLAG_MOVING_HOLD = (1 << 0), + /* Key block represents a static hold */ + ACTKEYBLOCK_FLAG_STATIC_HOLD = (1 << 1), + /* Key block represents any kind of hold */ + ACTKEYBLOCK_FLAG_ANY_HOLD = (1 << 2), + /* The curve segment uses non-bezier interpolation */ + ACTKEYBLOCK_FLAG_NON_BEZIER = (1 << 3), + /* The block is grease pencil */ + ACTKEYBLOCK_FLAG_GPENCIL = (1 << 4), +} eActKeyBlock_Flag; + +/* *********************** Keyframe Drawing ****************************** */ + +/* options for keyframe shape drawing */ +typedef enum eKeyframeShapeDrawOpts { + /* only the border */ + KEYFRAME_SHAPE_FRAME = 0, + /* only the inside filling */ + KEYFRAME_SHAPE_INSIDE, + /* the whole thing */ + KEYFRAME_SHAPE_BOTH, +} eKeyframeShapeDrawOpts; + +/* Handle type. */ +typedef enum eKeyframeHandleDrawOpts { + /* Don't draw */ + KEYFRAME_HANDLE_NONE = 0, + /* Various marks in order of increasing display priority. */ + KEYFRAME_HANDLE_AUTO_CLAMP, + KEYFRAME_HANDLE_AUTO, + KEYFRAME_HANDLE_VECTOR, + KEYFRAME_HANDLE_ALIGNED, + KEYFRAME_HANDLE_FREE, +} eKeyframeHandleDrawOpts; + +/* Extreme type. */ +typedef enum eKeyframeExtremeDrawOpts { + KEYFRAME_EXTREME_NONE = 0, + /* Minimum/maximum present. */ + KEYFRAME_EXTREME_MIN = (1 << 0), + KEYFRAME_EXTREME_MAX = (1 << 1), + /* Grouped keys have different states. */ + KEYFRAME_EXTREME_MIXED = (1 << 2), + /* Both neighbors are equal to this key. */ + KEYFRAME_EXTREME_FLAT = (1 << 3), +} eKeyframeExtremeDrawOpts; + +/* ******************************* Methods ****************************** */ + +/* Key-data Generation --------------- */ + +/* F-Curve */ +void fcurve_to_keylist(struct AnimData *adt, + struct FCurve *fcu, + struct DLRBT_Tree *keys, + int saction_flag); +/* Action Group */ +void agroup_to_keylist(struct AnimData *adt, + struct bActionGroup *agrp, + struct DLRBT_Tree *keys, + int saction_flag); +/* Action */ +void action_to_keylist(struct AnimData *adt, + struct bAction *act, + struct DLRBT_Tree *keys, + int saction_flag); +/* Object */ +void ob_to_keylist(struct bDopeSheet *ads, + struct Object *ob, + struct DLRBT_Tree *keys, + int saction_flag); +/* Cache File */ +void cachefile_to_keylist(struct bDopeSheet *ads, + struct CacheFile *cache_file, + struct DLRBT_Tree *keys, + int saction_flag); +/* Scene */ +void scene_to_keylist(struct bDopeSheet *ads, + struct Scene *sce, + struct DLRBT_Tree *keys, + int saction_flag); +/* DopeSheet Summary */ +void summary_to_keylist(struct bAnimContext *ac, struct DLRBT_Tree *keys, int saction_flag); +/* Grease Pencil datablock summary */ +void gpencil_to_keylist(struct bDopeSheet *ads, + struct bGPdata *gpd, + struct DLRBT_Tree *keys, + const bool active); +/* Grease Pencil Layer */ +void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, struct DLRBT_Tree *keys); +/* Mask */ +void mask_to_keylist(struct bDopeSheet *ads, struct MaskLayer *masklay, struct DLRBT_Tree *keys); + +/* ActKeyColumn API ---------------- */ +/* Comparator callback used for ActKeyColumns and cframe float-value pointer */ +short compare_ak_cfraPtr(void *node, void *data); + +/* Checks if ActKeyColumn has any block data */ +bool actkeyblock_is_valid(ActKeyColumn *ac); + +/* Checks if ActKeyColumn can be used as a block (i.e. drawn/used to detect "holds") */ +int actkeyblock_get_valid_hold(ActKeyColumn *ac); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/include/ED_render.h b/source/blender/editors/include/ED_render.h index 0fb06639dbf..5cdcbbab42a 100644 --- a/source/blender/editors/include/ED_render.h +++ b/source/blender/editors/include/ED_render.h @@ -72,13 +72,12 @@ struct Scene *ED_render_job_get_current_scene(const struct bContext *C); * - PR_NODE_RENDER: preview is rendered for node editor * - PR_ICON_DEFERRED: No render, we just ensure deferred icon data gets generated. */ - -enum { +typedef enum ePreviewRenderMethod { PR_BUTS_RENDER = 0, PR_ICON_RENDER = 1, PR_NODE_RENDER = 2, PR_ICON_DEFERRED = 3, -}; +} ePreviewRenderMethod; void ED_preview_ensure_dbase(void); void ED_preview_free_dbase(void); diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h index 953f26aa45f..0105af843bb 100644 --- a/source/blender/editors/include/ED_util.h +++ b/source/blender/editors/include/ED_util.h @@ -24,6 +24,7 @@ #pragma once #include "BLI_compiler_attrs.h" +#include "WM_types.h" #ifdef __cplusplus extern "C" { @@ -61,6 +62,24 @@ void ED_region_draw_mouse_line_cb(const struct bContext *C, void ED_region_image_metadata_draw( int x, int y, struct ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy); +/* Slider */ +struct tSlider; + +struct tSlider *ED_slider_create(struct bContext *C); +void ED_slider_init(struct tSlider *slider, const struct wmEvent *event); +bool ED_slider_modal(struct tSlider *slider, const struct wmEvent *event); +void ED_slider_destroy(struct bContext *C, struct tSlider *slider); + +void ED_slider_status_string_get(const struct tSlider *slider, + char *status_string, + const size_t size_of_status_string); + +float ED_slider_factor_get(struct tSlider *slider); +void ED_slider_factor_set(struct tSlider *slider, float factor); + +bool ED_slider_allow_overshoot_get(struct tSlider *slider); +void ED_slider_allow_overshoot_set(struct tSlider *slider, const bool value); + /* ************** XXX OLD CRUFT WARNING ************* */ void apply_keyb_grid( diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index a25aac5803c..a6e465d04e8 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -35,9 +35,11 @@ extern "C" { /* Struct Declarations */ struct ARegion; +struct AssetHandle; struct AssetFilterSettings; struct AutoComplete; struct EnumPropertyItem; +struct FileDirEntry; struct FileSelectParams; struct ID; struct IDProperty; @@ -769,9 +771,8 @@ int UI_but_return_value_get(uiBut *but); void UI_but_drag_set_id(uiBut *but, struct ID *id); void UI_but_drag_set_asset(uiBut *but, - const char *name, + const struct AssetHandle *asset, const char *path, - int id_type, int import_type, /* eFileAssetImportType */ int icon, struct ImBuf *imb, diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index 8191a9a9062..4ee7df89487 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -143,17 +143,19 @@ void UI_view2d_view_orthoSpecial(struct ARegion *region, struct View2D *v2d, con void UI_view2d_view_restore(const struct bContext *C); /* grid drawing */ -void UI_view2d_constant_grid_draw(const struct View2D *v2d, float step); void UI_view2d_multi_grid_draw( const struct View2D *v2d, int colorid, float step, int level_size, int totlevels); void UI_view2d_draw_lines_y__values(const struct View2D *v2d); void UI_view2d_draw_lines_x__values(const struct View2D *v2d); -void UI_view2d_draw_lines_x__discrete_values(const struct View2D *v2d); -void UI_view2d_draw_lines_x__discrete_time(const struct View2D *v2d, const struct Scene *scene); +void UI_view2d_draw_lines_x__discrete_values(const struct View2D *v2d, bool display_minor_lines); +void UI_view2d_draw_lines_x__discrete_time(const struct View2D *v2d, + const struct Scene *scene, + bool display_minor_lines); void UI_view2d_draw_lines_x__discrete_frames_or_seconds(const struct View2D *v2d, const struct Scene *scene, - bool display_seconds); + bool display_seconds, + bool display_minor_lines); void UI_view2d_draw_lines_x__frames_or_seconds(const struct View2D *v2d, const struct Scene *scene, bool display_seconds); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 9aeb8dc62ad..001e5b65fb9 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -3391,7 +3391,7 @@ static void ui_but_free_type_specific(uiBut *but) #include "BLI_threads.h" /* can be called with C==NULL */ -ATTR_NO_OPT static void ui_but_free(const bContext *C, uiBut *but) +static void ui_but_free(const bContext *C, uiBut *but) { if (!BLI_thread_is_main()) { printf("Evil!\n"); @@ -6210,10 +6210,12 @@ void UI_but_drag_set_id(uiBut *but, ID *id) but->dragpoin = (void *)id; } +/** + * \param asset: May be passed from a temporary variable, drag data only stores a copy of this. + */ void UI_but_drag_set_asset(uiBut *but, - const char *name, + const AssetHandle *asset, const char *path, - int id_type, int import_type, int icon, struct ImBuf *imb, @@ -6221,9 +6223,10 @@ void UI_but_drag_set_asset(uiBut *but, { wmDragAsset *asset_drag = MEM_mallocN(sizeof(*asset_drag), "wmDragAsset"); - BLI_strncpy(asset_drag->name, name, sizeof(asset_drag->name)); + asset_drag->asset_handle = MEM_mallocN(sizeof(asset_drag->asset_handle), + "wmDragAsset asset handle"); + *asset_drag->asset_handle = *asset; asset_drag->path = path; - asset_drag->id_type = id_type; asset_drag->import_type = import_type; but->dragtype = WM_DRAG_ASSET; diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 3049e2bd7b8..d917534895d 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -952,7 +952,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev } /* If the button represents an id, it can set the "id" context pointer. */ - if (U.experimental.use_asset_browser && ED_asset_can_make_single_from_context(C)) { + if (U.experimental.use_asset_browser && ED_asset_can_mark_single_from_context(C)) { ID *id = CTX_data_pointer_get_type(C, "id", &RNA_ID).data; /* Gray out items depending on if data-block is an asset. Preferably this could be done via diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 9ff5ab566cb..85e28a8eb2a 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3066,7 +3066,7 @@ static bool ui_textedit_set_cursor_pos_foreach_glyph(const char *UNUSED(str), /** * \param x: Screen space cursor location - #wmEvent.x * - * \note ``but->block->aspect`` is used here, so drawing button style is getting scaled too. + * \note `but->block->aspect` is used here, so drawing button style is getting scaled too. */ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, const float x) { @@ -3929,7 +3929,7 @@ static void ui_do_but_textedit( /* exception that's useful for number buttons, some keyboard * numpads have a comma instead of a period */ - if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) { /* could use data->min*/ + if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) { /* Could use `data->min`. */ if (event->type == EVT_PADPERIOD && ascii == ',') { ascii = '.'; utf8_buf = NULL; /* force ascii fallback */ @@ -9490,7 +9490,7 @@ static void ui_list_activate_row_from_index( /* A bit ugly, set the active index in RNA directly. That's because a button that's * scrolled away in the list box isn't created at all. * The custom activate operator (#uiList.custom_activate_opname) is not called in this case - * (which may need the row button context).*/ + * (which may need the row button context). */ RNA_property_int_set(&listbox->rnapoin, listbox->rnaprop, index); RNA_property_update(C, &listbox->rnapoin, listbox->rnaprop); ui_apply_but_undo(listbox); @@ -9509,7 +9509,7 @@ static int ui_list_get_increment(const uiList *ui_list, const int type, const in increment = (type == EVT_UPARROWKEY) ? -columns : columns; } else { - /* Left or right in grid layouts or any direction in single column layouts increments by 1. */ + /* Left or right in grid layouts or any direction in single column layouts increments by 1. */ increment = ELEM(type, EVT_UPARROWKEY, EVT_LEFTARROWKEY, WHEELUPMOUSE) ? -1 : 1; } diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 43ac646f053..0ffc5659191 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -66,6 +66,7 @@ #include "ED_datafiles.h" #include "ED_keyframes_draw.h" +#include "ED_keyframes_keylist.h" #include "ED_render.h" #include "UI_interface.h" @@ -285,18 +286,15 @@ static void vicon_small_tri_right_draw(int x, int y, int w, int UNUSED(h), float static void vicon_keytype_draw_wrapper( int x, int y, int w, int h, float alpha, short key_type, short handle_type) { - /* init dummy theme state for Action Editor - where these colors are defined - * (since we're doing this offscreen, free from any particular space_id) - */ + /* Initialize dummy theme state for Action Editor - where these colors are defined + * (since we're doing this off-screen, free from any particular space_id). */ struct bThemeState theme_state; UI_Theme_Store(&theme_state); UI_SetTheme(SPACE_ACTION, RGN_TYPE_WINDOW); - /* the "x" and "y" given are the bottom-left coordinates of the icon, - * while the draw_keyframe_shape() function needs the midpoint for - * the keyframe - */ + /* The "x" and "y" given are the bottom-left coordinates of the icon, + * while the #draw_keyframe_shape() function needs the midpoint for the keyframe. */ const float xco = x + w / 2 + 0.5f; const float yco = y + h / 2 + 0.5f; @@ -1346,8 +1344,8 @@ void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool bi case ICON_TYPE_PREVIEW: { ID *id = (icon->id_type != 0) ? icon->obj : NULL; PreviewImage *prv = id ? BKE_previewimg_id_ensure(id) : icon->obj; - /* Using jobs for screen previews crashes due to offscreen rendering. - * XXX would be nicer if PreviewImage could store if it supports jobs */ + /* Using jobs for screen previews crashes due to off-screen rendering. + * XXX: would be nicer if #PreviewImage could store if it supports jobs. */ const bool use_jobs = !id || (GS(id->name) != ID_SCR); if (prv) { @@ -2143,7 +2141,7 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id) static int ui_id_screen_get_icon(const bContext *C, ID *id) { BKE_icon_id_ensure(id); - /* Don't use jobs here, offscreen rendering doesn't like this and crashes. */ + /* Don't use jobs here, off-screen rendering doesn't like this and crashes. */ ui_id_icon_render(C, id, false); return id->icon_id; diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 8b9539f1d33..ea7bcf57f52 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -2911,6 +2911,12 @@ static uiBut *ui_item_menu(uiLayout *layout, void uiItemM_ptr(uiLayout *layout, MenuType *mt, const char *name, int icon) { + uiBlock *block = layout->root->block; + bContext *C = block->evil_C; + if (WM_menutype_poll(C, mt) == false) { + return; + } + if (!name) { name = CTX_IFACE_(mt->translation_context, mt->label); } @@ -2949,6 +2955,9 @@ void uiItemMContents(uiLayout *layout, const char *menuname) uiBlock *block = layout->root->block; bContext *C = block->evil_C; + if (WM_menutype_poll(C, mt) == false) { + return; + } UI_menutype_draw(C, mt, layout); } diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 376a41ff9bb..3ab49b8773b 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -675,7 +675,7 @@ static int override_remove_button_exec(bContext *C, wmOperator *op) PropertyRNA *src_prop; RNA_id_pointer_create(id->override_library->reference, &id_refptr); if (!RNA_path_resolve_property(&id_refptr, oprop->rna_path, &src, &src_prop)) { - BLI_assert(0 && "Failed to create matching source (linked data) RNA pointer"); + BLI_assert_msg(0, "Failed to create matching source (linked data) RNA pointer"); } } diff --git a/source/blender/editors/interface/interface_region_popover.c b/source/blender/editors/interface/interface_region_popover.c index b8c4d8ddb09..f8f19c2e43d 100644 --- a/source/blender/editors/interface/interface_region_popover.c +++ b/source/blender/editors/interface/interface_region_popover.c @@ -188,7 +188,7 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v } } - /* Estimated a maximum size so we don't go offscreen for low height + /* Estimated a maximum size so we don't go off-screen for low height * areas near the bottom of the window on refreshes. */ handle->max_size_y = UI_UNIT_Y * 16.0f; } diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc index 5a05813f947..a691fff4963 100644 --- a/source/blender/editors/interface/interface_template_asset_view.cc +++ b/source/blender/editors/interface/interface_template_asset_view.cc @@ -52,25 +52,25 @@ static void asset_view_item_but_drag_set(uiBut *but, AssetViewListData *list_data, AssetHandle *asset_handle) { - ID *id = asset_handle->file_data->id; + ID *id = ED_asset_handle_get_local_id(asset_handle); if (id != nullptr) { UI_but_drag_set_id(but, id); return; } - const blender::StringRef asset_list_path = ED_assetlist_library_path(&list_data->asset_library); char blend_path[FILE_MAX_LIBEXTRA]; + /* Context can be null here, it's only needed for a File Browser specific hack that should go + * away before too long. */ + ED_asset_handle_get_full_library_path( + nullptr, &list_data->asset_library, asset_handle, blend_path); - char path[FILE_MAX_LIBEXTRA]; - BLI_join_dirfile(path, sizeof(path), asset_list_path.data(), asset_handle->file_data->relpath); - if (BLO_library_path_explode(path, blend_path, nullptr, nullptr)) { + if (blend_path[0]) { ImBuf *imbuf = ED_assetlist_asset_image_get(asset_handle); UI_but_drag_set_asset(but, - asset_handle->file_data->name, + asset_handle, BLI_strdup(blend_path), - asset_handle->file_data->blentype, FILE_ASSET_IMPORT_APPEND, - asset_handle->file_data->preview_icon_id, + ED_asset_handle_get_preview_icon_id(asset_handle), imbuf, 1.0f); } @@ -101,8 +101,8 @@ static void asset_view_draw_item(uiList *ui_list, uiBut *but = uiDefIconTextBut(block, UI_BTYPE_PREVIEW_TILE, 0, - asset_handle->file_data->preview_icon_id, - asset_handle->file_data->name, + ED_asset_handle_get_preview_icon_id(asset_handle), + ED_asset_handle_get_name(asset_handle), 0, 0, size_x, @@ -114,7 +114,7 @@ static void asset_view_draw_item(uiList *ui_list, 0, ""); ui_def_but_icon(but, - asset_handle->file_data->preview_icon_id, + ED_asset_handle_get_preview_icon_id(asset_handle), /* NOLINTNEXTLINE: bugprone-suspicious-enum-usage */ UI_HAS_ICON | UI_BUT_ICON_PREVIEW); if (!ui_list->dyn_data->custom_drag_optype) { diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc index eaab33e32c9..0ab45ea0f81 100644 --- a/source/blender/editors/interface/interface_template_list.cc +++ b/source/blender/editors/interface/interface_template_list.cc @@ -1170,7 +1170,7 @@ uiList *uiTemplateList_ex(uiLayout *layout, enum uiTemplateListFlags flags, void *customdata) { - TemplateListInputData input_data = {nullptr}; + TemplateListInputData input_data = {{nullptr}}; uiListType *ui_list_type; if (!ui_template_list_data_retrieve(listtype_name, list_id, @@ -1271,7 +1271,9 @@ PointerRNA *UI_list_custom_activate_operator_set(uiList *ui_list, } if (create_properties) { - WM_operator_properties_alloc(&dyn_data->custom_activate_opptr, nullptr, opname); + PointerRNA *opptr = dyn_data->custom_activate_opptr; + WM_operator_properties_alloc( + &dyn_data->custom_activate_opptr, opptr ? (IDProperty **)&opptr->data : nullptr, opname); } return dyn_data->custom_activate_opptr; @@ -1291,7 +1293,9 @@ PointerRNA *UI_list_custom_drag_operator_set(uiList *ui_list, } if (create_properties) { - WM_operator_properties_alloc(&dyn_data->custom_drag_opptr, nullptr, opname); + PointerRNA *opptr = dyn_data->custom_drag_opptr; + WM_operator_properties_alloc( + &dyn_data->custom_drag_opptr, opptr ? (IDProperty **)&opptr->data : nullptr, opname); } return dyn_data->custom_drag_opptr; diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index afac254f542..c9bfd883332 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -1089,7 +1089,7 @@ bTheme *UI_GetTheme(void) } /** - * for the rare case we need to temp swap in a different theme (offscreen render) + * For the rare case we need to temp swap in a different theme (off-screen render). */ void UI_Theme_Store(struct bThemeState *theme_state) { diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index e9804840801..23c8a0d35bf 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -1194,78 +1194,6 @@ void UI_view2d_view_restore(const bContext *C) /** \name Grid-Line Drawing * \{ */ -/* Draw a constant grid in given 2d-region */ -void UI_view2d_constant_grid_draw(const View2D *v2d, float step) -{ - float start_x, start_y; - int count_x, count_y; - - start_x = v2d->cur.xmin; - if (start_x < 0.0) { - start_x += -(float)fmod(v2d->cur.xmin, step); - } - else { - start_x += (step - (float)fmod(v2d->cur.xmin, step)); - } - - if (start_x > v2d->cur.xmax) { - count_x = 0; - } - else { - count_x = (v2d->cur.xmax - start_x) / step + 1; - } - - start_y = v2d->cur.ymin; - if (start_y < 0.0) { - start_y += -(float)fmod(v2d->cur.ymin, step); - } - else { - start_y += (step - (float)fabs(fmod(v2d->cur.ymin, step))); - } - - if (start_y > v2d->cur.ymax) { - count_y = 0; - } - else { - count_y = (v2d->cur.ymax - start_y) / step + 1; - } - - if (count_x > 0 || count_y > 0) { - GPUVertFormat *format = immVertexFormat(); - const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - const uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - float theme_color[3]; - - UI_GetThemeColorShade3fv(TH_BACK, -10, theme_color); - - immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); - immBegin(GPU_PRIM_LINES, count_x * 2 + count_y * 2 + 4); - - immAttr3fv(color, theme_color); - for (int i = 0; i < count_x; start_x += step, i++) { - immVertex2f(pos, start_x, v2d->cur.ymin); - immVertex2f(pos, start_x, v2d->cur.ymax); - } - - for (int i = 0; i < count_y; start_y += step, i++) { - immVertex2f(pos, v2d->cur.xmin, start_y); - immVertex2f(pos, v2d->cur.xmax, start_y); - } - - /* X and Y axis */ - UI_GetThemeColorShade3fv(TH_BACK, -18, theme_color); - - immAttr3fv(color, theme_color); - immVertex2f(pos, 0.0f, v2d->cur.ymin); - immVertex2f(pos, 0.0f, v2d->cur.ymax); - immVertex2f(pos, v2d->cur.xmin, 0.0f); - immVertex2f(pos, v2d->cur.xmax, 0.0f); - - immEnd(); - immUnbindProgram(); - } -} - /* Draw a multi-level grid in given 2d-region */ void UI_view2d_multi_grid_draw( const View2D *v2d, int colorid, float step, int level_size, int totlevels) @@ -1914,7 +1842,7 @@ float UI_view2d_scale_get_y(const View2D *v2d) return BLI_rcti_size_y(&v2d->mask) / BLI_rctf_size_y(&v2d->cur); } /** - * Same as ``UI_view2d_scale_get() - 1.0f / x, y`` + * Same as `UI_view2d_scale_get() - 1.0f / x, y`. */ void UI_view2d_scale_get_inverse(const View2D *v2d, float *r_x, float *r_y) { diff --git a/source/blender/editors/interface/view2d_draw.c b/source/blender/editors/interface/view2d_draw.c index f7ef8c06389..1496ce027ae 100644 --- a/source/blender/editors/interface/view2d_draw.c +++ b/source/blender/editors/interface/view2d_draw.c @@ -326,10 +326,27 @@ static void draw_horizontal_scale_indicators(const ARegion *region, const float xmin = rect->xmin; const float xmax = rect->xmax; - for (uint i = 0; i < steps; i++) { + char text[32]; + + /* Calculate max_label_count and draw_frequency based on largest visible label. */ + to_string(to_string_data, start, 0, sizeof(text), text); + const float left_text_width = BLF_width(font_id, text, strlen(text)); + to_string(to_string_data, start + steps * distance, 0, sizeof(text), text); + const float right_text_width = BLF_width(font_id, text, strlen(text)); + const float max_text_width = max_ff(left_text_width, right_text_width); + const float max_label_count = BLI_rcti_size_x(&v2d->mask) / (max_text_width + 10.0f); + const int draw_frequency = ceil((float)steps / max_label_count); + + if (draw_frequency == 0) { + BLF_batch_draw_end(); + GPU_matrix_pop_projection(); + return; + } + + const int start_index = abs((int)(start / distance)) % draw_frequency; + for (uint i = start_index; i < steps; i += draw_frequency) { const float xpos_view = start + i * distance; const float xpos_region = UI_view2d_view_to_region_x(v2d, xpos_view); - char text[32]; to_string(to_string_data, xpos_view, distance, sizeof(text), text); const float text_width = BLF_width(font_id, text, strlen(text)); @@ -339,7 +356,6 @@ static void draw_horizontal_scale_indicators(const ARegion *region, } BLF_batch_draw_end(); - GPU_matrix_pop_projection(); } @@ -413,11 +429,15 @@ static void view_to_string__frame_number( } static void view_to_string__time( - void *user_data, float v2d_pos, float UNUSED(v2d_step), uint max_len, char *r_str) + void *user_data, float v2d_pos, float v2d_step, uint max_len, char *r_str) { const Scene *scene = (const Scene *)user_data; - const int brevity_level = 0; + int brevity_level = 0; + if (U.timecode_style == USER_TIMECODE_MINIMAL && v2d_step >= FPS) { + brevity_level = 1; + } + BLI_timecode_string_from_time( r_str, max_len, brevity_level, v2d_pos / (float)FPS, FPS, U.timecode_style); } @@ -460,10 +480,11 @@ float UI_view2d_grid_resolution_y__values(const struct View2D *v2d) /* Line Drawing API **************************************************/ -void UI_view2d_draw_lines_x__discrete_values(const View2D *v2d) +void UI_view2d_draw_lines_x__discrete_values(const View2D *v2d, bool display_minor_lines) { const uint major_line_distance = view2d_major_step_x__discrete(v2d); - view2d_draw_lines(v2d, major_line_distance, major_line_distance > 1, 'v'); + view2d_draw_lines( + v2d, major_line_distance, display_minor_lines && (major_line_distance > 1), 'v'); } void UI_view2d_draw_lines_x__values(const View2D *v2d) @@ -478,21 +499,25 @@ void UI_view2d_draw_lines_y__values(const View2D *v2d) view2d_draw_lines(v2d, major_line_distance, true, 'h'); } -void UI_view2d_draw_lines_x__discrete_time(const View2D *v2d, const Scene *scene) +void UI_view2d_draw_lines_x__discrete_time(const View2D *v2d, + const Scene *scene, + bool display_minor_lines) { const float major_line_distance = view2d_major_step_x__time(v2d, scene); - view2d_draw_lines(v2d, major_line_distance, major_line_distance > 1, 'v'); + view2d_draw_lines( + v2d, major_line_distance, display_minor_lines && (major_line_distance > 1), 'v'); } void UI_view2d_draw_lines_x__discrete_frames_or_seconds(const View2D *v2d, const Scene *scene, - bool display_seconds) + bool display_seconds, + bool display_minor_lines) { if (display_seconds) { - UI_view2d_draw_lines_x__discrete_time(v2d, scene); + UI_view2d_draw_lines_x__discrete_time(v2d, scene, display_minor_lines); } else { - UI_view2d_draw_lines_x__discrete_values(v2d); + UI_view2d_draw_lines_x__discrete_values(v2d, display_minor_lines); } } @@ -501,7 +526,7 @@ void UI_view2d_draw_lines_x__frames_or_seconds(const View2D *v2d, bool display_seconds) { if (display_seconds) { - UI_view2d_draw_lines_x__discrete_time(v2d, scene); + UI_view2d_draw_lines_x__discrete_time(v2d, scene, true); } else { UI_view2d_draw_lines_x__values(v2d); diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c index 880d27e1615..ad71f4d9da7 100644 --- a/source/blender/editors/mask/mask_add.c +++ b/source/blender/editors/mask/mask_add.c @@ -361,8 +361,10 @@ static bool add_vertex_extrude(const bContext *C, } } - // print_v2("", tangent_point); - // printf("%d\n", point_index); +#if 0 + print_v2("", tangent_point); + printf("%d\n", point_index); +#endif mask_spline_add_point_at_index(spline, point_index); diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index 73b3fb9724e..b2379610f65 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -482,8 +482,34 @@ bool ED_mesh_color_remove_named(Mesh *me, const char *name) return false; } +/*********************** General poll ************************/ + +static bool layers_poll(bContext *C) +{ + Object *ob = ED_object_context(C); + ID *data = (ob) ? ob->data : NULL; + return (ob && !ID_IS_LINKED(ob) && ob->type == OB_MESH && data && !ID_IS_LINKED(data)); +} + /*********************** Sculpt Vertex colors operators ************************/ +static bool sculpt_vertex_color_remove_poll(bContext *C) +{ + if (!layers_poll(C)) { + return false; + } + + Object *ob = ED_object_context(C); + Mesh *me = ob->data; + CustomData *vdata = GET_CD_DATA(me, vdata); + const int active = CustomData_get_active_layer(vdata, CD_PROP_COLOR); + if (active != -1) { + return true; + } + + return false; +} + /* NOTE: keep in sync with #ED_mesh_uv_texture_add. */ int ED_mesh_sculpt_color_add(Mesh *me, const char *name, const bool active_set, const bool do_init) { @@ -591,11 +617,21 @@ bool ED_mesh_sculpt_color_remove_named(Mesh *me, const char *name) /*********************** UV texture operators ************************/ -static bool layers_poll(bContext *C) +static bool uv_texture_remove_poll(bContext *C) { + if (!layers_poll(C)) { + return false; + } + Object *ob = ED_object_context(C); - ID *data = (ob) ? ob->data : NULL; - return (ob && !ID_IS_LINKED(ob) && ob->type == OB_MESH && data && !ID_IS_LINKED(data)); + Mesh *me = ob->data; + CustomData *ldata = GET_CD_DATA(me, ldata); + const int active = CustomData_get_active_layer(ldata, CD_MLOOPUV); + if (active != -1) { + return true; + } + + return false; } static int mesh_uv_texture_add_exec(bContext *C, wmOperator *UNUSED(op)) @@ -657,7 +693,7 @@ void MESH_OT_uv_texture_remove(wmOperatorType *ot) ot->idname = "MESH_OT_uv_texture_remove"; /* api callbacks */ - ot->poll = layers_poll; + ot->poll = uv_texture_remove_poll; ot->exec = mesh_uv_texture_remove_exec; /* flags */ @@ -666,6 +702,23 @@ void MESH_OT_uv_texture_remove(wmOperatorType *ot) /*********************** vertex color operators ************************/ +static bool vertex_color_remove_poll(bContext *C) +{ + if (!layers_poll(C)) { + return false; + } + + Object *ob = ED_object_context(C); + Mesh *me = ob->data; + CustomData *ldata = GET_CD_DATA(me, ldata); + const int active = CustomData_get_active_layer(ldata, CD_MLOOPCOL); + if (active != -1) { + return true; + } + + return false; +} + static int mesh_vertex_color_add_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); @@ -714,7 +767,7 @@ void MESH_OT_vertex_color_remove(wmOperatorType *ot) /* api callbacks */ ot->exec = mesh_vertex_color_remove_exec; - ot->poll = layers_poll; + ot->poll = vertex_color_remove_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -770,7 +823,7 @@ void MESH_OT_sculpt_vertex_color_remove(wmOperatorType *ot) /* api callbacks */ ot->exec = mesh_sculpt_vertex_color_remove_exec; - ot->poll = layers_poll; + ot->poll = sculpt_vertex_color_remove_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index 7962e687d7e..6df29316c07 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -339,7 +339,7 @@ void ED_operatormacros_mesh(void) ot = WM_operatortype_append_macro("MESH_OT_polybuild_face_at_cursor_move", "Face at Cursor Move", - "", + NULL, OPTYPE_UNDO | OPTYPE_REGISTER); WM_operatortype_macro_define(ot, "MESH_OT_polybuild_face_at_cursor"); otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); @@ -348,7 +348,7 @@ void ED_operatormacros_mesh(void) ot = WM_operatortype_append_macro("MESH_OT_polybuild_split_at_cursor_move", "Split at Cursor Move", - "", + NULL, OPTYPE_UNDO | OPTYPE_REGISTER); WM_operatortype_macro_define(ot, "MESH_OT_polybuild_split_at_cursor"); otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); @@ -357,7 +357,7 @@ void ED_operatormacros_mesh(void) ot = WM_operatortype_append_macro("MESH_OT_polybuild_transform_at_cursor_move", "Transform at Cursor Move", - "", + NULL, OPTYPE_UNDO | OPTYPE_REGISTER); WM_operatortype_macro_define(ot, "MESH_OT_polybuild_transform_at_cursor"); otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); @@ -366,7 +366,7 @@ void ED_operatormacros_mesh(void) ot = WM_operatortype_append_macro("MESH_OT_polybuild_extrude_at_cursor_move", "Extrude at Cursor Move", - "", + NULL, OPTYPE_UNDO | OPTYPE_REGISTER); WM_operatortype_macro_define(ot, "MESH_OT_polybuild_transform_at_cursor"); otmacro = WM_operatortype_macro_define(ot, "MESH_OT_extrude_edges_indiv"); diff --git a/source/blender/editors/object/object_facemap_ops.c b/source/blender/editors/object/object_facemap_ops.c index 3d4d3c8f622..92f3d28878c 100644 --- a/source/blender/editors/object/object_facemap_ops.c +++ b/source/blender/editors/object/object_facemap_ops.c @@ -176,6 +176,21 @@ static bool face_map_supported_edit_mode_poll(bContext *C) return false; } +static bool face_map_supported_remove_poll(bContext *C) +{ + if (!face_map_supported_poll(C)) { + return false; + } + + Object *ob = ED_object_context(C); + bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1); + if (fmap) { + return true; + } + + return false; +} + static int face_map_add_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); @@ -225,7 +240,7 @@ void OBJECT_OT_face_map_remove(struct wmOperatorType *ot) ot->description = "Remove a face map from the active object"; /* api callbacks */ - ot->poll = face_map_supported_poll; + ot->poll = face_map_supported_remove_poll; ot->exec = face_map_remove_exec; /* flags */ diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index f2bbd6d5084..5a629058c81 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -368,7 +368,7 @@ static PTCacheEdit *pe_get_current(Depsgraph *depsgraph, Scene *scene, Object *o else if (pset->edittype == PE_TYPE_SOFTBODY && pid->type == PTCACHE_TYPE_SOFTBODY) { if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) { pset->flag |= PE_FADE_TIME; - // NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB; + /* Nice to have but doesn't work: `pset->brushtype = PE_BRUSH_COMB;`. */ PE_create_particle_edit(depsgraph, scene, ob, pid->cache, NULL); } edit = pid->cache->edit; @@ -377,7 +377,7 @@ static PTCacheEdit *pe_get_current(Depsgraph *depsgraph, Scene *scene, Object *o else if (pset->edittype == PE_TYPE_CLOTH && pid->type == PTCACHE_TYPE_CLOTH) { if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) { pset->flag |= PE_FADE_TIME; - // NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB; + /* Nice to have but doesn't work: `pset->brushtype = PE_BRUSH_COMB;`. */ PE_create_particle_edit(depsgraph, scene, ob, pid->cache, NULL); } edit = pid->cache->edit; diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c index 387d10d538b..2668846284d 100644 --- a/source/blender/editors/physics/particle_object.c +++ b/source/blender/editors/physics/particle_object.c @@ -1331,7 +1331,12 @@ static int duplicate_particle_systems_exec(bContext *C, wmOperator *op) const bool duplicate_settings = RNA_boolean_get(op->ptr, "use_duplicate_settings"); Scene *scene = CTX_data_scene(C); Object *ob = ED_object_active_context(C); + /* Context pointer is only valid in the Properties Editor. */ ParticleSystem *psys = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data; + if (psys == NULL) { + psys = psys_get_current(ob); + } + copy_particle_systems_to_object( C, scene, ob, psys, ob, PAR_COPY_SPACE_OBJECT, duplicate_settings); return OPERATOR_FINISHED; diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 17aaa5aa79d..d3307ebf274 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -767,7 +767,7 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) sizey = (scene->r.size * scene->r.ysch) / 100; /* corrects render size with actual size, not every card supports non-power-of-two dimensions */ - DRW_opengl_context_enable(); /* Offscreen creation needs to be done in DRW context. */ + DRW_opengl_context_enable(); /* Off-screen creation needs to be done in DRW context. */ ofs = GPU_offscreen_create(sizex, sizey, true, true, err_out); DRW_opengl_context_disable(); diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index c7fa2a0ec87..fe1e850dcba 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -234,7 +234,7 @@ static Scene *preview_get_scene(Main *pr_main) return pr_main->scenes.first; } -static const char *preview_collection_name(const char pr_type) +static const char *preview_collection_name(const ePreviewType pr_type) { switch (pr_type) { case MA_FLAT: @@ -265,10 +265,7 @@ static const char *preview_collection_name(const char pr_type) } } -static void set_preview_visibility(Scene *scene, - ViewLayer *view_layer, - char pr_type, - int pr_method) +static void switch_preview_collection_visibilty(ViewLayer *view_layer, const ePreviewType pr_type) { /* Set appropriate layer as visible. */ LayerCollection *lc = view_layer->layer_collections.first; @@ -282,7 +279,11 @@ static void set_preview_visibility(Scene *scene, lc->collection->flag |= COLLECTION_RESTRICT_RENDER; } } +} +static void switch_preview_floor_visibility(ViewLayer *view_layer, + const ePreviewRenderMethod pr_method) +{ /* Hide floor for icon renders. */ LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { if (STREQ(base->object->id.name + 2, "Floor")) { @@ -294,7 +295,15 @@ static void set_preview_visibility(Scene *scene, } } } +} +static void set_preview_visibility(Scene *scene, + ViewLayer *view_layer, + const ePreviewType pr_type, + const ePreviewRenderMethod pr_method) +{ + switch_preview_collection_visibilty(view_layer, pr_type); + switch_preview_floor_visibility(view_layer, pr_method); BKE_layer_collection_sync(scene, view_layer); } @@ -348,6 +357,38 @@ static ID *duplicate_ids(ID *id, const bool allow_failure) } } +static World *preview_get_world(Main *pr_main) +{ + World *result = NULL; + const char *world_name = "World"; + result = BLI_findstring(&pr_main->worlds, world_name, offsetof(ID, name) + 2); + + /* No world found return first world. */ + if (result == NULL) { + result = pr_main->worlds.first; + } + + BLI_assert_msg(result, "Preview file has no world."); + return result; +} + +static void preview_sync_exposure(World *dst, const World *src) +{ + BLI_assert(dst); + BLI_assert(src); + dst->exp = src->exp; + dst->range = src->range; +} + +static World *preview_prepare_world(Main *pr_main, const World *world) +{ + World *result = preview_get_world(pr_main); + if (world) { + preview_sync_exposure(result, world); + } + return result; +} + /* call this with a pointer to initialize preview scene */ /* call this with NULL to restore assigned ID pointers in preview scene */ static Scene *preview_prepare_scene( @@ -368,13 +409,7 @@ static Scene *preview_prepare_scene( /* this flag tells render to not execute depsgraph or ipos etc */ sce->r.scemode |= R_BUTS_PREVIEW; - /* set world always back, is used now */ - sce->world = pr_main->worlds.first; - /* now: exposure copy */ - if (scene->world) { - sce->world->exp = scene->world->exp; - sce->world->range = scene->world->range; - } + BLI_strncpy(sce->r.engine, scene->r.engine, sizeof(sce->r.engine)); sce->r.color_mgt_flag = scene->r.color_mgt_flag; BKE_color_managed_display_settings_copy(&sce->display_settings, &scene->display_settings); @@ -400,13 +435,13 @@ static Scene *preview_prepare_scene( sce->r.cfra = scene->r.cfra; + /* Setup the world. */ + sce->world = preview_prepare_world(pr_main, scene->world); + if (id_type == ID_TE) { /* Texture is not actually rendered with engine, just set dummy value. */ BLI_strncpy(sce->r.engine, RE_engine_id_BLENDER_EEVEE, sizeof(sce->r.engine)); } - else { - BLI_strncpy(sce->r.engine, scene->r.engine, sizeof(sce->r.engine)); - } if (id_type == ID_MA) { Material *mat = NULL, *origmat = (Material *)id; @@ -432,14 +467,12 @@ static Scene *preview_prepare_scene( sce->world->horb = 0.05f; } - if (sp->pr_method == PR_ICON_RENDER && sp->pr_main == G_pr_main_grease_pencil) { - /* For grease pencil, always use sphere for icon renders. */ - set_preview_visibility(sce, view_layer, MA_SPHERE_A, sp->pr_method); - } - else { - /* Use specified preview shape for both preview panel and icon previews. */ - set_preview_visibility(sce, view_layer, mat->pr_type, sp->pr_method); - } + /* For grease pencil, always use sphere for icon renders. */ + const ePreviewType preview_type = (sp->pr_method == PR_ICON_RENDER && + sp->pr_main == G_pr_main_grease_pencil) ? + MA_SPHERE_A : + mat->pr_type; + set_preview_visibility(sce, view_layer, preview_type, sp->pr_method); if (sp->pr_method != PR_ICON_RENDER) { if (mat->nodetree && sp->pr_method == PR_NODE_RENDER) { @@ -691,9 +724,11 @@ void ED_preview_draw(const bContext *C, void *idp, void *parentp, void *slotp, r struct ObjectPreviewData { /* The main for the preview, not of the current file. */ Main *pr_main; - /* Copy of the object to create the preview for. The copy is for thread safety (and to insert it - * into an own main). */ + /* Copy of the object to create the preview for. The copy is for thread safety (and to insert + * it into an own main). */ Object *object; + /* Current frame. */ + int cfra; int sizex; int sizey; }; @@ -727,6 +762,10 @@ static Scene *object_preview_scene_create(const struct ObjectPreviewData *previe Depsgraph **r_depsgraph) { Scene *scene = BKE_scene_add(preview_data->pr_main, "Object preview scene"); + /* Preview need to be in the current frame to get a thumbnail similar of what + * viewport displays. */ + CFRA = preview_data->cfra; + ViewLayer *view_layer = scene->view_layers.first; Depsgraph *depsgraph = DEG_graph_new( preview_data->pr_main, scene, view_layer, DAG_EVAL_VIEWPORT); @@ -771,6 +810,7 @@ static void object_preview_render(IconPreview *preview, IconPreviewSize *preview .pr_main = preview_main, /* Act on a copy. */ .object = (Object *)preview->id_copy, + .cfra = preview->scene->r.cfra, .sizex = preview_sized->sizex, .sizey = preview_sized->sizey, }; @@ -1308,8 +1348,9 @@ static void icon_copy_rect(ImBuf *ibuf, uint w, uint h, uint *rect) scaledy = (float)h; } - ex = (short)scaledx; - ey = (short)scaledy; + /* Scaling down must never assign zero width/height, see: T89868. */ + ex = MAX2(1, (short)scaledx); + ey = MAX2(1, (short)scaledy); dx = (w - ex) / 2; dy = (h - ey) / 2; @@ -1550,8 +1591,8 @@ static void icon_preview_startjob_all_sizes(void *customdata, /* check_engine_supports_preview() checks whether the engine supports "preview mode" (think: * Material Preview). This check is only relevant when the render function called below is - * going to use such a mode. Object and Action render functions use Solid mode, though, so they - * can skip this test. */ + * going to use such a mode. Object and Action render functions use Solid mode, though, so + * they can skip this test. */ /* TODO: Decouple the ID-type-specific render functions from this function, so that it's not * necessary to know here what happens inside lower-level functions. */ const bool use_solid_render_mode = (ip->id != NULL) && ELEM(GS(ip->id->name), ID_OB, ID_AC); diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index d2b1ebdad78..8a3d8f9636b 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -56,6 +56,7 @@ #include "BKE_linestyle.h" #include "BKE_main.h" #include "BKE_material.h" +#include "BKE_node.h" #include "BKE_object.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -1043,6 +1044,10 @@ static int view_layer_add_aov_exec(bContext *C, wmOperator *UNUSED(op)) engine = NULL; } + if (scene->nodetree) { + ntreeCompositUpdateRLayers(scene->nodetree); + } + DEG_id_tag_update(&scene->id, 0); DEG_relations_tag_update(CTX_data_main(C)); WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene); @@ -1091,6 +1096,10 @@ static int view_layer_remove_aov_exec(bContext *C, wmOperator *UNUSED(op)) engine = NULL; } + if (scene->nodetree) { + ntreeCompositUpdateRLayers(scene->nodetree); + } + DEG_id_tag_update(&scene->id, 0); DEG_relations_tag_update(CTX_data_main(C)); WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene); diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c index 80b5623b9c3..fb9d11feb63 100644 --- a/source/blender/editors/render/render_update.c +++ b/source/blender/editors/render/render_update.c @@ -65,7 +65,7 @@ /***************************** Render Engines ********************************/ -/* Update 3D viewport render or draw engine on changes to the scene or view settings . */ +/* Update 3D viewport render or draw engine on changes to the scene or view settings. */ void ED_render_view3d_update(Depsgraph *depsgraph, wmWindow *window, ScrArea *area, diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 3ce2f326dca..8123d8bb104 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -1031,7 +1031,7 @@ static eContextResult screen_ctx_asset_library(const bContext *C, bContextDataRe { WorkSpace *workspace = CTX_wm_workspace(C); CTX_data_pointer_set( - result, &workspace->id, &RNA_AssetLibraryReference, &workspace->active_asset_library); + result, &workspace->id, &RNA_AssetLibraryReference, &workspace->asset_library); return CTX_RESULT_OK; } diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index ffeaf514642..3d0d856b1c5 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -71,7 +71,7 @@ #include "ED_armature.h" #include "ED_clip.h" #include "ED_image.h" -#include "ED_keyframes_draw.h" +#include "ED_keyframes_keylist.h" #include "ED_mesh.h" #include "ED_object.h" #include "ED_screen.h" diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 2ca19f76672..2db160d0565 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -270,7 +270,7 @@ typedef struct ProjPaintState { float stencil_value; /* projection painting only */ - /** for multithreading, the first item is sometimes used for non threaded cases too. */ + /** For multi-threading, the first item is sometimes used for non threaded cases too. */ MemArena *arena_mt[BLENDER_MAX_THREADS]; /** screen sized 2D array, each pixel has a linked list of ProjPixel's */ LinkNode **bucketRect; @@ -375,7 +375,7 @@ typedef struct ProjPaintState { /* -------------------------------------------------------------------- */ /* Vars shared between multiple views (keep last) */ /** - * This data is owned by ``ProjStrokeHandle.ps_views[0]``, + * This data is owned by `ProjStrokeHandle.ps_views[0]`, * all other views re-use the data. */ @@ -4462,7 +4462,7 @@ static void project_paint_begin(const bContext *C, ProjPaintFaceLookup face_lookup; const MLoopUV *mloopuv_base = NULL; - /* at the moment this is just ps->arena_mt[0], but use this to show were not multithreading */ + /* At the moment this is just ps->arena_mt[0], but use this to show were not multi-threading. */ MemArena *arena; const int diameter = 2 * BKE_brush_size_get(ps->scene, ps->brush); diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c index 756b991cc81..81118faed39 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c @@ -636,40 +636,12 @@ static void SCULPT_dynamic_topology_disable_ex( SCULPT_pbvh_clear(ob); - if (unode) { - /* Free all existing custom data. */ - CustomData_free(&me->vdata, me->totvert); - CustomData_free(&me->edata, me->totedge); - CustomData_free(&me->fdata, me->totface); - CustomData_free(&me->ldata, me->totloop); - CustomData_free(&me->pdata, me->totpoly); - - /* Copy over stored custom data. */ - SculptUndoNodeGeometry *geometry = &unode->geometry_bmesh_enter; - me->totvert = geometry->totvert; - me->totloop = geometry->totloop; - me->totpoly = geometry->totpoly; - me->totedge = geometry->totedge; - me->totface = 0; - CustomData_copy( - &geometry->vdata, &me->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, geometry->totvert); - CustomData_copy( - &geometry->edata, &me->edata, CD_MASK_MESH.emask, CD_DUPLICATE, geometry->totedge); - CustomData_copy( - &geometry->ldata, &me->ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, geometry->totloop); - CustomData_copy( - &geometry->pdata, &me->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, geometry->totpoly); - - BKE_mesh_update_customdata_pointers(me, false); - } - else { - BKE_sculptsession_bm_to_me(ob, true); + BKE_sculptsession_bm_to_me(ob, true); - /* Sync the visibility to vertices manually as the pmap is still not initialized. */ - for (int i = 0; i < me->totvert; i++) { - me->mvert[i].flag &= ~ME_HIDE; - me->mvert[i].flag |= ME_VERT_PBVH_UPDATE; - } + /* Sync the visibility to vertices manually as the pmap is still not initialized. */ + for (int i = 0; i < me->totvert; i++) { + me->mvert[i].flag &= ~ME_HIDE; + me->mvert[i].flag |= ME_VERT_PBVH_UPDATE; } /* Clear data. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 472ade36f7f..6e204705953 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -10,7 +10,7 @@ * 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, + * 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. diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index 12934af943f..a294108c228 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -10,7 +10,7 @@ * 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, + * 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. diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index f4a227b38de..769598ebab3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -23,6 +23,7 @@ */ #include <stddef.h> +#include <string.h> #include "MEM_guardedalloc.h" @@ -116,9 +117,17 @@ typedef struct UndoSculpt { BMLog *bm_restore; } UndoSculpt; +typedef struct SculptUndoStep { + UndoStep step; + /* NOTE: will split out into list for multi-object-sculpt-mode. */ + UndoSculpt data; + int id; +} SculptUndoStep; + static UndoSculpt *sculpt_undo_get_nodes(void); void sculpt_undo_print_nodes(void *active); - +static bool check_first_undo_entry_dyntopo(Object *ob); +void sculpt_undo_push_begin_ex(Object *ob, const char *name, bool no_first_entry_check); static void update_cb(PBVHNode *node, void *rebuild) { BKE_pbvh_node_mark_update(node); @@ -546,6 +555,7 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, bmesh_undo_on_face_kill, bmesh_undo_on_face_change, bmesh_undo_full_mesh, + NULL, (void *)&data}; if (unode->applied) { @@ -588,7 +598,7 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, } /* Create empty sculpt BMesh and enable logging. */ -static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode) +static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_redo) { SculptSession *ss = ob->sculpt; Mesh *me = ob->data; @@ -602,62 +612,112 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode) .use_unique_ids = true, .use_id_elem_mask = BM_VERT | BM_FACE, .use_id_map = true})); - SCULPT_dyntopo_node_layers_add(ss); - me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; + BM_mesh_bm_from_me(NULL, + ss->bm, + me, + (&(struct BMeshFromMeshParams){ + .calc_face_normal = true, + .use_shapekey = true, + .active_shapekey = ob->shapenr, + })); - ss->bm_log = BM_log_unfreeze(ss->bm, unode->bm_entry); + SCULPT_dyntopo_node_layers_add(ss); + SCULPT_dyntopo_node_layers_update_offsets(ss); if (!ss->bm_log) { /* Restore the BMLog using saved entries. */ ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode->bm_entry); + BMLogEntry *entry = is_redo ? BM_log_entry_prev(unode->bm_entry) : unode->bm_entry; + + BM_log_set_current_entry(ss->bm_log, entry); } - SCULPT_dyntopo_node_layers_update_offsets(ss); BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert); } -static void sculpt_undo_bmesh_restore_begin(bContext *C, - SculptUndoNode *unode, - Object *ob, - SculptSession *ss) +static void sculpt_undo_bmesh_restore_begin( + bContext *C, SculptUndoNode *unode, Object *ob, SculptSession *ss, int dir) { if (unode->applied) { + if (ss->bm && ss->bm_log) { + /*note that we can't log ids here. + not entirely sure why, and in thoery it shouldn't be necassary. + ids end up corrupted. + */ + // BM_log_all_ids(ss->bm, ss->bm_log, unode->bm_entry); + + if (dir == -1) { + BM_log_undo_skip(ss->bm, ss->bm_log); + } + else { + BM_log_redo_skip(ss->bm, ss->bm_log); + } + } + SCULPT_dynamic_topology_disable(C, unode); unode->applied = false; } else { - sculpt_undo_bmesh_enable(ob, unode); + /*load bmesh from mesh data*/ + sculpt_undo_bmesh_enable(ob, unode, true); - /* Restore the mesh from the first log entry. */ - BM_log_redo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id); + /* Restore mesh ids from last log entry, i.e. the one pushed in the primary if branch above */ + if (dir == 1) { + BM_log_redo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id); + } + else { + BM_log_undo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id); + } unode->applied = true; } + + if (ss->bm) { + BM_mesh_elem_index_ensure(ss->bm, BM_VERT | BM_FACE); + } } -static void sculpt_undo_bmesh_restore_end(bContext *C, - SculptUndoNode *unode, - Object *ob, - SculptSession *ss) +static void sculpt_undo_bmesh_restore_end( + bContext *C, SculptUndoNode *unode, Object *ob, SculptSession *ss, int dir) { if (unode->applied) { - sculpt_undo_bmesh_enable(ob, unode); + /*load bmesh from mesh data*/ + sculpt_undo_bmesh_enable(ob, unode, false); - /* Restore the mesh from the last log entry. */ - BM_log_undo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id); + /* Restore mesh ids from last log entry, i.e. the one pushed in the else branch below */ + if (dir == -1) { + BM_log_undo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id); + } + else { + BM_log_redo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id); + } unode->applied = false; } else { + if (ss->bm && ss->bm_log) { + /*note that we can't log ids here. + not entirely sure why, and in thoery it shouldn't be necassary. + ids end up corrupted. + */ + + if (dir == -1) { + BM_log_undo_skip(ss->bm, ss->bm_log); + } + else { + BM_log_redo_skip(ss->bm, ss->bm_log); + } + } + /* Disable dynamic topology sculpting. */ SCULPT_dynamic_topology_disable(C, NULL); unode->applied = true; } if (ss->bm) { - BM_mesh_elem_index_ensure(ss->bm, BM_VERT); + BM_mesh_elem_index_ensure(ss->bm, BM_VERT | BM_FACE); } } @@ -745,16 +805,15 @@ static void sculpt_undo_geometry_restore(SculptUndoNode *unode, Object *object) * * Returns true if this was a dynamic-topology undo step, otherwise * returns false to indicate the non-dyntopo code should run. */ -static int sculpt_undo_bmesh_restore(bContext *C, - SculptUndoNode *unode, - Object *ob, - SculptSession *ss) +static int sculpt_undo_bmesh_restore( + bContext *C, SculptUndoNode *unode, Object *ob, SculptSession *ss, int dir) { if (ss->bm_log && ss->bm && !ELEM(unode->type, SCULPT_UNDO_DYNTOPO_BEGIN, SCULPT_UNDO_DYNTOPO_END)) { SCULPT_dyntopo_node_layers_update_offsets(ss); BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert); +#if 0 if (ss->active_face_index.i && ss->active_face_index.i != -1LL) { ss->active_face_index.i = (intptr_t)BM_log_face_id_get(ss->bm_log, (BMFace *)ss->active_face_index.i); @@ -770,27 +829,33 @@ static int sculpt_undo_bmesh_restore(bContext *C, else { ss->active_vertex_index.i = -1; } +#endif + ss->active_face_index.i = ss->active_vertex_index.i = 0; } else { ss->active_face_index.i = ss->active_vertex_index.i = -1; } bool ret = false; + bool set_active_vertex = true; switch (unode->type) { case SCULPT_UNDO_DYNTOPO_BEGIN: - sculpt_undo_bmesh_restore_begin(C, unode, ob, ss); + sculpt_undo_bmesh_restore_begin(C, unode, ob, ss, dir); SCULPT_vertex_random_access_ensure(ss); + ss->active_face_index.i = ss->active_vertex_index.i = 0; + set_active_vertex = false; + ret = true; break; case SCULPT_UNDO_DYNTOPO_END: ss->active_face_index.i = ss->active_vertex_index.i = 0; + set_active_vertex = false; - if (ss->bm) { - sculpt_undo_bmesh_restore_end(C, unode, ob, ss); - } + sculpt_undo_bmesh_restore_end(C, unode, ob, ss, dir); SCULPT_vertex_random_access_ensure(ss); + ret = true; break; default: @@ -802,7 +867,7 @@ static int sculpt_undo_bmesh_restore(bContext *C, break; } - if (ss->bm_log && ss->bm) { + if (set_active_vertex && ss->bm_log && ss->bm) { if (ss->active_face_index.i != -1) { BMFace *f = BM_log_id_face_get(ss->bm_log, (uint)ss->active_face_index.i); if (f && f->head.htype == BM_FACE) { @@ -863,7 +928,7 @@ static void sculpt_undo_refine_subdiv(Depsgraph *depsgraph, MEM_freeN(deformed_verts); } -static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase *lb) +static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase *lb, int dir) { Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -878,6 +943,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase bool did_first_hack = false; for (unode = lb->first; unode; unode = unode->next) { +#if 0 if (unode->bm_entry && !ss->bm) { // file loading breaks undo because the stack isn't initialized // detect that case and try to fix it @@ -904,6 +970,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase // PBVH is corrupted at this point, destroy it SCULPT_pbvh_clear(ob); } +#endif /* Restore pivot. */ copy_v3_v3(ss->pivot_pos, unode->pivot_pos); @@ -955,11 +1022,12 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase * Undo steps like geometry does not need object to be updated before they run and will * ensure object is updated after the node is handled. */ const SculptUndoNode *first_unode = (const SculptUndoNode *)lb->first; - if (first_unode->type != SCULPT_UNDO_GEOMETRY) { + if (first_unode->type != SCULPT_UNDO_GEOMETRY && + first_unode->type != SCULPT_UNDO_DYNTOPO_BEGIN) { BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask, false); } - if (sculpt_undo_bmesh_restore(C, lb->first, ob, ss)) { + if (sculpt_undo_bmesh_restore(C, lb->first, ob, ss, dir)) { return; } } @@ -1533,23 +1601,16 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt unode->applied = true; if (type == SCULPT_UNDO_DYNTOPO_END) { - unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log); - BM_log_full_mesh(ss->bm, ss->bm_log); + unode->bm_entry = BM_log_all_ids(ss->bm, ss->bm_log, NULL); + // BM_log_full_mesh(ss->bm, ss->bm_log); // BM_log_before_all_removed(ss->bm, ss->bm_log); } else if (type == SCULPT_UNDO_DYNTOPO_BEGIN) { - /* Store a copy of the mesh's current vertices, loops, and - * polys. A full copy like this is needed because entering - * dynamic-topology immediately does topological edits - * (converting polys to triangles) that the BMLog can't - * fully restore from. */ - SculptUndoNodeGeometry *geometry = &unode->geometry_bmesh_enter; - sculpt_undo_geometry_store_data(geometry, ob); + unode->bm_entry = BM_log_all_ids(ss->bm, ss->bm_log, NULL); - unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log); // BM_log_all_added(ss->bm, ss->bm_log); - BM_log_full_mesh(ss->bm, ss->bm_log); + // BM_log_full_mesh(ss->bm, ss->bm_log); } else { unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log); @@ -1637,6 +1698,7 @@ bool SCULPT_ensure_dyntopo_node_undo(Object *ob, int extraType) { SculptSession *ss = ob->sculpt; + UndoSculpt *usculpt = sculpt_undo_get_nodes(); SculptUndoNode *unode = usculpt->nodes.first; @@ -1681,6 +1743,52 @@ bool SCULPT_ensure_dyntopo_node_undo(Object *ob, return true; } +static bool check_first_undo_entry_dyntopo(Object *ob) +{ + UndoStack *ustack = ED_undo_stack_get(); + if (!ustack || !ob->sculpt || !ob->sculpt->bm) { + return false; + } + + UndoStep *us = ustack->step_init ? ustack->step_init : ustack->step_active; + bool bad = false; + + if (!us) { + bad = true; + } + else if (us->type) { + if (!STREQ(us->type->name, "Sculpt")) { + bad = true; + } + else { + SculptUndoStep *step = (SculptUndoStep *)us; + SculptUndoNode *unode = step->data.nodes.first; + + if (!unode) { + bad = true; + } + else { + UndoStep *act = ustack->step_active; + + if (!act->type || !STREQ(act->type->name, "Sculpt")) { + bad = unode->type != SCULPT_UNDO_DYNTOPO_BEGIN; + } + } + } + } + else { + bad = true; + } + + if (bad) { + sculpt_undo_push_begin_ex(ob, "Dyntopo Begin", true); + SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); + SCULPT_undo_push_end(); + } + + return bad; +} + SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type) { SculptSession *ss = ob->sculpt; @@ -1782,11 +1890,15 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType return unode; } -void SCULPT_undo_push_begin(Object *ob, const char *name) +void sculpt_undo_push_begin_ex(Object *ob, const char *name, bool no_first_entry_check) { UndoStack *ustack = ED_undo_stack_get(); if (ob != NULL) { + if (!no_first_entry_check && ob->sculpt && ob->sculpt->bm) { + check_first_undo_entry_dyntopo(ob); + } + /* If possible, we need to tag the object and its geometry data as 'changed in the future' in * the previous undo step if it's a memfile one. */ ED_undosys_stack_memfile_id_changed_tag(ustack, &ob->id); @@ -1799,6 +1911,11 @@ void SCULPT_undo_push_begin(Object *ob, const char *name) BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_SCULPT); } +void SCULPT_undo_push_begin(Object *ob, const char *name) +{ + sculpt_undo_push_begin_ex(ob, name, false); +} + void SCULPT_undo_push_end(void) { SCULPT_undo_push_end_ex(false); @@ -1834,13 +1951,6 @@ void SCULPT_undo_push_end_ex(const bool use_nested_undo) /** \name Implements ED Undo System * \{ */ -typedef struct SculptUndoStep { - UndoStep step; - /* NOTE: will split out into list for multi-object-sculpt-mode. */ - UndoSculpt data; - int id; -} SculptUndoStep; - static void sculpt_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p) { SculptUndoStep *us = (SculptUndoStep *)us_p; @@ -1875,7 +1985,7 @@ static void sculpt_undosys_step_decode_undo_impl(struct bContext *C, SculptUndoStep *us) { BLI_assert(us->step.is_applied == true); - sculpt_undo_restore_list(C, depsgraph, &us->data.nodes); + sculpt_undo_restore_list(C, depsgraph, &us->data.nodes, -1); us->step.is_applied = false; sculpt_undo_print_nodes(us); @@ -1886,7 +1996,7 @@ static void sculpt_undosys_step_decode_redo_impl(struct bContext *C, SculptUndoStep *us) { BLI_assert(us->step.is_applied == false); - sculpt_undo_restore_list(C, depsgraph, &us->data.nodes); + sculpt_undo_restore_list(C, depsgraph, &us->data.nodes, 1); us->step.is_applied = true; sculpt_undo_print_nodes(us); @@ -2215,6 +2325,9 @@ static void print_sculpt_undo_step(UndoStep *us, UndoStep *active, int i) void sculpt_undo_print_nodes(void *active) { #if 0 + + printf("=================== sculpt undo steps ==============\n"); + UndoStack *ustack = ED_undo_stack_get(); UndoStep *us = ustack->steps.first; if (active == NULL) { diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 0d5b197ae93..59d2063ea84 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -51,8 +51,8 @@ #include "ED_anim_api.h" #include "ED_gpencil.h" -#include "ED_keyframes_draw.h" #include "ED_keyframes_edit.h" +#include "ED_keyframes_keylist.h" #include "ED_markers.h" #include "ED_mask.h" #include "ED_screen.h" diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index 28482faf6e3..5e5143723a6 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -196,7 +196,8 @@ static void action_main_region_draw(const bContext *C, ARegion *region) UI_view2d_view_ortho(v2d); /* time grid */ - UI_view2d_draw_lines_x__discrete_frames_or_seconds(v2d, scene, saction->flag & SACTION_DRAWTIME); + UI_view2d_draw_lines_x__discrete_frames_or_seconds( + v2d, scene, saction->flag & SACTION_DRAWTIME, true); ED_region_draw_cb_draw(C, region, REGION_DRAW_PRE_VIEW); diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index 326c221a2e3..aef3385f2dc 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -1112,7 +1112,7 @@ static void dopesheet_region_draw(const bContext *C, ARegion *region) UI_view2d_view_ortho(v2d); /* time grid */ - UI_view2d_draw_lines_x__discrete_frames_or_seconds(v2d, scene, sc->flag & SC_SHOW_SECONDS); + UI_view2d_draw_lines_x__discrete_frames_or_seconds(v2d, scene, sc->flag & SC_SHOW_SECONDS, true); /* data... */ clip_draw_dopesheet_main(sc, region, scene); diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index a314a85491d..37a56816677 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -185,9 +185,8 @@ static void file_draw_icon(const SpaceFile *sfile, BLI_assert(asset_params != NULL); UI_but_drag_set_asset(but, - file->name, + &(AssetHandle){.file_data = file}, BLI_strdup(blend_path), - file->blentype, asset_params->import_type, icon, preview_image, @@ -500,9 +499,8 @@ static void file_draw_preview(const SpaceFile *sfile, BLI_assert(asset_params != NULL); UI_but_drag_set_asset(but, - file->name, + &(AssetHandle){.file_data = file}, BLI_strdup(blend_path), - file->blentype, asset_params->import_type, icon, imb, diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 995383d9d0e..4d25524cd19 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -2340,7 +2340,9 @@ static int file_directory_new_exec(bContext *C, wmOperator *op) /* If we don't enter the directory directly, remember file to jump into editing. */ if (do_diropen == false) { - BLI_assert(params->rename_id == NULL || !"File rename handling should immediately clear rename_id when done, because otherwise it will keep taking precedence over renamefile."); + BLI_assert_msg(params->rename_id == NULL, + "File rename handling should immediately clear rename_id when done, " + "because otherwise it will keep taking precedence over renamefile."); BLI_strncpy(params->renamefile, name, FILE_MAXFILE); rename_flag = FILE_PARAMS_RENAME_PENDING; } diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 492a189fc81..93c27e1fe90 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -37,6 +37,8 @@ #endif #include "MEM_guardedalloc.h" +#include "BLF_api.h" + #include "BLI_blenlib.h" #include "BLI_fileops.h" #include "BLI_fileops_types.h" @@ -382,7 +384,7 @@ typedef struct FileList { eFileSelectType type; /* The library this list was created for. Stored here so we know when to re-read. */ - FileSelectAssetLibraryUID *asset_library; + AssetLibraryReference *asset_library; short flags; @@ -1045,8 +1047,8 @@ void filelist_setfilter_options(FileList *filelist, * Checks two libraries for equality. * \return True if the libraries match. */ -static bool filelist_compare_asset_libraries(const FileSelectAssetLibraryUID *library_a, - const FileSelectAssetLibraryUID *library_b) +static bool filelist_compare_asset_libraries(const AssetLibraryReference *library_a, + const AssetLibraryReference *library_b) { if (library_a->type != library_b->type) { return false; @@ -1065,7 +1067,7 @@ static bool filelist_compare_asset_libraries(const FileSelectAssetLibraryUID *li /** * \param asset_library: May be NULL to unset the library. */ -void filelist_setlibrary(FileList *filelist, const FileSelectAssetLibraryUID *asset_library) +void filelist_setlibrary(FileList *filelist, const AssetLibraryReference *asset_library) { /* Unset if needed. */ if (!asset_library) { @@ -1812,13 +1814,23 @@ BlendHandle *filelist_lib(struct FileList *filelist) return filelist->libfiledata; } -static const char *fileentry_uiname(const char *root, - const char *relpath, - const eFileSel_File_Types typeflag, - char *buff) +static char *fileentry_uiname(const char *root, + const char *relpath, + const eFileSel_File_Types typeflag, + char *buff) { char *name = NULL; + if (typeflag & FILE_TYPE_FTFONT && !(typeflag & FILE_TYPE_BLENDERLIB)) { + char abspath[FILE_MAX_LIBEXTRA]; + BLI_join_dirfile(abspath, sizeof(abspath), root, relpath); + name = BLF_display_name_from_file(abspath); + if (name) { + /* Allocated string, so no need to BLI_strdup.*/ + return name; + } + } + if (typeflag & FILE_TYPE_BLENDERLIB) { char abspath[FILE_MAX_LIBEXTRA]; char *group; @@ -1840,7 +1852,7 @@ static const char *fileentry_uiname(const char *root, } BLI_assert(name); - return name; + return BLI_strdup(name); } const char *filelist_dir(struct FileList *filelist) @@ -3203,7 +3215,7 @@ static void filelist_readjob_do(const bool do_lib, MEM_freeN(entry->relpath); entry->relpath = BLI_strdup(dir + 2); /* + 2 to remove '//' * added by BLI_path_rel to rel_subdir. */ - entry->name = BLI_strdup(fileentry_uiname(root, entry->relpath, entry->typeflag, dir)); + entry->name = fileentry_uiname(root, entry->relpath, entry->typeflag, dir); entry->free_name = true; /* Here we decide whether current filedirentry is to be listed too, or not. */ diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index cb98cf6e74a..6915e853681 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -29,7 +29,7 @@ extern "C" { struct BlendHandle; struct FileList; -struct FileSelectAssetLibraryUID; +struct AssetLibraryReference; struct FileSelection; struct wmWindowManager; @@ -73,7 +73,7 @@ void filelist_setfilter_options(struct FileList *filelist, const char *filter_search); void filelist_filter(struct FileList *filelist); void filelist_setlibrary(struct FileList *filelist, - const struct FileSelectAssetLibraryUID *asset_library); + const struct AssetLibraryReference *asset_library); void filelist_init_icons(void); void filelist_free_icons(void); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 7bc83e8fc79..68dd1e28f99 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -415,7 +415,7 @@ FileAssetSelectParams *ED_fileselect_get_asset_params(const SpaceFile *sfile) static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params) { - FileSelectAssetLibraryUID *library = &asset_params->asset_library; + AssetLibraryReference *library = &asset_params->asset_library; FileSelectParams *base_params = &asset_params->base_params; bUserAssetLibrary *user_library = NULL; diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 31c7dee294b..274b21f7043 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -910,13 +910,6 @@ static int /*eContextResult*/ file_context(const bContext *C, return CTX_RESULT_NO_DATA; } - BLI_STATIC_ASSERT(offsetof(FileSelectAssetLibraryUID, type) == - offsetof(AssetLibraryReference, type), - "Expected FileSelectAssetLibraryUID to match AssetLibraryReference"); - BLI_STATIC_ASSERT(offsetof(FileSelectAssetLibraryUID, custom_library_index) == - offsetof(AssetLibraryReference, custom_library_index), - "Expected FileSelectAssetLibraryUID to match AssetLibraryReference"); - CTX_data_pointer_set( result, &screen->id, &RNA_AssetLibraryReference, &asset_params->asset_library); return CTX_RESULT_OK; diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 6f1b0bb0d7d..9f9869a854c 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -261,7 +261,7 @@ static int graphkeys_insertkey_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* Which channels to affect?. */ + /* Which channels to affect? */ mode = RNA_enum_get(op->ptr, "type"); /* Insert keyframes. */ @@ -2814,7 +2814,7 @@ static int graph_fmodifier_paste_exec(bContext *C, wmOperator *op) } ANIM_animdata_freelist(&anim_data); - /* Successful or not?. */ + /* Successful or not? */ if (ok) { /* Set notifier that keyframes have changed. */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -2873,7 +2873,7 @@ static int graph_driver_vars_copy_exec(bContext *C, wmOperator *op) ok = ANIM_driver_vars_copy(op->reports, fcu); } - /* Successful or not?. */ + /* Successful or not? */ if (ok) { return OPERATOR_FINISHED; } @@ -2915,7 +2915,7 @@ static int graph_driver_vars_paste_exec(bContext *C, wmOperator *op) ok = ANIM_driver_vars_paste(op->reports, fcu, replace); } - /* Successful or not?. */ + /* Successful or not? */ if (ok) { /* Rebuild depsgraph, now that there are extra deps here. */ DEG_relations_tag_update(CTX_data_main(C)); diff --git a/source/blender/editors/space_graph/graph_view.c b/source/blender/editors/space_graph/graph_view.c index 31c53cde62c..036fd354c18 100644 --- a/source/blender/editors/space_graph/graph_view.c +++ b/source/blender/editors/space_graph/graph_view.c @@ -398,7 +398,7 @@ static void create_ghost_curves(bAnimContext *ac, int start, int end) ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - /* Loop through filtered data and add keys between selected keyframes on every frame . */ + /* Loop through filtered data and add keys between selected keyframes on every frame. */ for (ale = anim_data.first; ale; ale = ale->next) { FCurve *fcu = (FCurve *)ale->key_data; FCurve *gcu = BKE_fcurve_create(); diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 4779a82948d..86349a64681 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -1189,7 +1189,7 @@ void uiTemplateImageLayers(uiLayout *layout, bContext *C, Image *ima, ImageUser const int menus_width = 160 * dpi_fac; const bool is_render_result = (ima->type == IMA_TYPE_R_RESULT); - /* use BKE_image_acquire_renderresult so we get the correct slot in the menu */ + /* Use BKE_image_acquire_renderresult so we get the correct slot in the menu. */ rr = BKE_image_acquire_renderresult(scene, ima); uiblock_layer_pass_buttons( layout, ima, rr, iuser, menus_width, is_render_result ? &ima->render_slot : NULL); diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index dc693b25107..92ceb00d5c0 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -460,258 +460,6 @@ void ED_image_draw_info(Scene *scene, BLF_draw_ascii(blf_mono_font, str, sizeof(str)); } } - -/* image drawing */ -static void sima_draw_zbuf_pixels( - float x1, float y1, int rectx, int recty, const int *rect, float zoomx, float zoomy) -{ - const float red[4] = {1.0f, 0.0f, 0.0f, 0.0f}; - - /* Very slow! */ - float *rectf = MEM_mallocN(rectx * recty * sizeof(float), "temp"); - for (int a = rectx * recty - 1; a >= 0; a--) { - /* zbuffer values are signed, so we need to shift color range */ - rectf[a] = rect[a] * 0.5f + 0.5f; - } - - IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR); - GPU_shader_uniform_vector( - state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - - immDrawPixelsTex(&state, x1, y1, rectx, recty, GPU_R16F, false, rectf, zoomx, zoomy, NULL); - - MEM_freeN(rectf); -} - -static void sima_draw_zbuffloat_pixels(Scene *scene, - float x1, - float y1, - int rectx, - int recty, - const float *rect_float, - float zoomx, - float zoomy) -{ - float bias, scale, *rectf, clip_end; - int a; - const float red[4] = {1.0f, 0.0f, 0.0f, 0.0f}; - - if (scene->camera && scene->camera->type == OB_CAMERA) { - bias = ((Camera *)scene->camera->data)->clip_start; - clip_end = ((Camera *)scene->camera->data)->clip_end; - scale = 1.0f / (clip_end - bias); - } - else { - bias = 0.1f; - scale = 0.01f; - clip_end = 100.0f; - } - - rectf = MEM_mallocN(rectx * recty * sizeof(float), "temp"); - for (a = rectx * recty - 1; a >= 0; a--) { - if (rect_float[a] > clip_end) { - rectf[a] = 0.0f; - } - else if (rect_float[a] < bias) { - rectf[a] = 1.0f; - } - else { - rectf[a] = 1.0f - (rect_float[a] - bias) * scale; - rectf[a] *= rectf[a]; - } - } - - IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR); - GPU_shader_uniform_vector( - state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - - immDrawPixelsTex(&state, x1, y1, rectx, recty, GPU_R16F, false, rectf, zoomx, zoomy, NULL); - - MEM_freeN(rectf); -} - -static void draw_udim_label(ARegion *region, float fx, float fy, const char *label) -{ - if (label == NULL || !label[0]) { - return; - } - - /* find window pixel coordinates of origin */ - int x, y; - UI_view2d_view_to_region(®ion->v2d, fx, fy, &x, &y); - - GPU_blend(GPU_BLEND_ALPHA); - - int textwidth = BLF_width(blf_mono_font, label, strlen(label)) + 10; - float stepx = BLI_rcti_size_x(®ion->v2d.mask) / BLI_rctf_size_x(®ion->v2d.cur); - float opacity; - if (textwidth < 0.5f * (stepx - 10)) { - opacity = 1.0f; - } - else if (textwidth < (stepx - 10)) { - opacity = 2.0f - 2.0f * (textwidth / (stepx - 10)); - } - else { - opacity = 0.0f; - } - BLF_color4ub(blf_mono_font, 220, 220, 220, 150 * opacity); - BLF_position(blf_mono_font, (int)(x + 10), (int)(y + 10), 0); - BLF_draw_ascii(blf_mono_font, label, strlen(label)); - - GPU_blend(GPU_BLEND_NONE); -} - -static void draw_image_buffer(const bContext *C, - SpaceImage *sima, - ARegion *region, - Scene *scene, - ImBuf *ibuf, - float fx, - float fy, - float zoomx, - float zoomy) -{ - /* Image are still drawn in display space. */ - GPUFrameBuffer *fb = GPU_framebuffer_active_get(); - GPU_framebuffer_bind_no_srgb(fb); - - int x, y; - int sima_flag = sima->flag & ED_space_image_get_display_channel_mask(ibuf); - - /* find window pixel coordinates of origin */ - UI_view2d_view_to_region(®ion->v2d, fx, fy, &x, &y); - - /* this part is generic image display */ - if (sima_flag & SI_SHOW_ZBUF && (ibuf->zbuf || ibuf->zbuf_float || (ibuf->channels == 1))) { - if (ibuf->zbuf) { - sima_draw_zbuf_pixels(x, y, ibuf->x, ibuf->y, ibuf->zbuf, zoomx, zoomy); - } - else if (ibuf->zbuf_float) { - sima_draw_zbuffloat_pixels(scene, x, y, ibuf->x, ibuf->y, ibuf->zbuf_float, zoomx, zoomy); - } - else if (ibuf->channels == 1) { - sima_draw_zbuffloat_pixels(scene, x, y, ibuf->x, ibuf->y, ibuf->rect_float, zoomx, zoomy); - } - } - else { - int clip_max_x, clip_max_y; - UI_view2d_view_to_region( - ®ion->v2d, region->v2d.cur.xmax, region->v2d.cur.ymax, &clip_max_x, &clip_max_y); - - if (sima_flag & SI_USE_ALPHA) { - imm_draw_box_checker_2d(x, y, x + ibuf->x * zoomx, y + ibuf->y * zoomy); - - GPU_blend(GPU_BLEND_ALPHA); - } - - /* If RGBA display with color management */ - if ((sima_flag & (SI_SHOW_R | SI_SHOW_G | SI_SHOW_B | SI_SHOW_ALPHA)) == 0) { - - ED_draw_imbuf_ctx_clipping(C, ibuf, x, y, false, 0, 0, clip_max_x, clip_max_y, zoomx, zoomy); - } - else { - float shuffle[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - uchar *display_buffer; - void *cache_handle; - ColorManagedViewSettings *view_settings; - ColorManagedDisplaySettings *display_settings; - - if (sima_flag & SI_SHOW_R) { - shuffle[0] = 1.0f; - } - else if (sima_flag & SI_SHOW_G) { - shuffle[1] = 1.0f; - } - else if (sima_flag & SI_SHOW_B) { - shuffle[2] = 1.0f; - } - else if (sima_flag & SI_SHOW_ALPHA) { - shuffle[3] = 1.0f; - } - - IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR); - GPU_shader_uniform_vector( - state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, shuffle); - - IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings); - display_buffer = IMB_display_buffer_acquire( - ibuf, view_settings, display_settings, &cache_handle); - - if (display_buffer) { - immDrawPixelsTex_clipping(&state, - x, - y, - ibuf->x, - ibuf->y, - GPU_RGBA8, - false, - display_buffer, - 0, - 0, - clip_max_x, - clip_max_y, - zoomx, - zoomy, - NULL); - } - - IMB_display_buffer_release(cache_handle); - } - - if (sima_flag & SI_USE_ALPHA) { - GPU_blend(GPU_BLEND_NONE); - } - } - - GPU_framebuffer_bind(fb); -} - -static void draw_image_buffer_repeated(const bContext *C, - SpaceImage *sima, - ARegion *region, - Scene *scene, - ImBuf *ibuf, - float zoomx, - float zoomy) -{ - const double time_current = PIL_check_seconds_timer(); - - const int xmax = ceil(region->v2d.cur.xmax); - const int ymax = ceil(region->v2d.cur.ymax); - const int xmin = floor(region->v2d.cur.xmin); - const int ymin = floor(region->v2d.cur.ymin); - - for (int x = xmin; x < xmax; x++) { - for (int y = ymin; y < ymax; y++) { - draw_image_buffer(C, sima, region, scene, ibuf, x, y, zoomx, zoomy); - - /* only draw until running out of time */ - if ((PIL_check_seconds_timer() - time_current) > 0.25) { - return; - } - } - } -} - -/* draw uv edit */ - -/* draw grease pencil */ -void draw_image_grease_pencil(bContext *C, bool onlyv2d) -{ - /* draw in View2D space? */ - if (onlyv2d) { - /* draw grease-pencil ('image' strokes) */ - ED_annotation_draw_2dimage(C); - } - else { - /* assume that UI_view2d_restore(C) has been called... */ - // SpaceImage *sima = (SpaceImage *)CTX_wm_space_data(C); - - /* draw grease-pencil ('screen' strokes) */ - ED_annotation_draw_view2d(C, 0); - } -} - void draw_image_sample_line(SpaceImage *sima) { if (sima->sample_line_hist.flag & HISTO_FLAG_SAMPLELINE) { @@ -742,229 +490,6 @@ void draw_image_sample_line(SpaceImage *sima) } } -static void draw_udim_tile_grid(uint pos_attr, - uint color_attr, - ARegion *region, - int x, - int y, - float stepx, - float stepy, - const float color[3]) -{ - float x1, y1; - UI_view2d_view_to_region_fl(®ion->v2d, x, y, &x1, &y1); - const int gridpos[5][2] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}; - for (int i = 0; i < 4; i++) { - immAttr3fv(color_attr, color); - immVertex2f(pos_attr, x1 + gridpos[i][0] * stepx, y1 + gridpos[i][1] * stepy); - immAttr3fv(color_attr, color); - immVertex2f(pos_attr, x1 + gridpos[i + 1][0] * stepx, y1 + gridpos[i + 1][1] * stepy); - } -} - -static void draw_udim_tile_grids(ARegion *region, SpaceImage *sima, Image *ima) -{ - int num_tiles; - if (ima != NULL) { - num_tiles = BLI_listbase_count(&ima->tiles); - - if (ima->source != IMA_SRC_TILED) { - return; - } - } - else { - num_tiles = sima->tile_grid_shape[0] * sima->tile_grid_shape[1]; - } - - float stepx = BLI_rcti_size_x(®ion->v2d.mask) / BLI_rctf_size_x(®ion->v2d.cur); - float stepy = BLI_rcti_size_y(®ion->v2d.mask) / BLI_rctf_size_y(®ion->v2d.cur); - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); - immBegin(GPU_PRIM_LINES, 8 * num_tiles); - - float theme_color[3], selected_color[3]; - UI_GetThemeColorShade3fv(TH_GRID, 60.0f, theme_color); - UI_GetThemeColor3fv(TH_FACE_SELECT, selected_color); - - if (ima != NULL) { - ImageTile *cur_tile = BLI_findlink(&ima->tiles, ima->active_tile_index); - - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - if (tile != cur_tile) { - int x = (tile->tile_number - 1001) % 10; - int y = (tile->tile_number - 1001) / 10; - draw_udim_tile_grid(pos, color, region, x, y, stepx, stepy, theme_color); - } - } - - if (cur_tile != NULL) { - int cur_x = (cur_tile->tile_number - 1001) % 10; - int cur_y = (cur_tile->tile_number - 1001) / 10; - draw_udim_tile_grid(pos, color, region, cur_x, cur_y, stepx, stepy, selected_color); - } - } - else { - for (int y = 0; y < sima->tile_grid_shape[1]; y++) { - for (int x = 0; x < sima->tile_grid_shape[0]; x++) { - draw_udim_tile_grid(pos, color, region, x, y, stepx, stepy, theme_color); - } - } - } - - immEnd(); - immUnbindProgram(); -} - -/* draw main image region */ - -void draw_image_main(const bContext *C, ARegion *region) -{ - SpaceImage *sima = CTX_wm_space_image(C); - Scene *scene = CTX_data_scene(C); - Image *ima; - ImBuf *ibuf; - float zoomx, zoomy; - bool show_viewer, show_stereo3d, show_multilayer; - void *lock; - - /* XXX can we do this in refresh? */ -#if 0 - what_image(sima); - - if (sima->image) { - ED_image_get_aspect(sima->image, &xuser_asp, &yuser_asp); - - /* UGLY hack? until now iusers worked fine... but for flipbook viewer we need this */ - if (sima->image->type == IMA_TYPE_COMPOSITE) { - ImageUser *iuser = ntree_get_active_iuser(scene->nodetree); - if (iuser) { - BKE_image_user_calc_imanr(iuser, scene->r.cfra, 0); - sima->iuser = *iuser; - } - } - /* and we check for spare */ - ibuf = ED_space_image_buffer(sima); - } -#endif - - /* retrieve the image and information about it */ - ima = ED_space_image(sima); - ED_space_image_get_zoom(sima, region, &zoomx, &zoomy); - - /* Tag image as in active use for garbage collector. */ - if (ima) { - BKE_image_tag_time(ima); - } - - show_viewer = (ima && ima->source == IMA_SRC_VIEWER) != 0; - show_stereo3d = (ima && BKE_image_is_stereo(ima) && (sima->iuser.flag & IMA_SHOW_STEREO)); - show_multilayer = ima && BKE_image_is_multilayer(ima); - - if (show_viewer) { - /* use locked draw for drawing viewer image buffer since the compositor - * is running in separated thread and compositor could free this buffers. - * other images are not modifying in such a way so they does not require - * lock (sergey) - */ - BLI_thread_lock(LOCK_DRAW_IMAGE); - } - - if (show_stereo3d) { - if (show_multilayer) { - /* Update multi-index and pass for the current eye. */ - BKE_image_multilayer_index(ima->rr, &sima->iuser); - } - else { - BKE_image_multiview_index(ima, &sima->iuser); - } - } - - ibuf = ED_space_image_acquire_buffer(sima, &lock, 0); - - int main_w = 0; - int main_h = 0; - - /* draw the image or grid */ - if (ibuf == NULL) { - if (ima != NULL) { - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - int x = (tile->tile_number - 1001) % 10; - int y = (tile->tile_number - 1001) / 10; - ED_region_grid_draw(region, zoomx, zoomy, x, y); - } - } - else { - for (int y = 0; y < sima->tile_grid_shape[1]; y++) { - for (int x = 0; x < sima->tile_grid_shape[0]; x++) { - ED_region_grid_draw(region, zoomx, zoomy, x, y); - } - } - } - } - else { - if (sima->flag & SI_DRAW_TILE) { - draw_image_buffer_repeated(C, sima, region, scene, ibuf, zoomx, zoomy); - } - else { - main_w = ibuf->x; - main_h = ibuf->y; - - draw_image_buffer(C, sima, region, scene, ibuf, 0.0f, 0.0f, zoomx, zoomy); - if (ima->source == IMA_SRC_TILED) { - ImageTile *tile = BKE_image_get_tile(ima, 0); - char label[sizeof(tile->label)]; - BKE_image_get_tile_label(ima, tile, label, sizeof(label)); - draw_udim_label(region, 0.0f, 0.0f, label); - } - } - - if (sima->flag & SI_DRAW_METADATA) { - int x, y; - rctf frame; - - BLI_rctf_init(&frame, 0.0f, ibuf->x, 0.0f, ibuf->y); - UI_view2d_view_to_region(®ion->v2d, 0.0f, 0.0f, &x, &y); - - ED_region_image_metadata_draw(x, y, ibuf, &frame, zoomx, zoomy); - } - } - - ED_space_image_release_buffer(sima, ibuf, lock); - - if (ima != NULL && ima->source == IMA_SRC_TILED) { - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - if (tile->tile_number == 1001) { - continue; - } - - ibuf = ED_space_image_acquire_buffer(sima, &lock, tile->tile_number); - if (ibuf != NULL) { - int x_pos = (tile->tile_number - 1001) % 10; - int y_pos = (tile->tile_number - 1001) / 10; - char label[sizeof(tile->label)]; - BKE_image_get_tile_label(ima, tile, label, sizeof(label)); - - float tile_zoomx = (zoomx * main_w) / ibuf->x; - float tile_zoomy = (zoomy * main_h) / ibuf->y; - draw_image_buffer(C, sima, region, scene, ibuf, x_pos, y_pos, tile_zoomx, tile_zoomy); - draw_udim_label(region, x_pos, y_pos, label); - } - ED_space_image_release_buffer(sima, ibuf, lock); - } - } - - draw_udim_tile_grids(region, sima, ima); - draw_image_main_helpers(C, region); - - if (show_viewer) { - BLI_thread_unlock(LOCK_DRAW_IMAGE); - } -} - void draw_image_main_helpers(const bContext *C, ARegion *region) { SpaceImage *sima = CTX_wm_space_image(C); diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h index d302f099772..6af0f3a416b 100644 --- a/source/blender/editors/space_image/image_intern.h +++ b/source/blender/editors/space_image/image_intern.h @@ -36,10 +36,8 @@ struct wmOperatorType; extern const char *image_context_dir[]; /* doc access */ /* image_draw.c */ -void draw_image_main(const struct bContext *C, struct ARegion *region); void draw_image_main_helpers(const struct bContext *C, struct ARegion *region); void draw_image_cache(const struct bContext *C, struct ARegion *region); -void draw_image_grease_pencil(struct bContext *C, bool onlyv2d); void draw_image_sample_line(struct SpaceImage *sima); /* image_ops.c */ diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index f9fb386095d..c96047da0c8 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -44,6 +44,7 @@ #include "ED_anim_api.h" #include "ED_keyframes_draw.h" +#include "ED_keyframes_keylist.h" #include "GPU_immediate.h" #include "GPU_immediate_util.h" diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c index 011cd7e2651..987d06cfe5c 100644 --- a/source/blender/editors/space_nla/space_nla.c +++ b/source/blender/editors/space_nla/space_nla.c @@ -239,7 +239,7 @@ static void nla_main_region_draw(const bContext *C, ARegion *region) UI_view2d_view_ortho(v2d); /* time grid */ - UI_view2d_draw_lines_x__discrete_frames_or_seconds(v2d, scene, snla->flag & SNLA_DRAWTIME); + UI_view2d_draw_lines_x__discrete_frames_or_seconds(v2d, scene, snla->flag & SNLA_DRAWTIME, true); ED_region_draw_cb_draw(C, region, REGION_DRAW_PRE_VIEW); diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index df3afb42ab2..95eb1ccc025 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -165,7 +165,7 @@ static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA } #define SAMPLE_FLT_ISNONE FLT_MAX -/* bad bad, 2.5 will do better?... no it won't... */ +/* Bad bad, 2.5 will do better? ... no it won't! */ static float _sample_col[4] = {SAMPLE_FLT_ISNONE}; void ED_node_sample_set(const float col[4]) { diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 7eca5c0c933..6ec6315a238 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -2125,7 +2125,7 @@ void node_draw_space(const bContext *C, ARegion *region) SpaceNode *snode = CTX_wm_space_node(C); View2D *v2d = ®ion->v2d; - /* Setup offscreen buffers. */ + /* Setup off-screen buffers. */ GPUViewport *viewport = WM_draw_region_get_viewport(region); GPUFrameBuffer *framebuffer_overlay = GPU_viewport_framebuffer_overlay_get(viewport); diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index af9c888cbf7..cbf03f553f6 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -613,9 +613,8 @@ void snode_set_context(const bContext *C) /* check the tree type */ if (!treetype || (treetype->poll && !treetype->poll(C, treetype))) { /* invalid tree type, skip - * NB: not resetting the node path here, invalid bNodeTreeType - * may still be registered at a later point. - */ + * NOTE: not resetting the node path here, invalid #bNodeTreeType + * may still be registered at a later point. */ return; } @@ -1303,9 +1302,8 @@ static int node_duplicate_exec(bContext *C, wmOperator *op) } } - /* copy links between selected nodes - * NB: this depends on correct node->new_node and sock->new_sock pointers from above copy! - */ + /* Copy links between selected nodes. + * NOTE: this depends on correct node->new_node and sock->new_sock pointers from above copy! */ bNodeLink *lastlink = (bNodeLink *)ntree->links.last; LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { /* This creates new links between copied nodes. @@ -2163,9 +2161,9 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op)) } } - /* copy links between selected nodes - * NB: this depends on correct node->new_node and sock->new_sock pointers from above copy! - */ + /* Copy links between selected nodes. + * NOTE: this depends on correct node->new_node and sock->new_sock pointers from above copy! */ + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { /* This creates new links between copied nodes. */ if (link->tonode && (link->tonode->flag & NODE_SELECT) && link->fromnode && diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 725c872e98f..c6c3ca27d6e 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -299,7 +299,7 @@ static void pick_input_link_by_link_intersect(const bContext *C, /* If no linked was picked in this call, try using the one picked in the previous call. * Not essential for the basic behavior, but can make interaction feel a bit better if - * the mouse moves to the right and loses the "selection." */ + * the mouse moves to the right and loses the "selection." */ if (!link_to_pick) { bNodeLink *last_picked_link = nldrag->last_picked_multi_input_socket_link; if (last_picked_link) { @@ -664,9 +664,19 @@ static int node_link_viewer(const bContext *C, bNode *tonode) nodeRemLink(snode->edittree, link); /* find a socket after the previously connected socket */ - for (sock = sock->next; sock; sock = sock->next) { - if (!nodeSocketIsHidden(sock)) { - break; + if (ED_node_is_geometry(snode)) { + /* Geometry nodes viewer only supports geometry sockets for now. */ + for (sock = sock->next; sock; sock = sock->next) { + if (sock->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock)) { + break; + } + } + } + else { + for (sock = sock->next; sock; sock = sock->next) { + if (!nodeSocketIsHidden(sock)) { + break; + } } } } @@ -674,19 +684,40 @@ static int node_link_viewer(const bContext *C, bNode *tonode) if (tonode) { /* Find a selected socket that overrides the socket to connect to */ - LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) { - if (!nodeSocketIsHidden(sock2) && sock2->flag & SELECT) { - sock = sock2; - break; + if (ED_node_is_geometry(snode)) { + /* Geometry nodes viewer only supports geometry sockets for now. */ + LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) { + if (sock2->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock2) && sock2->flag & SELECT) { + sock = sock2; + break; + } + } + } + else { + LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) { + if (!nodeSocketIsHidden(sock2) && sock2->flag & SELECT) { + sock = sock2; + break; + } } } } /* find a socket starting from the first socket */ if (!sock) { - for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) { - if (!nodeSocketIsHidden(sock)) { - break; + if (ED_node_is_geometry(snode)) { + /* Geometry nodes viewer only supports geometry sockets for now. */ + for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) { + if (sock->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock)) { + break; + } + } + } + else { + for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) { + if (!nodeSocketIsHidden(sock)) { + break; + } } } } diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index a081cc83481..6c07c41f451 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -1196,7 +1196,7 @@ static void node_find_create_label(const bNode *node, char *str, int maxlen) } } -/* generic search invoke */ +/* Generic search invoke. */ static void node_find_update_fn(const struct bContext *C, void *UNUSED(arg), const char *str, diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc index cbe33fab64e..648ede7abd5 100644 --- a/source/blender/editors/space_node/node_templates.cc +++ b/source/blender/editors/space_node/node_templates.cc @@ -848,7 +848,10 @@ static void ui_node_draw_input( break; case SOCK_STRING: { const bNodeTree *node_tree = (const bNodeTree *)nodeptr.owner_id; - if (node_tree->type == NTREE_GEOMETRY) { + SpaceNode *snode = CTX_wm_space_node(C); + if (node_tree->type == NTREE_GEOMETRY && snode != nullptr) { + /* Only add the attribute search in the node editor, in other places there is not + * enough context. */ node_geometry_add_attribute_search_button(C, node_tree, node, &inputptr, row); } else { diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 35015356f0b..aaa52f6b649 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -697,7 +697,9 @@ static void tree_element_sequence_dup_activate(Scene *scene, TreeElement *UNUSED { Editing *ed = SEQ_editing_get(scene, false); - /* XXX select_single_seq(seq, 1); */ +#if 0 + select_single_seq(seq, 1); +#endif Sequence *p = ed->seqbasep->first; while (p) { if ((!p->strip) || (!p->strip->stripdata) || (p->strip->stripdata->name[0] == '\0')) { @@ -705,8 +707,11 @@ static void tree_element_sequence_dup_activate(Scene *scene, TreeElement *UNUSED continue; } - /* XXX: if (STREQ(p->strip->stripdata->name, seq->strip->stripdata->name)) select_single_seq(p, - * 0); */ +#if 0 + if (STREQ(p->strip->stripdata->name, seq->strip->stripdata->name)) { + select_single_seq(p, 0); + } +#endif p = p->next; } } diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 1239286d4da..b6b3d1841d2 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -1210,6 +1210,57 @@ static int sequencer_add_effect_strip_invoke(bContext *C, return sequencer_add_effect_strip_exec(C, op); } +static char *sequencer_add_effect_strip_desc(bContext *UNUSED(C), + wmOperatorType *UNUSED(op), + PointerRNA *ptr) +{ + const int type = RNA_enum_get(ptr, "type"); + + switch (type) { + case SEQ_TYPE_CROSS: + return BLI_strdup(TIP_("Add a crossfade transition to the sequencer")); + case SEQ_TYPE_ADD: + return BLI_strdup(TIP_("Add an add effect strip to the sequencer")); + case SEQ_TYPE_SUB: + return BLI_strdup(TIP_("Add a subtract effect strip to the sequencer")); + case SEQ_TYPE_ALPHAOVER: + return BLI_strdup(TIP_("Add an alpha over effect strip to the sequencer")); + case SEQ_TYPE_ALPHAUNDER: + return BLI_strdup(TIP_("Add an alpha under effect strip to the sequencer")); + case SEQ_TYPE_GAMCROSS: + return BLI_strdup(TIP_("Add a gamma cross transition to the sequencer")); + case SEQ_TYPE_MUL: + return BLI_strdup(TIP_("Add a multiply effect strip to the sequencer")); + case SEQ_TYPE_OVERDROP: + return BLI_strdup(TIP_("Add an alpha over drop effect strip to the sequencer")); + case SEQ_TYPE_WIPE: + return BLI_strdup(TIP_("Add a wipe transition to the sequencer")); + case SEQ_TYPE_GLOW: + return BLI_strdup(TIP_("Add a glow effect strip to the sequencer")); + case SEQ_TYPE_TRANSFORM: + return BLI_strdup(TIP_("Add a transform effect strip to the sequencer")); + case SEQ_TYPE_COLOR: + return BLI_strdup(TIP_("Add a color strip to the sequencer")); + case SEQ_TYPE_SPEED: + return BLI_strdup(TIP_("Add a speed effect strip to the sequencer")); + case SEQ_TYPE_MULTICAM: + return BLI_strdup(TIP_("Add a multicam selector effect strip to the sequencer")); + case SEQ_TYPE_ADJUSTMENT: + return BLI_strdup(TIP_("Add an adjustment layer effect strip to the sequencer")); + case SEQ_TYPE_GAUSSIAN_BLUR: + return BLI_strdup(TIP_("Add a gaussian blur effect strip to the sequencer")); + case SEQ_TYPE_TEXT: + return BLI_strdup(TIP_("Add a text strip to the sequencer")); + case SEQ_TYPE_COLORMIX: + return BLI_strdup(TIP_("Add a color mix effect strip to the sequencer")); + default: + break; + } + + /* Use default description. */ + return NULL; +} + void SEQUENCER_OT_effect_strip_add(struct wmOperatorType *ot) { PropertyRNA *prop; @@ -1224,6 +1275,7 @@ void SEQUENCER_OT_effect_strip_add(struct wmOperatorType *ot) ot->exec = sequencer_add_effect_strip_exec; ot->poll = ED_operator_sequencer_active_editable; ot->poll_property = seq_effect_add_properties_poll; + ot->get_description = sequencer_add_effect_strip_desc; /* Flags. */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index cdbe5bc63ce..3f8dea8b533 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -100,11 +100,7 @@ #define SEQ_HANDLE_SIZE 8.0f #define SEQ_SCROLLER_TEXT_OFFSET 8 #define MUTE_ALPHA 120 - -/* NOTE: Don't use SEQ_ALL_BEGIN/SEQ_ALL_END while drawing! - * it messes up transform. */ -#undef SEQ_ALL_BEGIN -#undef SEQ_ALL_END +#define OVERLAP_ALPHA 180 static Sequence *special_seq_update = NULL; @@ -802,11 +798,17 @@ static void draw_color_strip_band(Sequence *seq, uint pos, float text_margin_y, uchar col[4]; SolidColorVars *colvars = (SolidColorVars *)seq->effectdata; + GPU_blend(GPU_BLEND_ALPHA); rgb_float_to_uchar(col, colvars->col); + + /* Draw muted strips semi-transparent. */ if (seq->flag & SEQ_MUTE) { - GPU_blend(GPU_BLEND_ALPHA); col[3] = MUTE_ALPHA; } + /* Draw background semi-transparent when overlapping strips. */ + else if (seq->flag & SEQ_OVERLAP) { + col[3] = OVERLAP_ALPHA; + } else { col[3] = 255; } @@ -824,9 +826,7 @@ static void draw_color_strip_band(Sequence *seq, uint pos, float text_margin_y, immVertex2f(pos, seq->enddisp, text_margin_y); immEnd(); - if (seq->flag & SEQ_MUTE) { - GPU_blend(GPU_BLEND_NONE); - } + GPU_blend(GPU_BLEND_NONE); } static void draw_seq_background(Scene *scene, @@ -839,6 +839,7 @@ static void draw_seq_background(Scene *scene, bool is_single_image) { uchar col[4]; + GPU_blend(GPU_BLEND_ALPHA); /* Get the correct color per strip type, transitions use their inputs ones. */ if (ELEM(seq->type, SEQ_TYPE_CROSS, SEQ_TYPE_GAMCROSS, SEQ_TYPE_WIPE)) { @@ -855,14 +856,18 @@ static void draw_seq_background(Scene *scene, color3ubv_from_seq(scene, seq, col); } + /* Draw muted strips semi-transparent. */ if (seq->flag & SEQ_MUTE) { - GPU_blend(GPU_BLEND_ALPHA); - col[3] = MUTE_ALPHA; } + /* Draw background semi-transparent when overlapping strips. */ + else if (seq->flag & SEQ_OVERLAP) { + col[3] = OVERLAP_ALPHA; + } else { col[3] = 255; } + immUniformColor4ubv(col); /* Draw the main strip body. */ @@ -922,9 +927,7 @@ static void draw_seq_background(Scene *scene, immEnd(); } - if (seq->flag & SEQ_MUTE) { - GPU_blend(GPU_BLEND_NONE); - } + GPU_blend(GPU_BLEND_NONE); } static void draw_seq_locked(float x1, float y1, float x2, float y2) @@ -1978,7 +1981,7 @@ static void draw_seq_backdrop(View2D *v2d) /* Lines separating the horizontal bands. */ i = max_ii(1, ((int)v2d->cur.ymin) - 1); int line_len = (int)v2d->cur.ymax - i + 1; - immUniformThemeColor(TH_GRID); + immUniformThemeColorShade(TH_GRID, 10); immBegin(GPU_PRIM_LINES, line_len * 2); while (line_len--) { immVertex2f(pos, v2d->cur.xmax, i); @@ -2416,7 +2419,12 @@ void draw_timeline_seq(const bContext *C, ARegion *region) /* Get timeline bound-box, needed for the scroll-bars. */ SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &v2d->tot); draw_seq_backdrop(v2d); - UI_view2d_constant_grid_draw(v2d, FPS); + if ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY) && (sseq->flag & SEQ_SHOW_GRID)) { + U.v2d_min_gridsize *= 3; + UI_view2d_draw_lines_x__discrete_frames_or_seconds( + v2d, scene, (sseq->flag & SEQ_DRAWFRAMES) == 0, false); + U.v2d_min_gridsize /= 3; + } /* Only draw backdrop in timeline view. */ if (sseq->view == SEQ_VIEW_SEQUENCE && sseq->draw_flag & SEQ_DRAW_BACKDROP) { diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index cf26d1e3243..6de95f0995a 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -100,7 +100,7 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce sseq->mainb = SEQ_DRAW_IMG_IMBUF; sseq->flag = SEQ_SHOW_GPENCIL | SEQ_USE_ALPHA | SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES | SEQ_ZOOM_TO_FIT | SEQ_SHOW_STRIP_OVERLAY | SEQ_SHOW_STRIP_NAME | - SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_DURATION; + SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_DURATION | SEQ_SHOW_GRID; /* Tool header. */ region = MEM_callocN(sizeof(ARegion), "tool header for sequencer"); diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc index 1ac2075e281..c38e765caee 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc @@ -489,7 +489,7 @@ bool ED_spreadsheet_context_path_is_active(const bContext *C, SpaceSpreadsheet * break; } SpreadsheetContextNode *node_context = (SpreadsheetContextNode *)node_context_path[i]; - if (!STREQ(node_context->node_name, tree_path[i]->node_name)) { + if (!STREQ(node_context->node_name, tree_path[i + 1]->node_name)) { break; } valid_count++; diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 54f10e259f9..5baa12f7367 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -53,6 +53,7 @@ #include "BKE_screen.h" #include "BKE_workspace.h" +#include "ED_asset.h" #include "ED_render.h" #include "ED_screen.h" #include "ED_space_api.h" @@ -495,7 +496,7 @@ static ID_Type view3d_drop_id_in_main_region_poll_get_id_type(bContext *C, wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0); if (asset_drag) { - return asset_drag->id_type; + return ED_asset_handle_get_id_type(asset_drag->asset_handle); } return 0; diff --git a/source/blender/editors/space_view3d/view3d_camera_control.c b/source/blender/editors/space_view3d/view3d_camera_control.c index 638c8a49ffd..8380c87b999 100644 --- a/source/blender/editors/space_view3d/view3d_camera_control.c +++ b/source/blender/editors/space_view3d/view3d_camera_control.c @@ -24,7 +24,7 @@ * Typical view-control usage: * * - Acquire a view-control (#ED_view3d_cameracontrol_acquire). - * - Modify ``rv3d->ofs``, ``rv3d->viewquat``. + * - Modify `rv3d->ofs`, `rv3d->viewquat`. * - Update the view data (#ED_view3d_cameracontrol_acquire) - * within a loop which draws the viewport. * - Finish and release the view-control (#ED_view3d_cameracontrol_release), @@ -32,8 +32,8 @@ * * Notes: * - * - when acquiring ``rv3d->dist`` is set to zero - * (so ``rv3d->ofs`` is always the view-point) + * - when acquiring `rv3d->dist` is set to zero + * (so `rv3d->ofs` is always the view-point) * - updating can optionally keyframe the camera object. */ @@ -244,7 +244,7 @@ static bool object_apply_mat4_with_protect(Object *ob, } /** - * Updates cameras from the ``rv3d`` values, optionally auto-keyframing. + * Updates cameras from the `rv3d` values, optionally auto-keyframing. */ void ED_view3d_cameracontrol_update(View3DCameraControl *vctrl, /* args for keyframing */ diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index c97ba7ba7e9..d87c14b9844 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1602,7 +1602,7 @@ void view3d_main_region_draw(const bContext *C, ARegion *region) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Offscreen Drawing +/** \name Off-screen Drawing * \{ */ static void view3d_stereo3d_setup_offscreen(Depsgraph *depsgraph, diff --git a/source/blender/editors/space_view3d/view3d_iterators.c b/source/blender/editors/space_view3d/view3d_iterators.c index c7a4030c402..20e00356152 100644 --- a/source/blender/editors/space_view3d/view3d_iterators.c +++ b/source/blender/editors/space_view3d/view3d_iterators.c @@ -439,7 +439,7 @@ void mesh_foreachScreenEdge(ViewContext *vc, } BM_mesh_elem_table_ensure(vc->em->bm, BM_EDGE); - BKE_mesh_foreach_mapped_edge(me, mesh_foreachScreenEdge__mapFunc, &data); + BKE_mesh_foreach_mapped_edge(me, vc->em->bm->totedge, mesh_foreachScreenEdge__mapFunc, &data); } /** \} */ @@ -529,10 +529,11 @@ void mesh_foreachScreenEdge_clip_bb_segment(ViewContext *vc, if ((clip_flag & V3D_PROJ_TEST_CLIP_BB) && (vc->rv3d->clipbb != NULL)) { ED_view3d_clipping_local(vc->rv3d, vc->obedit->obmat); /* for local clipping lookups. */ - BKE_mesh_foreach_mapped_edge(me, mesh_foreachScreenEdge_clip_bb_segment__mapFunc, &data); + BKE_mesh_foreach_mapped_edge( + me, vc->em->bm->totedge, mesh_foreachScreenEdge_clip_bb_segment__mapFunc, &data); } else { - BKE_mesh_foreach_mapped_edge(me, mesh_foreachScreenEdge__mapFunc, &data); + BKE_mesh_foreach_mapped_edge(me, vc->em->bm->totedge, mesh_foreachScreenEdge__mapFunc, &data); } } diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index 8bcc05c1e55..e09453b9957 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -506,8 +506,8 @@ void ED_view3d_lock_clear(View3D *v3d) /** * For viewport operators that exit camera perspective. * - * \note This differs from simply setting ``rv3d->persp = persp`` because it - * sets the ``ofs`` and ``dist`` values of the viewport so it matches the camera, + * \note This differs from simply setting `rv3d->persp = persp` because it + * sets the `ofs` and `dist` values of the viewport so it matches the camera, * otherwise switching out of camera view may jump to a different part of the scene. */ void ED_view3d_persp_switch_from_camera(const Depsgraph *depsgraph, diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index 383f9870714..7377e47da3d 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -1981,7 +1981,7 @@ static void tc_mesh_partial_update(TransInfo *t, /* Promote the partial update types based on the previous state * so the values that no longer modified are reset before being left as-is. - * Needed for translation which can toggle snap-to-normal during transform. */ + * Needed for translation which can toggle snap-to-normal during transform. */ const enum ePartialType partial_for_looptri = MAX2(partial_state->for_looptri, partial_state_prev->for_looptri); const enum ePartialType partial_for_normals = MAX2(partial_state->for_normals, @@ -2067,27 +2067,6 @@ static void tc_mesh_transdata_mirror_apply(TransDataContainer *tc) } } -static bool tc_mesh_is_deform_only_update(TransInfo *t, TransDataContainer *tc) -{ - if (tc->custom.type.data && - ((struct TransCustomDataMesh *)tc->custom.type.data)->cd_layer_correct) { - return false; - } - - Mesh *me_eval = (Mesh *)DEG_get_evaluated_id(t->depsgraph, (ID *)tc->obedit->data); - Mesh *mesh_eval_cage = me_eval->edit_mesh->mesh_eval_cage; - Mesh *mesh_eval_final = me_eval->edit_mesh->mesh_eval_final; - if (mesh_eval_cage && !mesh_eval_cage->runtime.is_original) { - return false; - } - if (mesh_eval_final && mesh_eval_final != mesh_eval_cage && - !mesh_eval_final->runtime.is_original) { - return false; - } - - return me_eval->runtime.deformed_only; -} - void recalcData_mesh(TransInfo *t) { bool is_canceling = t->state == TRANS_CANCEL; @@ -2115,10 +2094,7 @@ void recalcData_mesh(TransInfo *t) tc_mesh_partial_types_calc(t, &partial_state); FOREACH_TRANS_DATA_CONTAINER (t, tc) { - const bool is_deform_only = tc_mesh_is_deform_only_update(t, tc); - - DEG_id_tag_update(tc->obedit->data, - is_deform_only ? ID_RECALC_GEOMETRY_DEFORM : ID_RECALC_GEOMETRY); + DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY); tc_mesh_partial_update(t, tc, &partial_state); } diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index aaac8e21cb9..e89ab6729d2 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -343,9 +343,9 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } else { if (region) { - /* XXX for now, get View2D from the active region */ + /* XXX: For now, get View2D from the active region. */ t->view = ®ion->v2d; - /* XXX for now, the center point is the midpoint of the data */ + /* XXX: For now, the center point is the midpoint of the data. */ } else { t->view = NULL; diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 2cbf52b6100..75744f26c15 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -266,7 +266,8 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_ if (t->flag & T_AUTOIK) { short chainlen = t->settings->autoik_chainlen; if (chainlen) { - ofs += BLI_snprintf_rlen(str + ofs, UI_MAX_DRAW_STR - ofs, TIP_("AutoIK-Len: %d"), chainlen); + ofs += BLI_snprintf_rlen( + str + ofs, UI_MAX_DRAW_STR - ofs, TIP_("Auto IK Length: %d"), chainlen); ofs += BLI_strncpy_rlen(str + ofs, " ", UI_MAX_DRAW_STR - ofs); } } diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index 155250261de..33f4b06eb0e 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -537,10 +537,10 @@ short ED_transform_calc_orientation_from_type_ex(const bContext *C, case V3D_ORIENT_LOCAL: { if (ob) { if (ob->mode & OB_MODE_POSE) { - /* each bone moves on its own local axis, but to avoid confusion, + /* Each bone moves on its own local axis, but to avoid confusion, * use the active pones axis for display T33575, this works as expected on a single * bone and users who select many bones will understand what's going on and what local - * means when they start transforming */ + * means when they start transforming. */ ED_getTransformOrientationMatrix(C, ob, obedit, pivot_point, r_mat); } else { diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index 2d98d756dba..bb04f557074 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -1142,7 +1142,7 @@ static void raycast_obj_fn(SnapObjectContext *sctx, * \param r_loc: Hit location. * \param r_no: Hit normal (optional). * \param r_index: Hit index or -1 when no valid index is found. - * (currently only set to the polygon index when using ``snap_to == SCE_SNAP_MODE_FACE``). + * (currently only set to the polygon index when using `snap_to == SCE_SNAP_MODE_FACE`). * \param r_ob: Hit object. * \param r_obmat: Object matrix (may not be #Object.obmat with dupli-instances). * \param r_hit_list: List of #SnapObjectHitDepth (caller must free). @@ -1213,49 +1213,56 @@ static bool snap_bound_box_check_dist(const float min[3], return true; } -static void cb_mvert_co_get(const int index, const float **co, const BVHTreeFromMesh *data) +static void cb_mvert_co_get(const int index, const void *user_data, const float **r_co) { - *co = data->vert[index].co; + const BVHTreeFromMesh *data = user_data; + *r_co = data->vert[index].co; } -static void cb_bvert_co_get(const int index, const float **co, const BMEditMesh *data) +static void cb_bvert_co_get(const int index, const void *user_data, const float **r_co) { + const BMEditMesh *data = user_data; BMVert *eve = BM_vert_at_index(data->bm, index); - *co = eve->co; + *r_co = eve->co; } -static void cb_mvert_no_copy(const int index, float r_no[3], const BVHTreeFromMesh *data) +static void cb_mvert_no_copy(const int index, const void *user_data, float r_no[3]) { + const BVHTreeFromMesh *data = user_data; const MVert *vert = data->vert + index; normal_short_to_float_v3(r_no, vert->no); } -static void cb_bvert_no_copy(const int index, float r_no[3], const BMEditMesh *data) +static void cb_bvert_no_copy(const int index, const void *user_data, float r_no[3]) { + const BMEditMesh *data = user_data; BMVert *eve = BM_vert_at_index(data->bm, index); copy_v3_v3(r_no, eve->no); } -static void cb_medge_verts_get(const int index, int v_index[2], const BVHTreeFromMesh *data) +static void cb_medge_verts_get(const int index, const void *user_data, int r_v_index[2]) { + const BVHTreeFromMesh *data = user_data; const MEdge *edge = &data->edge[index]; - v_index[0] = edge->v1; - v_index[1] = edge->v2; + r_v_index[0] = edge->v1; + r_v_index[1] = edge->v2; } -static void cb_bedge_verts_get(const int index, int v_index[2], const BMEditMesh *data) +static void cb_bedge_verts_get(const int index, const void *user_data, int r_v_index[2]) { + const BMEditMesh *data = user_data; BMEdge *eed = BM_edge_at_index(data->bm, index); - v_index[0] = BM_elem_index_get(eed->v1); - v_index[1] = BM_elem_index_get(eed->v2); + r_v_index[0] = BM_elem_index_get(eed->v1); + r_v_index[1] = BM_elem_index_get(eed->v2); } -static void cb_mlooptri_edges_get(const int index, int v_index[3], const BVHTreeFromMesh *data) +static void cb_mlooptri_edges_get(const int index, const void *user_data, int r_v_index[3]) { + const BVHTreeFromMesh *data = user_data; const MEdge *medge = data->edge; const MLoop *mloop = data->loop; const MLoopTri *lt = &data->looptri[index]; @@ -1264,22 +1271,23 @@ static void cb_mlooptri_edges_get(const int index, int v_index[3], const BVHTree const uint tri_edge[2] = {mloop[lt->tri[j]].v, mloop[lt->tri[j_next]].v}; if (ELEM(ed->v1, tri_edge[0], tri_edge[1]) && ELEM(ed->v2, tri_edge[0], tri_edge[1])) { // printf("real edge found\n"); - v_index[j] = mloop[lt->tri[j]].e; + r_v_index[j] = mloop[lt->tri[j]].e; } else { - v_index[j] = -1; + r_v_index[j] = -1; } } } -static void cb_mlooptri_verts_get(const int index, int v_index[3], const BVHTreeFromMesh *data) +static void cb_mlooptri_verts_get(const int index, const void *user_data, int r_v_index[3]) { + const BVHTreeFromMesh *data = user_data; const MLoop *loop = data->loop; const MLoopTri *looptri = &data->looptri[index]; - v_index[0] = loop[looptri->tri[0]].v; - v_index[1] = loop[looptri->tri[1]].v; - v_index[2] = loop[looptri->tri[2]].v; + r_v_index[0] = loop[looptri->tri[0]].v; + r_v_index[1] = loop[looptri->tri[1]].v; + r_v_index[2] = loop[looptri->tri[2]].v; } static bool test_projected_vert_dist(const struct DistProjectedAABBPrecalc *precalc, @@ -1348,12 +1356,20 @@ static bool test_projected_edge_dist(const struct DistProjectedAABBPrecalc *prec /** \name Walk DFS * \{ */ -typedef void (*Nearest2DGetVertCoCallback)(const int index, const float **co, void *data); -typedef void (*Nearest2DGetEdgeVertsCallback)(const int index, const int v_index[2], void *data); -typedef void (*Nearest2DGetTriVertsCallback)(const int index, const int v_index[3], void *data); +typedef void (*Nearest2DGetVertCoCallback)(const int index, + const void *user_data, + const float **r_co); +typedef void (*Nearest2DGetEdgeVertsCallback)(const int index, + const void *user_data, + int r_v_index[2]); +typedef void (*Nearest2DGetTriVertsCallback)(const int index, + const void *user_data, + int r_v_index[3]); /* Equal the previous one */ -typedef void (*Nearest2DGetTriEdgesCallback)(const int index, const int e_index[3], void *data); -typedef void (*Nearest2DCopyVertNoCallback)(const int index, const float r_no[3], void *data); +typedef void (*Nearest2DGetTriEdgesCallback)(const int index, + const void *user_data, + int r_e_index[3]); +typedef void (*Nearest2DCopyVertNoCallback)(const int index, const void *user_data, float r_no[3]); typedef struct Nearest2dUserData { void *userdata; @@ -1374,18 +1390,18 @@ static void nearest2d_data_init(SnapObjectData *sod, { if (sod->type == SNAP_MESH) { r_nearest2d->userdata = &sod->treedata_mesh; - r_nearest2d->get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get; - r_nearest2d->get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get; - r_nearest2d->copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy; - r_nearest2d->get_tri_verts_index = (Nearest2DGetTriVertsCallback)cb_mlooptri_verts_get; - r_nearest2d->get_tri_edges_index = (Nearest2DGetTriEdgesCallback)cb_mlooptri_edges_get; + r_nearest2d->get_vert_co = cb_mvert_co_get; + r_nearest2d->get_edge_verts_index = cb_medge_verts_get; + r_nearest2d->copy_vert_no = cb_mvert_no_copy; + r_nearest2d->get_tri_verts_index = cb_mlooptri_verts_get; + r_nearest2d->get_tri_edges_index = cb_mlooptri_edges_get; } else { BLI_assert(sod->type == SNAP_EDIT_MESH); r_nearest2d->userdata = sod->treedata_editmesh.em; - r_nearest2d->get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get; - r_nearest2d->get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get; - r_nearest2d->copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy; + r_nearest2d->get_vert_co = cb_bvert_co_get; + r_nearest2d->get_edge_verts_index = cb_bedge_verts_get; + r_nearest2d->copy_vert_no = cb_bvert_no_copy; r_nearest2d->get_tri_verts_index = NULL; r_nearest2d->get_tri_edges_index = NULL; } @@ -1404,7 +1420,7 @@ static void cb_snap_vert(void *userdata, struct Nearest2dUserData *data = userdata; const float *co; - data->get_vert_co(index, &co, data->userdata); + data->get_vert_co(index, data->userdata, &co); if (test_projected_vert_dist(precalc, clip_plane, @@ -1413,7 +1429,7 @@ static void cb_snap_vert(void *userdata, co, &nearest->dist_sq, nearest->co)) { - data->copy_vert_no(index, nearest->no, data->userdata); + data->copy_vert_no(index, data->userdata, nearest->no); nearest->index = index; } } @@ -1428,11 +1444,11 @@ static void cb_snap_edge(void *userdata, struct Nearest2dUserData *data = userdata; int vindex[2]; - data->get_edge_verts_index(index, vindex, data->userdata); + data->get_edge_verts_index(index, data->userdata, vindex); const float *v_pair[2]; - data->get_vert_co(vindex[0], &v_pair[0], data->userdata); - data->get_vert_co(vindex[1], &v_pair[1], data->userdata); + data->get_vert_co(vindex[0], data->userdata, &v_pair[0]); + data->get_vert_co(vindex[1], data->userdata, &v_pair[1]); if (test_projected_edge_dist(precalc, clip_plane, @@ -1457,7 +1473,7 @@ static void cb_snap_edge_verts(void *userdata, struct Nearest2dUserData *data = userdata; int vindex[2]; - data->get_edge_verts_index(index, vindex, data->userdata); + data->get_edge_verts_index(index, data->userdata, vindex); for (int i = 2; i--;) { if (vindex[i] == nearest->index) { @@ -1478,12 +1494,12 @@ static void cb_snap_tri_edges(void *userdata, if (data->use_backface_culling) { int vindex[3]; - data->get_tri_verts_index(index, vindex, data->userdata); + data->get_tri_verts_index(index, data->userdata, vindex); const float *t0, *t1, *t2; - data->get_vert_co(vindex[0], &t0, data->userdata); - data->get_vert_co(vindex[1], &t1, data->userdata); - data->get_vert_co(vindex[2], &t2, data->userdata); + data->get_vert_co(vindex[0], data->userdata, &t0); + data->get_vert_co(vindex[1], data->userdata, &t1); + data->get_vert_co(vindex[2], data->userdata, &t2); float dummy[3]; if (raycast_tri_backface_culling_test(precalc->ray_direction, t0, t1, t2, dummy)) { return; @@ -1491,7 +1507,7 @@ static void cb_snap_tri_edges(void *userdata, } int eindex[3]; - data->get_tri_edges_index(index, eindex, data->userdata); + data->get_tri_edges_index(index, data->userdata, eindex); for (int i = 3; i--;) { if (eindex[i] != -1) { if (eindex[i] == nearest->index) { @@ -1512,13 +1528,13 @@ static void cb_snap_tri_verts(void *userdata, struct Nearest2dUserData *data = userdata; int vindex[3]; - data->get_tri_verts_index(index, vindex, data->userdata); + data->get_tri_verts_index(index, data->userdata, vindex); if (data->use_backface_culling) { const float *t0, *t1, *t2; - data->get_vert_co(vindex[0], &t0, data->userdata); - data->get_vert_co(vindex[1], &t1, data->userdata); - data->get_vert_co(vindex[2], &t2, data->userdata); + data->get_vert_co(vindex[0], data->userdata, &t0); + data->get_vert_co(vindex[1], data->userdata, &t1); + data->get_vert_co(vindex[2], data->userdata, &t2); float dummy[3]; if (raycast_tri_backface_culling_test(precalc->ray_direction, t0, t1, t2, dummy)) { return; @@ -1693,11 +1709,11 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); int vindex[2]; - nearest2d.get_edge_verts_index(*r_index, vindex, nearest2d.userdata); + nearest2d.get_edge_verts_index(*r_index, nearest2d.userdata, vindex); const float *v_pair[2]; - nearest2d.get_vert_co(vindex[0], &v_pair[0], nearest2d.userdata); - nearest2d.get_vert_co(vindex[1], &v_pair[1], nearest2d.userdata); + nearest2d.get_vert_co(vindex[0], nearest2d.userdata, &v_pair[0]); + nearest2d.get_vert_co(vindex[1], nearest2d.userdata, &v_pair[1]); struct DistProjectedAABBPrecalc neasrest_precalc; { @@ -1744,7 +1760,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, if (r_no) { float imat[4][4]; invert_m4_m4(imat, obmat); - nearest2d.copy_vert_no(vindex[v_id], r_no, nearest2d.userdata); + nearest2d.copy_vert_no(vindex[v_id], nearest2d.userdata, r_no); mul_transposed_mat3_m4_v3(imat, r_no); normalize_v3(r_no); } @@ -2777,7 +2793,7 @@ static void snap_obj_fn(SnapObjectContext *sctx, * \param r_loc: Hit location. * \param r_no: Hit normal (optional). * \param r_index: Hit index or -1 when no valid index is found. - * (currently only set to the polygon index when using ``snap_to == SCE_SNAP_MODE_FACE``). + * (currently only set to the polygon index when using `snap_to == SCE_SNAP_MODE_FACE`). * \param r_ob: Hit object. * \param r_obmat: Object matrix (may not be #Object.obmat with dupli-instances). */ diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c index d0b730383d5..a1f396eb503 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.c +++ b/source/blender/editors/transform/transform_snap_sequencer.c @@ -34,6 +34,7 @@ #include "UI_view2d.h" +#include "SEQ_effects.h" #include "SEQ_iterator.h" #include "SEQ_sequencer.h" @@ -104,11 +105,41 @@ static void seq_snap_source_points_build(const TransInfo *UNUSED(t), /** \name Snap targets * \{ */ -static SeqCollection *query_snap_targets(const TransInfo *t) +/* Add effect strips directly or indirectly connected to `seq_reference` to `collection`. */ +static void query_strip_effects_fn(Sequence *seq_reference, + ListBase *seqbase, + SeqCollection *collection) { - const ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false)); + if (!SEQ_collection_append_strip(seq_reference, collection)) { + return; /* Strip is already in set, so all effects connected to it are as well. */ + } + + /* Find all strips connected to `seq_reference`. */ + LISTBASE_FOREACH (Sequence *, seq_test, seqbase) { + if (seq_test->seq1 == seq_reference || seq_test->seq2 == seq_reference || + seq_test->seq3 == seq_reference) { + query_strip_effects_fn(seq_test, seqbase, collection); + } + } +} + +static SeqCollection *seq_collection_extract_effects(SeqCollection *collection) +{ + SeqCollection *effects = SEQ_collection_create(__func__); + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + if (SEQ_effect_get_num_inputs(seq->type) > 0) { + SEQ_collection_append_strip(seq, effects); + } + } + return effects; +} + +static SeqCollection *query_snap_targets(const TransInfo *t, SeqCollection *snap_sources) +{ + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false)); const short snap_flag = SEQ_tool_settings_snap_flag_get(t->scene); - SeqCollection *collection = SEQ_collection_create(__func__); + SeqCollection *snap_targets = SEQ_collection_create(__func__); LISTBASE_FOREACH (Sequence *, seq, seqbase) { if ((seq->flag & SELECT)) { continue; /* Selected are being transformed. */ @@ -119,9 +150,18 @@ static SeqCollection *query_snap_targets(const TransInfo *t) if (seq->type == SEQ_TYPE_SOUND_RAM && (snap_flag & SEQ_SNAP_IGNORE_SOUND)) { continue; } - SEQ_collection_append_strip(seq, collection); + SEQ_collection_append_strip(seq, snap_targets); } - return collection; + + /* Effects will always change position with strip to which they are connected and they don't have + * to be selected. Remove such strips from `snap_targets` collection. */ + SeqCollection *snap_sources_temp = SEQ_collection_duplicate(snap_sources); + SEQ_collection_expand(seqbase, snap_sources_temp, query_strip_effects_fn); + SeqCollection *snap_sources_effects = seq_collection_extract_effects(snap_sources_temp); + SEQ_collection_exclude(snap_targets, snap_sources_effects); + SEQ_collection_free(snap_sources_temp); + + return snap_targets; } static int seq_get_snap_target_points_count(const TransInfo *t, @@ -178,11 +218,14 @@ static void seq_snap_target_points_build(const TransInfo *t, if (snap_mode & SEQ_SNAP_TO_STRIP_HOLD) { int content_start = min_ii(seq->enddisp, seq->start); int content_end = max_ii(seq->startdisp, seq->start + seq->len); - if (seq->anim_startofs == 0) { - content_start = seq->startdisp; - } - if (seq->anim_endofs == 0) { - content_end = seq->enddisp; + /* Effects and single image strips produce incorrect content length. Skip these strips. */ + if ((seq->type & SEQ_TYPE_EFFECT) != 0 || seq->len == 1) { + if (seq->anim_startofs == 0 && seq->startstill == 0) { + content_start = seq->startdisp; + } + if (seq->anim_endofs == 0 && seq->endstill == 0) { + content_end = seq->enddisp; + } } snap_data->target_snap_points[i] = content_start; snap_data->target_snap_points[i + 1] = content_end; @@ -214,16 +257,25 @@ TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t) TransSeqSnapData *snap_data = MEM_callocN(sizeof(TransSeqSnapData), __func__); ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false)); - /* Build arrays of snap points. */ SeqCollection *snap_sources = SEQ_query_selected_strips(seqbase); + SeqCollection *snap_targets = query_snap_targets(t, snap_sources); + + if (SEQ_collection_len(snap_sources) == 0 || SEQ_collection_len(snap_targets) == 0) { + SEQ_collection_free(snap_targets); + SEQ_collection_free(snap_sources); + MEM_freeN(snap_data); + return NULL; + } + + /* Build arrays of snap points. */ seq_snap_source_points_alloc(snap_data, snap_sources); seq_snap_source_points_build(t, snap_data, snap_sources); SEQ_collection_free(snap_sources); - SeqCollection *snap_targets = query_snap_targets(t); seq_snap_target_points_alloc(t, snap_data, snap_targets); seq_snap_target_points_build(t, snap_data, snap_targets); SEQ_collection_free(snap_targets); + return snap_data; } @@ -236,12 +288,16 @@ void transform_snap_sequencer_data_free(TransSeqSnapData *data) bool transform_snap_sequencer_calc(TransInfo *t) { + const TransSeqSnapData *snap_data = t->tsnap.seq_context; + if (snap_data == NULL) { + return false; + } + /* Prevent snapping when constrained to Y axis. */ if (t->con.mode & CON_APPLY && t->con.mode & CON_AXIS1) { return false; } - const TransSeqSnapData *snap_data = t->tsnap.seq_context; int best_dist = MAXFRAME, best_target_frame = 0, best_source_frame = 0; for (int i = 0; i < snap_data->source_snap_point_count; i++) { diff --git a/source/blender/editors/util/ed_draw.c b/source/blender/editors/util/ed_draw.c index 3b543cdf6ce..fc351ab728c 100644 --- a/source/blender/editors/util/ed_draw.c +++ b/source/blender/editors/util/ed_draw.c @@ -33,6 +33,8 @@ #include "BLI_string.h" #include "BLI_utildefines.h" +#include "BLT_translation.h" + #include "BKE_context.h" #include "BKE_image.h" @@ -56,6 +58,473 @@ #include "WM_api.h" #include "WM_types.h" +/* -------------------------------------------------------------------- */ +/** \name Generic Slider + * + * The generic slider is supposed to be called during modal operations. It calculates a factor + * value based on mouse position and draws a visual representation. In order to use it, you need to + * store a reference to a #tSlider in your operator which you get by calling #ED_slider_create. + * Then you need to update it during modal operations by calling #ED_slider_modal", which will + * update #tSlider.factor for you to use. To remove drawing and free the memory, call + * #ED_slider_destroy. + * \{ */ + +#define SLIDE_PIXEL_DISTANCE (300.0f * U.dpi_fac) +#define OVERSHOOT_RANGE_DELTA 0.2f + +typedef struct tSlider { + struct Scene *scene; + struct ScrArea *area; + + /** Header of the region used for drawing the slider. */ + struct ARegion *region_header; + + /** Draw callback handler. */ + void *draw_handle; + + /** Accumulative factor (not clamped or rounded). */ + float raw_factor; + + /** 0-1 value for determining the influence of whatever is relevant. */ + float factor; + + /** Last mouse cursor position used for mouse movement delta calculation. */ + float last_cursor[2]; + + /** Enable range beyond 0-100%. */ + bool allow_overshoot; + + /** Allow overshoot or clamp between 0% and 100%. */ + bool overshoot; + + /** Move factor in 10% steps. */ + bool increments; + + /** Reduces factor delta from mouse movement. */ + bool precision; +} tSlider; + +static void draw_overshoot_triangle(const uint8_t color[4], + const bool facing_right, + const float x, + const float y) +{ + const uint shdr_pos_2d = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + GPU_blend(GPU_BLEND_ALPHA); + GPU_polygon_smooth(true); + immUniformColor3ubvAlpha(color, 225); + const float triangle_side_length = facing_right ? 6 * U.pixelsize : -6 * U.pixelsize; + const float triangle_offset = facing_right ? 2 * U.pixelsize : -2 * U.pixelsize; + + immBegin(GPU_PRIM_TRIS, 3); + immVertex2f(shdr_pos_2d, x + triangle_offset + triangle_side_length, y); + immVertex2f(shdr_pos_2d, x + triangle_offset, y + triangle_side_length / 2); + immVertex2f(shdr_pos_2d, x + triangle_offset, y - triangle_side_length / 2); + immEnd(); + + GPU_polygon_smooth(false); + GPU_blend(GPU_BLEND_NONE); + immUnbindProgram(); +} + +static void draw_ticks(const float start_factor, + const float end_factor, + const float line_start[2], + const float base_tick_height, + const float line_width, + const uint8_t color_overshoot[4], + const uint8_t color_line[4]) +{ + /* Use factor represented as 0-100 int to avoid floating point precision problems. */ + const int tick_increment = 10; + + /* Round initial_tick_factor up to the next tick_increment. */ + int tick_percentage = ceil((start_factor * 100) / tick_increment) * tick_increment; + + while (tick_percentage <= (int)(end_factor * 100)) { + float tick_height; + /* Different ticks have different heights. Multiples of 100% are the tallest, 50% is a bit + * smaller and the rest is the minimum size. */ + if (tick_percentage % 100 == 0) { + tick_height = base_tick_height; + } + else if (tick_percentage % 50 == 0) { + tick_height = base_tick_height * 0.8; + } + else { + tick_height = base_tick_height * 0.5; + } + + const float x = line_start[0] + + (((float)tick_percentage / 100) - start_factor) * SLIDE_PIXEL_DISTANCE; + const rctf tick_rect = { + .xmin = x - (line_width / 2), + .xmax = x + (line_width / 2), + .ymin = line_start[1] - (tick_height / 2), + .ymax = line_start[1] + (tick_height / 2), + }; + + if (tick_percentage < 0 || tick_percentage > 100) { + UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_overshoot, 255); + } + else { + UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_line, 255); + } + tick_percentage += tick_increment; + } +} + +static void draw_main_line(const rctf *main_line_rect, + const float factor, + const bool overshoot, + const uint8_t color_overshoot[4], + const uint8_t color_line[4]) +{ + if (overshoot) { + /* In overshoot mode, draw the 0-100% range differently to provide a visual reference. */ + const float line_zero_percent = main_line_rect->xmin - + ((factor - 0.5f - OVERSHOOT_RANGE_DELTA) * + SLIDE_PIXEL_DISTANCE); + + const float clamped_line_zero_percent = clamp_f( + line_zero_percent, main_line_rect->xmin, main_line_rect->xmax); + const float clamped_line_hundred_percent = clamp_f( + line_zero_percent + SLIDE_PIXEL_DISTANCE, main_line_rect->xmin, main_line_rect->xmax); + + const rctf left_overshoot_line_rect = { + .xmin = main_line_rect->xmin, + .xmax = clamped_line_zero_percent, + .ymin = main_line_rect->ymin, + .ymax = main_line_rect->ymax, + }; + const rctf right_overshoot_line_rect = { + .xmin = clamped_line_hundred_percent, + .xmax = main_line_rect->xmax, + .ymin = main_line_rect->ymin, + .ymax = main_line_rect->ymax, + }; + UI_draw_roundbox_3ub_alpha(&left_overshoot_line_rect, true, 0, color_overshoot, 255); + UI_draw_roundbox_3ub_alpha(&right_overshoot_line_rect, true, 0, color_overshoot, 255); + + const rctf non_overshoot_line_rect = { + .xmin = clamped_line_zero_percent, + .xmax = clamped_line_hundred_percent, + .ymin = main_line_rect->ymin, + .ymax = main_line_rect->ymax, + }; + UI_draw_roundbox_3ub_alpha(&non_overshoot_line_rect, true, 0, color_line, 255); + } + else { + UI_draw_roundbox_3ub_alpha(main_line_rect, true, 0, color_line, 255); + } +} + +static void draw_backdrop(const int fontid, + const rctf *main_line_rect, + const float color_bg[4], + const short region_y_size, + const float base_tick_height) +{ + float string_pixel_size[2]; + const char *percentage_string_placeholder = "000%%"; + BLF_width_and_height(fontid, + percentage_string_placeholder, + sizeof(percentage_string_placeholder), + &string_pixel_size[0], + &string_pixel_size[1]); + const float pad[2] = {(region_y_size - base_tick_height) / 2, 2.0f * U.pixelsize}; + const rctf backdrop_rect = { + .xmin = main_line_rect->xmin - string_pixel_size[0] - pad[0], + .xmax = main_line_rect->xmax + pad[0], + .ymin = pad[1], + .ymax = region_y_size - pad[1], + }; + UI_draw_roundbox_aa(&backdrop_rect, true, 4.0f, color_bg); +} + +/** + * Draw an on screen Slider for a Pose Slide Operator. + */ +static void slider_draw(const struct bContext *UNUSED(C), ARegion *region, void *arg) +{ + tSlider *slider = arg; + + /* Only draw in region from which the Operator was started. */ + if (region != slider->region_header) { + return; + } + + uint8_t color_text[4]; + uint8_t color_line[4]; + uint8_t color_handle[4]; + uint8_t color_overshoot[4]; + float color_bg[4]; + + /* Get theme colors. */ + UI_GetThemeColor4ubv(TH_TEXT, color_text); + UI_GetThemeColor4ubv(TH_TEXT, color_line); + UI_GetThemeColor4ubv(TH_TEXT, color_overshoot); + UI_GetThemeColor4ubv(TH_ACTIVE, color_handle); + UI_GetThemeColor3fv(TH_BACK, color_bg); + + color_bg[3] = 0.5f; + color_overshoot[0] = color_overshoot[0] * 0.7; + color_overshoot[1] = color_overshoot[1] * 0.7; + color_overshoot[2] = color_overshoot[2] * 0.7; + + /* Get the default font. */ + const uiStyle *style = UI_style_get(); + const uiFontStyle *fstyle = &style->widget; + const int fontid = fstyle->uifont_id; + BLF_color3ubv(fontid, color_text); + BLF_rotation(fontid, 0.0f); + + const float line_width = 1.5 * U.pixelsize; + const float base_tick_height = 12.0 * U.pixelsize; + const float line_y = region->winy / 2; + + rctf main_line_rect = { + .xmin = (region->winx / 2) - (SLIDE_PIXEL_DISTANCE / 2), + .xmax = (region->winx / 2) + (SLIDE_PIXEL_DISTANCE / 2), + .ymin = line_y - line_width / 2, + .ymax = line_y + line_width / 2, + }; + float line_start_factor = 0; + int handle_pos_x = main_line_rect.xmin + SLIDE_PIXEL_DISTANCE * slider->factor; + + if (slider->overshoot) { + main_line_rect.xmin = main_line_rect.xmin - SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA; + main_line_rect.xmax = main_line_rect.xmax + SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA; + line_start_factor = slider->factor - 0.5f - OVERSHOOT_RANGE_DELTA; + handle_pos_x = region->winx / 2; + } + + draw_backdrop(fontid, &main_line_rect, color_bg, slider->region_header->winy, base_tick_height); + + draw_main_line(&main_line_rect, slider->factor, slider->overshoot, color_overshoot, color_line); + + const float factor_range = slider->overshoot ? 1 + OVERSHOOT_RANGE_DELTA * 2 : 1; + const float line_start_position[2] = {main_line_rect.xmin, line_y}; + draw_ticks(line_start_factor, + line_start_factor + factor_range, + line_start_position, + base_tick_height, + line_width, + color_overshoot, + color_line); + + /* Draw triangles at the ends of the line in overshoot mode to indicate direction of 0-100% + * range. */ + if (slider->overshoot) { + if (slider->factor > 1 + OVERSHOOT_RANGE_DELTA + 0.5) { + draw_overshoot_triangle(color_line, false, main_line_rect.xmin, line_y); + } + if (slider->factor < 0 - OVERSHOOT_RANGE_DELTA - 0.5) { + draw_overshoot_triangle(color_line, true, main_line_rect.xmax, line_y); + } + } + + char percentage_string[256]; + + /* Draw handle indicating current factor. */ + const rctf handle_rect = { + .xmin = handle_pos_x - (line_width), + .xmax = handle_pos_x + (line_width), + .ymin = line_y - (base_tick_height / 2), + .ymax = line_y + (base_tick_height / 2), + }; + + UI_draw_roundbox_3ub_alpha(&handle_rect, true, 1, color_handle, 255); + BLI_snprintf(percentage_string, sizeof(percentage_string), "%.0f%%", slider->factor * 100); + + /* Draw percentage string. */ + float percentage_string_pixel_size[2]; + BLF_width_and_height(fontid, + percentage_string, + sizeof(percentage_string), + &percentage_string_pixel_size[0], + &percentage_string_pixel_size[1]); + + BLF_position(fontid, + main_line_rect.xmin - 24.0 * U.pixelsize - percentage_string_pixel_size[0] / 2, + (region->winy / 2) - percentage_string_pixel_size[1] / 2, + 0.0f); + BLF_draw(fontid, percentage_string, sizeof(percentage_string)); +} + +static void slider_update_factor(tSlider *slider, const wmEvent *event) +{ + const float factor_delta = (event->x - slider->last_cursor[0]) / SLIDE_PIXEL_DISTANCE; + /* Reduced factor delta in precision mode (shift held). */ + slider->raw_factor += slider->precision ? (factor_delta / 8) : factor_delta; + slider->factor = slider->raw_factor; + slider->last_cursor[0] = event->x; + slider->last_cursor[1] = event->y; + + if (!slider->overshoot) { + slider->factor = clamp_f(slider->factor, 0, 1); + } + + if (slider->increments) { + slider->factor = round(slider->factor * 10) / 10; + } +} + +tSlider *ED_slider_create(struct bContext *C) +{ + tSlider *slider = MEM_callocN(sizeof(tSlider), "tSlider"); + slider->scene = CTX_data_scene(C); + slider->area = CTX_wm_area(C); + slider->region_header = CTX_wm_region(C); + + /* Default is true, caller needs to manually set to false. */ + slider->allow_overshoot = true; + + /* Set initial factor. */ + slider->raw_factor = 0.5f; + slider->factor = 0.5; + + /* Add draw callback. Always in header. */ + LISTBASE_FOREACH (ARegion *, region, &slider->area->regionbase) { + if (region->regiontype == RGN_TYPE_HEADER) { + slider->region_header = region; + slider->draw_handle = ED_region_draw_cb_activate( + region->type, slider_draw, slider, REGION_DRAW_POST_PIXEL); + } + } + + return slider; +} + +/** + * For modal operations so the percentage doesn't pop on the first mouse movement. + */ +void ED_slider_init(struct tSlider *slider, const wmEvent *event) +{ + slider->last_cursor[0] = event->x; + slider->last_cursor[1] = event->y; +} + +/** + * Calculate slider factor based on mouse position. + */ +bool ED_slider_modal(tSlider *slider, const wmEvent *event) +{ + bool event_handled = true; + /* Handle key presses. */ + switch (event->type) { + case EVT_EKEY: + if (slider->allow_overshoot) { + slider->overshoot = event->val == KM_PRESS ? !slider->overshoot : slider->overshoot; + slider_update_factor(slider, event); + } + break; + case EVT_LEFTSHIFTKEY: + case EVT_RIGHTSHIFTKEY: + slider->precision = event->val == KM_PRESS; + break; + case EVT_LEFTCTRLKEY: + case EVT_RIGHTCTRLKEY: + slider->increments = event->val == KM_PRESS; + break; + case MOUSEMOVE:; + /* Update factor. */ + slider_update_factor(slider, event); + break; + default: + event_handled = false; + break; + } + + ED_region_tag_redraw(slider->region_header); + + return event_handled; +} + +/** + * Return string based on the current state of the slider. + */ +void ED_slider_status_string_get(const struct tSlider *slider, + char *status_string, + const size_t size_of_status_string) +{ + /* 50 characters is enough to fit the individual setting strings. Extend if message is longer. */ + char overshoot_str[50]; + char precision_str[50]; + char increments_str[50]; + + if (slider->allow_overshoot) { + if (slider->overshoot) { + STRNCPY(overshoot_str, TIP_("[E] - Disable overshoot")); + } + else { + STRNCPY(overshoot_str, TIP_("[E] - Enable overshoot")); + } + } + else { + STRNCPY(overshoot_str, TIP_("Overshoot disabled")); + } + + if (slider->precision) { + STRNCPY(precision_str, TIP_("[Shift] - Precision active")); + } + else { + STRNCPY(precision_str, TIP_("Shift - Hold for precision")); + } + + if (slider->increments) { + STRNCPY(increments_str, TIP_("[Ctrl] - Increments active")); + } + else { + STRNCPY(increments_str, TIP_("Ctrl - Hold for 10% increments")); + } + + BLI_snprintf(status_string, + size_of_status_string, + "%s | %s | %s", + overshoot_str, + precision_str, + increments_str); +} + +void ED_slider_destroy(struct bContext *C, tSlider *slider) +{ + /* Remove draw callback. */ + ED_region_draw_cb_exit(slider->region_header->type, slider->draw_handle); + ED_area_status_text(slider->area, NULL); + ED_workspace_status_text(C, NULL); + MEM_freeN(slider); +} + +/* Setters & Getters */ + +float ED_slider_factor_get(struct tSlider *slider) +{ + return slider->factor; +} + +void ED_slider_factor_set(struct tSlider *slider, const float factor) +{ + slider->factor = factor; + if (!slider->overshoot) { + slider->factor = clamp_f(slider->factor, 0, 1); + } +} + +bool ED_slider_allow_overshoot_get(struct tSlider *slider) +{ + return slider->allow_overshoot; +} + +void ED_slider_allow_overshoot_set(struct tSlider *slider, const bool value) +{ + slider->allow_overshoot = value; +} + +/** \} */ + /** * Callback that draws a line between the mouse and a position given as the initial argument. */ @@ -309,7 +778,9 @@ static float metadata_box_height_get(ImBuf *ibuf, int fontid, const bool is_top) return 0; } -/* Should be kept in sync with BKE_image_stamp_buf */ +/** + * \note Keep in sync with #BKE_image_stamp_buf. + */ void ED_region_image_metadata_draw( int x, int y, ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy) { diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 7709b76290f..20aadb84b7b 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -1126,7 +1126,7 @@ static void uv_select_edgeloop_single_side_tag(const Scene *scene, while (l_step != NULL) { if (!uvedit_face_visible_test(scene, l_step->f) || - /* Check the boundary is still a boundary. */ + /* Check the boundary is still a boundary. */ (uvedit_loop_find_other_radial_loop_with_visible_face( scene, l_step, cd_loop_uv_offset) != NULL)) { break; diff --git a/source/blender/freestyle/intern/application/Controller.cpp b/source/blender/freestyle/intern/application/Controller.cpp index 9c675c93a2e..0d243a812fa 100644 --- a/source/blender/freestyle/intern/application/Controller.cpp +++ b/source/blender/freestyle/intern/application/Controller.cpp @@ -707,7 +707,7 @@ void Controller::ComputeSteerableViewMap() for (unsigned int x = 0; x < img[i]->width(); ++x) { //img[i]->setPixel(x, y, (float)qGray(qimg.pixel(x, y)) / 255.0f); img[i]->setPixel(x, y, (float)qGray(qimg.pixel(x, y))); - //float c = qGray(qimg.pixel(x, y)); + // float c = qGray(qimg.pixel(x, y)); //img[i]->setPixel(x, y, qGray(qimg.pixel(x, y))); } } diff --git a/source/blender/freestyle/intern/geometry/FastGrid.cpp b/source/blender/freestyle/intern/geometry/FastGrid.cpp index 1e0ae06da19..0f1edd647c7 100644 --- a/source/blender/freestyle/intern/geometry/FastGrid.cpp +++ b/source/blender/freestyle/intern/geometry/FastGrid.cpp @@ -62,7 +62,7 @@ Cell *FastGrid::getCell(const Vec3u &p) << " " << _cells_size << endl; } #endif - BLI_assert(_cells || ("_cells is a null pointer")); + BLI_assert_msg(_cells, "_cells is a null pointer"); BLI_assert((_cells_nb[0] * (p[2] * _cells_nb[1] + p[1]) + p[0]) < _cells_size); BLI_assert(p[0] < _cells_nb[0]); BLI_assert(p[1] < _cells_nb[1]); @@ -72,7 +72,7 @@ Cell *FastGrid::getCell(const Vec3u &p) void FastGrid::fillCell(const Vec3u &p, Cell &cell) { - BLI_assert(_cells || ("_cells is a null pointer")); + BLI_assert_msg(_cells, "_cells is a null pointer"); BLI_assert((_cells_nb[0] * (p[2] * _cells_nb[1] + p[1]) + p[0]) < _cells_size); BLI_assert(p[0] < _cells_nb[0]); BLI_assert(p[1] < _cells_nb[1]); diff --git a/source/blender/freestyle/intern/view_map/Interface0D.h b/source/blender/freestyle/intern/view_map/Interface0D.h index 065319578e5..d7b213706b5 100644 --- a/source/blender/freestyle/intern/view_map/Interface0D.h +++ b/source/blender/freestyle/intern/view_map/Interface0D.h @@ -311,13 +311,13 @@ class Interface0DIterator : public Iterator { return result; } - /** operator == . */ + /** operator `==`. */ bool operator==(const Interface0DIterator &it) const { return _iterator->operator==(*(it._iterator)); } - /** operator != . */ + /** operator `!=`. */ bool operator!=(const Interface0DIterator &it) const { return !(*this == it); diff --git a/source/blender/freestyle/intern/view_map/Silhouette.h b/source/blender/freestyle/intern/view_map/Silhouette.h index 3709b0ae11a..479dbaeedc1 100644 --- a/source/blender/freestyle/intern/view_map/Silhouette.h +++ b/source/blender/freestyle/intern/view_map/Silhouette.h @@ -745,7 +745,7 @@ class FEdge : public Interface1D { _VertexB = vB; } - /** Sets the FEdge Id . */ + /** Sets the FEdge Id. */ inline void setId(const Id &id) { _Id = id; @@ -1153,7 +1153,7 @@ class FEdgeSharp : public FEdge { bool _bFaceMark; public: - /** Returns the string "FEdgeSharp" . */ + /** Returns the string "FEdgeSharp". */ virtual string getExactTypeName() const { return "FEdgeSharp"; @@ -1305,7 +1305,7 @@ class FEdgeSmooth : public FEdge { bool _FaceMark; public: - /** Returns the string "FEdgeSmooth" . */ + /** Returns the string "FEdgeSmooth". */ virtual string getExactTypeName() const { return "FEdgeSmooth"; diff --git a/source/blender/freestyle/intern/view_map/ViewMap.h b/source/blender/freestyle/intern/view_map/ViewMap.h index bd7edad7642..e7d57c9dfb4 100644 --- a/source/blender/freestyle/intern/view_map/ViewMap.h +++ b/source/blender/freestyle/intern/view_map/ViewMap.h @@ -477,10 +477,14 @@ class TVertex : public ViewVertex { directedViewEdge _FrontEdgeB; directedViewEdge _BackEdgeA; directedViewEdge _BackEdgeB; - Id _Id; // id to identify t vertices . these id will be negative in order not to be mixed with - // NonTVertex ids. - edge_pointers_container - _sortedEdges; // the list of the four ViewEdges, ordered in CCW order (in the image plan) + + /** + * ID to identify t vertices. + * these id will be negative in order not to be mixed with NonTVertex ids. + */ + Id _Id; + /** The list of the four ViewEdges, ordered in CCW order (in the image plan). */ + edge_pointers_container _sortedEdges; public: /** Default constructor. */ diff --git a/source/blender/freestyle/intern/winged_edge/Curvature.cpp b/source/blender/freestyle/intern/winged_edge/Curvature.cpp index 478e48de66c..f052f1af104 100644 --- a/source/blender/freestyle/intern/winged_edge/Curvature.cpp +++ b/source/blender/freestyle/intern/winged_edge/Curvature.cpp @@ -371,29 +371,29 @@ void gts_vertex_principal_directions(WVertex *v, Vec3r Kh, real Kg, Vec3r &e1, V } e = *itE; - /* since this vertex passed the tests in gts_vertex_mean_curvature_normal(), this should be - * true. */ + /* Since this vertex passed the tests in gts_vertex_mean_curvature_normal(), + this should be true. */ // g_assert(gts_edge_face_number (e, s) == 2); - /* identify the two triangles bordering e in s */ + /* Identify the two triangles bordering e in s. */ f1 = e->GetaFace(); f2 = e->GetbFace(); /* We are solving for the values of the curvature tensor - * B = [ a b ; b c ]. - * The computations here are from section 5 of [Meyer et al 2002]. + * `B = [ a b ; b c ]`. + * The computations here are from section 5 of [Meyer et al 2002]. * - * The first step is to calculate the linear equations governing the values of (a,b,c). These + * The first step is to calculate the linear equations governing the values of (a,b,c). These * can be computed by setting the derivatives of the error E to zero (section 5.3). * - * Since a + c = norm(Kh), we only compute the linear equations for dE/da and dE/db. (NB: - * [Meyer et al 2002] has the equation a + b = norm(Kh), but I'm almost positive this is - * incorrect). + * Since a + c = norm(Kh), we only compute the linear equations for `dE/da` and `dE/db`. + * (NOTE: [Meyer et al 2002] has the equation `a + b = norm(Kh)`, + * but I'm almost positive this is incorrect). * - * Note that the w_ij (defined in section 5.2) are all scaled by (1/8*A_mixed). We drop this + * Note that the w_ij (defined in section 5.2) are all scaled by `(1/8*A_mixed)`. We drop this * uniform scale factor because the solution of the linear equations doesn't rely on it. * - * The terms of the linear equations are xterm_dy with x in {a,b,c} and y in {a,b}. There are + * The terms of the linear equations are xterm_dy with x in {a,b,c} and y in {a,b}. There are * also const_dy terms that are the constant factors in the equations. */ diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh index 421e5f0018d..bc3f398c8e9 100644 --- a/source/blender/functions/FN_cpp_type.hh +++ b/source/blender/functions/FN_cpp_type.hh @@ -163,7 +163,7 @@ class CPPType : NonCopyable, NonMovable { * Required memory in bytes for an instance of this type. * * C++ equivalent: - * sizeof(T); + * `sizeof(T);` */ int64_t size() const { diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c index a156fca5b7b..e750c22f0e8 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c @@ -9,7 +9,7 @@ * 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, + * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @@ -346,7 +346,7 @@ static void gpencil_modifier_panel_header(const bContext *UNUSED(C), Panel *pane uiItemMenuF(row, "", ICON_DOWNARROW_HLT, gpencil_modifier_ops_extra_draw, md); /* Remove button. */ - sub = uiLayoutRow(row, true); + sub = uiLayoutRow(row, false); uiLayoutSetEmboss(sub, UI_EMBOSS_NONE); uiItemO(sub, "", ICON_X, "OBJECT_OT_gpencil_modifier_remove"); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index fcc44aab583..ac458041ca3 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -320,13 +320,8 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) } uiLayout *row = uiLayoutRow(layout, true); uiLayoutSetRedAlert(row, !material_valid); - uiItemPointerR(row, - ptr, - "target_material", - &obj_data_ptr, - "materials", - NULL, - material_valid ? ICON_SHADING_TEXTURE : ICON_ERROR); + uiItemPointerR( + row, ptr, "target_material", &obj_data_ptr, "materials", NULL, ICON_SHADING_TEXTURE); gpencil_modifier_panel_end(layout, ptr); } @@ -385,6 +380,8 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) return; } + uiItemR(layout, ptr, "overscan", 0, NULL, ICON_NONE); + uiLayout *col = uiLayoutColumn(layout, true); uiItemR(col, ptr, "use_remove_doubles", 0, NULL, ICON_NONE); @@ -432,6 +429,17 @@ static void occlusion_panel_draw(const bContext *UNUSED(C), Panel *panel) } } +static bool anything_showing_through(PointerRNA *ptr) +{ + const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels"); + const int level_start = RNA_int_get(ptr, "level_start"); + const int level_end = RNA_int_get(ptr, "level_end"); + if (use_multiple_levels) { + return (MAX2(level_start, level_end) > 0); + } + return (level_start > 0); +} + static void material_mask_panel_draw_header(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; @@ -439,6 +447,7 @@ static void material_mask_panel_draw_header(const bContext *UNUSED(C), Panel *pa const bool is_baked = RNA_boolean_get(ptr, "is_baked"); uiLayoutSetEnabled(layout, !is_baked); + uiLayoutSetActive(layout, anything_showing_through(ptr)); uiItemR(layout, ptr, "use_material_mask", 0, IFACE_("Material Mask"), ICON_NONE); } @@ -450,24 +459,24 @@ static void material_mask_panel_draw(const bContext *UNUSED(C), Panel *panel) const bool is_baked = RNA_boolean_get(ptr, "is_baked"); uiLayoutSetEnabled(layout, !is_baked); + uiLayoutSetActive(layout, anything_showing_through(ptr)); uiLayoutSetPropSep(layout, true); uiLayoutSetEnabled(layout, RNA_boolean_get(ptr, "use_material_mask")); - uiLayout *row = uiLayoutRow(layout, true); - uiLayoutSetPropDecorate(row, false); - uiLayout *sub = uiLayoutRowWithHeading(row, true, IFACE_("Masks")); - char text[2] = "0"; + uiLayout *col = uiLayoutColumn(layout, true); + uiLayout *sub = uiLayoutRowWithHeading(col, true, IFACE_("Masks")); PropertyRNA *prop = RNA_struct_find_property(ptr, "use_material_mask_bits"); - for (int i = 0; i < 8; i++, text[0]++) { - uiItemFullR(sub, ptr, prop, i, 0, UI_ITEM_R_TOGGLE, text, ICON_NONE); + for (int i = 0; i < 8; i++) { + uiItemFullR(sub, ptr, prop, i, 0, UI_ITEM_R_TOGGLE, " ", ICON_NONE); + if (i == 3) { + sub = uiLayoutRow(col, true); + } } - uiItemL(row, "", ICON_BLANK1); /* Space for decorator. */ - uiLayout *col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_material_mask_match", 0, IFACE_("Match All Masks"), ICON_NONE); + uiItemR(layout, ptr, "use_material_mask_match", 0, IFACE_("Exact Match"), ICON_NONE); } static void intersection_panel_draw(const bContext *UNUSED(C), Panel *panel) @@ -482,19 +491,18 @@ static void intersection_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetActive(layout, RNA_boolean_get(ptr, "use_intersection")); - uiLayout *row = uiLayoutRow(layout, true); - uiLayoutSetPropDecorate(row, false); - uiLayout *sub = uiLayoutRowWithHeading(row, true, IFACE_("Masks")); - char text[2] = "0"; + uiLayout *col = uiLayoutColumn(layout, true); + uiLayout *sub = uiLayoutRowWithHeading(col, true, IFACE_("Collection Masks")); PropertyRNA *prop = RNA_struct_find_property(ptr, "use_intersection_mask"); - for (int i = 0; i < 8; i++, text[0]++) { - uiItemFullR(sub, ptr, prop, i, 0, UI_ITEM_R_TOGGLE, text, ICON_NONE); + for (int i = 0; i < 8; i++) { + uiItemFullR(sub, ptr, prop, i, 0, UI_ITEM_R_TOGGLE, " ", ICON_NONE); + if (i == 3) { + sub = uiLayoutRow(col, true); + } } - uiItemL(row, "", ICON_BLANK1); /* Space for decorator. */ - uiLayout *col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_intersection_match", 0, IFACE_("Match All Masks"), ICON_NONE); + uiItemR(layout, ptr, "use_intersection_match", 0, IFACE_("Exact Match"), ICON_NONE); } static void face_mark_panel_draw_header(const bContext *UNUSED(C), Panel *panel) { @@ -566,7 +574,7 @@ static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(col, ptr, "use_fuzzy_all", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_loose_edge_chain", 0, IFACE_("Loose Edges"), ICON_NONE); uiItemR(col, ptr, "use_loose_as_contour", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "use_geometry_space_chain", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_geometry_space_chain", 0, IFACE_("Geometry Space"), ICON_NONE); uiItemR(layout, ptr, @@ -613,7 +621,7 @@ static void vgroup_panel_draw(const bContext *UNUSED(C), Panel *panel) } } -static void baking_panel_draw(const bContext *UNUSED(C), Panel *panel) +static void bake_panel_draw(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; PointerRNA ob_ptr; @@ -669,7 +677,7 @@ static void panelRegister(ARegionType *region_type) gpencil_modifier_subpanel_register( region_type, "vgroup", "Vertex Weight Transfer", NULL, vgroup_panel_draw, panel_type); gpencil_modifier_subpanel_register( - region_type, "baking", "Baking", NULL, baking_panel_draw, panel_type); + region_type, "bake", "Bake", NULL, bake_panel_draw, panel_type); } GpencilModifierTypeInfo modifierType_Gpencil_Lineart = { diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 1d4370ed3a9..8eed2e705e8 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -228,6 +228,8 @@ typedef struct LineartRenderBuffer { double view_projection[4][4]; double view[4][4]; + float overscan; + struct LineartBoundingArea *initial_bounding_areas; unsigned int bounding_area_count; @@ -473,7 +475,8 @@ typedef struct LineartBoundingArea { BLI_INLINE int lineart_LineIntersectTest2d( const double *a1, const double *a2, const double *b1, const double *b2, double *aRatio) { -#define USE_VECTOR_LINE_INTERSECTION +/* Legacy intersection math aligns better with occlusion function quirks. */ +/* #define USE_VECTOR_LINE_INTERSECTION */ #ifdef USE_VECTOR_LINE_INTERSECTION /* from isect_line_line_v2_point() */ diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 30ac08c3f9d..97bfb2d197a 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -1804,10 +1804,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu tri->material_mask_bits |= ((mat && (mat->lineart.flags & LRT_MATERIAL_MASK_ENABLED)) ? mat->lineart.material_mask_bits : 0); - tri->mat_occlusion |= ((mat && - (mat->lineart.flags & LRT_MATERIAL_CUSTOM_OCCLUSION_EFFECTIVENESS)) ? - mat->lineart.mat_occlusion : - 1); + tri->mat_occlusion |= (mat ? mat->lineart.mat_occlusion : 1); tri->intersection_mask = obi->override_intersection_mask; @@ -2017,7 +2014,10 @@ static void lineart_geometry_load_assign_thread(LineartObjectLoadTaskInfo *olti_ use_olti->pending = obi; } -static bool lineart_geometry_check_visible(double (*model_view_proj)[4], Object *use_ob) +static bool lineart_geometry_check_visible(double (*model_view_proj)[4], + double shift_x, + double shift_y, + Object *use_ob) { BoundBox *bb = BKE_object_boundbox_get(use_ob); if (!bb) { @@ -2031,6 +2031,8 @@ static bool lineart_geometry_check_visible(double (*model_view_proj)[4], Object copy_v3db_v3fl(co[i], bb->vec[i]); copy_v3_v3_db(tmp, co[i]); mul_v4_m4v3_db(co[i], model_view_proj, tmp); + co[i][0] -= shift_x * 2 * co[i][3]; + co[i][1] -= shift_y * 2 * co[i][3]; } bool cond[6] = {true, true, true, true, true, true}; @@ -2066,11 +2068,6 @@ static void lineart_main_load_geometries( int fit = BKE_camera_sensor_fit(cam->sensor_fit, rb->w, rb->h); double asp = ((double)rb->w / (double)rb->h); - double t_start; - - if (G.debug_value == 4000) { - t_start = PIL_check_seconds_timer(); - } int bound_box_discard_count = 0; if (cam->type == CAM_PERSP) { @@ -2080,14 +2077,20 @@ static void lineart_main_load_geometries( if (fit == CAMERA_SENSOR_FIT_HOR && asp < 1) { sensor /= asp; } - double fov = focallength_to_fov(cam->lens, sensor); + const double fov = focallength_to_fov(cam->lens / (1 + rb->overscan), sensor); lineart_matrix_perspective_44d(proj, fov, asp, cam->clip_start, cam->clip_end); } else if (cam->type == CAM_ORTHO) { - double w = cam->ortho_scale / 2; + const double w = cam->ortho_scale / 2; lineart_matrix_ortho_44d(proj, -w, w, -w / asp, w / asp, cam->clip_start, cam->clip_end); } + double t_start; + + if (G.debug_value == 4000) { + t_start = PIL_check_seconds_timer(); + } + invert_m4_m4(inv, rb->cam_obmat); mul_m4db_m4db_m4fl_uniq(result, proj, inv); copy_m4_m4_db(proj, result); @@ -2126,7 +2129,7 @@ static void lineart_main_load_geometries( Object *use_ob = DEG_get_evaluated_object(depsgraph, ob); /* Prepare the matrix used for transforming this specific object (instance). This has to be - * done before mesh boundbox check because the function needs that. */ + * done before mesh boundbox check because the function needs that. */ mul_m4db_m4db_m4fl_uniq(obi->model_view_proj, rb->view_projection, ob->obmat); mul_m4db_m4db_m4fl_uniq(obi->model_view, rb->view, ob->obmat); @@ -2134,7 +2137,7 @@ static void lineart_main_load_geometries( continue; } - if (!lineart_geometry_check_visible(obi->model_view_proj, use_ob)) { + if (!lineart_geometry_check_visible(obi->model_view_proj, rb->shift_x, rb->shift_y, use_ob)) { if (G.debug_value == 4000) { bound_box_discard_count++; } @@ -2157,7 +2160,7 @@ static void lineart_main_load_geometries( obi->free_use_mesh = true; } - /* Make normal matrix. */ + /* Make normal matrix. */ float imat[4][4]; invert_m4_m4(imat, ob->obmat); transpose_m4(imat); @@ -2479,8 +2482,8 @@ static bool lineart_triangle_edge_image_space_occlusion(SpinLock *UNUSED(spl), } } else if (st_r == 0) { - INTERSECT_JUST_GREATER(is, order, 0, LCross); - if (LRT_ABC(LCross) && is[LCross] > 0) { + INTERSECT_JUST_GREATER(is, order, DBL_TRIANGLE_LIM, LCross); + if (LRT_ABC(LCross) && is[LCross] > DBL_TRIANGLE_LIM) { INTERSECT_JUST_GREATER(is, order, is[LCross], RCross); } else { @@ -3027,6 +3030,11 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, rb->shift_x = fit == CAMERA_SENSOR_FIT_HOR ? c->shiftx : c->shiftx / asp; rb->shift_y = fit == CAMERA_SENSOR_FIT_VERT ? c->shifty : c->shifty * asp; + rb->overscan = lmd->overscan; + + rb->shift_x /= (1 + rb->overscan); + rb->shift_y /= (1 + rb->overscan); + rb->crease_threshold = cos(M_PI - lmd->crease_threshold); rb->angle_splitting_threshold = lmd->angle_splitting_threshold; rb->chaining_image_threshold = lmd->chaining_image_threshold; diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h index 84b43d8060b..58ad02fc566 100644 --- a/source/blender/gpu/GPU_vertex_format.h +++ b/source/blender/gpu/GPU_vertex_format.h @@ -129,7 +129,7 @@ BLI_INLINE const char *GPU_vertformat_attr_name_get(const GPUVertFormat *format, } /* WARNING: Can only rename using a string with same character count. - * WARNING: This removes all other aliases of this attrib */ + * WARNING: This removes all other aliases of this attribute. */ void GPU_vertformat_attr_rename(GPUVertFormat *format, int attr, const char *new_name); void GPU_vertformat_safe_attr_name(const char *attr_name, char *r_safe_name, uint max_len); diff --git a/source/blender/gpu/intern/gpu_batch.cc b/source/blender/gpu/intern/gpu_batch.cc index 9dc24c59e22..0e5c7900da9 100644 --- a/source/blender/gpu/intern/gpu_batch.cc +++ b/source/blender/gpu/intern/gpu_batch.cc @@ -184,7 +184,7 @@ int GPU_batch_instbuf_add_ex(GPUBatch *batch, GPUVertBuf *insts, bool own_vbo) } } /* we only make it this far if there is no room for another GPUVertBuf */ - BLI_assert(0 && "Not enough Instance VBO slot in batch"); + BLI_assert_msg(0, "Not enough Instance VBO slot in batch"); return -1; } @@ -207,7 +207,7 @@ int GPU_batch_vertbuf_add_ex(GPUBatch *batch, GPUVertBuf *verts, bool own_vbo) } } /* we only make it this far if there is no room for another GPUVertBuf */ - BLI_assert(0 && "Not enough VBO slot in batch"); + BLI_assert_msg(0, "Not enough VBO slot in batch"); return -1; } @@ -268,7 +268,7 @@ void GPU_batch_draw_advanced( } if (i_count == 0) { i_count = (batch->inst[0]) ? batch->inst_(0)->vertex_len : 1; - /* Meh. This is to be able to use different numbers of verts in instance vbos. */ + /* Meh. This is to be able to use different numbers of verts in instance VBO's. */ if (batch->inst[1] != nullptr) { i_count = min_ii(i_count, batch->inst_(1)->vertex_len); } diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index fca1cf3e4da..b265b6590ee 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -1749,19 +1749,19 @@ static void GPU_pbvh_bmesh_buffers_update_indexed(GPU_PBVH_Buffers *buffers, /* Creates a vertex buffer (coordinate, normal, color) and, if smooth * shading, an element index buffer. * Threaded - do not call any functions that use OpenGL calls! */ -ATTR_NO_OPT void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers, - BMesh *bm, - TableGSet *bm_faces, - TableGSet *bm_unique_verts, - TableGSet *bm_other_verts, - PBVHTriBuf *tribuf, - const int update_flags, - const int cd_vert_node_offset, - int face_sets_color_seed, - int face_sets_color_default, - bool flat_vcol, - bool active_vcol_only, - short mat_nr) +void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers, + BMesh *bm, + TableGSet *bm_faces, + TableGSet *bm_unique_verts, + TableGSet *bm_other_verts, + PBVHTriBuf *tribuf, + const int update_flags, + const int cd_vert_node_offset, + int face_sets_color_seed, + int face_sets_color_default, + bool flat_vcol, + bool active_vcol_only, + short mat_nr) { if (flat_vcol && CustomData_has_layer(&bm->vdata, CD_PROP_COLOR)) { diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index d12cecd129e..bb1ebc0e85d 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -657,7 +657,7 @@ static const char *attr_prefix_get(CustomDataType type) case CD_AUTO_FROM_NAME: return "a"; default: - BLI_assert(false && "GPUVertAttr Prefix type not found : This should not happen!"); + BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!"); return ""; } } diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc index 4bb13d01c2d..a6f7d43e563 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.cc +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -495,7 +495,7 @@ void GPU_framebuffer_py_reference_set(GPUFrameBuffer *gpu_fb, void **py_ref) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Frame-Buffer Stack +/** \name Frame-Buffer Stack * * Keeps track of frame-buffer binding operation to restore previously bound frame-buffers. * \{ */ diff --git a/source/blender/gpu/intern/gpu_select_sample_query.cc b/source/blender/gpu/intern/gpu_select_sample_query.cc index fc755568687..7b9b3020639 100644 --- a/source/blender/gpu/intern/gpu_select_sample_query.cc +++ b/source/blender/gpu/intern/gpu_select_sample_query.cc @@ -56,7 +56,7 @@ struct GPUSelectQueryState { Vector<uint> *ids; /* Cache on initialization. */ uint (*buffer)[4]; - /* Buffer size (stores number of integers, for actual size multiply by sizeof integer). */ + /* Buffer size (stores number of integers, for actual size multiply by `sizeof(int)`). */ uint bufsize; /* Mode of operation. */ char mode; diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh index 2b8a5a5cc12..26be6f57312 100644 --- a/source/blender/gpu/intern/gpu_texture_private.hh +++ b/source/blender/gpu/intern/gpu_texture_private.hh @@ -316,7 +316,7 @@ inline size_t to_bytesize(eGPUTextureFormat format) case GPU_RGBA8_DXT5: return 1; /* Incorrect but actual size is fractional. */ default: - BLI_assert(!"Texture format incorrect or unsupported\n"); + BLI_assert_msg(0, "Texture format incorrect or unsupported"); return 0; } } @@ -333,7 +333,7 @@ inline size_t to_block_size(eGPUTextureFormat data_type) case GPU_RGBA8_DXT5: return 16; default: - BLI_assert(!"Texture format is not a compressed format\n"); + BLI_assert_msg(0, "Texture format is not a compressed format"); return 0; } } @@ -407,7 +407,7 @@ inline size_t to_bytesize(eGPUDataFormat data_format) case GPU_DATA_2_10_10_10_REV: return 4; default: - BLI_assert(!"Data format incorrect or unsupported\n"); + BLI_assert_msg(0, "Data format incorrect or unsupported"); return 0; } } @@ -503,7 +503,7 @@ inline eGPUFrameBufferBits to_framebuffer_bits(eGPUTextureFormat tex_format) static inline eGPUTextureFormat to_texture_format(const GPUVertFormat *format) { if (format->attr_len > 1 || format->attr_len == 0) { - BLI_assert(!"Incorrect vertex format for buffer texture"); + BLI_assert_msg(0, "Incorrect vertex format for buffer texture"); return GPU_DEPTH_COMPONENT24; } switch (format->attrs[0].comp_len) { @@ -584,7 +584,7 @@ static inline eGPUTextureFormat to_texture_format(const GPUVertFormat *format) default: break; } - BLI_assert(!"Unsupported vertex format for buffer texture"); + BLI_assert_msg(0, "Unsupported vertex format for buffer texture"); return GPU_DEPTH_COMPONENT24; } diff --git a/source/blender/gpu/opengl/gl_context.cc b/source/blender/gpu/opengl/gl_context.cc index 23654cb96f3..09bc0aa6e58 100644 --- a/source/blender/gpu/opengl/gl_context.cc +++ b/source/blender/gpu/opengl/gl_context.cc @@ -93,7 +93,7 @@ GLContext::GLContext(void *ghost_window, GLSharedOrphanLists &shared_orphan_list } } else { - /* For offscreen contexts. Default framebuffer is NULL. */ + /* For off-screen contexts. Default frame-buffer is NULL. */ back_left = new GLFrameBuffer("back_left", this, GL_NONE, 0, 0, 0); } diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc index 9cf072b2e8a..9900a4e0766 100644 --- a/source/blender/gpu/opengl/gl_shader_interface.cc +++ b/source/blender/gpu/opengl/gl_shader_interface.cc @@ -170,7 +170,7 @@ GLShaderInterface::GLShaderInterface(GLuint program) program, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &max_ssbo_name_len); } - BLI_assert(ubo_len <= 16 && "enabled_ubo_mask_ is uint16_t"); + BLI_assert_msg(ubo_len <= 16, "enabled_ubo_mask_ is uint16_t"); /* Work around driver bug with Intel HD 4600 on Windows 7/8, where * GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH does not work. */ diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh index dc048ea05c0..2a480e71017 100644 --- a/source/blender/gpu/opengl/gl_texture.hh +++ b/source/blender/gpu/opengl/gl_texture.hh @@ -192,7 +192,7 @@ inline GLenum to_gl_internal_format(eGPUTextureFormat format) case GPU_DEPTH_COMPONENT16: return GL_DEPTH_COMPONENT16; default: - BLI_assert(!"Texture format incorrect or unsupported\n"); + BLI_assert_msg(0, "Texture format incorrect or unsupported"); return 0; } } @@ -287,7 +287,7 @@ inline GLenum to_gl(eGPUDataFormat format) case GPU_DATA_10_11_11_REV: return GL_UNSIGNED_INT_10F_11F_11F_REV; default: - BLI_assert(!"Unhandled data format"); + BLI_assert_msg(0, "Unhandled data format"); return GL_FLOAT; } } @@ -359,7 +359,7 @@ inline GLenum to_gl_data_format(eGPUTextureFormat format) case GPU_RGBA8_DXT5: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; default: - BLI_assert(!"Texture format incorrect or unsupported\n"); + BLI_assert_msg(0, "Texture format incorrect or unsupported\n"); return 0; } } @@ -377,7 +377,7 @@ inline GLenum channel_len_to_gl(int channel_len) case 4: return GL_RGBA; default: - BLI_assert(!"Wrong number of texture channels"); + BLI_assert_msg(0, "Wrong number of texture channels"); return GL_RED; } } diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc index ea2770f099d..e324916b934 100644 --- a/source/blender/gpu/opengl/gl_vertex_array.cc +++ b/source/blender/gpu/opengl/gl_vertex_array.cc @@ -108,7 +108,7 @@ static uint16_t vbo_bind(const ShaderInterface *interface, return enabled_attrib; } -/* Update the Attrib Binding of the currently bound VAO. */ +/* Update the Attribute Binding of the currently bound VAO. */ void GLVertArray::update_bindings(const GLuint vao, const GPUBatch *batch_, /* Should be GLBatch. */ const ShaderInterface *interface, diff --git a/source/blender/imbuf/intern/iris.c b/source/blender/imbuf/intern/iris.c index e62473fb8c7..59a17fcc600 100644 --- a/source/blender/imbuf/intern/iris.c +++ b/source/blender/imbuf/intern/iris.c @@ -39,7 +39,7 @@ #define IMAGIC 0732 typedef struct { - ushort imagic; /* stuff saved on disk . . */ + ushort imagic; /* Stuff saved on disk. */ ushort type; ushort dim; ushort xsize; diff --git a/source/blender/imbuf/intern/oiio/openimageio_api.cpp b/source/blender/imbuf/intern/oiio/openimageio_api.cpp index 0941338160d..3ad902a241d 100644 --- a/source/blender/imbuf/intern/oiio/openimageio_api.cpp +++ b/source/blender/imbuf/intern/oiio/openimageio_api.cpp @@ -28,7 +28,7 @@ # define _USE_MATH_DEFINES #endif -// NOTE: Keep first, BLI_path_util conflicts with OIIO's format. +/* NOTE: Keep first, #BLI_path_util conflicts with OIIO's format. */ #include "openimageio_api.h" #include <OpenImageIO/imageio.h> #include <memory> diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index ab578e25b94..d1fa26e1a3e 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -1705,7 +1705,7 @@ static void exr_print_filecontents(MultiPartInputFile &file) } } -/* for non-multilayer, map R G B A channel names to something that's in this file */ +/* For non-multi-layer, map R G B A channel names to something that's in this file. */ static const char *exr_rgba_channelname(MultiPartInputFile &file, const char *chan) { const ChannelList &channels = file.header(0).channels(); diff --git a/source/blender/imbuf/intern/scaling.c b/source/blender/imbuf/intern/scaling.c index 757ec5f4b41..1c4b7af6ef1 100644 --- a/source/blender/imbuf/intern/scaling.c +++ b/source/blender/imbuf/intern/scaling.c @@ -867,7 +867,7 @@ static void q_scale_float( * * only handles common cases when we either * - * scale both, x and y or + * scale both, x and y or * shrink both, x and y * * but that is pretty fast: @@ -1666,6 +1666,8 @@ static void scalefast_Z_ImBuf(ImBuf *ibuf, int newx, int newy) */ bool IMB_scaleImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy) { + BLI_assert_msg(newx > 0 && newy > 0, "Images must be at least 1 on both dimensions!"); + if (ibuf == NULL) { return false; } @@ -1712,6 +1714,8 @@ struct imbufRGBA { */ bool IMB_scalefastImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy) { + BLI_assert_msg(newx > 0 && newy > 0, "Images must be at least 1 on both dimensions!"); + unsigned int *rect, *_newrect, *newrect; struct imbufRGBA *rectf, *_newrectf, *newrectf; int x, y; @@ -1884,6 +1888,8 @@ static void *do_scale_thread(void *data_v) void IMB_scaleImBuf_threaded(ImBuf *ibuf, unsigned int newx, unsigned int newy) { + BLI_assert_msg(newx > 0 && newy > 0, "Images must be at least 1 on both dimensions!"); + ScaleTreadInitData init_data = {NULL}; /* prepare initialization data */ diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc index 5146277ff33..fcd89b6731a 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -538,7 +538,7 @@ static void get_loop_normals(struct Mesh *mesh, BKE_mesh_calc_normals_split(mesh); const float(*lnors)[3] = static_cast<float(*)[3]>(CustomData_get_layer(&mesh->ldata, CD_NORMAL)); - BLI_assert(lnors != nullptr || !"BKE_mesh_calc_normals_split() should have computed CD_NORMAL"); + BLI_assert_msg(lnors != nullptr, "BKE_mesh_calc_normals_split() should have computed CD_NORMAL"); normals.resize(mesh->totloop); diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp index 626e4258239..e52bdca0d87 100644 --- a/source/blender/io/collada/AnimationImporter.cpp +++ b/source/blender/io/collada/AnimationImporter.cpp @@ -1090,7 +1090,7 @@ void AnimationImporter::translate_Animations( apply_matrix_curves(ob, animcurves, root, node, transform); } else { - /* calculate rnapaths and array index of fcurves according to transformation and + /* Calculate RNA-paths and array index of F-curves according to transformation and * animation class */ Assign_transform_animations(transform, &bindings[j], &animcurves, is_joint, joint_path); diff --git a/source/blender/io/collada/BCAnimationSampler.cpp b/source/blender/io/collada/BCAnimationSampler.cpp index 02a4e1f3167..d2bde193c0a 100644 --- a/source/blender/io/collada/BCAnimationSampler.cpp +++ b/source/blender/io/collada/BCAnimationSampler.cpp @@ -161,10 +161,12 @@ void BCAnimationSampler::update_animation_curves(BCAnimation &animation, BCSample &BCAnimationSampler::sample_object(Object *ob, int frame_index, bool for_opensim) { BCSample &ob_sample = sample_data.add(ob, frame_index); - // if (export_settings.get_apply_global_orientation()) { - // const BCMatrix &global_transform = export_settings.get_global_transform(); - // ob_sample.get_matrix(global_transform); - //} +#if 0 + if (export_settings.get_apply_global_orientation()) { + const BCMatrix &global_transform = export_settings.get_global_transform(); + ob_sample.get_matrix(global_transform); + } +#endif if (ob->type == OB_ARMATURE) { bPoseChannel *pchan; diff --git a/source/blender/io/collada/ControllerExporter.cpp b/source/blender/io/collada/ControllerExporter.cpp index e61ed47adee..d768d2e44e8 100644 --- a/source/blender/io/collada/ControllerExporter.cpp +++ b/source/blender/io/collada/ControllerExporter.cpp @@ -231,7 +231,7 @@ void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm) uint idx = vert->dw[j].def_nr; if (idx >= joint_index_by_def_index.size()) { /* XXX: Maybe better find out where and - * why the Out Of Bound indexes get created ? */ + * why the Out Of Bound indexes get created? */ oob_counter += 1; } else { diff --git a/source/blender/io/collada/DocumentExporter.cpp b/source/blender/io/collada/DocumentExporter.cpp index 46dfdda4ede..3c64591366c 100644 --- a/source/blender/io/collada/DocumentExporter.cpp +++ b/source/blender/io/collada/DocumentExporter.cpp @@ -180,7 +180,7 @@ int DocumentExporter::exportCurrentScene() bContext *C = blender_context.get_context(); PointerRNA sceneptr, unit_settings; - PropertyRNA *system; /* unused , *scale; */ + PropertyRNA *system; /* unused, *scale; */ clear_global_id_map(); diff --git a/source/blender/io/collada/MeshImporter.cpp b/source/blender/io/collada/MeshImporter.cpp index 5aa57159328..ef486375ce3 100644 --- a/source/blender/io/collada/MeshImporter.cpp +++ b/source/blender/io/collada/MeshImporter.cpp @@ -708,7 +708,7 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me) prim.totpoly++; } - /* Moving cursor to the next triangle fan. */ + /* Moving cursor to the next triangle fan. */ if (mp_has_normals) { normal_indices += 2; } diff --git a/source/blender/io/collada/TransformReader.cpp b/source/blender/io/collada/TransformReader.cpp index 81b0ffc340a..d23f8da2570 100644 --- a/source/blender/io/collada/TransformReader.cpp +++ b/source/blender/io/collada/TransformReader.cpp @@ -103,9 +103,11 @@ void TransformReader::dae_rotate_to_mat4(COLLADAFW::Transformation *tm, float m[ COLLADABU::Math::Vector3 &axis = ro->getRotationAxis(); const float angle = (float)DEG2RAD(ro->getRotationAngle()); const float ax[] = {(float)axis[0], (float)axis[1], (float)axis[2]}; - // float quat[4]; - // axis_angle_to_quat(quat, axis, angle); - // quat_to_mat4(m, quat); +#if 0 + float quat[4]; + axis_angle_to_quat(quat, axis, angle); + quat_to_mat4(m, quat); +#endif axis_angle_to_mat4(m, ax, angle); } diff --git a/source/blender/io/collada/collada_utils.cpp b/source/blender/io/collada/collada_utils.cpp index 1b0ddcc6161..f250fc8557e 100644 --- a/source/blender/io/collada/collada_utils.cpp +++ b/source/blender/io/collada/collada_utils.cpp @@ -1216,8 +1216,10 @@ static bNodeSocket *bc_group_add_input_socket(bNodeTree *ntree, { bNodeSocket *to_socket = (bNodeSocket *)BLI_findlink(&to_node->inputs, to_index); - //bNodeSocket *socket = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket); - //return socket; +# if 0 + bNodeSocket *socket = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket); + return socket; +# endif bNodeSocket *gsock = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket); bNode *inputGroup = ntreeFindType(ntree, NODE_GROUP_INPUT); @@ -1235,8 +1237,10 @@ static bNodeSocket *bc_group_add_output_socket(bNodeTree *ntree, { bNodeSocket *from_socket = (bNodeSocket *)BLI_findlink(&from_node->outputs, from_index); - //bNodeSocket *socket = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket); - //return socket; +# if 0 + bNodeSocket *socket = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket); + return socket; +# endif bNodeSocket *gsock = ntreeAddSocketInterfaceFromSocket(ntree, from_node, from_socket); bNode *outputGroup = ntreeFindType(ntree, NODE_GROUP_OUTPUT); diff --git a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc index 3b20ac9f110..82f8444d43e 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc @@ -91,19 +91,20 @@ bool GpencilExporterPDF::write() { /* Support unicode character paths on Windows. */ HPDF_STATUS res = 0; - /* TODO: It looks libharu does not support unicode. */ - //#ifdef WIN32 - // char filename_cstr[FILE_MAX]; - // BLI_strncpy(filename_cstr, filename_, FILE_MAX); - // - // UTF16_ENCODE(filename_cstr); - // std::wstring wstr(filename_cstr_16); - // res = HPDF_SaveToFile(pdf_, wstr.c_str()); - // - // UTF16_UN_ENCODE(filename_cstr); - //#else + + /* TODO: It looks `libharu` does not support unicode. */ +#if 0 /* `ifdef WIN32` */ + char filename_cstr[FILE_MAX]; + BLI_strncpy(filename_cstr, filename_, FILE_MAX); + + UTF16_ENCODE(filename_cstr); + std::wstring wstr(filename_cstr_16); + res = HPDF_SaveToFile(pdf_, wstr.c_str()); + + UTF16_UN_ENCODE(filename_cstr); +#else res = HPDF_SaveToFile(pdf_, filename_); - //#endif +#endif return (res == 0) ? true : false; } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 43969bf0768..c9d652ad03d 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -621,9 +621,9 @@ typedef enum IDRecalcFlag { * When a collection gets tagged with this flag, all objects depending on the geometry and * transforms on any of the objects in the collection are updated. */ ID_RECALC_GEOMETRY = (1 << 1), - /* Same as #ID_RECALC_GEOMETRY, but instead of tagging the batch cache as `dirty_all`, just tags - what matches the deform cache. */ - ID_RECALC_GEOMETRY_DEFORM = (1 << 2), + + /* ** Animation or time changed and animation is to be re-evaluated. ** */ + ID_RECALC_ANIMATION = (1 << 2), /* ** Particle system changed. ** */ /* Only do pathcache etc. */ @@ -683,9 +683,6 @@ typedef enum IDRecalcFlag { * have to be copied on every update. */ ID_RECALC_PARAMETERS = (1 << 21), - /* ** Animation or time changed and animation is to be re-evaluated. ** */ - ID_RECALC_ANIMATION = (1 << 22), - /* Input has changed and datablock is to be reload from disk. * Applies to movie clips to inform that copy-on-written version is to be refreshed for the new * input file or for color space changes. */ diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index aba6ccfd3ba..baed2aa2866 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -830,7 +830,7 @@ typedef struct SpaceAction { /** The currently active context (when not showing action). */ bDopeSheet ads; - /** For Time-Slide transform mode drawing - current frame?. */ + /** For Time-Slide transform mode drawing - current frame? */ float timeslide; short flag; @@ -838,7 +838,7 @@ typedef struct SpaceAction { char mode; /* Storage for sub-space types. */ char mode_prev; - /** Automatic keyframe snapping mode . */ + /** Automatic keyframe snapping mode. */ char autosnap; /** (eTimeline_Cache_Flag). */ char cache_display; diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h index c02035be4ac..a49a76c3f26 100644 --- a/source/blender/makesdna/DNA_anim_types.h +++ b/source/blender/makesdna/DNA_anim_types.h @@ -51,7 +51,7 @@ typedef struct FModifier { /** Pointer to modifier data. */ void *data; - /** User-defined description for the modifier - MAX_ID_NAME-2. */ + /** User-defined description for the modifier - `MAX_ID_NAME - 2`. */ char name[64]; /** Type of f-curve modifier. */ short type; @@ -185,7 +185,7 @@ typedef enum eFMod_Generator_Functions { /* envelope modifier - envelope data */ typedef struct FCM_EnvelopeData { - /** Min/max values for envelope at this point (absolute values) . */ + /** Min/max values for envelope at this point (absolute values). */ float min, max; /** Time for that this sample-point occurs. */ float time; @@ -198,7 +198,7 @@ typedef struct FCM_EnvelopeData { /* envelope-like adjustment to values (for fade in/out) */ typedef struct FMod_Envelope { - /** Data-points defining envelope to apply (array) . */ + /** Data-points defining envelope to apply (array). */ FCM_EnvelopeData *data; /** Number of envelope points. */ int totvert; @@ -210,7 +210,7 @@ typedef struct FMod_Envelope { } FMod_Envelope; /* cycling/repetition modifier data */ -// TODO: we can only do complete cycles... +/* TODO: we can only do complete cycles. */ typedef struct FMod_Cycles { /** Extrapolation mode to use before first keyframe. */ short before_mode; @@ -321,7 +321,7 @@ typedef struct DriverTarget { /** * Name of the posebone to use - * (for vars where DTAR_FLAG_STRUCT_REF is used) - MAX_ID_NAME-2. + * (for vars where DTAR_FLAG_STRUCT_REF is used) - `MAX_ID_NAME - 2`. */ char pchan_name[64]; /** Transform channel index (for #DVAR_TYPE_TRANSFORM_CHAN). */ @@ -418,7 +418,7 @@ typedef struct DriverVar { /** * Name of the variable to use in py-expression - * (must be valid python identifier) - MAX_ID_NAME-2. + * (must be valid python identifier) - `MAX_ID_NAME - 2`. */ char name[64]; @@ -734,7 +734,7 @@ typedef struct NlaStrip { /** F-Curve modifiers to be applied to the entire strip's referenced F-Curves. */ ListBase modifiers; - /** User-Visible Identifier for Strip - MAX_ID_NAME-2. */ + /** User-Visible Identifier for Strip - `MAX_ID_NAME - 2`. */ char name[64]; /** Influence of strip. */ @@ -873,7 +873,7 @@ typedef struct NlaTrack { * \note not really useful, but we need a '_pad' var anyways! */ int index; - /** Short user-description of this track - MAX_ID_NAME-2. */ + /** Short user-description of this track - `MAX_ID_NAME - 2`. */ char name[64]; } NlaTrack; @@ -917,7 +917,7 @@ typedef struct KS_Path { /** ID block that keyframes are for. */ ID *id; - /** Name of the group to add to - MAX_ID_NAME-2. */ + /** Name of the group to add to - `MAX_ID_NAME - 2`. */ char group[64]; /** ID-type that path can be used on. */ @@ -977,13 +977,13 @@ typedef struct KeyingSet { /** (KS_Path) paths to keyframe to. */ ListBase paths; - /** Unique name (for search, etc.) - MAX_ID_NAME-2 . */ + /** Unique name (for search, etc.) - `MAX_ID_NAME - 2`. */ char idname[64]; - /** User-viewable name for KeyingSet (for menus, etc.) - MAX_ID_NAME-2. */ + /** User-viewable name for KeyingSet (for menus, etc.) - `MAX_ID_NAME - 2`. */ char name[64]; /** (RNA_DYN_DESCR_MAX) short help text. */ char description[240]; - /** Name of the typeinfo data used for the relative paths - MAX_ID_NAME-2. */ + /** Name of the typeinfo data used for the relative paths - `MAX_ID_NAME - 2`. */ char typeinfo[64]; /** Index of the active path. */ diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h index f397b7f27b4..454d843112a 100644 --- a/source/blender/makesdna/DNA_armature_types.h +++ b/source/blender/makesdna/DNA_armature_types.h @@ -76,7 +76,12 @@ typedef struct Bone { /** dist, weight: for non-deformgroup deforms. */ float dist, weight; - /** width: for block bones. keep in this order, transform!. */ + /** + * The width for block bones. + * + * \note keep in this order for transform code which stores a pointer to `xwidth`, + * accessing length and `zwidth` as offsets. + */ float xwidth, length, zwidth; /** * Radius for head/tail sphere, defining deform as well, diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index 3907c158573..316f8631ece 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -89,7 +89,6 @@ typedef enum eAssetLibraryType { ASSET_LIBRARY_CUSTOM = 100, } eAssetLibraryType; -/* TODO copy of FileSelectAssetLibraryUID */ /** * Information to identify a asset library. May be either one of the predefined types (current * 'Main', builtin library, project library), or a custom type as defined in the Preferences. @@ -116,7 +115,7 @@ typedef struct AssetLibraryReference { # # typedef struct AssetHandle { - struct FileDirEntry *file_data; + const struct FileDirEntry *file_data; } AssetHandle; #ifdef __cplusplus diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 34f50b23c77..a77fbc9e45e 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -425,7 +425,7 @@ typedef struct bRigidBodyJointConstraint { typedef struct bClampToConstraint { /** 'target' must be a curve. */ struct Object *tar; - /** Which axis/plane to compare owner's location on . */ + /** Which axis/plane to compare owner's location on. */ int flag; /** For legacy reasons, this is flag2. used for any extra settings. */ int flag2; @@ -474,7 +474,7 @@ typedef struct bTransformConstraint { float from_min[3]; /** To map on to to_min/max range. */ float from_max[3]; - /** Range of motion on owner caused by target . */ + /** Range of motion on owner caused by target. */ float to_min[3]; float to_max[3]; @@ -482,7 +482,7 @@ typedef struct bTransformConstraint { float from_min_rot[3]; /** To map on to to_min/max range. */ float from_max_rot[3]; - /** Range of motion on owner caused by target . */ + /** Range of motion on owner caused by target. */ float to_min_rot[3]; float to_max_rot[3]; @@ -490,7 +490,7 @@ typedef struct bTransformConstraint { float from_min_scale[3]; /** To map on to to_min/max range. */ float from_max_scale[3]; - /** Range of motion on owner caused by target . */ + /** Range of motion on owner caused by target. */ float to_min_scale[3]; float to_max_scale[3]; } bTransformConstraint; @@ -1102,7 +1102,7 @@ typedef enum eRotLimit_Flags { /* distance limit constraint */ /* bDistLimitConstraint->flag */ typedef enum eDistLimit_Flag { - /* "soft" cushion effect when reaching the limit sphere */ // NOT IMPLEMENTED! + /* "soft" cushion effect when reaching the limit sphere */ /* NOT IMPLEMENTED! */ LIMITDIST_USESOFT = (1 << 0), /* as for all Limit constraints - allow to be used during transform? */ LIMITDIST_TRANSFORM = (1 << 1), diff --git a/source/blender/makesdna/DNA_documentation.h b/source/blender/makesdna/DNA_documentation.h index 0251625292c..85190d8a56d 100644 --- a/source/blender/makesdna/DNA_documentation.h +++ b/source/blender/makesdna/DNA_documentation.h @@ -56,7 +56,7 @@ * * Ignored structs can only be referred to from non-ignored structs * when referred to as a pointer (where they're usually allocated - * and cleared in ``readfile.c``). + * and cleared in `readfile.c`). * * - %Path to the header files * diff --git a/source/blender/makesdna/DNA_fluid_defaults.h b/source/blender/makesdna/DNA_fluid_defaults.h index 9454342654c..95f5b8b66b0 100644 --- a/source/blender/makesdna/DNA_fluid_defaults.h +++ b/source/blender/makesdna/DNA_fluid_defaults.h @@ -101,7 +101,6 @@ .noise_time_anim = 0.1f, \ .res_noise = {0, 0, 0}, \ .noise_scale = 2, \ - .noise_type = FLUID_NOISE_TYPE_WAVELET, \ .particle_randomness = 0.1f, \ .particle_number = 2, \ .particle_minimum = 8, \ diff --git a/source/blender/makesdna/DNA_fluid_types.h b/source/blender/makesdna/DNA_fluid_types.h index cec6eb0d044..57523f1c4d8 100644 --- a/source/blender/makesdna/DNA_fluid_types.h +++ b/source/blender/makesdna/DNA_fluid_types.h @@ -198,11 +198,6 @@ enum { FLUID_DOMAIN_TYPE_LIQUID = 1, }; -/* Smoke noise types. */ -enum { - FLUID_NOISE_TYPE_WAVELET = (1 << 0), -}; - /* Mesh levelset generator types. */ enum { FLUID_DOMAIN_MESH_IMPROVED = 0, @@ -580,8 +575,7 @@ typedef struct FluidDomainSettings { float noise_time_anim; int res_noise[3]; int noise_scale; - short noise_type; /* Noise type: wave, curl, anisotropic. */ - char _pad3[2]; /* Unused. */ + char _pad3[4]; /* Unused. */ /* Liquid domain options. */ float particle_randomness; diff --git a/source/blender/makesdna/DNA_freestyle_types.h b/source/blender/makesdna/DNA_freestyle_types.h index 884e11f3a8e..4d4fbaed29a 100644 --- a/source/blender/makesdna/DNA_freestyle_types.h +++ b/source/blender/makesdna/DNA_freestyle_types.h @@ -145,7 +145,7 @@ typedef struct FreestyleConfig { int flags; float sphere_radius; float dkr_epsilon; - /** In radians!. */ + /** In radians. */ float crease_angle; ListBase linesets; diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index b9697beeea9..81e9abc4916 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -307,6 +307,7 @@ .calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES, \ .angle_splitting_threshold = DEG2RAD(60.0f), \ .chaining_image_threshold = 0.001f, \ + .overscan = 0.1f,\ } #define _DNA_DEFAULT_LengthGpencilModifierData \ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index fac5bd3d4f4..c91afa58cb1 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -942,9 +942,11 @@ struct LineartCache; typedef struct LineartGpencilModifierData { GpencilModifierData modifier; - short edge_types; /* line type enable flags, bits in eLineartEdgeFlag */ + /** Line type enable flags, bits in #eLineartEdgeFlag. */ + short edge_types; - char source_type; /* Object or Collection, from eLineartGpencilModifierSource */ + /** Object or Collection, from #eLineartGpencilModifierSource. */ + char source_type; char use_multiple_levels; short level_start; @@ -963,14 +965,22 @@ typedef struct LineartGpencilModifierData { char source_vertex_group[64]; char vgname[64]; + /** + * Camera focal length is divided by `1 + overscan`, before calculation, which give a wider FOV, + * this doesn't change coordinates range internally (-1, 1), but makes the calculated frame + * bigger than actual output. This is for the easier shifting calculation. A value of 0.5 means + * the "internal" focal length become 2/3 of the actual camera. + */ + float overscan; + float opacity; short thickness; - unsigned char mask_switches; /* eLineartGpencilMaskSwitches */ + unsigned char mask_switches; /* #eLineartGpencilMaskSwitches */ unsigned char material_mask_bits; unsigned char intersection_mask; - char _pad[7]; + char _pad[3]; /** `0..1` range for cosine angle */ float crease_threshold; @@ -984,7 +994,7 @@ typedef struct LineartGpencilModifierData { /* Ported from SceneLineArt flags. */ int calculation_flags; - /* eLineArtGPencilModifierFlags, modifier internal state. */ + /* #eLineArtGPencilModifierFlags, modifier internal state. */ int flags; /* Runtime data. */ diff --git a/source/blender/makesdna/DNA_ipo_types.h b/source/blender/makesdna/DNA_ipo_types.h index 0552c449819..e2fdd19232c 100644 --- a/source/blender/makesdna/DNA_ipo_types.h +++ b/source/blender/makesdna/DNA_ipo_types.h @@ -63,9 +63,9 @@ typedef struct IpoDriver { typedef struct IpoCurve { struct IpoCurve *next, *prev; - /** Array of BPoints (sizeof(BPoint) * totvert) - i.e. baked/imported data. */ + /** Array of #BPoints `(sizeof(BPoint) * totvert)` - i.e. baked/imported data. */ struct BPoint *bp; - /** Array of BezTriples (sizeof(BezTriple) * totvert) - i.e. user-editable keyframes . */ + /** Array of #BezTriples `(sizeof(BezTriple) * totvert)` - i.e. user-editable keyframes. */ struct BezTriple *bezt; /** Bounding boxes. */ @@ -75,14 +75,14 @@ typedef struct IpoCurve { short blocktype, adrcode, vartype; /** Total number of BezTriples (i.e. keyframes) on curve. */ short totvert; - /** Interpolation and extrapolation modes . */ + /** Interpolation and extrapolation modes. */ short ipo, extrap; /** Flag= settings. */ short flag; char _pad0[2]; /** Minimum/maximum y-extents for curve. */ float ymin, ymax; - /** ???. */ + /** Unused since the first available revision. */ unsigned int bitmask; /** Minimum/maximum values for sliders (in action editor). */ @@ -102,7 +102,7 @@ typedef struct Ipo { /** A list of IpoCurve structs in a linked list. */ ListBase curve; - /** Rect defining extents of keyframes?. */ + /** Rect defining extents of keyframes? */ rctf cur; /** Blocktype: self-explanatory; showkey: either 0 or 1 diff --git a/source/blender/makesdna/DNA_light_types.h b/source/blender/makesdna/DNA_light_types.h index 82ff3c95834..5ec82a68a2d 100644 --- a/source/blender/makesdna/DNA_light_types.h +++ b/source/blender/makesdna/DNA_light_types.h @@ -134,7 +134,7 @@ typedef struct Light { /* #define LA_NO_DIFF (1 << 11) */ /* not used anywhere */ /* #define LA_NO_SPEC (1 << 12) */ /* not used anywhere */ /* #define LA_SHAD_RAY (1 << 13) */ /* not used anywhere - cleaned */ -/* yafray: light shadowbuffer flag, softlight */ +/* yafray: light shadowbuffer flag, softlight */ /* Since it is used with LOCAL light, can't use LA_SHAD */ /* #define LA_YF_SOFT (1 << 14) */ /* not used anymore */ /* #define LA_LAYER_SHADOW (1 << 15) */ /* not used anymore */ diff --git a/source/blender/makesdna/DNA_linestyle_types.h b/source/blender/makesdna/DNA_linestyle_types.h index b154de4507e..d83e24c5117 100644 --- a/source/blender/makesdna/DNA_linestyle_types.h +++ b/source/blender/makesdna/DNA_linestyle_types.h @@ -382,7 +382,7 @@ typedef struct LineStyleGeometryModifier_PerlinNoise1D { struct LineStyleModifier modifier; float frequency, amplitude; - /** In radians!. */ + /** In radians. */ float angle; unsigned int octaves; int seed; @@ -393,7 +393,7 @@ typedef struct LineStyleGeometryModifier_PerlinNoise2D { struct LineStyleModifier modifier; float frequency, amplitude; - /** In radians!. */ + /** In radians. */ float angle; unsigned int octaves; int seed; @@ -463,7 +463,7 @@ typedef struct LineStyleGeometryModifier_2DTransform { int pivot; float scale_x, scale_y; - /** In radians!. */ + /** In radians. */ float angle; float pivot_u; float pivot_x, pivot_y; @@ -483,7 +483,7 @@ typedef struct LineStyleThicknessModifier_Calligraphy { struct LineStyleModifier modifier; float min_thickness, max_thickness; - /** In radians!. */ + /** In radians. */ float orientation; char _pad[4]; } LineStyleThicknessModifier_Calligraphy; diff --git a/source/blender/makesdna/DNA_mask_types.h b/source/blender/makesdna/DNA_mask_types.h index 8868ac1ea12..e6a7c004078 100644 --- a/source/blender/makesdna/DNA_mask_types.h +++ b/source/blender/makesdna/DNA_mask_types.h @@ -93,7 +93,7 @@ typedef struct MaskSplinePointUW { } MaskSplinePointUW; typedef struct MaskSplinePoint { - /** Actual point coordinates and its handles . */ + /** Actual point coordinates and its handles. */ BezTriple bezt; char _pad[4]; /** Number of uv feather values. */ @@ -173,7 +173,7 @@ typedef struct MaskLayer { /** For animation. */ char flag; - /** Matching 'Object' flag of the same name - eventually use in the outliner . */ + /** Matching 'Object' flag of the same name - eventually use in the outliner. */ char restrictflag; } MaskLayer; diff --git a/source/blender/makesdna/DNA_material_defaults.h b/source/blender/makesdna/DNA_material_defaults.h index 3f4496ce735..3fa87800b2e 100644 --- a/source/blender/makesdna/DNA_material_defaults.h +++ b/source/blender/makesdna/DNA_material_defaults.h @@ -45,6 +45,8 @@ .alpha_threshold = 0.5f, \ \ .blend_shadow = MA_BS_SOLID, \ + \ + .lineart.mat_occlusion = 1, \ } /** \} */ diff --git a/source/blender/makesdna/DNA_material_types.h b/source/blender/makesdna/DNA_material_types.h index b7354aaa724..67cd68afb8a 100644 --- a/source/blender/makesdna/DNA_material_types.h +++ b/source/blender/makesdna/DNA_material_types.h @@ -160,6 +160,8 @@ typedef struct MaterialLineArt { typedef enum eMaterialLineArtFlags { LRT_MATERIAL_MASK_ENABLED = (1 << 0), + + /* Deprecated, kept for versioning code. */ LRT_MATERIAL_CUSTOM_OCCLUSION_EFFECTIVENESS = (1 << 1), } eMaterialLineArtFlags; @@ -295,37 +297,21 @@ typedef struct Material { #define MAP_COL (1 << 0) #define MAP_ALPHA (1 << 7) -/* pmapto */ -/* init */ -#define MAP_PA_INIT ((1 << 5) - 1) -#define MAP_PA_TIME (1 << 0) -#define MAP_PA_LIFE (1 << 1) -#define MAP_PA_DENS (1 << 2) -#define MAP_PA_SIZE (1 << 3) -#define MAP_PA_LENGTH (1 << 4) -/* reset */ -#define MAP_PA_IVEL (1 << 5) -/* physics */ -#define MAP_PA_PVEL (1 << 6) -/* path cache */ -#define MAP_PA_CLUMP (1 << 7) -#define MAP_PA_KINK (1 << 8) -#define MAP_PA_ROUGH (1 << 9) -#define MAP_PA_FREQ (1 << 10) - /* pr_type */ -#define MA_FLAT 0 -#define MA_SPHERE 1 -#define MA_CUBE 2 -#define MA_SHADERBALL 3 -#define MA_SPHERE_A 4 /* Used for icon renders only. */ -#define MA_TEXTURE 5 -#define MA_LAMP 6 -#define MA_SKY 7 -#define MA_HAIR 10 -#define MA_ATMOS 11 -#define MA_CLOTH 12 -#define MA_FLUID 13 +typedef enum ePreviewType { + MA_FLAT = 0, + MA_SPHERE = 1, + MA_CUBE = 2, + MA_SHADERBALL = 3, + MA_SPHERE_A = 4, /* Used for icon renders only. */ + MA_TEXTURE = 5, + MA_LAMP = 6, + MA_SKY = 7, + MA_HAIR = 10, + MA_ATMOS = 11, + MA_CLOTH = 12, + MA_FLUID = 13, +} ePreviewType; /* pr_flag */ #define MA_PREVIEW_WORLD (1 << 0) diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index c54c086affd..b59ea838719 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -175,7 +175,7 @@ typedef struct Mesh { struct Mesh *texcomesh; /* When the object is available, the preferred access method is: BKE_editmesh_from_object(ob) */ - /** Not saved in file!. */ + /** Not saved in file. */ struct BMEditMesh *edit_mesh; struct CustomData vdata, edata, fdata; diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 781e3acbe4a..f1ed3ac4bcc 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -952,7 +952,7 @@ typedef struct MeshDeformModifierData { MDefCell *dyngrid; /** Dynamic binding vertex influences. */ MDefInfluence *dyninfluences; - /** Is this vertex bound or not?. */ + /** Is this vertex bound or not? */ int *dynverts; /** Size of the dynamic bind grid. */ int dyngridsize; @@ -1469,7 +1469,7 @@ typedef struct WeightVGEditModifierData { float default_weight; /* Mapping stuff. */ - /** The custom mapping curve!. */ + /** The custom mapping curve. */ struct CurveMapping *cmap_curve; /* The add/remove vertices weight thresholds. */ @@ -1543,7 +1543,7 @@ typedef struct WeightVGMixModifierData { struct Object *mask_tex_map_obj; /** Name of the map bone. */ char mask_tex_map_bone[64]; - /** How to map the texture!. */ + /** How to map the texture. */ int mask_tex_mapping; /** Name of the UV map. MAX_CUSTOMDATA_LAYER_NAME. */ char mask_tex_uvlayer_name[64]; @@ -1601,7 +1601,7 @@ typedef struct WeightVGProximityModifierData { char defgrp_name[64]; /* Mapping stuff. */ - /** The custom mapping curve!. */ + /** The custom mapping curve. */ struct CurveMapping *cmap_curve; /* Proximity modes. */ @@ -1626,7 +1626,7 @@ typedef struct WeightVGProximityModifierData { struct Object *mask_tex_map_obj; /** Name of the map bone. */ char mask_tex_map_bone[64]; - /** How to map the texture!. */ + /** How to map the texture. */ int mask_tex_mapping; /** Name of the UV Map. MAX_CUSTOMDATA_LAYER_NAME. */ char mask_tex_uvlayer_name[64]; @@ -2021,6 +2021,7 @@ typedef struct WeldModifierData { /* WeldModifierData->flag */ enum { MOD_WELD_INVERT_VGROUP = (1 << 0), + MOD_WELD_LOOSE_EDGES = (1 << 1), }; /* #WeldModifierData.mode */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 94176d946b3..5152098f57a 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1355,6 +1355,13 @@ typedef struct NodeSwitch { uint8_t input_type; } NodeSwitch; +typedef struct NodeGeometryCurveSetHandles { + /* GeometryNodeCurveHandleType. */ + uint8_t handle_type; + /* GeometryNodeCurveHandleMode. */ + uint8_t mode; +} NodeGeometryCurveSetHandles; + typedef struct NodeGeometryCurvePrimitiveLine { /* GeometryNodeCurvePrimitiveLineMode. */ uint8_t mode; @@ -1385,6 +1392,11 @@ typedef struct NodeGeometryCurveSubdivide { uint8_t cuts_type; } NodeGeometryCurveSubdivide; +typedef struct NodeGeometryCurveTrim { + /* GeometryNodeCurveInterpolateMode. */ + uint8_t mode; +} NodeGeometryCurveTrim; + typedef struct NodeGeometryCurveToPoints { /* GeometryNodeCurveSampleMode. */ uint8_t mode; @@ -1821,6 +1833,18 @@ typedef enum GeometryNodeCurvePrimitiveCircleMode { GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_RADIUS = 1 } GeometryNodeCurvePrimitiveCircleMode; +typedef enum GeometryNodeCurveHandleType { + GEO_NODE_CURVE_HANDLE_FREE = 0, + GEO_NODE_CURVE_HANDLE_AUTO = 1, + GEO_NODE_CURVE_HANDLE_VECTOR = 2, + GEO_NODE_CURVE_HANDLE_ALIGN = 3 +} GeometryNodeCurveHandleType; + +typedef enum GeometryNodeCurveHandleMode { + GEO_NODE_CURVE_HANDLE_LEFT = (1 << 0), + GEO_NODE_CURVE_HANDLE_RIGHT = (1 << 1) +} GeometryNodeCurveHandleMode; + typedef enum GeometryNodeTriangulateNGons { GEO_NODE_TRIANGULATE_NGON_BEAUTY = 0, GEO_NODE_TRIANGULATE_NGON_EARCLIP = 1, @@ -1944,6 +1968,11 @@ typedef enum GeometryNodeCurveSampleMode { GEO_NODE_CURVE_SAMPLE_EVALUATED = 2, } GeometryNodeCurveSampleMode; +typedef enum GeometryNodeCurveInterpolateMode { + GEO_NODE_CURVE_INTERPOLATE_FACTOR = 0, + GEO_NODE_CURVE_INTERPOLATE_LENGTH = 1, +} GeometryNodeCurveInterpolateMode; + typedef enum GeometryNodeAttributeTransferMapMode { GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0, GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST = 1, diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index dd31e85647d..60a34fef899 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -336,7 +336,7 @@ typedef struct Object { /** Deprecated, use 'matbits'. */ short colbits DNA_DEPRECATED; - /** Transformation settings and transform locks . */ + /** Transformation settings and transform locks. */ short transflag, protectflag; short trackflag, upflag; /** Used for DopeSheet filtering settings (expanded/collapsed). */ diff --git a/source/blender/makesdna/DNA_rigidbody_types.h b/source/blender/makesdna/DNA_rigidbody_types.h index 1d5e5eeed31..aa11e74e89d 100644 --- a/source/blender/makesdna/DNA_rigidbody_types.h +++ b/source/blender/makesdna/DNA_rigidbody_types.h @@ -125,7 +125,7 @@ typedef struct RigidBodyOb_Shared { */ typedef struct RigidBodyOb { /* General Settings for this RigidBodyOb */ - /** (eRigidBodyOb_Type) role of RigidBody in sim . */ + /** (eRigidBodyOb_Type) role of RigidBody in sim. */ short type; /** (eRigidBody_Shape) collision shape to use. */ short shape; @@ -243,7 +243,7 @@ typedef struct RigidBodyCon { struct Object *ob2; /* General Settings for this RigidBodyCon */ - /** (eRigidBodyCon_Type) role of RigidBody in sim . */ + /** (eRigidBodyCon_Type) role of RigidBody in sim. */ short type; /** Number of constraint solver iterations made per simulation step. */ short num_solver_iterations; diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 1a2a8892e64..61707964191 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -374,8 +374,6 @@ /* GP Stroke Placement */ \ .gpencil_v3d_align = GP_PROJECT_VIEWSPACE, \ .gpencil_v2d_align = GP_PROJECT_VIEWSPACE, \ - .gpencil_seq_align = GP_PROJECT_VIEWSPACE, \ - .gpencil_ima_align = GP_PROJECT_VIEWSPACE, \ } /* clang-format off */ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index c64bff948c5..3c0f9ca349d 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -734,7 +734,7 @@ typedef struct RenderData { /* sequencer options */ char seq_prev_type; - /** UNUSED!. */ + /** UNUSED. */ char seq_rend_type; /** Flag use for sequence render/draw. */ char seq_flag; @@ -1411,10 +1411,7 @@ typedef struct ToolSettings { char gpencil_v3d_align; /** General 2D Editor. */ char gpencil_v2d_align; - /** Sequencer Preview. */ - char gpencil_seq_align; - /** Image Editor. */ - char gpencil_ima_align; + char _pad0[2]; /* Annotations */ /** Stroke placement settings - 3D View. */ @@ -1540,7 +1537,7 @@ typedef struct ToolSettings { typedef struct UnitSettings { /* Display/Editing unit options for each scene */ - /** Maybe have other unit conversions?. */ + /** Maybe have other unit conversions? */ float scale_length; /** Imperial, metric etc. */ char system; @@ -1752,7 +1749,7 @@ typedef struct Scene { /** (runtime) info/cache used for presenting playback framerate info to the user. */ void *fps_info; - /* none of the dependency graph vars is mean to be saved */ + /* None of the dependency graph vars is mean to be saved. */ struct GHash *depsgraph_hash; char _pad7[4]; diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 5bd9cc7a999..d5b7458ae7b 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -522,7 +522,7 @@ typedef struct ARegion { /** Use this string to draw info. */ char *headerstr; - /** XXX 2.50, need spacedata equivalent?. */ + /** XXX 2.50, need spacedata equivalent? */ void *regiondata; ARegion_Runtime runtime; diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 55dc51e0632..af524ff4866 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -201,6 +201,7 @@ typedef struct Sequence { ListBase anims; float effect_fader; + /* DEPRECATED, only used for versioning. */ float speed_fader; /* pointers for effects: */ @@ -335,12 +336,28 @@ typedef struct SolidColorVars { typedef struct SpeedControlVars { float *frameMap; + /* DEPRECATED, only used for versioning. */ float globalSpeed; + /* DEPRECATED, only used for versioning. */ int flags; + int length; int lastValidFrame; + int speed_control_type; + + float speed_fader; + float speed_fader_length; + float speed_fader_frame_number; } SpeedControlVars; +/* SpeedControlVars.speed_control_type */ +enum { + SEQ_SPEED_STRETCH = 0, + SEQ_SPEED_MULTIPLY = 1, + SEQ_SPEED_LENGTH = 2, + SEQ_SPEED_FRAME_NUMBER = 3, +}; + typedef struct GaussianBlurVars { float size_x; float size_y; @@ -485,9 +502,9 @@ typedef struct SequencerScopes { #define SEQ_EDIT_PROXY_DIR_STORAGE 1 /* SpeedControlVars->flags */ -#define SEQ_SPEED_INTEGRATE (1 << 0) +#define SEQ_SPEED_UNUSED_2 (1 << 0) /* cleared */ #define SEQ_SPEED_UNUSED_1 (1 << 1) /* cleared */ -#define SEQ_SPEED_COMPRESS_IPO_Y (1 << 2) +#define SEQ_SPEED_UNUSED_3 (1 << 2) /* cleared */ #define SEQ_SPEED_USE_INTERPOLATION (1 << 3) /* ***************** SEQUENCE ****************** */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index b990de29ff3..1321bd669f1 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -24,6 +24,7 @@ #pragma once +#include "DNA_asset_types.h" #include "DNA_color_types.h" /* for Histogram */ #include "DNA_defs.h" #include "DNA_image_types.h" /* ImageUser */ @@ -653,6 +654,7 @@ typedef enum eSpaceSeq_Flag { SEQ_SHOW_STRIP_SOURCE = (1 << 15), SEQ_SHOW_STRIP_DURATION = (1 << 16), SEQ_USE_PROXIES = (1 << 17), + SEQ_SHOW_GRID = (1 << 18), } eSpaceSeq_Flag; /* SpaceSeq.view */ @@ -696,24 +698,6 @@ typedef enum eSpaceSeq_OverlayType { /** \name File Selector * \{ */ -/** - * Information to identify a asset library. May be either one of the predefined types (current - * 'Main', builtin library, project library), or a custom type as defined in the Preferences. - * - * If the type is set to #ASSET_LIBRARY_CUSTOM, idname must have the name to identify the - * custom library. Otherwise idname is not used. - */ -typedef struct FileSelectAssetLibraryUID { - short type; /* eFileAssetLibrary_Type */ - char _pad[2]; - /** - * If showing a custom asset library (#ASSET_LIBRARY_CUSTOM), this is the index of the - * #bUserAssetLibrary within #UserDef.asset_libraries. - * Should be ignored otherwise (but better set to -1 then, for sanity and debugging). - */ - int custom_library_index; -} FileSelectAssetLibraryUID; - /* Config and Input for File Selector */ typedef struct FileSelectParams { /** Title, also used for the text of the execute button. */ @@ -785,7 +769,7 @@ typedef struct FileSelectParams { typedef struct FileAssetSelectParams { FileSelectParams base_params; - FileSelectAssetLibraryUID asset_library; + AssetLibraryReference asset_library; short import_type; /* eFileAssetImportType */ char _pad[6]; diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h index 2308f04c4c7..ee33e8666ec 100644 --- a/source/blender/makesdna/DNA_texture_types.h +++ b/source/blender/makesdna/DNA_texture_types.h @@ -54,11 +54,10 @@ typedef struct MTex { float ofs[3], size[3], rot, random_angle; char _pad0[2]; - short colormodel, pmapto, pmaptoneg; + short colormodel; short normapspace, which_output; float r, g, b, k; float def_var; - char _pad1[4]; /* common */ float colfac, varfac; diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index d35e0e7afa4..136d7a4856a 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -513,7 +513,7 @@ typedef struct bTheme { typedef struct bAddon { struct bAddon *next, *prev; char module[64]; - /** User-Defined Properties on this Addon (for storing preferences). */ + /** User-Defined Properties on this add-on (for storing preferences). */ IDProperty *prop; } bAddon; @@ -801,7 +801,7 @@ typedef struct UserDef { short rvisize; /** Rotating view icon brightness. */ short rvibright; - /** Maximum number of recently used files to remember . */ + /** Maximum number of recently used files to remember. */ short recent_files; /** Milliseconds to spend spinning the view. */ short smooth_viewtx; @@ -1292,7 +1292,7 @@ typedef enum eTimecodeStyles { USER_TIMECODE_SECONDS_ONLY = 4, /** * Private (not exposed as generic choices) options. - * milliseconds for sub-frames , SubRip format- HH:MM:SS,sss. + * milliseconds for sub-frames, SubRip format- HH:MM:SS,sss. */ USER_TIMECODE_SUBRIP = 100, } eTimecodeStyles; diff --git a/source/blender/makesdna/DNA_view2d_types.h b/source/blender/makesdna/DNA_view2d_types.h index 131772d49b4..c385ac04bd3 100644 --- a/source/blender/makesdna/DNA_view2d_types.h +++ b/source/blender/makesdna/DNA_view2d_types.h @@ -50,7 +50,7 @@ typedef struct View2D { /** Scroll_ui - temp settings used for UI drawing of scrollers. */ short scroll_ui; - /** Keeptot - 'cur' rect cannot move outside the 'tot' rect?. */ + /** Keeptot - 'cur' rect cannot move outside the 'tot' rect? */ short keeptot; /** Keepzoom - axes that zooming cannot occur on, and also clamp within zoom-limits. */ short keepzoom; diff --git a/source/blender/makesdna/DNA_view3d_enums.h b/source/blender/makesdna/DNA_view3d_enums.h index 3de430e1177..aec52da1bf9 100644 --- a/source/blender/makesdna/DNA_view3d_enums.h +++ b/source/blender/makesdna/DNA_view3d_enums.h @@ -24,7 +24,7 @@ extern "C" { #endif -/** Settings for offscreen rendering */ +/** Settings for off-screen rendering. */ typedef enum eV3DOffscreenDrawFlag { V3D_OFSDRAW_NONE = (0), V3D_OFSDRAW_SHOW_ANNOTATION = (1 << 0), diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index 94e89944f08..a9016dd4edd 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -94,7 +94,7 @@ typedef struct Report { /** ReportType. */ short type; short flag; - /** `strlen(message)`, saves some time calculating the word wrap . */ + /** `strlen(message)`, saves some time calculating the word wrap. */ int len; const char *typestr; const char *message; diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h index 9ed01a7dbcc..2bac040ea90 100644 --- a/source/blender/makesdna/DNA_workspace_types.h +++ b/source/blender/makesdna/DNA_workspace_types.h @@ -139,7 +139,7 @@ typedef struct WorkSpace { /** Workspace-wide active asset library, for asset UIs to use (e.g. asset view UI template). The * Asset Browser has its own and doesn't use this. */ - AssetLibraryReference active_asset_library; + AssetLibraryReference asset_library; } WorkSpace; /** diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h index 8e63760fef7..fc00d5eb839 100644 --- a/source/blender/makesdna/DNA_xr_types.h +++ b/source/blender/makesdna/DNA_xr_types.h @@ -50,6 +50,7 @@ typedef struct XrSessionSettings { typedef enum eXrSessionFlag { XR_SESSION_USE_POSITION_TRACKING = (1 << 0), + XR_SESSION_USE_ABSOLUTE_TRACKING = (1 << 1), } eXrSessionFlag; typedef enum eXRSessionBasePoseType { diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c index d23b9441822..24d0d39292e 100644 --- a/source/blender/makesdna/intern/dna_genfile.c +++ b/source/blender/makesdna/intern/dna_genfile.c @@ -84,8 +84,8 @@ * **Remember to read/write integer and short aligned!** * * While writing a file, the names of a struct is indicated with a type number, - * to be found with: ``type = DNA_struct_find_nr(SDNA *, const char *)`` - * The value of ``type`` corresponds with the index within the structs array + * to be found with: `type = DNA_struct_find_nr(SDNA *, const char *)` + * The value of `type` corresponds with the index within the structs array * * For the moment: the complete DNA file is included in a .blend file. For * the future we can think of smarter methods, like only included the used @@ -101,7 +101,7 @@ * - Change of a pointer type: when the name doesn't change the contents is copied. * * NOT YET: - * - array (``vec[3]``) to float struct (``vec3f``). + * - array (`vec[3]`) to float struct (`vec3f`). * * DONE: * - Endian compatibility. diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h index c2335b82fca..d880c892aa9 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -226,7 +226,7 @@ typedef enum PropertyFlag { PROP_ICONS_CONSECUTIVE = (1 << 12), PROP_ICONS_REVERSE = (1 << 8), - /** Hidden in the user interface. */ + /** Hidden in the user interface. */ PROP_HIDDEN = (1 << 19), /** Do not write in presets. */ PROP_SKIP_SAVE = (1 << 28), diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 0285ef44e17..feacd47c98c 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -5031,7 +5031,7 @@ static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen, int } } else { - /* get data until . or [ */ + /* Get data until `.` or `[`. */ p = *path; while (*p && *p != '.' && *p != '[') { @@ -5998,7 +5998,7 @@ static void rna_path_array_multi_string_from_flat_index(PointerRNA *ptr, /** * \param index_dim: The dimension to show, 0 disables. 1 for 1d array, 2 for 2d. etc. - * \param index: The *flattened* index to use when \a ``index_dim > 0``, + * \param index: The *flattened* index to use when \a `index_dim > 0`, * this is expanded when used with multi-dimensional arrays. */ char *RNA_path_from_ID_to_property_index(PointerRNA *ptr, diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c index 0020d90ba1a..80b2594d0c9 100644 --- a/source/blender/makesrna/intern/rna_asset.c +++ b/source/blender/makesrna/intern/rna_asset.c @@ -129,7 +129,17 @@ static void rna_AssetMetaData_active_tag_range( static PointerRNA rna_AssetHandle_file_data_get(PointerRNA *ptr) { AssetHandle *asset_handle = ptr->data; - return rna_pointer_inherit_refine(ptr, &RNA_FileSelectEntry, asset_handle->file_data); + /* Have to cast away const, but the file entry API doesn't allow modifications anyway. */ + return rna_pointer_inherit_refine( + ptr, &RNA_FileSelectEntry, (FileDirEntry *)asset_handle->file_data); +} + +static void rna_AssetHandle_file_data_set(PointerRNA *ptr, + PointerRNA value, + struct ReportList *UNUSED(reports)) +{ + AssetHandle *asset_handle = ptr->data; + asset_handle->file_data = value.data; } static void rna_AssetHandle_get_full_library_path( @@ -146,83 +156,22 @@ static void rna_AssetHandle_get_full_library_path( static PointerRNA rna_AssetHandle_local_id_get(PointerRNA *ptr) { const AssetHandle *asset = ptr->data; - ID *id = ED_assetlist_asset_local_id_get(asset); + ID *id = ED_asset_handle_get_local_id(asset); return rna_pointer_inherit_refine(ptr, &RNA_ID, id); } -static void rna_AssetHandle_file_data_set(PointerRNA *ptr, - PointerRNA value, - struct ReportList *UNUSED(reports)) -{ - AssetHandle *asset_handle = ptr->data; - asset_handle->file_data = value.data; -} - -int rna_asset_library_reference_get(const AssetLibraryReference *library) -{ - return ED_asset_library_reference_to_enum_value(library); -} - -void rna_asset_library_reference_set(AssetLibraryReference *library, int value) -{ - *library = ED_asset_library_reference_from_enum_value(value); -} - const EnumPropertyItem *rna_asset_library_reference_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) { - const EnumPropertyItem predefined_items[] = { - /* For the future. */ - // {ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"}, - {ASSET_LIBRARY_LOCAL, - "LOCAL", - ICON_BLENDER, - "Current File", - "Show the assets currently available in this Blender session"}, - {0, NULL, 0, NULL, NULL}, - }; - - EnumPropertyItem *item = NULL; - int totitem = 0; - - /* Add separator if needed. */ - if (!BLI_listbase_is_empty(&U.asset_libraries)) { - const EnumPropertyItem sepr = {0, "", 0, "Custom", NULL}; - RNA_enum_item_add(&item, &totitem, &sepr); + const EnumPropertyItem *items = ED_asset_library_reference_to_rna_enum_itemf(); + if (!items) { + *r_free = false; } - int i = 0; - for (bUserAssetLibrary *user_library = U.asset_libraries.first; user_library; - user_library = user_library->next, i++) { - /* Note that the path itself isn't checked for validity here. If an invalid library path is - * used, the Asset Browser can give a nice hint on what's wrong. */ - const bool is_valid = (user_library->name[0] && user_library->path[0]); - if (!is_valid) { - continue; - } - - /* Use library path as description, it's a nice hint for users. */ - EnumPropertyItem tmp = {ASSET_LIBRARY_CUSTOM + i, - user_library->name, - ICON_NONE, - user_library->name, - user_library->path}; - RNA_enum_item_add(&item, &totitem, &tmp); - } - - if (totitem) { - const EnumPropertyItem sepr = {0, "", 0, "Built-in", NULL}; - RNA_enum_item_add(&item, &totitem, &sepr); - } - - /* Add predefined items. */ - RNA_enum_items_add(&item, &totitem, predefined_items); - - RNA_enum_item_end(&item, &totitem); *r_free = true; - return item; + return items; } #else @@ -284,7 +233,7 @@ static void rna_def_asset_data(BlenderRNA *brna) srna = RNA_def_struct(brna, "AssetMetaData", NULL); RNA_def_struct_ui_text(srna, "Asset Data", "Additional data stored for an asset data-block"); - // RNA_def_struct_ui_icon(srna, ICON_ASSET); /* TODO: Icon doesn't exist!. */ + // RNA_def_struct_ui_icon(srna, ICON_ASSET); /* TODO: Icon doesn't exist! */ /* The struct has custom properties, but no pointer properties to other IDs! */ RNA_def_struct_idprops_func(srna, "rna_AssetMetaData_idprops"); RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES); /* Mandatory! */ @@ -343,13 +292,16 @@ static void rna_def_asset_handle(BlenderRNA *brna) srna = RNA_def_struct(brna, "AssetHandle", "PropertyGroup"); RNA_def_struct_ui_text(srna, "Asset Handle", "Reference to some asset"); - /* TODO why is this editable? There probably shouldn't be a setter. */ + /* TODO It is super ugly to expose the file data here. We have to do it though so the asset view + * template can populate a RNA collection with asset-handles, which are just file entries + * currently. A proper design is being worked on. */ prop = RNA_def_property(srna, "file_data", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_struct_type(prop, "FileSelectEntry"); RNA_def_property_pointer_funcs( prop, "rna_AssetHandle_file_data_get", "rna_AssetHandle_file_data_set", NULL, NULL); - RNA_def_property_ui_text(prop, "File Entry", "File data used to refer to the asset"); + RNA_def_property_ui_text( + prop, "File Entry", "TEMPORARY, DO NOT USE - File data used to refer to the asset"); prop = RNA_def_property(srna, "local_id", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "ID"); @@ -377,7 +329,7 @@ PropertyRNA *rna_def_asset_library_reference_common(struct StructRNA *srna, const char *get, const char *set) { - PropertyRNA *prop = RNA_def_property(srna, "active_asset_library", PROP_ENUM, PROP_NONE); + PropertyRNA *prop = RNA_def_property(srna, "asset_library", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, DummyRNA_NULL_items); RNA_def_property_enum_funcs(prop, get, set, "rna_asset_library_reference_itemf"); diff --git a/source/blender/makesrna/intern/rna_collection.c b/source/blender/makesrna/intern/rna_collection.c index 752c9495e50..608a8e51b09 100644 --- a/source/blender/makesrna/intern/rna_collection.c +++ b/source/blender/makesrna/intern/rna_collection.c @@ -170,7 +170,7 @@ static bool rna_Collection_objects_override_apply(Main *bmain, Collection *coll_dst = (Collection *)ptr_dst->owner_id; if (ptr_item_dst->type == NULL || ptr_item_src->type == NULL) { - // BLI_assert(0 && "invalid source or destination object."); + // BLI_assert_msg(0, "invalid source or destination object."); return false; } @@ -185,7 +185,7 @@ static bool rna_Collection_objects_override_apply(Main *bmain, &coll_dst->gobject, ob_dst, offsetof(CollectionObject, ob)); if (cob_dst == NULL) { - BLI_assert(0 && "Could not find destination object in destination collection!"); + BLI_assert_msg(0, "Could not find destination object in destination collection!"); return false; } @@ -288,7 +288,7 @@ static bool rna_Collection_children_override_apply(Main *bmain, &coll_dst->children, subcoll_dst, offsetof(CollectionChild, collection)); if (collchild_dst == NULL) { - BLI_assert(0 && "Could not find destination sub-collection in destination collection!"); + BLI_assert_msg(0, "Could not find destination sub-collection in destination collection!"); return false; } diff --git a/source/blender/makesrna/intern/rna_context.c b/source/blender/makesrna/intern/rna_context.c index 9da08de2168..70fb10c54b0 100644 --- a/source/blender/makesrna/intern/rna_context.c +++ b/source/blender/makesrna/intern/rna_context.c @@ -146,7 +146,9 @@ static PointerRNA rna_Context_asset_file_handle_get(PointerRNA *ptr) } PointerRNA newptr; - RNA_pointer_create(NULL, &RNA_FileSelectEntry, asset_handle.file_data, &newptr); + /* Have to cast away const, but the file entry API doesn't allow modifications anyway. */ + RNA_pointer_create( + NULL, &RNA_FileSelectEntry, (struct FileDirEntry *)asset_handle.file_data, &newptr); return newptr; } diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c index 5e015af8e20..10e899b7ee3 100644 --- a/source/blender/makesrna/intern/rna_fluid.c +++ b/source/blender/makesrna/intern/rna_fluid.c @@ -1268,11 +1268,6 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) {FLUID_DOMAIN_TYPE_LIQUID, "LIQUID", 0, "Liquid", "Create domain for liquids"}, {0, NULL, 0, NULL, NULL}}; - static const EnumPropertyItem prop_noise_type_items[] = { - {FLUID_NOISE_TYPE_WAVELET, "NOISEWAVE", 0, "Wavelet", ""}, - {0, NULL, 0, NULL, NULL}, - }; - static const EnumPropertyItem prop_compression_items[] = { {VDB_COMPRESSION_ZIP, "ZIP", 0, "Zip", "Effective but slow compression"}, # ifdef WITH_OPENVDB_BLOSC @@ -1813,14 +1808,6 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); 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"); - RNA_def_property_enum_items(prop, prop_noise_type_items); - 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_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); RNA_def_property_ui_text(prop, "Use Noise", "Enable fluid noise (using amplification)"); diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index f1c05079d9c..4e95174e42b 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -656,6 +656,24 @@ static void rna_TextureGpencilModifier_material_set(PointerRNA *ptr, rna_GpencilModifier_material_set(ptr, value, ma_target, reports); } +static void rna_Lineart_start_level_set(PointerRNA *ptr, int value) +{ + LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)ptr->data; + + CLAMP(value, 0, 128); + lmd->level_start = value; + lmd->level_end = MAX2(value, lmd->level_end); +} + +static void rna_Lineart_end_level_set(PointerRNA *ptr, int value) +{ + LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)ptr->data; + + CLAMP(value, 0, 128); + lmd->level_end = value; + lmd->level_start = MIN2(value, lmd->level_start); +} + #else static void rna_def_modifier_gpencilnoise(BlenderRNA *brna) @@ -3015,15 +3033,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) prop = RNA_def_property(srna, "source_object", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); RNA_def_property_struct_type(prop, "Object"); - RNA_def_property_ui_text( - prop, "Source Object", "Source object that this modifier uses data from"); + RNA_def_property_ui_text(prop, "Object", "Generate strokes from this object"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); prop = RNA_def_property(srna, "source_collection", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); RNA_def_property_struct_type(prop, "Collection"); RNA_def_property_ui_text( - prop, "Source Collection", "Source collection that this modifier uses data from"); + prop, "Collection", "Generate strokes from the objects in this collection"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); /* types */ @@ -3068,12 +3085,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Level Start", "Minimum number of occlusions for the generated strokes"); RNA_def_property_range(prop, 0, 128); + RNA_def_property_int_funcs(prop, NULL, "rna_Lineart_start_level_set", NULL); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "level_end", PROP_INT, PROP_NONE); RNA_def_property_ui_text( prop, "Level End", "Maximum number of occlusions for the generated strokes"); RNA_def_property_range(prop, 0, 128); + RNA_def_property_int_funcs(prop, NULL, "rna_Lineart_end_level_set", NULL); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "target_material", PROP_POINTER, PROP_NONE); @@ -3085,12 +3104,11 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) NULL, "rna_GpencilModifier_material_poll"); RNA_def_property_ui_text( - prop, "Target Material", "Grease Pencil material assigned to the generated strokes"); + prop, "Material", "Grease Pencil material assigned to the generated strokes"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "target_layer", PROP_STRING, PROP_NONE); - RNA_def_property_ui_text( - prop, "Target Layer", "Grease Pencil layer assigned to the generated strokes"); + RNA_def_property_ui_text(prop, "Layer", "Grease Pencil layer assigned to the generated strokes"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "source_vertex_group", PROP_STRING, PROP_NONE); @@ -3119,6 +3137,15 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) "Certain settings will be unavailable"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "overscan", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text( + prop, + "Overscan", + "A margin to prevent strokes from ending abruptly at the edge of the image"); + RNA_def_property_ui_range(prop, 0.0f, 0.5f, 0.01f, 3); + RNA_def_property_range(prop, 0.0f, 0.5f); + RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "thickness", PROP_INT, PROP_NONE); RNA_def_property_ui_text(prop, "Thickness", "The thickness for the generated strokes"); RNA_def_property_ui_range(prop, 1, 100, 1, 1); @@ -3255,7 +3282,7 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna) /* data */ srna = RNA_def_struct(brna, "GpencilModifier", NULL); - RNA_def_struct_ui_text(srna, "GpencilModifier", "Modifier affecting the grease pencil object"); + RNA_def_struct_ui_text(srna, "GpencilModifier", "Modifier affecting the Grease Pencil object"); RNA_def_struct_refine_func(srna, "rna_GpencilModifier_refine"); RNA_def_struct_path_func(srna, "rna_GpencilModifier_path"); RNA_def_struct_sdna(srna, "GpencilModifierData"); diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 9dc08430307..0bb76fd933a 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -270,8 +270,6 @@ void rna_def_view_layer_common(struct BlenderRNA *brna, struct StructRNA *srna, PropertyRNA *rna_def_asset_library_reference_common(struct StructRNA *srna, const char *get, const char *set); -int rna_asset_library_reference_get(const struct AssetLibraryReference *library); -void rna_asset_library_reference_set(struct AssetLibraryReference *library, int value); const EnumPropertyItem *rna_asset_library_reference_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index b662f54ed4c..d91c0bfaf29 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -702,14 +702,6 @@ static void rna_def_material_lineart(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Mask", ""); RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update"); - prop = RNA_def_property(srna, "use_mat_occlusion", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_default(prop, 0); - RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_MATERIAL_CUSTOM_OCCLUSION_EFFECTIVENESS); - RNA_def_property_ui_text(prop, - "Custom Occlusion Effectiveness", - "Use custom occlusion effectiveness for this material"); - RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialLineArt_update"); - prop = RNA_def_property(srna, "mat_occlusion", PROP_INT, PROP_NONE); RNA_def_property_int_default(prop, 1); RNA_def_property_ui_range(prop, 0.0f, 5.0f, 1.0f, 1); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 1e7e940448e..04f77b9177e 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -6293,6 +6293,12 @@ static void rna_def_modifier_weld(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "loose_edges", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_WELD_LOOSE_EDGES); + RNA_def_property_ui_text( + prop, "Only Loose Edges", "Collapse edges without faces, cloth sewing edges"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + RNA_define_lib_overridable(false); } diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 4712f4a0a0b..3d4256db335 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -6622,10 +6622,9 @@ static void def_cmp_image(StructRNA *srna) "Put node output buffer to straight alpha instead of premultiplied"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); - /* NB: image user properties used in the UI are redefined in def_node_image_user, + /* NOTE: Image user properties used in the UI are redefined in def_node_image_user, * to trigger correct updates of the node editor. RNA design problem that prevents - * updates from nested structs ... - */ + * updates from nested structs. */ RNA_def_struct_sdna_from(srna, "ImageUser", "storage"); def_node_image_user(srna); } @@ -6722,9 +6721,8 @@ static void rna_def_cmp_output_file_slots_api(BlenderRNA *brna, parm = RNA_def_pointer(func, "socket", "NodeSocket", "", "New socket"); RNA_def_function_return(func, parm); - /* NB: methods below can use the standard node socket API functions, - * included here for completeness. - */ + /* NOTE: methods below can use the standard node socket API functions, + * included here for completeness. */ func = RNA_def_function(srna, "remove", "rna_Node_socket_remove"); RNA_def_function_ui_description(func, "Remove a file slot from this node"); @@ -9443,6 +9441,52 @@ static void def_geo_attribute_vector_rotate(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_curve_set_handles(StructRNA *srna) +{ + static const EnumPropertyItem type_items[] = { + {GEO_NODE_CURVE_HANDLE_FREE, + "FREE", + ICON_HANDLE_FREE, + "Free", + "The handle can be moved anywhere, and doesn't influence the point's other handle"}, + {GEO_NODE_CURVE_HANDLE_AUTO, + "AUTO", + ICON_HANDLE_AUTO, + "Auto", + "The location is automatically calculated to be smooth"}, + {GEO_NODE_CURVE_HANDLE_VECTOR, + "VECTOR", + ICON_HANDLE_VECTOR, + "Vector", + "The location is calculated to point to the next/previous control point"}, + {GEO_NODE_CURVE_HANDLE_ALIGN, + "ALIGN", + ICON_HANDLE_ALIGNED, + "Align", + "The location is constrained to point in the opposite direction as the other handle"}, + {0, NULL, 0, NULL, NULL}}; + + static const EnumPropertyItem mode_items[] = { + {GEO_NODE_CURVE_HANDLE_LEFT, "LEFT", ICON_NONE, "Left", "Update the left handles"}, + {GEO_NODE_CURVE_HANDLE_RIGHT, "RIGHT", ICON_NONE, "Right", "Update the right handles"}, + {0, NULL, 0, NULL, NULL}}; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSetHandles", "storage"); + + prop = RNA_def_property(srna, "handle_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "handle_type"); + RNA_def_property_enum_items(prop, type_items); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "Whether to update left and right handles"); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_curve_primitive_circle(StructRNA *srna) { static const EnumPropertyItem mode_items[] = { @@ -10039,6 +10083,32 @@ static void def_geo_curve_to_points(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_curve_trim(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem mode_items[] = { + {GEO_NODE_CURVE_INTERPOLATE_FACTOR, + "FACTOR", + 0, + "Factor", + "Find the endpoint positions using a factor of each spline's length"}, + {GEO_NODE_CURVE_INTERPOLATE_LENGTH, + "LENGTH", + 0, + "Length", + "Find the endpoint positions using a length from the start of each spline"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveTrim", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "How to find endpoint positions for the trimmed spline"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_attribute_transfer(StructRNA *srna) { static EnumPropertyItem mapping_items[] = { @@ -10275,11 +10345,11 @@ static void rna_def_node_socket(BlenderRNA *brna) 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. + /* NOTE: The type property is used by standard sockets. * Ideally should be defined only for the registered subclass, * but to use the existing DNA is added in the base type here. - * Future socket types can ignore or override this if needed. - */ + * Future socket types can ignore or override this if needed. */ + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "type"); RNA_def_property_enum_items(prop, node_socket_type_items); diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c index 4fe61df9387..73924c45d52 100644 --- a/source/blender/makesrna/intern/rna_render.c +++ b/source/blender/makesrna/intern/rna_render.c @@ -72,9 +72,6 @@ const EnumPropertyItem rna_enum_render_pass_type_items[] = { {SCE_PASS_SUBSURFACE_DIRECT, "SUBSURFACE_DIRECT", 0, "Subsurface Direct", ""}, {SCE_PASS_SUBSURFACE_INDIRECT, "SUBSURFACE_INDIRECT", 0, "Subsurface Indirect", ""}, {SCE_PASS_SUBSURFACE_COLOR, "SUBSURFACE_COLOR", 0, "Subsurface Color", ""}, -#ifdef WITH_CYCLES_DEBUG - {SCE_PASS_DEBUG, "DEBUG", 0, "Pass used for render engine debugging", ""}, -#endif {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c index 6008ef40b60..b080e572fa2 100644 --- a/source/blender/makesrna/intern/rna_rna.c +++ b/source/blender/makesrna/intern/rna_rna.c @@ -2089,7 +2089,7 @@ bool rna_property_override_store_default(Main *UNUSED(bmain), switch (RNA_property_type(prop_local)) { case PROP_BOOLEAN: /* TODO: support boolean ops? Really doubt this would ever be useful though. */ - BLI_assert(0 && "Boolean properties support no override diff operation"); + BLI_assert_msg(0, "Boolean properties support no override diff operation"); break; case PROP_INT: { int prop_min, prop_max; @@ -2123,7 +2123,7 @@ bool rna_property_override_store_default(Main *UNUSED(bmain), for (int j = len_local; j--;) { array_b[j] = j >= i ? -array_b[j] : fac * (array_a[j] - array_b[j]); if (array_b[j] < prop_min || array_b[j] > prop_max) { - /* We failed to find a suitable diff op, + /* We failed to find a suitable diff op, * fall back to plain REPLACE one. */ opop->operation = IDOVERRIDE_LIBRARY_OP_REPLACE; do_set = false; @@ -2143,7 +2143,7 @@ bool rna_property_override_store_default(Main *UNUSED(bmain), break; } default: - BLI_assert(0 && "Unsupported RNA override diff operation on integer"); + BLI_assert_msg(0, "Unsupported RNA override diff operation on integer"); break; } @@ -2175,7 +2175,7 @@ bool rna_property_override_store_default(Main *UNUSED(bmain), break; } default: - BLI_assert(0 && "Unsupported RNA override diff operation on integer"); + BLI_assert_msg(0, "Unsupported RNA override diff operation on integer"); break; } } @@ -2213,7 +2213,7 @@ bool rna_property_override_store_default(Main *UNUSED(bmain), for (int j = len_local; j--;) { array_b[j] = j >= i ? -array_b[j] : fac * (array_a[j] - array_b[j]); if (array_b[j] < prop_min || array_b[j] > prop_max) { - /* We failed to find a suitable diff op, + /* We failed to find a suitable diff op, * fall back to plain REPLACE one. */ opop->operation = IDOVERRIDE_LIBRARY_OP_REPLACE; do_set = false; @@ -2256,7 +2256,7 @@ bool rna_property_override_store_default(Main *UNUSED(bmain), break; } default: - BLI_assert(0 && "Unsupported RNA override diff operation on float"); + BLI_assert_msg(0, "Unsupported RNA override diff operation on float"); break; } @@ -2299,7 +2299,7 @@ bool rna_property_override_store_default(Main *UNUSED(bmain), break; } default: - BLI_assert(0 && "Unsupported RNA override diff operation on float"); + BLI_assert_msg(0, "Unsupported RNA override diff operation on float"); break; } } @@ -2307,17 +2307,17 @@ bool rna_property_override_store_default(Main *UNUSED(bmain), } case PROP_ENUM: /* TODO: support add/sub, for bitflags? */ - BLI_assert(0 && "Enum properties support no override diff operation"); + BLI_assert_msg(0, "Enum properties support no override diff operation"); break; case PROP_POINTER: - BLI_assert(0 && "Pointer properties support no override diff operation"); + BLI_assert_msg(0, "Pointer properties support no override diff operation"); break; case PROP_STRING: - BLI_assert(0 && "String properties support no override diff operation"); + BLI_assert_msg(0, "String properties support no override diff operation"); break; case PROP_COLLECTION: /* XXX TODO: support this of course... */ - BLI_assert(0 && "Collection properties support no override diff operation"); + BLI_assert_msg(0, "Collection properties support no override diff operation"); break; default: break; @@ -2364,7 +2364,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain), RNA_property_boolean_set_array(ptr_dst, prop_dst, array_a); break; default: - BLI_assert(0 && "Unsupported RNA override operation on boolean"); + BLI_assert_msg(0, "Unsupported RNA override operation on boolean"); return false; } @@ -2380,7 +2380,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain), RNA_PROPERTY_SET_SINGLE(boolean, ptr_dst, prop_dst, index, value); break; default: - BLI_assert(0 && "Unsupported RNA override operation on boolean"); + BLI_assert_msg(0, "Unsupported RNA override operation on boolean"); return false; } } @@ -2421,7 +2421,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain), } break; default: - BLI_assert(0 && "Unsupported RNA override operation on integer"); + BLI_assert_msg(0, "Unsupported RNA override operation on integer"); return false; } @@ -2459,7 +2459,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain), storage_value); break; default: - BLI_assert(0 && "Unsupported RNA override operation on integer"); + BLI_assert_msg(0, "Unsupported RNA override operation on integer"); return false; } } @@ -2506,7 +2506,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain), } break; default: - BLI_assert(0 && "Unsupported RNA override operation on float"); + BLI_assert_msg(0, "Unsupported RNA override operation on float"); return false; } @@ -2552,7 +2552,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain), storage_value); break; default: - BLI_assert(0 && "Unsupported RNA override operation on float"); + BLI_assert_msg(0, "Unsupported RNA override operation on float"); return false; } } @@ -2566,7 +2566,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain), break; /* TODO: support add/sub, for bitflags? */ default: - BLI_assert(0 && "Unsupported RNA override operation on enum"); + BLI_assert_msg(0, "Unsupported RNA override operation on enum"); return false; } return true; @@ -2579,7 +2579,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain), RNA_property_pointer_set(ptr_dst, prop_dst, value, NULL); break; default: - BLI_assert(0 && "Unsupported RNA override operation on pointer"); + BLI_assert_msg(0, "Unsupported RNA override operation on pointer"); return false; } return true; @@ -2593,7 +2593,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain), RNA_property_string_set(ptr_dst, prop_dst, value); break; default: - BLI_assert(0 && "Unsupported RNA override operation on string"); + BLI_assert_msg(0, "Unsupported RNA override operation on string"); return false; } @@ -2609,7 +2609,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain), const bool is_dst_idprop = (prop_dst->magic != RNA_MAGIC) || (prop_dst->flag & PROP_IDPROPERTY) != 0; if (!(is_src_idprop && is_dst_idprop)) { - BLI_assert(0 && "You need to define a specific override apply callback for collections"); + BLI_assert_msg(0, "You need to define a specific override apply callback for collections"); return false; } @@ -2668,7 +2668,7 @@ bool rna_property_override_apply_default(Main *UNUSED(bmain), return RNA_property_collection_move(ptr_dst, prop_dst, item_index_added, item_index_dst); } default: - BLI_assert(0 && "Unsupported RNA override operation on collection"); + BLI_assert_msg(0, "Unsupported RNA override operation on collection"); return false; } } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 5c9c7b50339..6a28cf34965 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -2834,7 +2834,18 @@ static void rna_def_tool_settings(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; - static const EnumPropertyItem annotation_stroke_placement_items[] = { + static const EnumPropertyItem annotation_stroke_placement_view2d_items[] = { + {GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR, + "IMAGE", + ICON_IMAGE_DATA, + "Image", + "Strick stroke to the image"}, + /* Weird, GP_PROJECT_VIEWALIGN is inverted. */ + {0, "VIEW", ICON_RESTRICT_VIEW_ON, "View", "Stick stroke to the view"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem annotation_stroke_placement_view3d_items[] = { {GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR, "CURSOR", ICON_PIVOT_CURSOR, @@ -3338,30 +3349,15 @@ static void rna_def_tool_settings(BlenderRNA *brna) /* Annotations - 2D Views Stroke Placement */ prop = RNA_def_property(srna, "annotation_stroke_placement_view2d", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_v2d_align"); - RNA_def_property_enum_items(prop, annotation_stroke_placement_items); + RNA_def_property_enum_items(prop, annotation_stroke_placement_view2d_items); RNA_def_property_ui_text(prop, "Stroke Placement (2D View)", ""); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - /* Annotations - Sequencer Preview Stroke Placement */ - prop = RNA_def_property( - srna, "annotation_stroke_placement_sequencer_preview", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_seq_align"); - RNA_def_property_enum_items(prop, annotation_stroke_placement_items); - RNA_def_property_ui_text(prop, "Stroke Placement (Sequencer Preview)", ""); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - - /* Annotations - Image Editor Stroke Placement */ - prop = RNA_def_property(srna, "annotation_stroke_placement_image_editor", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_ima_align"); - RNA_def_property_enum_items(prop, annotation_stroke_placement_items); - RNA_def_property_ui_text(prop, "Stroke Placement (Image Editor)", ""); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); - /* Annotations - 3D View Stroke Placement */ /* XXX: Do we need to decouple the stroke_endpoints setting too? */ prop = RNA_def_property(srna, "annotation_stroke_placement_view3d", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "annotate_v3d_align"); - RNA_def_property_enum_items(prop, annotation_stroke_placement_items); + RNA_def_property_enum_items(prop, annotation_stroke_placement_view3d_items); RNA_def_property_enum_default(prop, GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR); RNA_def_property_ui_text(prop, "Annotation Stroke Placement (3D View)", diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index b1f0b0d760f..74fe2a26505 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -306,6 +306,10 @@ static void do_sequence_frame_change_update(Scene *scene, Sequence *seq) SEQ_transform_seqbase_shuffle(seqbase, seq, scene); /* XXX: BROKEN!, uses context seqbasep. */ } SEQ_sort(seqbase); + + if (seq->type == SEQ_TYPE_SOUND_RAM) { + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); + } } /* A simple wrapper around above func, directly usable as prop update func. @@ -324,7 +328,6 @@ static void rna_Sequence_start_frame_set(PointerRNA *ptr, int value) Sequence *seq = (Sequence *)ptr->data; Scene *scene = (Scene *)ptr->owner_id; - SEQ_relations_invalidate_cache_composite(scene, seq); SEQ_transform_translate_sequence(scene, seq, value - seq->start); do_sequence_frame_change_update(scene, seq); SEQ_relations_invalidate_cache_composite(scene, seq); @@ -335,7 +338,6 @@ static void rna_Sequence_start_frame_final_set(PointerRNA *ptr, int value) Sequence *seq = (Sequence *)ptr->data; Scene *scene = (Scene *)ptr->owner_id; - SEQ_relations_invalidate_cache_composite(scene, seq); SEQ_transform_set_left_handle_frame(seq, value); SEQ_transform_fix_single_image_seq_offsets(seq); do_sequence_frame_change_update(scene, seq); @@ -347,7 +349,6 @@ static void rna_Sequence_end_frame_final_set(PointerRNA *ptr, int value) Sequence *seq = (Sequence *)ptr->data; Scene *scene = (Scene *)ptr->owner_id; - SEQ_relations_invalidate_cache_composite(scene, seq); SEQ_transform_set_right_handle_frame(seq, value); SEQ_transform_fix_single_image_seq_offsets(seq); do_sequence_frame_change_update(scene, seq); @@ -451,7 +452,6 @@ static void rna_Sequence_frame_length_set(PointerRNA *ptr, int value) Sequence *seq = (Sequence *)ptr->data; Scene *scene = (Scene *)ptr->owner_id; - SEQ_relations_invalidate_cache_composite(scene, seq); SEQ_transform_set_right_handle_frame(seq, SEQ_transform_get_left_handle_frame(seq) + value); do_sequence_frame_change_update(scene, seq); SEQ_relations_invalidate_cache_composite(scene, seq); @@ -477,7 +477,6 @@ static void rna_Sequence_channel_set(PointerRNA *ptr, int value) Editing *ed = SEQ_editing_get(scene, false); ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); - SEQ_relations_invalidate_cache_composite(scene, seq); /* check channel increment or decrement */ const int channel_delta = (value >= seq->machine) ? 1 : -1; seq->machine = value; @@ -1921,16 +1920,6 @@ static void rna_def_sequence(BlenderRNA *brna) RNA_def_property_update( prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update"); - prop = RNA_def_property(srna, "speed_factor", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "speed_fader"); - RNA_def_property_ui_text( - prop, - "Speed Factor", - "Multiply the current speed of the sequence with this number or remap current frame " - "to this frame"); - RNA_def_property_update( - prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update"); - /* modifiers */ prop = RNA_def_property(srna, "modifiers", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "SequenceModifier"); @@ -2787,24 +2776,48 @@ static void rna_def_speed_control(StructRNA *srna) RNA_def_struct_sdna_from(srna, "SpeedControlVars", "effectdata"); - prop = RNA_def_property(srna, "multiply_speed", PROP_FLOAT, PROP_UNSIGNED); - RNA_def_property_float_sdna(prop, NULL, "globalSpeed"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); /* seq->facf0 is used to animate this */ - RNA_def_property_ui_text( - prop, "Multiply Speed", "Multiply the resulting speed after the speed factor"); - RNA_def_property_ui_range(prop, 0.0f, 100.0f, 1, -1); + static const EnumPropertyItem speed_control_items[] = { + {SEQ_SPEED_STRETCH, + "STRETCH", + 0, + "Stretch", + "Adjust input playback speed, so its duration fits strip length"}, + {SEQ_SPEED_MULTIPLY, "MULTIPLY", 0, "Multiply", "Multiply with the speed factor"}, + {SEQ_SPEED_FRAME_NUMBER, + "FRAME_NUMBER", + 0, + "Frame Number", + "Frame number of the input strip"}, + {SEQ_SPEED_LENGTH, "LENGTH", 0, "Length", "Percentage of the input strip length"}, + {0, NULL, 0, NULL, NULL}, + }; + + prop = RNA_def_property(srna, "speed_control", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "speed_control_type"); + RNA_def_property_enum_items(prop, speed_control_items); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Speed Control", "Speed control method"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update"); - prop = RNA_def_property(srna, "use_as_speed", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flags", SEQ_SPEED_INTEGRATE); + prop = RNA_def_property(srna, "speed_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "speed_fader"); RNA_def_property_ui_text( - prop, "Use as Speed", "Interpret the value as speed instead of a frame number"); + prop, + "Multiply Factor", + "Multiply the current speed of the sequence with this number or remap current frame " + "to this frame"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update"); - prop = RNA_def_property(srna, "use_scale_to_length", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flags", SEQ_SPEED_COMPRESS_IPO_Y); - RNA_def_property_ui_text( - prop, "Scale to Length", "Scale values from 0.0 to 1.0 to target sequence length"); + prop = RNA_def_property(srna, "speed_frame_number", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "speed_fader_frame_number"); + RNA_def_property_ui_text(prop, "Frame Number", "Frame number of input strip"); + RNA_def_property_ui_range(prop, 0.0, MAXFRAME, 1.0, -1); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update"); + + prop = RNA_def_property(srna, "speed_length", PROP_FLOAT, PROP_PERCENTAGE); + RNA_def_property_float_sdna(prop, NULL, "speed_fader_length"); + RNA_def_property_ui_text(prop, "Length", "Percentage of input strip length"); + RNA_def_property_ui_range(prop, 0.0, 100.0, 1, -1); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_raw_update"); prop = RNA_def_property(srna, "use_frame_interpolate", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index 057f49c0319..4895ab11618 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -711,7 +711,7 @@ void RNA_api_sequence_strip(StructRNA *srna) RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_enum(func, "split_method", seq_split_method_items, 0, "", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); - /* Retirn type. */ + /* Return type. */ parm = RNA_def_pointer(func, "sequence", "Sequence", "", "Right side Sequence"); RNA_def_function_return(func, parm); } diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index f2d2b190d87..1d4318602c2 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -359,7 +359,7 @@ static const EnumPropertyItem display_channels_items[] = { "Color and Alpha", "Display image with RGB colors and alpha transparency"}, {0, "COLOR", ICON_IMAGE_RGB, "Color", "Display image with RGB colors"}, - {SI_SHOW_ALPHA, "ALPHA", ICON_IMAGE_ALPHA, "Alpha", "Display alpha transparency channel"}, + {SI_SHOW_ALPHA, "ALPHA", ICON_IMAGE_ALPHA, "Alpha", "Display alpha transparency channel"}, {SI_SHOW_ZBUF, "Z_BUFFER", ICON_IMAGE_ZDEPTH, @@ -536,6 +536,7 @@ static const EnumPropertyItem rna_enum_curve_display_handle_items[] = { # include "DEG_depsgraph_build.h" # include "ED_anim_api.h" +# include "ED_asset.h" # include "ED_buttons.h" # include "ED_clip.h" # include "ED_fileselect.h" @@ -2562,112 +2563,19 @@ static PointerRNA rna_FileSelectParams_filter_id_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_FileSelectIDFilter, ptr->data); } -/* TODO use rna_def_asset_library_reference_common() */ - static int rna_FileAssetSelectParams_asset_library_get(PointerRNA *ptr) { FileAssetSelectParams *params = ptr->data; /* Just an extra sanity check to ensure this isn't somehow called for RNA_FileSelectParams. */ BLI_assert(ptr->type == &RNA_FileAssetSelectParams); - /* Simple case: Predefined repo, just set the value. */ - if (params->asset_library.type < ASSET_LIBRARY_CUSTOM) { - return params->asset_library.type; - } - - /* Note that the path isn't checked for validity here. If an invalid library path is used, the - * Asset Browser can give a nice hint on what's wrong. */ - const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index( - &U, params->asset_library.custom_library_index); - if (user_library) { - return ASSET_LIBRARY_CUSTOM + params->asset_library.custom_library_index; - } - - BLI_assert(0); - return ASSET_LIBRARY_LOCAL; + return ED_asset_library_reference_to_enum_value(¶ms->asset_library); } static void rna_FileAssetSelectParams_asset_library_set(PointerRNA *ptr, int value) { FileAssetSelectParams *params = ptr->data; - - /* Simple case: Predefined repo, just set the value. */ - if (value < ASSET_LIBRARY_CUSTOM) { - params->asset_library.type = value; - params->asset_library.custom_library_index = -1; - BLI_assert(ELEM(value, ASSET_LIBRARY_LOCAL)); - return; - } - - const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_from_index( - &U, value - ASSET_LIBRARY_CUSTOM); - - /* Note that the path isn't checked for validity here. If an invalid library path is used, the - * Asset Browser can give a nice hint on what's wrong. */ - const bool is_valid = (user_library->name[0] && user_library->path[0]); - if (!user_library) { - params->asset_library.type = ASSET_LIBRARY_LOCAL; - params->asset_library.custom_library_index = -1; - } - else if (user_library && is_valid) { - params->asset_library.custom_library_index = value - ASSET_LIBRARY_CUSTOM; - params->asset_library.type = ASSET_LIBRARY_CUSTOM; - } -} - -static const EnumPropertyItem *rna_FileAssetSelectParams_asset_library_itemf( - bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) -{ - const EnumPropertyItem predefined_items[] = { - /* For the future. */ - // {ASSET_REPO_BUNDLED, "BUNDLED", 0, "Bundled", "Show the default user assets"}, - {ASSET_LIBRARY_LOCAL, - "LOCAL", - ICON_BLENDER, - "Current File", - "Show the assets currently available in this Blender session"}, - {0, NULL, 0, NULL, NULL}, - }; - - EnumPropertyItem *item = NULL; - int totitem = 0; - - /* Add separator if needed. */ - if (!BLI_listbase_is_empty(&U.asset_libraries)) { - const EnumPropertyItem sepr = {0, "", 0, "Custom", NULL}; - RNA_enum_item_add(&item, &totitem, &sepr); - } - - int i = 0; - for (bUserAssetLibrary *user_library = U.asset_libraries.first; user_library; - user_library = user_library->next, i++) { - /* Note that the path itself isn't checked for validity here. If an invalid library path is - * used, the Asset Browser can give a nice hint on what's wrong. */ - const bool is_valid = (user_library->name[0] && user_library->path[0]); - if (!is_valid) { - continue; - } - - /* Use library path as description, it's a nice hint for users. */ - EnumPropertyItem tmp = {ASSET_LIBRARY_CUSTOM + i, - user_library->name, - ICON_NONE, - user_library->name, - user_library->path}; - RNA_enum_item_add(&item, &totitem, &tmp); - } - - if (totitem) { - const EnumPropertyItem sepr = {0, "", 0, "Built-in", NULL}; - RNA_enum_item_add(&item, &totitem, &sepr); - } - - /* Add predefined items. */ - RNA_enum_items_add(&item, &totitem, predefined_items); - - RNA_enum_item_end(&item, &totitem); - *r_free = true; - return item; + params->asset_library = ED_asset_library_reference_from_enum_value(value); } static void rna_FileAssetSelectParams_asset_category_set(PointerRNA *ptr, uint64_t value) @@ -5611,6 +5519,11 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "draw_flag", SEQ_DRAW_TRANSFORM_PREVIEW); RNA_def_property_ui_text(prop, "Transform Preview", "Show preview of the transformed frames"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_grid", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_GRID); + RNA_def_property_ui_text(prop, "Show Grid", "Show vertical grid lines"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); } static void rna_def_space_text(BlenderRNA *brna) @@ -6559,12 +6472,9 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna) RNA_def_struct_ui_text( srna, "Asset Select Parameters", "Settings for the file selection in Asset Browser mode"); - prop = RNA_def_property(srna, "asset_library", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, DummyRNA_NULL_items); - RNA_def_property_enum_funcs(prop, - "rna_FileAssetSelectParams_asset_library_get", - "rna_FileAssetSelectParams_asset_library_set", - "rna_FileAssetSelectParams_asset_library_itemf"); + prop = rna_def_asset_library_reference_common(srna, + "rna_FileAssetSelectParams_asset_library_get", + "rna_FileAssetSelectParams_asset_library_set"); RNA_def_property_ui_text(prop, "Asset Library", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); diff --git a/source/blender/makesrna/intern/rna_volume.c b/source/blender/makesrna/intern/rna_volume.c index 76db6f3e325..8ed53c9f70f 100644 --- a/source/blender/makesrna/intern/rna_volume.c +++ b/source/blender/makesrna/intern/rna_volume.c @@ -495,7 +495,7 @@ static void rna_def_volume_render(BlenderRNA *brna) prop = RNA_def_property(srna, "space", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, space_items); RNA_def_property_ui_text( - prop, "Space", "Specify volume density and step size in object or world space"); + prop, "Space", "Specify volume density and step size in object or world space"); RNA_def_property_update(prop, 0, "rna_Volume_update_display"); prop = RNA_def_property(srna, "step_size", PROP_FLOAT, PROP_DISTANCE); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 2a4abac04f8..667f3822935 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -1825,7 +1825,7 @@ static void rna_KeyMapItem_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Poi #else /* RNA_RUNTIME */ /** - * expose ``Operator.options`` as its own type so we can control each flags use + * expose `Operator.options` as its own type so we can control each flags use * (some are read-only). */ static void rna_def_operator_options_runtime(BlenderRNA *brna) @@ -1912,7 +1912,7 @@ static void rna_def_operator(BlenderRNA *brna) /* Registration */ prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "type->idname"); - /* else it uses the pointer size!. -3 because '.' -> '_OT_' */ + /* Without setting the length the pointer size would be used. -3 because `.` -> `_OT_`. */ RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME - 3); RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_idname_set"); /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */ diff --git a/source/blender/makesrna/intern/rna_workspace.c b/source/blender/makesrna/intern/rna_workspace.c index b053bb0ff62..95f62d7de16 100644 --- a/source/blender/makesrna/intern/rna_workspace.c +++ b/source/blender/makesrna/intern/rna_workspace.c @@ -45,6 +45,8 @@ # include "DNA_screen_types.h" # include "DNA_space_types.h" +# include "ED_asset.h" + # include "RNA_access.h" # include "WM_toolsystem.h" @@ -107,16 +109,16 @@ static void rna_WorkSpace_owner_ids_clear(WorkSpace *workspace) WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | NA_REMOVED, workspace); } -static int rna_WorkSpace_active_asset_library_get(PointerRNA *ptr) +static int rna_WorkSpace_asset_library_get(PointerRNA *ptr) { const WorkSpace *workspace = ptr->data; - return rna_asset_library_reference_get(&workspace->active_asset_library); + return ED_asset_library_reference_to_enum_value(&workspace->asset_library); } -static void rna_WorkSpace_active_asset_library_set(PointerRNA *ptr, int value) +static void rna_WorkSpace_asset_library_set(PointerRNA *ptr, int value) { WorkSpace *workspace = ptr->data; - rna_asset_library_reference_set(&workspace->active_asset_library, value); + workspace->asset_library = ED_asset_library_reference_from_enum_value(value); } static bToolRef *rna_WorkSpace_tools_from_tkey(WorkSpace *workspace, @@ -420,7 +422,7 @@ static void rna_def_workspace(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_window_update_all"); prop = rna_def_asset_library_reference_common( - srna, "rna_WorkSpace_active_asset_library_get", "rna_WorkSpace_active_asset_library_set"); + srna, "rna_WorkSpace_asset_library_get", "rna_WorkSpace_asset_library_set"); RNA_def_property_ui_text(prop, "Asset Library", "Active asset library to show in the UI, not used by the Asset Browser " diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index 04a8500d136..56e8418972c 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -34,6 +34,63 @@ # include "WM_api.h" +# ifdef WITH_XR_OPENXR +static wmXrData *rna_XrSession_wm_xr_data_get(PointerRNA *ptr) +{ + /* Callers could also get XrSessionState pointer through ptr->data, but prefer if we just + * consistently pass wmXrData pointers to the WM_xr_xxx() API. */ + + BLI_assert((ptr->type == &RNA_XrSessionSettings) || (ptr->type == &RNA_XrSessionState)); + + wmWindowManager *wm = (wmWindowManager *)ptr->owner_id; + BLI_assert(wm && (GS(wm->id.name) == ID_WM)); + + return &wm->xr; +} +# endif + +static bool rna_XrSessionSettings_use_positional_tracking_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + return (xr->session_settings.flag & XR_SESSION_USE_POSITION_TRACKING) != 0; +# else + UNUSED_VARS(ptr); + return false; +# endif +} + +static void rna_XrSessionSettings_use_positional_tracking_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + SET_FLAG_FROM_TEST(xr->session_settings.flag, value, XR_SESSION_USE_POSITION_TRACKING); +# else + UNUSED_VARS(ptr, value); +# endif +} + +static bool rna_XrSessionSettings_use_absolute_tracking_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + return (xr->session_settings.flag & XR_SESSION_USE_ABSOLUTE_TRACKING) != 0; +# else + UNUSED_VARS(ptr); + return false; +# endif +} + +static void rna_XrSessionSettings_use_absolute_tracking_set(PointerRNA *ptr, bool value) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + SET_FLAG_FROM_TEST(xr->session_settings.flag, value, XR_SESSION_USE_ABSOLUTE_TRACKING); +# else + UNUSED_VARS(ptr, value); +# endif +} + static bool rna_XrSessionState_is_running(bContext *C) { # ifdef WITH_XR_OPENXR @@ -55,25 +112,10 @@ static void rna_XrSessionState_reset_to_base_pose(bContext *C) # endif } -# ifdef WITH_XR_OPENXR -static wmXrData *rna_XrSessionState_wm_xr_data_get(PointerRNA *ptr) -{ - /* Callers could also get XrSessionState pointer through ptr->data, but prefer if we just - * consistently pass wmXrData pointers to the WM_xr_xxx() API. */ - - BLI_assert(ptr->type == &RNA_XrSessionState); - - wmWindowManager *wm = (wmWindowManager *)ptr->owner_id; - BLI_assert(wm && (GS(wm->id.name) == ID_WM)); - - return &wm->xr; -} -# endif - static void rna_XrSessionState_viewer_pose_location_get(PointerRNA *ptr, float *r_values) { # ifdef WITH_XR_OPENXR - const wmXrData *xr = rna_XrSessionState_wm_xr_data_get(ptr); + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); WM_xr_session_state_viewer_pose_location_get(xr, r_values); # else UNUSED_VARS(ptr); @@ -84,7 +126,7 @@ static void rna_XrSessionState_viewer_pose_location_get(PointerRNA *ptr, float * static void rna_XrSessionState_viewer_pose_rotation_get(PointerRNA *ptr, float *r_values) { # ifdef WITH_XR_OPENXR - const wmXrData *xr = rna_XrSessionState_wm_xr_data_get(ptr); + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); WM_xr_session_state_viewer_pose_rotation_get(xr, r_values); # else UNUSED_VARS(ptr); @@ -181,12 +223,22 @@ static void rna_def_xr_session_settings(BlenderRNA *brna) RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); prop = RNA_def_property(srna, "use_positional_tracking", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", XR_SESSION_USE_POSITION_TRACKING); + RNA_def_property_boolean_funcs(prop, + "rna_XrSessionSettings_use_positional_tracking_get", + "rna_XrSessionSettings_use_positional_tracking_set"); RNA_def_property_ui_text( prop, "Positional Tracking", "Allow VR headsets to affect the location in virtual space, in addition to the rotation"); RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + + prop = RNA_def_property(srna, "use_absolute_tracking", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, + "rna_XrSessionSettings_use_absolute_tracking_get", + "rna_XrSessionSettings_use_absolute_tracking_set"); + RNA_def_property_ui_text( + prop, "Absolute Tracking", "Use unadjusted location/rotation as defined by the XR runtime"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); } static void rna_def_xr_session_state(BlenderRNA *brna) diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 722614c4831..6a9c9715994 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -194,8 +194,8 @@ static void dm_mvert_map_doubles(int *doubles_map, i_target_low_bound = 0; target_scan_completed = false; - /* Scan source vertices, in SortVertsElem sorted array, */ - /* all the while maintaining the lower bound of possible doubles in target vertices */ + /* Scan source vertices, in #SortVertsElem sorted array, + * all the while maintaining the lower bound of possible doubles in target vertices. */ for (i_source = 0, sve_source = sorted_verts_source; i_source < source_num_verts; i_source++, sve_source++) { int best_target_vertex = -1; diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 87fce26c45e..5fa11ffdd10 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -10,7 +10,7 @@ * 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, + * 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 by the Blender Foundation. @@ -1133,6 +1133,18 @@ static void panel_draw(const bContext *C, Panel *panel) } } + /* Draw node warnings. */ + if (nmd->runtime_eval_log != nullptr) { + const geo_log::ModifierLog &log = *static_cast<geo_log::ModifierLog *>(nmd->runtime_eval_log); + log.foreach_node_log([layout](const geo_log::NodeLog &node_log) { + for (const geo_log::NodeWarning &warning : node_log.warnings()) { + if (warning.type != geo_log::NodeWarningType::Info) { + uiItemL(layout, warning.message.c_str(), ICON_ERROR); + } + } + }); + } + modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index 54bb68dc21a..e7750f0a0d1 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -257,10 +257,10 @@ static void normalEditModifier_do_radial(NormalEditModifierData *enmd, generate_vert_coordinates(mesh, ob, ob_target, enmd->offset, num_verts, cos, size); /** - * size gives us our spheroid coefficients ``(A, B, C)``. + * size gives us our spheroid coefficients `(A, B, C)`. * Then, we want to find out for each vert its (a, b, c) triple (proportional to (A, B, C) one). * - * Ellipsoid basic equation: ``(x^2/a^2) + (y^2/b^2) + (z^2/c^2) = 1.`` + * Ellipsoid basic equation: `(x^2/a^2) + (y^2/b^2) + (z^2/c^2) = 1`. * Since we want to find (a, b, c) matching this equation and proportional to (A, B, C), * we can do: * <pre> diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c index b48cbf233ef..33385e91637 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.c +++ b/source/blender/modifiers/intern/MOD_particlesystem.c @@ -282,13 +282,13 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) if (!(ob->mode & OB_MODE_PARTICLE_EDIT)) { if (ELEM(psys->part->ren_as, PART_DRAW_GR, PART_DRAW_OB)) { uiItemO(layout, - CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Convert"), + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Make Instances Real"), ICON_NONE, "OBJECT_OT_duplicates_make_real"); } else if (psys->part->ren_as == PART_DRAW_PATH) { uiItemO(layout, - CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Convert"), + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Convert to Mesh"), ICON_NONE, "OBJECT_OT_modifier_convert"); } diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c index 9245afdb096..6239ee45e59 100644 --- a/source/blender/modifiers/intern/MOD_ui_common.c +++ b/source/blender/modifiers/intern/MOD_ui_common.c @@ -9,7 +9,7 @@ * 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, + * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c index bbdd7d0e647..3b147c69716 100644 --- a/source/blender/modifiers/intern/MOD_weighted_normal.c +++ b/source/blender/modifiers/intern/MOD_weighted_normal.c @@ -623,7 +623,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * CustomData *ldata = &result->ldata; clnors = CustomData_get_layer(ldata, CD_CUSTOMLOOPNORMAL); - /* Keep info whether we had clnors, + /* Keep info whether we had clnors, * it helps when generating clnor spaces and default normals. */ const bool has_clnors = clnors != NULL; if (!clnors) { diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.c b/source/blender/modifiers/intern/MOD_weightvg_util.c index 696c4c855c7..e403051d1be 100644 --- a/source/blender/modifiers/intern/MOD_weightvg_util.c +++ b/source/blender/modifiers/intern/MOD_weightvg_util.c @@ -167,9 +167,9 @@ void weightvg_do_mask(const ModifierEvalContext *ctx, const int numVerts = mesh->totvert; /* Use new generic get_texture_coords, but do not modify our DNA struct for it... - * XXX Why use a ModifierData stuff here ? Why not a simple, generic struct for parameters ? - * What e.g. if a modifier wants to use several textures ? - * Why use only v_co, and not MVert (or both) ? + * XXX Why use a ModifierData stuff here ? Why not a simple, generic struct for parameters? + * What e.g. if a modifier wants to use several textures? + * Why use only v_co, and not MVert (or both)? */ t_map.texture = texture; t_map.map_object = tex_map_object; diff --git a/source/blender/modifiers/intern/MOD_weld.c b/source/blender/modifiers/intern/MOD_weld.c index fe2d699aea8..b1fa2a7d912 100644 --- a/source/blender/modifiers/intern/MOD_weld.c +++ b/source/blender/modifiers/intern/MOD_weld.c @@ -1735,6 +1735,9 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, uint v1 = me->v1; uint v2 = me->v2; + if (wmd->flag & MOD_WELD_LOOSE_EDGES && (me->flag & ME_LOOSEEDGE) == 0) { + continue; + } while (v1 != vert_dest_map[v1]) { v1 = vert_dest_map[v1]; } @@ -2019,11 +2022,15 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA ob_ptr; PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); + int weld_mode = RNA_enum_get(ptr, "mode"); uiLayoutSetPropSep(layout, true); uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE); uiItemR(layout, ptr, "merge_threshold", 0, IFACE_("Distance"), ICON_NONE); + if (weld_mode == MOD_WELD_MODE_CONNECTED) { + uiItemR(layout, ptr, "loose_edges", 0, NULL, ICON_NONE); + } modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", NULL); modifier_panel_end(layout, ptr); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index dc19508be04..36e5be6a292 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -175,9 +175,11 @@ set(SRC geometry/nodes/node_geo_curve_primitive_star.cc geometry/nodes/node_geo_curve_resample.cc geometry/nodes/node_geo_curve_reverse.cc + geometry/nodes/node_geo_curve_set_handles.cc geometry/nodes/node_geo_curve_subdivide.cc geometry/nodes/node_geo_curve_to_mesh.cc geometry/nodes/node_geo_curve_to_points.cc + geometry/nodes/node_geo_curve_trim.cc geometry/nodes/node_geo_delete_geometry.cc geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_input_material.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index ad3a838f4c0..868fcbb33af 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -62,9 +62,11 @@ void register_node_type_geo_curve_primitive_spiral(void); void register_node_type_geo_curve_primitive_star(void); void register_node_type_geo_curve_resample(void); void register_node_type_geo_curve_reverse(void); +void register_node_type_geo_curve_set_handles(void); void register_node_type_geo_curve_subdivide(void); void register_node_type_geo_curve_to_mesh(void); void register_node_type_geo_curve_to_points(void); +void register_node_type_geo_curve_trim(void); void register_node_type_geo_delete_geometry(void); void register_node_type_geo_edge_split(void); void register_node_type_geo_input_material(void); diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index b85862a0176..00d97b24646 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -31,6 +31,7 @@ */ #include "BLI_enumerable_thread_specific.hh" +#include "BLI_function_ref.hh" #include "BLI_linear_allocator.hh" #include "BLI_map.hh" @@ -267,6 +268,7 @@ class TreeLog { const NodeLog *lookup_node_log(StringRef node_name) const; const NodeLog *lookup_node_log(const bNode &node) const; const TreeLog *lookup_child_log(StringRef node_name) const; + void foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const; }; /** Contains information about an entire geometry nodes evaluation. */ @@ -296,6 +298,7 @@ class ModifierLog { const bNodeSocket &socket); static const NodeLog *find_node_by_spreadsheet_editor_context( const SpaceSpreadsheet &sspreadsheet); + void foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const; private: using LogByTreeContext = Map<const DTreeContext *, TreeLog *>; diff --git a/source/blender/nodes/NOD_math_functions.hh b/source/blender/nodes/NOD_math_functions.hh index f3eb1c24087..9443be820d1 100644 --- a/source/blender/nodes/NOD_math_functions.hh +++ b/source/blender/nodes/NOD_math_functions.hh @@ -266,7 +266,7 @@ inline bool try_dispatch_float_math_fl3_fl3_to_fl3(const NodeVectorMathOperation return dispatch([](float3 a, float3 b) { return float3::cross_high_precision(a, b); }); case NODE_VECTOR_MATH_PROJECT: return dispatch([](float3 a, float3 b) { - float length_squared = float3::dot(a, b); + float length_squared = b.length_squared(); return (length_squared != 0.0) ? (float3::dot(a, b) / length_squared) * b : float3(0.0f); }); case NODE_VECTOR_MATH_REFLECT: diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 73d4a002991..1298acf475d 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -156,7 +156,7 @@ DefNode(CompositorNode, CMP_NODE_HUE_SAT, 0, "HUE_SA DefNode(CompositorNode, CMP_NODE_IMAGE, def_cmp_image, "IMAGE", Image, "Image", "" ) DefNode(CompositorNode, CMP_NODE_R_LAYERS, def_cmp_render_layers, "R_LAYERS", RLayers, "Render Layers", "" ) DefNode(CompositorNode, CMP_NODE_COMPOSITE, def_cmp_composite, "COMPOSITE", Composite, "Composite", "" ) -/* NB: OutputFile node has special rna setup function called in rna_nodetree.c */ +/* NOTE: #OutputFile node has special RNA setup function called in rna_nodetree.c */ DefNode(CompositorNode, CMP_NODE_OUTPUT_FILE, 0, "OUTPUT_FILE", OutputFile, "File Output", "" ) DefNode(CompositorNode, CMP_NODE_TEXTURE, def_texture, "TEXTURE", Texture, "Texture", "" ) DefNode(CompositorNode, CMP_NODE_TRANSLATE, def_cmp_translate, "TRANSLATE", Translate, "Translate", "" ) @@ -300,8 +300,10 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMI DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "") DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "") DefNode(GeometryNode, GEO_NODE_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "CURVE_SUBDIVIDE", CurveSubdivide, "Curve Subdivide", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") +DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") DefNode(GeometryNode, GEO_NODE_CURVE_REVERSE, 0, "CURVE_REVERSE", CurveReverse, "Curve Reverse", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINTS, 0, "CURVE_ENDPOINTS", CurveEndpoints, "Curve Endpoints", "") diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c index 013d196e1c8..d21e0938356 100644 --- a/source/blender/nodes/composite/node_composite_tree.c +++ b/source/blender/nodes/composite/node_composite_tree.c @@ -264,14 +264,15 @@ void ntreeCompositExecTree(Scene *scene, /* *********************************************** */ -/* Update the outputs of the render layer nodes. +/** + * Update the outputs of the render layer nodes. * Since the outputs depend on the render engine, this part is a bit complex: - * - ntreeCompositUpdateRLayers is called and loops over all render layer nodes. + * - #ntreeCompositUpdateRLayers is called and loops over all render layer nodes. * - Each render layer node calls the update function of the * render engine that's used for its scene. * - The render engine calls RE_engine_register_pass for each pass. - * - RE_engine_register_pass calls ntreeCompositRegisterPass,. - * which calls node_cmp_rlayers_register_pass for every render layer node. + * - #RE_engine_register_pass calls #ntreeCompositRegisterPass, + * which calls #node_cmp_rlayers_register_pass for every render layer node. */ void ntreeCompositUpdateRLayers(bNodeTree *ntree) { diff --git a/source/blender/nodes/composite/nodes/node_composite_common.c b/source/blender/nodes/composite/nodes/node_composite_common.c index f5eaaef8a31..61abc80fe93 100644 --- a/source/blender/nodes/composite/nodes/node_composite_common.c +++ b/source/blender/nodes/composite/nodes/node_composite_common.c @@ -36,9 +36,8 @@ void register_node_type_cmp_group(void) { static bNodeType ntype; - /* NB: cannot use sh_node_type_base for node group, because it would map the node type - * to the shared NODE_GROUP integer type id. - */ + /* NOTE: Cannot use sh_node_type_base for node group, because it would map the node type + * to the shared NODE_GROUP integer type id. */ node_type_base_custom( &ntype, "CompositorNodeGroup", "Group", NODE_CLASS_GROUP, NODE_CONST_OUTPUT); ntype.type = NODE_GROUP; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc index 9309863f4e9..368c4025eb7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc @@ -16,6 +16,8 @@ #include "BLI_task.hh" +#include "RNA_enum_types.h" + #include "UI_interface.h" #include "UI_resources.h" @@ -132,6 +134,17 @@ static void geo_node_attribute_math_init(bNodeTree *UNUSED(tree), bNode *node) namespace blender::nodes { +static void geo_node_math_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen) +{ + NodeAttributeMath &node_storage = *(NodeAttributeMath *)node->storage; + const char *name; + bool enum_label = RNA_enum_name(rna_enum_node_math_items, node_storage.operation, &name); + if (!enum_label) { + name = "Unknown"; + } + BLI_strncpy(label, IFACE_(name), maxlen); +} + static void geo_node_attribute_math_update(bNodeTree *UNUSED(ntree), bNode *node) { NodeAttributeMath &node_storage = *(NodeAttributeMath *)node->storage; @@ -296,6 +309,7 @@ void register_node_type_geo_attribute_math() node_type_socket_templates(&ntype, geo_node_attribute_math_in, geo_node_attribute_math_out); ntype.geometry_node_execute = blender::nodes::geo_node_attribute_math_exec; ntype.draw_buttons = geo_node_attribute_math_layout; + node_type_label(&ntype, blender::nodes::geo_node_math_label); node_type_update(&ntype, blender::nodes::geo_node_attribute_math_update); node_type_init(&ntype, geo_node_attribute_math_init); node_type_storage( diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc index 756f93f154f..6a23443d3ab 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc @@ -370,10 +370,12 @@ static void transfer_attribute_nearest(const GeometrySet &src_geometry, break; } case ATTR_DOMAIN_CORNER: { - use_mesh = true; - mesh_indices.reinitialize(tot_samples); - mesh_distances_sq.reinitialize(tot_samples); - get_closest_mesh_corners(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {}); + if (mesh->totloop > 0) { + use_mesh = true; + mesh_indices.reinitialize(tot_samples); + mesh_distances_sq.reinitialize(tot_samples); + get_closest_mesh_corners(*mesh, dst_positions, mesh_indices, mesh_distances_sq, {}); + } break; } default: { diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc index e017786ae89..be70ebdebfe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc @@ -17,6 +17,8 @@ #include "BLI_math_base_safe.h" #include "BLI_task.hh" +#include "RNA_enum_types.h" + #include "UI_interface.h" #include "UI_resources.h" @@ -154,6 +156,20 @@ static CustomDataType operation_get_result_type(const NodeVectorMathOperation op namespace blender::nodes { +static void geo_node_vector_math_label(bNodeTree *UNUSED(ntree), + bNode *node, + char *label, + int maxlen) +{ + NodeAttributeMath &node_storage = *(NodeAttributeMath *)node->storage; + const char *name; + bool enum_label = RNA_enum_name(rna_enum_node_vec_math_items, node_storage.operation, &name); + if (!enum_label) { + name = "Unknown"; + } + BLI_snprintf(label, maxlen, IFACE_("Vector %s"), IFACE_(name)); +} + static void geo_node_attribute_vector_math_update(bNodeTree *UNUSED(ntree), bNode *node) { const NodeAttributeVectorMath *node_storage = (NodeAttributeVectorMath *)node->storage; @@ -549,6 +565,7 @@ void register_node_type_geo_attribute_vector_math() &ntype, geo_node_attribute_vector_math_in, geo_node_attribute_vector_math_out); ntype.geometry_node_execute = blender::nodes::geo_node_attribute_vector_math_exec; ntype.draw_buttons = geo_node_attribute_vector_math_layout; + node_type_label(&ntype, blender::nodes::geo_node_vector_math_label); node_type_update(&ntype, blender::nodes::geo_node_attribute_vector_math_update); node_type_init(&ntype, geo_node_attribute_vector_math_init); node_type_storage( diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc index e167219ea6b..78b5b109419 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc @@ -74,7 +74,7 @@ static std::unique_ptr<CurveEval> create_bezier_segment_curve( if (mode == GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_POSITION) { spline->add_point(start, BezierSpline::HandleType::Align, - start - (start_handle_right - start) * -1.0f, + 2.0f * start - start_handle_right, BezierSpline::HandleType::Align, start_handle_right, 1.0f, @@ -83,7 +83,7 @@ static std::unique_ptr<CurveEval> create_bezier_segment_curve( BezierSpline::HandleType::Align, end_handle_left, BezierSpline::HandleType::Align, - end - (end_handle_left - end) * -1.0f, + 2.0f * end - end_handle_left, 1.0f, 0.0f); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc new file mode 100644 index 00000000000..72bd8ab188d --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc @@ -0,0 +1,149 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_curve_set_handles_in[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {SOCK_STRING, N_("Selection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_set_handles_out[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +static void geo_node_curve_set_handles_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE); +} + +namespace blender::nodes { + +static void geo_node_curve_set_handles_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSetHandles *data = (NodeGeometryCurveSetHandles *)MEM_callocN( + sizeof(NodeGeometryCurveSetHandles), __func__); + + data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; + data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT; + node->storage = data; +} + +static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) +{ + switch (type) { + case GEO_NODE_CURVE_HANDLE_AUTO: + return BezierSpline::HandleType::Auto; + case GEO_NODE_CURVE_HANDLE_ALIGN: + return BezierSpline::HandleType::Align; + case GEO_NODE_CURVE_HANDLE_FREE: + return BezierSpline::HandleType::Free; + case GEO_NODE_CURVE_HANDLE_VECTOR: + return BezierSpline::HandleType::Vector; + } + BLI_assert_unreachable(); + return BezierSpline::HandleType::Auto; +} + +static void geo_node_curve_set_handles_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveSetHandles *node_storage = + (NodeGeometryCurveSetHandles *)params.node().storage; + const GeometryNodeCurveHandleType type = (GeometryNodeCurveHandleType)node_storage->handle_type; + const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode; + + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + if (!geometry_set.has_curve()) { + params.set_output("Curve", geometry_set); + return; + } + + /* Retrieve data for write access so we can avoid new allocations for the handles data. */ + CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); + CurveEval &curve = *curve_component.get_for_write(); + MutableSpan<SplinePtr> splines = curve.splines(); + + const std::string selection_name = params.extract_input<std::string>("Selection"); + GVArray_Typed<bool> selection = curve_component.attribute_get_for_read( + selection_name, ATTR_DOMAIN_POINT, true); + + const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type); + int point_index = 0; + bool has_bezier_spline = false; + for (SplinePtr &spline : splines) { + if (spline->type() != Spline::Type::Bezier) { + point_index += spline->positions().size(); + continue; + } + + BezierSpline &bezier_spline = static_cast<BezierSpline &>(*spline); + if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) { + /* In this case the automatically calculated handle types need to be "baked", because + * they're possibly changing from a type that is calculated automatically to a type that + * is positioned manually. */ + bezier_spline.ensure_auto_handles(); + } + has_bezier_spline = true; + for (int i_point : IndexRange(bezier_spline.size())) { + if (selection[point_index]) { + if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { + bezier_spline.handle_types_left()[i_point] = new_handle_type; + } + if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) { + bezier_spline.handle_types_right()[i_point] = new_handle_type; + } + } + point_index++; + } + bezier_spline.mark_cache_invalid(); + } + + if (!has_bezier_spline) { + params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve")); + } + + params.set_output("Curve", geometry_set); +} +} // namespace blender::nodes + +void register_node_type_geo_curve_set_handles() +{ + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_curve_set_handles_in, geo_node_curve_set_handles_out); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_set_handles_exec; + node_type_init(&ntype, blender::nodes::geo_node_curve_set_handles_init); + node_type_storage(&ntype, + "NodeGeometryCurveSetHandles", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = geo_node_curve_set_handles_layout; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index f1bcb4ed47f..ae5ad4e350b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -47,27 +47,27 @@ static void vert_extrude_to_mesh_data(const Spline &spline, const float3 profile_vert, MutableSpan<MVert> r_verts, MutableSpan<MEdge> r_edges, - int vert_offset, - int edge_offset) + const int vert_offset, + const int edge_offset) { Span<float3> positions = spline.evaluated_positions(); for (const int i : IndexRange(positions.size() - 1)) { - MEdge &edge = r_edges[edge_offset++]; + MEdge &edge = r_edges[edge_offset + i]; edge.v1 = vert_offset + i; edge.v2 = vert_offset + i + 1; edge.flag = ME_LOOSEEDGE; } if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) { - MEdge &edge = r_edges[edge_offset++]; + MEdge &edge = r_edges[edge_offset + spline.evaluated_edges_size() - 1]; edge.v1 = vert_offset; edge.v2 = vert_offset + positions.size() - 1; edge.flag = ME_LOOSEEDGE; } for (const int i : positions.index_range()) { - MVert &vert = r_verts[vert_offset++]; + MVert &vert = r_verts[vert_offset + i]; copy_v3_v3(vert.co, positions[i] + profile_vert); } } @@ -81,14 +81,14 @@ static void mark_edges_sharp(MutableSpan<MEdge> edges) static void spline_extrude_to_mesh_data(const Spline &spline, const Spline &profile_spline, + const int vert_offset, + const int edge_offset, + const int loop_offset, + const int poly_offset, MutableSpan<MVert> r_verts, MutableSpan<MEdge> r_edges, MutableSpan<MLoop> r_loops, - MutableSpan<MPoly> r_polys, - int vert_offset, - int edge_offset, - int loop_offset, - int poly_offset) + MutableSpan<MPoly> r_polys) { const int spline_vert_len = spline.evaluated_points_size(); const int spline_edge_len = spline.evaluated_edges_size(); @@ -111,13 +111,14 @@ static void spline_extrude_to_mesh_data(const Spline &spline, /* Add the edges running along the length of the curve, starting at each profile vertex. */ const int spline_edges_start = edge_offset; for (const int i_profile : IndexRange(profile_vert_len)) { + const int profile_edge_offset = spline_edges_start + i_profile * spline_edge_len; for (const int i_ring : IndexRange(spline_edge_len)) { const int i_next_ring = (i_ring == spline_vert_len - 1) ? 0 : i_ring + 1; const int ring_vert_offset = vert_offset + profile_vert_len * i_ring; const int next_ring_vert_offset = vert_offset + profile_vert_len * i_next_ring; - MEdge &edge = r_edges[edge_offset++]; + MEdge &edge = r_edges[profile_edge_offset + i_ring]; edge.v1 = ring_vert_offset + i_profile; edge.v2 = next_ring_vert_offset + i_profile; edge.flag = ME_EDGEDRAW | ME_EDGERENDER; @@ -125,14 +126,15 @@ static void spline_extrude_to_mesh_data(const Spline &spline, } /* Add the edges running along each profile ring. */ - const int profile_edges_start = edge_offset; + const int profile_edges_start = spline_edges_start + profile_vert_len * spline_edge_len; for (const int i_ring : IndexRange(spline_vert_len)) { const int ring_vert_offset = vert_offset + profile_vert_len * i_ring; + const int ring_edge_offset = profile_edges_start + i_ring * profile_edge_len; for (const int i_profile : IndexRange(profile_edge_len)) { const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1; - MEdge &edge = r_edges[edge_offset++]; + MEdge &edge = r_edges[ring_edge_offset + i_profile]; edge.v1 = ring_vert_offset + i_profile; edge.v2 = ring_vert_offset + i_next_profile; edge.flag = ME_EDGEDRAW | ME_EDGERENDER; @@ -149,29 +151,33 @@ static void spline_extrude_to_mesh_data(const Spline &spline, const int ring_edge_start = profile_edges_start + profile_edge_len * i_ring; const int next_ring_edge_offset = profile_edges_start + profile_edge_len * i_next_ring; + const int ring_poly_offset = poly_offset + i_ring * profile_edge_len; + const int ring_loop_offset = loop_offset + i_ring * profile_edge_len * 4; + for (const int i_profile : IndexRange(profile_edge_len)) { + const int ring_segment_loop_offset = ring_loop_offset + i_profile * 4; const int i_next_profile = (i_profile == profile_vert_len - 1) ? 0 : i_profile + 1; const int spline_edge_start = spline_edges_start + spline_edge_len * i_profile; const int next_spline_edge_start = spline_edges_start + spline_edge_len * i_next_profile; - MPoly &poly = r_polys[poly_offset++]; - poly.loopstart = loop_offset; + MPoly &poly = r_polys[ring_poly_offset + i_profile]; + poly.loopstart = ring_segment_loop_offset; poly.totloop = 4; poly.flag = ME_SMOOTH; - MLoop &loop_a = r_loops[loop_offset++]; + MLoop &loop_a = r_loops[ring_segment_loop_offset]; loop_a.v = ring_vert_offset + i_profile; - loop_a.e = ring_edge_start + i_profile; - MLoop &loop_b = r_loops[loop_offset++]; - loop_b.v = ring_vert_offset + i_next_profile; - loop_b.e = next_spline_edge_start + i_ring; - MLoop &loop_c = r_loops[loop_offset++]; + loop_a.e = spline_edge_start + i_ring; + MLoop &loop_b = r_loops[ring_segment_loop_offset + 1]; + loop_b.v = next_ring_vert_offset + i_profile; + loop_b.e = next_ring_edge_offset + i_profile; + MLoop &loop_c = r_loops[ring_segment_loop_offset + 2]; loop_c.v = next_ring_vert_offset + i_next_profile; - loop_c.e = next_ring_edge_offset + i_profile; - MLoop &loop_d = r_loops[loop_offset++]; - loop_d.v = next_ring_vert_offset + i_profile; - loop_d.e = spline_edge_start + i_ring; + loop_c.e = next_spline_edge_start + i_ring; + MLoop &loop_d = r_loops[ring_segment_loop_offset + 3]; + loop_d.v = ring_vert_offset + i_next_profile; + loop_d.e = ring_edge_start + i_profile; } } @@ -185,11 +191,11 @@ static void spline_extrude_to_mesh_data(const Spline &spline, for (const int i_ring : IndexRange(spline_vert_len)) { float4x4 point_matrix = float4x4::from_normalized_axis_data( positions[i_ring], normals[i_ring], tangents[i_ring]); - point_matrix.apply_scale(radii[i_ring]); + const int ring_vert_start = vert_offset + i_ring * profile_vert_len; for (const int i_profile : IndexRange(profile_vert_len)) { - MVert &vert = r_verts[vert_offset++]; + MVert &vert = r_verts[ring_vert_start + i_profile]; copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]); } } @@ -275,7 +281,7 @@ static ResultOffsets calculate_result_offsets(Span<SplinePtr> profiles, Span<Spl * Although it would be a sensible decision to use the better topology information available while * generating the mesh to also generate the normals, that work may wasted if the output mesh is * changed anyway in a way that affects the normals. So currently this code uses the safer / - * simpler solution of not calculating normals. + * simpler solution of deferring normal calculation to the rest of Blender. */ static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &profile) { @@ -303,14 +309,14 @@ static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &pr const int i_mesh = spline_start_index + i_profile; spline_extrude_to_mesh_data(*curves[i_spline], *profiles[i_profile], - {mesh->mvert, mesh->totvert}, - {mesh->medge, mesh->totedge}, - {mesh->mloop, mesh->totloop}, - {mesh->mpoly, mesh->totpoly}, offsets.vert[i_mesh], offsets.edge[i_mesh], offsets.loop[i_mesh], - offsets.poly[i_mesh]); + offsets.poly[i_mesh], + {mesh->mvert, mesh->totvert}, + {mesh->medge, mesh->totedge}, + {mesh->mloop, mesh->totloop}, + {mesh->mpoly, mesh->totpoly}); } }); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc new file mode 100644 index 00000000000..f9415c6d27b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -0,0 +1,410 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" +#include "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +using blender::attribute_math::mix2; + +static bNodeSocketTemplate geo_node_curve_trim_in[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {SOCK_FLOAT, N_("Start"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("End"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Start"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 10000.0f, PROP_DISTANCE}, + {SOCK_FLOAT, N_("End"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 10000.0f, PROP_DISTANCE}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_curve_trim_out[] = { + {SOCK_GEOMETRY, N_("Curve")}, + {-1, ""}, +}; + +static void geo_node_curve_trim_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static void geo_node_curve_trim_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveTrim *data = (NodeGeometryCurveTrim *)MEM_callocN(sizeof(NodeGeometryCurveTrim), + __func__); + + data->mode = GEO_NODE_CURVE_INTERPOLATE_FACTOR; + node->storage = data; +} + +static void geo_node_curve_trim_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)node->storage; + const GeometryNodeCurveInterpolateMode mode = (GeometryNodeCurveInterpolateMode) + node_storage.mode; + + bNodeSocket *start_fac = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *end_fac = start_fac->next; + bNodeSocket *start_len = end_fac->next; + bNodeSocket *end_len = start_len->next; + + nodeSetSocketAvailability(start_fac, mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR); + nodeSetSocketAvailability(end_fac, mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR); + nodeSetSocketAvailability(start_len, mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH); + nodeSetSocketAvailability(end_len, mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH); +} + +namespace blender::nodes { + +struct TrimLocation { + /* Control point index at the start side of the trim location. */ + int left_index; + /* Control point index at the end of the trim location's segment. */ + int right_index; + /* The factor between the left and right indices. */ + float factor; +}; + +template<typename T> +static void shift_slice_to_start(MutableSpan<T> data, const int start_index, const int size) +{ + BLI_assert(start_index + size - 1 <= data.size()); + memmove(data.data(), &data[start_index], sizeof(T) * size); +} + +/* Shift slice to start of span and modifies start and end data. */ +template<typename T> +static void linear_trim_data(const TrimLocation &start, + const TrimLocation &end, + MutableSpan<T> data) +{ + const int size = end.right_index - start.left_index + 1; + + if (start.left_index > 0) { + shift_slice_to_start<T>(data, start.left_index, size); + } + + const T start_data = mix2<T>(start.factor, data.first(), data[1]); + const T end_data = mix2<T>(end.factor, data[size - 2], data[size - 1]); + + data.first() = start_data; + data[size - 1] = end_data; +} + +/** + * Identical operation as #linear_trim_data, but copy data to a new #MutableSpan rather than + * modifying the original data. + */ +template<typename T> +static void linear_trim_to_output_data(const TrimLocation &start, + const TrimLocation &end, + Span<T> src, + MutableSpan<T> dst) +{ + const int size = end.right_index - start.left_index + 1; + + const T start_data = mix2<T>(start.factor, src[start.left_index], src[start.right_index]); + const T end_data = mix2<T>(end.factor, src[end.left_index], src[end.right_index]); + + dst.copy_from(src.slice(start.left_index, size)); + dst.first() = start_data; + dst.last() = end_data; +} + +/* Look up the control points to the left and right of factor, and get the factor between them. */ +static TrimLocation lookup_control_point_position(const Spline::LookupResult &lookup, + Span<int> control_point_offsets) +{ + const int *left_offset = std::lower_bound( + control_point_offsets.begin(), control_point_offsets.end(), lookup.evaluated_index); + const int index = left_offset - control_point_offsets.begin(); + const int left = control_point_offsets[index] > lookup.evaluated_index ? index - 1 : index; + const int right = left + 1; + + const float factor = std::clamp( + (lookup.evaluated_index + lookup.factor - control_point_offsets[left]) / + (control_point_offsets[right] - control_point_offsets[left]), + 0.0f, + 1.0f); + + return {left, right, factor}; +} + +static void trim_poly_spline(Spline &spline, + const Spline::LookupResult &start_lookup, + const Spline::LookupResult &end_lookup) +{ + /* Poly splines have a 1 to 1 mapping between control points and evaluated points. */ + const TrimLocation start = { + start_lookup.evaluated_index, start_lookup.next_evaluated_index, start_lookup.factor}; + const TrimLocation end = { + end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor}; + + const int size = end.right_index - start.left_index + 1; + + linear_trim_data<float3>(start, end, spline.positions()); + linear_trim_data<float>(start, end, spline.radii()); + linear_trim_data<float>(start, end, spline.tilts()); + + spline.attributes.foreach_attribute( + [&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) { + std::optional<GMutableSpan> src = spline.attributes.get_for_write(name); + BLI_assert(src); + attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { + using T = decltype(dummy); + linear_trim_data<T>(start, end, src->typed<T>()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + + spline.resize(size); +} + +/** + * Trim NURB splines by converting to a poly spline. + */ +static PolySpline trim_nurbs_spline(const Spline &spline, + const Spline::LookupResult &start_lookup, + const Spline::LookupResult &end_lookup) +{ + /* Since this outputs a poly spline, the evaluated indices are the control point indices. */ + const TrimLocation start = { + start_lookup.evaluated_index, start_lookup.next_evaluated_index, start_lookup.factor}; + const TrimLocation end = { + end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor}; + + const int size = end.right_index - start.left_index + 1; + + /* Create poly spline and copy trimmed data to it. */ + PolySpline new_spline; + new_spline.resize(size); + + /* Copy generic attribute data. */ + spline.attributes.foreach_attribute( + [&](StringRefNull name, const AttributeMetaData &meta_data) { + std::optional<GSpan> src = spline.attributes.get_for_read(name); + BLI_assert(src); + if (!new_spline.attributes.create(name, meta_data.data_type)) { + BLI_assert_unreachable(); + return false; + } + std::optional<GMutableSpan> dst = new_spline.attributes.get_for_write(name); + BLI_assert(dst); + + attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { + using T = decltype(dummy); + GVArray_Typed<T> eval_data = spline.interpolate_to_evaluated<T>(src->typed<T>()); + linear_trim_to_output_data<T>( + start, end, eval_data->get_internal_span(), dst->typed<T>()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + + linear_trim_to_output_data<float3>( + start, end, spline.evaluated_positions(), new_spline.positions()); + + GVArray_Typed<float> evaluated_radii = spline.interpolate_to_evaluated(spline.radii()); + linear_trim_to_output_data<float>( + start, end, evaluated_radii->get_internal_span(), new_spline.radii()); + + GVArray_Typed<float> evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts()); + linear_trim_to_output_data<float>( + start, end, evaluated_tilts->get_internal_span(), new_spline.tilts()); + + return new_spline; +} + +/** + * Trim Bezier splines by adjusting the first and last handles + * and control points to maintain the original shape. + */ +static void trim_bezier_spline(Spline &spline, + const Spline::LookupResult &start_lookup, + const Spline::LookupResult &end_lookup) +{ + BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline); + Span<int> control_offsets = bezier_spline.control_point_offsets(); + + const TrimLocation start = lookup_control_point_position(start_lookup, control_offsets); + TrimLocation end = lookup_control_point_position(end_lookup, control_offsets); + + /* The number of control points in the resulting spline. */ + const int size = end.right_index - start.left_index + 1; + + /* Trim the spline attributes. Done before end.factor recalculation as it needs + * the original end.factor value. */ + linear_trim_data<float>(start, end, bezier_spline.radii()); + linear_trim_data<float>(start, end, bezier_spline.tilts()); + spline.attributes.foreach_attribute( + [&](StringRefNull name, const AttributeMetaData &UNUSED(meta_data)) { + std::optional<GMutableSpan> src = spline.attributes.get_for_write(name); + BLI_assert(src); + attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { + using T = decltype(dummy); + linear_trim_data<T>(start, end, src->typed<T>()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + + /* Recalculate end.factor if the size is two, because the adjustment in the + * position of the control point of the spline to the left of the new end point will change the + * factor between them. */ + if (size == 2) { + if (start_lookup.factor == 1.0f) { + end.factor = 0.0f; + } + else { + end.factor = (end_lookup.evaluated_index + end_lookup.factor - + (start_lookup.evaluated_index + start_lookup.factor)) / + (control_offsets[end.right_index] - + (start_lookup.evaluated_index + start_lookup.factor)); + end.factor = std::clamp(end.factor, 0.0f, 1.0f); + } + } + + BezierSpline::InsertResult start_point = bezier_spline.calculate_segment_insertion( + start.left_index, start.right_index, start.factor); + + /* Update the start control point parameters so they are used calculating the new end point. */ + bezier_spline.positions()[start.left_index] = start_point.position; + bezier_spline.handle_positions_right()[start.left_index] = start_point.right_handle; + bezier_spline.handle_positions_left()[start.right_index] = start_point.handle_next; + + const BezierSpline::InsertResult end_point = bezier_spline.calculate_segment_insertion( + end.left_index, end.right_index, end.factor); + + /* If size is two, then the start point right handle needs to change to reflect the end point + * previous handle update. */ + if (size == 2) { + start_point.right_handle = end_point.handle_prev; + } + + /* Shift control point position data to start at beginning of array. */ + if (start.left_index > 0) { + shift_slice_to_start(bezier_spline.positions(), start.left_index, size); + shift_slice_to_start(bezier_spline.handle_positions_left(), start.left_index, size); + shift_slice_to_start(bezier_spline.handle_positions_right(), start.left_index, size); + } + + bezier_spline.positions().first() = start_point.position; + bezier_spline.positions()[size - 1] = end_point.position; + + bezier_spline.handle_positions_left().first() = start_point.left_handle; + bezier_spline.handle_positions_left()[size - 1] = end_point.left_handle; + + bezier_spline.handle_positions_right().first() = start_point.right_handle; + bezier_spline.handle_positions_right()[size - 1] = end_point.right_handle; + + /* If there is at least one control point between the endpoints, update the control + * point handle to the right of the start point and to the left of the end point. */ + if (size > 2) { + bezier_spline.handle_positions_left()[start.right_index - start.left_index] = + start_point.handle_next; + bezier_spline.handle_positions_right()[end.left_index - start.left_index] = + end_point.handle_prev; + } + + bezier_spline.resize(size); +} + +static void geo_node_curve_trim_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)params.node().storage; + const GeometryNodeCurveInterpolateMode mode = (GeometryNodeCurveInterpolateMode) + node_storage.mode; + + GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + if (!geometry_set.has_curve()) { + params.set_output("Curve", std::move(geometry_set)); + return; + } + + CurveComponent &curve_component = geometry_set.get_component_for_write<CurveComponent>(); + CurveEval &curve = *curve_component.get_for_write(); + MutableSpan<SplinePtr> splines = curve.splines(); + + const float start = mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR ? + params.extract_input<float>("Start") : + params.extract_input<float>("Start_001"); + const float end = mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR ? + params.extract_input<float>("End") : + params.extract_input<float>("End_001"); + + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + Spline &spline = *splines[i]; + + /* Currently this node doesn't support cyclic splines, it could in the future though. */ + if (spline.is_cyclic()) { + continue; + } + + /* Return a spline with one point instead of implicitly + * reversing the spline or switching the parameters. */ + if (end < start) { + spline.resize(1); + continue; + } + + const Spline::LookupResult start_lookup = + (mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH) ? + spline.lookup_evaluated_length(std::clamp(start, 0.0f, spline.length())) : + spline.lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f)); + const Spline::LookupResult end_lookup = + (mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH) ? + spline.lookup_evaluated_length(std::clamp(end, 0.0f, spline.length())) : + spline.lookup_evaluated_factor(std::clamp(end, 0.0f, 1.0f)); + + switch (spline.type()) { + case Spline::Type::Bezier: + trim_bezier_spline(spline, start_lookup, end_lookup); + break; + case Spline::Type::Poly: + trim_poly_spline(spline, start_lookup, end_lookup); + break; + case Spline::Type::NURBS: + splines[i] = std::make_unique<PolySpline>( + trim_nurbs_spline(spline, start_lookup, end_lookup)); + break; + } + splines[i]->mark_cache_invalid(); + } + }); + + params.set_output("Curve", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_trim() +{ + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_CURVE_TRIM, "Curve Trim", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_curve_trim_in, geo_node_curve_trim_out); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_trim_exec; + ntype.draw_buttons = geo_node_curve_trim_layout; + node_type_storage( + &ntype, "NodeGeometryCurveTrim", node_free_standard_storage, node_copy_standard_storage); + node_type_init(&ntype, geo_node_curve_trim_init); + node_type_update(&ntype, geo_node_curve_trim_update); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc index 3d5557d4049..f7279cf7524 100644 --- a/source/blender/nodes/intern/derived_node_tree.cc +++ b/source/blender/nodes/intern/derived_node_tree.cc @@ -73,7 +73,9 @@ void DerivedNodeTree::destruct_context_recursively(DTreeContext *context) context->~DTreeContext(); } -/* Returns true if there are any cycles in the node tree. */ +/** + * \return True when there is a link cycle. Unavailable sockets are ignored. + */ bool DerivedNodeTree::has_link_cycles() const { for (const NodeTreeRef *tree_ref : used_node_tree_refs_) { diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index 3024cc51cad..7487f11d77d 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -99,6 +99,13 @@ SocketLog &ModifierLog::lookup_or_add_socket_log(LogByTreeContext &log_by_tree_c return socket_log; } +void ModifierLog::foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const +{ + if (root_tree_logs_) { + root_tree_logs_->foreach_node_log(fn); + } +} + const NodeLog *TreeLog::lookup_node_log(StringRef node_name) const { const destruct_ptr<NodeLog> *node_log = node_logs_.lookup_ptr_as(node_name); @@ -122,6 +129,17 @@ const TreeLog *TreeLog::lookup_child_log(StringRef node_name) const return tree_log->get(); } +void TreeLog::foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const +{ + for (auto node_log : node_logs_.items()) { + fn(*node_log.value); + } + + for (auto child : child_logs_.items()) { + child.value->foreach_node_log(fn); + } +} + const SocketLog *NodeLog::lookup_socket_log(eNodeSocketInOut in_out, int index) const { BLI_assert(index >= 0); diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 9ce9d6fc273..641d02af902 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -423,7 +423,13 @@ static bool has_link_cycles_recursive(const NodeRef &node, is_in_stack[node_id] = true; for (const OutputSocketRef *from_socket : node.outputs()) { + if (!from_socket->is_available()) { + continue; + } for (const InputSocketRef *to_socket : from_socket->directly_linked_sockets()) { + if (!to_socket->is_available()) { + continue; + } const NodeRef &to_node = to_socket->node(); if (has_link_cycles_recursive(to_node, visited, is_in_stack)) { return true; @@ -435,6 +441,9 @@ static bool has_link_cycles_recursive(const NodeRef &node, return false; } +/** + * \return True when there is a link cycle. Unavailable sockets are ignored. + */ bool NodeTreeRef::has_link_cycles() const { const int node_amount = nodes_by_id_.size(); diff --git a/source/blender/nodes/shader/nodes/node_shader_common.c b/source/blender/nodes/shader/nodes/node_shader_common.c index a864bef60d9..4df5add7151 100644 --- a/source/blender/nodes/shader/nodes/node_shader_common.c +++ b/source/blender/nodes/shader/nodes/node_shader_common.c @@ -229,9 +229,9 @@ void register_node_type_sh_group(void) { static bNodeType ntype; - /* NB: cannot use sh_node_type_base for node group, because it would map the node type - * to the shared NODE_GROUP integer type id. - */ + /* NOTE: cannot use #sh_node_type_base for node group, because it would map the node type + * to the shared #NODE_GROUP integer type id. */ + node_type_base_custom(&ntype, "ShaderNodeGroup", "Group", NODE_CLASS_GROUP, NODE_CONST_OUTPUT); ntype.type = NODE_GROUP; ntype.poll = sh_node_poll_default; diff --git a/source/blender/nodes/texture/nodes/node_texture_common.c b/source/blender/nodes/texture/nodes/node_texture_common.c index b87720be5b0..2de64779ea6 100644 --- a/source/blender/nodes/texture/nodes/node_texture_common.c +++ b/source/blender/nodes/texture/nodes/node_texture_common.c @@ -158,9 +158,9 @@ void register_node_type_tex_group(void) { static bNodeType ntype; - /* NB: cannot use sh_node_type_base for node group, because it would map the node type - * to the shared NODE_GROUP integer type id. - */ + /* NOTE: Cannot use #sh_node_type_base for node group, because it would map the node type + * to the shared #NODE_GROUP integer type id. */ + node_type_base_custom(&ntype, "TextureNodeGroup", "Group", NODE_CLASS_GROUP, NODE_CONST_OUTPUT); ntype.type = NODE_GROUP; ntype.poll = tex_node_poll_default; diff --git a/source/blender/nodes/texture/nodes/node_texture_rotate.c b/source/blender/nodes/texture/nodes/node_texture_rotate.c index 06eb632378c..9985499772e 100644 --- a/source/blender/nodes/texture/nodes/node_texture_rotate.c +++ b/source/blender/nodes/texture/nodes/node_texture_rotate.c @@ -47,7 +47,7 @@ static void rotate(float new_co[3], float a, const float ax[3], const float co[3 float cos_a = cosf(a * (float)(2 * M_PI)); float sin_a = sinf(a * (float)(2 * M_PI)); - // x' = xcosa + n(n.x)(1-cosa) + (x*n)sina + /* `x' = xcosa + n(n.x)(1-cosa) + (x*n)sina`. */ mul_v3_v3fl(perp, co, cos_a); mul_v3_v3fl(para, ax, dot_v3v3(co, ax) * (1 - cos_a)); diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h index 84d804f8bdf..43a73363c98 100644 --- a/source/blender/python/BPY_extern.h +++ b/source/blender/python/BPY_extern.h @@ -30,7 +30,7 @@ struct PathResolvedRNA; struct Text; /* defined in DNA_text_types.h */ struct bConstraint; /* DNA_constraint_types.h */ struct bConstraintOb; /* DNA_constraint_types.h */ -struct bConstraintTarget; /* DNA_constraint_types.h*/ +struct bConstraintTarget; /* DNA_constraint_types.h */ struct bContext; struct bContextDataResult; struct bPythonConstraint; /* DNA_constraint_types.h */ diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index bfdc763e4df..024900db691 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -1836,7 +1836,7 @@ static int BPy_IDArray_ass_slice(BPy_IDArray *self, int begin, int end, PyObject /* NOTE: we count on int/float being the same size here */ vec = MEM_mallocN(alloc_len, "array assignment"); - if (PyC_AsArray(vec, seq, size, py_type, is_double, "slice assignment: ") == -1) { + if (PyC_AsArray(vec, elem_size, seq, size, py_type, "slice assignment: ") == -1) { MEM_freeN(vec); return -1; } diff --git a/source/blender/python/generic/imbuf_py_api.c b/source/blender/python/generic/imbuf_py_api.c index 08ddef992a3..87afc40330c 100644 --- a/source/blender/python/generic/imbuf_py_api.c +++ b/source/blender/python/generic/imbuf_py_api.c @@ -242,7 +242,7 @@ static int py_imbuf_ppm_set(Py_ImBuf *self, PyObject *value, void *UNUSED(closur PY_IMBUF_CHECK_INT(self); double ppm[2]; - if (PyC_AsArray(ppm, value, 2, &PyFloat_Type, true, "ppm") == -1) { + if (PyC_AsArray(ppm, sizeof(*ppm), value, 2, &PyFloat_Type, "ppm") == -1) { return -1; } diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c index a27ef30c849..51e20a31ba8 100644 --- a/source/blender/python/generic/py_capi_utils.c +++ b/source/blender/python/generic/py_capi_utils.c @@ -57,10 +57,10 @@ /* array utility function */ int PyC_AsArray_FAST(void *array, + const size_t array_item_size, PyObject *value_fast, const Py_ssize_t length, const PyTypeObject *type, - const bool is_double, const char *error_prefix) { const Py_ssize_t value_len = PySequence_Fast_GET_SIZE(value_fast); @@ -80,30 +80,97 @@ int PyC_AsArray_FAST(void *array, /* for each type */ if (type == &PyFloat_Type) { - if (is_double) { - double *array_double = array; - for (i = 0; i < length; i++) { - array_double[i] = PyFloat_AsDouble(value_fast_items[i]); + switch (array_item_size) { + case sizeof(double): { + double *array_double = array; + for (i = 0; i < length; i++) { + array_double[i] = PyFloat_AsDouble(value_fast_items[i]); + } + break; } - } - else { - float *array_float = array; - for (i = 0; i < length; i++) { - array_float[i] = PyFloat_AsDouble(value_fast_items[i]); + case sizeof(float): { + float *array_float = array; + for (i = 0; i < length; i++) { + array_float[i] = PyFloat_AsDouble(value_fast_items[i]); + } + break; + } + default: { + /* Internal error. */ + BLI_assert_unreachable(); } } } else if (type == &PyLong_Type) { - /* could use is_double for 'long int' but no use now */ - int *array_int = array; - for (i = 0; i < length; i++) { - array_int[i] = PyC_Long_AsI32(value_fast_items[i]); + switch (array_item_size) { + case sizeof(int64_t): { + int64_t *array_int = array; + for (i = 0; i < length; i++) { + array_int[i] = PyC_Long_AsI64(value_fast_items[i]); + } + break; + } + case sizeof(int32_t): { + int32_t *array_int = array; + for (i = 0; i < length; i++) { + array_int[i] = PyC_Long_AsI32(value_fast_items[i]); + } + break; + } + case sizeof(int16_t): { + int16_t *array_int = array; + for (i = 0; i < length; i++) { + array_int[i] = PyC_Long_AsI16(value_fast_items[i]); + } + break; + } + case sizeof(int8_t): { + int8_t *array_int = array; + for (i = 0; i < length; i++) { + array_int[i] = PyC_Long_AsI8(value_fast_items[i]); + } + break; + } + default: { + /* Internal error. */ + BLI_assert_unreachable(); + } } } else if (type == &PyBool_Type) { - bool *array_bool = array; - for (i = 0; i < length; i++) { - array_bool[i] = (PyLong_AsLong(value_fast_items[i]) != 0); + switch (array_item_size) { + case sizeof(int64_t): { + int64_t *array_bool = array; + for (i = 0; i < length; i++) { + array_bool[i] = (PyLong_AsLong(value_fast_items[i]) != 0); + } + break; + } + case sizeof(int32_t): { + int32_t *array_bool = array; + for (i = 0; i < length; i++) { + array_bool[i] = (PyLong_AsLong(value_fast_items[i]) != 0); + } + break; + } + case sizeof(int16_t): { + int16_t *array_bool = array; + for (i = 0; i < length; i++) { + array_bool[i] = (PyLong_AsLong(value_fast_items[i]) != 0); + } + break; + } + case sizeof(int8_t): { + int8_t *array_bool = array; + for (i = 0; i < length; i++) { + array_bool[i] = (PyLong_AsLong(value_fast_items[i]) != 0); + } + break; + } + default: { + /* Internal error. */ + BLI_assert_unreachable(); + } } } else { @@ -123,10 +190,10 @@ int PyC_AsArray_FAST(void *array, } int PyC_AsArray(void *array, + const size_t array_item_size, PyObject *value, const Py_ssize_t length, const PyTypeObject *type, - const bool is_double, const char *error_prefix) { PyObject *value_fast; @@ -136,11 +203,111 @@ int PyC_AsArray(void *array, return -1; } - ret = PyC_AsArray_FAST(array, value_fast, length, type, is_double, error_prefix); + ret = PyC_AsArray_FAST(array, array_item_size, value_fast, length, type, error_prefix); Py_DECREF(value_fast); return ret; } +static int PyC_AsArray_Multi_impl(void **array_p, + const size_t array_item_size, + PyObject *value, + const int *dims, + const int dims_len, + const PyTypeObject *type, + const char *error_prefix); + +static int PyC_AsArray_Multi_FAST_impl(void **array_p, + const size_t array_item_size, + PyObject *value_fast, + const int *dims, + const int dims_len, + const PyTypeObject *type, + const char *error_prefix) +{ + const Py_ssize_t value_len = PySequence_Fast_GET_SIZE(value_fast); + const int length = dims[0]; + + if (dims_len == 1) { + if (PyC_AsArray_FAST(*array_p, array_item_size, value_fast, length, type, error_prefix) == + -1) { + return -1; + } + *array_p = POINTER_OFFSET(*array_p, array_item_size * length); + } + else { + if (value_len != length) { + PyErr_Format(PyExc_TypeError, + "%.200s: invalid sequence length. expected %d, got %d", + error_prefix, + length, + value_len); + return -1; + } + + PyObject **value_fast_items = PySequence_Fast_ITEMS(value_fast); + const int *dims_next = dims + 1; + const int dims_next_len = dims_len - 1; + + for (int i = 0; i < length; i++) { + if (PyC_AsArray_Multi_impl(array_p, + array_item_size, + value_fast_items[i], + dims_next, + dims_next_len, + type, + error_prefix) == -1) { + return -1; + } + } + } + return 0; +} + +static int PyC_AsArray_Multi_impl(void **array_p, + const size_t array_item_size, + PyObject *value, + const int *dims, + const int dims_len, + const PyTypeObject *type, + const char *error_prefix) +{ + PyObject *value_fast; + int ret; + + if (!(value_fast = PySequence_Fast(value, error_prefix))) { + return -1; + } + + ret = PyC_AsArray_Multi_FAST_impl( + array_p, array_item_size, value_fast, dims, dims_len, type, error_prefix); + Py_DECREF(value_fast); + return ret; +} + +int PyC_AsArray_Multi_FAST(void *array, + const size_t array_item_size, + PyObject *value_fast, + const int *dims, + const int dims_len, + const PyTypeObject *type, + const char *error_prefix) +{ + return PyC_AsArray_Multi_FAST_impl( + &array, array_item_size, value_fast, dims, dims_len, type, error_prefix); +} + +int PyC_AsArray_Multi(void *array, + const size_t array_item_size, + PyObject *value, + const int *dims, + const int dims_len, + const PyTypeObject *type, + const char *error_prefix) +{ + return PyC_AsArray_Multi_impl( + &array, array_item_size, value, dims, dims_len, type, error_prefix); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -198,6 +365,108 @@ PyObject *PyC_Tuple_PackArray_Bool(const bool *array, uint len) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Typed Tuple Packing (Multi-Dimensional) + * \{ */ + +static PyObject *PyC_Tuple_PackArray_Multi_F32_impl(const float **array_p, + const int dims[], + const int dims_len) +{ + const int len = dims[0]; + if (dims_len == 1) { + PyObject *tuple = PyC_Tuple_PackArray_F32(*array_p, len); + *array_p = (*array_p) + len; + return tuple; + } + PyObject *tuple = PyTuple_New(dims[0]); + const int *dims_next = dims + 1; + const int dims_next_len = dims_len - 1; + for (uint i = 0; i < len; i++) { + PyTuple_SET_ITEM( + tuple, i, PyC_Tuple_PackArray_Multi_F32_impl(array_p, dims_next, dims_next_len)); + } + return tuple; +} +PyObject *PyC_Tuple_PackArray_Multi_F32(const float *array, const int dims[], const int dims_len) +{ + return PyC_Tuple_PackArray_Multi_F32_impl(&array, dims, dims_len); +} + +static PyObject *PyC_Tuple_PackArray_Multi_F64_impl(const double **array_p, + const int dims[], + const int dims_len) +{ + const int len = dims[0]; + if (dims_len == 1) { + PyObject *tuple = PyC_Tuple_PackArray_F64(*array_p, len); + *array_p = (*array_p) + len; + return tuple; + } + PyObject *tuple = PyTuple_New(dims[0]); + const int *dims_next = dims + 1; + const int dims_next_len = dims_len - 1; + for (uint i = 0; i < len; i++) { + PyTuple_SET_ITEM( + tuple, i, PyC_Tuple_PackArray_Multi_F64_impl(array_p, dims_next, dims_next_len)); + } + return tuple; +} +PyObject *PyC_Tuple_PackArray_Multi_F64(const double *array, const int dims[], const int dims_len) +{ + return PyC_Tuple_PackArray_Multi_F64_impl(&array, dims, dims_len); +} + +static PyObject *PyC_Tuple_PackArray_Multi_I32_impl(const int **array_p, + const int dims[], + const int dims_len) +{ + const int len = dims[0]; + if (dims_len == 1) { + PyObject *tuple = PyC_Tuple_PackArray_I32(*array_p, len); + *array_p = (*array_p) + len; + return tuple; + } + PyObject *tuple = PyTuple_New(dims[0]); + const int *dims_next = dims + 1; + const int dims_next_len = dims_len - 1; + for (uint i = 0; i < len; i++) { + PyTuple_SET_ITEM( + tuple, i, PyC_Tuple_PackArray_Multi_I32_impl(array_p, dims_next, dims_next_len)); + } + return tuple; +} +PyObject *PyC_Tuple_PackArray_Multi_I32(const int *array, const int dims[], const int dims_len) +{ + return PyC_Tuple_PackArray_Multi_I32_impl(&array, dims, dims_len); +} + +static PyObject *PyC_Tuple_PackArray_Multi_Bool_impl(const bool **array_p, + const int dims[], + const int dims_len) +{ + const int len = dims[0]; + if (dims_len == 1) { + PyObject *tuple = PyC_Tuple_PackArray_Bool(*array_p, len); + *array_p = (*array_p) + len; + return tuple; + } + PyObject *tuple = PyTuple_New(dims[0]); + const int *dims_next = dims + 1; + const int dims_next_len = dims_len - 1; + for (uint i = 0; i < len; i++) { + PyTuple_SET_ITEM( + tuple, i, PyC_Tuple_PackArray_Multi_Bool_impl(array_p, dims_next, dims_next_len)); + } + return tuple; +} +PyObject *PyC_Tuple_PackArray_Multi_Bool(const bool *array, const int dims[], const int dims_len) +{ + return PyC_Tuple_PackArray_Multi_Bool_impl(&array, dims, dims_len); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Tuple/List Filling * \{ */ @@ -525,7 +794,7 @@ PyObject *PyC_FrozenSetFromStrings(const char **strings) * * Implementation - we can't actually prepend the existing exception, * because it could have _any_ arguments given to it, so instead we get its - * ``__str__`` output and raise our own exception including it. + * `__str__` output and raise our own exception including it. */ PyObject *PyC_Err_Format_Prefix(PyObject *exception_type_prefix, const char *format, ...) { diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h index 842e1482c06..1591413530c 100644 --- a/source/blender/python/generic/py_capi_utils.h +++ b/source/blender/python/generic/py_capi_utils.h @@ -42,18 +42,34 @@ void PyC_Err_PrintWithFunc(PyObject *py_func); void PyC_FileAndNum(const char **r_filename, int *r_lineno); void PyC_FileAndNum_Safe(const char **r_filename, int *r_lineno); /* checks python is running */ int PyC_AsArray_FAST(void *array, + const size_t array_elem_size, PyObject *value_fast, const Py_ssize_t length, const PyTypeObject *type, - const bool is_double, const char *error_prefix); int PyC_AsArray(void *array, + const size_t array_elem_size, PyObject *value, const Py_ssize_t length, const PyTypeObject *type, - const bool is_double, const char *error_prefix); +int PyC_AsArray_Multi_FAST(void *array, + const size_t array_item_size, + PyObject *value_fast, + const int *dims, + const int dims_len, + const PyTypeObject *type, + const char *error_prefix); + +int PyC_AsArray_Multi(void *array, + const size_t array_item_size, + PyObject *value, + const int *dims, + const int dims_len, + const PyTypeObject *type, + const char *error_prefix); + PyObject *PyC_Tuple_PackArray_F32(const float *array, uint len); PyObject *PyC_Tuple_PackArray_F64(const double *array, uint len); PyObject *PyC_Tuple_PackArray_I32(const int *array, uint len); @@ -71,6 +87,11 @@ PyObject *PyC_Tuple_PackArray_Bool(const bool *array, uint len); #define PyC_Tuple_Pack_Bool(...) \ PyC_Tuple_PackArray_Bool(((const bool[]){__VA_ARGS__}), VA_NARGS_COUNT(__VA_ARGS__)) +PyObject *PyC_Tuple_PackArray_Multi_F32(const float *array, const int dims[], const int dims_len); +PyObject *PyC_Tuple_PackArray_Multi_F64(const double *array, const int dims[], const int dims_len); +PyObject *PyC_Tuple_PackArray_Multi_I32(const int *array, const int dims[], const int dims_len); +PyObject *PyC_Tuple_PackArray_Multi_Bool(const bool *array, const int dims[], const int dims_len); + void PyC_Tuple_Fill(PyObject *tuple, PyObject *value); void PyC_List_Fill(PyObject *list, PyObject *value); diff --git a/source/blender/python/gpu/gpu_py.c b/source/blender/python/gpu/gpu_py.c index 7aea940da94..e6ba46b2b05 100644 --- a/source/blender/python/gpu/gpu_py.c +++ b/source/blender/python/gpu/gpu_py.c @@ -17,8 +17,8 @@ /** \file * \ingroup bpygpu * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> diff --git a/source/blender/python/gpu/gpu_py_api.c b/source/blender/python/gpu/gpu_py_api.c index 5119b3612f8..db84e03adcb 100644 --- a/source/blender/python/gpu/gpu_py_api.c +++ b/source/blender/python/gpu/gpu_py_api.c @@ -20,8 +20,8 @@ * Experimental Python API, not considered public yet (called '_gpu'), * we may re-expose as public later. * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> diff --git a/source/blender/python/gpu/gpu_py_batch.c b/source/blender/python/gpu/gpu_py_batch.c index 5214d156adf..c0c0aa0028d 100644 --- a/source/blender/python/gpu/gpu_py_batch.c +++ b/source/blender/python/gpu/gpu_py_batch.c @@ -19,11 +19,11 @@ /** \file * \ingroup bpygpu * - * This file defines the offscreen functionalities of the 'gpu' module + * This file defines the off-screen functionalities of the 'gpu' module * used for off-screen OpenGL rendering. * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c index 5c004459b44..a1fc89e772e 100644 --- a/source/blender/python/gpu/gpu_py_buffer.c +++ b/source/blender/python/gpu/gpu_py_buffer.c @@ -19,8 +19,8 @@ * * This file defines the gpu.state API. * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> diff --git a/source/blender/python/gpu/gpu_py_capabilities.c b/source/blender/python/gpu/gpu_py_capabilities.c index cedce485253..f3fb93021b2 100644 --- a/source/blender/python/gpu/gpu_py_capabilities.c +++ b/source/blender/python/gpu/gpu_py_capabilities.c @@ -17,8 +17,8 @@ /** \file * \ingroup bpygpu * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> diff --git a/source/blender/python/gpu/gpu_py_element.c b/source/blender/python/gpu/gpu_py_element.c index 0cb5e9a2785..2fb722f74db 100644 --- a/source/blender/python/gpu/gpu_py_element.c +++ b/source/blender/python/gpu/gpu_py_element.c @@ -17,8 +17,8 @@ /** \file * \ingroup bpygpu * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> @@ -144,8 +144,12 @@ static PyObject *pygpu_IndexBuf__tp_new(PyTypeObject *UNUSED(type), PyObject *ar goto finally; } - ok = PyC_AsArray_FAST( - values, seq_fast_item, verts_per_prim, &PyLong_Type, false, error_prefix) == 0; + ok = PyC_AsArray_FAST(values, + sizeof(*values), + seq_fast_item, + verts_per_prim, + &PyLong_Type, + error_prefix) == 0; if (ok) { for (uint j = 0; j < verts_per_prim; j++) { diff --git a/source/blender/python/gpu/gpu_py_framebuffer.c b/source/blender/python/gpu/gpu_py_framebuffer.c index 2f081fcfd8c..a9347b71723 100644 --- a/source/blender/python/gpu/gpu_py_framebuffer.c +++ b/source/blender/python/gpu/gpu_py_framebuffer.c @@ -20,8 +20,8 @@ * This file defines the framebuffer functionalities of the 'gpu' module * used for off-screen OpenGL rendering. * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> @@ -489,7 +489,7 @@ static PyObject *pygpu_framebuffer_read_color(BPyGPUFrameBuffer *self, static const char *_keywords[] = { "x", "y", "xsize", "ysize", "channels", "slot", "format", "data", NULL}; - static _PyArg_Parser _parser = {"iiiiiIO&|$O!:GPUTexture.__new__", _keywords, 0}; + static _PyArg_Parser _parser = {"iiiiiIO&|$O!:read_color", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, @@ -551,6 +551,57 @@ static PyObject *pygpu_framebuffer_read_color(BPyGPUFrameBuffer *self, return (PyObject *)py_buffer; } +PyDoc_STRVAR(pygpu_framebuffer_read_depth_doc, + ".. function:: read_depth(x, y, xsize, ysize, data=data)\n" + "\n" + " Read a pixel depth block from the frame buffer.\n" + "\n" + " :param x, y: Lower left corner of a rectangular block of pixels.\n" + " :param xsize, ysize: Dimensions of the pixel rectangle.\n" + " :type x, y, xsize, ysize: int\n" + " :arg data: Optional Buffer object to fill with the pixels values.\n" + " :type data: :class:`gpu.types.Buffer`\n" + " :return: The Buffer with the read pixels.\n" + " :rtype: :class:`gpu.types.Buffer`\n"); +static PyObject *pygpu_framebuffer_read_depth(BPyGPUFrameBuffer *self, + PyObject *args, + PyObject *kwds) +{ + PYGPU_FRAMEBUFFER_CHECK_OBJ(self); + int x, y, w, h; + BPyGPUBuffer *py_buffer = NULL; + + static const char *_keywords[] = {"x", "y", "xsize", "ysize", "data", NULL}; + static _PyArg_Parser _parser = {"iiii|$O!:read_depth", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast( + args, kwds, &_parser, &x, &y, &w, &h, &BPyGPU_BufferType, &py_buffer)) { + return NULL; + } + + if (py_buffer) { + if (py_buffer->format != GPU_DATA_FLOAT) { + PyErr_SetString(PyExc_AttributeError, "the format of the buffer must be 'GPU_DATA_FLOAT'"); + return NULL; + } + + size_t size_curr = bpygpu_Buffer_size(py_buffer); + size_t size_expected = w * h * GPU_texture_dataformat_size(GPU_DATA_FLOAT); + if (size_curr < size_expected) { + PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected"); + return NULL; + } + } + else { + py_buffer = BPyGPU_Buffer_CreatePyObject(GPU_DATA_FLOAT, (Py_ssize_t[]){h, w}, 2, NULL); + BLI_assert(bpygpu_Buffer_size(py_buffer) == + w * h * GPU_texture_dataformat_size(GPU_DATA_FLOAT)); + } + + GPU_framebuffer_read_depth(self->fb, x, y, w, h, GPU_DATA_FLOAT, py_buffer->buf.as_void); + + return (PyObject *)py_buffer; +} + #ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD PyDoc_STRVAR(pygpu_framebuffer_free_doc, ".. method:: free()\n" @@ -598,6 +649,10 @@ static struct PyMethodDef pygpu_framebuffer__tp_methods[] = { (PyCFunction)pygpu_framebuffer_read_color, METH_VARARGS | METH_KEYWORDS, pygpu_framebuffer_read_color_doc}, + {"read_depth", + (PyCFunction)pygpu_framebuffer_read_depth, + METH_VARARGS | METH_KEYWORDS, + pygpu_framebuffer_read_depth_doc}, #ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD {"free", (PyCFunction)pygpu_framebuffer_free, METH_NOARGS, pygpu_framebuffer_free_doc}, #endif diff --git a/source/blender/python/gpu/gpu_py_matrix.c b/source/blender/python/gpu/gpu_py_matrix.c index b379600d33c..cc9b3d702e2 100644 --- a/source/blender/python/gpu/gpu_py_matrix.c +++ b/source/blender/python/gpu/gpu_py_matrix.c @@ -22,8 +22,8 @@ * \warning While these functions attempt to ensure correct stack usage. * Mixing Python and C functions may still crash on invalid use. * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c index 0a8b294ea41..457f00b1267 100644 --- a/source/blender/python/gpu/gpu_py_offscreen.c +++ b/source/blender/python/gpu/gpu_py_offscreen.c @@ -22,8 +22,8 @@ * This file defines the offscreen functionalities of the 'gpu' module * used for off-screen OpenGL rendering. * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> diff --git a/source/blender/python/gpu/gpu_py_platform.c b/source/blender/python/gpu/gpu_py_platform.c index e49ad18dfd8..132052b6f1d 100644 --- a/source/blender/python/gpu/gpu_py_platform.c +++ b/source/blender/python/gpu/gpu_py_platform.c @@ -17,8 +17,8 @@ /** \file * \ingroup bpygpu * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> diff --git a/source/blender/python/gpu/gpu_py_select.c b/source/blender/python/gpu/gpu_py_select.c index b72aca6a792..4db102118f1 100644 --- a/source/blender/python/gpu/gpu_py_select.c +++ b/source/blender/python/gpu/gpu_py_select.c @@ -22,8 +22,8 @@ * \note Currently only used for gizmo selection, * will need to add begin/end and a way to access the hits. * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index fc3a7d1360b..b3f1c186716 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -17,8 +17,8 @@ /** \file * \ingroup bpygpu * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> @@ -309,7 +309,8 @@ static PyObject *pygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args) ret = -1; } else { - ret = PyC_AsArray_FAST(values, seq_fast, length, &PyLong_Type, false, error_prefix); + ret = PyC_AsArray_FAST( + values, sizeof(*values), seq_fast, length, &PyLong_Type, error_prefix); } Py_DECREF(seq_fast); } @@ -448,7 +449,8 @@ static PyObject *pygpu_shader_uniform_int(BPyGPUShader *self, PyObject *args) ret = -1; } else { - ret = PyC_AsArray_FAST(values, seq_fast, length, &PyLong_Type, false, error_prefix); + ret = PyC_AsArray_FAST( + values, sizeof(*values), seq_fast, length, &PyLong_Type, error_prefix); } Py_DECREF(seq_fast); } diff --git a/source/blender/python/gpu/gpu_py_state.c b/source/blender/python/gpu/gpu_py_state.c index 173c5afba56..7b7a61cc338 100644 --- a/source/blender/python/gpu/gpu_py_state.c +++ b/source/blender/python/gpu/gpu_py_state.c @@ -19,8 +19,8 @@ * * This file defines the gpu.state API. * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> diff --git a/source/blender/python/gpu/gpu_py_texture.c b/source/blender/python/gpu/gpu_py_texture.c index 2181c09b537..5d136921300 100644 --- a/source/blender/python/gpu/gpu_py_texture.c +++ b/source/blender/python/gpu/gpu_py_texture.c @@ -19,8 +19,8 @@ * * This file defines the texture functionalities of the 'gpu' module * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> @@ -153,7 +153,13 @@ static PyObject *pygpu_texture__tp_new(PyTypeObject *UNUSED(self), PyObject *arg int len = 1; if (PySequence_Check(py_size)) { len = PySequence_Size(py_size); - if (PyC_AsArray(size, py_size, len, &PyLong_Type, false, "GPUTexture.__new__") == -1) { + if ((len < 1) || (len > 3)) { + PyErr_Format(PyExc_ValueError, + "GPUTexture.__new__: \"size\" must be between 1 and 3 in length (got %d)", + len); + return NULL; + } + if (PyC_AsArray(size, sizeof(*size), py_size, len, &PyLong_Type, "GPUTexture.__new__") == -1) { return NULL; } } @@ -321,10 +327,11 @@ static PyObject *pygpu_texture_clear(BPyGPUTexture *self, PyObject *args, PyObje memset(&values, 0, sizeof(values)); if (PyC_AsArray(&values, + (pygpu_dataformat.value_found == GPU_DATA_FLOAT) ? sizeof(*values.f) : + sizeof(*values.i), py_values, shape, - pygpu_dataformat.value_found == GPU_DATA_FLOAT ? &PyFloat_Type : &PyLong_Type, - false, + (pygpu_dataformat.value_found == GPU_DATA_FLOAT) ? &PyFloat_Type : &PyLong_Type, "clear") == -1) { return NULL; } diff --git a/source/blender/python/gpu/gpu_py_types.c b/source/blender/python/gpu/gpu_py_types.c index fdd589d788e..a8787cf5c7f 100644 --- a/source/blender/python/gpu/gpu_py_types.c +++ b/source/blender/python/gpu/gpu_py_types.c @@ -17,8 +17,8 @@ /** \file * \ingroup bpygpu * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> diff --git a/source/blender/python/gpu/gpu_py_uniformbuffer.c b/source/blender/python/gpu/gpu_py_uniformbuffer.c index cfef20e2e4d..616ef65a5d1 100644 --- a/source/blender/python/gpu/gpu_py_uniformbuffer.c +++ b/source/blender/python/gpu/gpu_py_uniformbuffer.c @@ -19,8 +19,8 @@ * * This file defines the uniform buffer functionalities of the 'gpu' module * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> diff --git a/source/blender/python/gpu/gpu_py_vertex_buffer.c b/source/blender/python/gpu/gpu_py_vertex_buffer.c index 3c7038186b9..949086378a8 100644 --- a/source/blender/python/gpu/gpu_py_vertex_buffer.c +++ b/source/blender/python/gpu/gpu_py_vertex_buffer.c @@ -17,8 +17,8 @@ /** \file * \ingroup bpygpu * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> diff --git a/source/blender/python/gpu/gpu_py_vertex_format.c b/source/blender/python/gpu/gpu_py_vertex_format.c index 39c91966cab..c344429576a 100644 --- a/source/blender/python/gpu/gpu_py_vertex_format.c +++ b/source/blender/python/gpu/gpu_py_vertex_format.c @@ -17,8 +17,8 @@ /** \file * \ingroup bpygpu * - * - Use ``bpygpu_`` for local API. - * - Use ``BPyGPU`` for public API. + * - Use `bpygpu_` for local API. + * - Use `BPyGPU` for public API. */ #include <Python.h> diff --git a/source/blender/python/intern/bpy_app_ffmpeg.c b/source/blender/python/intern/bpy_app_ffmpeg.c index d2261ee7311..5f277b0ef07 100644 --- a/source/blender/python/intern/bpy_app_ffmpeg.c +++ b/source/blender/python/intern/bpy_app_ffmpeg.c @@ -36,7 +36,7 @@ static PyTypeObject BlenderAppFFmpegType; #define DEF_FFMPEG_LIB_VERSION(lib) \ - {(#lib "_version"), ("The " #lib " version as a tuple of 3 numbers")}, \ + {(#lib "_version"), ("The " #lib " version as a tuple of 3 numbers")}, \ { \ (#lib "_version_string"), ("The " #lib " version formatted as a string") \ } diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c index 9ee9f2e477f..d5afb2334f0 100644 --- a/source/blender/python/intern/bpy_operator.c +++ b/source/blender/python/intern/bpy_operator.c @@ -339,7 +339,7 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) return NULL; } - /* When calling bpy.ops.wm.read_factory_settings() bpy.data's main pointer + /* When calling `bpy.ops.wm.read_factory_settings()` `bpy.data's` main pointer * is freed by clear_globals(), further access will crash blender. * Setting context is not needed in this case, only calling because this * function corrects bpy.data (internal Main pointer) */ diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index f332d547965..6d5ca209866 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -492,6 +492,145 @@ static void bpy_prop_assign_flag_override(PropertyRNA *prop, const int flag_over /** \} */ /* -------------------------------------------------------------------- */ +/** \name Multi-Dimensional Property Utilities + * \{ */ + +struct BPYPropArrayLength { + int len_total; + /** Ignore `dims` when `dims_len == 0`. */ + int dims[RNA_MAX_ARRAY_DIMENSION]; + int dims_len; +}; + +/** + * Use with PyArg_ParseTuple's "O&" formatting. + */ +static int bpy_prop_array_length_parse(PyObject *o, void *p) +{ + struct BPYPropArrayLength *array_len_info = p; + + if (PyLong_CheckExact(o)) { + int size; + if (((size = PyLong_AsLong(o)) == -1)) { + PyErr_Format( + PyExc_ValueError, "expected number or sequence of numbers, got %s", Py_TYPE(o)->tp_name); + return 0; + } + if (size < 1 || size > PYRNA_STACK_ARRAY) { + PyErr_Format( + PyExc_TypeError, "(size=%d) must be between 1 and " STRINGIFY(PYRNA_STACK_ARRAY), size); + return 0; + } + array_len_info->len_total = size; + + /* Don't use this value. */ + array_len_info->dims_len = 0; + } + else { + PyObject *seq_fast; + if (!(seq_fast = PySequence_Fast(o, "size must be a number of a sequence of numbers"))) { + return 0; + } + const int seq_len = PySequence_Fast_GET_SIZE(seq_fast); + if (seq_len < 1 || seq_len > RNA_MAX_ARRAY_DIMENSION) { + PyErr_Format( + PyExc_TypeError, + "(len(size)=%d) length must be between 1 and " STRINGIFY(RNA_MAX_ARRAY_DIMENSION), + seq_len); + Py_DECREF(seq_fast); + return 0; + } + + PyObject **seq_items = PySequence_Fast_ITEMS(seq_fast); + for (int i = 0; i < seq_len; i++) { + int size; + if (((size = PyLong_AsLong(seq_items[i])) == -1)) { + Py_DECREF(seq_fast); + PyErr_Format(PyExc_ValueError, + "expected number in sequence, got %s at index %d", + Py_TYPE(o)->tp_name, + i); + return 0; + } + if (size < 1 || size > PYRNA_STACK_ARRAY) { + Py_DECREF(seq_fast); + PyErr_Format(PyExc_TypeError, + "(size[%d]=%d) must be between 1 and " STRINGIFY(PYRNA_STACK_ARRAY), + i, + size); + return 0; + } + + array_len_info->dims[i] = size; + array_len_info->dims_len = seq_len; + } + } + return 1; +} + +/** + * Return -1 on error. + */ +static int bpy_prop_array_from_py_with_dims(void *values, + size_t values_elem_size, + PyObject *py_values, + const struct BPYPropArrayLength *array_len_info, + const PyTypeObject *type, + const char *error_str) +{ + if (array_len_info->dims_len == 0) { + return PyC_AsArray( + values, values_elem_size, py_values, array_len_info->len_total, type, error_str); + } + const int *dims = array_len_info->dims; + const int dims_len = array_len_info->dims_len; + return PyC_AsArray_Multi(values, values_elem_size, py_values, dims, dims_len, type, error_str); +} + +static bool bpy_prop_array_is_matrix_compatible_ex(int subtype, + const struct BPYPropArrayLength *array_len_info) +{ + return ((subtype == PROP_MATRIX) && (array_len_info->dims_len == 2) && + ((array_len_info->dims[0] >= 2) && (array_len_info->dims[0] >= 4)) && + ((array_len_info->dims[1] >= 2) && (array_len_info->dims[1] >= 4))); +} + +static bool bpy_prop_array_is_matrix_compatible(PropertyRNA *prop, + const struct BPYPropArrayLength *array_len_info) +{ + BLI_assert(RNA_property_type(prop) == PROP_FLOAT); + return bpy_prop_array_is_matrix_compatible_ex(RNA_property_subtype(prop), array_len_info); +} + +/** + * Needed since the internal storage of matrices swaps row/column. + */ +static void bpy_prop_array_matrix_swap_row_column_vn_vn( + float *values_dst, const float *values_src, const struct BPYPropArrayLength *array_len_info) +{ + BLI_assert(values_dst != values_src); + const int dim0 = array_len_info->dims[0], dim1 = array_len_info->dims[1]; + BLI_assert(dim0 <= 4 && dim1 <= 4); + for (int i = 0; i < dim0; i++) { + for (int j = 0; j < dim1; j++) { + values_dst[(j * dim0) + i] = values_src[(i * dim1) + j]; + } + } +} + +static void bpy_prop_array_matrix_swap_row_column_vn( + float *values, const struct BPYPropArrayLength *array_len_info) +{ + const int dim0 = array_len_info->dims[0], dim1 = array_len_info->dims[1]; + BLI_assert(dim0 <= 4 && dim1 <= 4); + float values_orig[4 * 4]; + memcpy(values_orig, values, sizeof(float) * (dim0 * dim1)); + bpy_prop_array_matrix_swap_row_column_vn_vn(values, values_orig, array_len_info); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Shared Property Callbacks * * Unique data is accessed via #RNA_property_py_data_get @@ -687,7 +826,10 @@ static void bpy_prop_boolean_array_get_fn(struct PointerRNA *ptr, PyGILState_STATE gilstate; bool use_gil; const bool is_write_ok = pyrna_write_check(); + bool is_values_set = false; int i, len = RNA_property_array_length(ptr, prop); + struct BPYPropArrayLength array_len_info = {.len_total = len}; + array_len_info.dims_len = RNA_property_array_dimension(ptr, prop, array_len_info.dims); BLI_assert(prop_store != NULL); @@ -711,25 +853,25 @@ static void bpy_prop_boolean_array_get_fn(struct PointerRNA *ptr, Py_DECREF(args); - if (ret == NULL) { - PyC_Err_PrintWithFunc(py_func); - - for (i = 0; i < len; i++) { - values[i] = false; - } - } - else { - if (PyC_AsArray(values, ret, len, &PyBool_Type, false, "BoolVectorProperty get") == -1) { + if (ret != NULL) { + if (bpy_prop_array_from_py_with_dims(values, + sizeof(*values), + ret, + &array_len_info, + &PyBool_Type, + "BoolVectorProperty get callback") == -1) { PyC_Err_PrintWithFunc(py_func); - - for (i = 0; i < len; i++) { - values[i] = false; - } - - /* PyC_AsArray decrements refcount internally on error */ } else { - Py_DECREF(ret); + is_values_set = true; + } + Py_DECREF(ret); + } + + if (is_values_set == false) { + /* This is the flattened length for multi-dimensional arrays. */ + for (i = 0; i < len; i++) { + values[i] = false; } } @@ -756,6 +898,8 @@ static void bpy_prop_boolean_array_set_fn(struct PointerRNA *ptr, bool use_gil; const bool is_write_ok = pyrna_write_check(); const int len = RNA_property_array_length(ptr, prop); + struct BPYPropArrayLength array_len_info = {.len_total = len}; + array_len_info.dims_len = RNA_property_array_dimension(ptr, prop, array_len_info.dims); BLI_assert(prop_store != NULL); @@ -775,7 +919,13 @@ static void bpy_prop_boolean_array_set_fn(struct PointerRNA *ptr, self = pyrna_struct_as_instance(ptr); PyTuple_SET_ITEM(args, 0, self); - py_values = PyC_Tuple_PackArray_Bool(values, len); + if (array_len_info.dims_len == 0) { + py_values = PyC_Tuple_PackArray_Bool(values, len); + } + else { + py_values = PyC_Tuple_PackArray_Multi_Bool( + values, array_len_info.dims, array_len_info.dims_len); + } PyTuple_SET_ITEM(args, 1, py_values); ret = PyObject_CallObject(py_func, args); @@ -937,7 +1087,10 @@ static void bpy_prop_int_array_get_fn(struct PointerRNA *ptr, PyGILState_STATE gilstate; bool use_gil; const bool is_write_ok = pyrna_write_check(); + bool is_values_set = false; int i, len = RNA_property_array_length(ptr, prop); + struct BPYPropArrayLength array_len_info = {.len_total = len}; + array_len_info.dims_len = RNA_property_array_dimension(ptr, prop, array_len_info.dims); BLI_assert(prop_store != NULL); @@ -961,25 +1114,25 @@ static void bpy_prop_int_array_get_fn(struct PointerRNA *ptr, Py_DECREF(args); - if (ret == NULL) { - PyC_Err_PrintWithFunc(py_func); - - for (i = 0; i < len; i++) { - values[i] = 0; - } - } - else { - if (PyC_AsArray(values, ret, len, &PyLong_Type, false, "IntVectorProperty get") == -1) { + if (ret != NULL) { + if (bpy_prop_array_from_py_with_dims(values, + sizeof(*values), + ret, + &array_len_info, + &PyLong_Type, + "IntVectorProperty get callback") == -1) { PyC_Err_PrintWithFunc(py_func); - - for (i = 0; i < len; i++) { - values[i] = 0; - } - - /* PyC_AsArray decrements refcount internally on error */ } else { - Py_DECREF(ret); + is_values_set = true; + } + Py_DECREF(ret); + } + + if (is_values_set == false) { + /* This is the flattened length for multi-dimensional arrays. */ + for (i = 0; i < len; i++) { + values[i] = 0; } } @@ -1006,6 +1159,8 @@ static void bpy_prop_int_array_set_fn(struct PointerRNA *ptr, bool use_gil; const bool is_write_ok = pyrna_write_check(); const int len = RNA_property_array_length(ptr, prop); + struct BPYPropArrayLength array_len_info = {.len_total = len}; + array_len_info.dims_len = RNA_property_array_dimension(ptr, prop, array_len_info.dims); BLI_assert(prop_store != NULL); @@ -1025,7 +1180,14 @@ static void bpy_prop_int_array_set_fn(struct PointerRNA *ptr, self = pyrna_struct_as_instance(ptr); PyTuple_SET_ITEM(args, 0, self); - py_values = PyC_Tuple_PackArray_I32(values, len); + if (array_len_info.dims_len == 0) { + py_values = PyC_Tuple_PackArray_I32(values, len); + } + else { + py_values = PyC_Tuple_PackArray_Multi_I32( + values, array_len_info.dims, array_len_info.dims_len); + } + PyTuple_SET_ITEM(args, 1, py_values); ret = PyObject_CallObject(py_func, args); @@ -1187,7 +1349,10 @@ static void bpy_prop_float_array_get_fn(struct PointerRNA *ptr, PyGILState_STATE gilstate; bool use_gil; const bool is_write_ok = pyrna_write_check(); + bool is_values_set = false; int i, len = RNA_property_array_length(ptr, prop); + struct BPYPropArrayLength array_len_info = {.len_total = len}; + array_len_info.dims_len = RNA_property_array_dimension(ptr, prop, array_len_info.dims); BLI_assert(prop_store != NULL); @@ -1211,25 +1376,29 @@ static void bpy_prop_float_array_get_fn(struct PointerRNA *ptr, Py_DECREF(args); - if (ret == NULL) { - PyC_Err_PrintWithFunc(py_func); - - for (i = 0; i < len; i++) { - values[i] = 0.0f; - } - } - else { - if (PyC_AsArray(values, ret, len, &PyFloat_Type, false, "FloatVectorProperty get") == -1) { + if (ret != NULL) { + if (bpy_prop_array_from_py_with_dims(values, + sizeof(*values), + ret, + &array_len_info, + &PyFloat_Type, + "FloatVectorProperty get callback") == -1) { PyC_Err_PrintWithFunc(py_func); - - for (i = 0; i < len; i++) { - values[i] = 0.0f; - } - - /* PyC_AsArray decrements refcount internally on error */ } else { - Py_DECREF(ret); + /* Only for float types. */ + if (bpy_prop_array_is_matrix_compatible(prop, &array_len_info)) { + bpy_prop_array_matrix_swap_row_column_vn(values, &array_len_info); + } + is_values_set = true; + } + Py_DECREF(ret); + } + + if (is_values_set == false) { + /* This is the flattened length for multi-dimensional arrays. */ + for (i = 0; i < len; i++) { + values[i] = 0.0f; } } @@ -1256,6 +1425,8 @@ static void bpy_prop_float_array_set_fn(struct PointerRNA *ptr, bool use_gil; const bool is_write_ok = pyrna_write_check(); const int len = RNA_property_array_length(ptr, prop); + struct BPYPropArrayLength array_len_info = {.len_total = len}; + array_len_info.dims_len = RNA_property_array_dimension(ptr, prop, array_len_info.dims); BLI_assert(prop_store != NULL); @@ -1275,7 +1446,14 @@ static void bpy_prop_float_array_set_fn(struct PointerRNA *ptr, self = pyrna_struct_as_instance(ptr); PyTuple_SET_ITEM(args, 0, self); - py_values = PyC_Tuple_PackArray_F32(values, len); + if (array_len_info.dims_len == 0) { + py_values = PyC_Tuple_PackArray_F32(values, len); + } + else { + /* No need for matrix column/row swapping here unless the matrix data is read directly. */ + py_values = PyC_Tuple_PackArray_Multi_F32( + values, array_len_info.dims, array_len_info.dims_len); + } PyTuple_SET_ITEM(args, 1, py_values); ret = PyObject_CallObject(py_func, args); @@ -2240,27 +2418,33 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop, /** \name Shared Method Utilities * \{ */ -/* this define runs at the start of each function and deals with - * returning a deferred property (to be registered later) */ +/** + * This define runs at the start of each function and deals with + * returning a deferred property #BPy_PropDeferred (to be registered later). + * + * \note `srna` will always be left set if this function doesn't return. + */ #define BPY_PROPDEF_HEAD(_func) \ - if (PyTuple_GET_SIZE(args) == 1) { \ - PyObject *ret; \ - self = PyTuple_GET_ITEM(args, 0); \ - args = PyTuple_New(0); \ - ret = BPy_##_func(self, args, kw); \ - Py_DECREF(args); \ - return ret; \ - } \ - if (PyTuple_GET_SIZE(args) > 1) { \ - PyErr_SetString(PyExc_ValueError, "all args must be keywords"); \ - return NULL; \ - } \ - srna = srna_from_self(self, #_func "(...):"); \ - if (srna == NULL) { \ - if (PyErr_Occurred()) { \ + { \ + const int args_len = PyTuple_GET_SIZE(args); \ + if (args_len == 1) { \ + self = PyTuple_GET_ITEM(args, 0); \ + args = PyTuple_New(0); \ + PyObject *ret = BPy_##_func(self, args, kw); \ + Py_DECREF(args); \ + return ret; \ + } \ + if (args_len > 1) { \ + PyErr_SetString(PyExc_ValueError, "all args must be keywords"); \ return NULL; \ } \ - return bpy_prop_deferred_data_CreatePyObject(pymeth_##_func, kw); \ + srna = srna_from_self(self, #_func "(...):"); \ + if (srna == NULL) { \ + if (PyErr_Occurred()) { \ + return NULL; \ + } \ + return bpy_prop_deferred_data_CreatePyObject(pymeth_##_func, kw); \ + } \ } \ (void)0 @@ -2355,8 +2539,9 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop, "value in the UI.\n" #define BPY_PROPDEF_VECSIZE_DOC \ - " :arg size: Vector dimensions in [1, " STRINGIFY(PYRNA_STACK_ARRAY) "].\n" \ -" :type size: int\n" + " :arg size: Vector dimensions in [1, " STRINGIFY(PYRNA_STACK_ARRAY) "]. " \ +"An int sequence can be used to define multi-dimension arrays.\n" \ +" :type size: int or int sequence\n" #define BPY_PROPDEF_INT_STEP_DOC \ " :arg step: Step of increment/decrement in UI, in [1, 100], defaults to 1 (WARNING: unused " \ @@ -2439,96 +2624,95 @@ PyDoc_STRVAR(BPy_BoolProperty_doc, BPY_PROPDEF_SET_DOC); static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) { + /* Keep this block first. */ StructRNA *srna; - BPY_PROPDEF_HEAD(BoolProperty); + BLI_assert(srna != NULL); + + const char *id = NULL, *name = NULL, *description = ""; + Py_ssize_t id_len; + 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; + PyObject *update_fn = NULL; + PyObject *get_fn = NULL; + PyObject *set_fn = NULL; + PyObject *py_tags = NULL; - if (srna) { - const char *id = NULL, *name = NULL, *description = ""; - Py_ssize_t id_len; - 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; - PyObject *update_fn = NULL; - PyObject *get_fn = NULL; - PyObject *set_fn = NULL; - PyObject *py_tags = NULL; - - static const char *_keywords[] = { - "attr", - "name", - "description", - "default", - "options", - "override", - "tags", - "subtype", - "update", - "get", - "set", - NULL, - }; - static _PyArg_Parser _parser = {"s#|$ssO&O!O!O!sOOO:BoolProperty", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast(args, - kw, - &_parser, - &id, - &id_len, - &name, - &description, - PyC_ParseBool, - &def, - &PySet_Type, - &pyopts, - &PySet_Type, - &pyopts_override, - &PySet_Type, - &py_tags, - &pysubtype, - &update_fn, - &get_fn, - &set_fn)) { - return NULL; - } + static const char *_keywords[] = { + "attr", + "name", + "description", + "default", + "options", + "override", + "tags", + "subtype", + "update", + "get", + "set", + NULL, + }; + static _PyArg_Parser _parser = {"s#|$ssO&O!O!O!sOOO:BoolProperty", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + &id, + &id_len, + &name, + &description, + PyC_ParseBool, + &def, + &PySet_Type, + &pyopts, + &PySet_Type, + &pyopts_override, + &PySet_Type, + &py_tags, + &pysubtype, + &update_fn, + &get_fn, + &set_fn)) { + return NULL; + } - BPY_PROPDEF_SUBTYPE_CHECK(BoolProperty, - property_flag_items, - property_flag_override_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_fn, "update", 2) == -1) { - return NULL; - } - if (bpy_prop_callback_check(get_fn, "get", 1) == -1) { - return NULL; - } - if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { - return NULL; - } + if (bpy_prop_callback_check(update_fn, "update", 2) == -1) { + return NULL; + } + if (bpy_prop_callback_check(get_fn, "get", 1) == -1) { + return NULL; + } + if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { + return NULL; + } - prop = RNA_def_property(srna, id, PROP_BOOLEAN, subtype); - RNA_def_property_boolean_default(prop, def); - RNA_def_property_ui_text(prop, name ? name : id, description); + prop = RNA_def_property(srna, id, PROP_BOOLEAN, subtype); + RNA_def_property_boolean_default(prop, def); + RNA_def_property_ui_text(prop, name ? name : id, description); - if (py_tags) { - RNA_def_property_tags(prop, prop_tags); - } - 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_fn); - bpy_prop_callback_assign_boolean(prop, get_fn, set_fn); - RNA_def_property_duplicate_pointers(srna, prop); + if (py_tags) { + RNA_def_property_tags(prop, prop_tags); + } + 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_fn); + bpy_prop_callback_assign_boolean(prop, get_fn, set_fn); + RNA_def_property_duplicate_pointers(srna, prop); Py_RETURN_NONE; } @@ -2555,121 +2739,128 @@ PyDoc_STRVAR( BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject *kw) { + /* Keep this block first. */ StructRNA *srna; - BPY_PROPDEF_HEAD(BoolVectorProperty); + BLI_assert(srna != NULL); + + const char *id = NULL, *name = NULL, *description = ""; + Py_ssize_t id_len; + bool def[RNA_MAX_ARRAY_DIMENSION][PYRNA_STACK_ARRAY] = {{false}}; + struct BPYPropArrayLength array_len_info = {.len_total = 3}; + 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; + PyObject *update_fn = NULL; + PyObject *get_fn = NULL; + PyObject *set_fn = NULL; + PyObject *py_tags = NULL; - if (srna) { - const char *id = NULL, *name = NULL, *description = ""; - Py_ssize_t id_len; - bool def[PYRNA_STACK_ARRAY] = {0}; - int size = 3; - 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; - PyObject *update_fn = NULL; - PyObject *get_fn = NULL; - PyObject *set_fn = NULL; - PyObject *py_tags = NULL; - - static const char *_keywords[] = { - "attr", - "name", - "description", - "default", - "options", - "override", - "tags", - "subtype", - "size", - "update", - "get", - "set", - NULL, - }; - static _PyArg_Parser _parser = {"s#|$ssOO!O!O!siOOO:BoolVectorProperty", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast(args, - kw, - &_parser, - &id, - &id_len, - &name, - &description, - &pydef, - &PySet_Type, - &pyopts, - &PySet_Type, - &pyopts_override, - &PySet_Type, - &py_tags, - &pysubtype, - &size, - &update_fn, - &get_fn, - &set_fn)) { - return NULL; - } - - BPY_PROPDEF_SUBTYPE_CHECK(BoolVectorProperty, - property_flag_items, - property_flag_override_items, - property_subtype_array_items); + static const char *_keywords[] = { + "attr", + "name", + "description", + "default", + "options", + "override", + "tags", + "subtype", + "size", + "update", + "get", + "set", + NULL, + }; + static _PyArg_Parser _parser = {"s#|$ssOO!O!O!sO&OOO:BoolVectorProperty", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + &id, + &id_len, + &name, + &description, + &pydef, + &PySet_Type, + &pyopts, + &PySet_Type, + &pyopts_override, + &PySet_Type, + &py_tags, + &pysubtype, + bpy_prop_array_length_parse, + &array_len_info, + &update_fn, + &get_fn, + &set_fn)) { + return NULL; + } - if (size < 1 || size > PYRNA_STACK_ARRAY) { - PyErr_Format( - PyExc_TypeError, - "BoolVectorProperty(size=%d): size must be between 0 and " STRINGIFY(PYRNA_STACK_ARRAY), - size); - return NULL; - } + BPY_PROPDEF_SUBTYPE_CHECK(BoolVectorProperty, + property_flag_items, + property_flag_override_items, + property_subtype_array_items); - if (pydef && - PyC_AsArray( - def, pydef, size, &PyBool_Type, false, "BoolVectorProperty(default=sequence)") == -1) { + if (pydef != NULL) { + if (bpy_prop_array_from_py_with_dims(def[0], + sizeof(*def[0]), + pydef, + &array_len_info, + &PyBool_Type, + "BoolVectorProperty(default=sequence)") == -1) { return NULL; } + } - if (bpy_prop_callback_check(update_fn, "update", 2) == -1) { - return NULL; - } - if (bpy_prop_callback_check(get_fn, "get", 1) == -1) { - return NULL; - } - if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { - return NULL; - } + if (bpy_prop_callback_check(update_fn, "update", 2) == -1) { + return NULL; + } + if (bpy_prop_callback_check(get_fn, "get", 1) == -1) { + return NULL; + } + if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { + return NULL; + } #if 0 prop = RNA_def_boolean_array( srna, id, size, pydef ? def : NULL, name ? name : id, description); #endif - prop = RNA_def_property(srna, id, PROP_BOOLEAN, subtype); - RNA_def_property_array(prop, size); - if (pydef) { - RNA_def_property_boolean_array_default(prop, def); - } - RNA_def_property_ui_text(prop, name ? name : id, description); - if (py_tags) { - RNA_def_property_tags(prop, prop_tags); - } - if (pyopts) { - bpy_prop_assign_flag(prop, opts); + prop = RNA_def_property(srna, id, PROP_BOOLEAN, subtype); + if (array_len_info.dims_len == 0) { + RNA_def_property_array(prop, array_len_info.len_total); + if (pydef != NULL) { + RNA_def_property_boolean_array_default(prop, def[0]); } - if (pyopts_override) { - bpy_prop_assign_flag_override(prop, opts_override); + } + else { + RNA_def_property_multi_array(prop, array_len_info.dims_len, array_len_info.dims); + if (pydef != NULL) { + RNA_def_property_boolean_array_default(prop, &def[0][0]); } - bpy_prop_callback_assign_update(prop, update_fn); - bpy_prop_callback_assign_boolean_array(prop, get_fn, set_fn); - RNA_def_property_duplicate_pointers(srna, prop); } + RNA_def_property_ui_text(prop, name ? name : id, description); + + if (py_tags) { + RNA_def_property_tags(prop, prop_tags); + } + 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_fn); + bpy_prop_callback_assign_boolean_array(prop, get_fn, set_fn); + RNA_def_property_duplicate_pointers(srna, prop); + Py_RETURN_NONE; } @@ -2698,107 +2889,107 @@ PyDoc_STRVAR( BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw) { + /* Keep this block first. */ StructRNA *srna; - BPY_PROPDEF_HEAD(IntProperty); + BLI_assert(srna != NULL); + + const char *id = NULL, *name = NULL, *description = ""; + Py_ssize_t id_len; + int min = INT_MIN, max = INT_MAX, soft_min = INT_MIN, soft_max = INT_MAX, step = 1, def = 0; + 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; + PyObject *update_fn = NULL; + PyObject *get_fn = NULL; + PyObject *set_fn = NULL; + PyObject *py_tags = NULL; - if (srna) { - const char *id = NULL, *name = NULL, *description = ""; - Py_ssize_t id_len; - int min = INT_MIN, max = INT_MAX, soft_min = INT_MIN, soft_max = INT_MAX, step = 1, def = 0; - 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; - PyObject *update_fn = NULL; - PyObject *get_fn = NULL; - PyObject *set_fn = NULL; - PyObject *py_tags = NULL; - - static const char *_keywords[] = { - "attr", - "name", - "description", - "default", - "min", - "max", - "soft_min", - "soft_max", - "step", - "options", - "override", - "tags", - "subtype", - "update", - "get", - "set", - NULL, - }; - static _PyArg_Parser _parser = {"s#|$ssiiiiiiO!O!O!sOOO:IntProperty", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast(args, - kw, - &_parser, - &id, - &id_len, - &name, - &description, - &def, - &min, - &max, - &soft_min, - &soft_max, - &step, - &PySet_Type, - &pyopts, - &PySet_Type, - &pyopts_override, - &PySet_Type, - &py_tags, - &pysubtype, - &update_fn, - &get_fn, - &set_fn)) { - return NULL; - } + static const char *_keywords[] = { + "attr", + "name", + "description", + "default", + "min", + "max", + "soft_min", + "soft_max", + "step", + "options", + "override", + "tags", + "subtype", + "update", + "get", + "set", + NULL, + }; + static _PyArg_Parser _parser = {"s#|$ssiiiiiiO!O!O!sOOO:IntProperty", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + &id, + &id_len, + &name, + &description, + &def, + &min, + &max, + &soft_min, + &soft_max, + &step, + &PySet_Type, + &pyopts, + &PySet_Type, + &pyopts_override, + &PySet_Type, + &py_tags, + &pysubtype, + &update_fn, + &get_fn, + &set_fn)) { + return NULL; + } - BPY_PROPDEF_SUBTYPE_CHECK(IntProperty, - property_flag_items, - property_flag_override_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_fn, "update", 2) == -1) { - return NULL; - } - if (bpy_prop_callback_check(get_fn, "get", 1) == -1) { - return NULL; - } - if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { - return NULL; - } + if (bpy_prop_callback_check(update_fn, "update", 2) == -1) { + return NULL; + } + if (bpy_prop_callback_check(get_fn, "get", 1) == -1) { + return NULL; + } + if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { + return NULL; + } - prop = RNA_def_property(srna, id, PROP_INT, subtype); - RNA_def_property_int_default(prop, def); - RNA_def_property_ui_text(prop, name ? name : id, description); - RNA_def_property_range(prop, min, max); - RNA_def_property_ui_range(prop, MAX2(soft_min, min), MIN2(soft_max, max), step, 3); + prop = RNA_def_property(srna, id, PROP_INT, subtype); + RNA_def_property_int_default(prop, def); + RNA_def_property_ui_text(prop, name ? name : id, description); + RNA_def_property_range(prop, min, max); + RNA_def_property_ui_range(prop, MAX2(soft_min, min), MIN2(soft_max, max), step, 3); - if (py_tags) { - RNA_def_property_tags(prop, prop_tags); - } - 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_fn); - bpy_prop_callback_assign_int(prop, get_fn, set_fn); - RNA_def_property_duplicate_pointers(srna, prop); + if (py_tags) { + RNA_def_property_tags(prop, prop_tags); + } + 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_fn); + bpy_prop_callback_assign_int(prop, get_fn, set_fn); + RNA_def_property_duplicate_pointers(srna, prop); + Py_RETURN_NONE; } @@ -2831,129 +3022,136 @@ PyDoc_STRVAR(BPy_IntVectorProperty_doc, BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject *kw) { + /* Keep this block first. */ StructRNA *srna; - BPY_PROPDEF_HEAD(IntVectorProperty); + BLI_assert(srna != NULL); + + const char *id = NULL, *name = NULL, *description = ""; + Py_ssize_t id_len; + int min = INT_MIN, max = INT_MAX, soft_min = INT_MIN, soft_max = INT_MAX, step = 1; + int def[RNA_MAX_ARRAY_DIMENSION][PYRNA_STACK_ARRAY] = {0}; + struct BPYPropArrayLength array_len_info = {.len_total = 3}; + PropertyRNA *prop; + 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; + PyObject *update_fn = NULL; + PyObject *get_fn = NULL; + PyObject *set_fn = NULL; + PyObject *py_tags = NULL; - if (srna) { - const char *id = NULL, *name = NULL, *description = ""; - Py_ssize_t id_len; - int min = INT_MIN, max = INT_MAX, soft_min = INT_MIN, soft_max = INT_MAX, step = 1; - int def[PYRNA_STACK_ARRAY] = {0}; - int size = 3; - PropertyRNA *prop; - 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; - PyObject *update_fn = NULL; - PyObject *get_fn = NULL; - PyObject *set_fn = NULL; - PyObject *py_tags = NULL; - - static const char *_keywords[] = { - "attr", - "name", - "description", - "default", - "min", - "max", - "soft_min", - "soft_max", - "step", - "options", - "override", - "tags", - "subtype", - "size", - "update", - "get", - "set", - NULL, - }; - static _PyArg_Parser _parser = {"s#|$ssOiiiiiO!O!O!siOOO:IntVectorProperty", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast(args, - kw, - &_parser, - &id, - &id_len, - &name, - &description, - &pydef, - &min, - &max, - &soft_min, - &soft_max, - &step, - &PySet_Type, - &pyopts, - &PySet_Type, - &pyopts_override, - &PySet_Type, - &py_tags, - &pysubtype, - &size, - &update_fn, - &get_fn, - &set_fn)) { - return NULL; - } + static const char *_keywords[] = { + "attr", + "name", + "description", + "default", + "min", + "max", + "soft_min", + "soft_max", + "step", + "options", + "override", + "tags", + "subtype", + "size", + "update", + "get", + "set", + NULL, + }; + static _PyArg_Parser _parser = {"s#|$ssOiiiiiO!O!O!sO&OOO:IntVectorProperty", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + &id, + &id_len, + &name, + &description, + &pydef, + &min, + &max, + &soft_min, + &soft_max, + &step, + &PySet_Type, + &pyopts, + &PySet_Type, + &pyopts_override, + &PySet_Type, + &py_tags, + &pysubtype, + bpy_prop_array_length_parse, + &array_len_info, + &update_fn, + &get_fn, + &set_fn)) { + return NULL; + } - BPY_PROPDEF_SUBTYPE_CHECK(IntVectorProperty, - property_flag_items, - property_flag_override_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( - PyExc_TypeError, - "IntVectorProperty(size=%d): size must be between 0 and " STRINGIFY(PYRNA_STACK_ARRAY), - size); + if (pydef != NULL) { + if (bpy_prop_array_from_py_with_dims(def[0], + sizeof(*def[0]), + pydef, + &array_len_info, + &PyLong_Type, + "IntVectorProperty(default=sequence)") == -1) { return NULL; } + } - if (pydef && - PyC_AsArray( - def, pydef, size, &PyLong_Type, false, "IntVectorProperty(default=sequence)") == -1) { - return NULL; - } + if (bpy_prop_callback_check(update_fn, "update", 2) == -1) { + return NULL; + } + if (bpy_prop_callback_check(get_fn, "get", 1) == -1) { + return NULL; + } + if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { + return NULL; + } - if (bpy_prop_callback_check(update_fn, "update", 2) == -1) { - return NULL; - } - if (bpy_prop_callback_check(get_fn, "get", 1) == -1) { - return NULL; + prop = RNA_def_property(srna, id, PROP_INT, subtype); + if (array_len_info.dims_len == 0) { + RNA_def_property_array(prop, array_len_info.len_total); + if (pydef != NULL) { + RNA_def_property_int_array_default(prop, def[0]); } - if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { - return NULL; + } + else { + RNA_def_property_multi_array(prop, array_len_info.dims_len, array_len_info.dims); + if (pydef != NULL) { + RNA_def_property_int_array_default(prop, &def[0][0]); } + } - prop = RNA_def_property(srna, id, PROP_INT, subtype); - RNA_def_property_array(prop, size); - if (pydef) { - RNA_def_property_int_array_default(prop, def); - } - RNA_def_property_range(prop, min, max); - RNA_def_property_ui_text(prop, name ? name : id, description); - RNA_def_property_ui_range(prop, MAX2(soft_min, min), MIN2(soft_max, max), step, 3); + RNA_def_property_range(prop, min, max); + RNA_def_property_ui_text(prop, name ? name : id, description); + RNA_def_property_ui_range(prop, MAX2(soft_min, min), MIN2(soft_max, max), step, 3); - if (py_tags) { - RNA_def_property_tags(prop, prop_tags); - } - 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_fn); - bpy_prop_callback_assign_int_array(prop, get_fn, set_fn); - RNA_def_property_duplicate_pointers(srna, prop); + if (py_tags) { + RNA_def_property_tags(prop, prop_tags); } + 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_fn); + bpy_prop_callback_assign_int_array(prop, get_fn, set_fn); + RNA_def_property_duplicate_pointers(srna, prop); + Py_RETURN_NONE; } @@ -2985,104 +3183,104 @@ PyDoc_STRVAR(BPy_FloatProperty_doc, BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw) { + /* Keep this block first. */ StructRNA *srna; - BPY_PROPDEF_HEAD(FloatProperty); + BLI_assert(srna != NULL); + + const char *id = NULL, *name = NULL, *description = ""; + Py_ssize_t id_len; + float min = -FLT_MAX, max = FLT_MAX, soft_min = -FLT_MAX, soft_max = FLT_MAX, step = 3, + def = 0.0f; + int precision = 2; + 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; + const char *pyunit = NULL; + int unit = PROP_UNIT_NONE; + PyObject *update_fn = NULL; + PyObject *get_fn = NULL; + PyObject *set_fn = NULL; + PyObject *py_tags = NULL; - if (srna) { - const char *id = NULL, *name = NULL, *description = ""; - Py_ssize_t id_len; - float min = -FLT_MAX, max = FLT_MAX, soft_min = -FLT_MAX, soft_max = FLT_MAX, step = 3, - def = 0.0f; - int precision = 2; - 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; - const char *pyunit = NULL; - int unit = PROP_UNIT_NONE; - PyObject *update_fn = NULL; - PyObject *get_fn = NULL; - PyObject *set_fn = NULL; - PyObject *py_tags = NULL; - - static const char *_keywords[] = { - "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!O!ssOOO:FloatProperty", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast(args, - kw, - &_parser, - &id, - &id_len, - &name, - &description, - &def, - &min, - &max, - &soft_min, - &soft_max, - &step, - &precision, - &PySet_Type, - &pyopts, - &PySet_Type, - &pyopts_override, - &PySet_Type, - &py_tags, - &pysubtype, - &pyunit, - &update_fn, - &get_fn, - &set_fn)) { - return NULL; - } + static const char *_keywords[] = { + "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!O!ssOOO:FloatProperty", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + &id, + &id_len, + &name, + &description, + &def, + &min, + &max, + &soft_min, + &soft_max, + &step, + &precision, + &PySet_Type, + &pyopts, + &PySet_Type, + &pyopts_override, + &PySet_Type, + &py_tags, + &pysubtype, + &pyunit, + &update_fn, + &get_fn, + &set_fn)) { + return NULL; + } - BPY_PROPDEF_SUBTYPE_CHECK(FloatProperty, - property_flag_items, - property_flag_override_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); - return NULL; - } + 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); + return NULL; + } - if (bpy_prop_callback_check(update_fn, "update", 2) == -1) { - return NULL; - } - if (bpy_prop_callback_check(get_fn, "get", 1) == -1) { - return NULL; - } - if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { - return NULL; - } + if (bpy_prop_callback_check(update_fn, "update", 2) == -1) { + return NULL; + } + if (bpy_prop_callback_check(get_fn, "get", 1) == -1) { + return NULL; + } + if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { + return NULL; + } - prop = RNA_def_property(srna, id, PROP_FLOAT, subtype | unit); - RNA_def_property_float_default(prop, def); - RNA_def_property_range(prop, min, max); - RNA_def_property_ui_text(prop, name ? name : id, description); - RNA_def_property_ui_range(prop, MAX2(soft_min, min), MIN2(soft_max, max), step, precision); + prop = RNA_def_property(srna, id, PROP_FLOAT, subtype | unit); + RNA_def_property_float_default(prop, def); + RNA_def_property_range(prop, min, max); + RNA_def_property_ui_text(prop, name ? name : id, description); + RNA_def_property_ui_range(prop, MAX2(soft_min, min), MIN2(soft_max, max), step, precision); - if (py_tags) { - RNA_def_property_tags(prop, prop_tags); - } - 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_fn); - bpy_prop_callback_assign_float(prop, get_fn, set_fn); - RNA_def_property_duplicate_pointers(srna, prop); + if (py_tags) { + RNA_def_property_tags(prop, prop_tags); } + 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_fn); + bpy_prop_callback_assign_float(prop, get_fn, set_fn); + RNA_def_property_duplicate_pointers(srna, prop); + Py_RETURN_NONE; } @@ -3117,124 +3315,134 @@ PyDoc_STRVAR(BPy_FloatVectorProperty_doc, BPY_PROPDEF_UPDATE_DOC BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObject *kw) { + /* Keep this block first. */ StructRNA *srna; - BPY_PROPDEF_HEAD(FloatVectorProperty); + BLI_assert(srna != NULL); + + const char *id = NULL, *name = NULL, *description = ""; + Py_ssize_t id_len; + float min = -FLT_MAX, max = FLT_MAX, soft_min = -FLT_MAX, soft_max = FLT_MAX, step = 3; + float def[RNA_MAX_ARRAY_DIMENSION][PYRNA_STACK_ARRAY] = {{0.0f}}; + int precision = 2; + struct BPYPropArrayLength array_len_info = {.len_total = 3}; + PropertyRNA *prop; + 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; + const char *pyunit = NULL; + int unit = PROP_UNIT_NONE; + PyObject *update_fn = NULL; + PyObject *get_fn = NULL; + PyObject *set_fn = NULL; + PyObject *py_tags = NULL; - if (srna) { - const char *id = NULL, *name = NULL, *description = ""; - Py_ssize_t id_len; - float min = -FLT_MAX, max = FLT_MAX, soft_min = -FLT_MAX, soft_max = FLT_MAX, step = 3; - float def[PYRNA_STACK_ARRAY] = {0.0f}; - int precision = 2, size = 3; - PropertyRNA *prop; - 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; - const char *pyunit = NULL; - int unit = PROP_UNIT_NONE; - PyObject *update_fn = NULL; - PyObject *get_fn = NULL; - PyObject *set_fn = NULL; - PyObject *py_tags = NULL; - - static const char *_keywords[] = { - "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!O!ssiOOO:FloatVectorProperty", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast(args, - kw, - &_parser, - &id, - &id_len, - &name, - &description, - &pydef, - &min, - &max, - &soft_min, - &soft_max, - &step, - &precision, - &PySet_Type, - &pyopts, - &PySet_Type, - &pyopts_override, - &PySet_Type, - &py_tags, - &pysubtype, - &pyunit, - &size, - &update_fn, - &get_fn, - &set_fn)) { - return NULL; - } + static const char *_keywords[] = { + "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!O!ssO&OOO:FloatVectorProperty", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + &id, + &id_len, + &name, + &description, + &pydef, + &min, + &max, + &soft_min, + &soft_max, + &step, + &precision, + &PySet_Type, + &pyopts, + &PySet_Type, + &pyopts_override, + &PySet_Type, + &py_tags, + &pysubtype, + &pyunit, + bpy_prop_array_length_parse, + &array_len_info, + &update_fn, + &get_fn, + &set_fn)) { + return NULL; + } - BPY_PROPDEF_SUBTYPE_CHECK(FloatVectorProperty, - property_flag_items, - property_flag_override_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); - return NULL; - } + 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); + return NULL; + } - if (size < 1 || size > PYRNA_STACK_ARRAY) { - PyErr_Format( - PyExc_TypeError, - "FloatVectorProperty(size=%d): size must be between 0 and " STRINGIFY(PYRNA_STACK_ARRAY), - size); + if (pydef != NULL) { + if (bpy_prop_array_from_py_with_dims(def[0], + sizeof(*def[0]), + pydef, + &array_len_info, + &PyFloat_Type, + "FloatVectorProperty(default=sequence)") == -1) { return NULL; } - - if (pydef && - PyC_AsArray( - def, pydef, size, &PyFloat_Type, false, "FloatVectorProperty(default=sequence)") == - -1) { - return NULL; + if (bpy_prop_array_is_matrix_compatible_ex(subtype, &array_len_info)) { + bpy_prop_array_matrix_swap_row_column_vn(&def[0][0], &array_len_info); } + } - if (bpy_prop_callback_check(update_fn, "update", 2) == -1) { - return NULL; - } - if (bpy_prop_callback_check(get_fn, "get", 1) == -1) { - return NULL; + if (bpy_prop_callback_check(update_fn, "update", 2) == -1) { + return NULL; + } + if (bpy_prop_callback_check(get_fn, "get", 1) == -1) { + return NULL; + } + if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { + return NULL; + } + + prop = RNA_def_property(srna, id, PROP_FLOAT, subtype | unit); + if (array_len_info.dims_len == 0) { + RNA_def_property_array(prop, array_len_info.len_total); + if (pydef != NULL) { + RNA_def_property_float_array_default(prop, def[0]); } - if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { - return NULL; + } + else { + RNA_def_property_multi_array(prop, array_len_info.dims_len, array_len_info.dims); + if (pydef != NULL) { + RNA_def_property_float_array_default(prop, &def[0][0]); } + } - prop = RNA_def_property(srna, id, PROP_FLOAT, subtype | unit); - RNA_def_property_array(prop, size); - if (pydef) { - RNA_def_property_float_array_default(prop, def); - } - RNA_def_property_range(prop, min, max); - RNA_def_property_ui_text(prop, name ? name : id, description); - RNA_def_property_ui_range(prop, MAX2(soft_min, min), MIN2(soft_max, max), step, precision); + RNA_def_property_range(prop, min, max); + RNA_def_property_ui_text(prop, name ? name : id, description); + RNA_def_property_ui_range(prop, MAX2(soft_min, min), MIN2(soft_max, max), step, precision); - if (py_tags) { - RNA_def_property_tags(prop, prop_tags); - } - 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_fn); - bpy_prop_callback_assign_float_array(prop, get_fn, set_fn); - RNA_def_property_duplicate_pointers(srna, prop); + if (py_tags) { + RNA_def_property_tags(prop, prop_tags); + } + 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_fn); + bpy_prop_callback_assign_float_array(prop, get_fn, set_fn); + RNA_def_property_duplicate_pointers(srna, prop); + Py_RETURN_NONE; } @@ -3261,103 +3469,103 @@ PyDoc_STRVAR(BPy_StringProperty_doc, BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw) { + /* Keep this block first. */ StructRNA *srna; - BPY_PROPDEF_HEAD(StringProperty); + BLI_assert(srna != NULL); + + const char *id = NULL, *name = NULL, *description = "", *def = ""; + Py_ssize_t id_len; + int maxlen = 0; + 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; + PyObject *update_fn = NULL; + PyObject *get_fn = NULL; + PyObject *set_fn = NULL; + PyObject *py_tags = NULL; - if (srna) { - const char *id = NULL, *name = NULL, *description = "", *def = ""; - Py_ssize_t id_len; - int maxlen = 0; - 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; - PyObject *update_fn = NULL; - PyObject *get_fn = NULL; - PyObject *set_fn = NULL; - PyObject *py_tags = NULL; - - static const char *_keywords[] = { - "attr", - "name", - "description", - "default", - "maxlen", - "options", - "override", - "tags", - "subtype", - "update", - "get", - "set", - NULL, - }; - static _PyArg_Parser _parser = {"s#|$sssiO!O!O!sOOO:StringProperty", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast(args, - kw, - &_parser, - &id, - &id_len, - &name, - &description, - &def, - &maxlen, - &PySet_Type, - &pyopts, - &PySet_Type, - &pyopts_override, - &PySet_Type, - &py_tags, - &pysubtype, - &update_fn, - &get_fn, - &set_fn)) { - return NULL; - } + static const char *_keywords[] = { + "attr", + "name", + "description", + "default", + "maxlen", + "options", + "override", + "tags", + "subtype", + "update", + "get", + "set", + NULL, + }; + static _PyArg_Parser _parser = {"s#|$sssiO!O!O!sOOO:StringProperty", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + &id, + &id_len, + &name, + &description, + &def, + &maxlen, + &PySet_Type, + &pyopts, + &PySet_Type, + &pyopts_override, + &PySet_Type, + &py_tags, + &pysubtype, + &update_fn, + &get_fn, + &set_fn)) { + return NULL; + } - BPY_PROPDEF_SUBTYPE_CHECK(StringProperty, - property_flag_items, - property_flag_override_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_fn, "update", 2) == -1) { - return NULL; - } - if (bpy_prop_callback_check(get_fn, "get", 1) == -1) { - return NULL; - } - if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { - return NULL; - } + if (bpy_prop_callback_check(update_fn, "update", 2) == -1) { + return NULL; + } + if (bpy_prop_callback_check(get_fn, "get", 1) == -1) { + return NULL; + } + if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { + return NULL; + } - prop = RNA_def_property(srna, id, PROP_STRING, subtype); - if (maxlen != 0) { - /* +1 since it includes null terminator. */ - RNA_def_property_string_maxlength(prop, maxlen + 1); - } - if (def && def[0]) { - RNA_def_property_string_default(prop, def); - } - RNA_def_property_ui_text(prop, name ? name : id, description); + prop = RNA_def_property(srna, id, PROP_STRING, subtype); + if (maxlen != 0) { + /* +1 since it includes null terminator. */ + RNA_def_property_string_maxlength(prop, maxlen + 1); + } + if (def && def[0]) { + RNA_def_property_string_default(prop, def); + } + RNA_def_property_ui_text(prop, name ? name : id, description); - if (py_tags) { - RNA_def_property_tags(prop, prop_tags); - } - 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_fn); - bpy_prop_callback_assign_string(prop, get_fn, set_fn); - RNA_def_property_duplicate_pointers(srna, prop); + if (py_tags) { + RNA_def_property_tags(prop, prop_tags); } + 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_fn); + bpy_prop_callback_assign_string(prop, get_fn, set_fn); + RNA_def_property_duplicate_pointers(srna, prop); + Py_RETURN_NONE; } @@ -3418,152 +3626,152 @@ PyDoc_STRVAR( BPY_PROPDEF_GET_DOC BPY_PROPDEF_SET_DOC); static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) { + /* Keep this block first. */ StructRNA *srna; - BPY_PROPDEF_HEAD(EnumProperty); + BLI_assert(srna != NULL); + + const char *id = NULL, *name = NULL, *description = ""; + PyObject *def = NULL; + Py_ssize_t id_len; + int defvalue = 0; + PyObject *items, *items_fast; + const EnumPropertyItem *eitems; + 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_fn = NULL; + PyObject *get_fn = NULL; + PyObject *set_fn = NULL; + PyObject *py_tags = NULL; - if (srna) { - const char *id = NULL, *name = NULL, *description = ""; - PyObject *def = NULL; - Py_ssize_t id_len; - int defvalue = 0; - PyObject *items, *items_fast; - const EnumPropertyItem *eitems; - 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_fn = NULL; - PyObject *get_fn = NULL; - PyObject *set_fn = NULL; - PyObject *py_tags = NULL; - - static const char *_keywords[] = { - "attr", - "items", - "name", - "description", - "default", - "options", - "override", - "tags", - "update", - "get", - "set", - NULL, - }; - static _PyArg_Parser _parser = {"s#O|$ssOO!O!O!OOO:EnumProperty", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast(args, - kw, - &_parser, - &id, - &id_len, - &items, - &name, - &description, - &def, - &PySet_Type, - &pyopts, - &PySet_Type, - &pyopts_override, - &PySet_Type, - &py_tags, - &update_fn, - &get_fn, - &set_fn)) { - return NULL; - } + static const char *_keywords[] = { + "attr", + "items", + "name", + "description", + "default", + "options", + "override", + "tags", + "update", + "get", + "set", + NULL, + }; + static _PyArg_Parser _parser = {"s#O|$ssOO!O!O!OOO:EnumProperty", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + &id, + &id_len, + &items, + &name, + &description, + &def, + &PySet_Type, + &pyopts, + &PySet_Type, + &pyopts_override, + &PySet_Type, + &py_tags, + &update_fn, + &get_fn, + &set_fn)) { + return NULL; + } - BPY_PROPDEF_CHECK(EnumProperty, property_flag_enum_items, property_flag_override_items); + BPY_PROPDEF_CHECK(EnumProperty, property_flag_enum_items, property_flag_override_items); - if (bpy_prop_callback_check(update_fn, "update", 2) == -1) { - return NULL; - } - if (bpy_prop_callback_check(get_fn, "get", 1) == -1) { - return NULL; - } - if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { - return NULL; - } + if (bpy_prop_callback_check(update_fn, "update", 2) == -1) { + return NULL; + } + if (bpy_prop_callback_check(get_fn, "get", 1) == -1) { + return NULL; + } + if (bpy_prop_callback_check(set_fn, "set", 2) == -1) { + return NULL; + } + + if (def == Py_None) { + /* This allows to get same behavior when explicitly passing None as default value, + * and not defining a default value at all! */ + def = NULL; + } - if (def == Py_None) { - /* This allows to get same behavior when explicitly passing None as default value, - * and not defining a default value at all! */ - def = NULL; + /* items can be a list or a callable */ + if (PyFunction_Check( + items)) { /* don't use PyCallable_Check because we need the function code for errors */ + PyCodeObject *f_code = (PyCodeObject *)PyFunction_GET_CODE(items); + if (f_code->co_argcount != 2) { + PyErr_Format(PyExc_ValueError, + "EnumProperty(...): expected 'items' function to take 2 arguments, not %d", + f_code->co_argcount); + return NULL; } - /* items can be a list or a callable */ - if (PyFunction_Check( - items)) { /* don't use PyCallable_Check because we need the function code for errors */ - PyCodeObject *f_code = (PyCodeObject *)PyFunction_GET_CODE(items); - if (f_code->co_argcount != 2) { - PyErr_Format(PyExc_ValueError, - "EnumProperty(...): expected 'items' function to take 2 arguments, not %d", - f_code->co_argcount); + if (def) { + /* Only support getting integer default values here. */ + if (!py_long_as_int(def, &defvalue)) { + /* NOTE: using type error here is odd but python does this for invalid arguments. */ + PyErr_SetString( + PyExc_TypeError, + "EnumProperty(...): 'default' can only be an integer when 'items' is a function"); return NULL; } + } - if (def) { - /* Only support getting integer default values here. */ - if (!py_long_as_int(def, &defvalue)) { - /* NOTE: using type error here is odd but python does this for invalid arguments. */ - PyErr_SetString( - PyExc_TypeError, - "EnumProperty(...): 'default' can only be an integer when 'items' is a function"); - return NULL; - } - } - - is_itemf = true; - eitems = DummyRNA_NULL_items; + is_itemf = true; + eitems = DummyRNA_NULL_items; + } + else { + if (!(items_fast = PySequence_Fast( + items, + "EnumProperty(...): " + "expected a sequence of tuples for the enum items or a function"))) { + return NULL; } - else { - if (!(items_fast = PySequence_Fast( - items, - "EnumProperty(...): " - "expected a sequence of tuples for the enum items or a function"))) { - return NULL; - } - eitems = enum_items_from_py(items_fast, def, &defvalue, (opts & PROP_ENUM_FLAG) != 0); + eitems = enum_items_from_py(items_fast, def, &defvalue, (opts & PROP_ENUM_FLAG) != 0); - if (!eitems) { - Py_DECREF(items_fast); - return NULL; - } + if (!eitems) { + Py_DECREF(items_fast); + return NULL; } + } - if (opts & PROP_ENUM_FLAG) { - prop = RNA_def_enum_flag(srna, id, eitems, defvalue, name ? name : id, description); - } - else { - prop = RNA_def_enum(srna, id, eitems, defvalue, name ? name : id, description); - } + if (opts & PROP_ENUM_FLAG) { + prop = RNA_def_enum_flag(srna, id, eitems, defvalue, name ? name : id, description); + } + else { + prop = RNA_def_enum(srna, id, eitems, defvalue, name ? name : id, description); + } - if (py_tags) { - RNA_def_property_tags(prop, prop_tags); - } - 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_fn); - bpy_prop_callback_assign_enum(prop, get_fn, set_fn, (is_itemf ? items : NULL)); - RNA_def_property_duplicate_pointers(srna, prop); + if (py_tags) { + RNA_def_property_tags(prop, prop_tags); + } + 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_fn); + bpy_prop_callback_assign_enum(prop, get_fn, set_fn, (is_itemf ? items : NULL)); + RNA_def_property_duplicate_pointers(srna, prop); - if (is_itemf == false) { - /* NOTE: this must be postponed until after #RNA_def_property_duplicate_pointers - * otherwise if this is a generator it may free the strings before we copy them */ - Py_DECREF(items_fast); + if (is_itemf == false) { + /* NOTE: this must be postponed until after #RNA_def_property_duplicate_pointers + * otherwise if this is a generator it may free the strings before we copy them */ + Py_DECREF(items_fast); - MEM_freeN((void *)eitems); - } + MEM_freeN((void *)eitems); } + Py_RETURN_NONE; } @@ -3608,95 +3816,95 @@ PyDoc_STRVAR(BPy_PointerProperty_doc, BPY_PROPDEF_POLL_DOC BPY_PROPDEF_UPDATE_DOC); PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw) { + /* Keep this block first. */ StructRNA *srna; - BPY_PROPDEF_HEAD(PointerProperty); + BLI_assert(srna != NULL); + + const char *id = NULL, *name = NULL, *description = ""; + Py_ssize_t id_len; + PropertyRNA *prop; + 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_fn = NULL, *poll_fn = NULL; - if (srna) { - const char *id = NULL, *name = NULL, *description = ""; - Py_ssize_t id_len; - PropertyRNA *prop; - 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_fn = NULL, *poll_fn = NULL; - - static const char *_keywords[] = { - "attr", - "type", - "name", - "description", - "options", - "override", - "tags", - "poll", - "update", - NULL, - }; - static _PyArg_Parser _parser = {"s#O|$ssO!O!O!OO:PointerProperty", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast(args, - kw, - &_parser, - &id, - &id_len, - &type, - &name, - &description, - &PySet_Type, - &pyopts, - &PySet_Type, - &pyopts_override, - &PySet_Type, - &py_tags, - &poll_fn, - &update_fn)) { - return NULL; - } + static const char *_keywords[] = { + "attr", + "type", + "name", + "description", + "options", + "override", + "tags", + "poll", + "update", + NULL, + }; + static _PyArg_Parser _parser = {"s#O|$ssO!O!O!OO:PointerProperty", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + &id, + &id_len, + &type, + &name, + &description, + &PySet_Type, + &pyopts, + &PySet_Type, + &pyopts_override, + &PySet_Type, + &py_tags, + &poll_fn, + &update_fn)) { + return NULL; + } - BPY_PROPDEF_CHECK(PointerProperty, property_flag_items, property_flag_override_items); + BPY_PROPDEF_CHECK(PointerProperty, property_flag_items, property_flag_override_items); - ptype = pointer_type_from_py(type, "PointerProperty(...)"); - if (!ptype) { - return NULL; - } - if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup) && !RNA_struct_is_ID(ptype)) { - PyErr_Format(PyExc_TypeError, - "PointerProperty(...) expected an RNA type derived from %.200s or %.200s", - RNA_struct_ui_name(&RNA_ID), - RNA_struct_ui_name(&RNA_PropertyGroup)); - return NULL; - } - if (bpy_prop_callback_check(update_fn, "update", 2) == -1) { - return NULL; - } - if (bpy_prop_callback_check(poll_fn, "poll", 2) == -1) { - return NULL; - } - prop = RNA_def_pointer_runtime(srna, id, ptype, name ? name : id, description); - if (py_tags) { - RNA_def_property_tags(prop, prop_tags); - } - if (pyopts) { - bpy_prop_assign_flag(prop, opts); - } - if (pyopts_override) { - bpy_prop_assign_flag_override(prop, opts_override); - } + ptype = pointer_type_from_py(type, "PointerProperty(...)"); + if (!ptype) { + return NULL; + } + if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup) && !RNA_struct_is_ID(ptype)) { + PyErr_Format(PyExc_TypeError, + "PointerProperty(...) expected an RNA type derived from %.200s or %.200s", + RNA_struct_ui_name(&RNA_ID), + RNA_struct_ui_name(&RNA_PropertyGroup)); + return NULL; + } + if (bpy_prop_callback_check(update_fn, "update", 2) == -1) { + return NULL; + } + if (bpy_prop_callback_check(poll_fn, "poll", 2) == -1) { + return NULL; + } + prop = RNA_def_pointer_runtime(srna, id, ptype, name ? name : id, description); + if (py_tags) { + RNA_def_property_tags(prop, prop_tags); + } + 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)) { - RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES); - } + if (RNA_struct_idprops_contains_datablock(ptype)) { + if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) { + RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES); } - bpy_prop_callback_assign_update(prop, update_fn); - bpy_prop_callback_assign_pointer(prop, poll_fn); - RNA_def_property_duplicate_pointers(srna, prop); } + bpy_prop_callback_assign_update(prop, update_fn); + bpy_prop_callback_assign_pointer(prop, poll_fn); + RNA_def_property_duplicate_pointers(srna, prop); + Py_RETURN_NONE; } @@ -3714,84 +3922,84 @@ PyDoc_STRVAR(BPy_CollectionProperty_doc, BPY_PROPDEF_TAGS_DOC); PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw) { + /* Keep this block first. */ StructRNA *srna; - BPY_PROPDEF_HEAD(CollectionProperty); + BLI_assert(srna != NULL); + + Py_ssize_t id_len; + const char *id = NULL, *name = NULL, *description = ""; + PropertyRNA *prop; + 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; - if (srna) { - Py_ssize_t id_len; - const char *id = NULL, *name = NULL, *description = ""; - PropertyRNA *prop; - 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[] = { - "attr", - "type", - "name", - "description", - "options", - "override", - "tags", - NULL, - }; - static _PyArg_Parser _parser = {"s#O|$ssO!O!O!:CollectionProperty", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast(args, - kw, - &_parser, - &id, - &id_len, - &type, - &name, - &description, - &PySet_Type, - &pyopts, - &PySet_Type, - &pyopts_override, - &PySet_Type, - &py_tags)) { - return NULL; - } + static const char *_keywords[] = { + "attr", + "type", + "name", + "description", + "options", + "override", + "tags", + NULL, + }; + static _PyArg_Parser _parser = {"s#O|$ssO!O!O!:CollectionProperty", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + &id, + &id_len, + &type, + &name, + &description, + &PySet_Type, + &pyopts, + &PySet_Type, + &pyopts_override, + &PySet_Type, + &py_tags)) { + return NULL; + } - BPY_PROPDEF_CHECK( - CollectionProperty, property_flag_items, property_flag_override_collection_items); + BPY_PROPDEF_CHECK( + CollectionProperty, property_flag_items, property_flag_override_collection_items); - ptype = pointer_type_from_py(type, "CollectionProperty(...):"); - if (!ptype) { - return NULL; - } + ptype = pointer_type_from_py(type, "CollectionProperty(...):"); + if (!ptype) { + return NULL; + } - if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup)) { - PyErr_Format(PyExc_TypeError, - "CollectionProperty(...) expected an RNA type derived from %.200s", - RNA_struct_ui_name(&RNA_PropertyGroup)); - return NULL; - } + if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup)) { + PyErr_Format(PyExc_TypeError, + "CollectionProperty(...) expected an RNA type derived from %.200s", + RNA_struct_ui_name(&RNA_PropertyGroup)); + return NULL; + } - prop = RNA_def_collection_runtime(srna, id, ptype, name ? name : id, description); - if (py_tags) { - RNA_def_property_tags(prop, prop_tags); - } - if (pyopts) { - bpy_prop_assign_flag(prop, opts); - } - if (pyopts_override) { - bpy_prop_assign_flag_override(prop, opts_override); - } + prop = RNA_def_collection_runtime(srna, id, ptype, name ? name : id, description); + if (py_tags) { + RNA_def_property_tags(prop, prop_tags); + } + 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)) { - RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES); - } + if (RNA_struct_idprops_contains_datablock(ptype)) { + if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) { + RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES); } - RNA_def_property_duplicate_pointers(srna, prop); } + RNA_def_property_duplicate_pointers(srna, prop); + Py_RETURN_NONE; } diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index e6f3e509469..7ecdbe4b4d9 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -4254,7 +4254,7 @@ static PyObject *pyrna_struct_dir(BPy_StructRNA *self) } PyDoc_STRVAR(pyrna_struct_id_properties_ensure_doc, - ".. method:: id_properties_ensure()\n" + ".. method:: id_properties_ensure()\n\n" " :return: the parent group for an RNA struct's custom IDProperties.\n" " :rtype: :class:`bpy.types.IDPropertyGroup`\n"); static PyObject *pyrna_struct_id_properties_ensure(BPy_StructRNA *self) @@ -4282,7 +4282,7 @@ static PyObject *pyrna_struct_id_properties_ensure(BPy_StructRNA *self) } PyDoc_STRVAR(pyrna_struct_id_properties_clear_doc, - ".. method:: id_properties_clear()\n" + ".. method:: id_properties_clear()\n\n" " :return: Remove the parent group for an RNA struct's custom IDProperties.\n"); static PyObject *pyrna_struct_id_properties_clear(BPy_StructRNA *self) { @@ -7424,10 +7424,13 @@ static PyObject *pyrna_srna_Subtype(StructRNA *srna) PyObject *metaclass; const char *idname = RNA_struct_identifier(srna); - /* Remove __doc__ for now. */ - // const char *descr = RNA_struct_ui_description(srna); - // if (!descr) descr = "(no docs)"; - // "__doc__", descr + /* Remove `__doc__` for now because we don't need it to generate docs. */ +#if 0 + const char *descr = RNA_struct_ui_description(srna); + if (!descr) { + descr = "(no docs)"; + } +#endif if (RNA_struct_idprops_check(srna) && !PyObject_IsSubclass(py_base, (PyObject *)&pyrna_struct_meta_idprop_Type)) { diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c index 9745f39b6b8..a716f4ab9e5 100644 --- a/source/blender/python/intern/bpy_rna_anim.c +++ b/source/blender/python/intern/bpy_rna_anim.c @@ -605,7 +605,7 @@ PyObject *pyrna_struct_driver_add(BPy_StructRNA *self, PyObject *args) DEG_relations_tag_update(CTX_data_main(context)); } else { - /* XXX, should be handled by reports, */ + /* XXX: should be handled by reports. */ PyErr_SetString(PyExc_TypeError, "bpy_struct.driver_add(): failed because of an internal error"); return NULL; diff --git a/source/blender/python/intern/bpy_rna_gizmo.c b/source/blender/python/intern/bpy_rna_gizmo.c index 869019692df..f121bfd6e36 100644 --- a/source/blender/python/intern/bpy_rna_gizmo.c +++ b/source/blender/python/intern/bpy_rna_gizmo.c @@ -80,10 +80,10 @@ static void py_rna_gizmo_handler_get_cb(const wmGizmo *UNUSED(gz), } else { if (PyC_AsArray(value, + sizeof(*value), ret, gz_prop->type->array_length, &PyFloat_Type, - false, "Gizmo get callback: ") == -1) { goto fail; } @@ -103,6 +103,8 @@ fail: PyErr_Print(); PyErr_Clear(); + Py_DECREF(ret); + PyGILState_Release(gilstate); } @@ -139,6 +141,7 @@ static void py_rna_gizmo_handler_set_cb(const wmGizmo *UNUSED(gz), if (ret == NULL) { goto fail; } + Py_DECREF(args); Py_DECREF(ret); PyGILState_Release(gilstate); @@ -199,11 +202,11 @@ static void py_rna_gizmo_handler_range_get_cb(const wmGizmo *UNUSED(gz), return; fail: - Py_XDECREF(ret); - PyErr_Print(); PyErr_Clear(); + Py_XDECREF(ret); + PyGILState_Release(gilstate); } @@ -426,11 +429,11 @@ static PyObject *bpy_gizmo_target_set_value(PyObject *UNUSED(self), PyObject *ar if (array_len != 0) { float *value = BLI_array_alloca(value, array_len); if (PyC_AsArray(value, + sizeof(*value), params.value, gz_prop->type->array_length, &PyFloat_Type, - false, - "Gizmo target property array") == -1) { + "Gizmo target property array: ") == -1) { goto fail; } WM_gizmo_target_property_float_set_array(BPY_context_get(), gz, gz_prop, value); diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c index 66044311321..1bb68babc3c 100644 --- a/source/blender/python/intern/bpy_rna_id_collection.c +++ b/source/blender/python/intern/bpy_rna_id_collection.c @@ -377,9 +377,16 @@ static PyObject *bpy_orphans_purge(PyObject *UNUSED(self), PyObject *args, PyObj bool do_recursive_cleanup = false; static const char *_keywords[] = {"do_local_ids", "do_linked_ids", "do_recursive", NULL}; - static _PyArg_Parser _parser = {"|$ppp:orphans_purge", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast( - args, kwds, &_parser, &do_local_ids, &do_linked_ids, &do_recursive_cleanup)) { + static _PyArg_Parser _parser = {"|O&O&O&:orphans_purge", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kwds, + &_parser, + PyC_ParseBool, + &do_local_ids, + PyC_ParseBool, + &do_linked_ids, + PyC_ParseBool, + &do_recursive_cleanup)) { return NULL; } diff --git a/source/blender/python/intern/bpy_utils_units.c b/source/blender/python/intern/bpy_utils_units.c index e5ac1ba9a95..aa8cf8f2a9f 100644 --- a/source/blender/python/intern/bpy_utils_units.c +++ b/source/blender/python/intern/bpy_utils_units.c @@ -67,7 +67,7 @@ static const char *bpyunits_ucategorie_items[] = { /** * These fields are just empty placeholders, actual values get set in initializations functions. * This allows us to avoid many handwriting, and above all, - * to keep all systems/categories definition stuff in ``BKE_unit.h``. + * to keep all systems/categories definition stuff in `BKE_unit.h`. */ static PyStructSequence_Field bpyunits_systems_fields[ARRAY_SIZE(bpyunits_usystem_items)]; static PyStructSequence_Field bpyunits_categories_fields[ARRAY_SIZE(bpyunits_ucategorie_items)]; diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c index 16bf7120606..5beca7bd71a 100644 --- a/source/blender/python/mathutils/mathutils.c +++ b/source/blender/python/mathutils/mathutils.c @@ -78,7 +78,7 @@ static int mathutils_array_parse_fast(float *array, } /** - * helper function that returns a Python ``__hash__``. + * helper function that returns a Python `__hash__`. * * \note consistent with the equivalent tuple of floats (CPython's 'tuplehash') */ diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index 8b8130f3cc2..36b8b0b6d35 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -1174,7 +1174,7 @@ static void matrix_invert_with_det_n_internal(float *mat_dst, } /** - * \param r_mat: can be from ``self->matrix`` or not. + * \param r_mat: can be from `self->matrix` or not. */ static bool matrix_invert_internal(const MatrixObject *self, float *r_mat) { @@ -1191,8 +1191,8 @@ static bool matrix_invert_internal(const MatrixObject *self, float *r_mat) } /** - * Similar to ``matrix_invert_internal`` but should never error. - * \param r_mat: can be from ``self->matrix`` or not. + * Similar to `matrix_invert_internal` but should never error. + * \param r_mat: can be from `self->matrix` or not. */ static void matrix_invert_safe_internal(const MatrixObject *self, float *r_mat) { diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c index 88b3bddddf6..c73dea79aac 100644 --- a/source/blender/python/mathutils/mathutils_geometry.c +++ b/source/blender/python/mathutils/mathutils_geometry.c @@ -1505,6 +1505,9 @@ static PyObject *list_of_lists_from_arrays(const int *array, PyObject *ret, *sublist; int i, j, sublist_len, sublist_start, val; + if (array == NULL) { + return PyList_New(0); + } ret = PyList_New(toplevel_len); for (i = 0; i < toplevel_len; i++) { sublist_len = len_table[i]; @@ -1521,7 +1524,8 @@ static PyObject *list_of_lists_from_arrays(const int *array, PyDoc_STRVAR( M_Geometry_delaunay_2d_cdt_doc, - ".. function:: delaunay_2d_cdt(vert_coords, edges, faces, output_type, epsilon)\n" + ".. function:: delaunay_2d_cdt(vert_coords, edges, faces, output_type, epsilon, " + "need_ids=True)\n" "\n" " Computes the Constrained Delaunay Triangulation of a set of vertices,\n" " with edges and faces that must appear in the triangulation.\n" @@ -1533,6 +1537,8 @@ PyDoc_STRVAR( " input element indices corresponding to the positionally same output element.\n" " For edges, the orig indices start with the input edges and then continue\n" " with the edges implied by each of the faces (n of them for an n-gon).\n" + " If the need_ids argument is supplied, and False, then the code skips the preparation\n" + " of the orig arrays, which may save some time." "\n" " :arg vert_coords: Vertex coordinates (2d)\n" " :type vert_coords: list of :class:`mathutils.Vector`\n" @@ -1543,10 +1549,14 @@ PyDoc_STRVAR( " :arg output_type: What output looks like. 0 => triangles with convex hull. " "1 => triangles inside constraints. " "2 => the input constraints, intersected. " - "3 => like 2 but with extra edges to make valid BMesh faces.\n" + "3 => like 2 but detect holes and omit them from output. " + "4 => like 2 but with extra edges to make valid BMesh faces. " + "5 => like 4 but detect holes and omit them from output.\n" " :type output_type: int\\n" " :arg epsilon: For nearness tests; should not be zero\n" " :type epsilon: float\n" + " :arg need_ids: are the orig output arrays needed?\n" + " :type need_args: bool\n" " :return: Output tuple, (vert_coords, edges, faces, orig_verts, orig_edges, orig_faces)\n" " :rtype: (list of `mathutils.Vector`, " "list of (int, int), " @@ -1561,6 +1571,7 @@ static PyObject *M_Geometry_delaunay_2d_cdt(PyObject *UNUSED(self), PyObject *ar PyObject *vert_coords, *edges, *faces, *item; int output_type; float epsilon; + bool need_ids = true; float(*in_coords)[2] = NULL; int(*in_edges)[2] = NULL; int *in_faces = NULL; @@ -1578,8 +1589,14 @@ static PyObject *M_Geometry_delaunay_2d_cdt(PyObject *UNUSED(self), PyObject *ar PyObject *ret_value = NULL; int i; - if (!PyArg_ParseTuple( - args, "OOOif:delaunay_2d_cdt", &vert_coords, &edges, &faces, &output_type, &epsilon)) { + if (!PyArg_ParseTuple(args, + "OOOif|p:delaunay_2d_cdt", + &vert_coords, + &edges, + &faces, + &output_type, + &epsilon, + &need_ids)) { return NULL; } @@ -1609,6 +1626,7 @@ static PyObject *M_Geometry_delaunay_2d_cdt(PyObject *UNUSED(self), PyObject *ar in.faces_start_table = in_faces_start_table; in.faces_len_table = in_faces_len_table; in.epsilon = epsilon; + in.need_ids = need_ids; res = BLI_delaunay_2d_cdt_calc(&in, output_type); diff --git a/source/blender/python/mathutils/mathutils_noise.c b/source/blender/python/mathutils/mathutils_noise.c index 707fd40e9d0..69d37b345c6 100644 --- a/source/blender/python/mathutils/mathutils_noise.c +++ b/source/blender/python/mathutils/mathutils_noise.c @@ -131,8 +131,7 @@ static void next_state(void) ulong *p = state; int j; - /* if init_genrand() has not been called, */ - /* a default initial seed is used */ + /* If init_genrand() has not been called, a default initial seed is used. */ if (initf == 0) { init_genrand(5489UL); } diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c index ada897ab81c..f9c8a26dd1a 100644 --- a/source/blender/render/intern/bake.c +++ b/source/blender/render/intern/bake.c @@ -26,7 +26,7 @@ * The Bake API is fully implemented with Python rna functions. * The operator expects/call a function: * - * ``def bake(scene, object, pass_type, object_id, pixel_array, num_pixels, depth, result)`` + * `def bake(scene, object, pass_type, object_id, pixel_array, num_pixels, depth, result)` * - scene: current scene (Python object) * - object: object to render (Python object) * - pass_type: pass to render (string, e.g., "COMBINED", "AO", "NORMAL", ...) @@ -53,10 +53,10 @@ * \endcode * * In python you have access to: - * - ``primitive_id``, ``object_id``, ``uv``, ``du_dx``, ``du_dy``, ``next`` - * - ``next()`` is a function that returns the next #BakePixel in the array. + * - `primitive_id`, `object_id`, `uv`, `du_dx`, `du_dy`, `next`. + * - `next()` is a function that returns the next #BakePixel in the array. * - * \note Pixels that should not be baked have ``primitive_id == -1`` + * \note Pixels that should not be baked have `primitive_id == -1`. * * For a complete implementation example look at the Cycles Bake commit. */ diff --git a/source/blender/render/intern/texture_image.c b/source/blender/render/intern/texture_image.c index c4ee16a0ecc..62aee564626 100644 --- a/source/blender/render/intern/texture_image.c +++ b/source/blender/render/intern/texture_image.c @@ -932,8 +932,8 @@ static void feline_eval(TexResult *texr, ImBuf *ibuf, float fx, float fy, afdata #endif /* `const int out =` */ ibuf_get_color_clip_bilerp( tc, ibuf, ibuf->x * u, ibuf->y * v, AFD->intpol, AFD->extflag); - /* TXF alpha: clip |= out; - * TXF alpha: cw += out ? 0.0f : wt; */ + /* TXF alpha: `clip |= out;` + * TXF alpha: `cw += out ? 0.0f : wt;` */ texr->tr += tc[0] * wt; texr->tg += tc[1] * wt; texr->tb += tc[2] * wt; diff --git a/source/blender/sequencer/SEQ_effects.h b/source/blender/sequencer/SEQ_effects.h index 79a36be5a0b..4bd5b54b36b 100644 --- a/source/blender/sequencer/SEQ_effects.h +++ b/source/blender/sequencer/SEQ_effects.h @@ -70,9 +70,6 @@ struct SeqEffectHandle { * 2: out = ibuf2 */ int (*early_out)(struct Sequence *seq, float facf0, float facf1); - /* stores the y-range of the effect IPO */ - void (*store_icu_yrange)(struct Sequence *seq, short adrcode, float *ymin, float *ymax); - /* stores the default facf0 and facf1 if no IPO is present */ void (*get_default_fac)(struct Sequence *seq, float timeline_frame, float *facf0, float *facf1); diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h index e4c9f20f736..cb2091511a9 100644 --- a/source/blender/sequencer/SEQ_iterator.h +++ b/source/blender/sequencer/SEQ_iterator.h @@ -71,11 +71,13 @@ bool SEQ_iterator_ensure(SeqCollection *collection, struct Sequence *SEQ_iterator_yield(SeqIterator *iterator); SeqCollection *SEQ_collection_create(const char *name); +SeqCollection *SEQ_collection_duplicate(SeqCollection *collection); uint SEQ_collection_len(const SeqCollection *collection); bool SEQ_collection_append_strip(struct Sequence *seq, SeqCollection *data); bool SEQ_collection_remove_strip(struct Sequence *seq, SeqCollection *data); void SEQ_collection_free(SeqCollection *collection); void SEQ_collection_merge(SeqCollection *collection_dst, SeqCollection *collection_src); +void SEQ_collection_exclude(SeqCollection *collection, SeqCollection *exclude_elements); void SEQ_collection_expand(struct ListBase *seqbase, SeqCollection *collection, void query_func(struct Sequence *seq_reference, diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index 0a8be3b33ae..7757271a2e5 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -1428,10 +1428,9 @@ static void do_mul_effect_byte(float facf0, fac1 = (int)(256.0f * facf0); fac3 = (int)(256.0f * facf1); - /* formula: - * fac * (a * b) + (1 - fac) * a => fac * a * (b - 1) + axaux = c * px + py * s; //+centx - * yaux = -s * px + c * py; //+centy - */ + /* Formula: + * `fac * (a * b) + (1 - fac) * a => fac * a * (b - 1) + axaux = c * px + py * s;` // + centx + * `yaux = -s * px + c * py;` // + centy */ while (y--) { @@ -1483,9 +1482,8 @@ static void do_mul_effect_float( fac1 = facf0; fac3 = facf1; - /* formula: - * fac * (a * b) + (1 - fac) * a => fac * a * (b - 1) + a - */ + /* Formula: + * `fac * (a * b) + (1 - fac) * a => fac * a * (b - 1) + a`. */ while (y--) { x = xo; @@ -3086,10 +3084,12 @@ static void init_speed_effect(Sequence *seq) seq->effectdata = MEM_callocN(sizeof(SpeedControlVars), "speedcontrolvars"); v = (SpeedControlVars *)seq->effectdata; - v->globalSpeed = 1.0; v->frameMap = NULL; - v->flags |= SEQ_SPEED_INTEGRATE; /* should be default behavior */ v->length = 0; + v->speed_control_type = SEQ_SPEED_STRETCH; + v->speed_fader = 1.0f; + v->speed_fader_length = 0.0f; + v->speed_fader_frame_number = 0.0f; } static void load_speed_effect(Sequence *seq) @@ -3131,29 +3131,6 @@ static int early_out_speed(Sequence *UNUSED(seq), float UNUSED(facf0), float UNU return EARLY_DO_EFFECT; } -static void store_icu_yrange_speed(Sequence *seq, short UNUSED(adrcode), float *ymin, float *ymax) -{ - SpeedControlVars *v = (SpeedControlVars *)seq->effectdata; - - /* if not already done, load / initialize data */ - SEQ_effect_handle_get(seq); - - if ((v->flags & SEQ_SPEED_INTEGRATE) != 0) { - *ymin = -100.0; - *ymax = 100.0; - } - else { - if (v->flags & SEQ_SPEED_COMPRESS_IPO_Y) { - *ymin = 0.0; - *ymax = 1.0; - } - else { - *ymin = 0.0; - *ymax = seq->len; - } - } -} - /** * Generator strips with zero inputs have their length set to 1 permanently. In some cases it is * useful to use speed effect on these strips because they can be animated. This can be done by @@ -3174,7 +3151,6 @@ void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq, bool force) float fallback_fac = 1.0f; SpeedControlVars *v = (SpeedControlVars *)seq->effectdata; FCurve *fcu = NULL; - int flags = v->flags; /* if not already done, load / initialize data */ SEQ_effect_handle_get(seq); @@ -3189,7 +3165,20 @@ void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq, bool force) /* XXX(campbell): new in 2.5x. should we use the animation system this way? * The fcurve is needed because many frames need evaluating at once. */ - fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_factor", 0, NULL); + switch (v->speed_control_type) { + case SEQ_SPEED_MULTIPLY: { + fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_factor", 0, NULL); + break; + } + case SEQ_SPEED_FRAME_NUMBER: { + fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_frame_number", 0, NULL); + break; + } + case SEQ_SPEED_LENGTH: { + fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_length", 0, NULL); + break; + } + } if (!v->frameMap || v->length != seq->len) { if (v->frameMap) { MEM_freeN(v->frameMap); @@ -3204,21 +3193,33 @@ void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq, bool force) const int target_strip_length = seq_effect_speed_get_strip_content_length(seq->seq1); - if (seq->flag & SEQ_USE_EFFECT_DEFAULT_FADE) { + if (v->speed_control_type == SEQ_SPEED_STRETCH) { if ((seq->seq1->enddisp != seq->seq1->start) && (target_strip_length != 0)) { fallback_fac = (float)target_strip_length / (float)(seq->seq1->enddisp - seq->seq1->start); - flags = SEQ_SPEED_INTEGRATE; fcu = NULL; } } else { /* if there is no fcurve, use value as simple multiplier */ if (!fcu) { - fallback_fac = seq->speed_fader; /* Same as speed_factor in RNA. */ + switch (v->speed_control_type) { + case SEQ_SPEED_MULTIPLY: { + fallback_fac = v->speed_fader; + break; + } + case SEQ_SPEED_FRAME_NUMBER: { + fallback_fac = v->speed_fader_frame_number; + break; + } + case SEQ_SPEED_LENGTH: { + fallback_fac = v->speed_fader_length; + break; + } + } } } - if (flags & SEQ_SPEED_INTEGRATE) { + if (ELEM(v->speed_control_type, SEQ_SPEED_MULTIPLY, SEQ_SPEED_STRETCH)) { float cursor = 0; float facf; @@ -3232,7 +3233,6 @@ void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq, bool force) else { facf = fallback_fac; } - facf *= v->globalSpeed; cursor += facf; @@ -3258,10 +3258,10 @@ void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq, bool force) facf = fallback_fac; } - if (flags & SEQ_SPEED_COMPRESS_IPO_Y) { + if (v->speed_control_type == SEQ_SPEED_LENGTH) { facf *= target_strip_length; + facf /= 100.0f; } - facf *= v->globalSpeed; if (facf >= target_strip_length) { facf = target_strip_length - 1; @@ -4083,14 +4083,6 @@ static int early_out_mul_input2(Sequence *UNUSED(seq), float facf0, float facf1) return EARLY_DO_EFFECT; } -static void store_icu_yrange_noop(Sequence *UNUSED(seq), - short UNUSED(adrcode), - float *UNUSED(ymin), - float *UNUSED(ymax)) -{ - /* defaults are fine */ -} - static void get_default_fac_noop(Sequence *UNUSED(seq), float UNUSED(timeline_frame), float *facf0, @@ -4130,7 +4122,6 @@ static struct SeqEffectHandle get_sequence_effect_impl(int seq_type) rval.free = free_noop; rval.early_out = early_out_noop; rval.get_default_fac = get_default_fac_noop; - rval.store_icu_yrange = store_icu_yrange_noop; rval.execute = NULL; rval.init_execution = init_execution; rval.execute_slice = NULL; @@ -4244,7 +4235,6 @@ static struct SeqEffectHandle get_sequence_effect_impl(int seq_type) rval.copy = copy_speed_effect; rval.execute = do_speed_effect; rval.early_out = early_out_speed; - rval.store_icu_yrange = store_icu_yrange_speed; break; case SEQ_TYPE_COLOR: rval.init = init_solid_color; diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c index 20a2ee3b183..09f033e70fb 100644 --- a/source/blender/sequencer/intern/iterator.c +++ b/source/blender/sequencer/intern/iterator.c @@ -184,6 +184,22 @@ void SEQ_collection_merge(SeqCollection *collection_dst, SeqCollection *collecti } /** + * Remove strips from collection that are also in `exclude_elements`. Source collection will be + * freed. + * + * \param collection: collection from which strips are removed + * \param exclude_elements: collection of strips to be removed + */ +void SEQ_collection_exclude(SeqCollection *collection, SeqCollection *exclude_elements) +{ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, exclude_elements) { + SEQ_collection_remove_strip(seq, collection); + } + SEQ_collection_free(exclude_elements); +} + +/** * Expand collection by running SEQ_query() for each strip, which will be used as reference. * Results of these queries will be merged into provided collection. * @@ -213,6 +229,22 @@ void SEQ_collection_expand(ListBase *seqbase, } } +/** + * Duplicate collection + * + * \param collection: collection to be duplicated + * \return duplicate of collection + */ +SeqCollection *SEQ_collection_duplicate(SeqCollection *collection) +{ + SeqCollection *duplicate = SEQ_collection_create(__func__); + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + SEQ_collection_append_strip(seq, duplicate); + } + return duplicate; +} + /** \} */ /** diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c index 0dc8dfa10d7..e92afee08cd 100644 --- a/source/blender/sequencer/intern/strip_edit.c +++ b/source/blender/sequencer/intern/strip_edit.c @@ -112,8 +112,8 @@ static void seq_update_muting_recursive(ListBase *seqbasep, Sequence *metaseq, i Sequence *seq; int seqmute; - /* for sound we go over full meta tree to update muted state, - * since sound is played outside of evaluating the imbufs, */ + /* For sound we go over full meta tree to update muted state, + * since sound is played outside of evaluating the imbufs. */ for (seq = seqbasep->first; seq; seq = seq->next) { seqmute = (mute || (seq->flag & SEQ_MUTE)); diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index 20e2421ea88..68128690773 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -111,8 +111,8 @@ static void seq_update_sound_bounds_recursive_impl(Scene *scene, { Sequence *seq; - /* for sound we go over full meta tree to update bounds of the sound strips, - * since sound is played outside of evaluating the imbufs, */ + /* For sound we go over full meta tree to update bounds of the sound strips, + * since sound is played outside of evaluating the imbufs. */ for (seq = metaseq->seqbase.first; seq; seq = seq->next) { if (seq->type == SEQ_TYPE_META) { seq_update_sound_bounds_recursive_impl( diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index b7989349ebe..c9af2fced65 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -269,9 +269,9 @@ bool SEQ_transform_seqbase_shuffle_ex(ListBase *seqbasep, } test->machine += channel_delta; - SEQ_time_update_sequence( - evil_scene, - test); // XXX: I don't think this is needed since were only moving vertically, Campbell. + + /* XXX: I don't think this is needed since were only moving vertically, Campbell. */ + SEQ_time_update_sequence(evil_scene, test); } if ((test->machine < 1) || (test->machine > MAXSEQ)) { diff --git a/source/blender/shader_fx/intern/FX_ui_common.c b/source/blender/shader_fx/intern/FX_ui_common.c index 8a259c6aaff..8cb1ea6b66c 100644 --- a/source/blender/shader_fx/intern/FX_ui_common.c +++ b/source/blender/shader_fx/intern/FX_ui_common.c @@ -9,7 +9,7 @@ * 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, + * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ diff --git a/source/blender/simulation/intern/SIM_mass_spring.cpp b/source/blender/simulation/intern/SIM_mass_spring.cpp index ca01120eecb..f9a22276363 100644 --- a/source/blender/simulation/intern/SIM_mass_spring.cpp +++ b/source/blender/simulation/intern/SIM_mass_spring.cpp @@ -213,7 +213,8 @@ int SIM_cloth_solver_init(Object *UNUSED(ob), ClothModifierData *clmd) return 1; } -void SIM_mass_spring_set_implicit_vertex_mass(Implicit_Data *data, int index, float mass){ +void SIM_mass_spring_set_implicit_vertex_mass(Implicit_Data *data, int index, float mass) +{ SIM_mass_spring_set_vertex_mass(data, index, mass); } @@ -1051,18 +1052,22 @@ static void cloth_continuum_step(ClothModifierData *clmd, float dt) SIM_hair_volume_grid_interpolate(grid, x, &gdensity, gvel, gvel_smooth, NULL, NULL); - // BKE_sim_debug_data_add_circle( - // clmd->debug_data, x, gdensity, 0.7, 0.3, 1, - // "grid density", i, j, 3111); +# if 0 + BKE_sim_debug_data_add_circle( + clmd->debug_data, x, gdensity, 0.7, 0.3, 1, + "grid density", i, j, 3111); +# endif if (!is_zero_v3(gvel) || !is_zero_v3(gvel_smooth)) { float dvel[3]; sub_v3_v3v3(dvel, gvel_smooth, gvel); - // BKE_sim_debug_data_add_vector( - // clmd->debug_data, x, gvel, 0.4, 0, 1, - // "grid velocity", i, j, 3112); - // BKE_sim_debug_data_add_vector( - // clmd->debug_data, x, gvel_smooth, 0.6, 1, 1, - // "grid velocity", i, j, 3113); +# if 0 + BKE_sim_debug_data_add_vector( + clmd->debug_data, x, gvel, 0.4, 0, 1, + "grid velocity", i, j, 3112); + BKE_sim_debug_data_add_vector( + clmd->debug_data, x, gvel_smooth, 0.6, 1, 1, + "grid velocity", i, j, 3113); +# endif BKE_sim_debug_data_add_vector( clmd->debug_data, x, dvel, 0.4, 1, 0.7, "grid velocity", i, j, 3114); # if 0 @@ -1073,12 +1078,14 @@ static void cloth_continuum_step(ClothModifierData *clmd, float dt) interp_v3_v3v3(col, col0, col1, CLAMPIS(gdensity * clmd->sim_parms->density_strength, 0.0, 1.0)); - // BKE_sim_debug_data_add_circle( - // clmd->debug_data, x, gdensity * clmd->sim_parms->density_strength, 0, 1, 0.4, - // "grid velocity", i, j, 3115); - // BKE_sim_debug_data_add_dot( - // clmd->debug_data, x, col[0], col[1], col[2], - // "grid velocity", i, j, 3115); +# if 0 + BKE_sim_debug_data_add_circle( + clmd->debug_data, x, gdensity * clmd->sim_parms->density_strength, 0, 1, 0.4, + "grid velocity", i, j, 3115); + BKE_sim_debug_data_add_dot( + clmd->debug_data, x, col[0], col[1], col[2], + "grid velocity", i, j, 3115); +# endif BKE_sim_debug_data_add_circle( clmd->debug_data, x, 0.01f, col[0], col[1], col[2], "grid velocity", i, j, 3115); } diff --git a/source/blender/simulation/intern/hair_volume.cpp b/source/blender/simulation/intern/hair_volume.cpp index 4966fa2510d..348c8683be9 100644 --- a/source/blender/simulation/intern/hair_volume.cpp +++ b/source/blender/simulation/intern/hair_volume.cpp @@ -112,9 +112,11 @@ BLI_INLINE int hair_grid_interp_weights( uvw[1] = (vec[1] - gmin[1]) * scale - (float)j; uvw[2] = (vec[2] - gmin[2]) * scale - (float)k; - // BLI_assert(0.0f <= uvw[0] && uvw[0] <= 1.0001f); - // BLI_assert(0.0f <= uvw[1] && uvw[1] <= 1.0001f); - // BLI_assert(0.0f <= uvw[2] && uvw[2] <= 1.0001f); +#if 0 + BLI_assert(0.0f <= uvw[0] && uvw[0] <= 1.0001f); + BLI_assert(0.0f <= uvw[1] && uvw[1] <= 1.0001f); + BLI_assert(0.0f <= uvw[2] && uvw[2] <= 1.0001f); +#endif return offset; } @@ -350,7 +352,7 @@ BLI_INLINE int hair_grid_weights( weights[6] = dist_tent_v3f3(uvw, (float)i, (float)(j + 1), (float)(k + 1)); weights[7] = dist_tent_v3f3(uvw, (float)(i + 1), (float)(j + 1), (float)(k + 1)); - // BLI_assert(fabsf(weights_sum(weights) - 1.0f) < 0.0001f); + // BLI_assert(fabsf(weights_sum(weights) - 1.0f) < 0.0001f); return offset; } @@ -488,9 +490,10 @@ BLI_INLINE void hair_volume_add_segment_2D(HairGrid *grid, BKE_sim_debug_data_add_dot(x2w, 0.1, 0.1, 0.7, "grid", 649, debug_i, j, k); BKE_sim_debug_data_add_line(wloc, x2w, 0.3, 0.8, 0.3, "grid", 253, debug_i, j, k); BKE_sim_debug_data_add_line(wloc, x3w, 0.8, 0.3, 0.3, "grid", 254, debug_i, j, k); - // BKE_sim_debug_data_add_circle( - // x2w, len_v3v3(wloc, x2w), 0.2, 0.7, 0.2, - // "grid", 255, i, j, k); +# if 0 + BKE_sim_debug_data_add_circle( + x2w, len_v3v3(wloc, x2w), 0.2, 0.7, 0.2, "grid", 255, i, j, k); +# endif } } # endif @@ -1003,9 +1006,10 @@ bool SIM_hair_volume_solve_divergence(HairGrid *grid, if (!is_margin) { float dvel[3]; sub_v3_v3v3(dvel, vert->velocity_smooth, vert->velocity); - // BKE_sim_debug_data_add_vector( - // grid->debug_data, wloc, dvel, 1, 1, 1, - // "grid", 5566, i, j, k); +# if 0 + BKE_sim_debug_data_add_vector( + grid->debug_data, wloc, dvel, 1, 1, 1, "grid", 5566, i, j, k); +# endif } if (!is_margin) { @@ -1015,11 +1019,12 @@ bool SIM_hair_volume_solve_divergence(HairGrid *grid, float col[3]; interp_v3_v3v3(col, col0, colp, d); - // if (d > 0.05f) { - // BKE_sim_debug_data_add_dot( - // grid->debug_data, wloc, col[0], col[1], col[2], - // "grid", 5544, i, j, k); - // } +# if 0 + if (d > 0.05f) { + BKE_sim_debug_data_add_dot( + grid->debug_data, wloc, col[0], col[1], col[2], "grid", 5544, i, j, k); + } +# endif } } } diff --git a/source/blender/simulation/intern/implicit_blender.c b/source/blender/simulation/intern/implicit_blender.c index 8aa3774a3f2..10a5f153662 100644 --- a/source/blender/simulation/intern/implicit_blender.c +++ b/source/blender/simulation/intern/implicit_blender.c @@ -180,8 +180,8 @@ DO_INLINE void mul_lfvectorS(float (*to)[3], mul_fvector_S(to[i], fLongVector[i], scalar); } } -/* Multiply long vector with scalar. */ -/* A -= B * float */ +/* Multiply long vector with scalar. + * `A -= B * float` */ DO_INLINE void submul_lfvectorS(float (*to)[3], float (*fLongVector)[3], float scalar, @@ -209,7 +209,7 @@ DO_INLINE float dot_lfvector(float (*fLongVectorA)[3], } return temp; } -/* A = B + C --> for big vector */ +/* `A = B + C` -> for big vector. */ DO_INLINE void add_lfvector_lfvector(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], @@ -221,7 +221,7 @@ DO_INLINE void add_lfvector_lfvector(float (*to)[3], add_v3_v3v3(to[i], fLongVectorA[i], fLongVectorB[i]); } } -/* A = B + C * float --> for big vector */ +/* `A = B + C * float` -> for big vector. */ DO_INLINE void add_lfvector_lfvectorS(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], @@ -234,7 +234,7 @@ DO_INLINE void add_lfvector_lfvectorS(float (*to)[3], VECADDS(to[i], fLongVectorA[i], fLongVectorB[i], bS); } } -/* A = B * float + C * float --> for big vector */ +/* `A = B * float + C * float` -> for big vector */ DO_INLINE void add_lfvectorS_lfvectorS(float (*to)[3], float (*fLongVectorA)[3], float aS, @@ -248,7 +248,7 @@ DO_INLINE void add_lfvectorS_lfvectorS(float (*to)[3], VECADDSS(to[i], fLongVectorA[i], aS, fLongVectorB[i], bS); } } -/* A = B - C * float --> for big vector */ +/* `A = B - C * float` -> for big vector. */ DO_INLINE void sub_lfvector_lfvectorS(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], @@ -260,7 +260,7 @@ DO_INLINE void sub_lfvector_lfvectorS(float (*to)[3], VECSUBS(to[i], fLongVectorA[i], fLongVectorB[i], bS); } } -/* A = B - C --> for big vector */ +/* `A = B - C` -> for big vector. */ DO_INLINE void sub_lfvector_lfvector(float (*to)[3], float (*fLongVectorA)[3], float (*fLongVectorB)[3], @@ -455,7 +455,7 @@ DO_INLINE void add_fmatrix_fmatrix(float to[3][3], add_v3_v3v3(to[1], matrixA[1], matrixB[1]); add_v3_v3v3(to[2], matrixA[2], matrixB[2]); } -/* A -= B*x + C*y (3x3 matrix sub-addition with 3x3 matrix) */ +/* `A -= B*x + (C * y)` (3x3 matrix sub-addition with 3x3 matrix). */ DO_INLINE void subadd_fmatrixS_fmatrixS( float to[3][3], const float matrixA[3][3], float aS, const float matrixB[3][3], float bS) { @@ -463,7 +463,7 @@ DO_INLINE void subadd_fmatrixS_fmatrixS( VECSUBADDSS(to[1], matrixA[1], aS, matrixB[1], bS); VECSUBADDSS(to[2], matrixA[2], aS, matrixB[2], bS); } -/* A = B - C (3x3 matrix subtraction with 3x3 matrix) */ +/* `A = B - C` (3x3 matrix subtraction with 3x3 matrix). */ DO_INLINE void sub_fmatrix_fmatrix(float to[3][3], const float matrixA[3][3], const float matrixB[3][3]) @@ -1439,7 +1439,7 @@ void SIM_mass_spring_force_drag(Implicit_Data *data, float drag) for (i = 0; i < numverts; i++) { float tmp[3][3]; - /* NB: uses root space velocity, no need to transform */ + /* NOTE: Uses root space velocity, no need to transform. */ madd_v3_v3fl(data->F[i], data->V[i], -drag); copy_m3_m3(tmp, I); @@ -1683,8 +1683,8 @@ BLI_INLINE void dfdx_damp(float to[3][3], float rest, float damping) { - /* inner spring damping vel is the relative velocity of the endpoints. */ - // return (I-outerprod(dir, dir)) * (-damping * -(dot(dir, vel)/Max(length, rest))); + /* Inner spring damping `vel` is the relative velocity of the endpoints. */ + // return (I - outerprod(dir, dir)) * (-damping * -(dot(dir, vel) / Max(length, rest))); mul_fvectorT_fvector(to, dir, dir); sub_fmatrix_fmatrix(to, I, to); mul_fmatrix_S(to, (-damping * -(dot_v3v3(dir, vel) / MAX2(length, rest)))); diff --git a/source/blender/simulation/intern/implicit_eigen.cpp b/source/blender/simulation/intern/implicit_eigen.cpp index aa9d5d1d34d..d509b3db873 100644 --- a/source/blender/simulation/intern/implicit_eigen.cpp +++ b/source/blender/simulation/intern/implicit_eigen.cpp @@ -797,7 +797,7 @@ void SIM_mass_spring_force_drag(Implicit_Data *data, float drag) for (int i = 0; i < numverts; i++) { float tmp[3][3]; - /* NB: uses root space velocity, no need to transform */ + /* NOTE: Uses root space velocity, no need to transform. */ madd_v3_v3fl(data->F.v3(i), data->V.v3(i), -drag); copy_m3_m3(tmp, I); @@ -894,8 +894,8 @@ BLI_INLINE void dfdx_damp(float to[3][3], float rest, float damping) { - /* inner spring damping vel is the relative velocity of the endpoints. */ - // return (I-outerprod(dir, dir)) * (-damping * -(dot(dir, vel)/Max(length, rest))); + /* Inner spring damping vel is the relative velocity of the endpoints. */ + // return (I - outerprod(dir, dir)) * (-damping * -(dot(dir, vel) / Max(length, rest))); mul_fvectorT_fvector(to, dir, dir); sub_fmatrix_fmatrix(to, I, to); mul_fmatrix_S(to, (-damping * -(dot_v3v3(dir, vel) / MAX2(length, rest)))); diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 2b48a5f6648..cc8fb307c92 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -924,10 +924,10 @@ typedef struct wmDragID { } wmDragID; typedef struct wmDragAsset { - char name[64]; /* MAX_NAME */ + /* Owning pointer. Contains the file with all the asset data (name, local ID, etc.) */ + struct AssetHandle *asset_handle; /* Always freed. */ const char *path; - int id_type; int import_type; /* eFileAssetImportType */ } wmDragAsset; diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h index 1fbbec7f949..eab62ffce4c 100644 --- a/source/blender/windowmanager/gizmo/WM_gizmo_types.h +++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h @@ -186,7 +186,7 @@ typedef enum eWM_GizmoFlagMapTypeUpdateFlag { * \brief Gizmo tweak flag. * Bitflag passed to gizmo while tweaking. * - * \note Gizmos are responsible for handling this #wmGizmo.modal callback!. + * \note Gizmos are responsible for handling this #wmGizmo.modal callback. */ typedef enum { /* Drag with extra precision (Shift). */ diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 6a328679c2e..5ec26a7b208 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -91,7 +91,7 @@ enum { /* -------------------------------------------------------------------- */ /** \name wmGizmoMap Selection Array API * - * Just handle ``wm_gizmomap_select_array_*``, not flags or callbacks. + * Just handle `wm_gizmomap_select_array_*`, not flags or callbacks. * * \{ */ diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index da40040ce56..319e83f667f 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -42,6 +42,8 @@ #include "BKE_idtype.h" #include "BKE_lib_id.h" +#include "ED_asset.h" + #include "GPU_shader.h" #include "GPU_state.h" #include "GPU_viewport.h" @@ -196,6 +198,7 @@ void WM_drag_data_free(int dragtype, void *poin) /* Not too nice, could become a callback. */ if (dragtype == WM_DRAG_ASSET) { wmDragAsset *asset_drag = poin; + MEM_SAFE_FREE(asset_drag->asset_handle); MEM_freeN((void *)asset_drag->path); } MEM_freeN(poin); @@ -373,18 +376,20 @@ wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode) } wmDragAsset *asset_drag = drag->poin; - return (ELEM(idcode, 0, asset_drag->id_type)) ? asset_drag : NULL; + ID_Type idtype = ED_asset_handle_get_id_type(asset_drag->asset_handle); + return (ELEM(idcode, 0, (int)idtype)) ? asset_drag : NULL; } static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) { + const char *name = ED_asset_handle_get_name(asset_drag->asset_handle); + ID_Type idtype = ED_asset_handle_get_id_type(asset_drag->asset_handle); + switch ((eFileAssetImportType)asset_drag->import_type) { case FILE_ASSET_IMPORT_LINK: - return WM_file_link_datablock( - G_MAIN, NULL, NULL, NULL, asset_drag->path, asset_drag->id_type, asset_drag->name); + return WM_file_link_datablock(G_MAIN, NULL, NULL, NULL, asset_drag->path, idtype, name); case FILE_ASSET_IMPORT_APPEND: - return WM_file_append_datablock( - G_MAIN, NULL, NULL, NULL, asset_drag->path, asset_drag->id_type, asset_drag->name); + return WM_file_append_datablock(G_MAIN, NULL, NULL, NULL, asset_drag->path, idtype, name); } BLI_assert_unreachable(); @@ -444,7 +449,8 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox * return; } - ID *id = BKE_libblock_find_name(bmain, asset_drag->id_type, name); + ID_Type idtype = ED_asset_handle_get_id_type(asset_drag->asset_handle); + ID *id = BKE_libblock_find_name(bmain, idtype, name); if (id) { BKE_id_delete(bmain, id); } @@ -478,7 +484,7 @@ static const char *wm_drag_name(wmDrag *drag) } case WM_DRAG_ASSET: { const wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0); - return asset_drag->name; + return ED_asset_handle_get_name(asset_drag->asset_handle); } case WM_DRAG_PATH: case WM_DRAG_NAME: diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index e7603a02cff..e22285214f0 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -85,10 +85,10 @@ void WM_event_print(const wmEvent *event) event_ids_from_type_and_value(event->prevtype, event->prevval, &prev_type_id, &prev_val_id); printf( - "wmEvent type:%d / %s, val:%d / %s,\n" - " prev_type:%d / %s, prev_val:%d / %s,\n" - " shift:%d, ctrl:%d, alt:%d, oskey:%d, keymodifier:%d, is_repeat:%d,\n" - " mouse:(%d,%d), ascii:'%c', utf8:'%.*s', pointer:%p\n", + "wmEvent type:%d / %s, val:%d / %s,\n" + " prev_type:%d / %s, prev_val:%d / %s,\n" + " shift:%d, ctrl:%d, alt:%d, oskey:%d, keymodifier:%d, is_repeat:%d,\n" + " mouse:(%d,%d), ascii:'%c', utf8:'%.*s', pointer:%p\n", event->type, type_id, event->val, @@ -487,11 +487,13 @@ int WM_event_absolute_delta_y(const struct wmEvent *event) /** * Most OS's use `Ctrl+Space` / `OsKey+Space` to switch IME, * so don't type in the space character. + * + * \note Shift is excluded from this check since it prevented typing `Shift+Space`, see: T85517. */ bool WM_event_is_ime_switch(const struct wmEvent *event) { return event->val == KM_PRESS && event->type == EVT_SPACEKEY && - (event->ctrl || event->oskey || event->shift || event->alt); + (event->ctrl || event->oskey || event->alt); } #endif diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index a883c5f8c6b..e0e5d890e6c 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -987,7 +987,7 @@ const char *WM_init_state_app_template_get(void) * or called for 'New File' both startup.blend and userpref.blend are checked. * * \param use_factory_settings: - * Ignore on-disk startup file, use bundled ``datatoc_startup_blend`` instead. + * Ignore on-disk startup file, use bundled `datatoc_startup_blend` instead. * Used for "Restore Factory Settings". * * \param use_userdef: Load factory settings as well as startup file. diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index 9da901d6c4e..92ca0b87527 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -23,8 +23,8 @@ * Default operator callbacks for use with gestures (border/circle/lasso/straightline). * Operators themselves are defined elsewhere. * - * - Keymaps are in ``wm_operators.c``. - * - Property definitions are in ``wm_operator_props.c``. + * - Keymaps are in `wm_operators.c`. + * - Property definitions are in `wm_operator_props.c`. */ #include "MEM_guardedalloc.h" diff --git a/source/blender/windowmanager/intern/wm_keymap_utils.c b/source/blender/windowmanager/intern/wm_keymap_utils.c index b0b4f0f5904..795f78e215f 100644 --- a/source/blender/windowmanager/intern/wm_keymap_utils.c +++ b/source/blender/windowmanager/intern/wm_keymap_utils.c @@ -207,7 +207,7 @@ wmKeyMap *WM_keymap_guess_from_context(const bContext *C) /* Needs to be kept up to date with Keymap and Operator naming */ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname) { - /* Op types purposely skipped for now: + /* Op types purposely skipped for now: * BRUSH_OT * BOID_OT * BUTTONS_OT diff --git a/source/blender/windowmanager/intern/wm_operator_type.c b/source/blender/windowmanager/intern/wm_operator_type.c index e17d5a9ae70..39435721d1a 100644 --- a/source/blender/windowmanager/intern/wm_operator_type.c +++ b/source/blender/windowmanager/intern/wm_operator_type.c @@ -498,12 +498,11 @@ wmOperatorType *WM_operatortype_append_macro(const char *idname, ot->cancel = wm_macro_cancel; ot->poll = NULL; - if (!ot->description) { - /* XXX All ops should have a description but for now allow them not to. */ - ot->description = UNDOCUMENTED_OPERATOR_TIP; - } + /* XXX All ops should have a description but for now allow them not to. */ + BLI_assert((ot->description == NULL) || (ot->description[0])); - RNA_def_struct_ui_text(ot->srna, ot->name, ot->description); + RNA_def_struct_ui_text( + ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP); RNA_def_struct_identifier(&BLENDER_RNA, ot->srna, ot->idname); /* Use i18n context from rna_ext.srna if possible (py operators). */ i18n_context = ot->rna_ext.srna ? RNA_struct_translation_context(ot->rna_ext.srna) : @@ -530,16 +529,16 @@ void WM_operatortype_append_macro_ptr(void (*opfunc)(wmOperatorType *, void *), ot->cancel = wm_macro_cancel; ot->poll = NULL; - if (!ot->description) { - ot->description = UNDOCUMENTED_OPERATOR_TIP; - } + /* XXX All ops should have a description but for now allow them not to. */ + BLI_assert((ot->description == NULL) || (ot->description[0])); /* Set the default i18n context now, so that opfunc can redefine it if needed! */ RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT; opfunc(ot, userdata); - RNA_def_struct_ui_text(ot->srna, ot->name, ot->description); + RNA_def_struct_ui_text( + ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP); RNA_def_struct_identifier(&BLENDER_RNA, ot->srna, ot->idname); BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot); diff --git a/source/blender/windowmanager/wm_surface.h b/source/blender/windowmanager/wm_surface.h index 06c29231361..a2483d38154 100644 --- a/source/blender/windowmanager/wm_surface.h +++ b/source/blender/windowmanager/wm_surface.h @@ -19,7 +19,7 @@ * * \name WM-Surface * - * Container to manage painting in an offscreen context. + * Container to manage painting in an off-screen context. */ #pragma once @@ -42,9 +42,9 @@ typedef struct wmSurface { /** Free customdata, not the surface itself (done by wm_surface API) */ void (*free_data)(struct wmSurface *); - /** Called when surface is activated for drawing (made drawable). */ + /** Called when surface is activated for drawing (made drawable). */ void (*activate)(void); - /** Called when surface is deactivated for drawing (current drawable cleared). */ + /** Called when surface is deactivated for drawing (current drawable cleared). */ void (*deactivate)(void); } wmSurface; diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c index 1f722855696..4ac05e339b9 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c @@ -24,6 +24,7 @@ #include <string.h> +#include "BLI_listbase.h" #include "BLI_math.h" #include "ED_view3d_offscreen.h" @@ -61,10 +62,12 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, copy_qt_qt(eye_pose.orientation_quat, draw_view->eye_pose.orientation_quat); copy_v3_v3(eye_pose.position, draw_view->eye_pose.position); - sub_v3_v3(eye_pose.position, draw_data->eye_position_ofs); if ((session_settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) { sub_v3_v3(eye_pose.position, draw_view->local_pose.position); } + if ((session_settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING) == 0) { + sub_v3_v3(eye_pose.position, draw_data->eye_position_ofs); + } perspective_m4_fov(r_proj_mat, draw_view->fov.angle_left, @@ -89,6 +92,9 @@ static void wm_xr_draw_viewport_buffers_to_active_framebuffer( const wmXrSurfaceData *surface_data, const GHOST_XrDrawViewInfo *draw_view) { + const wmXrViewportPair *vp = BLI_findlink(&surface_data->viewports, draw_view->view_idx); + BLI_assert(vp && vp->viewport); + const bool is_upside_down = GHOST_XrSessionNeedsUpsideDownDrawing(runtime_data->context); rcti rect = {.xmin = 0, .ymin = 0, .xmax = draw_view->width - 1, .ymax = draw_view->height - 1}; @@ -98,8 +104,7 @@ static void wm_xr_draw_viewport_buffers_to_active_framebuffer( if (is_upside_down) { SWAP(int, rect.ymin, rect.ymax); } - GPU_viewport_draw_to_screen_ex( - surface_data->viewport, 0, &rect, draw_view->expects_srgb_buffer, true); + GPU_viewport_draw_to_screen_ex(vp->viewport, 0, &rect, draw_view->expects_srgb_buffer, true); } /** @@ -130,6 +135,9 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata) return; } + const wmXrViewportPair *vp = BLI_findlink(&surface_data->viewports, draw_view->view_idx); + BLI_assert(vp && vp->offscreen && vp->viewport); + /* In case a framebuffer is still bound from drawing the last eye. */ GPU_framebuffer_restore(); /* Some systems have drawing glitches without this. */ @@ -151,8 +159,8 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata) true, NULL, false, - surface_data->offscreen, - surface_data->viewport); + vp->offscreen, + vp->viewport); /* The draw-manager uses both GPUOffscreen and GPUViewport to manage frame and texture buffers. A * call to GPU_viewport_draw_to_screen() is still needed to get the final result from the @@ -162,7 +170,7 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata) * In a next step, Ghost-XR will use the currently bound frame-buffer to retrieve the image * to be submitted to the OpenXR swap-chain. So do not un-bind the off-screen yet! */ - GPU_offscreen_bind(surface_data->offscreen, false); + GPU_offscreen_bind(vp->offscreen, false); wm_xr_draw_viewport_buffers_to_active_framebuffer(xr_data->runtime, surface_data, draw_view); } diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index 9bf63be61dd..6415f96e322 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -85,9 +85,15 @@ typedef struct wmXrRuntimeData { wmXrSessionExitFn exit_fn; } wmXrRuntimeData; -typedef struct { +typedef struct wmXrViewportPair { + struct wmXrViewportPair *next, *prev; struct GPUOffScreen *offscreen; struct GPUViewport *viewport; +} wmXrViewportPair; + +typedef struct { + /** Off-screen buffers/viewports for each view. */ + ListBase viewports; /* wmXrViewportPair */ } wmXrSurfaceData; typedef struct wmXrDrawData { diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 1ddbe228e05..252f358c798 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -225,7 +225,7 @@ typedef enum wmXrSessionStateEvent { SESSION_STATE_EVENT_NONE = 0, SESSION_STATE_EVENT_START, SESSION_STATE_EVENT_RESET_TO_BASE_POSE, - SESSION_STATE_EVENT_POSITON_TRACKING_TOGGLE, + SESSION_STATE_EVENT_POSITION_TRACKING_TOGGLE, } wmXrSessionStateEvent; static bool wm_xr_session_draw_data_needs_reset_to_base_pose(const wmXrSessionState *state, @@ -253,7 +253,7 @@ static wmXrSessionStateEvent wm_xr_session_state_to_event(const wmXrSessionState XR_SESSION_USE_POSITION_TRACKING) != (settings->flag & XR_SESSION_USE_POSITION_TRACKING)); if (position_tracking_toggled) { - return SESSION_STATE_EVENT_POSITON_TRACKING_TOGGLE; + return SESSION_STATE_EVENT_POSITION_TRACKING_TOGGLE; } return SESSION_STATE_EVENT_NONE; @@ -288,7 +288,7 @@ void wm_xr_session_draw_data_update(const wmXrSessionState *state, copy_v3_fl(draw_data->eye_position_ofs, 0.0f); } break; - case SESSION_STATE_EVENT_POSITON_TRACKING_TOGGLE: + case SESSION_STATE_EVENT_POSITION_TRACKING_TOGGLE: if (use_position_tracking) { /* Keep the current position, and let the user move from there. */ copy_v3_v3(draw_data->eye_position_ofs, state->prev_eye_position_ofs); @@ -317,6 +317,7 @@ void wm_xr_session_state_update(const XrSessionSettings *settings, { GHOST_XrPose viewer_pose; const bool use_position_tracking = settings->flag & XR_SESSION_USE_POSITION_TRACKING; + const bool use_absolute_tracking = settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING; mul_qt_qtqt(viewer_pose.orientation_quat, draw_data->base_pose.orientation_quat, @@ -324,14 +325,16 @@ void wm_xr_session_state_update(const XrSessionSettings *settings, copy_v3_v3(viewer_pose.position, draw_data->base_pose.position); /* The local pose and the eye pose (which is copied from an earlier local pose) both are view * space, so Y-up. In this case we need them in regular Z-up. */ - viewer_pose.position[0] -= draw_data->eye_position_ofs[0]; - viewer_pose.position[1] += draw_data->eye_position_ofs[2]; - viewer_pose.position[2] -= draw_data->eye_position_ofs[1]; if (use_position_tracking) { viewer_pose.position[0] += draw_view->local_pose.position[0]; viewer_pose.position[1] -= draw_view->local_pose.position[2]; viewer_pose.position[2] += draw_view->local_pose.position[1]; } + if (!use_absolute_tracking) { + viewer_pose.position[0] -= draw_data->eye_position_ofs[0]; + viewer_pose.position[1] += draw_data->eye_position_ofs[2]; + viewer_pose.position[2] -= draw_data->eye_position_ofs[1]; + } copy_v3_v3(state->viewer_pose.position, viewer_pose.position); copy_qt_qt(state->viewer_pose.orientation_quat, viewer_pose.orientation_quat); @@ -451,9 +454,14 @@ static void wm_xr_session_controller_mats_update(const XrSessionSettings *settin float base_inv[4][4]; float tmp[4][4]; - zero_v3(view_ofs); if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) { - add_v3_v3(view_ofs, state->prev_local_pose.position); + copy_v3_v3(view_ofs, state->prev_local_pose.position); + } + else { + zero_v3(view_ofs); + } + if ((settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING) == 0) { + add_v3_v3(view_ofs, state->prev_eye_position_ofs); } wm_xr_pose_to_viewmat(&state->prev_base_pose, base_inv); @@ -538,7 +546,6 @@ void wm_xr_session_controller_data_clear(wmXrSessionState *state) */ static void wm_xr_session_surface_draw(bContext *C) { - wmXrSurfaceData *surface_data = g_xr_surface->customdata; wmWindowManager *wm = CTX_wm_manager(C); Main *bmain = CTX_data_main(C); wmXrDrawData draw_data; @@ -554,38 +561,50 @@ static void wm_xr_session_surface_draw(bContext *C) GHOST_XrSessionDrawViews(wm->xr.runtime->context, &draw_data); - GPU_offscreen_unbind(surface_data->offscreen, false); + GPU_framebuffer_restore(); } bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data, const GHOST_XrDrawViewInfo *draw_view) { - const bool size_changed = surface_data->offscreen && - (GPU_offscreen_width(surface_data->offscreen) != draw_view->width) && - (GPU_offscreen_height(surface_data->offscreen) != draw_view->height); + wmXrViewportPair *vp = NULL; + if (draw_view->view_idx >= BLI_listbase_count(&surface_data->viewports)) { + vp = MEM_callocN(sizeof(*vp), __func__); + BLI_addtail(&surface_data->viewports, vp); + } + else { + vp = BLI_findlink(&surface_data->viewports, draw_view->view_idx); + } + BLI_assert(vp); + + GPUOffScreen *offscreen = vp->offscreen; + GPUViewport *viewport = vp->viewport; + const bool size_changed = offscreen && (GPU_offscreen_width(offscreen) != draw_view->width) && + (GPU_offscreen_height(offscreen) != draw_view->height); char err_out[256] = "unknown"; bool failure = false; - if (surface_data->offscreen) { - BLI_assert(surface_data->viewport); + if (offscreen) { + BLI_assert(viewport); if (!size_changed) { return true; } - GPU_viewport_free(surface_data->viewport); - GPU_offscreen_free(surface_data->offscreen); - } - - if (!(surface_data->offscreen = GPU_offscreen_create( - draw_view->width, draw_view->height, true, false, err_out))) { - failure = true; - } - - if (failure) { - /* Pass. */ + GPU_viewport_free(viewport); + GPU_offscreen_free(offscreen); + } + + offscreen = vp->offscreen = GPU_offscreen_create( + draw_view->width, draw_view->height, true, false, err_out); + if (offscreen) { + viewport = vp->viewport = GPU_viewport_create(); + if (!viewport) { + GPU_offscreen_free(offscreen); + offscreen = vp->offscreen = NULL; + failure = true; + } } - else if (!(surface_data->viewport = GPU_viewport_create())) { - GPU_offscreen_free(surface_data->offscreen); + else { failure = true; } @@ -600,12 +619,17 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data, static void wm_xr_session_surface_free_data(wmSurface *surface) { wmXrSurfaceData *data = surface->customdata; + ListBase *lb = &data->viewports; + wmXrViewportPair *vp; - if (data->viewport) { - GPU_viewport_free(data->viewport); - } - if (data->offscreen) { - GPU_offscreen_free(data->offscreen); + while ((vp = BLI_pophead(lb))) { + if (vp->viewport) { + GPU_viewport_free(vp->viewport); + } + if (vp->offscreen) { + GPU_offscreen_free(vp->offscreen); + } + BLI_freelinkN(lb, vp); } MEM_freeN(surface->customdata); diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index a4b32fac9fc..c3aeffe8fda 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -1060,13 +1060,6 @@ elseif(APPLE) endif() endif() - if(WITH_LLVM AND NOT LLVM_STATIC) - install( - FILES ${LIBDIR}/llvm/lib/libLLVM-3.4.dylib - DESTINATION Blender.app/Contents/MacOS - ) - endif() - # python if(WITH_PYTHON AND NOT WITH_PYTHON_MODULE AND NOT WITH_PYTHON_FRAMEWORK) # Copy the python libs into the install directory diff --git a/source/creator/blender.map b/source/creator/blender.map index 9aafddd9666..a817908acfb 100644 --- a/source/creator/blender.map +++ b/source/creator/blender.map @@ -74,21 +74,55 @@ local: *YAML*; /* LLVM symbols not in the LLVM namespace that can conflict with LLVM usage - * in OpenGL and OpenCL drivers. */ + * in OpenGL and OpenCL drivers. + * + * These are found by doing a Blender build with and without OSL, and + * comparing the output of nm -gD ./bin/blender to find symbols. */ + AlwaysSpillBase; + AsmMacroMaxNestingDepth; + AttributorRun; + CheckBFIUnknownBlockQueries; + *cloneBitwiseIVUser*; + *computeHostNumHardwareThread*; + *computeHostNumPhysicalCores*; decodeInstruction; + DisableBasicAA; + DisablePreInliner; + DisableWholeProgramVisibility; + EnableCHR; + EnableConstraintElimination; + EnableGVNHoist; + EnableGVNSink; EnableHotColdSplit; EnableIPRA; + EnableIROutliner; + EnableKnowledgeRetention; + EnableLoopFlatten; + EnableMatrix; EnableOrderFileInstrumentation; + EnablePGSO; + EnableUnrollAndJam; EnableVPlanNativePath; EnableVPlanPredication; + ExtraVectorizerPasses; FlattenedProfileUsed; + ForcePGSO; ForceStackAlign; ForceSummaryEdgesCold; FSEC; + *getExtendedOperandRecurrence*; + *getWideRecurrence*; + InlinerFunctionImportStats; + *IROutlinerLegacyPass*; __jit_debug_descriptor; __jit_debug_register_code; _Jv_RegisterClasses; + *LiveDebugValues*; + *LoopInterchangeLegacyPass*; MachineRegionInfoPassID; + MaxDevirtIterations; + MaxRegistersForGCPointers; + MemOPOptMemcmpBcmp; MemOPSizeLarge; MemOPSizeRange; MISchedPostRA; @@ -97,18 +131,40 @@ local: Name; NumNamedVarArgParams; PGOViewCounts; + PGSOColdCodeOnly; + PGSOColdCodeOnlyForInstrPGO; + PGSOColdCodeOnlyForPartialSamplePGO; + PGSOColdCodeOnlyForSamplePGO; + PgsoCutoffInstrProf; + PgsoCutoffSampleProf; + PGSOLargeWorkingSetSizeOnly; + PreInlineThreshold; PrintBlockFreqFuncName; PrintBranchProbFuncName; ProfileLikelyProb; + RunNewGVN; + RunPartialInlining; + RunSLPVectorization; + ScalePartialSampleProfileWorkingSetSize; + *ScopedAliasMetadataDeepCloner*; + ShouldPreserveAllAttributes; + SkipFunctionNames; StartAfterOptName; StartBeforeOptName; StaticLikelyProb; StopAfterOptName; StopBeforeOptName; + UseContextLessSummary; UseDbgAddr; + UseLEB128Directives; + UseRegistersForDeoptValues; + UseRegistersForGCPointersInLandingPad; ViewBlockFreqFuncName; ViewBlockLayoutWithBFI; ViewHotFreqPercent; + WholeProgramVisibility; + *widenLoopCompare*; + *widenWithVariant*; WriteRelBFToSummary; X86CompilationCallback*; }; diff --git a/source/tools b/source/tools -Subproject 5fdcb9fed44fff7a5965cc6d12d8fefba6d16e4 +Subproject c8579c5cf43229843df505da9644b5b0b720197 diff --git a/tests/performance/api/config.py b/tests/performance/api/config.py index 68f4df8d487..d3a79eede14 100644 --- a/tests/performance/api/config.py +++ b/tests/performance/api/config.py @@ -31,6 +31,7 @@ class TestEntry: device_id: str = 'CPU' device_name: str = 'Unknown CPU' status: str = 'queued' + error_msg: str = '' output: Dict = field(default_factory=dict) benchmark_type: str = 'comparison' @@ -42,7 +43,8 @@ class TestEntry: def from_json(self, json_dict): for field in self.__dataclass_fields__: - setattr(self, field, json_dict[field]) + if field in json_dict: + setattr(self, field, json_dict[field]) class TestQueue: @@ -112,7 +114,7 @@ class TestConfig: self.base_dir = env.base_dir / name self.logs_dir = self.base_dir / 'logs' - config = self._read_config_module() + config = TestConfig._read_config_module(self.base_dir) self.tests = TestCollection(env, getattr(config, 'tests', ['*']), getattr(config, 'categories', ['*'])) @@ -154,10 +156,17 @@ class TestConfig: with open(config_file, 'w') as f: f.write(default_config) - def _read_config_module(self) -> None: + @staticmethod + def read_blender_executables(env, name) -> List: + config = TestConfig._read_config_module(env.base_dir / name) + builds = getattr(config, 'builds', {}) + return [pathlib.Path(build) for build in builds.values()] + + @staticmethod + def _read_config_module(base_dir: pathlib.Path) -> None: # Import config.py as a module. import importlib.util - spec = importlib.util.spec_from_file_location("testconfig", self.base_dir / 'config.py') + spec = importlib.util.spec_from_file_location("testconfig", base_dir / 'config.py') mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) return mod @@ -195,14 +204,14 @@ class TestConfig: # Get entries for revisions based on existing builds. for revision_name, executable in self.builds.items(): - executable_path = pathlib.Path(executable) - if not executable_path.exists(): + executable_path = env._blender_executable_from_path(pathlib.Path(executable)) + if not executable_path: sys.stderr.write(f'Error: build {executable} not found\n') sys.exit(1) env.set_blender_executable(executable_path) git_hash, _ = env.run_in_blender(get_build_hash, {}) - env.unset_blender_executable() + env.set_default_blender_executable() mtime = executable_path.stat().st_mtime entries += self._get_entries(revision_name, git_hash, executable, mtime) diff --git a/tests/performance/api/environment.py b/tests/performance/api/environment.py index c9ddd493394..70fc15f251c 100644 --- a/tests/performance/api/environment.py +++ b/tests/performance/api/environment.py @@ -27,9 +27,10 @@ class TestEnvironment: self.git_executable = 'git' self.cmake_executable = 'cmake' self.cmake_options = ['-DWITH_INTERNATIONAL=OFF', '-DWITH_BUILDINFO=OFF'] - self.unset_blender_executable() self.log_file = None self.machine = None + self._init_default_blender_executable() + self.set_default_blender_executable() def get_machine(self, need_gpus: bool=True) -> None: if not self.machine or (need_gpus and not self.machine.has_gpus): @@ -46,7 +47,7 @@ class TestEnvironment: print(f'Init {self.base_dir}') self.base_dir.mkdir(parents=True, exist_ok=True) - if len(self.get_configs(names_only=True)) == 0: + if len(self.get_configs_names()) == 0: config_dir = self.base_dir / 'default' print(f'Creating default configuration in {config_dir}') TestConfig.write_default_config(self, config_dir) @@ -77,7 +78,7 @@ class TestEnvironment: print('Done') - def checkout(self) -> None: + def checkout(self, git_hash) -> None: # Checkout Blender revision if not self.blender_dir.exists(): sys.stderr.write('\n\nError: no build set up, run `./benchmark init --build` first\n') @@ -87,32 +88,68 @@ class TestEnvironment: self.call([self.git_executable, 'reset', '--hard', 'HEAD'], self.blender_dir) self.call([self.git_executable, 'checkout', '--detach', git_hash], self.blender_dir) - self.build() - - def build(self) -> None: + def build(self) -> bool: # Build Blender revision if not self.build_dir.exists(): sys.stderr.write('\n\nError: no build set up, run `./benchmark init --build` first\n') sys.exit(1) jobs = str(multiprocessing.cpu_count()) - self.call([self.cmake_executable, '.'] + self.cmake_options, self.build_dir) - self.call([self.cmake_executable, '--build', '.', '-j', jobs, '--target', 'install'], self.build_dir) + try: + self.call([self.cmake_executable, '.'] + self.cmake_options, self.build_dir) + self.call([self.cmake_executable, '--build', '.', '-j', jobs, '--target', 'install'], self.build_dir) + except: + return False + + self._init_default_blender_executable() + return True def set_blender_executable(self, executable_path: pathlib.Path) -> None: # Run all Blender commands with this executable. self.blender_executable = executable_path - def unset_blender_executable(self) -> None: + def _blender_executable_name(self) -> pathlib.Path: if platform.system() == "Windows": - self.blender_executable = self.build_dir / 'bin' / 'blender.exe' + return pathlib.Path('blender.exe') elif platform.system() == "Darwin": - self.blender_executable = self.build_dir / 'bin' / 'Blender.app' / 'Contents' / 'MacOS' / 'Blender' + return pathlib.Path('Blender.app') / 'Contents' / 'MacOS' / 'Blender' else: - self.blender_executable = self.build_dir / 'bin' / 'blender' - - if not self.blender_executable.exists(): - self.blender_executable = 'blender' + return pathlib.Path('blender') + + def _blender_executable_from_path(self, executable: pathlib.Path) -> pathlib.Path: + if executable.is_dir(): + # Directory + executable = executable / self._blender_executable_name() + elif not executable.is_file() and executable.name == 'blender': + # Executable path without proper path on Windows or macOS. + executable = executable.parent / self._blender_executable_name() + + if executable.is_file(): + return executable + + return None + + def _init_default_blender_executable(self) -> None: + # Find a default executable to run commands independent of testing a specific build. + # Try own built executable. + built_executable = self._blender_executable_from_path(self.build_dir / 'bin') + if built_executable: + self.default_blender_executable = built_executable + return + + # Try find an executable in the configs. + for config_name in self.get_config_names(): + for executable in TestConfig.read_blender_executables(self, config_name): + executable = self._blender_executable_from_path(executable) + if executable: + self.default_blender_executable = executable + return + + # Fallback to a "blender" command in the hope it's available. + self.default_blender_executable = pathlib.Path("blender") + + def set_default_blender_executable(self) -> None: + self.blender_executable = self.default_blender_executable def set_log_file(self, filepath: pathlib.Path, clear=True) -> None: # Log all commands and output to this file. @@ -219,26 +256,36 @@ class TestEnvironment: filepaths.append(pathlib.Path(filename)) return filepaths + def get_config_names(self) -> List: + names = [] + + if self.base_dir.exists(): + for dirname in os.listdir(self.base_dir): + dirpath = self.base_dir / dirname / 'config.py' + if dirpath.exists(): + names.append(dirname) + + return names + def get_configs(self, name: str=None, names_only: bool=False) -> List: # Get list of configurations in the benchmarks directory. configs = [] - if self.base_dir.exists(): - for dirname in os.listdir(self.base_dir): - if not name or dirname == name: - dirpath = self.base_dir / dirname / 'config.py' - if dirpath.exists(): - if names_only: - configs.append(dirname) - else: - configs.append(TestConfig(self, dirname)) + for config_name in self.get_config_names(): + if not name or config_name == name: + if names_only: + configs.append(config_name) + else: + configs.append(TestConfig(self, config_name)) return configs def resolve_git_hash(self, revision): # Get git hash for a tag or branch. - return self.call([self.git_executable, 'rev-parse', revision], self.blender_git_dir)[0].strip() + lines = self.call([self.git_executable, 'rev-parse', revision], self.blender_git_dir) + return lines[0].strip() if len(lines) else revision def git_hash_date(self, git_hash): # Get commit data for a git hash. - return int(self.call([self.git_executable, 'log', '-n1', git_hash, '--format=%at'], self.blender_git_dir)[0].strip()) + lines = self.call([self.git_executable, 'log', '-n1', git_hash, '--format=%at'], self.blender_git_dir) + return int(lines[0].strip()) if len(lines) else 0 diff --git a/tests/performance/api/graph.py b/tests/performance/api/graph.py index b3c8329ff27..4ee5ae7cf0e 100644 --- a/tests/performance/api/graph.py +++ b/tests/performance/api/graph.py @@ -30,6 +30,7 @@ class TestGraph: data = [] for device_name, device_entries in devices.items(): + # Gather used categories. categories = {} for entry in device_entries: @@ -57,6 +58,8 @@ class TestGraph: self.json = json.dumps(data, indent=2) def chart(self, device_name: str, chart_name: str, entries: List, chart_type: str, output: str) -> Dict: + entries = sorted(entries, key=lambda entry: entry.date) + # Gather used tests. tests = {} for entry in entries: diff --git a/tests/performance/benchmark b/tests/performance/benchmark index 3b43bd0aa96..ad1e07d0ef3 100755 --- a/tests/performance/benchmark +++ b/tests/performance/benchmark @@ -66,6 +66,8 @@ def print_row(config: api.TestConfig, entries: List, end='\n') -> None: if status == 'outdated': result += " (outdated)" + elif status == 'failed': + result = "failed: " + entry.error_msg else: result = status @@ -105,20 +107,37 @@ def run_entry(env: api.TestEnvironment, config: api.TestConfig, row: List, entry logname += '_' + device_id env.set_log_file(config.logs_dir / (logname + '.log'), clear=True) + # Clear output + entry.output = None + entry.error_msg = '' + # Build revision, or just set path to existing executable. entry.status = 'building' print_row(config, row, end='\r') + executable_ok = True if len(entry.executable): env.set_blender_executable(pathlib.Path(entry.executable)) else: env.checkout(git_hash) - env.build(git_hash) + executable_ok = env.build() + if not executable_ok: + entry.status = 'failed' + entry.error_msg = 'Failed to build' # Run test and update output and status. - entry.status = 'running' - print_row(config, row, end='\r') - entry.output = test.run(env, device_id) - entry.status = 'done' if entry.output else 'failed' + if executable_ok: + entry.status = 'running' + print_row(config, row, end='\r') + + try: + entry.output = test.run(env, device_id) + if not entry.output: + raise Exception("Test produced no output") + entry.status = 'done' + except Exception as e: + entry.status = 'failed' + entry.error_msg = str(e) + print_row(config, row, end='\r') # Update device name in case the device changed since the entry was created. @@ -126,7 +145,7 @@ def run_entry(env: api.TestEnvironment, config: api.TestConfig, row: List, entry # Restore default logging and Blender executable. env.unset_log_file() - env.unset_blender_executable() + env.set_default_blender_executable() return True @@ -155,7 +174,7 @@ def cmd_list(env: api.TestEnvironment, argv: List) -> None: print('') print('CONFIGS') - configs = env.get_configs(names_only=True) + configs = env.get_config_names() for config_name in configs: print(config_name) diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 92cebb7d274..0f9665f0a95 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -740,6 +740,29 @@ if(WITH_COMPOSITOR) endif() +set(geo_node_tests + curves + geometry + mesh + points +) + +foreach(geo_node_test ${geo_node_tests}) + if(EXISTS "${TEST_SRC_DIR}/modeling/geometry_nodes/${geo_node_test}/") + file(GLOB files "${TEST_SRC_DIR}/modeling/geometry_nodes/${geo_node_test}/*.blend") + foreach(file ${files}) + get_filename_component(filename ${file} NAME_WE) + add_blender_test( + geo_node_${geo_node_test}_test_${filename} + ${file} + --python ${TEST_PYTHON_DIR}/geo_node_test.py + ) + endforeach() + else() + MESSAGE(STATUS "No directory named ${TEST_SRC_DIR}/modeling/geometry_nodes/${geo_node_test}/ found, disabling test.") + endif() +endforeach() + if(WITH_OPENGL_DRAW_TESTS) if(NOT OPENIMAGEIO_IDIFF) MESSAGE(STATUS "Disabling OpenGL draw tests because OIIO idiff does not exist") diff --git a/tests/python/bevel_operator.py b/tests/python/bevel_operator.py index c732d437b57..726bf4b5b03 100644 --- a/tests/python/bevel_operator.py +++ b/tests/python/bevel_operator.py @@ -27,272 +27,272 @@ import os import sys sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import MeshTest, OperatorSpecEditMode, RunTest +from modules.mesh_test import SpecMeshTest, OperatorSpecEditMode, RunTest def main(): tests = [ # 0 - MeshTest('Cube_test_1', 'Cube_test', 'Cube_result_1', + SpecMeshTest('Cube_test_1', 'Cube_test', 'Cube_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {10})]), - MeshTest('Cube_test_2', 'Cube_test', 'Cube_result_2', + SpecMeshTest('Cube_test_2', 'Cube_test', 'Cube_result_2', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'offset_type': 'WIDTH'}, 'EDGE', {10, 7}, )]), - MeshTest('Cube_test_3', 'Cube_test', 'Cube_result_3', + SpecMeshTest('Cube_test_3', 'Cube_test', 'Cube_result_3', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'offset_type': 'DEPTH'}, 'EDGE', {8, 10, 7}, )]), - MeshTest('Cube_test_4', 'Cube_test', 'Cube_result_4', + SpecMeshTest('Cube_test_4', 'Cube_test', 'Cube_result_4', [OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 2}, 'EDGE', {10}, )]), - MeshTest('Cube_test_5', 'Cube_test', 'Cube_result_5', + SpecMeshTest('Cube_test_5', 'Cube_test', 'Cube_result_5', [OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 3}, 'EDGE', {10, 7}, )]), # 5 - MeshTest('Cube_test_6', 'Cube_test', 'Cube_result_6', + SpecMeshTest('Cube_test_6', 'Cube_test', 'Cube_result_6', [OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 4}, 'EDGE', {8, 10, 7}, )]), - MeshTest('Cube_test_7', 'Cube_test', 'Cube_result_7', + SpecMeshTest('Cube_test_7', 'Cube_test', 'Cube_result_7', [OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 5, 'profile': 0.2}, 'EDGE', {0, 10, 4, 7}, )]), - MeshTest('Cube_test_8', 'Cube_test', 'Cube_result_8', + SpecMeshTest('Cube_test_8', 'Cube_test', 'Cube_result_8', [OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 5, 'profile': 0.25}, 'EDGE', {8, 10, 7}, )]), - MeshTest('Cube_test_9', 'Cube_test', 'Cube_result_9', + SpecMeshTest('Cube_test_9', 'Cube_test', 'Cube_result_9', [OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 6, 'profile': 0.9}, 'EDGE', {8, 10, 7}, )]), - MeshTest('Cube_test_10', 'Cube_test', 'Cube_result_10', + SpecMeshTest('Cube_test_10', 'Cube_test', 'Cube_result_10', [OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 4, 'profile': 1.0}, 'EDGE', {10, 7}, )]), # 10 - MeshTest('Cube_test_11', 'Cube_test', 'Cube_result_11', + SpecMeshTest('Cube_test_11', 'Cube_test', 'Cube_result_11', [OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 5, 'profile': 1.0}, 'EDGE', {8, 10, 7}, )]), - MeshTest("test 12", 'Cube_test', 'Cube_result_12', + SpecMeshTest("test 12", 'Cube_test', 'Cube_result_12', [OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 8}, 'EDGE', {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, )]), - MeshTest('Pyramid4_test_1', 'Pyr4_test', 'Pyr4_result_1', + SpecMeshTest('Pyramid4_test_1', 'Pyr4_test', 'Pyr4_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {5}, )]), - MeshTest('Pyramid4_test_2', 'Pyr4_test', 'Pyr4_result_2', + SpecMeshTest('Pyramid4_test_2', 'Pyr4_test', 'Pyr4_result_2', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {2, 5}, )]), - MeshTest('Pyramid4_test_3', 'Pyr4_test', 'Pyr4_result_3', + SpecMeshTest('Pyramid4_test_3', 'Pyr4_test', 'Pyr4_result_3', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {2, 3, 5}, )]), # 15 - MeshTest('Pyramid4_test_4', 'Pyr4_test', 'Pyr4_result_4', + SpecMeshTest('Pyramid4_test_4', 'Pyr4_test', 'Pyr4_result_4', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {1, 2, 3, 5}, )]), - MeshTest('Pyramid4_test_5', 'Pyr4_test', 'Pyr4_result_5', + SpecMeshTest('Pyramid4_test_5', 'Pyr4_test', 'Pyr4_result_5', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {1, 2, 3, 5}, )]), - MeshTest('Pyramid4_test_6', 'Pyr4_test', 'Pyr4_result_6', + SpecMeshTest('Pyramid4_test_6', 'Pyr4_test', 'Pyr4_result_6', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {2, 3}, )]), - MeshTest('Pyramid4_test_7', 'Pyr4_test', 'Pyr4_result_7', + SpecMeshTest('Pyramid4_test_7', 'Pyr4_test', 'Pyr4_result_7', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 4, 'profile': 0.15}, 'EDGE', {1, 2, 3, 5}, )]), - MeshTest('Pyramid4_test_8', 'Pyr4_test', 'Pyr4_result_8', + SpecMeshTest('Pyramid4_test_8', 'Pyr4_test', 'Pyr4_result_8', [OperatorSpecEditMode('bevel', {'offset': 0.75, 'segments': 4, 'affect': 'VERTICES'}, 'VERT', {1}, )]), # 20 - MeshTest('Pyramid4_test_9', 'Pyr4_test', 'Pyr4_result_9', + SpecMeshTest('Pyramid4_test_9', 'Pyr4_test', 'Pyr4_result_9', [OperatorSpecEditMode('bevel', {'offset': 0.75, 'segments': 3, 'affect': 'VERTICES', 'profile': 0.25}, 'VERT', {1}, )]), - MeshTest('Pyramid6_test_1', 'Pyr6_test', 'Pyr6_result_1', + SpecMeshTest('Pyramid6_test_1', 'Pyr6_test', 'Pyr6_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {2, 3}, )]), - MeshTest('Pyramid6_test_2', 'Pyr6_test', 'Pyr6_result_2', + SpecMeshTest('Pyramid6_test_2', 'Pyr6_test', 'Pyr6_result_2', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {8, 2, 3}, )]), - MeshTest('Pyramid6_test_3', 'Pyr6_test', 'Pyr6_result_3', + SpecMeshTest('Pyramid6_test_3', 'Pyr6_test', 'Pyr6_result_3', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 4, 'profile': 0.8}, 'EDGE', {0, 2, 3, 4, 6, 7, 9, 10, 11}, )]), - MeshTest('Sept_test_1', 'Sept_test', 'Sept_result_1', + SpecMeshTest('Sept_test_1', 'Sept_test', 'Sept_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.1}, 'EDGE', {8, 9, 3, 11}, )]), # 25 - MeshTest('Sept_test_2', 'Sept_test', 'Sept_result_2', + SpecMeshTest('Sept_test_2', 'Sept_test', 'Sept_result_2', [OperatorSpecEditMode('bevel', {'offset': 0.1, 'offset_type': 'WIDTH'}, 'EDGE', {8, 9, 11}, )]), - MeshTest('Saddle_test_1', 'Saddle_test', 'Saddle_result_1', + SpecMeshTest('Saddle_test_1', 'Saddle_test', 'Saddle_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.3, 'segments': 5}, 'EDGE', {2, 8, 9, 12, 13, 14}, )]), - MeshTest('Saddle_test_2', 'Saddle_test', 'Saddle_result_2', + SpecMeshTest('Saddle_test_2', 'Saddle_test', 'Saddle_result_2', [OperatorSpecEditMode('bevel', {'offset': 0.6, 'segments': 6, 'affect': 'VERTICES'}, 'VERT', {4}, )]), - MeshTest('Bent_test', 'Bent_test', 'Bent_result_1', + SpecMeshTest('Bent_test', 'Bent_test', 'Bent_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {2, 5, 8, 11, 14, 18, 21, 24, 27, 30, 34, 37, 40, 43, 46, 50, 53, 56, 59, 62, 112, 113, 114, 115}, )]), - MeshTest('Bentlines_test_1', 'Bentlines_test', 'Bentlines_result_1', + SpecMeshTest('Bentlines_test_1', 'Bentlines_test', 'Bentlines_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {1, 8, 9, 10, 11}, )]), # 30 - MeshTest('Flaretop_test_1', 'Flaretop_test', 'Flaretop_result_1', + SpecMeshTest('Flaretop_test_1', 'Flaretop_test', 'Flaretop_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 2}, 'EDGE', {26, 12, 20}, )]), - MeshTest('Flaretop_test_2', 'Flaretop_test', 'Flaretop_result_2', + SpecMeshTest('Flaretop_test_2', 'Flaretop_test', 'Flaretop_result_2', [OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 2, 'profile': 1.0}, 'EDGE', {26, 12, 20}, )]), - MeshTest('Flaretop_test_3', 'Flaretop_test', 'Flaretop_result_3', + SpecMeshTest('Flaretop_test_3', 'Flaretop_test', 'Flaretop_result_3', [OperatorSpecEditMode('bevel', {'offset': 0.4, 'segments': 4}, 'FACE', {1, 6, 7, 8, 9, 10, 11, 12}, )]), - MeshTest('BentL_test', 'BentL_test', 'BentL_result_1', + SpecMeshTest('BentL_test', 'BentL_test', 'BentL_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {4, 8, 10, 18, 24}, )]), - MeshTest('Wires_test_1', 'Wires_test', 'Wires_test_result_1', + SpecMeshTest('Wires_test_1', 'Wires_test', 'Wires_test_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.3}, 'EDGE', {0, 1, 2, 10}, )]), # 35 - MeshTest('Wires_test_2', 'Wires_test', 'Wires_test_result_2', + SpecMeshTest('Wires_test_2', 'Wires_test', 'Wires_test_result_2', [OperatorSpecEditMode('bevel', {'offset': 0.3, 'affect': 'VERTICES'}, 'VERT', {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, )]), - MeshTest('tri_test_1', 'tri', 'tri_result_1', + SpecMeshTest('tri_test_1', 'tri', 'tri_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]), - MeshTest('tri_test_2', 'tri', 'tri_result_2', + SpecMeshTest('tri_test_2', 'tri', 'tri_result_2', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4, 5}, )]), - MeshTest('tri_test_3', 'tri', 'tri_result_3', + SpecMeshTest('tri_test_3', 'tri', 'tri_result_3', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]), - MeshTest('tri_test_4', 'tri', 'tri_result_4', + SpecMeshTest('tri_test_4', 'tri', 'tri_result_4', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4}, )]), # 40 - MeshTest('tri_test_5', 'tri', 'tri_result_5', + SpecMeshTest('tri_test_5', 'tri', 'tri_result_5', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4}, )]), - MeshTest('tri_test_6', 'tri', 'tri_result_6', + SpecMeshTest('tri_test_6', 'tri', 'tri_result_6', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'affect': 'VERTICES'}, 'VERT', {3}, )]), - MeshTest('tri_test_7', 'tri', 'tri_result_7', + SpecMeshTest('tri_test_7', 'tri', 'tri_result_7', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2, 'affect': 'VERTICES'}, 'VERT', {3}, )]), - MeshTest('tri_test_8', 'tri', 'tri_result_8', + SpecMeshTest('tri_test_8', 'tri', 'tri_result_8', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3, 'affect': 'VERTICES'}, 'VERT', {3}, )]), - MeshTest('tri_test_9', 'tri', 'tri_result_9', + SpecMeshTest('tri_test_9', 'tri', 'tri_result_9', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'affect': 'VERTICES'}, 'VERT', {1}, )]), # 45 - MeshTest('tri1gap_test_2', 'tri1gap', 'tri1gap_result_2', + SpecMeshTest('tri1gap_test_2', 'tri1gap', 'tri1gap_result_2', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4, 5}, )]), - MeshTest('tri1gap_test_3', 'tri1gap', 'tri1gap_result_3', + SpecMeshTest('tri1gap_test_3', 'tri1gap', 'tri1gap_result_3', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]), - MeshTest('tri1gap_test_1', 'tri1gap', 'tri1gap_result_1', + SpecMeshTest('tri1gap_test_1', 'tri1gap', 'tri1gap_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]), - MeshTest('tri1gap_test_4', 'tri1gap', 'tri1gap_result_4', + SpecMeshTest('tri1gap_test_4', 'tri1gap', 'tri1gap_result_4', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4}, )]), - MeshTest('tri1gap_test_5', 'tri1gap', 'tri1gap_result_5', + SpecMeshTest('tri1gap_test_5', 'tri1gap', 'tri1gap_result_5', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4}, )]), # 50 - MeshTest('tri1gap_test_6', 'tri1gap', 'tri1gap_result_6', + SpecMeshTest('tri1gap_test_6', 'tri1gap', 'tri1gap_result_6', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4}, )]), - MeshTest('tri1gap_test_7', 'tri1gap', 'tri1gap_result_7', + SpecMeshTest('tri1gap_test_7', 'tri1gap', 'tri1gap_result_7', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 5}, )]), - MeshTest('tri1gap_test_8', 'tri1gap', 'tri1gap_result_8', + SpecMeshTest('tri1gap_test_8', 'tri1gap', 'tri1gap_result_8', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 5}, )]), - MeshTest('tri1gap_test_9', 'tri1gap', 'tri1gap_result_9', + SpecMeshTest('tri1gap_test_9', 'tri1gap', 'tri1gap_result_9', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 5}, )]), - MeshTest('tri1gap_test_10', 'tri1gap', 'tri1gap_result_10', + SpecMeshTest('tri1gap_test_10', 'tri1gap', 'tri1gap_result_10', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'affect': 'VERTICES'}, 'VERT', {3}, )]), # 55 - MeshTest('tri2gaps_test_1', 'tri2gaps', 'tri2gaps_result_1', + SpecMeshTest('tri2gaps_test_1', 'tri2gaps', 'tri2gaps_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]), - MeshTest('tri2gaps_test_2', 'tri2gaps', 'tri2gaps_result_2', + SpecMeshTest('tri2gaps_test_2', 'tri2gaps', 'tri2gaps_result_2', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4, 5}, )]), - MeshTest('tri2gaps_test_3', 'tri2gaps', 'tri2gaps_result_3', + SpecMeshTest('tri2gaps_test_3', 'tri2gaps', 'tri2gaps_result_3', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]), - MeshTest('tri2gaps_test_4', 'tri2gaps', 'tri2gaps_result_4', + SpecMeshTest('tri2gaps_test_4', 'tri2gaps', 'tri2gaps_result_4', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4}, )]), - MeshTest('tri2gaps_test_5', 'tri2gaps', 'tri2gaps_result_5', + SpecMeshTest('tri2gaps_test_5', 'tri2gaps', 'tri2gaps_result_5', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4}, )]), # 60 - MeshTest('tri2gaps_test_6', 'tri2gaps', 'tri2gaps_result_6', + SpecMeshTest('tri2gaps_test_6', 'tri2gaps', 'tri2gaps_result_6', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4}, )]), - MeshTest('tri3gaps_test_1', 'tri3gaps', 'tri3gaps_result_1', + SpecMeshTest('tri3gaps_test_1', 'tri3gaps', 'tri3gaps_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {3, 4, 5}, )]), - MeshTest('tri3gaps_test_2', 'tri3gaps', 'tri3gaps_result_2', + SpecMeshTest('tri3gaps_test_2', 'tri3gaps', 'tri3gaps_result_2', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {3, 4, 5}, )]), - MeshTest('tri3gaps_test_3', 'tri3gaps', 'tri3gaps_result_3', + SpecMeshTest('tri3gaps_test_3', 'tri3gaps', 'tri3gaps_result_3', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {3, 4, 5}, )]), - MeshTest('cube3_test_1', 'cube3', 'cube3_result_1', + SpecMeshTest('cube3_test_1', 'cube3', 'cube3_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {32, 33, 34, 35, 24, 25, 26, 27, 28, 29, 30, 31}, )]), # 65 - MeshTest('cube3_test_2', 'cube3', 'cube3_result_2', + SpecMeshTest('cube3_test_2', 'cube3', 'cube3_result_2', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {32, 33, 34, 35, 24, 25, 26, 27, 28, 29, 30, 31}, )]), - MeshTest('cube3_test_3', 'cube3', 'cube3_result_3', + SpecMeshTest('cube3_test_3', 'cube3', 'cube3_result_3', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {32, 35}, )]), - MeshTest('cube3_test_4', 'cube3', 'cube3_result_4', + SpecMeshTest('cube3_test_4', 'cube3', 'cube3_result_4', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {24, 35}, )]), - MeshTest('cube3_test_5', 'cube3', 'cube3_result_5', + SpecMeshTest('cube3_test_5', 'cube3', 'cube3_result_5', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2}, 'EDGE', {24, 32, 35}, )]), - MeshTest('cube3_test_6', 'cube3', 'cube3_result_6', + SpecMeshTest('cube3_test_6', 'cube3', 'cube3_result_6', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {24, 32, 35}, )]), # 70 - MeshTest('Tray', 'Tray', 'Tray_result_1', + SpecMeshTest('Tray', 'Tray', 'Tray_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.01, 'segments': 2}, 'EDGE', {0, 1, 6, 7, 12, 14, 16, 17}, )]), - MeshTest("test 73", 'Bumptop', 'Bumptop_result_1', + SpecMeshTest("test 73", 'Bumptop', 'Bumptop_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.1, 'segments': 4}, 'EDGE', {33, 4, 38, 8, 41, 10, 42, 12, 14, 17, 24, 31}, )]), - MeshTest('Multisegment_test_1', 'Multisegment_test', 'Multisegment_result_1', + SpecMeshTest('Multisegment_test_1', 'Multisegment_test', 'Multisegment_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.2}, 'EDGE', {16, 14, 15}, )]), - MeshTest('Window_test', 'Window_test', 'Window_result_1', + SpecMeshTest('Window_test', 'Window_test', 'Window_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.05, 'segments': 2}, 'EDGE', {19, 20, 23, 15}, )]), # 75 - MeshTest("test 77", 'Cube_hn_test', 'Cube_hn_result_1', + SpecMeshTest("test 77", 'Cube_hn_test', 'Cube_hn_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'harden_normals': True}, 'EDGE', {8}, )]), - MeshTest('Blocksteps_test_1', 'Blocksteps_test', 'Blocksteps_result_1', + SpecMeshTest('Blocksteps_test_1', 'Blocksteps_test', 'Blocksteps_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'miter_outer': 'PATCH'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]), - MeshTest('Blocksteps_test_2', 'Blocksteps_test', 'Blocksteps_result_2', + SpecMeshTest('Blocksteps_test_2', 'Blocksteps_test', 'Blocksteps_result_2', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2, 'miter_outer': 'PATCH'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]), - MeshTest('Blocksteps_test_3', 'Blocksteps_test', 'Blocksteps_result_3', + SpecMeshTest('Blocksteps_test_3', 'Blocksteps_test', 'Blocksteps_result_3', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3, 'miter_outer': 'PATCH'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]), - MeshTest('Blocksteps_test_4', 'Blocksteps_test', 'Blocksteps_result_4', + SpecMeshTest('Blocksteps_test_4', 'Blocksteps_test', 'Blocksteps_result_4', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'miter_outer': 'ARC'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]), # 80 - MeshTest('Blocksteps_test_5', 'Blocksteps_test', 'Blocksteps_result_5', + SpecMeshTest('Blocksteps_test_5', 'Blocksteps_test', 'Blocksteps_result_5', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]), - MeshTest('Blocksteps_test_6', 'Blocksteps_test', 'Blocksteps_result_6', + SpecMeshTest('Blocksteps_test_6', 'Blocksteps_test', 'Blocksteps_result_6', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3, 'miter_outer': 'ARC'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]), - MeshTest('Blocksteps_test_7', 'Blocksteps_test', 'Blocksteps_result_7', + SpecMeshTest('Blocksteps_test_7', 'Blocksteps_test', 'Blocksteps_result_7', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'miter_outer': 'PATCH', 'miter_inner': 'ARC'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]), - MeshTest("Blocksteps_test_8", 'Blocksteps_test', 'Blocksteps_result_8', + SpecMeshTest("Blocksteps_test_8", 'Blocksteps_test', 'Blocksteps_result_8', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2, 'miter_outer': 'PATCH', 'miter_inner': 'ARC'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]), - MeshTest('Blocksteps2_test', 'Blocksteps2_test', 'Blocksteps2_result_9', + SpecMeshTest('Blocksteps2_test', 'Blocksteps2_test', 'Blocksteps2_result_9', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]), # 85 - MeshTest('Blocksteps3_test', 'Blocksteps3_test', 'Blocksteps3_result_10', + SpecMeshTest('Blocksteps3_test', 'Blocksteps3_test', 'Blocksteps3_result_10', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]), - MeshTest('Blocksteps4_test_1', 'Blocksteps4_test', 'Blocksteps4_result_11', + SpecMeshTest('Blocksteps4_test_1', 'Blocksteps4_test', 'Blocksteps4_result_11', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 2, 'miter_outer': 'ARC'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]), - MeshTest('Blocksteps4_test_2', 'Blocksteps4_test', 'Blocksteps4_result_12', + SpecMeshTest('Blocksteps4_test_2', 'Blocksteps4_test', 'Blocksteps4_result_12', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3, 'miter_outer': 'ARC'}, 'EDGE', {4, 7, 39, 27, 30, 31}, )]), - MeshTest('Spike_test', 'Spike_test', 'Spike_result_1', + SpecMeshTest('Spike_test', 'Spike_test', 'Spike_result_1', [OperatorSpecEditMode('bevel', {'offset': 0.2, 'segments': 3}, 'EDGE', {1, 7})]) ] diff --git a/tests/python/bl_pyapi_prop_array.py b/tests/python/bl_pyapi_prop_array.py index ac1082c009e..2f38b67d7a1 100644 --- a/tests/python/bl_pyapi_prop_array.py +++ b/tests/python/bl_pyapi_prop_array.py @@ -2,18 +2,61 @@ # ./blender.bin --background -noaudio --python tests/python/bl_pyapi_prop_array.py -- --verbose import bpy +from bpy.props import ( + BoolVectorProperty, + FloatVectorProperty, + IntVectorProperty, +) import unittest import numpy as np +id_inst = bpy.context.scene +id_type = bpy.types.Scene + + +# ----------------------------------------------------------------------------- +# Utility Functions + +def seq_items_xform(data, xform_fn): + """ + Recursively expand items using `xform_fn`. + """ + if hasattr(data, "__len__"): + return tuple(seq_items_xform(v, xform_fn) for v in data) + return xform_fn(data) + + +def seq_items_as_tuple(data): + """ + Return nested sequences as a nested tuple. + Useful when comparing different kinds of nested sequences. + """ + return seq_items_xform(data, lambda v: v) + + +def seq_items_as_dims(data): + """ + Nested length calculation, extracting the length from each sequence. + Where a 4x4 matrix returns ``(4, 4)`` for example. + """ + return ((len(data),) + seq_items_as_dims(data[0])) if hasattr(data, "__len__") else () + + +# ----------------------------------------------------------------------------- +# Tests class TestPropArray(unittest.TestCase): def setUp(self): - bpy.types.Scene.test_array_f = bpy.props.FloatVectorProperty(size=10) - bpy.types.Scene.test_array_i = bpy.props.IntVectorProperty(size=10) + id_type.test_array_f = FloatVectorProperty(size=10) + id_type.test_array_i = IntVectorProperty(size=10) scene = bpy.context.scene self.array_f = scene.test_array_f self.array_i = scene.test_array_i + def tearDown(self): + del id_type.test_array_f + del id_type.test_array_i + def test_foreach_getset_i(self): with self.assertRaises(TypeError): self.array_i.foreach_set(range(5)) @@ -79,6 +122,79 @@ class TestPropArray(unittest.TestCase): self.assertEqual(v1, v2) +class TestPropArrayMultiDimensional(unittest.TestCase): + + def setUp(self): + self._initial_dir = set(dir(id_type)) + + def tearDown(self): + for member in (set(dir(id_type)) - self._initial_dir): + delattr(id_type, member) + + def test_defaults(self): + # The data is in int format, converted into float & bool to avoid duplication. + default_data = ( + # 1D. + (1,), + (1, 2), + (1, 2, 3), + (1, 2, 3, 4), + # 2D. + ((1,),), + ((1,), (11,)), + ((1, 2), (11, 22)), + ((1, 2, 3), (11, 22, 33)), + ((1, 2, 3, 4), (11, 22, 33, 44)), + # 3D. + (((1,),),), + ((1,), (11,), (111,)), + ((1, 2), (11, 22), (111, 222),), + ((1, 2, 3), (11, 22, 33), (111, 222, 333)), + ((1, 2, 3, 4), (11, 22, 33, 44), (111, 222, 333, 444)), + ) + for data in default_data: + for (vector_prop_fn, xform_fn) in ( + (BoolVectorProperty, lambda v: bool(v % 2)), + (FloatVectorProperty, lambda v: float(v)), + (IntVectorProperty, lambda v: v), + ): + data_native = seq_items_xform(data, xform_fn) + size = seq_items_as_dims(data) + id_type.temp = vector_prop_fn(size=size, default=data_native) + data_as_tuple = seq_items_as_tuple(id_inst.temp) + self.assertEqual(data_as_tuple, data_native) + del id_type.temp + + def test_matrix(self): + data = ((1, 2, 3, 4), (11, 22, 33, 44), (111, 222, 333, 444), (1111, 2222, 3333, 4444),) + data_native = seq_items_xform(data, lambda v: float(v)) + id_type.temp = FloatVectorProperty(size=(4, 4), subtype='MATRIX', default=data_native) + data_as_tuple = seq_items_as_tuple(id_inst.temp) + self.assertEqual(data_as_tuple, data_native) + del id_type.temp + + def test_matrix_with_callbacks(self): + # """ + # Internally matrices have rows/columns swapped, + # This test ensures this is being done properly. + # """ + data = ((1, 2, 3, 4), (11, 22, 33, 44), (111, 222, 333, 444), (1111, 2222, 3333, 4444),) + data_native = seq_items_xform(data, lambda v: float(v)) + local_data = {"array": data} + + def get_fn(id_arg): + return local_data["array"] + + def set_fn(id_arg, value): + local_data["array"] = value + + id_type.temp = FloatVectorProperty(size=(4, 4), subtype='MATRIX', get=get_fn, set=set_fn) + id_inst.temp = data_native + data_as_tuple = seq_items_as_tuple(id_inst.temp) + self.assertEqual(data_as_tuple, data_native) + del id_type.temp + + if __name__ == '__main__': import sys sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []) diff --git a/tests/python/boolean_operator.py b/tests/python/boolean_operator.py index 0db6a074699..7d4de7143dd 100644 --- a/tests/python/boolean_operator.py +++ b/tests/python/boolean_operator.py @@ -29,37 +29,37 @@ import os import sys sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import MeshTest, OperatorSpecEditMode, RunTest +from modules.mesh_test import SpecMeshTest, OperatorSpecEditMode, RunTest def main(): tests = [ - MeshTest('Cubecube_intersect_union', 'Cubecube', 'Cubecube_result_1', + SpecMeshTest('Cubecube_intersect_union', 'Cubecube', 'Cubecube_result_1', [OperatorSpecEditMode('intersect_boolean', {'operation': 'UNION', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]), - MeshTest('Cubecube_intersect_intersect', 'Cubecube', 'Cubecube_result_2', + SpecMeshTest('Cubecube_intersect_intersect', 'Cubecube', 'Cubecube_result_2', [OperatorSpecEditMode('intersect_boolean', {'operation': 'INTERSECT', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]), - MeshTest('Cubecube_intersect_difference', 'Cubecube', 'Cubecube_result_3', + SpecMeshTest('Cubecube_intersect_difference', 'Cubecube', 'Cubecube_result_3', [OperatorSpecEditMode('intersect_boolean', {'operation': 'DIFFERENCE', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]), - MeshTest('Cubecube_intersect_cut', 'Cubecube', 'Cubecube_result_4', [OperatorSpecEditMode('intersect', + SpecMeshTest('Cubecube_intersect_cut', 'Cubecube', 'Cubecube_result_4', [OperatorSpecEditMode('intersect', {'separate_mode': 'CUT', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]), - MeshTest('Cubecube_intersect_all', 'Cubecube', 'Cubecube_result_5', + SpecMeshTest('Cubecube_intersect_all', 'Cubecube', 'Cubecube_result_5', [OperatorSpecEditMode('intersect', {'separate_mode': 'ALL', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]), - MeshTest('Cubecube_intersect_none', 'Cubecube', 'Cubecube_result_6', + SpecMeshTest('Cubecube_intersect_none', 'Cubecube', 'Cubecube_result_6', [OperatorSpecEditMode('intersect', {'separate_mode': 'NONE', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]), - MeshTest('Cubecube_intersect_select_none', 'Cubecube', + SpecMeshTest('Cubecube_intersect_select_none', 'Cubecube', 'Cubecube_result_7', [OperatorSpecEditMode('intersect', {'mode': 'SELECT', 'separate_mode': 'NONE', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, )]), - MeshTest('Cubecone_intersect_union', 'Cubecone', 'Cubecone_result_1', + SpecMeshTest('Cubecone_intersect_union', 'Cubecone', 'Cubecone_result_1', [OperatorSpecEditMode('intersect_boolean', {'operation': 'UNION', 'solver': 'FAST'}, 'FACE', {6, 7, 8, 9, 10}, )]), - MeshTest('Cubecones_intersect_union', 'Cubecones', 'Cubecones_result_1', + SpecMeshTest('Cubecones_intersect_union', 'Cubecones', 'Cubecones_result_1', [OperatorSpecEditMode('intersect_boolean', {'operation': 'UNION', 'solver': 'FAST'}, 'FACE', {0, 1, 2, 3, 4, 5}, )]), ] diff --git a/tests/python/curve_to_mesh.py b/tests/python/curve_to_mesh.py index 998ecdae116..654cfc750d7 100644 --- a/tests/python/curve_to_mesh.py +++ b/tests/python/curve_to_mesh.py @@ -24,74 +24,74 @@ import os import sys sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import MeshTest, OperatorSpecObjectMode, RunTest +from modules.mesh_test import SpecMeshTest, OperatorSpecObjectMode, RunTest def main(): tests = [ - MeshTest('2D Non Cyclic', 'test2DNonCyclic', 'expected2DNonCyclic', + SpecMeshTest('2D Non Cyclic', 'test2DNonCyclic', 'expected2DNonCyclic', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('2D NURBS With Tail', 'test2DNURBSWithTail', 'expected2DNURBSWithTail', + SpecMeshTest('2D NURBS With Tail', 'test2DNURBSWithTail', 'expected2DNURBSWithTail', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('2D Shape With Hole', 'test2DShapeWithHole', 'expected2DShapeWithHole', + SpecMeshTest('2D Shape With Hole', 'test2DShapeWithHole', 'expected2DShapeWithHole', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('2D Simple Lower Res', 'test2DSimpleLowerRes', 'expected2DSimpleLowerRes', + SpecMeshTest('2D Simple Lower Res', 'test2DSimpleLowerRes', 'expected2DSimpleLowerRes', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('2D Simple Low Res', 'test2DSimpleLowRes', 'expected2DSimpleLowRes', + SpecMeshTest('2D Simple Low Res', 'test2DSimpleLowRes', 'expected2DSimpleLowRes', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('2D Square', 'test2DSquare', 'expected2DSquare', + SpecMeshTest('2D Square', 'test2DSquare', 'expected2DSquare', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('2D Extrude', 'test2DExtrude', 'expected2DExtrude', + SpecMeshTest('2D Extrude', 'test2DExtrude', 'expected2DExtrude', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Bevel Back', 'testBevelBack', 'expectedBevelBack', + SpecMeshTest('Bevel Back', 'testBevelBack', 'expectedBevelBack', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Bevel Back Low Res', 'testBevelBackLowRes', 'expectedBevelBackLowRes', + SpecMeshTest('Bevel Back Low Res', 'testBevelBackLowRes', 'expectedBevelBackLowRes', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Bevel Extrude Back', 'testBevelExtrudeBack', 'expectedBevelExtrudeBack', + SpecMeshTest('Bevel Extrude Back', 'testBevelExtrudeBack', 'expectedBevelExtrudeBack', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Bevel Extrude Front', 'testBevelExtrudeFront', 'expectedBevelExtrudeFront', + SpecMeshTest('Bevel Extrude Front', 'testBevelExtrudeFront', 'expectedBevelExtrudeFront', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Bevel Extrude Full', 'testBevelExtrudeFull', 'expectedBevelExtrudeFull', + SpecMeshTest('Bevel Extrude Full', 'testBevelExtrudeFull', 'expectedBevelExtrudeFull', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Bevel Extrude Half', 'testBevelExtrudeHalf', 'expectedBevelExtrudeHalf', + SpecMeshTest('Bevel Extrude Half', 'testBevelExtrudeHalf', 'expectedBevelExtrudeHalf', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Bevel Front', 'testBevelFront', 'expectedBevelFront', + SpecMeshTest('Bevel Front', 'testBevelFront', 'expectedBevelFront', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Bevel Front Low Res', 'testBevelFrontLowRes', 'expectedBevelFrontLowRes', + SpecMeshTest('Bevel Front Low Res', 'testBevelFrontLowRes', 'expectedBevelFrontLowRes', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Bevel Full', 'testBevelFull', 'expectedBevelFull', + SpecMeshTest('Bevel Full', 'testBevelFull', 'expectedBevelFull', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Bevel Full Low Res', 'testBevelFullLowRes', 'expectedBevelFullLowRes', + SpecMeshTest('Bevel Full Low Res', 'testBevelFullLowRes', 'expectedBevelFullLowRes', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Bevel Half', 'testBevelHalf', 'expectedBevelHalf', + SpecMeshTest('Bevel Half', 'testBevelHalf', 'expectedBevelHalf', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Bevel Half Low Res', 'testBevelHalfLowRes', 'expectedBevelHalfLowRes', + SpecMeshTest('Bevel Half Low Res', 'testBevelHalfLowRes', 'expectedBevelHalfLowRes', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Caps None', 'testCapsNone', 'expectedCapsNone', + SpecMeshTest('Caps None', 'testCapsNone', 'expectedCapsNone', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Caps Object Bevel', 'testCapsObjectBevel', 'expectedCapsObjectBevel', + SpecMeshTest('Caps Object Bevel', 'testCapsObjectBevel', 'expectedCapsObjectBevel', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Caps Profile Bevel', 'testCapsProfileBevel', 'expectedCapsProfileBevel', + SpecMeshTest('Caps Profile Bevel', 'testCapsProfileBevel', 'expectedCapsProfileBevel', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Caps Profile Bevel Half', 'testCapsProfileBevelHalf', 'expectedCapsProfileBevelHalf', + SpecMeshTest('Caps Profile Bevel Half', 'testCapsProfileBevelHalf', 'expectedCapsProfileBevelHalf', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Caps Profile Bevel Quarter', 'testCapsProfileBevelQuarter', 'expectedCapsProfileBevelQuarter', + SpecMeshTest('Caps Profile Bevel Quarter', 'testCapsProfileBevelQuarter', 'expectedCapsProfileBevelQuarter', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Caps Round Bevel', 'testCapsRoundBevel', 'expectedCapsRoundBevel', + SpecMeshTest('Caps Round Bevel', 'testCapsRoundBevel', 'expectedCapsRoundBevel', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Caps Round Bevel Extrude', 'testCapsRoundBevelExtrude', 'expectedCapsRoundBevelExtrude', + SpecMeshTest('Caps Round Bevel Extrude', 'testCapsRoundBevelExtrude', 'expectedCapsRoundBevelExtrude', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Caps Round Bevel Half', 'testCapsRoundBevelHalf', 'expectedCapsRoundBevelHalf', + SpecMeshTest('Caps Round Bevel Half', 'testCapsRoundBevelHalf', 'expectedCapsRoundBevelHalf', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Caps Round Bevel Quarter', 'testCapsRoundBevelQuarter', 'expectedCapsRoundBevelQuarter', + SpecMeshTest('Caps Round Bevel Quarter', 'testCapsRoundBevelQuarter', 'expectedCapsRoundBevelQuarter', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Extrude Back', 'testExtrudeBack', 'expectedExtrudeBack', + SpecMeshTest('Extrude Back', 'testExtrudeBack', 'expectedExtrudeBack', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Extrude Front', 'testExtrudeFront', 'expectedExtrudeFront', + SpecMeshTest('Extrude Front', 'testExtrudeFront', 'expectedExtrudeFront', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Extrude Full', 'testExtrudeFull', 'expectedExtrudeFull', + SpecMeshTest('Extrude Full', 'testExtrudeFull', 'expectedExtrudeFull', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), - MeshTest('Extrude Half', 'testExtrudeHalf', 'expectedExtrudeHalf', + SpecMeshTest('Extrude Half', 'testExtrudeHalf', 'expectedExtrudeHalf', [OperatorSpecObjectMode('convert', {'target': 'MESH'})]), ] operator_test = RunTest(tests) diff --git a/tests/python/deform_modifiers.py b/tests/python/deform_modifiers.py index 7c4ea457e9d..d1241e4d9f0 100644 --- a/tests/python/deform_modifiers.py +++ b/tests/python/deform_modifiers.py @@ -28,14 +28,14 @@ import sys import bpy sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import MeshTest, ModifierSpec, OperatorSpecObjectMode, DeformModifierSpec, RunTest +from modules.mesh_test import SpecMeshTest, ModifierSpec, OperatorSpecObjectMode, DeformModifierSpec, RunTest tests = [ # Surface Deform Test, finally can bind to the Target object. # Actual deformation occurs by animating imitating user input. - MeshTest("SurfaceDeform", "testObjMonkeySurfaceDeform", "expObjMonkeySurfaceDeform", + SpecMeshTest("SurfaceDeform", "testObjMonkeySurfaceDeform", "expObjMonkeySurfaceDeform", [DeformModifierSpec(10, [ ModifierSpec('surface_deform', 'SURFACE_DEFORM', {'target': bpy.data.objects["Cube"]})], OperatorSpecObjectMode('surfacedeform_bind', {'modifier': 'surface_deform'}))]), @@ -43,7 +43,7 @@ tests = [ # Mesh Deform Test, finally can bind to the Target object. # Actual deformation occurs by animating imitating user input. - MeshTest("MeshDeform", "testObjMonkeyMeshDeform", "expObjMonkeyMeshDeform", + SpecMeshTest("MeshDeform", "testObjMonkeyMeshDeform", "expObjMonkeyMeshDeform", [DeformModifierSpec(10, [ModifierSpec('mesh_deform', 'MESH_DEFORM', {'object': bpy.data.objects["MeshCube"], 'precision': 2})], OperatorSpecObjectMode('meshdeform_bind', {'modifier': 'mesh_deform'}))]), @@ -51,21 +51,21 @@ tests = [ # Surface Deform Test, finally can bind to the Target object. # Actual deformation occurs by animating imitating user input. - MeshTest("Hook", "testObjHookPlane", "expObjHookPlane", + SpecMeshTest("Hook", "testObjHookPlane", "expObjHookPlane", [DeformModifierSpec(10, [ModifierSpec('hook', 'HOOK', {'object': bpy.data.objects["Empty"], 'falloff_radius': 1, 'vertex_group': 'Group'})])]), # Laplacian Deform Test, first a hook is attached. - MeshTest("Laplace", "testObjCubeLaplacian", "expObjCubeLaplacian", + SpecMeshTest("Laplace", "testObjCubeLaplacian", "expObjCubeLaplacian", [DeformModifierSpec(10, [ModifierSpec('hook2', 'HOOK', {'object': bpy.data.objects["Empty.001"], 'vertex_group': 'hook_vg'}), ModifierSpec('laplace', 'LAPLACIANDEFORM', {'vertex_group': 'laplace_vg'})], OperatorSpecObjectMode('laplaciandeform_bind', {'modifier': 'laplace'}))]), - MeshTest("WarpPlane", "testObjPlaneWarp", "expObjPlaneWarp", + SpecMeshTest("WarpPlane", "testObjPlaneWarp", "expObjPlaneWarp", [DeformModifierSpec(10, [ModifierSpec('warp', 'WARP', {'object_from': bpy.data.objects["From"], 'object_to': bpy.data.objects["To"], @@ -74,29 +74,29 @@ tests = [ ############################################# # Curves Deform Modifiers ############################################# - MeshTest("CurveArmature", "testObjBezierCurveArmature", "expObjBezierCurveArmature", + SpecMeshTest("CurveArmature", "testObjBezierCurveArmature", "expObjBezierCurveArmature", [DeformModifierSpec(10, [ModifierSpec('curve_armature', 'ARMATURE', {'object': bpy.data.objects['testArmatureHelper'], 'use_vertex_groups': False, 'use_bone_envelopes': True})])]), - MeshTest("CurveLattice", "testObjBezierCurveLattice", "expObjBezierCurveLattice", + SpecMeshTest("CurveLattice", "testObjBezierCurveLattice", "expObjBezierCurveLattice", [DeformModifierSpec(10, [ModifierSpec('curve_lattice', 'LATTICE', {'object': bpy.data.objects['testLatticeCurve']})])]), # HOOK for Curves can't be tested with current framework, as it requires going to Edit Mode to select vertices, # here is no equivalent of a vertex group in Curves. # Dummy test for Hook, can also be called corner case - MeshTest("CurveHook", "testObjBezierCurveHook", "expObjBezierCurveHook", + SpecMeshTest("CurveHook", "testObjBezierCurveHook", "expObjBezierCurveHook", [DeformModifierSpec(10, [ModifierSpec('curve_Hook', 'HOOK', {'object': bpy.data.objects['EmptyCurve']})])]), - MeshTest("MeshDeformCurve", "testObjCurveMeshDeform", "expObjCurveMeshDeform", + SpecMeshTest("MeshDeformCurve", "testObjCurveMeshDeform", "expObjCurveMeshDeform", [DeformModifierSpec(10, [ ModifierSpec('mesh_deform_curve', 'MESH_DEFORM', {'object': bpy.data.objects["Cylinder"], 'precision': 2})], OperatorSpecObjectMode('meshdeform_bind', {'modifier': 'mesh_deform_curve'}))]), - MeshTest("WarpCurve", "testObjBezierCurveWarp", "expObjBezierCurveWarp", + SpecMeshTest("WarpCurve", "testObjBezierCurveWarp", "expObjBezierCurveWarp", [DeformModifierSpec(10, [ModifierSpec('warp_curve', 'WARP', {'object_from': bpy.data.objects["From_curve"], 'object_to': bpy.data.objects["To_curve"]})])]), diff --git a/tests/python/geo_node_test.py b/tests/python/geo_node_test.py new file mode 100644 index 00000000000..fa87fb23bee --- /dev/null +++ b/tests/python/geo_node_test.py @@ -0,0 +1,33 @@ +# ##### 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 ##### + +# <pep8 compliant> + + +import os +import sys + +sys.path.append(os.path.dirname(os.path.realpath(__file__))) +from modules.mesh_test import BlendFileTest + +geo_node_test = BlendFileTest("test_object", "expected_object") +result = geo_node_test.run_test() + +# Telling `ctest` about the failed test by raising Exception. +if result == False: + raise Exception("Failed {}".format(geo_node_test.test_name)) diff --git a/tests/python/modifiers.py b/tests/python/modifiers.py index 09de29df401..1d9242046b3 100644 --- a/tests/python/modifiers.py +++ b/tests/python/modifiers.py @@ -26,7 +26,7 @@ from random import shuffle, seed import bpy sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import RunTest, ModifierSpec, MeshTest +from modules.mesh_test import RunTest, ModifierSpec, SpecMeshTest seed(0) @@ -87,22 +87,22 @@ def main(): # List of 'Generate' modifiers on a cube ############################### # 0 - # MeshTest("testCube", "expectedCube", get_generate_modifiers_list("testCube")), - MeshTest("CubeRandom", "testCubeRandom", "expectedCubeRandom", + # SpecMeshTest("testCube", "expectedCube", get_generate_modifiers_list("testCube")), + SpecMeshTest("CubeRandom", "testCubeRandom", "expectedCubeRandom", get_generate_modifiers_list("testCubeRandom", randomize=True)), - MeshTest("CubeMaskFirst", "testCubeMaskFirst", "expectedCubeMaskFirst", mask_first_list), + SpecMeshTest("CubeMaskFirst", "testCubeMaskFirst", "expectedCubeMaskFirst", mask_first_list), - MeshTest("CollapseDecimate", "testCollapseDecimate", "expectedCollapseDecimate", + SpecMeshTest("CollapseDecimate", "testCollapseDecimate", "expectedCollapseDecimate", [ModifierSpec('decimate', 'DECIMATE', {'decimate_type': 'COLLAPSE', 'ratio': 0.25, 'use_collapse_triangulate': True})]), - MeshTest("PlanarDecimate", "testPlanarDecimate", "expectedPlanarDecimate", + SpecMeshTest("PlanarDecimate", "testPlanarDecimate", "expectedPlanarDecimate", [ModifierSpec('decimate', 'DECIMATE', {'decimate_type': 'DISSOLVE', 'angle_limit': math.radians(30)})]), - MeshTest("UnsubdivideDecimate", "testUnsubdivideDecimate", "expectedUnsubdivideDecimate", + SpecMeshTest("UnsubdivideDecimate", "testUnsubdivideDecimate", "expectedUnsubdivideDecimate", [ModifierSpec('decimate', 'DECIMATE', {'decimate_type': 'UNSUBDIV', 'iterations': 2})]), # 5 - MeshTest("RadialBisectMirror", "testRadialBisectMirror", "expectedRadialBisectMirror", + SpecMeshTest("RadialBisectMirror", "testRadialBisectMirror", "expectedRadialBisectMirror", [ModifierSpec('mirror1', 'MIRROR', {'use_bisect_axis': (True, False, False)}), ModifierSpec('mirror2', 'MIRROR', {'use_bisect_axis': (True, False, False), 'mirror_object': bpy.data.objects[ @@ -111,17 +111,17 @@ def main(): {'use_axis': (False, True, False), 'use_bisect_axis': (False, True, False), 'use_bisect_flip_axis': (False, True, False), 'mirror_object': bpy.data.objects["testRadialBisectMirrorHelper"]})]), - MeshTest("T58411Mirror", "regressT58411Mirror", "expectedT58411Mirror", + SpecMeshTest("T58411Mirror", "regressT58411Mirror", "expectedT58411Mirror", [ModifierSpec('mirror', 'MIRROR', {}), ModifierSpec('bevel', 'BEVEL', {'segments': 2, 'limit_method': 'WEIGHT'}), ModifierSpec('subd', 'SUBSURF', {'levels': 1})]), - MeshTest("BasicScrew", "testBasicScrew", "expectedBasicScrew", + SpecMeshTest("BasicScrew", "testBasicScrew", "expectedBasicScrew", [ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testBasicScrewHelper"]}), ModifierSpec("screw", 'SCREW', {'angle': math.radians(400), 'steps': 20, 'iterations': 2, 'screw_offset': 2, 'use_normal_calculate': True})]), - MeshTest("ObjectScrew", "testObjectScrew", "expectedObjectScrew", + SpecMeshTest("ObjectScrew", "testObjectScrew", "expectedObjectScrew", [ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testObjectScrewHelper2"]}), ModifierSpec("screw", 'SCREW', {"angle": math.radians(600), 'steps': 32, 'iterations': 1, @@ -129,16 +129,16 @@ def main(): 'use_normal_calculate': True, 'object': bpy.data.objects["testObjectScrewHelper1"]})]), # 9 - MeshTest("MergedScrewWeld", "testMergedScrewWeld", "expectedMergedScrewWeld", + SpecMeshTest("MergedScrewWeld", "testMergedScrewWeld", "expectedMergedScrewWeld", [ModifierSpec("screw", 'SCREW', {'angle': math.radians(360), 'steps': 12, 'iterations': 1, 'screw_offset': 1, 'use_normal_calculate': True, 'use_merge_vertices': True}), ModifierSpec("weld", 'WELD', {"merge_threshold": 0.001})]), - MeshTest("T72380Weld", "regressT72380Weld", "expectedT72380Weld", + SpecMeshTest("T72380Weld", "regressT72380Weld", "expectedT72380Weld", [ModifierSpec('vedit', 'VERTEX_WEIGHT_EDIT', {'vertex_group': 'Group', 'use_remove': True, 'remove_threshold': 1}), ModifierSpec("weld", 'WELD', {"merge_threshold": 0.2, "vertex_group": "Group"})]), - MeshTest("T72792Weld", "regressT72792Weld", "expectedT72792Weld", + SpecMeshTest("T72792Weld", "regressT72792Weld", "expectedT72792Weld", [ModifierSpec('array', 'ARRAY', {'fit_type': 'FIXED_COUNT', 'count': 2}), ModifierSpec("weld", 'WELD', {"merge_threshold": 0.1, "vertex_group": "Group"})]), @@ -146,89 +146,89 @@ def main(): # One 'Generate' modifier on primitive meshes ############################################# # 12 - MeshTest("CubeArray", "testCubeArray", "expectedCubeArray", + SpecMeshTest("CubeArray", "testCubeArray", "expectedCubeArray", [ModifierSpec('array', 'ARRAY', {})]), - MeshTest("CapArray", "testCapArray", "expectedCapArray", + SpecMeshTest("CapArray", "testCapArray", "expectedCapArray", [ModifierSpec('array', 'ARRAY', {'fit_type': 'FIT_LENGTH', 'fit_length': 2.0, 'start_cap': bpy.data.objects["testCapStart"], 'end_cap': bpy.data.objects["testCapEnd"]})]), - MeshTest("CurveArray", "testCurveArray", "expectedCurveArray", + SpecMeshTest("CurveArray", "testCurveArray", "expectedCurveArray", [ModifierSpec('array', 'ARRAY', {'fit_type': 'FIT_CURVE', 'curve': bpy.data.objects["testCurveArrayHelper"], 'use_relative_offset': False, 'use_constant_offset': True, 'constant_offset_displace': (0.5, 0, 0)})]), - MeshTest("RadialArray", "testRadialArray", "expectedRadialArray", + SpecMeshTest("RadialArray", "testRadialArray", "expectedRadialArray", [ModifierSpec('array', 'ARRAY', {'fit_type': 'FIXED_COUNT', 'count': 3, 'use_merge_vertices': True, 'use_merge_vertices_cap': True, 'use_relative_offset': False, 'use_object_offset': True, 'offset_object': bpy.data.objects["testRadialArrayHelper"]})]), - MeshTest("CylinderBuild", "testCylinderBuild", "expectedCylinderBuild", + SpecMeshTest("CylinderBuild", "testCylinderBuild", "expectedCylinderBuild", [ModifierSpec('build', 'BUILD', {'frame_start': 1, 'frame_duration': 1}, 2)]), # 17 - MeshTest("ConeDecimate", "testConeDecimate", "expectedConeDecimate", + SpecMeshTest("ConeDecimate", "testConeDecimate", "expectedConeDecimate", [ModifierSpec('decimate', 'DECIMATE', {'ratio': 0.5})]), - MeshTest("CubeEdgeSplit", "testCubeEdgeSplit", "expectedCubeEdgeSplit", + SpecMeshTest("CubeEdgeSplit", "testCubeEdgeSplit", "expectedCubeEdgeSplit", [ModifierSpec('edge split', 'EDGE_SPLIT', {})]), - MeshTest("SphereMirror", "testSphereMirror", "expectedSphereMirror", + SpecMeshTest("SphereMirror", "testSphereMirror", "expectedSphereMirror", [ModifierSpec('mirror', 'MIRROR', {})]), - MeshTest("LocalMirror", "testLocalMirror", "expectedLocalMirror", + SpecMeshTest("LocalMirror", "testLocalMirror", "expectedLocalMirror", [ModifierSpec('mirror', 'MIRROR', {'use_clip': True})]), - MeshTest("ObjectOffsetMirror", "testObjectOffsetMirror", "expectedObjectOffsetMirror", + SpecMeshTest("ObjectOffsetMirror", "testObjectOffsetMirror", "expectedObjectOffsetMirror", [ModifierSpec('mirror', 'MIRROR', {'mirror_object': bpy.data.objects["testObjectOffsetMirrorHelper"]})]), - MeshTest("CylinderMask", "testCylinderMask", "expectedCylinderMask", + SpecMeshTest("CylinderMask", "testCylinderMask", "expectedCylinderMask", [ModifierSpec('mask', 'MASK', {'vertex_group': "mask_vertex_group"})]), - MeshTest("ConeMultiRes", "testConeMultiRes", "expectedConeMultiRes", + SpecMeshTest("ConeMultiRes", "testConeMultiRes", "expectedConeMultiRes", [ModifierSpec('multires', 'MULTIRES', {})]), # 24 - MeshTest("CubeScrew", "testCubeScrew", "expectedCubeScrew", + SpecMeshTest("CubeScrew", "testCubeScrew", "expectedCubeScrew", [ModifierSpec('screw', 'SCREW', {})]), - MeshTest("CubeSolidify", "testCubeSolidify", "expectedCubeSolidify", + SpecMeshTest("CubeSolidify", "testCubeSolidify", "expectedCubeSolidify", [ModifierSpec('solidify', 'SOLIDIFY', {})]), - MeshTest("ComplexSolidify", "testComplexSolidify", "expectedComplexSolidify", + SpecMeshTest("ComplexSolidify", "testComplexSolidify", "expectedComplexSolidify", [ModifierSpec('solidify', 'SOLIDIFY', {'solidify_mode': 'NON_MANIFOLD', 'thickness': 0.05, 'offset': 0, 'nonmanifold_thickness_mode': 'CONSTRAINTS'})]), - MeshTest("T63063Solidify", "regressT63063Solidify", "expectedT63063Solidify", + SpecMeshTest("T63063Solidify", "regressT63063Solidify", "expectedT63063Solidify", [ModifierSpec('solid', 'SOLIDIFY', {'thickness': 0.1, 'offset': 0.7})]), - MeshTest("T61979Solidify", "regressT61979Solidify", "expectedT61979Solidify", + SpecMeshTest("T61979Solidify", "regressT61979Solidify", "expectedT61979Solidify", [ModifierSpec('solid', 'SOLIDIFY', {'thickness': -0.25, 'use_even_offset': True, 'use_quality_normals': True})]), - MeshTest("MonkeySubsurf", "testMonkeySubsurf", "expectedMonkeySubsurf", + SpecMeshTest("MonkeySubsurf", "testMonkeySubsurf", "expectedMonkeySubsurf", [ModifierSpec('subsurf', 'SUBSURF', {})]), - MeshTest("CatmullClarkSubdivisionSurface", "testCatmullClarkSubdivisionSurface", + SpecMeshTest("CatmullClarkSubdivisionSurface", "testCatmullClarkSubdivisionSurface", "expectedCatmullClarkSubdivisionSurface", [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]), - MeshTest("SimpleSubdivisionSurface", "testSimpleSubdivisionSurface", "expectedSimpleSubdivisionSurface", + SpecMeshTest("SimpleSubdivisionSurface", "testSimpleSubdivisionSurface", "expectedSimpleSubdivisionSurface", [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2, 'subdivision_type': 'SIMPLE'})]), - MeshTest("Crease2dSubdivisionSurface", "testCrease2dSubdivisionSurface", "expectedCrease2dSubdivisionSurface", + SpecMeshTest("Crease2dSubdivisionSurface", "testCrease2dSubdivisionSurface", "expectedCrease2dSubdivisionSurface", [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]), - MeshTest("Crease3dSubdivisionSurface", "testCrease3dSubdivisionSurface", "expectedCrease3dSubdivisionSurface", + SpecMeshTest("Crease3dSubdivisionSurface", "testCrease3dSubdivisionSurface", "expectedCrease3dSubdivisionSurface", [ModifierSpec("subdivision", 'SUBSURF', {"levels": 2})]), # 34 - MeshTest("SphereTriangulate", "testSphereTriangulate", "expectedSphereTriangulate", + SpecMeshTest("SphereTriangulate", "testSphereTriangulate", "expectedSphereTriangulate", [ModifierSpec('triangulate', 'TRIANGULATE', {})]), - MeshTest("MonkeyWireframe", "testMonkeyWireframe", "expectedMonkeyWireframe", + SpecMeshTest("MonkeyWireframe", "testMonkeyWireframe", "expectedMonkeyWireframe", [ModifierSpec('wireframe', 'WIREFRAME', {})]), # Duplicate the object, test object and expected object have same world coordinates. - MeshTest("Skin", "testObjPlaneSkin", "expObjPlaneSkin", + SpecMeshTest("Skin", "testObjPlaneSkin", "expObjPlaneSkin", [ModifierSpec('skin', 'SKIN', {})]), - MeshTest("MergedWeld", "testMergedWeld", "expectedMergedWeld", + SpecMeshTest("MergedWeld", "testMergedWeld", "expectedMergedWeld", [ModifierSpec("weld", 'WELD', {"merge_threshold": 0.021})]), - MeshTest("MergedAllWeld", "testMergedAllWeld", "expectedMergedAllWeld", + SpecMeshTest("MergedAllWeld", "testMergedAllWeld", "expectedMergedAllWeld", [ModifierSpec("weld", 'WELD', {"merge_threshold": 1.8})]), - MeshTest("MergedNoneWeld", "testMergedNoneWeld", "expectedMergedNoneWeld", + SpecMeshTest("MergedNoneWeld", "testMergedNoneWeld", "expectedMergedNoneWeld", [ModifierSpec("weld", 'WELD', {"merge_threshold": 0.019})]), @@ -236,109 +236,109 @@ def main(): # One 'Deform' modifier on primitive meshes ############################################# # 39 - MeshTest("MonkeyArmature", "testMonkeyArmature", "expectedMonkeyArmature", + SpecMeshTest("MonkeyArmature", "testMonkeyArmature", "expectedMonkeyArmature", [ModifierSpec('armature', 'ARMATURE', {'object': bpy.data.objects['testArmature'], 'use_vertex_groups': True})]), - MeshTest("TorusCast", "testTorusCast", "expectedTorusCast", + SpecMeshTest("TorusCast", "testTorusCast", "expectedTorusCast", [ModifierSpec('cast', 'CAST', {'factor': 2.64})]), - MeshTest("CubeCurve", "testCubeCurve", "expectedCubeCurve", + SpecMeshTest("CubeCurve", "testCubeCurve", "expectedCubeCurve", [ModifierSpec('curve', 'CURVE', {'object': bpy.data.objects['testBezierCurve']})]), - MeshTest("MonkeyDisplace", "testMonkeyDisplace", "expectedMonkeyDisplace", + SpecMeshTest("MonkeyDisplace", "testMonkeyDisplace", "expectedMonkeyDisplace", [ModifierSpec('displace', "DISPLACE", {})]), # Hook modifier requires moving the hook object to get a mesh change # so can't test it with the current framework - # MeshTest("MonkeyHook", "testMonkeyHook", "expectedMonkeyHook", + # SpecMeshTest("MonkeyHook", "testMonkeyHook", "expectedMonkeyHook", # [ModifierSpec('hook', 'HOOK', {'object': bpy.data.objects["EmptyHook"], 'vertex_group': # "HookVertexGroup"})]), # 43 # ModifierSpec('laplacian_deform', 'LAPLACIANDEFORM', {}) Laplacian requires a more complex mesh - MeshTest("CubeLattice", "testCubeLattice", "expectedCubeLattice", + SpecMeshTest("CubeLattice", "testCubeLattice", "expectedCubeLattice", [ModifierSpec('lattice', 'LATTICE', {'object': bpy.data.objects["testLattice"]})]), - MeshTest("PlaneShrinkWrap", "testPlaneShrinkWrap", "expectedPlaneShrinkWrap", + SpecMeshTest("PlaneShrinkWrap", "testPlaneShrinkWrap", "expectedPlaneShrinkWrap", [ModifierSpec('shrinkwrap', 'SHRINKWRAP', {'target': bpy.data.objects["testCubeWrap"], 'offset': 0.5})]), - MeshTest("CylinderSimpleDeform", "testCylinderSimpleDeform", "expectedCylinderSimpleDeform", + SpecMeshTest("CylinderSimpleDeform", "testCylinderSimpleDeform", "expectedCylinderSimpleDeform", [ModifierSpec('simple_deform', 'SIMPLE_DEFORM', {'angle': math.radians(180), 'deform_axis': 'Z'})]), - MeshTest("PlaneSmooth", "testPlaneSmooth", "expectedPlaneSmooth", + SpecMeshTest("PlaneSmooth", "testPlaneSmooth", "expectedPlaneSmooth", [ModifierSpec('smooth', 'SMOOTH', {'iterations': 11})]), # Smooth corrective requires a complex mesh. - MeshTest("BalloonLaplacianSmooth", "testBalloonLaplacianSmooth", "expectedBalloonLaplacianSmooth", + SpecMeshTest("BalloonLaplacianSmooth", "testBalloonLaplacianSmooth", "expectedBalloonLaplacianSmooth", [ModifierSpec('laplaciansmooth', 'LAPLACIANSMOOTH', {'lambda_factor': 12, 'lambda_border': 12})]), # Gets updated often - MeshTest("WavePlane", "testObjPlaneWave", "expObjPlaneWave", + SpecMeshTest("WavePlane", "testObjPlaneWave", "expObjPlaneWave", [ModifierSpec('wave', 'WAVE', {})]), ############################################# # CURVES Generate Modifiers ############################################# # Caution: Make sure test object has no modifier in "added" state, the test may fail. - MeshTest("BezCurveArray", "testObjBezierCurveArray", "expObjBezierCurveArray", + SpecMeshTest("BezCurveArray", "testObjBezierCurveArray", "expObjBezierCurveArray", [ModifierSpec('array', 'ARRAY', {})]), - MeshTest("CurveBevel", "testObjBezierCurveBevel", "expObjBezierCurveBevel", + SpecMeshTest("CurveBevel", "testObjBezierCurveBevel", "expObjBezierCurveBevel", [ModifierSpec('bevel', 'BEVEL', {'limit_method': 'NONE'})]), - MeshTest("CurveBuild", "testObjBezierCurveBuild", "expObjBezierCurveBuild", + SpecMeshTest("CurveBuild", "testObjBezierCurveBuild", "expObjBezierCurveBuild", [ModifierSpec('build', 'BUILD', {'frame_start': 1, 'frame_duration': 1}, 2)]), - MeshTest("CurveDecimate", "testObjBezierCurveDecimate", "expObjBezierCurveDecimate", + SpecMeshTest("CurveDecimate", "testObjBezierCurveDecimate", "expObjBezierCurveDecimate", [ModifierSpec('decimate', 'DECIMATE', {'ratio': 0.5})]), - MeshTest("CurveEdgeSplit", "testObjBezierCurveEdgeSplit", "expObjBezierCurveEdgeSplit", + SpecMeshTest("CurveEdgeSplit", "testObjBezierCurveEdgeSplit", "expObjBezierCurveEdgeSplit", [ModifierSpec('edgeSplit', 'EDGE_SPLIT', {})]), - MeshTest("CurveMirror", "testObjBezierCurveMirror", "expObjBezierCurveMirror", + SpecMeshTest("CurveMirror", "testObjBezierCurveMirror", "expObjBezierCurveMirror", [ModifierSpec('mirror', 'MIRROR', {'use_axis': (True, True, False)})]), - MeshTest("CurveScrew", "testObjBezierCurveScrew", "expObjBezierCurveScrew", + SpecMeshTest("CurveScrew", "testObjBezierCurveScrew", "expObjBezierCurveScrew", [ModifierSpec('screw', 'SCREW', {})]), - MeshTest("CurveSolidify", "testObjBezierCurveSolidify", "expObjBezierCurveSolidify", + SpecMeshTest("CurveSolidify", "testObjBezierCurveSolidify", "expObjBezierCurveSolidify", [ModifierSpec('solidify', 'SOLIDIFY', {'thickness': 1})]), - MeshTest("CurveSubSurf", "testObjBezierCurveSubSurf", "expObjBezierCurveSubSurf", + SpecMeshTest("CurveSubSurf", "testObjBezierCurveSubSurf", "expObjBezierCurveSubSurf", [ModifierSpec('subSurf', 'SUBSURF', {})]), - MeshTest("CurveTriangulate", "testObjBezierCurveTriangulate", "expObjBezierCurveTriangulate", + SpecMeshTest("CurveTriangulate", "testObjBezierCurveTriangulate", "expObjBezierCurveTriangulate", [ModifierSpec('triangulate', 'TRIANGULATE', {})]), # Test 60 # Caution Weld: if the distance is increased beyond a limit, the object disappears - MeshTest("CurveWeld", "testObjBezierCurveWeld", "expObjBezierCurveWeld", + SpecMeshTest("CurveWeld", "testObjBezierCurveWeld", "expObjBezierCurveWeld", [ModifierSpec('weld', 'WELD', {})]), - MeshTest("CurveWeld2", "testObjBezierCurveWeld2", "expObjBezierCurveWeld2", + SpecMeshTest("CurveWeld2", "testObjBezierCurveWeld2", "expObjBezierCurveWeld2", [ModifierSpec('weld', 'WELD', {})]), ############################################# # Curves Deform Modifiers ############################################# # Test 62 - MeshTest("CurveCast", "testObjBezierCurveCast", "expObjBezierCurveCast", + SpecMeshTest("CurveCast", "testObjBezierCurveCast", "expObjBezierCurveCast", [ModifierSpec('Cast', 'CAST', {'cast_type': 'CYLINDER', 'factor': 10})]), - MeshTest("CurveShrinkWrap", "testObjBezierCurveShrinkWrap", "expObjBezierCurveShrinkWrap", + SpecMeshTest("CurveShrinkWrap", "testObjBezierCurveShrinkWrap", "expObjBezierCurveShrinkWrap", [ModifierSpec('ShrinkWrap', 'SHRINKWRAP', {'target': bpy.data.objects['testShrinkWrapHelperSuzanne']})]), - MeshTest("CurveSimpleDeform", "testObjBezierCurveSimpleDeform", "expObjBezierCurveSimpleDeform", + SpecMeshTest("CurveSimpleDeform", "testObjBezierCurveSimpleDeform", "expObjBezierCurveSimpleDeform", [ModifierSpec('simple_deform', 'SIMPLE_DEFORM', {'angle': math.radians(90)})]), - MeshTest("CurveSmooth", "testObjBezierCurveSmooth", "expObjBezierCurveSmooth", + SpecMeshTest("CurveSmooth", "testObjBezierCurveSmooth", "expObjBezierCurveSmooth", [ModifierSpec('smooth', 'SMOOTH', {'factor': 10})]), - MeshTest("CurveWave", "testObjBezierCurveWave", "expObjBezierCurveWave", + SpecMeshTest("CurveWave", "testObjBezierCurveWave", "expObjBezierCurveWave", [ModifierSpec('curve_wave', 'WAVE', {'time_offset': -1.5})]), - MeshTest("CurveCurve", "testObjBezierCurveCurve", "expObjBezierCurveCurve", + SpecMeshTest("CurveCurve", "testObjBezierCurveCurve", "expObjBezierCurveCurve", [ModifierSpec('curve_Curve', 'CURVE', {'object': bpy.data.objects['NurbsCurve']})]), ] diff --git a/tests/python/modules/mesh_test.py b/tests/python/modules/mesh_test.py index 6d921959e6f..4cf545d3292 100644 --- a/tests/python/modules/mesh_test.py +++ b/tests/python/modules/mesh_test.py @@ -33,13 +33,14 @@ # delete the duplicate object # # The words in angle brackets are parameters of the test, and are specified in -# the main class MeshTest. +# the abstract class MeshTest. # # If the environment variable BLENDER_TEST_UPDATE is set to 1, the <expected_object> # is updated with the new test result. # Tests are verbose when the environment variable BLENDER_VERBOSE is set. +from abc import ABC, abstractmethod import bpy import functools import inspect @@ -161,121 +162,321 @@ class DeformModifierSpec: return "Modifier: " + str(self.modifier_list) + " with object operator " + str(self.object_operator_spec) -class MeshTest: +class MeshTest(ABC): """ - A mesh testing class targeted at testing modifiers and operators on a single object. - It holds a stack of mesh operations, i.e. modifiers or operators. The test is executed using - the public method run_test(). + A mesh testing Abstract class that hold common functionalities for testting operations. """ - def __init__( - self, - test_name: str, - test_object_name: str, - expected_object_name: str, - operations_stack=None, - apply_modifiers=False, - do_compare=False, - threshold=None - ): - """ - Constructs a MeshTest object. Raises a KeyError if objects with names expected_object_name - or test_object_name don't exist. - :param test_name: str - unique test name identifier. + def __init__(self, test_object_name, exp_object_name, test_name=None, threshold=None, do_compare=True): + """ :param test_object_name: str - Name of object of mesh type to run the operations on. - :param expected_object_name: str - Name of object of mesh type that has the expected + :param exp_object_name: str - Name of object of mesh type that has the expected geometry after running the operations. - :param operations_stack: list - stack holding operations to perform on the test_object. - :param apply_modifiers: bool - True if we want to apply the modifiers right after adding them to the object. - - True if we want to apply the modifier to a list of modifiers, after some operation. - This affects operations of type ModifierSpec and DeformModifierSpec. + :param test_name: str - Name of the test. + :param threshold: exponent: To allow variations and accept difference to a certain degree. :param do_compare: bool - True if we want to compare the test and expected objects, False otherwise. - :param threshold : exponent: To allow variations and accept difference to a certain degree. - """ - if operations_stack is None: - operations_stack = [] - for operation in operations_stack: - if not (isinstance(operation, ModifierSpec) or isinstance(operation, OperatorSpecEditMode) - or isinstance(operation, OperatorSpecObjectMode) or isinstance(operation, DeformModifierSpec) - or isinstance(operation, ParticleSystemSpec)): - raise ValueError("Expected operation of type {} or {} or {} or {}. Got {}". - format(type(ModifierSpec), type(OperatorSpecEditMode), - type(DeformModifierSpec), type(ParticleSystemSpec), - type(operation))) - self.operations_stack = operations_stack - self.apply_modifier = apply_modifiers - self.do_compare = do_compare + self.test_object_name = test_object_name + self.exp_object_name = exp_object_name + if test_name: + self.test_name = test_name + else: + filepath = bpy.data.filepath + self.test_name = bpy.path.display_name_from_filepath(filepath) self.threshold = threshold - self.test_name = test_name + self.do_compare = do_compare + self.update = os.getenv("BLENDER_TEST_UPDATE") is not None + self.verbose = os.getenv("BLENDER_VERBOSE") is not None + self.test_updated_counter = 0 + objects = bpy.data.objects + self.evaluated_object = None + self.test_object = objects[self.test_object_name] - self.verbose = os.environ.get("BLENDER_VERBOSE") is not None - self.update = os.getenv('BLENDER_TEST_UPDATE') is not None + if self.update: + if exp_object_name in objects: + self.expected_object = objects[self.exp_object_name] + else: + self.create_expected_object() + else: + self.expected_object = objects[self.exp_object_name] - # Initialize test objects. - objects = bpy.data.objects - self.test_object = objects[test_object_name] - self.expected_object = objects[expected_object_name] + def create_expected_object(self): + """ + Creates an expected object 10 units away + in Y direction from test object. + """ if self.verbose: - print("Found test object {}".format(test_object_name)) - print("Found test object {}".format(expected_object_name)) + print("Creating expected object...") + self.create_evaluated_object() + self.expected_object = self.evaluated_object + self.expected_object.name = self.exp_object_name + x, y, z = self.test_object.location + self.expected_object.location = (x, y+10, z) + bpy.ops.wm.save_as_mainfile(filepath=bpy.data.filepath) - # Private flag to indicate whether the blend file was updated after the test. - self._test_updated = False + def create_evaluated_object(self): + """ + Creates an evaluated object. + """ + bpy.context.view_layer.objects.active = self.test_object - def set_test_object(self, test_object_name): + # Duplicate test object. + bpy.ops.object.mode_set(mode="OBJECT") + bpy.ops.object.select_all(action="DESELECT") + bpy.context.view_layer.objects.active = self.test_object + + self.test_object.select_set(True) + bpy.ops.object.duplicate() + self.evaluated_object = bpy.context.active_object + self.evaluated_object.name = "evaluated_object" + + @staticmethod + def _print_result(result): """ - Set test object for the test. Raises a KeyError if object with given name does not exist. - :param test_object_name: name of test object to run operations on. + Prints the comparison, selection and validation result. """ - objects = bpy.data.objects - self.test_object = objects[test_object_name] + print("Results:") + for key in result: + print("{} : {}".format(key, result[key][1])) + print() - def set_expected_object(self, expected_object_name): + def run_test(self): """ - Set expected object for the test. Raises a KeyError if object with given name does not exist - :param expected_object_name: Name of expected object. + Runs a single test, runs it again if test file is updated. """ - objects = bpy.data.objects - self.expected_object = objects[expected_object_name] - def _on_failed_test(self, compare_result, validation_success, evaluated_test_object): - if self.update and validation_success: - if self.verbose: - print("Test failed expectantly. Updating expected mesh...") + self.create_evaluated_object() + self.apply_operations(self.evaluated_object.name) - # Replace expected object with object we ran operations on, i.e. evaluated_test_object. - evaluated_test_object.location = self.expected_object.location - expected_object_name = self.expected_object.name - evaluated_selection = {v.index for v in evaluated_test_object.data.vertices if v.select} + if not self.do_compare: + print("\nVisualization purpose only: Open Blender in GUI mode") + print("Compare evaluated and expected object in Blender.\n") + return False + + result = self.compare_meshes(self.evaluated_object, self.expected_object, self.threshold) - bpy.data.objects.remove(self.expected_object, do_unlink=True) - evaluated_test_object.name = expected_object_name - self._do_selection(evaluated_test_object.data, "VERT", evaluated_selection) + # Initializing with True to get correct resultant of result_code booleans. + success = True + inside_loop_flag = False + for key in result: + inside_loop_flag = True + success = success and result[key][0] - # Save file. - bpy.ops.wm.save_as_mainfile(filepath=bpy.data.filepath) + # Check "success" is actually evaluated and is not the default True value. + if not inside_loop_flag: + success = False - self._test_updated = True - # Set new expected object. - self.expected_object = evaluated_test_object + if success: + self.print_passed_test_result(result) + # Clean up. + if self.verbose: + print("Cleaning up...") + # Delete evaluated_test_object. + bpy.ops.object.delete() return True - else: - print("Test comparison result: {}".format(compare_result)) - print("Test validation result: {}".format(validation_success)) - print("Resulting object mesh '{}' did not match expected object '{}' from file {}". - format(evaluated_test_object.name, self.expected_object.name, bpy.data.filepath)) + elif self.update: + self.print_failed_test_result(result) + self.update_failed_test() + # Check for testing the blend file is updated and re-running. + # Also safety check to avoid infinite recursion loop. + if self.test_updated_counter == 1: + print("Re-running test...") + self.run_test() + else: + print("The test fails consistently. Exiting...") + return False + else: + self.print_failed_test_result(result) return False - def is_test_updated(self): + def print_failed_test_result(self, result): + """ + Print results for failed test. + """ + print("\nFAILED {} test with the following: ".format(self.test_name)) + self._print_result(result) + + def print_passed_test_result(self, result): + """ + Print results for passing test. + """ + print("\nPASSED {} test successfully.".format(self.test_name)) + self._print_result(result) + + def do_selection(self, mesh: bpy.types.Mesh, select_mode: str, selection: set): + """ + Do selection on a mesh. + :param mesh: bpy.types.Mesh - input mesh + :param: select_mode: str - selection mode. Must be 'VERT', 'EDGE' or 'FACE' + :param: selection: set - indices of selection. + + Example: select_mode='VERT' and selection={1,2,3} selects veritces 1, 2 and 3 of input mesh """ - Check whether running the test with BLENDER_TEST_UPDATE actually modified the .blend test file. - :return: Bool - True if blend file has been updated. False otherwise. + # Deselect all objects. + bpy.ops.object.mode_set(mode='EDIT') + bpy.ops.mesh.select_all(action='DESELECT') + bpy.ops.object.mode_set(mode='OBJECT') + + bpy.context.tool_settings.mesh_select_mode = (select_mode == 'VERT', + select_mode == 'EDGE', + select_mode == 'FACE') + + items = (mesh.vertices if select_mode == 'VERT' + else mesh.edges if select_mode == 'EDGE' + else mesh.polygons if select_mode == 'FACE' + else None) + if items is None: + raise ValueError("Invalid selection mode") + for index in selection: + items[index].select = True + + def update_failed_test(self): + """ + Updates expected object. """ - return self._test_updated + self.evaluated_object.location = self.expected_object.location + expected_object_name = self.expected_object.name + evaluated_selection = { + v.index for v in self.evaluated_object.data.vertices if v.select} + + bpy.data.objects.remove(self.expected_object, do_unlink=True) + self.evaluated_object.name = expected_object_name + self.do_selection(self.evaluated_object.data, + "VERT", evaluated_selection) + + # Save file. + bpy.ops.wm.save_as_mainfile(filepath=bpy.data.filepath) + self.test_updated_counter += 1 + self.expected_object = self.evaluated_object + + @staticmethod + def compare_meshes(evaluated_object, expected_object, threshold): + """ + Compares evaluated object mesh with expected object mesh. + :param evaluated_object: first object for comparison. + :param expected_object: second object for comparison. + :param threshold: exponent: To allow variations and accept difference to a certain degree. + :return: dict: Contains results of different comparisons. + """ + objects = bpy.data.objects + evaluated_test_mesh = objects[evaluated_object.name].data + expected_mesh = expected_object.data + result_codes = {} + + # Mesh Comparison. + if threshold: + result_mesh = expected_mesh.unit_test_compare( + mesh=evaluated_test_mesh, threshold=threshold) + else: + result_mesh = expected_mesh.unit_test_compare( + mesh=evaluated_test_mesh) + + if result_mesh == "Same": + result_codes['Mesh Comparison'] = (True, result_mesh) + else: + result_codes['Mesh Comparison'] = (False, result_mesh) + + # Selection comparison. + + selected_evaluated_verts = [ + v.index for v in evaluated_test_mesh.vertices if v.select] + selected_expected_verts = [ + v.index for v in expected_mesh.vertices if v.select] + + if selected_evaluated_verts == selected_expected_verts: + result_selection = "Same" + result_codes['Selection Comparison'] = (True, result_selection) + else: + result_selection = "Selection doesn't match." + result_codes['Selection Comparison'] = (False, result_selection) + + # Validation check. + result_validation = evaluated_test_mesh.validate(verbose=True) + if result_validation: + result_validation = "Invalid Mesh" + result_codes['Mesh Validation'] = (False, result_validation) + else: + result_validation = "Valid" + result_codes['Mesh Validation'] = (True, result_validation) + + return result_codes + + @abstractmethod + def apply_operations(self, object_name): + """ + Apply operations on this object. + + object_name (str): Name of the test object on which operations will be applied. + """ + pass + + +class SpecMeshTest(MeshTest): + """ + A mesh testing class inherited from MeshTest class targeted at testing modifiers and operators on a single object. + It holds a stack of mesh operations, i.e. modifiers or operators. The test is executed using MeshTest's run_test. + """ + + def __init__(self, test_name, + test_object_name, + exp_object_name, + operations_stack=None, + apply_modifier=True, + threshold=None): + """ + Constructor for SpecMeshTest. + + :param test_name: str - Name of the test. + :param test_object_name: str - Name of object of mesh type to run the operations on. + :param exp_object_name: str - Name of object of mesh type that has the expected + geometry after running the operations. + :param operations_stack: list - stack holding operations to perform on the test_object. + :param apply_modifier: bool - True if we want to apply the modifiers right after adding them to the object. + - True if we want to apply the modifier to list of modifiers, after some operation. + This affects operations of type ModifierSpec and DeformModifierSpec. + """ + + super().__init__(test_object_name, exp_object_name, test_name, threshold) + self.test_name = test_name + if operations_stack is None: + self.operations_stack = [] + else: + self.operations_stack = operations_stack + self.apply_modifier = apply_modifier + + def apply_operations(self, evaluated_test_object_name): + # Add modifiers and operators. + SpecMeshTest.apply_operations.__doc__ = MeshTest.apply_operations.__doc__ + evaluated_test_object = bpy.data.objects[evaluated_test_object_name] + if self.verbose: + print("Applying operations...") + for operation in self.operations_stack: + if isinstance(operation, ModifierSpec): + self._add_modifier(evaluated_test_object, operation) + if self.apply_modifier: + self._apply_modifier( + evaluated_test_object, operation.modifier_name) + + elif isinstance(operation, OperatorSpecEditMode): + self._apply_operator_edit_mode( + evaluated_test_object, operation) + + elif isinstance(operation, OperatorSpecObjectMode): + self._apply_operator_object_mode(operation) + + elif isinstance(operation, DeformModifierSpec): + self._apply_deform_modifier(evaluated_test_object, operation) + + elif isinstance(operation, ParticleSystemSpec): + self._apply_particle_system(evaluated_test_object, operation) + + else: + raise ValueError("Expected operation of type {} or {} or {} or {}. Got {}". + format(type(ModifierSpec), type(OperatorSpecEditMode), + type(OperatorSpecObjectMode), type(ParticleSystemSpec), type(operation))) def _set_parameters_impl(self, modifier, modifier_parameters, nested_settings_path, modifier_name): """ @@ -312,14 +513,15 @@ class MeshTest: for key in modifier_parameters: nested_settings_path.append(key) - self._set_parameters_impl(modifier, modifier_parameters[key], nested_settings_path, modifier_name) + self._set_parameters_impl( + modifier, modifier_parameters[key], nested_settings_path, modifier_name) if nested_settings_path: nested_settings_path.pop() def set_parameters(self, modifier, modifier_parameters): """ - Wrapper for _set_parameters_util + Wrapper for _set_parameters_impl. """ settings = [] modifier_name = modifier.name @@ -430,40 +632,14 @@ class MeshTest: if self.apply_modifier: self._apply_modifier(test_object, particle_sys_spec.modifier_name) - def _do_selection(self, mesh: bpy.types.Mesh, select_mode: str, selection: set): - """ - Do selection on a mesh - :param mesh: bpy.types.Mesh - input mesh - :param: select_mode: str - selection mode. Must be 'VERT', 'EDGE' or 'FACE' - :param: selection: set - indices of selection. - - Example: select_mode='VERT' and selection={1,2,3} selects veritces 1, 2 and 3 of input mesh - """ - # deselect all - bpy.ops.object.mode_set(mode='EDIT') - bpy.ops.mesh.select_all(action='DESELECT') - bpy.ops.object.mode_set(mode='OBJECT') - - bpy.context.tool_settings.mesh_select_mode = (select_mode == 'VERT', - select_mode == 'EDGE', - select_mode == 'FACE') - - items = (mesh.vertices if select_mode == 'VERT' - else mesh.edges if select_mode == 'EDGE' - else mesh.polygons if select_mode == 'FACE' - else None) - if items is None: - raise ValueError("Invalid selection mode") - for index in selection: - items[index].select = True - def _apply_operator_edit_mode(self, test_object, operator: OperatorSpecEditMode): """ Apply operator on test object. :param test_object: bpy.types.Object - Blender object to apply operator on. :param operator: OperatorSpecEditMode - OperatorSpecEditMode object with parameters. """ - self._do_selection(test_object.data, operator.select_mode, operator.selection) + self.do_selection( + test_object.data, operator.select_mode, operator.selection) # Apply operator in edit mode. bpy.ops.object.mode_set(mode='EDIT') @@ -528,96 +704,27 @@ class MeshTest: for mod_name in modifier_names: self._apply_modifier(test_object, mod_name) - def run_test(self): - """ - Apply operations in self.operations_stack on self.test_object and compare the - resulting mesh with self.expected_object.data - :return: bool - True if the test passed, False otherwise. - """ - self._test_updated = False - bpy.context.view_layer.objects.active = self.test_object - - # Duplicate test object. - bpy.ops.object.mode_set(mode="OBJECT") - bpy.ops.object.select_all(action="DESELECT") - bpy.context.view_layer.objects.active = self.test_object - - self.test_object.select_set(True) - bpy.ops.object.duplicate() - evaluated_test_object = bpy.context.active_object - evaluated_test_object.name = "evaluated_object" - if self.verbose: - print() - print(evaluated_test_object.name, "is set to active") - - # Add modifiers and operators. - for operation in self.operations_stack: - if isinstance(operation, ModifierSpec): - self._add_modifier(evaluated_test_object, operation) - if self.apply_modifier: - self._apply_modifier(evaluated_test_object, operation.modifier_name) - - elif isinstance(operation, OperatorSpecEditMode): - self._apply_operator_edit_mode(evaluated_test_object, operation) - - elif isinstance(operation, OperatorSpecObjectMode): - self._apply_operator_object_mode(operation) - - elif isinstance(operation, DeformModifierSpec): - self._apply_deform_modifier(evaluated_test_object, operation) - - elif isinstance(operation, ParticleSystemSpec): - self._apply_particle_system(evaluated_test_object, operation) - - else: - raise ValueError("Expected operation of type {} or {} or {} or {}. Got {}". - format(type(ModifierSpec), type(OperatorSpecEditMode), - type(OperatorSpecObjectMode), type(ParticleSystemSpec), type(operation))) - - # Compare resulting mesh with expected one. - # Compare only when self.do_compare is set to True, it is set to False for run-test and returns. - if not self.do_compare: - print("Meshes/objects are not compared, compare evaluated and expected object in Blender for " - "visualization only.") - return False - - if self.verbose: - print("Comparing expected mesh with resulting mesh...") - evaluated_test_mesh = evaluated_test_object.data - expected_mesh = self.expected_object.data - if self.threshold: - compare_result = evaluated_test_mesh.unit_test_compare(mesh=expected_mesh, threshold=self.threshold) - else: - compare_result = evaluated_test_mesh.unit_test_compare(mesh=expected_mesh) - compare_success = (compare_result == 'Same') - - selected_evaluatated_verts = [v.index for v in evaluated_test_mesh.vertices if v.select] - selected_expected_verts = [v.index for v in expected_mesh.vertices if v.select] - if selected_evaluatated_verts != selected_expected_verts: - compare_result = "Selection doesn't match" - compare_success = False - # Also check if invalid geometry (which is never expected) had to be corrected... - validation_success = not evaluated_test_mesh.validate(verbose=True) +class BlendFileTest(MeshTest): + """ + A mesh testing class inherited from MeshTest aimed at testing operations like modifiers loaded directly from + blend file i.e. without adding them from scratch or without adding specifications. + """ - if compare_success and validation_success: - if self.verbose: - print("Success!") + def apply_operations(self, evaluated_test_object_name): - # Clean up. - if self.verbose: - print("Cleaning up...") - # Delete evaluated_test_object. - bpy.ops.object.delete() - return True - - else: - return self._on_failed_test(compare_result, validation_success, evaluated_test_object) + BlendFileTest.apply_operations.__doc__ = MeshTest.apply_operations.__doc__ + evaluated_test_object = bpy.data.objects[evaluated_test_object_name] + modifiers_list = evaluated_test_object.modifiers + if not modifiers_list: + raise Exception("No modifiers are added to test object.") + for modifier in modifiers_list: + bpy.ops.object.modifier_apply(modifier=modifier.name) class RunTest: """ - Helper class that stores and executes modifier tests. + Helper class that stores and executes SpecMeshTest tests. Example usage: @@ -629,9 +736,9 @@ class RunTest: >>> OperatorSpecEditMode("delete_edgeloop", {}, "EDGE", MONKEY_LOOP_EDGE), >>> ] >>> tests = [ - >>> MeshTest("Test1", "testCube", "expectedCube", modifier_list), - >>> MeshTest("Test2", "testCube_2", "expectedCube_2", modifier_list), - >>> MeshTest("MonkeyDeleteEdge", "testMonkey","expectedMonkey", operator_list) + >>> SpecMeshTest("Test1", "testCube", "expectedCube", modifier_list), + >>> SpecMeshTest("Test2", "testCube_2", "expectedCube_2", modifier_list), + >>> SpecMeshTest("MonkeyDeleteEdge", "testMonkey","expectedMonkey", operator_list) >>> ] >>> modifiers_test = RunTest(tests) >>> modifiers_test.run_all_tests() @@ -639,7 +746,7 @@ class RunTest: def __init__(self, tests, apply_modifiers=False, do_compare=False): """ - Construct a modifier test. + Construct a test suite. :param tests: list - list of modifier or operator test cases. Each element in the list must contain the following in the correct order: @@ -674,7 +781,7 @@ class RunTest: def run_all_tests(self): """ - Run all tests in self.tests list. Raises an exception if one the tests fails. + Run all tests in self.tests list. Displays all failed tests at bottom. """ for test_number, each_test in enumerate(self.tests): test_name = each_test.test_name @@ -717,15 +824,8 @@ class RunTest: raise Exception('No test called {} found!'.format(test_name)) test = case - if self.apply_modifiers: - test.apply_modifier = True - - if self.do_compare: - test.do_compare = True + test.apply_modifier = self.apply_modifiers + test.do_compare = self.do_compare success = test.run_test() - if test.is_test_updated(): - # Run the test again if the blend file has been updated. - success = test.run_test() - return success diff --git a/tests/python/operators.py b/tests/python/operators.py index 4501df82175..9e5ac0054e8 100644 --- a/tests/python/operators.py +++ b/tests/python/operators.py @@ -26,7 +26,7 @@ from random import shuffle, seed seed(0) sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import MeshTest, OperatorSpecEditMode, RunTest +from modules.mesh_test import SpecMeshTest, OperatorSpecEditMode, RunTest # Central vertical loop of Suzanne MONKEY_LOOP_VERT = {68, 69, 71, 73, 74, 75, 76, 77, 90, 129, 136, 175, 188, 189, 198, 207, @@ -38,242 +38,242 @@ MONKEY_LOOP_EDGE = {131, 278, 299, 305, 307, 334, 337, 359, 384, 396, 399, 412, def main(): tests = [ # bisect - MeshTest("CubeBisect", "testCubeBisect", "expectedCubeBisect", + SpecMeshTest("CubeBisect", "testCubeBisect", "expectedCubeBisect", [OperatorSpecEditMode("bisect", {"plane_co": (0, 0, 0), "plane_no": (0, 1, 1), "clear_inner": True, "use_fill": True}, 'FACE', {0, 1, 2, 3, 4, 5}, )]), # blend from shape - MeshTest("CubeBlendFromShape", "testCubeBlendFromShape", "expectedCubeBlendFromShape", + SpecMeshTest("CubeBlendFromShape", "testCubeBlendFromShape", "expectedCubeBlendFromShape", [OperatorSpecEditMode("blend_from_shape", {"shape": "Key 1"}, 'FACE', {0, 1, 2, 3, 4, 5})]), # bridge edge loops - MeshTest("CubeBridgeEdgeLoop", "testCubeBrigeEdgeLoop", "expectedCubeBridgeEdgeLoop", + SpecMeshTest("CubeBridgeEdgeLoop", "testCubeBrigeEdgeLoop", "expectedCubeBridgeEdgeLoop", [OperatorSpecEditMode("bridge_edge_loops", {}, "FACE", {0, 1})]), # decimate - MeshTest("MonkeyDecimate", "testMonkeyDecimate", "expectedMonkeyDecimate", + SpecMeshTest("MonkeyDecimate", "testMonkeyDecimate", "expectedMonkeyDecimate", [OperatorSpecEditMode("decimate", {"ratio": 0.1}, "FACE", {i for i in range(500)})]), # delete - MeshTest("CubeDeleteVertices", "testCubeDeleteVertices", "expectedCubeDeleteVertices", + SpecMeshTest("CubeDeleteVertices", "testCubeDeleteVertices", "expectedCubeDeleteVertices", [OperatorSpecEditMode("delete", {}, "VERT", {3})]), - MeshTest("CubeDeleteFaces", "testCubeDeleteFaces", "expectedCubeDeleteFaces", + SpecMeshTest("CubeDeleteFaces", "testCubeDeleteFaces", "expectedCubeDeleteFaces", [OperatorSpecEditMode("delete", {}, "FACE", {0})]), - MeshTest("CubeDeleteEdges", "testCubeDeleteEdges", "expectedCubeDeleteEdges", + SpecMeshTest("CubeDeleteEdges", "testCubeDeleteEdges", "expectedCubeDeleteEdges", [OperatorSpecEditMode("delete", {}, "EDGE", {0, 1, 2, 3})]), # delete edge loop - MeshTest("MonkeyDeleteEdgeLoopVertices", "testMokneyDeleteEdgeLoopVertices", + SpecMeshTest("MonkeyDeleteEdgeLoopVertices", "testMokneyDeleteEdgeLoopVertices", "expectedMonkeyDeleteEdgeLoopVertices", [OperatorSpecEditMode("delete_edgeloop", {}, "VERT", MONKEY_LOOP_VERT)]), - MeshTest("MonkeyDeleteEdgeLoopEdges", "testMokneyDeleteEdgeLoopEdges", + SpecMeshTest("MonkeyDeleteEdgeLoopEdges", "testMokneyDeleteEdgeLoopEdges", "expectedMonkeyDeleteEdgeLoopEdges", [OperatorSpecEditMode("delete_edgeloop", {}, "EDGE", MONKEY_LOOP_EDGE)]), # delete loose - MeshTest("CubeDeleteLooseVertices", "testCubeDeleteLooseVertices", + SpecMeshTest("CubeDeleteLooseVertices", "testCubeDeleteLooseVertices", "expectedCubeDeleteLooseVertices", [OperatorSpecEditMode("delete_loose", {"use_verts": True, "use_edges": False, "use_faces": False}, "VERT", {i for i in range(12)})]), - MeshTest("CubeDeleteLooseEdges", "testCubeDeleteLooseEdges", + SpecMeshTest("CubeDeleteLooseEdges", "testCubeDeleteLooseEdges", "expectedCubeDeleteLooseEdges", [OperatorSpecEditMode("delete_loose", {"use_verts": False, "use_edges": True, "use_faces": False}, "EDGE", {i for i in range(14)})]), - MeshTest("CubeDeleteLooseFaces", "testCubeDeleteLooseFaces", + SpecMeshTest("CubeDeleteLooseFaces", "testCubeDeleteLooseFaces", "expectedCubeDeleteLooseFaces", [OperatorSpecEditMode("delete_loose", {"use_verts": False, "use_edges": False, "use_faces": True}, "FACE", {i for i in range(7)})]), # dissolve degenerate - MeshTest("CubeDissolveDegenerate", "testCubeDissolveDegenerate", + SpecMeshTest("CubeDissolveDegenerate", "testCubeDissolveDegenerate", "expectedCubeDissolveDegenerate", [OperatorSpecEditMode("dissolve_degenerate", {}, "VERT", {i for i in range(8)})]), # dissolve edges - MeshTest("CylinderDissolveEdges", "testCylinderDissolveEdges", "expectedCylinderDissolveEdges", + SpecMeshTest("CylinderDissolveEdges", "testCylinderDissolveEdges", "expectedCylinderDissolveEdges", [OperatorSpecEditMode("dissolve_edges", {}, "EDGE", {0, 5, 6, 9})]), # dissolve faces - MeshTest("CubeDissolveFaces", "testCubeDissolveFaces", "expectedCubeDissolveFaces", + SpecMeshTest("CubeDissolveFaces", "testCubeDissolveFaces", "expectedCubeDissolveFaces", [OperatorSpecEditMode("dissolve_faces", {}, "VERT", {5, 34, 47, 49, 83, 91, 95})]), # dissolve verts - MeshTest("CubeDissolveVerts", "testCubeDissolveVerts", "expectedCubeDissolveVerts", + SpecMeshTest("CubeDissolveVerts", "testCubeDissolveVerts", "expectedCubeDissolveVerts", [OperatorSpecEditMode("dissolve_verts", {}, "VERT", {16, 20, 22, 23, 25})]), # duplicate - MeshTest("ConeDuplicateVertices", "testConeDuplicateVertices", + SpecMeshTest("ConeDuplicateVertices", "testConeDuplicateVertices", "expectedConeDuplicateVertices", [OperatorSpecEditMode("duplicate", {}, "VERT", {i for i in range(33)} - {23})]), - MeshTest("ConeDuplicateOneVertex", "testConeDuplicateOneVertex", "expectedConeDuplicateOneVertex", + SpecMeshTest("ConeDuplicateOneVertex", "testConeDuplicateOneVertex", "expectedConeDuplicateOneVertex", [OperatorSpecEditMode("duplicate", {}, "VERT", {23})]), - MeshTest("ConeDuplicateFaces", "testConeDuplicateFaces", "expectedConeDuplicateFaces", + SpecMeshTest("ConeDuplicateFaces", "testConeDuplicateFaces", "expectedConeDuplicateFaces", [OperatorSpecEditMode("duplicate", {}, "FACE", {6, 9})]), - MeshTest("ConeDuplicateEdges", "testConeDuplicateEdges", "expectedConeDuplicateEdges", + SpecMeshTest("ConeDuplicateEdges", "testConeDuplicateEdges", "expectedConeDuplicateEdges", [OperatorSpecEditMode("duplicate", {}, "EDGE", {i for i in range(64)})]), # edge collapse - MeshTest("CylinderEdgeCollapse", "testCylinderEdgeCollapse", "expectedCylinderEdgeCollapse", + SpecMeshTest("CylinderEdgeCollapse", "testCylinderEdgeCollapse", "expectedCylinderEdgeCollapse", [OperatorSpecEditMode("edge_collapse", {}, "EDGE", {1, 9, 4})]), # edge face add - MeshTest("CubeEdgeFaceAddFace", "testCubeEdgeFaceAddFace", "expectedCubeEdgeFaceAddFace", + SpecMeshTest("CubeEdgeFaceAddFace", "testCubeEdgeFaceAddFace", "expectedCubeEdgeFaceAddFace", [OperatorSpecEditMode("edge_face_add", {}, "VERT", {1, 3, 4, 5, 7})]), - MeshTest("CubeEdgeFaceAddEdge", "testCubeEdgeFaceAddEdge", "expectedCubeEdgeFaceAddEdge", + SpecMeshTest("CubeEdgeFaceAddEdge", "testCubeEdgeFaceAddEdge", "expectedCubeEdgeFaceAddEdge", [OperatorSpecEditMode("edge_face_add", {}, "VERT", {4, 5})]), # edge rotate - MeshTest("CubeEdgeRotate", "testCubeEdgeRotate", "expectedCubeEdgeRotate", + SpecMeshTest("CubeEdgeRotate", "testCubeEdgeRotate", "expectedCubeEdgeRotate", [OperatorSpecEditMode("edge_rotate", {}, "EDGE", {1})]), # edge split - MeshTest("CubeEdgeSplit", "testCubeEdgeSplit", "expectedCubeEdgeSplit", + SpecMeshTest("CubeEdgeSplit", "testCubeEdgeSplit", "expectedCubeEdgeSplit", [OperatorSpecEditMode("edge_split", {}, "EDGE", {2, 5, 8, 11, 14, 17, 20, 23})]), # edge ring select - Cannot be tested. Need user input. - # MeshTest("CubeEdgeRingSelect", "testCubeEdgeRingSelect", "expectedCubeEdgeRingSelect", + # SpecMeshTest("CubeEdgeRingSelect", "testCubeEdgeRingSelect", "expectedCubeEdgeRingSelect", # [OperatorSpecEditMode("edgering_select", {}, "EDGE", {5, 20, 25, 26})]), - # MeshTest("EmptyMeshEdgeRingSelect", "testGridEdgeRingSelect", "expectedGridEdgeRingSelect", + # SpecMeshTest("EmptyMeshEdgeRingSelect", "testGridEdgeRingSelect", "expectedGridEdgeRingSelect", # [OperatorSpecEditMode("edgering_select", {}, "VERT", {65, 66, 67})]), - # MeshTest("EmptyMeshEdgeRingSelect", "testEmptyMeshdgeRingSelect", "expectedEmptyMeshEdgeRingSelect", + # SpecMeshTest("EmptyMeshEdgeRingSelect", "testEmptyMeshdgeRingSelect", "expectedEmptyMeshEdgeRingSelect", # [OperatorSpecEditMode("edgering_select", {}, "VERT", {})]), # edges select sharp - MeshTest("CubeEdgesSelectSharp", "testCubeEdgeSelectSharp", "expectedCubeEdgeSelectSharp", + SpecMeshTest("CubeEdgesSelectSharp", "testCubeEdgeSelectSharp", "expectedCubeEdgeSelectSharp", [OperatorSpecEditMode("edges_select_sharp", {}, "EDGE", {20})]), - MeshTest("SphereEdgesSelectSharp", "testSphereEdgesSelectSharp", "expectedSphereEdgeSelectSharp", + SpecMeshTest("SphereEdgesSelectSharp", "testSphereEdgesSelectSharp", "expectedSphereEdgeSelectSharp", [OperatorSpecEditMode("edges_select_sharp", {"sharpness": 0.25}, "EDGE", {288})]), - MeshTest("HoledSphereEdgesSelectSharp", "testHoledSphereEdgesSelectSharp", "expectedHoledSphereEdgeSelectSharp", + SpecMeshTest("HoledSphereEdgesSelectSharp", "testHoledSphereEdgesSelectSharp", "expectedHoledSphereEdgeSelectSharp", [OperatorSpecEditMode("edges_select_sharp", {"sharpness": 0.18}, "VERT", {})]), - MeshTest("EmptyMeshEdgesSelectSharp", "testEmptyMeshEdgeSelectSharp", "expectedEmptyMeshEdgeSelectSharp", + SpecMeshTest("EmptyMeshEdgesSelectSharp", "testEmptyMeshEdgeSelectSharp", "expectedEmptyMeshEdgeSelectSharp", [OperatorSpecEditMode("edges_select_sharp", {}, "VERT", {})]), # face make planar - MeshTest("MonkeyFaceMakePlanar", "testMonkeyFaceMakePlanar", + SpecMeshTest("MonkeyFaceMakePlanar", "testMonkeyFaceMakePlanar", "expectedMonkeyFaceMakePlanar", [OperatorSpecEditMode("face_make_planar", {}, "FACE", {i for i in range(500)})]), # face split by edges - MeshTest("PlaneFaceSplitByEdges", "testPlaneFaceSplitByEdges", + SpecMeshTest("PlaneFaceSplitByEdges", "testPlaneFaceSplitByEdges", "expectedPlaneFaceSplitByEdges", [OperatorSpecEditMode("face_split_by_edges", {}, "VERT", {i for i in range(6)})]), # faces select linked flat - MeshTest("CubeFacesSelectLinkedFlat", "testCubeFaceSelectLinkedFlat", "expectedCubeFaceSelectLinkedFlat", + SpecMeshTest("CubeFacesSelectLinkedFlat", "testCubeFaceSelectLinkedFlat", "expectedCubeFaceSelectLinkedFlat", [OperatorSpecEditMode("faces_select_linked_flat", {}, "FACE", {7})]), - MeshTest("PlaneFacesSelectLinkedFlat", "testPlaneFaceSelectLinkedFlat", "expectedPlaneFaceSelectLinkedFlat", + SpecMeshTest("PlaneFacesSelectLinkedFlat", "testPlaneFaceSelectLinkedFlat", "expectedPlaneFaceSelectLinkedFlat", [OperatorSpecEditMode("faces_select_linked_flat", {}, "VERT", {1})]), - MeshTest("EmptyMeshFacesSelectLinkedFlat", "testEmptyMeshFaceSelectLinkedFlat", + SpecMeshTest("EmptyMeshFacesSelectLinkedFlat", "testEmptyMeshFaceSelectLinkedFlat", "expectedEmptyMeshFaceSelectLinkedFlat", [OperatorSpecEditMode("faces_select_linked_flat", {}, "VERT", {})]), # fill - MeshTest("IcosphereFill", "testIcosphereFill", "expectedIcosphereFill", + SpecMeshTest("IcosphereFill", "testIcosphereFill", "expectedIcosphereFill", [OperatorSpecEditMode("fill", {}, "EDGE", {20, 21, 22, 23, 24, 45, 46, 47, 48, 49})]), - MeshTest("IcosphereFillUseBeautyFalse", + SpecMeshTest("IcosphereFillUseBeautyFalse", "testIcosphereFillUseBeautyFalse", "expectedIcosphereFillUseBeautyFalse", [OperatorSpecEditMode("fill", {"use_beauty": False}, "EDGE", {20, 21, 22, 23, 24, 45, 46, 47, 48, 49})]), # fill grid - MeshTest("PlaneFillGrid", "testPlaneFillGrid", + SpecMeshTest("PlaneFillGrid", "testPlaneFillGrid", "expectedPlaneFillGrid", [OperatorSpecEditMode("fill_grid", {}, "EDGE", {1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15})]), - MeshTest("PlaneFillGridSimpleBlending", + SpecMeshTest("PlaneFillGridSimpleBlending", "testPlaneFillGridSimpleBlending", "expectedPlaneFillGridSimpleBlending", [OperatorSpecEditMode("fill_grid", {"use_interp_simple": True}, "EDGE", {1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 15})]), # fill holes - MeshTest("SphereFillHoles", "testSphereFillHoles", "expectedSphereFillHoles", + SpecMeshTest("SphereFillHoles", "testSphereFillHoles", "expectedSphereFillHoles", [OperatorSpecEditMode("fill_holes", {"sides": 9}, "VERT", {i for i in range(481)})]), # face shade smooth (not a real test) - MeshTest("CubeShadeSmooth", "testCubeShadeSmooth", "expectedCubeShadeSmooth", + SpecMeshTest("CubeShadeSmooth", "testCubeShadeSmooth", "expectedCubeShadeSmooth", [OperatorSpecEditMode("faces_shade_smooth", {}, "VERT", {i for i in range(8)})]), # faces shade flat (not a real test) - MeshTest("CubeShadeFlat", "testCubeShadeFlat", "expectedCubeShadeFlat", + SpecMeshTest("CubeShadeFlat", "testCubeShadeFlat", "expectedCubeShadeFlat", [OperatorSpecEditMode("faces_shade_flat", {}, "FACE", {i for i in range(6)})]), # inset faces - MeshTest("CubeInset", + SpecMeshTest("CubeInset", "testCubeInset", "expectedCubeInset", [OperatorSpecEditMode("inset", {"thickness": 0.2}, "VERT", {5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, 62, 65, 83, 91, 95})]), - MeshTest("CubeInsetEvenOffsetFalse", + SpecMeshTest("CubeInsetEvenOffsetFalse", "testCubeInsetEvenOffsetFalse", "expectedCubeInsetEvenOffsetFalse", [OperatorSpecEditMode("inset", {"thickness": 0.2, "use_even_offset": False}, "VERT", {5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, 62, 65, 83, 91, 95})]), - MeshTest("CubeInsetDepth", + SpecMeshTest("CubeInsetDepth", "testCubeInsetDepth", "expectedCubeInsetDepth", [OperatorSpecEditMode("inset", {"thickness": 0.2, "depth": 0.2}, "VERT", {5, 16, 17, 19, 20, 22, 23, 34, 47, 49, 50, 52, 59, 61, 62, 65, 83, 91, 95})]), - MeshTest("GridInsetRelativeOffset", "testGridInsetRelativeOffset", + SpecMeshTest("GridInsetRelativeOffset", "testGridInsetRelativeOffset", "expectedGridInsetRelativeOffset", [OperatorSpecEditMode("inset", {"thickness": 0.4, "use_relative_offset": True}, "FACE", {35, 36, 37, 45, 46, 47, 55, 56, 57})]), # loop multi select - MeshTest("MokeyLoopMultiSelect", "testMonkeyLoopMultiSelect", "expectedMonkeyLoopMultiSelect", + SpecMeshTest("MokeyLoopMultiSelect", "testMonkeyLoopMultiSelect", "expectedMonkeyLoopMultiSelect", [OperatorSpecEditMode("loop_multi_select", {}, "VERT", {355, 359, 73, 301, 302})]), - MeshTest("HoledGridLoopMultiSelect", "testGridLoopMultiSelect", "expectedGridLoopMultiSelect", + SpecMeshTest("HoledGridLoopMultiSelect", "testGridLoopMultiSelect", "expectedGridLoopMultiSelect", [OperatorSpecEditMode("loop_multi_select", {}, "VERT", {257, 169, 202, 207, 274, 278, 63})]), - MeshTest("EmptyMeshLoopMultiSelect", "testEmptyMeshLoopMultiSelect", "expectedEmptyMeshLoopMultiSelect", + SpecMeshTest("EmptyMeshLoopMultiSelect", "testEmptyMeshLoopMultiSelect", "expectedEmptyMeshLoopMultiSelect", [OperatorSpecEditMode("loop_multi_select", {}, "VERT", {})]), # mark seam - MeshTest("CubeMarkSeam", "testCubeMarkSeam", "expectedCubeMarkSeam", + SpecMeshTest("CubeMarkSeam", "testCubeMarkSeam", "expectedCubeMarkSeam", [OperatorSpecEditMode("mark_seam", {}, "EDGE", {1})]), # select all - MeshTest("CircleSelectAll", "testCircleSelectAll", "expectedCircleSelectAll", + SpecMeshTest("CircleSelectAll", "testCircleSelectAll", "expectedCircleSelectAll", [OperatorSpecEditMode("select_all", {}, "VERT", {1})]), - MeshTest("IsolatedVertsSelectAll", "testIsolatedVertsSelectAll", "expectedIsolatedVertsSelectAll", + SpecMeshTest("IsolatedVertsSelectAll", "testIsolatedVertsSelectAll", "expectedIsolatedVertsSelectAll", [OperatorSpecEditMode("select_all", {}, "VERT", {})]), - MeshTest("EmptyMeshSelectAll", "testEmptyMeshSelectAll", "expectedEmptyMeshSelectAll", + SpecMeshTest("EmptyMeshSelectAll", "testEmptyMeshSelectAll", "expectedEmptyMeshSelectAll", [OperatorSpecEditMode("select_all", {}, "VERT", {})]), # select axis - Cannot be tested. Needs active vert selection - # MeshTest("MonkeySelectAxisX", "testMonkeySelectAxisX", "expectedMonkeySelectAxisX", + # SpecMeshTest("MonkeySelectAxisX", "testMonkeySelectAxisX", "expectedMonkeySelectAxisX", # [OperatorSpecEditMode("select_axis", {"axis": "X"}, "VERT", {13})]), - # MeshTest("MonkeySelectAxisY", "testMonkeySelectAxisY", "expectedMonkeySelectAxisY", + # SpecMeshTest("MonkeySelectAxisY", "testMonkeySelectAxisY", "expectedMonkeySelectAxisY", # [OperatorSpecEditMode("select_axis", {"axis": "Y", "sign": "NEG"}, "FACE", {317})]), - # MeshTest("MonkeySelectAxisXYZ", "testMonkeySelectAxisXYZ", "expectedMonkeySelectAxisXYZ", + # SpecMeshTest("MonkeySelectAxisXYZ", "testMonkeySelectAxisXYZ", "expectedMonkeySelectAxisXYZ", # [OperatorSpecEditMode("select_axis", {"axis": "X", "sign": "NEG"}, "FACE", {317}), # OperatorSpecEditMode("select_axis", {"axis": "Y", "sign": "POS"}, "FACE", {}), # OperatorSpecEditMode("select_axis", {"axis": "Z", "sign": "NEG"}, "FACE", {})]), # select faces by sides - MeshTest("CubeSelectFacesBySide", "testCubeSelectFacesBySide", "expectedCubeSelectFacesBySide", + SpecMeshTest("CubeSelectFacesBySide", "testCubeSelectFacesBySide", "expectedCubeSelectFacesBySide", [OperatorSpecEditMode("select_face_by_sides", {"number": 4}, "FACE", {})]), - MeshTest("CubeSelectFacesBySideGreater", "testCubeSelectFacesBySideGreater", "expectedCubeSelectFacesBySideGreater", + SpecMeshTest("CubeSelectFacesBySideGreater", "testCubeSelectFacesBySideGreater", "expectedCubeSelectFacesBySideGreater", [OperatorSpecEditMode("select_face_by_sides", {"number": 4, "type": "GREATER", "extend": True}, "FACE", {})]), - MeshTest("CubeSelectFacesBySideLess", "testCubeSelectFacesBySideLess", "expectedCubeSelectFacesBySideLess", + SpecMeshTest("CubeSelectFacesBySideLess", "testCubeSelectFacesBySideLess", "expectedCubeSelectFacesBySideLess", [OperatorSpecEditMode("select_face_by_sides", {"number": 4, "type": "GREATER", "extend": True}, "FACE", {})]), # select interior faces - MeshTest("CubeSelectInteriorFaces", "testCubeSelectInteriorFaces", "expectedCubeSelectInteriorFaces", + SpecMeshTest("CubeSelectInteriorFaces", "testCubeSelectInteriorFaces", "expectedCubeSelectInteriorFaces", [OperatorSpecEditMode("select_face_by_sides", {"number": 4}, "FACE", {})]), - MeshTest("HoledCubeSelectInteriorFaces", "testHoledCubeSelectInteriorFaces", "expectedHoledCubeSelectInteriorFaces", + SpecMeshTest("HoledCubeSelectInteriorFaces", "testHoledCubeSelectInteriorFaces", "expectedHoledCubeSelectInteriorFaces", [OperatorSpecEditMode("select_face_by_sides", {"number": 4}, "FACE", {})]), - MeshTest("EmptyMeshSelectInteriorFaces", "testEmptyMeshSelectInteriorFaces", "expectedEmptyMeshSelectInteriorFaces", + SpecMeshTest("EmptyMeshSelectInteriorFaces", "testEmptyMeshSelectInteriorFaces", "expectedEmptyMeshSelectInteriorFaces", [OperatorSpecEditMode("select_face_by_sides", {"number": 4}, "FACE", {})]), # select less - MeshTest("MonkeySelectLess", "testMonkeySelectLess", "expectedMonkeySelectLess", + SpecMeshTest("MonkeySelectLess", "testMonkeySelectLess", "expectedMonkeySelectLess", [OperatorSpecEditMode("select_less", {}, "VERT", {2, 8, 24, 34, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 68, 69, 70, 71, 74, 75, 78, 80, 81, 82, 83, 90, 91, 93, 95, 97, 99, 101, 109, 111, 115, 117, 119, 121, 123, 125, 127, 129, 130, 131, @@ -295,37 +295,37 @@ def main(): 462, 463, 464, 471, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 495, 496, 497, 498, 499, 502, 505})]), - MeshTest("HoledCubeSelectLess", "testHoledCubeSelectLess", "expectedHoledCubeSelectLess", + SpecMeshTest("HoledCubeSelectLess", "testHoledCubeSelectLess", "expectedHoledCubeSelectLess", [OperatorSpecEditMode("select_face_by_sides", {}, "FACE", {})]), - MeshTest("EmptyMeshSelectLess", "testEmptyMeshSelectLess", "expectedEmptyMeshSelectLess", + SpecMeshTest("EmptyMeshSelectLess", "testEmptyMeshSelectLess", "expectedEmptyMeshSelectLess", [OperatorSpecEditMode("select_face_by_sides", {}, "VERT", {})]), # select linked - MeshTest("PlanesSelectLinked", "testPlanesSelectLinked", "expectedPlanesSelectedLinked", + SpecMeshTest("PlanesSelectLinked", "testPlanesSelectLinked", "expectedPlanesSelectedLinked", [OperatorSpecEditMode("select_linked", {}, "VERT", {7})]), - MeshTest("CubesSelectLinked", "testCubesSelectLinked", "expectedCubesSelectLinked", + SpecMeshTest("CubesSelectLinked", "testCubesSelectLinked", "expectedCubesSelectLinked", [OperatorSpecEditMode("select_linked", {}, "VERT", {11})]), - MeshTest("EmptyMeshSelectLinked", "testEmptyMeshSelectLinked", "expectedEmptyMeshSelectLinked", + SpecMeshTest("EmptyMeshSelectLinked", "testEmptyMeshSelectLinked", "expectedEmptyMeshSelectLinked", [OperatorSpecEditMode("select_linked", {}, "VERT", {})]), # select nth (checkered deselect) - MeshTest("CircleSelect2nd", "testCircleSelect2nd", "expectedCircleSelect2nd", + SpecMeshTest("CircleSelect2nd", "testCircleSelect2nd", "expectedCircleSelect2nd", [OperatorSpecEditMode("select_nth", {}, "VERT", {i for i in range(32)})]), # unsubdivide # normal case - MeshTest("CubeFaceUnsubdivide", "testCubeUnsubdivide", "expectedCubeUnsubdivide", + SpecMeshTest("CubeFaceUnsubdivide", "testCubeUnsubdivide", "expectedCubeUnsubdivide", [OperatorSpecEditMode("unsubdivide", {}, "FACE", {i for i in range(6)})]), # T87259 - test cases - MeshTest("CubeEdgeUnsubdivide", "testCubeEdgeUnsubdivide", "expectedCubeEdgeUnsubdivide", + SpecMeshTest("CubeEdgeUnsubdivide", "testCubeEdgeUnsubdivide", "expectedCubeEdgeUnsubdivide", [OperatorSpecEditMode("unsubdivide", {}, "EDGE", {i for i in range(6)})]), - MeshTest("UVSphereUnsubdivide", "testUVSphereUnsubdivide", "expectedUVSphereUnsubdivide", - [OperatorSpecEditMode("unsubdivide", {'iterations': 9}, "FACE", {i for i in range(512)})]), + SpecMeshTest("UVSphereUnsubdivide", "testUVSphereUnsubdivide", "expectedUVSphereUnsubdivide", + [OperatorSpecEditMode("unsubdivide", {'iterations': 9}, "FACE", {i for i in range(512)})]), # vert connect path # Tip: It works only if there is an already existing face or more than 2 vertices. - MeshTest("CubeVertConnectPath", "testCubeVertConnectPath", "expectedCubeVertConnectPath", + SpecMeshTest("CubeVertConnectPath", "testCubeVertConnectPath", "expectedCubeVertConnectPath", [OperatorSpecEditMode("vert_connect_path", {}, "VERT", {0, 5})]), ] diff --git a/tests/python/physics_cloth.py b/tests/python/physics_cloth.py index b88b4d63f9d..83f1366037c 100644 --- a/tests/python/physics_cloth.py +++ b/tests/python/physics_cloth.py @@ -24,25 +24,25 @@ import sys import bpy sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import RunTest, ModifierSpec, MeshTest +from modules.mesh_test import RunTest, ModifierSpec, SpecMeshTest def main(): test = [ - MeshTest("ClothSimple", "testClothPlane", "expectedClothPlane", + SpecMeshTest("ClothSimple", "testClothPlane", "expectedClothPlane", [ModifierSpec('Cloth', 'CLOTH', {'settings': {'quality': 5}}, 15)], threshold=1e-3), # Not reproducible - # MeshTest("ClothPressure", "testObjClothPressure", "expObjClothPressure", + # SpecMeshTest("ClothPressure", "testObjClothPressure", "expObjClothPressure", # [ModifierSpec('Cloth2', 'CLOTH', {'settings': {'use_pressure': True, # 'uniform_pressure_force': 1}}, 16)]), # Not reproducible - # MeshTest("ClothSelfCollision", "testClothCollision", "expClothCollision", + # SpecMeshTest("ClothSelfCollision", "testClothCollision", "expClothCollision", # [ModifierSpec('Cloth', 'CLOTH', {'collision_settings': {'use_self_collision': True}}, 67)]), - MeshTest("ClothSpring", "testTorusClothSpring", "expTorusClothSpring", + SpecMeshTest("ClothSpring", "testTorusClothSpring", "expTorusClothSpring", [ModifierSpec('Cloth2', 'CLOTH', {'settings': {'use_internal_springs': True}}, 10)], threshold=1e-3), ] diff --git a/tests/python/physics_dynamic_paint.py b/tests/python/physics_dynamic_paint.py index b5d09c8cb3a..d1f31b2718b 100644 --- a/tests/python/physics_dynamic_paint.py +++ b/tests/python/physics_dynamic_paint.py @@ -24,13 +24,13 @@ import sys import bpy sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import RunTest, ModifierSpec, MeshTest +from modules.mesh_test import RunTest, ModifierSpec, SpecMeshTest def main(): test = [ - MeshTest("DynamicPaintSimple", "testObjDynamicPaintPlane", "expObjDynamicPaintPlane", + SpecMeshTest("DynamicPaintSimple", "testObjDynamicPaintPlane", "expObjDynamicPaintPlane", [ModifierSpec('dynamic_paint', 'DYNAMIC_PAINT', {'ui_type': 'CANVAS', 'canvas_settings': {'canvas_surfaces': {'surface_type': 'WAVE', 'frame_end': 15}}}, diff --git a/tests/python/physics_ocean.py b/tests/python/physics_ocean.py index 40227d3d8d7..59eb71aa9f1 100644 --- a/tests/python/physics_ocean.py +++ b/tests/python/physics_ocean.py @@ -24,13 +24,13 @@ import sys import bpy sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import RunTest, ModifierSpec, MeshTest +from modules.mesh_test import RunTest, ModifierSpec, SpecMeshTest def main(): test = [ # World coordinates of test and expected object should be same. - MeshTest("PlaneOcean", "testObjPlaneOcean", "expObjPlaneOcean", + SpecMeshTest("PlaneOcean", "testObjPlaneOcean", "expObjPlaneOcean", [ModifierSpec('Ocean', 'OCEAN', {})]), ] ocean_test = RunTest(test) diff --git a/tests/python/physics_particle_instance.py b/tests/python/physics_particle_instance.py index e12e357e4ce..ce0bdffcd8b 100644 --- a/tests/python/physics_particle_instance.py +++ b/tests/python/physics_particle_instance.py @@ -24,13 +24,13 @@ import sys import bpy sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import RunTest, ModifierSpec, MeshTest +from modules.mesh_test import RunTest, ModifierSpec, SpecMeshTest def main(): test = [ - MeshTest("ParticleInstanceSimple", "testParticleInstance", "expectedParticleInstance", + SpecMeshTest("ParticleInstanceSimple", "testParticleInstance", "expectedParticleInstance", [ModifierSpec('ParticleInstance', 'PARTICLE_INSTANCE', {'object': bpy.data.objects['Cube']})], threshold=1e-3), diff --git a/tests/python/physics_particle_system.py b/tests/python/physics_particle_system.py index 0adc5ab1c54..4f54b87e8b0 100644 --- a/tests/python/physics_particle_system.py +++ b/tests/python/physics_particle_system.py @@ -20,16 +20,15 @@ import os import sys - import bpy sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import RunTest, ParticleSystemSpec, MeshTest +from modules.mesh_test import RunTest, ParticleSystemSpec, SpecMeshTest def main(): test = [ - MeshTest("ParticleSystemTest", "testParticleSystem", "expParticleSystem", + SpecMeshTest("ParticleSystemTest", "testParticleSystem", "expParticleSystem", [ParticleSystemSpec('Particles', 'PARTICLE_SYSTEM', {'render_type': "OBJECT", 'instance_object': bpy.data.objects['Cube']}, 20)], threshold=1e-3), diff --git a/tests/python/physics_softbody.py b/tests/python/physics_softbody.py index 985b3a29bb4..6d4d4cc08d5 100644 --- a/tests/python/physics_softbody.py +++ b/tests/python/physics_softbody.py @@ -24,13 +24,13 @@ import sys import bpy sys.path.append(os.path.dirname(os.path.realpath(__file__))) -from modules.mesh_test import RunTest, ModifierSpec, MeshTest +from modules.mesh_test import RunTest, ModifierSpec, SpecMeshTest def main(): test = [ - MeshTest("SoftBodySimple", "testSoftBody", "expectedSoftBody", + SpecMeshTest("SoftBodySimple", "testSoftBody", "expectedSoftBody", [ModifierSpec('Softbody', 'SOFT_BODY', {'settings': {'use_goal': False, 'bend': 8, 'pull': 0.8, 'push': 0.8}}, 45)]), |