diff options
author | Jacques Lucke <jacques@blender.org> | 2020-05-04 13:29:39 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2020-05-04 13:29:39 +0300 |
commit | dc1c562f632e1370702e85df0711929f3f77d7ea (patch) | |
tree | c99ea932ed0c7ac04ecce774377803e7a5dc2fac | |
parent | 10fad7c88db642277be784ba77941faa7abc5d9a (diff) | |
parent | 9adb81f58494731438f3afdcbacb1599a4a32bb9 (diff) |
Merge branch 'master' into simulation-access-modifier
479 files changed, 16980 insertions, 12016 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f0ff3d1427..19f25e3c108 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,11 +138,6 @@ get_blender_version() #----------------------------------------------------------------------------- # Options -# First platform specific non-cached vars -if(UNIX AND NOT (APPLE OR HAIKU)) - set(WITH_X11 ON) -endif() - # Blender internal features option(WITH_BLENDER "Build blender (disable to build only the blender player)" ON) mark_as_advanced(WITH_BLENDER) @@ -207,7 +202,15 @@ mark_as_advanced(WITH_GHOST_DEBUG) option(WITH_GHOST_SDL "Enable building Blender against SDL for windowing rather than the native APIs" OFF) mark_as_advanced(WITH_GHOST_SDL) -if(WITH_X11) +if(UNIX AND NOT (APPLE OR HAIKU)) + option(WITH_GHOST_X11 "Enable building Blender against X11 for windowing" ON) + mark_as_advanced(WITH_GHOST_X11) + + option(WITH_GHOST_WAYLAND "Enable building Blender against Wayland for windowing (under development)" OFF) + mark_as_advanced(WITH_GHOST_WAYLAND) +endif() + +if(WITH_GHOST_X11) option(WITH_GHOST_XDND "Enable drag'n'drop support on X11 using XDND protocol" ON) endif() @@ -232,7 +235,7 @@ if(UNIX AND NOT APPLE) mark_as_advanced(WITH_OPENMP_STATIC) endif() -if(WITH_X11) +if(WITH_GHOST_X11) option(WITH_X11_XINPUT "Enable X11 Xinput (tablet support and unicode input)" ON) option(WITH_X11_XF86VMODE "Enable X11 video mode switching" ON) option(WITH_X11_XFIXES "Enable X11 XWayland cursor warping workaround" ON) @@ -543,6 +546,12 @@ if(WIN32) option(WITH_WINDOWS_SCCACHE "Use sccache to speed up builds (Ninja builder only)" OFF) mark_as_advanced(WITH_WINDOWS_SCCACHE) + option(WITH_WINDOWS_PDB "Generate a pdb file for client side stacktraces" ON) + mark_as_advanced(WITH_WINDOWS_PDB) + + option(WITH_WINDOWS_STRIPPED_PDB "Use a stripped PDB file" On) + mark_as_advanced(WITH_WINDOWS_STRIPPED_PDB) + endif() # The following only works with the Ninja generator in CMake >= 3.0. @@ -689,7 +698,8 @@ if(WITH_INSTALL_PORTABLE) endif() if(WITH_GHOST_SDL OR WITH_HEADLESS) - set(WITH_X11 OFF) + set(WITH_GHOST_WAYLAND OFF) + set(WITH_GHOST_X11 OFF) set(WITH_X11_XINPUT OFF) set(WITH_X11_XF86VMODE OFF) set(WITH_X11_XFIXES OFF) diff --git a/build_files/build_environment/cmake/bzip2.cmake b/build_files/build_environment/cmake/bzip2.cmake index fbf5551e719..bb2be7c634a 100644 --- a/build_files/build_environment/cmake/bzip2.cmake +++ b/build_files/build_environment/cmake/bzip2.cmake @@ -25,6 +25,8 @@ if(UNIX AND NOT APPLE) set(BZIP2_CFLAGS "-fPIC -Wall -Winline -O2 -g -D_FILE_OFFSET_BITS=64") set(BZIP2_CONFIGURE_ENV ${BZIP2_CONFIGURE_ENV} && export LDFLAGS=${BZIP2_LDFLAGS} && export CFLAGS=${BZIP2_CFLAGS} && export PREFIX=${BZIP2_PREFIX}) +else() + set(BZIP2_CONFIGURE_ENV ${CONFIGURE_ENV}) endif() ExternalProject_Add(external_bzip2 diff --git a/build_files/build_environment/cmake/openimagedenoise.cmake b/build_files/build_environment/cmake/openimagedenoise.cmake index b20bb838ede..1332a38fea6 100644 --- a/build_files/build_environment/cmake/openimagedenoise.cmake +++ b/build_files/build_environment/cmake/openimagedenoise.cmake @@ -21,7 +21,7 @@ set(OIDN_EXTRA_ARGS -DWITH_EXAMPLE=OFF -DWITH_TEST=OFF -DTBB_ROOT=${LIBDIR}/tbb - -DTBB_STATIC_LIB=ON + -DTBB_STATIC_LIB=${TBB_STATIC_LIBRARY} -DOIDN_STATIC_LIB=ON ) diff --git a/build_files/build_environment/cmake/opensubdiv.cmake b/build_files/build_environment/cmake/opensubdiv.cmake index 5c1ebe46a20..9ec849a219b 100644 --- a/build_files/build_environment/cmake/opensubdiv.cmake +++ b/build_files/build_environment/cmake/opensubdiv.cmake @@ -36,7 +36,7 @@ if(WIN32) set(OPENSUBDIV_EXTRA_ARGS ${OPENSUBDIV_EXTRA_ARGS} -DTBB_INCLUDE_DIR=${LIBDIR}/tbb/include - -DTBB_LIBRARIES=${LIBDIR}/tbb/lib/tbb_static.lib + -DTBB_LIBRARIES=${LIBDIR}/tbb/lib/tbb.lib -DCLEW_INCLUDE_DIR=${LIBDIR}/clew/include/CL -DCLEW_LIBRARY=${LIBDIR}/clew/lib/clew${LIBEXT} -DCUEW_INCLUDE_DIR=${LIBDIR}/cuew/include diff --git a/build_files/build_environment/cmake/tbb.cmake b/build_files/build_environment/cmake/tbb.cmake index 8bd2f3160d0..1cb852fb5d1 100644 --- a/build_files/build_environment/cmake/tbb.cmake +++ b/build_files/build_environment/cmake/tbb.cmake @@ -20,8 +20,10 @@ if(WIN32) -DTBB_BUILD_SHARED=On -DTBB_BUILD_TBBMALLOC=On -DTBB_BUILD_TBBMALLOC_PROXY=On - -DTBB_BUILD_STATIC=On -) + -DTBB_BUILD_STATIC=Off + ) + set(TBB_LIBRARY tbb) + set(TBB_STATIC_LIBRARY Off) else() set(TBB_EXTRA_ARGS -DTBB_BUILD_SHARED=Off @@ -29,6 +31,8 @@ else() -DTBB_BUILD_TBBMALLOC_PROXY=Off -DTBB_BUILD_STATIC=On ) + set(TBB_LIBRARY tbb_static) + set(TBB_STATIC_LIBRARY On) endif() # CMake script for TBB from https://github.com/wjakob/tbb/blob/master/CMakeLists.txt @@ -46,7 +50,8 @@ ExternalProject_Add(external_tbb if(WIN32) if(BUILD_MODE STREQUAL Release) ExternalProject_Add_Step(external_tbb after_install - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_static.lib ${HARVEST_TARGET}/tbb/lib/tbb.lib + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.lib ${HARVEST_TARGET}/tbb/lib/tbb.lib + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb.dll ${HARVEST_TARGET}/tbb/lib/tbb.dll COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc.lib COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.dll ${HARVEST_TARGET}/tbb/lib/tbbmalloc.dll COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_proxy.lib @@ -57,7 +62,12 @@ if(WIN32) endif() if(BUILD_MODE STREQUAL Debug) ExternalProject_Add_Step(external_tbb after_install - COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_static.lib ${HARVEST_TARGET}/tbb/lib/tbb_debug.lib + # findtbb.cmake in some deps *NEEDS* to find tbb.lib even if they are not going to use it + # to make that test pass, we place a copy with the right name in the lib folder. + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_debug.lib ${LIBDIR}/tbb/lib/tbb.lib + # Normal collection of build artifacts + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_debug.lib ${HARVEST_TARGET}/tbb/lib/debug/tbb_debug.lib + COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbb_debug.dll ${HARVEST_TARGET}/tbb/lib/debug/tbb_debug.dll COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy.lib ${HARVEST_TARGET}/tbb/lib/tbbmalloc_proxy_debug.lib COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc.dll ${HARVEST_TARGET}/tbb/lib/debug/tbbmalloc.dll COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/tbb/lib/tbbmalloc_proxy.dll ${HARVEST_TARGET}/tbb/lib/debug/tbbmalloc_proxy.dll diff --git a/build_files/build_environment/cmake/usd.cmake b/build_files/build_environment/cmake/usd.cmake index c3594390f80..505607535e9 100644 --- a/build_files/build_environment/cmake/usd.cmake +++ b/build_files/build_environment/cmake/usd.cmake @@ -23,8 +23,8 @@ set(USD_EXTRA_ARGS -DBoost_USE_STATIC_RUNTIME=OFF -DBOOST_ROOT=${LIBDIR}/boost -DTBB_INCLUDE_DIRS=${LIBDIR}/tbb/include - -DTBB_LIBRARIES=${LIBDIR}/tbb/lib/${LIBPREFIX}tbb_static${LIBEXT} - -DTbb_TBB_LIBRARY=${LIBDIR}/tbb/lib/${LIBPREFIX}tbb_static${LIBEXT} + -DTBB_LIBRARIES=${LIBDIR}/tbb/lib/${LIBPREFIX}${TBB_LIBRARY}${LIBEXT} + -DTbb_TBB_LIBRARY=${LIBDIR}/tbb/lib/${LIBPREFIX}${TBB_LIBRARY}${LIBEXT} # This is a preventative measure that avoids possible conflicts when add-ons # try to load another USD library into the same process space. diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index e8f49c0c75c..6a247e81148 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -2654,7 +2654,7 @@ compile_USD() { cd $CWD INFO "Done compiling USD-$USD_VERSION!" - _is_building=true + _is_building=false else INFO "Own USD-$USD_VERSION is up to date, nothing to do!" INFO "If you want to force rebuild of this lib, use the --force-usd option." @@ -5354,6 +5354,11 @@ print_info() { _1="-D WITH_USD=ON" PRINT " $_1" _buildargs="$_buildargs $_1" + if [ -d $INST/usd ]; then + _1="-D USD_ROOT_DIR=$INST/usd" + PRINT " $_1" + _buildargs="$_buildargs $_1" + fi fi if [ "$NO_SYSTEM_GLEW" = true ]; then diff --git a/build_files/build_environment/patches/cmakelists_tbb.txt b/build_files/build_environment/patches/cmakelists_tbb.txt index da9fd938943..7edf3aa2785 100644 --- a/build_files/build_environment/patches/cmakelists_tbb.txt +++ b/build_files/build_environment/patches/cmakelists_tbb.txt @@ -109,6 +109,9 @@ if (WIN32) MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/${ARCH_PREFIX}-tbbmalloc-export.def COMMENT "Preprocessing tbbmalloc.def" ) + list(APPEND tbb_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/tbb_resource.rc) + list(APPEND tbbmalloc_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/tbbmalloc.rc) + list(APPEND tbbmalloc_proxy_src ${CMAKE_CURRENT_SOURCE_DIR}/src/tbbmalloc/tbbmalloc.rc) else() add_custom_command(OUTPUT tbb.def COMMAND ${CMAKE_CXX_COMPILER} -xc++ -E ${CMAKE_CURRENT_SOURCE_DIR}/src/tbb/${ARCH_PREFIX}-tbb-export.def -I ${CMAKE_CURRENT_SOURCE_DIR}/include -o tbb.def @@ -145,8 +148,12 @@ if (TBB_BUILD_SHARED) set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "-Wl,-version-script,${CMAKE_CURRENT_BINARY_DIR}/tbb.def") elseif(WIN32) set_property(TARGET tbb APPEND PROPERTY LINK_FLAGS "/DEF:${CMAKE_CURRENT_BINARY_DIR}/tbb.def") + endif() install(TARGETS tbb DESTINATION lib) + if(WIN32) + set_target_properties(tbb PROPERTIES OUTPUT_NAME "tbb$<$<CONFIG:Debug>:_debug>") + endif() endif() if(CMAKE_COMPILER_IS_GNUCC) @@ -196,7 +203,7 @@ if(TBB_BUILD_TBBMALLOC_PROXY) add_library(tbbmalloc_proxy SHARED ${tbbmalloc_proxy_src}) set_property(TARGET tbbmalloc_proxy APPEND PROPERTY COMPILE_DEFINITIONS "__TBBMALLOC_BUILD=1") set_property(TARGET tbbmalloc_proxy APPEND_STRING PROPERTY COMPILE_FLAGS ${DISABLE_RTTI}) - link_libraries(tbbmalloc_proxy tbbmalloc) + target_link_libraries(tbbmalloc_proxy tbbmalloc) install(TARGETS tbbmalloc_proxy DESTINATION lib) endif() endif() diff --git a/build_files/build_environment/patches/openimagedenoise.diff b/build_files/build_environment/patches/openimagedenoise.diff index f83b0776c30..7bfc3aa2eba 100644 --- a/build_files/build_environment/patches/openimagedenoise.diff +++ b/build_files/build_environment/patches/openimagedenoise.diff @@ -18,17 +18,6 @@ diff --git a/mkl-dnn/cmake/TBB.cmake b/mkl-dnn/cmake/TBB.cmake index 0711e699..c14210b6 100644 --- a/mkl-dnn/cmake/TBB.cmake +++ b/mkl-dnn/cmake/TBB.cmake -@@ -90,8 +90,8 @@ if(WIN32) - NO_DEFAULT_PATH - ) - set(TBB_LIB_DIR ${TBB_ROOT}/lib/${TBB_ARCH}/${TBB_VCVER}) -- find_library(TBB_LIBRARY tbb PATHS ${TBB_LIB_DIR} ${TBB_ROOT}/lib NO_DEFAULT_PATH) -- find_library(TBB_LIBRARY_MALLOC tbbmalloc PATHS ${TBB_LIB_DIR} ${TBB_ROOT}/lib NO_DEFAULT_PATH) -+ find_library(TBB_LIBRARY tbb_static PATHS ${TBB_LIB_DIR} ${TBB_ROOT}/lib NO_DEFAULT_PATH) -+ find_library(TBB_LIBRARY_MALLOC tbbmalloc_static PATHS ${TBB_LIB_DIR} ${TBB_ROOT}/lib NO_DEFAULT_PATH) - endif() - - else() @@ -138,13 +138,13 @@ else() set(TBB_LIBRARY_MALLOC TBB_LIBRARY_MALLOC-NOTFOUND) if(APPLE) diff --git a/build_files/cmake/Modules/GTestTesting.cmake b/build_files/cmake/Modules/GTestTesting.cmake index 04e1670aef6..c36b264a300 100644 --- a/build_files/cmake/Modules/GTestTesting.cmake +++ b/build_files/cmake/Modules/GTestTesting.cmake @@ -66,6 +66,9 @@ macro(BLENDER_SRC_GTEST_EX) if(UNIX AND NOT APPLE) target_link_libraries(${TARGET_NAME} bf_intern_libc_compat) endif() + if(WITH_TBB) + target_link_libraries(${TARGET_NAME} ${TBB_LIBRARIES}) + endif() get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(GENERATOR_IS_MULTI_CONFIG) diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index 6287da55580..d99e46ce76e 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -440,6 +440,14 @@ function(SETUP_LIBDIRS) link_directories(${HDF5_LIBPATH}) endif() + if(WITH_GHOST_WAYLAND) + link_directories( + ${wayland-client_LIBRARY_DIRS} + ${wayland-egl_LIBRARY_DIRS} + ${xkbcommon_LIBRARY_DIRS} + ${wayland-cursor_LIBRARY_DIRS}) + endif() + if(WIN32 AND NOT UNIX) link_directories(${PTHREADS_LIBPATH}) endif() diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index 8a89ce40432..3cfe8309bf1 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -504,7 +504,27 @@ if(WITH_SYSTEM_AUDASPACE) endif() endif() -if(WITH_X11) +if(WITH_GHOST_WAYLAND) + find_package(PkgConfig) + pkg_check_modules(wayland-client REQUIRED wayland-client>=1.12) + pkg_check_modules(wayland-egl REQUIRED wayland-egl) + pkg_check_modules(wayland-scanner REQUIRED wayland-scanner) + pkg_check_modules(xkbcommon REQUIRED xkbcommon) + pkg_check_modules(wayland-cursor REQUIRED wayland-cursor) + + set(WITH_GL_EGL ON) + + if(WITH_GHOST_WAYLAND) + list(APPEND PLATFORM_LINKLIBS + ${wayland-client_LIBRARIES} + ${wayland-egl_LIBRARIES} + ${xkbcommon_LIBRARIES} + ${wayland-cursor_LIBRARIES} + ) + endif() +endif() + +if(WITH_GHOST_X11) find_package(X11 REQUIRED) find_path(X11_XF86keysym_INCLUDE_PATH X11/XF86keysym.h ${X11_INC_SEARCH_PATH}) diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index f882ff6238b..5076057e0f6 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -51,6 +51,10 @@ if(CMAKE_C_COMPILER_ID MATCHES "Clang") endif() set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} \"${CLANG_OPENMP_LIB}\"") endif() + if(WITH_WINDOWS_STRIPPED_PDB) + message(WARNING "stripped pdb not supported with clang, disabling..") + set(WITH_WINDOWS_STRIPPED_PDB Off) + endif() endif() set_property(GLOBAL PROPERTY USE_FOLDERS ${WINDOWS_USE_VISUAL_STUDIO_PROJECT_FOLDERS}) @@ -112,7 +116,7 @@ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH:NO") list(APPEND PLATFORM_LINKLIBS - ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 Comctl32 + ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 Comctl32 version advapi32 shfolder shell32 ole32 oleaut32 uuid psapi Dbghelp Shlwapi ) @@ -136,6 +140,11 @@ add_definitions(-D_WIN32_WINNT=0x601) include(build_files/cmake/platform/platform_win32_bundle_crt.cmake) remove_cc_flag("/MDd" "/MD" "/Zi") +if(WITH_WINDOWS_PDB) + set(PDB_INFO_OVERRIDE_FLAGS "/Z7") + set(PDB_INFO_OVERRIDE_LINKER_FLAGS "/DEBUG /OPT:REF /OPT:ICF /INCREMENTAL:NO") +endif() + if(MSVC_CLANG) # Clangs version of cl doesn't support all flags set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_WARN_FLAGS} /nologo /J /Gd /EHsc -Wno-unused-command-line-argument -Wno-microsoft-enum-forward-reference ") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /nologo /J /Gd -Wno-unused-command-line-argument -Wno-microsoft-enum-forward-reference") @@ -168,12 +177,12 @@ endif() set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd ${SYMBOL_FORMAT}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MDd ${SYMBOL_FORMAT}") -set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD") -set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MD") -set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} /MD") -set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} /MD") -set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /MD") -set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} /MD") +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD ${PDB_INFO_OVERRIDE_FLAGS}") +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MD ${PDB_INFO_OVERRIDE_FLAGS}") +set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} /MD ${PDB_INFO_OVERRIDE_FLAGS}") +set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} /MD ${PDB_INFO_OVERRIDE_FLAGS}") +set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /MD ${SYMBOL_FORMAT}") +set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} /MD ${SYMBOL_FORMAT}") unset(SYMBOL_FORMAT) # JMC is available on msvc 15.8 (1915) and up if(MSVC_VERSION GREATER 1914 AND NOT MSVC_CLANG) @@ -186,6 +195,7 @@ set(PLATFORM_LINKFLAGS_DEBUG "${PLATFORM_LINKFLAGS_DEBUG} /IGNORE:4099 /NODEFAUL # Ignore meaningless for us linker warnings. set(PLATFORM_LINKFLAGS "${PLATFORM_LINKFLAGS} /ignore:4049 /ignore:4217 /ignore:4221") +set(PLATFORM_LINKFLAGS_RELEASE "${PLATFORM_LINKFLAGS} ${PDB_INFO_OVERRIDE_LINKER_FLAGS}") set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221") if(CMAKE_CL_64) @@ -586,7 +596,7 @@ if(WITH_SYSTEM_AUDASPACE) endif() if(WITH_TBB) - set(TBB_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbb.lib debug ${LIBDIR}/tbb/lib/tbb_debug.lib) + set(TBB_LIBRARIES optimized ${LIBDIR}/tbb/lib/tbb.lib debug ${LIBDIR}/tbb/lib/debug/tbb_debug.lib) set(TBB_INCLUDE_DIR ${LIBDIR}/tbb/include) set(TBB_INCLUDE_DIRS ${TBB_INCLUDE_DIR}) if(WITH_TBB_MALLOC_PROXY) diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index e79aba0e988..235c2fa931a 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -72,7 +72,7 @@ if(WITH_CYCLES OR WITH_COMPOSITOR OR WITH_OPENSUBDIV) endif() endif() -if(WITH_X11 AND WITH_GHOST_XDND) +if(WITH_GHOST_X11 AND WITH_GHOST_XDND) add_subdirectory(xdnd) endif() diff --git a/extern/audaspace/bindings/C/AUD_Device.cpp b/extern/audaspace/bindings/C/AUD_Device.cpp index 441f228deac..d4643094bc2 100644 --- a/extern/audaspace/bindings/C/AUD_Device.cpp +++ b/extern/audaspace/bindings/C/AUD_Device.cpp @@ -290,14 +290,14 @@ AUD_API AUD_Device* AUD_Device_getCurrent() return new AUD_Device(device); } -AUD_API void AUD_seekSynchronizer(AUD_Handle* handle, float time) +AUD_API void AUD_seekSynchronizer(AUD_Handle* handle, double time) { auto synchronizer = DeviceManager::getDevice()->getSynchronizer(); if(synchronizer) synchronizer->seek(*reinterpret_cast<std::shared_ptr<IHandle>*>(handle), time); } -AUD_API float AUD_getSynchronizerPosition(AUD_Handle* handle) +AUD_API double AUD_getSynchronizerPosition(AUD_Handle* handle) { auto synchronizer = DeviceManager::getDevice()->getSynchronizer(); if(synchronizer) diff --git a/extern/audaspace/bindings/C/AUD_Device.h b/extern/audaspace/bindings/C/AUD_Device.h index 0dfa21f0660..05e004a17db 100644 --- a/extern/audaspace/bindings/C/AUD_Device.h +++ b/extern/audaspace/bindings/C/AUD_Device.h @@ -221,14 +221,14 @@ extern AUD_API AUD_Device* AUD_Device_getCurrent(); * \param handle Playback handle. * \param time Time in seconds to seek to. */ -extern AUD_API void AUD_seekSynchronizer(AUD_Handle* handle, float time); +extern AUD_API void AUD_seekSynchronizer(AUD_Handle* handle, double time); /** * Returns the current sound scene playback time. * \param handle Playback handle. * \return The playback time in seconds. */ -extern AUD_API float AUD_getSynchronizerPosition(AUD_Handle* handle); +extern AUD_API double AUD_getSynchronizerPosition(AUD_Handle* handle); /** * Starts the playback of jack transport if possible. diff --git a/extern/audaspace/bindings/C/AUD_DynamicMusic.cpp b/extern/audaspace/bindings/C/AUD_DynamicMusic.cpp index bb7a129dde3..62e626cdc6c 100644 --- a/extern/audaspace/bindings/C/AUD_DynamicMusic.cpp +++ b/extern/audaspace/bindings/C/AUD_DynamicMusic.cpp @@ -101,14 +101,14 @@ AUD_API int AUD_DynamicMusic_pause(AUD_DynamicMusic* player) return (*player)->pause(); } -AUD_API int AUD_DynamicMusic_seek(AUD_DynamicMusic* player, float position) +AUD_API int AUD_DynamicMusic_seek(AUD_DynamicMusic* player, double position) { assert(player); return (*player)->seek(position); } -AUD_API float AUD_DynamicMusic_getPosition(AUD_DynamicMusic* player) +AUD_API double AUD_DynamicMusic_getPosition(AUD_DynamicMusic* player) { assert(player); @@ -141,4 +141,4 @@ AUD_API int AUD_DynamicMusic_stop(AUD_DynamicMusic* player) assert(player); return (*player)->stop(); -}
\ No newline at end of file +} diff --git a/extern/audaspace/bindings/C/AUD_DynamicMusic.h b/extern/audaspace/bindings/C/AUD_DynamicMusic.h index c362479591e..b8b78e57b55 100644 --- a/extern/audaspace/bindings/C/AUD_DynamicMusic.h +++ b/extern/audaspace/bindings/C/AUD_DynamicMusic.h @@ -103,14 +103,14 @@ extern AUD_API int AUD_DynamicMusic_pause(AUD_DynamicMusic* player); * \param position The new position from which to play back, in seconds. * \return 0 if the seeking wasn't possible. */ -extern AUD_API int AUD_DynamicMusic_seek(AUD_DynamicMusic* player, float position); +extern AUD_API int AUD_DynamicMusic_seek(AUD_DynamicMusic* player, double position); /** * Retrieves the position of the current scene of a dynamic music player. * \param player The DynamicMusic object. * \return The position of the current playing scene. */ -extern AUD_API float AUD_DynamicMusic_getPosition(AUD_DynamicMusic* player); +extern AUD_API double AUD_DynamicMusic_getPosition(AUD_DynamicMusic* player); /** * Retrieves the volume of the current scene of a dynamic music player. @@ -142,4 +142,4 @@ extern AUD_API int AUD_DynamicMusic_stop(AUD_DynamicMusic* player); #ifdef __cplusplus } -#endif
\ No newline at end of file +#endif diff --git a/extern/audaspace/bindings/C/AUD_Handle.cpp b/extern/audaspace/bindings/C/AUD_Handle.cpp index 265c7bf08d2..88d46d635e3 100644 --- a/extern/audaspace/bindings/C/AUD_Handle.cpp +++ b/extern/audaspace/bindings/C/AUD_Handle.cpp @@ -259,13 +259,13 @@ AUD_API int AUD_Handle_setPitch(AUD_Handle* handle, float value) return (*handle)->setPitch(value); } -AUD_API float AUD_Handle_getPosition(AUD_Handle* handle) +AUD_API double AUD_Handle_getPosition(AUD_Handle* handle) { assert(handle); return (*handle)->getPosition(); } -AUD_API int AUD_Handle_setPosition(AUD_Handle* handle, float value) +AUD_API int AUD_Handle_setPosition(AUD_Handle* handle, double value) { assert(handle); return (*handle)->seek(value); diff --git a/extern/audaspace/bindings/C/AUD_Handle.h b/extern/audaspace/bindings/C/AUD_Handle.h index 27cbd251de5..2182346c451 100644 --- a/extern/audaspace/bindings/C/AUD_Handle.h +++ b/extern/audaspace/bindings/C/AUD_Handle.h @@ -211,14 +211,14 @@ extern AUD_API int AUD_Handle_setPitch(AUD_Handle* handle, float value); * param handle The handle to get the position from. * return The position of the handle. */ -extern AUD_API float AUD_Handle_getPosition(AUD_Handle* handle); +extern AUD_API double AUD_Handle_getPosition(AUD_Handle* handle); /** * Sets the position of a handle. * param handle The handle to set the position from. * param value The new position to set. */ -extern AUD_API int AUD_Handle_setPosition(AUD_Handle* handle, float value); +extern AUD_API int AUD_Handle_setPosition(AUD_Handle* handle, double value); /** * Retrieves the relative of a handle. diff --git a/extern/audaspace/bindings/C/AUD_Sequence.cpp b/extern/audaspace/bindings/C/AUD_Sequence.cpp index d278cb148a1..e3f88629657 100644 --- a/extern/audaspace/bindings/C/AUD_Sequence.cpp +++ b/extern/audaspace/bindings/C/AUD_Sequence.cpp @@ -41,7 +41,7 @@ AUD_API void AUD_Sequence_free(AUD_Sound* sequence) delete sequence; } -AUD_API AUD_SequenceEntry* AUD_Sequence_add(AUD_Sound* sequence, AUD_Sound* sound, float begin, float end, float skip) +AUD_API AUD_SequenceEntry* AUD_Sequence_add(AUD_Sound* sequence, AUD_Sound* sound, double begin, double end, double skip) { if(!sound) return new AUD_SequenceEntry(((Sequence *)sequence->get())->add(AUD_Sound(), begin, end, skip)); @@ -160,7 +160,7 @@ AUD_API void AUD_Sequence_setSpeedOfSound(AUD_Sound* sequence, float value) -AUD_API void AUD_SequenceEntry_move(AUD_SequenceEntry* entry, float begin, float end, float skip) +AUD_API void AUD_SequenceEntry_move(AUD_SequenceEntry* entry, double begin, double end, double skip) { (*entry)->move(begin, end, skip); } diff --git a/extern/audaspace/bindings/C/AUD_Sequence.h b/extern/audaspace/bindings/C/AUD_Sequence.h index 668960c7d50..bdf1a61a2de 100644 --- a/extern/audaspace/bindings/C/AUD_Sequence.h +++ b/extern/audaspace/bindings/C/AUD_Sequence.h @@ -55,7 +55,7 @@ extern AUD_API void AUD_Sequence_free(AUD_Sound* sequence); * \param skip How much seconds should be skipped at the beginning. * \return The entry added. */ -extern AUD_API AUD_SequenceEntry* AUD_Sequence_add(AUD_Sound* sequence, AUD_Sound* sound, float begin, float end, float skip); +extern AUD_API AUD_SequenceEntry* AUD_Sequence_add(AUD_Sound* sequence, AUD_Sound* sound, double begin, double end, double skip); /** * Removes an entry from the scene. @@ -167,7 +167,7 @@ extern AUD_API void AUD_Sequence_setSpeedOfSound(AUD_Sound* sequence, float valu * \param end The new end time or a negative value if unknown. * \param skip How many seconds to skip at the beginning. */ -extern AUD_API void AUD_SequenceEntry_move(AUD_SequenceEntry* entry, float begin, float end, float skip); +extern AUD_API void AUD_SequenceEntry_move(AUD_SequenceEntry* entry, double begin, double end, double skip); /** * Writes animation data to a sequenced entry. diff --git a/extern/audaspace/bindings/C/AUD_Special.cpp b/extern/audaspace/bindings/C/AUD_Special.cpp index 30148fa1487..c7155276a30 100644 --- a/extern/audaspace/bindings/C/AUD_Special.cpp +++ b/extern/audaspace/bindings/C/AUD_Special.cpp @@ -175,7 +175,7 @@ static void pauseSound(AUD_Handle* handle) (*handle)->pause(); } -AUD_API AUD_Handle* AUD_pauseAfter(AUD_Handle* handle, float seconds) +AUD_API AUD_Handle* AUD_pauseAfter(AUD_Handle* handle, double seconds) { auto device = DeviceManager::getDevice(); @@ -336,7 +336,7 @@ AUD_API const char* AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start } } -AUD_API AUD_Device* AUD_openMixdownDevice(AUD_DeviceSpecs specs, AUD_Sound* sequencer, float volume, float start) +AUD_API AUD_Device* AUD_openMixdownDevice(AUD_DeviceSpecs specs, AUD_Sound* sequencer, float volume, double start) { try { diff --git a/extern/audaspace/bindings/C/AUD_Special.h b/extern/audaspace/bindings/C/AUD_Special.h index ab79ae915a2..9faf9e4ee74 100644 --- a/extern/audaspace/bindings/C/AUD_Special.h +++ b/extern/audaspace/bindings/C/AUD_Special.h @@ -45,7 +45,7 @@ extern AUD_API float* AUD_readSoundBuffer(const char* filename, float low, float * \param seconds The time in seconds. * \return The silence handle. */ -extern AUD_API AUD_Handle* AUD_pauseAfter(AUD_Handle* handle, float seconds); +extern AUD_API AUD_Handle* AUD_pauseAfter(AUD_Handle* handle, double seconds); /** * Reads a sound into a buffer for drawing at a specific sampling rate. @@ -101,7 +101,7 @@ extern AUD_API const char* AUD_mixdown_per_channel(AUD_Sound* sound, unsigned in * \param start The start time of the mixdown in the sound scene. * \return The read device for the mixdown. */ -extern AUD_API AUD_Device* AUD_openMixdownDevice(AUD_DeviceSpecs specs, AUD_Sound* sequencer, float volume, float start); +extern AUD_API AUD_Device* AUD_openMixdownDevice(AUD_DeviceSpecs specs, AUD_Sound* sequencer, float volume, double start); /** * Initializes audio routines (FFMPEG/JACK if it is enabled). diff --git a/extern/audaspace/bindings/doc/device.rst b/extern/audaspace/bindings/doc/device.rst index fd6b334022c..d6f49bd7407 100644 --- a/extern/audaspace/bindings/doc/device.rst +++ b/extern/audaspace/bindings/doc/device.rst @@ -4,4 +4,5 @@ Device .. currentmodule:: aud .. autoclass:: Device :members: + :noindex: diff --git a/extern/audaspace/bindings/doc/handle.rst b/extern/audaspace/bindings/doc/handle.rst index aceedbca3a6..1d118e1ef62 100644 --- a/extern/audaspace/bindings/doc/handle.rst +++ b/extern/audaspace/bindings/doc/handle.rst @@ -4,4 +4,5 @@ Handle .. currentmodule:: aud .. autoclass:: Handle :members: + :noindex: diff --git a/extern/audaspace/bindings/doc/index.rst b/extern/audaspace/bindings/doc/index.rst index b8a26822949..cc6a543067d 100644 --- a/extern/audaspace/bindings/doc/index.rst +++ b/extern/audaspace/bindings/doc/index.rst @@ -7,6 +7,7 @@ Welcome to audaspace's documentation! ===================================== .. automodule:: aud + :no-members: This documentation is valid for both the Python and C bindings of audaspace. If you are looking for installation instructions check the `C++ API documentation <../index.html>`_. As C is not an object oriented language everything is accessible via functions where the first paramter is always the object. For methods these are named as ``AUD_ClassName_method()`` and properties are accessed via ``AUD_ClassName_property_get/set()``. Python users simply ``import aud`` to access the library. @@ -18,7 +19,7 @@ This documentation is valid for both the Python and C bindings of audaspace. If Classes: .. toctree:: - :maxdepth: 2 + :maxdepth: 1 device sound diff --git a/extern/audaspace/bindings/doc/sequence.rst b/extern/audaspace/bindings/doc/sequence.rst index 16fcb00f4dc..51aef1a1d37 100644 --- a/extern/audaspace/bindings/doc/sequence.rst +++ b/extern/audaspace/bindings/doc/sequence.rst @@ -4,4 +4,5 @@ Sequence .. currentmodule:: aud .. autoclass:: Sequence :members: + :noindex: diff --git a/extern/audaspace/bindings/doc/sequence_entry.rst b/extern/audaspace/bindings/doc/sequence_entry.rst index 0a3d83388e9..797b950dcdd 100644 --- a/extern/audaspace/bindings/doc/sequence_entry.rst +++ b/extern/audaspace/bindings/doc/sequence_entry.rst @@ -4,4 +4,5 @@ Sequence Entry .. currentmodule:: aud .. autoclass:: SequenceEntry :members: + :noindex: diff --git a/extern/audaspace/bindings/doc/sound.rst b/extern/audaspace/bindings/doc/sound.rst index 2f14721cf3a..d88c113eead 100644 --- a/extern/audaspace/bindings/doc/sound.rst +++ b/extern/audaspace/bindings/doc/sound.rst @@ -4,4 +4,5 @@ Sound .. currentmodule:: aud .. autoclass:: Sound :members: + :noindex: diff --git a/extern/audaspace/bindings/doc/tutorials.rst b/extern/audaspace/bindings/doc/tutorials.rst index 2a0e7541861..919c9083fc8 100644 --- a/extern/audaspace/bindings/doc/tutorials.rst +++ b/extern/audaspace/bindings/doc/tutorials.rst @@ -40,7 +40,7 @@ and create a :func:`aud.Sound.sine` signal with a frequency of 440 Hz. sine = aud.Sound.sine(440) .. note:: At this point nothing is playing back yet, -:class:`aud.Sound` objects are just descriptions of sounds. + :class:`aud.Sound` objects are just descriptions of sounds. However instead of a sine wave, we would like to have a square wave to produce a more retro gaming sound. We could of course use the diff --git a/extern/audaspace/bindings/python/PyDynamicMusic.cpp b/extern/audaspace/bindings/python/PyDynamicMusic.cpp index cab856359e0..8a7d8265bf4 100644 --- a/extern/audaspace/bindings/python/PyDynamicMusic.cpp +++ b/extern/audaspace/bindings/python/PyDynamicMusic.cpp @@ -228,9 +228,9 @@ PyDoc_STRVAR(M_aud_DynamicMusic_position_doc, static int DynamicMusic_set_position(DynamicMusicP* self, PyObject* args, void* nothing) { - float position; + double position; - if(!PyArg_Parse(args, "f:position", &position)) + if(!PyArg_Parse(args, "d:position", &position)) return -1; try @@ -252,7 +252,7 @@ DynamicMusic_get_position(DynamicMusicP* self, void* nothing) { try { - return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<aud::DynamicMusic>*>(self->dynamicMusic))->getPosition()); + return Py_BuildValue("d", (*reinterpret_cast<std::shared_ptr<aud::DynamicMusic>*>(self->dynamicMusic))->getPosition()); } catch(aud::Exception& e) { diff --git a/extern/audaspace/bindings/python/PyHandle.cpp b/extern/audaspace/bindings/python/PyHandle.cpp index 828c1b1afef..4ecf2ffd210 100644 --- a/extern/audaspace/bindings/python/PyHandle.cpp +++ b/extern/audaspace/bindings/python/PyHandle.cpp @@ -696,7 +696,7 @@ Handle_get_position(Handle* self, void* nothing) { try { - return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->getPosition()); + return Py_BuildValue("d", (*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->getPosition()); } catch(Exception& e) { @@ -708,9 +708,9 @@ Handle_get_position(Handle* self, void* nothing) static int Handle_set_position(Handle* self, PyObject* args, void* nothing) { - float position; + double position; - if(!PyArg_Parse(args, "f:position", &position)) + if(!PyArg_Parse(args, "d:position", &position)) return -1; try diff --git a/extern/audaspace/bindings/python/PySequence.cpp b/extern/audaspace/bindings/python/PySequence.cpp index 2e08059cefb..26f0c9e566f 100644 --- a/extern/audaspace/bindings/python/PySequence.cpp +++ b/extern/audaspace/bindings/python/PySequence.cpp @@ -104,11 +104,11 @@ PyDoc_STRVAR(M_aud_Sequence_add_doc, " :arg sound: The sound this entry should play.\n" " :type sound: :class:`Sound`\n" " :arg begin: The start time.\n" - " :type begin: float\n" + " :type begin: double\n" " :arg end: The end time or a negative value if determined by the sound.\n" - " :type end: float\n" + " :type end: double\n" " :arg skip: How much seconds should be skipped at the beginning.\n" - " :type skip: float\n" + " :type skip: double\n" " :return: The entry added.\n" " :rtype: :class:`SequenceEntry`"); @@ -116,13 +116,13 @@ static PyObject * Sequence_add(Sequence* self, PyObject* args, PyObject* kwds) { PyObject* object; - float begin; - float end = -1.0f; - float skip = 0.0f; + double begin; + double end = -1.0; + double skip = 0.0; static const char* kwlist[] = {"sound", "begin", "end", "skip", nullptr}; - if(!PyArg_ParseTupleAndKeywords(args, kwds, "Of|ff:add", const_cast<char**>(kwlist), &object, &begin, &end, &skip)) + if(!PyArg_ParseTupleAndKeywords(args, kwds, "Od|dd:add", const_cast<char**>(kwlist), &object, &begin, &end, &skip)) return nullptr; Sound* sound = checkSound(object); diff --git a/extern/audaspace/bindings/python/PySequenceEntry.cpp b/extern/audaspace/bindings/python/PySequenceEntry.cpp index a1bf3db21d2..74c038de0b0 100644 --- a/extern/audaspace/bindings/python/PySequenceEntry.cpp +++ b/extern/audaspace/bindings/python/PySequenceEntry.cpp @@ -46,18 +46,18 @@ PyDoc_STRVAR(M_aud_SequenceEntry_move_doc, ".. classmethod:: move()\n\n" " Moves the entry.\n\n" " :arg begin: The new start time.\n" - " :type begin: float\n" + " :type begin: double\n" " :arg end: The new end time or a negative value if unknown.\n" - " :type end: float\n" + " :type end: double\n" " :arg skip: How many seconds to skip at the beginning.\n" - " :type skip: float\n"); + " :type skip: double\n"); static PyObject * SequenceEntry_move(SequenceEntry* self, PyObject* args) { - float begin, end, skip; + double begin, end, skip; - if(!PyArg_ParseTuple(args, "fff:move", &begin, &end, &skip)) + if(!PyArg_ParseTuple(args, "ddd:move", &begin, &end, &skip)) return nullptr; try diff --git a/extern/audaspace/bindings/python/PySound.cpp b/extern/audaspace/bindings/python/PySound.cpp index c37e3f7fa52..62ee3435e82 100644 --- a/extern/audaspace/bindings/python/PySound.cpp +++ b/extern/audaspace/bindings/python/PySound.cpp @@ -1394,7 +1394,7 @@ PyDoc_STRVAR(M_aud_Sound_threshold_doc, " with a amplitude >= threshold to 1, all <= -threshold to -1 and\n" " all between to 0.\n\n" " :arg threshold: Threshold value over which an amplitude counts\n" - " non-zero.\n" + " non-zero.\n\n" ":type threshold: float\n" ":return: The created :class:`Sound` object.\n" ":rtype: :class:`Sound`"); @@ -1434,7 +1434,8 @@ PyDoc_STRVAR(M_aud_Sound_volume_doc, " :type volume: float\n" " :return: The created :class:`Sound` object.\n" " :rtype: :class:`Sound`\n\n" - " .. note:: Should be in the range [0, 1] to avoid clipping.\n\n" + " .. note::\n\n" + " Should be in the range [0, 1] to avoid clipping.\n\n" " .. note::\n\n" " This is a filter function, you might consider using\n" " :attr:`Handle.volume` instead."); @@ -1475,8 +1476,8 @@ PyDoc_STRVAR(M_aud_Sound_join_doc, " :return: The created :class:`Sound` object.\n" " :rtype: :class:`Sound`\n\n" " .. note::\n\n" - " The two factories have to have the same specifications\n" - " (channels and samplerate)."); + " The two factories have to have the same specifications\n" + " (channels and samplerate)."); static PyObject * Sound_join(Sound* self, PyObject* object) diff --git a/extern/audaspace/include/devices/DefaultSynchronizer.h b/extern/audaspace/include/devices/DefaultSynchronizer.h index 31f6c65219c..e818306603c 100644 --- a/extern/audaspace/include/devices/DefaultSynchronizer.h +++ b/extern/audaspace/include/devices/DefaultSynchronizer.h @@ -33,8 +33,8 @@ AUD_NAMESPACE_BEGIN class AUD_API DefaultSynchronizer : public ISynchronizer { public: - virtual void seek(std::shared_ptr<IHandle> handle, float time); - virtual float getPosition(std::shared_ptr<IHandle> handle); + virtual void seek(std::shared_ptr<IHandle> handle, double time); + virtual double getPosition(std::shared_ptr<IHandle> handle); virtual void play(); virtual void stop(); virtual void setSyncCallback(syncFunction function, void* data); diff --git a/extern/audaspace/include/devices/IDeviceFactory.h b/extern/audaspace/include/devices/IDeviceFactory.h index 7023cc058c5..c0769fa8015 100644 --- a/extern/audaspace/include/devices/IDeviceFactory.h +++ b/extern/audaspace/include/devices/IDeviceFactory.h @@ -35,6 +35,9 @@ AUD_NAMESPACE_BEGIN class AUD_API IDeviceFactory { public: + /** + * Destroys the device factory. + */ virtual ~IDeviceFactory() {} /** diff --git a/extern/audaspace/include/devices/IHandle.h b/extern/audaspace/include/devices/IHandle.h index 3f42fc33c3a..a10ef3d71e4 100644 --- a/extern/audaspace/include/devices/IHandle.h +++ b/extern/audaspace/include/devices/IHandle.h @@ -105,14 +105,14 @@ public: * - false if the handle is invalid. * \warning Whether the seek works or not depends on the sound source. */ - virtual bool seek(float position)=0; + virtual bool seek(double position)=0; /** * Retrieves the current playback position of a sound. * \return The playback position in seconds, or 0.0 if the handle is * invalid. */ - virtual float getPosition()=0; + virtual double getPosition()=0; /** * Returns the status of a played back sound. diff --git a/extern/audaspace/include/devices/ISynchronizer.h b/extern/audaspace/include/devices/ISynchronizer.h index 6f14de59565..430230fbcb3 100644 --- a/extern/audaspace/include/devices/ISynchronizer.h +++ b/extern/audaspace/include/devices/ISynchronizer.h @@ -56,14 +56,14 @@ public: * @param handle The handle that should be synchronized/seeked. * @param time The absolute time to synchronize to. */ - virtual void seek(std::shared_ptr<IHandle> handle, float time) = 0; + virtual void seek(std::shared_ptr<IHandle> handle, double time) = 0; /** * Retrieves the position of the synchronizer. * @param handle The handle which is synchronized. * @return The position in seconds. */ - virtual float getPosition(std::shared_ptr<IHandle> handle) = 0; + virtual double getPosition(std::shared_ptr<IHandle> handle) = 0; /** * Starts the synchronizer playback. diff --git a/extern/audaspace/include/devices/NULLDevice.h b/extern/audaspace/include/devices/NULLDevice.h index 76211a799b9..9af78919f88 100644 --- a/extern/audaspace/include/devices/NULLDevice.h +++ b/extern/audaspace/include/devices/NULLDevice.h @@ -53,8 +53,8 @@ private: virtual bool stop(); virtual bool getKeep(); virtual bool setKeep(bool keep); - virtual bool seek(float position); - virtual float getPosition(); + virtual bool seek(double position); + virtual double getPosition(); virtual Status getStatus(); virtual float getVolume(); virtual bool setVolume(float volume); diff --git a/extern/audaspace/include/devices/SoftwareDevice.h b/extern/audaspace/include/devices/SoftwareDevice.h index 8f3846394c6..e92a35e5402 100644 --- a/extern/audaspace/include/devices/SoftwareDevice.h +++ b/extern/audaspace/include/devices/SoftwareDevice.h @@ -180,8 +180,8 @@ protected: virtual bool stop(); virtual bool getKeep(); virtual bool setKeep(bool keep); - virtual bool seek(float position); - virtual float getPosition(); + virtual bool seek(double position); + virtual double getPosition(); virtual Status getStatus(); virtual float getVolume(); virtual bool setVolume(float volume); diff --git a/extern/audaspace/include/file/IFileInput.h b/extern/audaspace/include/file/IFileInput.h index aec929e7639..64074910d13 100644 --- a/extern/audaspace/include/file/IFileInput.h +++ b/extern/audaspace/include/file/IFileInput.h @@ -40,7 +40,10 @@ class Buffer; class AUD_API IFileInput { public: - virtual ~IFileInput() {}; + /** + * Destroys the file input. + */ + virtual ~IFileInput() {} /** * Creates a reader for a file to be read. diff --git a/extern/audaspace/include/fx/Delay.h b/extern/audaspace/include/fx/Delay.h index d6ab93ca351..d8730802c6f 100644 --- a/extern/audaspace/include/fx/Delay.h +++ b/extern/audaspace/include/fx/Delay.h @@ -35,7 +35,7 @@ private: /** * The delay in samples. */ - const float m_delay; + const double m_delay; // delete copy constructor and operator= Delay(const Delay&) = delete; @@ -47,12 +47,12 @@ public: * \param sound The input sound. * \param delay The desired delay in seconds. */ - Delay(std::shared_ptr<ISound> sound, float delay = 0); + Delay(std::shared_ptr<ISound> sound, double delay = 0); /** * Returns the delay in seconds. */ - float getDelay() const; + double getDelay() const; virtual std::shared_ptr<IReader> createReader(); }; diff --git a/extern/audaspace/include/fx/DelayReader.h b/extern/audaspace/include/fx/DelayReader.h index fe37e56d83e..38106082020 100644 --- a/extern/audaspace/include/fx/DelayReader.h +++ b/extern/audaspace/include/fx/DelayReader.h @@ -52,7 +52,7 @@ public: * \param reader The reader to read from. * \param delay The delay in seconds. */ - DelayReader(std::shared_ptr<IReader> reader, float delay); + DelayReader(std::shared_ptr<IReader> reader, double delay); virtual void seek(int position); virtual int getLength() const; diff --git a/extern/audaspace/include/fx/DynamicMusic.h b/extern/audaspace/include/fx/DynamicMusic.h index 5d59f77401a..c2a1c75b47e 100644 --- a/extern/audaspace/include/fx/DynamicMusic.h +++ b/extern/audaspace/include/fx/DynamicMusic.h @@ -55,7 +55,7 @@ private: /** * Length of the crossfade transition in seconds, used when no custom transition has been set. */ - float m_fadeTime; + double m_fadeTime; /** * Handle to the playback of the current scene. @@ -145,13 +145,13 @@ public: * Sets the length of the crossfade transition (default 1 second). * \param seconds The time in seconds. */ - void setFadeTime(float seconds); + void setFadeTime(double seconds); /** * Gets the length of the crossfade transition (default 1 second). * \return The length of the cressfade transition in seconds. */ - float getFadeTime(); + double getFadeTime(); /** * Resumes a paused sound. @@ -177,14 +177,14 @@ public: * - false if the handle is invalid. * \warning Whether the seek works or not depends on the sound source. */ - bool seek(float position); + bool seek(double position); /** * Retrieves the current playback position of a sound. * \return The playback position in seconds, or 0.0 if the handle is * invalid. */ - float getPosition(); + double getPosition(); /** * Retrieves the volume of the scenes. diff --git a/extern/audaspace/include/fx/Fader.h b/extern/audaspace/include/fx/Fader.h index 63280aec292..452d525e8ca 100644 --- a/extern/audaspace/include/fx/Fader.h +++ b/extern/audaspace/include/fx/Fader.h @@ -43,12 +43,12 @@ private: /** * The fading start. */ - const float m_start; + const double m_start; /** * The fading length. */ - const float m_length; + const double m_length; // delete copy constructor and operator= Fader(const Fader&) = delete; @@ -64,7 +64,7 @@ public: */ Fader(std::shared_ptr<ISound> sound, FadeType type = FADE_IN, - float start = 0.0f, float length = 1.0f); + double start = 0, double length = 1); /** * Returns the fading type. @@ -74,12 +74,12 @@ public: /** * Returns the fading start. */ - float getStart() const; + double getStart() const; /** * Returns the fading length. */ - float getLength() const; + double getLength() const; virtual std::shared_ptr<IReader> createReader(); }; diff --git a/extern/audaspace/include/fx/FaderReader.h b/extern/audaspace/include/fx/FaderReader.h index 99ea3d28938..9e5fc6d265f 100644 --- a/extern/audaspace/include/fx/FaderReader.h +++ b/extern/audaspace/include/fx/FaderReader.h @@ -49,12 +49,12 @@ private: /** * The fading start. */ - const float m_start; + const double m_start; /** * The fading length. */ - const float m_length; + const double m_length; // delete copy constructor and operator= FaderReader(const FaderReader&) = delete; @@ -69,7 +69,7 @@ public: * \param length How long fading should last in seconds. */ FaderReader(std::shared_ptr<IReader> reader, FadeType type, - float start,float length); + double start,double length); virtual void read(int& length, bool& eos, sample_t* buffer); }; diff --git a/extern/audaspace/include/fx/Limiter.h b/extern/audaspace/include/fx/Limiter.h index 0b5451b4eed..b3cf598db2e 100644 --- a/extern/audaspace/include/fx/Limiter.h +++ b/extern/audaspace/include/fx/Limiter.h @@ -35,12 +35,12 @@ private: /** * The start time. */ - const float m_start; + const double m_start; /** * The end time. */ - const float m_end; + const double m_end; // delete copy constructor and operator= Limiter(const Limiter&) = delete; @@ -55,17 +55,17 @@ public: * play to the end. */ Limiter(std::shared_ptr<ISound> sound, - float start = 0, float end = -1); + double start = 0, double end = -1); /** * Returns the start time. */ - float getStart() const; + double getStart() const; /** * Returns the end time. */ - float getEnd() const; + double getEnd() const; virtual std::shared_ptr<IReader> createReader(); }; diff --git a/extern/audaspace/include/fx/LimiterReader.h b/extern/audaspace/include/fx/LimiterReader.h index 49a07b5c29e..00ad02e343d 100644 --- a/extern/audaspace/include/fx/LimiterReader.h +++ b/extern/audaspace/include/fx/LimiterReader.h @@ -35,12 +35,12 @@ private: /** * The start sample: inclusive. */ - const float m_start; + const double m_start; /** * The end sample: exlusive. */ - const float m_end; + const double m_end; // delete copy constructor and operator= LimiterReader(const LimiterReader&) = delete; @@ -54,7 +54,7 @@ public: * \param end The desired end time (sample exklusive), a negative value * signals that it should play to the end. */ - LimiterReader(std::shared_ptr<IReader> reader, float start = 0, float end = -1); + LimiterReader(std::shared_ptr<IReader> reader, double start = 0, double end = -1); virtual void seek(int position); virtual int getLength() const; diff --git a/extern/audaspace/include/sequence/Sequence.h b/extern/audaspace/include/sequence/Sequence.h index 7005171e2c8..de14fd9fa38 100644 --- a/extern/audaspace/include/sequence/Sequence.h +++ b/extern/audaspace/include/sequence/Sequence.h @@ -151,7 +151,7 @@ public: * \param skip How much seconds should be skipped at the beginning. * \return The entry added. */ - std::shared_ptr<SequenceEntry> add(std::shared_ptr<ISound> sound, float begin, float end, float skip); + std::shared_ptr<SequenceEntry> add(std::shared_ptr<ISound> sound, double begin, double end, double skip); /** * Removes an entry from the scene. diff --git a/extern/audaspace/include/sequence/SequenceData.h b/extern/audaspace/include/sequence/SequenceData.h index b3df0548a4d..c3380e66924 100644 --- a/extern/audaspace/include/sequence/SequenceData.h +++ b/extern/audaspace/include/sequence/SequenceData.h @@ -203,7 +203,7 @@ public: * \param skip How much seconds should be skipped at the beginning. * \return The entry added. */ - std::shared_ptr<SequenceEntry> add(std::shared_ptr<ISound> sound, float begin, float end, float skip); + std::shared_ptr<SequenceEntry> add(std::shared_ptr<ISound> sound, double begin, double end, double skip); /** * Removes an entry from the scene. diff --git a/extern/audaspace/include/sequence/SequenceEntry.h b/extern/audaspace/include/sequence/SequenceEntry.h index 98f15faf7ff..b8e9f116ee4 100644 --- a/extern/audaspace/include/sequence/SequenceEntry.h +++ b/extern/audaspace/include/sequence/SequenceEntry.h @@ -55,13 +55,13 @@ private: std::shared_ptr<ISound> m_sound; /// The begin time. - float m_begin; + double m_begin; /// The end time. - float m_end; + double m_end; /// How many seconds are skipped at the beginning. - float m_skip; + double m_skip; /// Whether the entry is muted. bool m_muted; @@ -124,7 +124,7 @@ public: * \param skip How much seconds should be skipped at the beginning. * \param id The ID of the entry. */ - SequenceEntry(std::shared_ptr<ISound> sound, float begin, float end, float skip, int id); + SequenceEntry(std::shared_ptr<ISound> sound, double begin, double end, double skip, int id); virtual ~SequenceEntry(); /** @@ -155,7 +155,7 @@ public: * \param end The new end time or a negative value if unknown. * \param skip How many seconds to skip at the beginning. */ - void move(float begin, float end, float skip); + void move(double begin, double end, double skip); /** * Retrieves the muting state of the entry. diff --git a/extern/audaspace/plugins/jack/JackDevice.cpp b/extern/audaspace/plugins/jack/JackDevice.cpp index 1d238f74c3a..32874fd1315 100644 --- a/extern/audaspace/plugins/jack/JackDevice.cpp +++ b/extern/audaspace/plugins/jack/JackDevice.cpp @@ -292,7 +292,7 @@ void JackDevice::stopPlayback() m_nextState = JackTransportStopped; } -void JackDevice::seekPlayback(float time) +void JackDevice::seekPlayback(double time) { if(time >= 0.0f) AUD_jack_transport_locate(m_client, time * m_specs.rate); @@ -304,11 +304,11 @@ void JackDevice::setSyncCallback(ISynchronizer::syncFunction sync, void* data) m_syncFuncData = data; } -float JackDevice::getPlaybackPosition() +double JackDevice::getPlaybackPosition() { jack_position_t position; AUD_jack_transport_query(m_client, &position); - return position.frame / (float) m_specs.rate; + return position.frame / (double) m_specs.rate; } bool JackDevice::doesPlayback() diff --git a/extern/audaspace/plugins/jack/JackDevice.h b/extern/audaspace/plugins/jack/JackDevice.h index 72143eda149..4e6b1f5d12c 100644 --- a/extern/audaspace/plugins/jack/JackDevice.h +++ b/extern/audaspace/plugins/jack/JackDevice.h @@ -174,7 +174,7 @@ public: * Seeks jack transport playback. * \param time The time to seek to. */ - void seekPlayback(float time); + void seekPlayback(double time); /** * Sets the sync callback for jack transport playback. @@ -187,7 +187,7 @@ public: * Retrieves the jack transport playback time. * \return The current time position. */ - float getPlaybackPosition(); + double getPlaybackPosition(); /** * Returns whether jack transport plays back. diff --git a/extern/audaspace/plugins/jack/JackSynchronizer.cpp b/extern/audaspace/plugins/jack/JackSynchronizer.cpp index cd4c448786d..0bcafa19ca5 100644 --- a/extern/audaspace/plugins/jack/JackSynchronizer.cpp +++ b/extern/audaspace/plugins/jack/JackSynchronizer.cpp @@ -25,12 +25,12 @@ JackSynchronizer::JackSynchronizer(JackDevice* device) : { } -void JackSynchronizer::seek(std::shared_ptr<IHandle> handle, float time) +void JackSynchronizer::seek(std::shared_ptr<IHandle> handle, double time) { m_device->seekPlayback(time); } -float JackSynchronizer::getPosition(std::shared_ptr<IHandle> handle) +double JackSynchronizer::getPosition(std::shared_ptr<IHandle> handle) { return m_device->getPlaybackPosition(); } diff --git a/extern/audaspace/plugins/jack/JackSynchronizer.h b/extern/audaspace/plugins/jack/JackSynchronizer.h index 5c7341a7872..8a1f930ebed 100644 --- a/extern/audaspace/plugins/jack/JackSynchronizer.h +++ b/extern/audaspace/plugins/jack/JackSynchronizer.h @@ -48,8 +48,8 @@ public: */ JackSynchronizer(JackDevice* device); - virtual void seek(std::shared_ptr<IHandle> handle, float time); - virtual float getPosition(std::shared_ptr<IHandle> handle); + virtual void seek(std::shared_ptr<IHandle> handle, double time); + virtual double getPosition(std::shared_ptr<IHandle> handle); virtual void play(); virtual void stop(); virtual void setSyncCallback(syncFunction function, void* data); diff --git a/extern/audaspace/plugins/openal/OpenALDevice.cpp b/extern/audaspace/plugins/openal/OpenALDevice.cpp index f41e9c6bef8..536ec4ccb1b 100644 --- a/extern/audaspace/plugins/openal/OpenALDevice.cpp +++ b/extern/audaspace/plugins/openal/OpenALDevice.cpp @@ -269,7 +269,7 @@ bool OpenALDevice::OpenALHandle::setKeep(bool keep) return true; } -bool OpenALDevice::OpenALHandle::seek(float position) +bool OpenALDevice::OpenALHandle::seek(double position) { if(!m_status) return false; @@ -335,7 +335,7 @@ bool OpenALDevice::OpenALHandle::seek(float position) return true; } -float OpenALDevice::OpenALHandle::getPosition() +double OpenALDevice::OpenALHandle::getPosition() { if(!m_status) return false; diff --git a/extern/audaspace/plugins/openal/OpenALDevice.h b/extern/audaspace/plugins/openal/OpenALDevice.h index c2bec443933..b6ba5456d85 100644 --- a/extern/audaspace/plugins/openal/OpenALDevice.h +++ b/extern/audaspace/plugins/openal/OpenALDevice.h @@ -126,8 +126,8 @@ private: virtual bool stop(); virtual bool getKeep(); virtual bool setKeep(bool keep); - virtual bool seek(float position); - virtual float getPosition(); + virtual bool seek(double position); + virtual double getPosition(); virtual Status getStatus(); virtual float getVolume(); virtual bool setVolume(float volume); diff --git a/extern/audaspace/src/devices/DefaultSynchronizer.cpp b/extern/audaspace/src/devices/DefaultSynchronizer.cpp index aa8945dadaa..3ef1f0bfc41 100644 --- a/extern/audaspace/src/devices/DefaultSynchronizer.cpp +++ b/extern/audaspace/src/devices/DefaultSynchronizer.cpp @@ -19,12 +19,12 @@ AUD_NAMESPACE_BEGIN -void DefaultSynchronizer::seek(std::shared_ptr<IHandle> handle, float time) +void DefaultSynchronizer::seek(std::shared_ptr<IHandle> handle, double time) { handle->seek(time); } -float DefaultSynchronizer::getPosition(std::shared_ptr<IHandle> handle) +double DefaultSynchronizer::getPosition(std::shared_ptr<IHandle> handle) { return handle->getPosition(); } diff --git a/extern/audaspace/src/devices/NULLDevice.cpp b/extern/audaspace/src/devices/NULLDevice.cpp index a82537f43b2..c3290465563 100644 --- a/extern/audaspace/src/devices/NULLDevice.cpp +++ b/extern/audaspace/src/devices/NULLDevice.cpp @@ -52,12 +52,12 @@ bool NULLDevice::NULLHandle::setKeep(bool keep) return false; } -bool NULLDevice::NULLHandle::seek(float position) +bool NULLDevice::NULLHandle::seek(double position) { return false; } -float NULLDevice::NULLHandle::getPosition() +double NULLDevice::NULLHandle::getPosition() { return std::numeric_limits<float>::quiet_NaN(); } diff --git a/extern/audaspace/src/devices/SoftwareDevice.cpp b/extern/audaspace/src/devices/SoftwareDevice.cpp index 8c16c75e8e3..7186f8b9442 100644 --- a/extern/audaspace/src/devices/SoftwareDevice.cpp +++ b/extern/audaspace/src/devices/SoftwareDevice.cpp @@ -347,7 +347,7 @@ bool SoftwareDevice::SoftwareHandle::setKeep(bool keep) return true; } -bool SoftwareDevice::SoftwareHandle::seek(float position) +bool SoftwareDevice::SoftwareHandle::seek(double position) { if(!m_status) return false; @@ -366,7 +366,7 @@ bool SoftwareDevice::SoftwareHandle::seek(float position) return true; } -float SoftwareDevice::SoftwareHandle::getPosition() +double SoftwareDevice::SoftwareHandle::getPosition() { if(!m_status) return false; @@ -376,7 +376,7 @@ float SoftwareDevice::SoftwareHandle::getPosition() if(!m_status) return 0.0f; - float position = m_reader->getPosition() / (float)m_device->m_specs.rate; + double position = m_reader->getPosition() / (double)m_device->m_specs.rate; return position; } diff --git a/extern/audaspace/src/fx/Delay.cpp b/extern/audaspace/src/fx/Delay.cpp index e2a82299bc0..3ce16f54636 100644 --- a/extern/audaspace/src/fx/Delay.cpp +++ b/extern/audaspace/src/fx/Delay.cpp @@ -19,13 +19,13 @@ AUD_NAMESPACE_BEGIN -Delay::Delay(std::shared_ptr<ISound> sound, float delay) : +Delay::Delay(std::shared_ptr<ISound> sound, double delay) : Effect(sound), m_delay(delay) { } -float Delay::getDelay() const +double Delay::getDelay() const { return m_delay; } diff --git a/extern/audaspace/src/fx/DelayReader.cpp b/extern/audaspace/src/fx/DelayReader.cpp index 530aed69cba..ec171a76f93 100644 --- a/extern/audaspace/src/fx/DelayReader.cpp +++ b/extern/audaspace/src/fx/DelayReader.cpp @@ -20,7 +20,7 @@ AUD_NAMESPACE_BEGIN -DelayReader::DelayReader(std::shared_ptr<IReader> reader, float delay) : +DelayReader::DelayReader(std::shared_ptr<IReader> reader, double delay) : EffectReader(reader), m_delay(int((SampleRate)delay * reader->getSpecs().rate)), m_remdelay(int((SampleRate)delay * reader->getSpecs().rate)) diff --git a/extern/audaspace/src/fx/DynamicMusic.cpp b/extern/audaspace/src/fx/DynamicMusic.cpp index b8f5c975b3f..ad4a76fee95 100644 --- a/extern/audaspace/src/fx/DynamicMusic.cpp +++ b/extern/audaspace/src/fx/DynamicMusic.cpp @@ -133,14 +133,14 @@ bool DynamicMusic::addTransition(int init, int end, std::shared_ptr<ISound> soun return false; } -void DynamicMusic::setFadeTime(float seconds) +void DynamicMusic::setFadeTime(double seconds) { m_device->lock(); m_fadeTime = seconds; m_device->unlock(); } -float DynamicMusic::getFadeTime() +double DynamicMusic::getFadeTime() { return m_fadeTime; } @@ -169,7 +169,7 @@ bool DynamicMusic::pause() return result || resultTrans; } -bool DynamicMusic::seek(float position) +bool DynamicMusic::seek(double position) { bool result = false; @@ -183,9 +183,9 @@ bool DynamicMusic::seek(float position) return result; } -float DynamicMusic::getPosition() +double DynamicMusic::getPosition() { - float result = 0.0f; + double result = 0.0f; if(m_currentHandle != nullptr) result = m_currentHandle->getPosition(); diff --git a/extern/audaspace/src/fx/Fader.cpp b/extern/audaspace/src/fx/Fader.cpp index 041d8369a01..778720b6059 100644 --- a/extern/audaspace/src/fx/Fader.cpp +++ b/extern/audaspace/src/fx/Fader.cpp @@ -18,7 +18,7 @@ AUD_NAMESPACE_BEGIN -Fader::Fader(std::shared_ptr<ISound> sound, FadeType type, float start, float length) : +Fader::Fader(std::shared_ptr<ISound> sound, FadeType type, double start, double length) : Effect(sound), m_type(type), m_start(start), @@ -31,12 +31,12 @@ FadeType Fader::getType() const return m_type; } -float Fader::getStart() const +double Fader::getStart() const { return m_start; } -float Fader::getLength() const +double Fader::getLength() const { return m_length; } diff --git a/extern/audaspace/src/fx/FaderReader.cpp b/extern/audaspace/src/fx/FaderReader.cpp index b1e23b993f3..10783f9f00c 100644 --- a/extern/audaspace/src/fx/FaderReader.cpp +++ b/extern/audaspace/src/fx/FaderReader.cpp @@ -20,7 +20,7 @@ AUD_NAMESPACE_BEGIN -FaderReader::FaderReader(std::shared_ptr<IReader> reader, FadeType type, float start,float length) : +FaderReader::FaderReader(std::shared_ptr<IReader> reader, FadeType type, double start, double length) : EffectReader(reader), m_type(type), m_start(start), @@ -36,14 +36,14 @@ void FaderReader::read(int& length, bool& eos, sample_t* buffer) m_reader->read(length, eos, buffer); - if((position + length) / (float)specs.rate <= m_start) + if((position + length) / specs.rate <= m_start) { if(m_type != FADE_OUT) { std::memset(buffer, 0, length * samplesize); } } - else if(position / (float)specs.rate >= m_start+m_length) + else if(position / specs.rate >= m_start+m_length) { if(m_type == FADE_OUT) { @@ -58,7 +58,7 @@ void FaderReader::read(int& length, bool& eos, sample_t* buffer) { if(i % specs.channels == 0) { - volume = (((position+i)/(float)specs.rate)-m_start) / m_length; + volume = float((((position + i) / specs.rate) - m_start) / m_length); if(volume > 1.0f) volume = 1.0f; else if(volume < 0.0f) diff --git a/extern/audaspace/src/fx/Limiter.cpp b/extern/audaspace/src/fx/Limiter.cpp index 38a7288e8d7..031283bb322 100644 --- a/extern/audaspace/src/fx/Limiter.cpp +++ b/extern/audaspace/src/fx/Limiter.cpp @@ -20,19 +20,19 @@ AUD_NAMESPACE_BEGIN Limiter::Limiter(std::shared_ptr<ISound> sound, - float start, float end) : + double start, double end) : Effect(sound), m_start(start), m_end(end) { } -float Limiter::getStart() const +double Limiter::getStart() const { return m_start; } -float Limiter::getEnd() const +double Limiter::getEnd() const { return m_end; } diff --git a/extern/audaspace/src/fx/LimiterReader.cpp b/extern/audaspace/src/fx/LimiterReader.cpp index 1d003c29679..28eb47aed2f 100644 --- a/extern/audaspace/src/fx/LimiterReader.cpp +++ b/extern/audaspace/src/fx/LimiterReader.cpp @@ -21,7 +21,7 @@ AUD_NAMESPACE_BEGIN -LimiterReader::LimiterReader(std::shared_ptr<IReader> reader, float start, float end) : +LimiterReader::LimiterReader(std::shared_ptr<IReader> reader, double start, double end) : EffectReader(reader), m_start(start), m_end(end) diff --git a/extern/audaspace/src/respec/ChannelMapperReader.cpp b/extern/audaspace/src/respec/ChannelMapperReader.cpp index 6558d2444f5..f7ddf3dbc73 100644 --- a/extern/audaspace/src/respec/ChannelMapperReader.cpp +++ b/extern/audaspace/src/respec/ChannelMapperReader.cpp @@ -16,9 +16,9 @@ #include "respec/ChannelMapperReader.h" +#include <algorithm> #include <cmath> #include <limits> -#include <algorithm> AUD_NAMESPACE_BEGIN diff --git a/extern/audaspace/src/respec/Mixer.cpp b/extern/audaspace/src/respec/Mixer.cpp index d63f0bab2bb..ad8d885df4e 100644 --- a/extern/audaspace/src/respec/Mixer.cpp +++ b/extern/audaspace/src/respec/Mixer.cpp @@ -66,11 +66,11 @@ void Mixer::setSpecs(Specs specs) void Mixer::clear(int length) { - m_buffer.assureSize(length * m_specs.channels * AUD_SAMPLE_SIZE(m_specs)); + m_buffer.assureSize(length * AUD_SAMPLE_SIZE(m_specs)); m_length = length; - std::memset(m_buffer.getBuffer(), 0, length * m_specs.channels * AUD_SAMPLE_SIZE(m_specs)); + std::memset(m_buffer.getBuffer(), 0, length * AUD_SAMPLE_SIZE(m_specs)); } void Mixer::mix(sample_t* buffer, int start, int length, float volume) diff --git a/extern/audaspace/src/sequence/Sequence.cpp b/extern/audaspace/src/sequence/Sequence.cpp index eaec4d84ae1..ab7e6e77857 100644 --- a/extern/audaspace/src/sequence/Sequence.cpp +++ b/extern/audaspace/src/sequence/Sequence.cpp @@ -90,7 +90,7 @@ AnimateableProperty* Sequence::getAnimProperty(AnimateablePropertyType type) return m_sequence->getAnimProperty(type); } -std::shared_ptr<SequenceEntry> Sequence::add(std::shared_ptr<ISound> sound, float begin, float end, float skip) +std::shared_ptr<SequenceEntry> Sequence::add(std::shared_ptr<ISound> sound, double begin, double end, double skip) { return m_sequence->add(sound, begin, end, skip); } diff --git a/extern/audaspace/src/sequence/SequenceData.cpp b/extern/audaspace/src/sequence/SequenceData.cpp index fb920acc1a8..288f0bd225d 100644 --- a/extern/audaspace/src/sequence/SequenceData.cpp +++ b/extern/audaspace/src/sequence/SequenceData.cpp @@ -149,7 +149,7 @@ AnimateableProperty* SequenceData::getAnimProperty(AnimateablePropertyType type) } } -std::shared_ptr<SequenceEntry> SequenceData::add(std::shared_ptr<ISound> sound, float begin, float end, float skip) +std::shared_ptr<SequenceEntry> SequenceData::add(std::shared_ptr<ISound> sound, double begin, double end, double skip) { std::lock_guard<std::recursive_mutex> lock(m_mutex); diff --git a/extern/audaspace/src/sequence/SequenceEntry.cpp b/extern/audaspace/src/sequence/SequenceEntry.cpp index de538199d7d..b63bdd2ffca 100644 --- a/extern/audaspace/src/sequence/SequenceEntry.cpp +++ b/extern/audaspace/src/sequence/SequenceEntry.cpp @@ -22,7 +22,7 @@ AUD_NAMESPACE_BEGIN -SequenceEntry::SequenceEntry(std::shared_ptr<ISound> sound, float begin, float end, float skip, int id) : +SequenceEntry::SequenceEntry(std::shared_ptr<ISound> sound, double begin, double end, double skip, int id) : m_status(0), m_pos_status(1), m_sound_status(0), @@ -84,7 +84,7 @@ void SequenceEntry::setSound(std::shared_ptr<ISound> sound) } } -void SequenceEntry::move(float begin, float end, float skip) +void SequenceEntry::move(double begin, double end, double skip) { std::lock_guard<std::recursive_mutex> lock(m_mutex); diff --git a/extern/audaspace/src/sequence/SequenceHandle.cpp b/extern/audaspace/src/sequence/SequenceHandle.cpp index 140b1fbd94a..0437b05c85d 100644 --- a/extern/audaspace/src/sequence/SequenceHandle.cpp +++ b/extern/audaspace/src/sequence/SequenceHandle.cpp @@ -57,7 +57,7 @@ void SequenceHandle::start() m_valid = m_handle.get(); } -bool SequenceHandle::updatePosition(float position) +bool SequenceHandle::updatePosition(double position) { std::lock_guard<ILockable> lock(*m_entry); @@ -140,7 +140,7 @@ void SequenceHandle::stop() m_3dhandle = nullptr; } -void SequenceHandle::update(float position, float frame, float fps) +void SequenceHandle::update(double position, float frame, float fps) { if(m_sound_status != m_entry->m_sound_status) { @@ -229,7 +229,7 @@ void SequenceHandle::update(float position, float frame, float fps) m_handle->setVolume(0); } -bool SequenceHandle::seek(float position) +bool SequenceHandle::seek(double position) { if(!m_valid) // sound not valid, aborting @@ -240,7 +240,7 @@ bool SequenceHandle::seek(float position) return false; std::lock_guard<ILockable> lock(*m_entry); - float seekpos = position - m_entry->m_begin; + double seekpos = position - m_entry->m_begin; if(seekpos < 0) seekpos = 0; seekpos += m_entry->m_skip; diff --git a/extern/audaspace/src/sequence/SequenceHandle.h b/extern/audaspace/src/sequence/SequenceHandle.h index 9a77489a8f8..14a94365878 100644 --- a/extern/audaspace/src/sequence/SequenceHandle.h +++ b/extern/audaspace/src/sequence/SequenceHandle.h @@ -71,7 +71,7 @@ private: * \param position Current playback position in seconds. * \return Whether the handle is valid. */ - bool updatePosition(float position); + bool updatePosition(double position); public: /** @@ -104,14 +104,14 @@ public: * \param frame The current frame during playback. * \param fps The animation frames per second. */ - void update(float position, float frame, float fps); + void update(double position, float frame, float fps); /** * Seeks the handle to a specific time position. * \param position The time to seek to. * \return Whether the handle is valid. */ - bool seek(float position); + bool seek(double position); }; AUD_NAMESPACE_END diff --git a/extern/audaspace/src/sequence/SequenceReader.cpp b/extern/audaspace/src/sequence/SequenceReader.cpp index 38647aaeadf..c903e8ef42e 100644 --- a/extern/audaspace/src/sequence/SequenceReader.cpp +++ b/extern/audaspace/src/sequence/SequenceReader.cpp @@ -49,7 +49,7 @@ void SequenceReader::seek(int position) for(auto& handle : m_handles) { - handle->seek(position / m_sequence->m_specs.rate); + handle->seek(position / (double)m_sequence->m_specs.rate); } } @@ -150,13 +150,12 @@ void SequenceReader::read(int& length, bool& eos, sample_t* buffer) Specs specs = m_sequence->m_specs; int pos = 0; - float time = float(m_position) / float(specs.rate); + double time = double(m_position) / double(specs.rate); float volume, frame; int len, cfra; Vector3 v, v2; Quaternion q; - while(pos < length) { frame = time * m_sequence->m_fps; @@ -187,7 +186,7 @@ void SequenceReader::read(int& length, bool& eos, sample_t* buffer) m_device.read(reinterpret_cast<data_t*>(buffer + specs.channels * pos), len); pos += len; - time += float(len) / float(specs.rate); + time += double(len) / double(specs.rate); } m_position += length; diff --git a/extern/mantaflow/preprocessed/gitinfo.h b/extern/mantaflow/preprocessed/gitinfo.h index 791dd001bbe..e5cd4a3d6ce 100644 --- a/extern/mantaflow/preprocessed/gitinfo.h +++ b/extern/mantaflow/preprocessed/gitinfo.h @@ -1,3 +1,3 @@ -#define MANTA_GIT_VERSION "commit 21303fab2eda588ec22988bf9e5762d2001c131f" +#define MANTA_GIT_VERSION "commit b4a2742bd743e2913fba94dd35846042e2650212" diff --git a/extern/mantaflow/preprocessed/plugin/extforces.cpp b/extern/mantaflow/preprocessed/plugin/extforces.cpp index 36221fbbc10..798bb3daeee 100644 --- a/extern/mantaflow/preprocessed/plugin/extforces.cpp +++ b/extern/mantaflow/preprocessed/plugin/extforces.cpp @@ -244,13 +244,15 @@ struct KnApplyForce : public KernelBase { bool additive; }; -//! add gravity forces to all fluid cells, automatically adapts to different grid sizes +//! add gravity forces to all fluid cells, optionally adapts to different grid sizes automatically void addGravity(const FlagGrid &flags, MACGrid &vel, Vec3 gravity, - const Grid<Real> *exclude = NULL) + const Grid<Real> *exclude = NULL, + bool scale = true) { - Vec3 f = gravity * flags.getParent()->getDt() / flags.getDx(); + float gridScale = (scale) ? flags.getDx() : 1; + Vec3 f = gravity * flags.getParent()->getDt() / gridScale; KnApplyForce(flags, vel, f, exclude, true); } static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) @@ -267,8 +269,9 @@ static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) MACGrid &vel = *_args.getPtr<MACGrid>("vel", 1, &_lock); Vec3 gravity = _args.get<Vec3>("gravity", 2, &_lock); const Grid<Real> *exclude = _args.getPtrOpt<Grid<Real>>("exclude", 3, NULL, &_lock); + bool scale = _args.getOpt<bool>("scale", 4, true, &_lock); _retval = getPyNone(); - addGravity(flags, vel, gravity, exclude); + addGravity(flags, vel, gravity, exclude, scale); _args.check(); } pbFinalizePlugin(parent, "addGravity", !noTiming); @@ -287,14 +290,13 @@ void PbRegister_addGravity() } } -//! add gravity forces to all fluid cells , but dont account for changing cell size +//! Deprecated: use addGravity(scale=false) instead void addGravityNoScale(const FlagGrid &flags, MACGrid &vel, const Vec3 &gravity, const Grid<Real> *exclude = NULL) { - const Vec3 f = gravity * flags.getParent()->getDt(); - KnApplyForce(flags, vel, f, exclude, true); + addGravity(flags, vel, gravity, exclude, false); } static PyObject *_W_1(PyObject *_self, PyObject *_linargs, PyObject *_kwds) { @@ -412,14 +414,17 @@ struct KnAddBuoyancy : public KernelBase { Vec3 strength; }; -//! add Buoyancy force based on fctor (e.g. smoke density) +//! add Buoyancy force based on factor (e.g. smoke density), optionally adapts to different grid +//! sizes automatically void addBuoyancy(const FlagGrid &flags, const Grid<Real> &density, MACGrid &vel, Vec3 gravity, - Real coefficient = 1.) + Real coefficient = 1., + bool scale = true) { - Vec3 f = -gravity * flags.getParent()->getDt() / flags.getParent()->getDx() * coefficient; + float gridScale = (scale) ? flags.getDx() : 1; + Vec3 f = -gravity * flags.getParent()->getDt() / gridScale * coefficient; KnAddBuoyancy(flags, density, vel, f); } static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds) @@ -437,8 +442,9 @@ static PyObject *_W_2(PyObject *_self, PyObject *_linargs, PyObject *_kwds) MACGrid &vel = *_args.getPtr<MACGrid>("vel", 2, &_lock); Vec3 gravity = _args.get<Vec3>("gravity", 3, &_lock); Real coefficient = _args.getOpt<Real>("coefficient", 4, 1., &_lock); + bool scale = _args.getOpt<bool>("scale", 5, true, &_lock); _retval = getPyNone(); - addBuoyancy(flags, density, vel, gravity, coefficient); + addBuoyancy(flags, density, vel, gravity, coefficient, scale); _args.check(); } pbFinalizePlugin(parent, "addBuoyancy", !noTiming); diff --git a/intern/CMakeLists.txt b/intern/CMakeLists.txt index 4e780dc9f0f..fa18f4d793a 100644 --- a/intern/CMakeLists.txt +++ b/intern/CMakeLists.txt @@ -20,7 +20,6 @@ # add_subdirectory(atomic) # header only add_subdirectory(clog) -add_subdirectory(string) add_subdirectory(ghost) add_subdirectory(guardedalloc) add_subdirectory(libmv) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 92f0a8fb830..08641c05941 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -770,8 +770,9 @@ class CYCLES_RENDER_PT_filter(CyclesButtonsPanel, Panel): col.prop(view_layer, "use_strand", text="Hair") col.prop(view_layer, "use_volumes", text="Volumes") if with_freestyle: - col.prop(view_layer, "use_freestyle", text="Freestyle") - col.active = rd.use_freestyle + sub = col.row(align=True) + sub.prop(view_layer, "use_freestyle", text="Freestyle") + sub.active = rd.use_freestyle class CYCLES_RENDER_PT_override(CyclesButtonsPanel, Panel): @@ -1409,6 +1410,8 @@ class CYCLES_LIGHT_PT_nodes(CyclesButtonsPanel, Panel): def draw(self, context): layout = self.layout + layout.use_property_split = True + light = context.light panel_node_draw(layout, light, 'OUTPUT_LIGHT', 'Surface') @@ -1458,6 +1461,8 @@ class CYCLES_WORLD_PT_surface(CyclesButtonsPanel, Panel): def draw(self, context): layout = self.layout + layout.use_property_split = True + world = context.world if not panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Surface'): @@ -1477,6 +1482,8 @@ class CYCLES_WORLD_PT_volume(CyclesButtonsPanel, Panel): def draw(self, context): layout = self.layout + layout.use_property_split = True + world = context.world panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Volume') @@ -1664,6 +1671,8 @@ class CYCLES_MATERIAL_PT_surface(CyclesButtonsPanel, Panel): def draw(self, context): layout = self.layout + layout.use_property_split = True + mat = context.material if not panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Surface'): layout.prop(mat, "diffuse_color") @@ -1682,6 +1691,8 @@ class CYCLES_MATERIAL_PT_volume(CyclesButtonsPanel, Panel): def draw(self, context): layout = self.layout + layout.use_property_split = True + mat = context.material # cmat = mat.cycles @@ -1700,6 +1711,8 @@ class CYCLES_MATERIAL_PT_displacement(CyclesButtonsPanel, Panel): def draw(self, context): layout = self.layout + layout.use_property_split = True + mat = context.material panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Displacement') diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index f4c100bcd2b..0c120b944a7 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -108,7 +108,7 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d } if (dicing_prop_changed) { - for (const pair<GeometryKey, Geometry *> &iter : geometry_map.key_to_scene_data()) { + for (const pair<const GeometryKey, Geometry *> &iter : geometry_map.key_to_scene_data()) { Geometry *geom = iter.second; if (geom->type == Geometry::MESH) { Mesh *mesh = static_cast<Mesh *>(geom); diff --git a/intern/cycles/bvh/bvh_optix.cpp b/intern/cycles/bvh/bvh_optix.cpp index 26b64c24db5..740994b2ebc 100644 --- a/intern/cycles/bvh/bvh_optix.cpp +++ b/intern/cycles/bvh/bvh_optix.cpp @@ -156,6 +156,19 @@ void BVHOptiX::pack_tlas() PackedBVH &bvh_pack = geom->bvh->pack; int geom_prim_offset = geom->prim_offset; + // Merge visibility flags of all objects and fix object indices for non-instanced geometry + int object_index = 0; // Unused for instanced geometry + int object_visibility = 0; + foreach (Object *ob, objects) { + if (ob->geometry == geom) { + object_visibility |= ob->visibility_for_tracing(); + if (!geom->is_instanced()) { + object_index = ob->get_device_index(); + break; + } + } + } + // Merge primitive, object and triangle indexes if (!bvh_pack.prim_index.empty()) { int *bvh_prim_type = &bvh_pack.prim_type[0]; @@ -174,8 +187,8 @@ void BVHOptiX::pack_tlas() } pack_prim_type[pack_offset] = bvh_prim_type[i]; - pack_prim_object[pack_offset] = 0; // Unused for instanced geometry - pack_prim_visibility[pack_offset] = bvh_prim_visibility[i]; + pack_prim_object[pack_offset] = object_index; + pack_prim_visibility[pack_offset] = bvh_prim_visibility[i] | object_visibility; } } @@ -188,27 +201,6 @@ void BVHOptiX::pack_tlas() pack_verts_offset += prim_tri_size; } } - - // Merge visibility flags of all objects and fix object indices for non-instanced geometry - foreach (Object *ob, objects) { - Geometry *const geom = ob->geometry; - size_t num_primitives = 0; - - if (geom->type == Geometry::MESH) { - num_primitives = static_cast<Mesh *const>(geom)->num_triangles(); - } - else if (geom->type == Geometry::HAIR) { - num_primitives = static_cast<Hair *const>(geom)->num_segments(); - } - - for (size_t i = 0; i < num_primitives; ++i) { - if (!geom->is_instanced()) { - assert(pack.prim_object[geom->optix_prim_offset + i] == 0); - pack.prim_object[geom->optix_prim_offset + i] = ob->get_device_index(); - } - pack.prim_visibility[geom->optix_prim_offset + i] |= ob->visibility_for_tracing(); - } - } } void BVHOptiX::pack_nodes(const BVHNode *) diff --git a/intern/cycles/kernel/kernel_compat_cuda.h b/intern/cycles/kernel/kernel_compat_cuda.h index 3c5a10540d5..4094e173da9 100644 --- a/intern/cycles/kernel/kernel_compat_cuda.h +++ b/intern/cycles/kernel/kernel_compat_cuda.h @@ -71,6 +71,7 @@ __device__ half __float2half(const float f) #define ccl_may_alias #define ccl_addr_space #define ccl_restrict __restrict__ +#define ccl_loop_no_unroll /* TODO(sergey): In theory we might use references with CUDA, however * performance impact yet to be investigated. */ diff --git a/intern/cycles/kernel/kernel_compat_opencl.h b/intern/cycles/kernel/kernel_compat_opencl.h index 4963f1cd196..35dc95ca10d 100644 --- a/intern/cycles/kernel/kernel_compat_opencl.h +++ b/intern/cycles/kernel/kernel_compat_opencl.h @@ -43,6 +43,7 @@ #define ccl_local __local #define ccl_local_param __local #define ccl_private __private +#define ccl_loop_no_unroll __attribute__((opencl_unroll_hint(1))) #define ccl_restrict restrict #define ccl_ref #define ccl_align(n) __attribute__((aligned(n))) diff --git a/intern/cycles/kernel/kernel_compat_optix.h b/intern/cycles/kernel/kernel_compat_optix.h index 7068acc3a32..970f5cf864c 100644 --- a/intern/cycles/kernel/kernel_compat_optix.h +++ b/intern/cycles/kernel/kernel_compat_optix.h @@ -70,6 +70,7 @@ __device__ half __float2half(const float f) #define ccl_private #define ccl_may_alias #define ccl_addr_space +#define ccl_loop_no_unroll #define ccl_restrict __restrict__ #define ccl_ref #define ccl_align(n) __align__(n) diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index 2857de533f3..5292b5f8055 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -1011,7 +1011,13 @@ bool OSLRenderServices::get_userdata( return false; /* disabled by lockgeom */ } +#if OSL_LIBRARY_VERSION_CODE >= 11100 +TextureSystem::TextureHandle *OSLRenderServices::get_texture_handle(ustring filename, + OSL::ShadingContext *) +#else + TextureSystem::TextureHandle *OSLRenderServices::get_texture_handle(ustring filename) +#endif { OSLTextureHandleMap::iterator it = textures.find(filename); @@ -1365,6 +1371,17 @@ bool OSLRenderServices::environment(ustring filename, return status; } +#if OSL_LIBRARY_VERSION_CODE >= 11100 +bool OSLRenderServices::get_texture_info(ustring filename, + TextureHandle *texture_handle, + TexturePerthread *, + OSL::ShadingContext *, + int subimage, + ustring dataname, + TypeDesc datatype, + void *data, + ustring *) +#else bool OSLRenderServices::get_texture_info(OSL::ShaderGlobals *sg, ustring filename, TextureHandle *texture_handle, @@ -1372,6 +1389,7 @@ bool OSLRenderServices::get_texture_info(OSL::ShaderGlobals *sg, ustring dataname, TypeDesc datatype, void *data) +#endif { OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle; diff --git a/intern/cycles/kernel/osl/osl_services.h b/intern/cycles/kernel/osl/osl_services.h index d32dace23bf..894d6e471ba 100644 --- a/intern/cycles/kernel/osl/osl_services.h +++ b/intern/cycles/kernel/osl/osl_services.h @@ -173,7 +173,12 @@ class OSLRenderServices : public OSL::RendererServices { void *val, bool derivatives) override; +#if OSL_LIBRARY_VERSION_CODE >= 11100 + TextureSystem::TextureHandle *get_texture_handle(ustring filename, + OSL::ShadingContext *context) override; +#else TextureSystem::TextureHandle *get_texture_handle(ustring filename) override; +#endif bool good(TextureSystem::TextureHandle *texture_handle) override; @@ -224,6 +229,17 @@ class OSLRenderServices : public OSL::RendererServices { float *dresultdt, ustring *errormessage) override; +#if OSL_LIBRARY_VERSION_CODE >= 11100 + bool get_texture_info(ustring filename, + TextureHandle *texture_handle, + TexturePerthread *texture_thread_info, + OSL::ShadingContext *shading_context, + int subimage, + ustring dataname, + TypeDesc datatype, + void *data, + ustring *errormessage) override; +#else bool get_texture_info(OSL::ShaderGlobals *sg, ustring filename, TextureHandle *texture_handle, @@ -231,6 +247,7 @@ class OSLRenderServices : public OSL::RendererServices { ustring dataname, TypeDesc datatype, void *data) override; +#endif static bool get_background_attribute( KernelGlobals *kg, ShaderData *sd, ustring name, TypeDesc type, bool derivatives, void *val); diff --git a/intern/cycles/kernel/svm/svm_voronoi.h b/intern/cycles/kernel/svm/svm_voronoi.h index 2ad22592eef..f0fc0068fa2 100644 --- a/intern/cycles/kernel/svm/svm_voronoi.h +++ b/intern/cycles/kernel/svm/svm_voronoi.h @@ -684,7 +684,8 @@ ccl_device void voronoi_f1_4d(float4 coord, float4 targetPosition = make_float4(0.0f, 0.0f, 0.0f, 0.0f); for (int u = -1; u <= 1; u++) { for (int k = -1; k <= 1; k++) { - for (int j = -1; j <= 1; j++) { + ccl_loop_no_unroll for (int j = -1; j <= 1; j++) + { for (int i = -1; i <= 1; i++) { float4 cellOffset = make_float4(i, j, k, u); float4 pointPosition = cellOffset + @@ -722,7 +723,8 @@ ccl_device void voronoi_smooth_f1_4d(float4 coord, float4 smoothPosition = make_float4(0.0f, 0.0f, 0.0f, 0.0f); for (int u = -2; u <= 2; u++) { for (int k = -2; k <= 2; k++) { - for (int j = -2; j <= 2; j++) { + ccl_loop_no_unroll for (int j = -2; j <= 2; j++) + { for (int i = -2; i <= 2; i++) { float4 cellOffset = make_float4(i, j, k, u); float4 pointPosition = cellOffset + @@ -765,7 +767,8 @@ ccl_device void voronoi_f2_4d(float4 coord, float4 positionF2 = make_float4(0.0f, 0.0f, 0.0f, 0.0f); for (int u = -1; u <= 1; u++) { for (int k = -1; k <= 1; k++) { - for (int j = -1; j <= 1; j++) { + ccl_loop_no_unroll for (int j = -1; j <= 1; j++) + { for (int i = -1; i <= 1; i++) { float4 cellOffset = make_float4(i, j, k, u); float4 pointPosition = cellOffset + @@ -803,7 +806,8 @@ ccl_device void voronoi_distance_to_edge_4d(float4 coord, float randomness, floa float minDistance = 8.0f; for (int u = -1; u <= 1; u++) { for (int k = -1; k <= 1; k++) { - for (int j = -1; j <= 1; j++) { + ccl_loop_no_unroll for (int j = -1; j <= 1; j++) + { for (int i = -1; i <= 1; i++) { float4 cellOffset = make_float4(i, j, k, u); float4 vectorToPoint = cellOffset + @@ -822,7 +826,8 @@ ccl_device void voronoi_distance_to_edge_4d(float4 coord, float randomness, floa minDistance = 8.0f; for (int u = -1; u <= 1; u++) { for (int k = -1; k <= 1; k++) { - for (int j = -1; j <= 1; j++) { + ccl_loop_no_unroll for (int j = -1; j <= 1; j++) + { for (int i = -1; i <= 1; i++) { float4 cellOffset = make_float4(i, j, k, u); float4 vectorToPoint = cellOffset + @@ -851,7 +856,8 @@ ccl_device void voronoi_n_sphere_radius_4d(float4 coord, float randomness, float float minDistance = 8.0f; for (int u = -1; u <= 1; u++) { for (int k = -1; k <= 1; k++) { - for (int j = -1; j <= 1; j++) { + ccl_loop_no_unroll for (int j = -1; j <= 1; j++) + { for (int i = -1; i <= 1; i++) { float4 cellOffset = make_float4(i, j, k, u); float4 pointPosition = cellOffset + @@ -871,7 +877,8 @@ ccl_device void voronoi_n_sphere_radius_4d(float4 coord, float randomness, float float4 closestPointToClosestPoint = make_float4(0.0f, 0.0f, 0.0f, 0.0f); for (int u = -1; u <= 1; u++) { for (int k = -1; k <= 1; k++) { - for (int j = -1; j <= 1; j++) { + ccl_loop_no_unroll for (int j = -1; j <= 1; j++) + { for (int i = -1; i <= 1; i++) { if (i == 0 && j == 0 && k == 0 && u == 0) { continue; diff --git a/intern/cycles/render/mesh_volume.cpp b/intern/cycles/render/mesh_volume.cpp index 74b8fc9e5ba..607363d01c6 100644 --- a/intern/cycles/render/mesh_volume.cpp +++ b/intern/cycles/render/mesh_volume.cpp @@ -19,6 +19,7 @@ #include "render/scene.h" #include "util/util_foreach.h" +#include "util/util_hash.h" #include "util/util_logging.h" #include "util/util_progress.h" #include "util/util_types.h" @@ -447,7 +448,14 @@ void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress) start_point = transform_point(&itfm, start_point); cell_size = transform_direction(&itfm, cell_size); - volume_params.start_point = start_point; + /* Slightly offset vertex coordinates to avoid overlapping faces with other + * volumes or meshes. The proper solution would be to improve intersection in + * the kernel to support robust handling of multiple overlapping faces or use + * an all-hit intersection similar to shadows. */ + const float3 face_overlap_avoidance = cell_size * 0.1f * + hash_uint_to_float(hash_string(mesh->name.c_str())); + + volume_params.start_point = start_point + face_overlap_avoidance; volume_params.cell_size = cell_size; volume_params.pad_size = pad_size; diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 58bcc7ccdfb..f7df81a0601 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -293,14 +293,12 @@ void Session::run_gpu() * reset and draw in between */ thread_scoped_lock buffers_lock(buffers_mutex); - /* avoid excessive denoising in viewport after reaching a certain amount of samples */ - bool need_denoise = tile_manager.schedule_denoising || tile_manager.state.sample < 20 || - (time_dt() - last_display_time) >= params.progressive_update_timeout; - /* update status and timing */ update_status_time(); /* render */ + bool delayed_denoise = false; + const bool need_denoise = render_need_denoise(delayed_denoise); render(need_denoise); device->task_wait(); @@ -311,7 +309,7 @@ void Session::run_gpu() /* update status and timing */ update_status_time(); - gpu_need_display_buffer_update = need_denoise || !params.run_denoising; + gpu_need_display_buffer_update = !delayed_denoise; gpu_draw_ready = true; progress.set_update(); @@ -477,7 +475,7 @@ void Session::update_tile_sample(RenderTile &rtile) update_status_time(); } -void Session::release_tile(RenderTile &rtile) +void Session::release_tile(RenderTile &rtile, const bool need_denoise) { thread_scoped_lock tile_lock(tile_mutex); @@ -485,7 +483,7 @@ void Session::release_tile(RenderTile &rtile) bool delete_tile; - if (tile_manager.finish_tile(rtile.tile_index, delete_tile)) { + if (tile_manager.finish_tile(rtile.tile_index, need_denoise, delete_tile)) { if (write_render_tile_cb && params.progressive_refine == false) { write_render_tile_cb(rtile); } @@ -687,21 +685,19 @@ void Session::run_cpu() * reset and draw in between */ thread_scoped_lock buffers_lock(buffers_mutex); - /* avoid excessive denoising in viewport after reaching a certain amount of samples */ - bool need_denoise = tile_manager.schedule_denoising || tile_manager.state.sample < 20 || - (time_dt() - last_display_time) >= params.progressive_update_timeout; - /* update status and timing */ update_status_time(); /* render */ + bool delayed_denoise = false; + const bool need_denoise = render_need_denoise(delayed_denoise); render(need_denoise); /* update status and timing */ update_status_time(); if (!params.background) - need_copy_to_display_buffer = need_denoise || !params.run_denoising; + need_copy_to_display_buffer = !delayed_denoise; if (!device->error_message().empty()) progress.set_error(device->error_message()); @@ -1083,7 +1079,46 @@ void Session::update_status_time(bool show_pause, bool show_done) progress.set_status(status, substatus); } -void Session::render(bool with_denoising) +bool Session::render_need_denoise(bool &delayed) +{ + delayed = false; + + /* Denoising enabled? */ + if (!params.run_denoising) { + return false; + } + + if (params.background) { + /* Background render, only denoise when rendering the last sample. */ + return tile_manager.done(); + } + + /* Viewport render. */ + + /* It can happen that denoising was already enabled, but the scene still needs an update. */ + if (scene->film->need_update || !scene->film->denoising_data_offset) { + return false; + } + + /* Do not denoise until the sample at which denoising should start is reached. */ + if (tile_manager.state.sample < params.denoising_start_sample) { + return false; + } + + /* Cannot denoise with resolution divider and separate denoising devices. + * It breaks the copy in 'MultiDevice::map_neighbor_tiles' (which operates on + * the full buffer dimensions and not the scaled ones). */ + if (!params.device.denoising_devices.empty() && tile_manager.state.resolution_divider > 1) { + return false; + } + + /* Avoid excessive denoising in viewport after reaching a certain amount of samples. */ + delayed = (tile_manager.state.sample >= 20 && + (time_dt() - last_display_time) < params.progressive_update_timeout); + return !delayed; +} + +void Session::render(bool need_denoise) { if (buffers && tile_manager.state.sample == tile_manager.range_start_sample) { /* Clear buffers. */ @@ -1098,7 +1133,7 @@ void Session::render(bool with_denoising) DeviceTask task(DeviceTask::RENDER); task.acquire_tile = function_bind(&Session::acquire_tile, this, _2, _1, _3); - task.release_tile = function_bind(&Session::release_tile, this, _1); + task.release_tile = function_bind(&Session::release_tile, this, _1, need_denoise); task.map_neighbor_tiles = function_bind(&Session::map_neighbor_tiles, this, _1, _2); task.unmap_neighbor_tiles = function_bind(&Session::unmap_neighbor_tiles, this, _1, _2); task.get_cancel = function_bind(&Progress::get_cancel, &this->progress); @@ -1115,27 +1150,7 @@ void Session::render(bool with_denoising) /* Acquire render tiles by default. */ task.tile_types = RenderTile::PATH_TRACE; - with_denoising = params.run_denoising && with_denoising; - if (with_denoising) { - /* Do not denoise viewport until the sample at which denoising should start is reached. */ - if (!params.background && tile_manager.state.sample < params.denoising_start_sample) { - with_denoising = false; - } - - /* Cannot denoise with resolution divider and separate denoising devices. - * It breaks the copy in 'MultiDevice::map_neighbor_tiles' (which operates on the full buffer - * dimensions and not the scaled ones). */ - if (!params.device.denoising_devices.empty() && tile_manager.state.resolution_divider > 1) { - with_denoising = false; - } - - /* It can happen that denoising was already enabled, but the scene still needs an update. */ - if (scene->film->need_update || !scene->film->denoising_data_offset) { - with_denoising = false; - } - } - - if (with_denoising) { + if (need_denoise) { task.denoising = params.denoising; task.pass_stride = scene->film->pass_stride; diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h index 61970d87e9c..f06952e8020 100644 --- a/intern/cycles/render/session.h +++ b/intern/cycles/render/session.h @@ -186,7 +186,7 @@ class Session { void update_status_time(bool show_pause = false, bool show_done = false); - void render(bool with_denoising); + void render(bool use_denoise); void copy_to_display_buffer(int sample); void reset_(BufferParams ¶ms, int samples); @@ -199,9 +199,11 @@ class Session { bool draw_gpu(BufferParams ¶ms, DeviceDrawParams &draw_params); void reset_gpu(BufferParams ¶ms, int samples); + bool render_need_denoise(bool &delayed); + bool acquire_tile(RenderTile &tile, Device *tile_device, uint tile_types); void update_tile_sample(RenderTile &tile); - void release_tile(RenderTile &tile); + void release_tile(RenderTile &tile, const bool need_denoise); void map_neighbor_tiles(RenderTile *tiles, Device *tile_device); void unmap_neighbor_tiles(RenderTile *tiles, Device *tile_device); diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/render/tile.cpp index 1480b6d1aab..375c9fd8e09 100644 --- a/intern/cycles/render/tile.cpp +++ b/intern/cycles/render/tile.cpp @@ -441,13 +441,13 @@ bool TileManager::check_neighbor_state(int index, Tile::State min_state) /* Returns whether the tile should be written (and freed if no denoising is used) instead of * updating. */ -bool TileManager::finish_tile(int index, bool &delete_tile) +bool TileManager::finish_tile(const int index, const bool need_denoise, bool &delete_tile) { delete_tile = false; switch (state.tiles[index].state) { case Tile::RENDER: { - if (!schedule_denoising) { + if (!(schedule_denoising && need_denoise)) { state.tiles[index].state = Tile::DONE; delete_tile = !progressive; return true; diff --git a/intern/cycles/render/tile.h b/intern/cycles/render/tile.h index 9fb9c1ca782..4858a275d5c 100644 --- a/intern/cycles/render/tile.h +++ b/intern/cycles/render/tile.h @@ -107,7 +107,7 @@ class TileManager { void set_samples(int num_samples); bool next(); bool next_tile(Tile *&tile, int device, uint tile_types); - bool finish_tile(int index, bool &delete_tile); + bool finish_tile(const int index, const bool need_denoise, bool &delete_tile); bool done(); bool has_tiles(); diff --git a/intern/cycles/util/util_defines.h b/intern/cycles/util/util_defines.h index 24a20a969ab..e8e414587fb 100644 --- a/intern/cycles/util/util_defines.h +++ b/intern/cycles/util/util_defines.h @@ -45,6 +45,7 @@ # define ccl_restrict __restrict # define ccl_ref & # define ccl_optional_struct_init +# define ccl_loop_no_unroll # define __KERNEL_WITH_SSE_ALIGN__ # if defined(_WIN32) && !defined(FREE_WINDOWS) diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index 68fc9637e02..699ac4afe88 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -21,7 +21,6 @@ set(INC . ../glew-mx - ../string ../../source/blender/imbuf ../../source/blender/makesdna ) @@ -86,7 +85,6 @@ set(SRC set(LIB bf_intern_glew_mx - bf_intern_string ${GLEW_LIBRARY} ) @@ -151,7 +149,7 @@ if(WITH_HEADLESS OR WITH_GHOST_SDL) endif() endif() -elseif(APPLE AND NOT WITH_X11) +elseif(APPLE AND NOT WITH_GHOST_X11) list(APPEND SRC intern/GHOST_DisplayManagerCocoa.mm intern/GHOST_SystemCocoa.mm @@ -179,73 +177,143 @@ elseif(APPLE AND NOT WITH_X11) ) endif() -elseif(WITH_X11) - list(APPEND INC_SYS - ${X11_X11_INCLUDE_PATH} - ) - - list(APPEND SRC - intern/GHOST_DisplayManagerX11.cpp - intern/GHOST_SystemX11.cpp - intern/GHOST_TaskbarX11.cpp - intern/GHOST_WindowX11.cpp - - intern/GHOST_DisplayManagerX11.h - intern/GHOST_IconX11.h - intern/GHOST_SystemX11.h - intern/GHOST_TaskbarX11.h - intern/GHOST_WindowX11.h - ) +elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND) + if(WITH_GHOST_X11) + list(APPEND INC_SYS + ${X11_X11_INCLUDE_PATH} + ) - if(NOT WITH_GL_EGL) list(APPEND SRC - intern/GHOST_ContextGLX.cpp - - intern/GHOST_ContextGLX.h + intern/GHOST_DisplayManagerX11.cpp + intern/GHOST_SystemX11.cpp + intern/GHOST_TaskbarX11.cpp + intern/GHOST_WindowX11.cpp + + intern/GHOST_DisplayManagerX11.h + intern/GHOST_IconX11.h + intern/GHOST_SystemX11.h + intern/GHOST_TaskbarX11.h + intern/GHOST_WindowX11.h ) - endif() - if(WITH_GHOST_XDND) - add_definitions(-DWITH_XDND) + if(NOT WITH_GL_EGL) + list(APPEND SRC + intern/GHOST_ContextGLX.cpp - list(APPEND LIB - extern_xdnd - ) + intern/GHOST_ContextGLX.h + ) + endif() - list(APPEND INC - ../../extern/xdnd - ) + if(WITH_GHOST_XDND) + add_definitions(-DWITH_XDND) - list(APPEND SRC - intern/GHOST_DropTargetX11.cpp + list(APPEND LIB + extern_xdnd + ) - intern/GHOST_DropTargetX11.h - ) + list(APPEND INC + ../../extern/xdnd + ) + + list(APPEND SRC + intern/GHOST_DropTargetX11.cpp + + intern/GHOST_DropTargetX11.h + ) + endif() + + if(X11_XF86keysym_INCLUDE_PATH) + add_definitions(-DWITH_XF86KEYSYM) + list(APPEND INC_SYS + ${X11_XF86keysym_INCLUDE_PATH} + ) + endif() + + if(WITH_X11_XF86VMODE) + add_definitions(-DWITH_X11_XF86VMODE) + list(APPEND INC_SYS + ${X11_xf86vmode_INCLUDE_PATH} + ) + endif() + + if(WITH_X11_XFIXES) + add_definitions(-DWITH_X11_XFIXES) + list(APPEND INC_SYS + ${X11_Xfixes_INCLUDE_PATH} + ) + endif() + + if(WITH_X11_ALPHA) + add_definitions(-DWITH_X11_ALPHA) + endif() + + if(WITH_X11_XINPUT) + add_definitions(-DWITH_X11_XINPUT) + list(APPEND INC_SYS + ${X11_Xinput_INCLUDE_PATH} + ) + endif() + + add_definitions(-DWITH_GHOST_X11) endif() - if(X11_XF86keysym_INCLUDE_PATH) - add_definitions(-DWITH_XF86KEYSYM) + if(WITH_GHOST_WAYLAND) list(APPEND INC_SYS - ${X11_XF86keysym_INCLUDE_PATH} + ${wayland-client_INCLUDE_DIRS} + ${wayland-egl_INCLUDE_DIRS} + ${xkbcommon_INCLUDE_DIRS} + ${wayland-cursor_INCLUDE_DIRS} ) - endif() - if(WITH_X11_XF86VMODE) - add_definitions(-DWITH_X11_XF86VMODE) - list(APPEND INC_SYS - ${X11_xf86vmode_INCLUDE_PATH} + list(APPEND SRC + intern/GHOST_SystemWayland.cpp + intern/GHOST_WindowWayland.cpp + + intern/GHOST_SystemWayland.h + intern/GHOST_WindowWayland.h ) - endif() - if(WITH_X11_XFIXES) - add_definitions(-DWITH_X11_XFIXES) + pkg_get_variable(WAYLAND_SCANNER wayland-scanner wayland_scanner) + pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir) + + # Generate protocols bindings. + macro(generate_protocol_bindings NAME PROT_DEF) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${NAME}-client-protocol.h + COMMAND ${WAYLAND_SCANNER} client-header ${PROT_DEF} ${NAME}-client-protocol.h + ) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${NAME}-client-protocol.c + COMMAND ${WAYLAND_SCANNER} private-code ${PROT_DEF} ${NAME}-client-protocol.c + DEPENDS ${NAME}-client-protocol.h + ) + list(APPEND SRC + ${CMAKE_CURRENT_BINARY_DIR}/${NAME}-client-protocol.c + ${CMAKE_CURRENT_BINARY_DIR}/${NAME}-client-protocol.h + ) + endmacro() + list(APPEND INC_SYS - ${X11_Xfixes_INCLUDE_PATH} + ${CMAKE_CURRENT_BINARY_DIR} + ) + + # xdg-shell. + generate_protocol_bindings( + xdg-shell + "${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml" + ) + # Pointer-constraints. + generate_protocol_bindings( + pointer-constraints + "${WAYLAND_PROTOCOLS_DIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" + ) + # Relative-pointer. + generate_protocol_bindings( + relative-pointer + "${WAYLAND_PROTOCOLS_DIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml" ) - endif() - if(WITH_X11_ALPHA) - add_definitions(-DWITH_X11_ALPHA) + add_definitions(-DWITH_GHOST_WAYLAND) endif() if(WITH_INPUT_NDOF) @@ -260,14 +328,6 @@ elseif(WITH_X11) add_definitions(-DPREFIX="${CMAKE_INSTALL_PREFIX}") endif() - if(WITH_X11_XINPUT) - add_definitions(-DWITH_X11_XINPUT) - list(APPEND INC_SYS - ${X11_Xinput_INCLUDE_PATH} - ) - endif() - - add_definitions(-DWITH_X11) elseif(WIN32) # # Warnings as errors, this is too strict! diff --git a/intern/ghost/GHOST_IContext.h b/intern/ghost/GHOST_IContext.h index 1225262a908..8c24261644a 100644 --- a/intern/ghost/GHOST_IContext.h +++ b/intern/ghost/GHOST_IContext.h @@ -26,7 +26,6 @@ #define __GHOST_IContext_H__ #include "GHOST_Types.h" -#include "STR_String.h" /** * Interface for GHOST context. diff --git a/intern/ghost/GHOST_ISystem.h b/intern/ghost/GHOST_ISystem.h index 58d1a08da74..9b619f5c684 100644 --- a/intern/ghost/GHOST_ISystem.h +++ b/intern/ghost/GHOST_ISystem.h @@ -27,6 +27,8 @@ #ifndef __GHOST_ISYSTEM_H__ #define __GHOST_ISYSTEM_H__ +#include <stdlib.h> + #include "GHOST_IContext.h" #include "GHOST_ITimerTask.h" #include "GHOST_IWindow.h" @@ -240,7 +242,7 @@ class GHOST_ISystem { * \param parentWindow: Parent (embedder) window * \return The new window (or 0 if creation failed). */ - virtual GHOST_IWindow *createWindow(const STR_String &title, + virtual GHOST_IWindow *createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, diff --git a/intern/ghost/GHOST_IWindow.h b/intern/ghost/GHOST_IWindow.h index daf07b81e01..62290d20f1c 100644 --- a/intern/ghost/GHOST_IWindow.h +++ b/intern/ghost/GHOST_IWindow.h @@ -27,7 +27,9 @@ #include "GHOST_Rect.h" #include "GHOST_Types.h" -#include "STR_String.h" + +#include <stdlib.h> +#include <string> /** * Interface for GHOST windows. @@ -81,13 +83,13 @@ class GHOST_IWindow { * Sets the title displayed in the title bar. * \param title The title to display in the title bar. */ - virtual void setTitle(const STR_String &title) = 0; + virtual void setTitle(const char *title) = 0; /** * Returns the title displayed in the title bar. * \param title The title displayed in the title bar. */ - virtual void getTitle(STR_String &title) const = 0; + virtual std::string getTitle() const = 0; /** * Returns the window rectangle dimensions. diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index 1c95814f0d9..843684b6d2e 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -24,6 +24,7 @@ */ #include <stdlib.h> +#include <string.h> #include "GHOST_C-api.h" #include "GHOST_IEvent.h" @@ -527,17 +528,15 @@ void GHOST_SetTitle(GHOST_WindowHandle windowhandle, const char *title) char *GHOST_GetTitle(GHOST_WindowHandle windowhandle) { GHOST_IWindow *window = (GHOST_IWindow *)windowhandle; - STR_String title; + std::string title = window->getTitle(); - window->getTitle(title); - - char *ctitle = (char *)malloc(title.Length() + 1); + char *ctitle = (char *)malloc(title.size() + 1); if (ctitle == NULL) { return NULL; } - strcpy(ctitle, title.Ptr()); + strcpy(ctitle, title.c_str()); return ctitle; } diff --git a/intern/ghost/intern/GHOST_Debug.h b/intern/ghost/intern/GHOST_Debug.h index 0163197e14a..5b5c2688297 100644 --- a/intern/ghost/intern/GHOST_Debug.h +++ b/intern/ghost/intern/GHOST_Debug.h @@ -33,15 +33,11 @@ #endif #ifdef WITH_GHOST_DEBUG -# define GHOST_DEBUG // spit ghost events to stdout -#endif // WITH_GHOST_DEBUG - -#ifdef GHOST_DEBUG # include <iostream> # include <stdio.h> //for printf() -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG # define GHOST_PRINT(x) \ { \ std::cout << x; \ @@ -52,10 +48,10 @@ printf(x, __VA_ARGS__); \ } \ (void)0 -#else // GHOST_DEBUG +#else // WITH_GHOST_DEBUG # define GHOST_PRINT(x) # define GHOST_PRINTF(x, ...) -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG #ifdef WITH_ASSERT_ABORT # include <stdio.h> //for fprintf() @@ -70,7 +66,7 @@ } \ } \ (void)0 -#elif defined(GHOST_DEBUG) +#elif defined(WITH_GHOST_DEBUG) # define GHOST_ASSERT(x, info) \ { \ if (!(x)) { \ @@ -80,8 +76,8 @@ } \ } \ (void)0 -#else // GHOST_DEBUG +#else // WITH_GHOST_DEBUG # define GHOST_ASSERT(x, info) ((void)0) -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG #endif // __GHOST_DEBUG_H__ diff --git a/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp b/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp index aabaffc7732..3557c4cd0c5 100644 --- a/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp +++ b/intern/ghost/intern/GHOST_DisplayManagerWin32.cpp @@ -80,13 +80,13 @@ GHOST_TSuccess GHOST_DisplayManagerWin32::getDisplaySetting(GHOST_TUns8 display, GHOST_TSuccess success; DEVMODE dm; if (::EnumDisplaySettings(display_device.DeviceName, index, &dm)) { -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG printf("display mode: width=%d, height=%d, bpp=%d, frequency=%d\n", dm.dmPelsWidth, dm.dmPelsHeight, dm.dmBitsPerPel, dm.dmDisplayFrequency); -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG setting.xPixels = dm.dmPelsWidth; setting.yPixels = dm.dmPelsHeight; setting.bpp = dm.dmBitsPerPel; @@ -142,16 +142,16 @@ GHOST_TSuccess GHOST_DisplayManagerWin32::setCurrentDisplaySetting( * dm.dmSize = sizeof(DEVMODE); * dm.dmDriverExtra = 0; */ -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG printf("display change: Requested settings:\n"); printf(" dmBitsPerPel=%d\n", dm.dmBitsPerPel); printf(" dmPelsWidth=%d\n", dm.dmPelsWidth); printf(" dmPelsHeight=%d\n", dm.dmPelsHeight); printf(" dmDisplayFrequency=%d\n", dm.dmDisplayFrequency); -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG LONG status = ::ChangeDisplaySettings(&dm, CDS_FULLSCREEN); -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG switch (status) { case DISP_CHANGE_SUCCESSFUL: printf("display change: The settings change was successful.\n"); @@ -182,6 +182,6 @@ GHOST_TSuccess GHOST_DisplayManagerWin32::setCurrentDisplaySetting( printf("display change: Return value invalid\n"); break; } -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG return status == DISP_CHANGE_SUCCESSFUL ? GHOST_kSuccess : GHOST_kFailure; } diff --git a/intern/ghost/intern/GHOST_DropTargetWin32.cpp b/intern/ghost/intern/GHOST_DropTargetWin32.cpp index 9f8ce3b5095..fe11d9a28f2 100644 --- a/intern/ghost/intern/GHOST_DropTargetWin32.cpp +++ b/intern/ghost/intern/GHOST_DropTargetWin32.cpp @@ -28,10 +28,10 @@ #include "utf_winfunc.h" #include "utfconv.h" -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG // utility void printLastError(void); -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG GHOST_DropTargetWin32::GHOST_DropTargetWin32(GHOST_WindowWin32 *window, GHOST_SystemWin32 *system) : m_window(window), m_system(system) @@ -209,9 +209,9 @@ void *GHOST_DropTargetWin32::getGhostData(IDataObject *pDataObject) // return getDropDataAsBitmap(pDataObject); break; default: -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG ::printf("\nGHOST_kDragnDropTypeUnknown"); -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG return NULL; break; } @@ -284,10 +284,10 @@ void *GHOST_DropTargetWin32::getDropDataAsString(IDataObject *pDataObject) // Free memory ::GlobalUnlock(stgmed.hGlobal); ::ReleaseStgMedium(&stgmed); -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG ::printf("\n<converted droped unicode string>\n%s\n</droped converted unicode string>\n", tmp_string); -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG return tmp_string; } } @@ -336,9 +336,9 @@ int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char *&out) NULL); if (!size) { -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG ::printLastError(); -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG return 0; } @@ -351,16 +351,16 @@ int GHOST_DropTargetWin32::WideCharToANSI(LPCWSTR in, char *&out) size = ::WideCharToMultiByte(CP_ACP, 0x00000400, in, -1, (LPSTR)out, size, NULL, NULL); if (!size) { -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG ::printLastError(); -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG ::free(out); out = NULL; } return size; } -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG void printLastError(void) { LPTSTR s; @@ -378,4 +378,4 @@ void printLastError(void) LocalFree(s); } } -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG diff --git a/intern/ghost/intern/GHOST_EventKey.h b/intern/ghost/intern/GHOST_EventKey.h index 24e20b20659..8f59c555914 100644 --- a/intern/ghost/intern/GHOST_EventKey.h +++ b/intern/ghost/intern/GHOST_EventKey.h @@ -25,6 +25,8 @@ #ifndef __GHOST_EVENTKEY_H__ #define __GHOST_EVENTKEY_H__ +#include <string.h> + #include "GHOST_Event.h" /** diff --git a/intern/ghost/intern/GHOST_EventPrinter.h b/intern/ghost/intern/GHOST_EventPrinter.h index fad9ec3cc69..ead16525ec6 100644 --- a/intern/ghost/intern/GHOST_EventPrinter.h +++ b/intern/ghost/intern/GHOST_EventPrinter.h @@ -27,8 +27,6 @@ #include "GHOST_IEventConsumer.h" -#include "STR_String.h" - /** * An Event consumer that prints all the events to standard out. * Really useful when debugging. diff --git a/intern/ghost/intern/GHOST_ISystem.cpp b/intern/ghost/intern/GHOST_ISystem.cpp index 914f6712676..7c12bfe0306 100644 --- a/intern/ghost/intern/GHOST_ISystem.cpp +++ b/intern/ghost/intern/GHOST_ISystem.cpp @@ -27,20 +27,22 @@ #include "GHOST_ISystem.h" -#ifdef WITH_X11 +#if defined(WITH_HEADLESS) +# include "GHOST_SystemNULL.h" +#elif defined(WITH_GHOST_X11) && defined(WITH_GHOST_WAYLAND) +# include "GHOST_SystemWayland.h" # include "GHOST_SystemX11.h" -#else -# ifdef WITH_HEADLESS -# include "GHOST_SystemNULL.h" -# elif defined(WITH_GHOST_SDL) -# include "GHOST_SystemSDL.h" -# elif defined(WIN32) -# include "GHOST_SystemWin32.h" -# else -# ifdef __APPLE__ -# include "GHOST_SystemCocoa.h" -# endif -# endif +# include <stdexcept> +#elif defined(WITH_GHOST_X11) +# include "GHOST_SystemX11.h" +#elif defined(WITH_GHOST_WAYLAND) +# include "GHOST_SystemWayland.h" +#elif defined(WITH_GHOST_SDL) +# include "GHOST_SystemSDL.h" +#elif defined(WIN32) +# include "GHOST_SystemWin32.h" +#elif defined(__APPLE__) +# include "GHOST_SystemCocoa.h" #endif GHOST_ISystem *GHOST_ISystem::m_system = NULL; @@ -49,20 +51,29 @@ GHOST_TSuccess GHOST_ISystem::createSystem() { GHOST_TSuccess success; if (!m_system) { -#ifdef WITH_X11 - m_system = new GHOST_SystemX11(); -#else -# ifdef WITH_HEADLESS +#if defined(WITH_HEADLESS) m_system = new GHOST_SystemNULL(); -# elif defined(WITH_GHOST_SDL) +#elif defined(WITH_GHOST_X11) && defined(WITH_GHOST_WAYLAND) + /* Special case, try Wayland, fall back to X11. */ + try { + m_system = new GHOST_SystemWayland(); + } + catch (const std::runtime_error &) { + /* fallback to X11. */ + } + if (!m_system) { + m_system = new GHOST_SystemX11(); + } +#elif defined(WITH_GHOST_X11) + m_system = new GHOST_SystemX11(); +#elif defined(WITH_GHOST_WAYLAND) + m_system = new GHOST_SystemWayland(); +#elif defined(WITH_GHOST_SDL) m_system = new GHOST_SystemSDL(); -# elif defined(WIN32) +#elif defined(WIN32) m_system = new GHOST_SystemWin32(); -# else -# ifdef __APPLE__ +#elif defined(__APPLE__) m_system = new GHOST_SystemCocoa(); -# endif -# endif #endif success = m_system != NULL ? GHOST_kSuccess : GHOST_kFailure; } diff --git a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h index cddb557d163..5794a682023 100644 --- a/intern/ghost/intern/GHOST_IXrGraphicsBinding.h +++ b/intern/ghost/intern/GHOST_IXrGraphicsBinding.h @@ -33,7 +33,7 @@ class GHOST_IXrGraphicsBinding { public: union { -#if defined(WITH_X11) +#if defined(WITH_GHOST_X11) XrGraphicsBindingOpenGLXlibKHR glx; #elif defined(WIN32) XrGraphicsBindingOpenGLWin32KHR wgl; diff --git a/intern/ghost/intern/GHOST_NDOFManager.cpp b/intern/ghost/intern/GHOST_NDOFManager.cpp index a9fbadab37a..dda78c0ac5b 100644 --- a/intern/ghost/intern/GHOST_NDOFManager.cpp +++ b/intern/ghost/intern/GHOST_NDOFManager.cpp @@ -19,6 +19,8 @@ #include "GHOST_EventKey.h" #include "GHOST_EventNDOF.h" #include "GHOST_WindowManager.h" + +#include <limits.h> #include <math.h> #include <stdio.h> // for error/info reporting #include <string.h> // for memory functions diff --git a/intern/ghost/intern/GHOST_System.cpp b/intern/ghost/intern/GHOST_System.cpp index 85eb6d58679..587e4c28102 100644 --- a/intern/ghost/intern/GHOST_System.cpp +++ b/intern/ghost/intern/GHOST_System.cpp @@ -309,12 +309,12 @@ GHOST_TSuccess GHOST_System::init() m_windowManager = new GHOST_WindowManager(); m_eventManager = new GHOST_EventManager(); -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG if (m_eventManager) { m_eventPrinter = new GHOST_EventPrinter(); m_eventManager->addConsumer(m_eventPrinter); } -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG if (m_timerManager && m_windowManager && m_eventManager) { return GHOST_kSuccess; @@ -367,7 +367,7 @@ GHOST_TSuccess GHOST_System::createFullScreenWindow(GHOST_Window **window, GHOST_ASSERT(m_displayManager, "GHOST_System::createFullScreenWindow(): invalid display manager"); // GHOST_PRINT("GHOST_System::createFullScreenWindow(): creating full-screen window\n"); - *window = (GHOST_Window *)createWindow(STR_String(""), + *window = (GHOST_Window *)createWindow("", 0, 0, settings.xPixels, diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h index eaaa2ff6ee6..0f58be49dff 100644 --- a/intern/ghost/intern/GHOST_System.h +++ b/intern/ghost/intern/GHOST_System.h @@ -31,9 +31,9 @@ #include "GHOST_Debug.h" #include "GHOST_EventManager.h" #include "GHOST_ModifierKeys.h" -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG # include "GHOST_EventPrinter.h" -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG class GHOST_DisplayManager; class GHOST_Event; @@ -390,9 +390,9 @@ class GHOST_System : public GHOST_ISystem { #endif /** Prints all the events. */ -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG GHOST_EventPrinter *m_eventPrinter; -#endif // GHOST_DEBUG +#endif // WITH_GHOST_DEBUG /** Settings of the display before the display went fullscreen. */ GHOST_DisplaySetting m_preFullScreenSetting; diff --git a/intern/ghost/intern/GHOST_SystemCocoa.h b/intern/ghost/intern/GHOST_SystemCocoa.h index 1e44c3e31d4..d058697470a 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.h +++ b/intern/ghost/intern/GHOST_SystemCocoa.h @@ -100,7 +100,7 @@ class GHOST_SystemCocoa : public GHOST_System { * \param parentWindow Parent (embedder) window * \return The new window (or 0 if creation failed). */ - GHOST_IWindow *createWindow(const STR_String &title, + GHOST_IWindow *createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index 3d6d187587c..2b6a2902757 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -698,7 +698,7 @@ void GHOST_SystemCocoa::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns3 getMainDisplayDimensions(width, height); } -GHOST_IWindow *GHOST_SystemCocoa::createWindow(const STR_String &title, +GHOST_IWindow *GHOST_SystemCocoa::createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, diff --git a/intern/ghost/intern/GHOST_SystemNULL.h b/intern/ghost/intern/GHOST_SystemNULL.h index 68a726f2be8..186cb92d1aa 100644 --- a/intern/ghost/intern/GHOST_SystemNULL.h +++ b/intern/ghost/intern/GHOST_SystemNULL.h @@ -106,7 +106,7 @@ class GHOST_SystemNULL : public GHOST_System { return GHOST_kFailure; } - GHOST_IWindow *createWindow(const STR_String &title, + GHOST_IWindow *createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index 97a75d7a0f5..b32ec4306e8 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -49,7 +49,7 @@ GHOST_SystemSDL::~GHOST_SystemSDL() SDL_Quit(); } -GHOST_IWindow *GHOST_SystemSDL::createWindow(const STR_String &title, +GHOST_IWindow *GHOST_SystemSDL::createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, diff --git a/intern/ghost/intern/GHOST_SystemSDL.h b/intern/ghost/intern/GHOST_SystemSDL.h index 1994781530b..8feec9de61d 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.h +++ b/intern/ghost/intern/GHOST_SystemSDL.h @@ -80,7 +80,7 @@ class GHOST_SystemSDL : public GHOST_System { private: GHOST_TSuccess init(); - GHOST_IWindow *createWindow(const STR_String &title, + GHOST_IWindow *createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp new file mode 100644 index 00000000000..666515ed088 --- /dev/null +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -0,0 +1,1654 @@ +/* + * 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 GHOST + */ + +#include "GHOST_SystemWayland.h" +#include "GHOST_Event.h" +#include "GHOST_EventButton.h" +#include "GHOST_EventCursor.h" +#include "GHOST_EventDragnDrop.h" +#include "GHOST_EventKey.h" +#include "GHOST_EventWheel.h" +#include "GHOST_WindowManager.h" + +#include "GHOST_ContextEGL.h" + +#include <EGL/egl.h> +#include <wayland-egl.h> + +#include <algorithm> +#include <atomic> +#include <exception> +#include <thread> +#include <unordered_map> +#include <unordered_set> + +#include <pointer-constraints-client-protocol.h> +#include <relative-pointer-client-protocol.h> +#include <wayland-cursor.h> +#include <xkbcommon/xkbcommon.h> + +#include <fcntl.h> +#include <linux/input-event-codes.h> +#include <sys/mman.h> +#include <unistd.h> + +#include <cstring> + +struct output_t { + struct wl_output *output; + int32_t width, height; + int transform; + int scale; + std::string make; + std::string model; +}; + +struct buffer_t { + void *data; + size_t size; +}; + +struct cursor_t { + bool visible; + struct wl_surface *surface = nullptr; + struct wl_buffer *buffer; + struct wl_cursor_image image; + struct buffer_t *file_buffer = nullptr; +}; + +struct data_offer_t { + std::unordered_set<std::string> types; + uint32_t source_actions; + uint32_t dnd_action; + struct wl_data_offer *id; + std::atomic<bool> in_use; + struct { + int x, y; + } dnd; +}; + +struct data_source_t { + struct wl_data_source *data_source; + /** Last device that was active. */ + uint32_t source_serial; + char *buffer_out; +}; + +struct input_t { + GHOST_SystemWayland *system; + + std::string name; + struct wl_seat *seat; + struct wl_pointer *pointer = nullptr; + struct wl_keyboard *keyboard = nullptr; + + uint32_t pointer_serial; + int x, y; + GHOST_Buttons buttons; + struct cursor_t cursor; + + struct zwp_relative_pointer_v1 *relative_pointer; + struct zwp_locked_pointer_v1 *locked_pointer; + + struct xkb_context *xkb_context; + struct xkb_state *xkb_state; + + struct wl_data_device *data_device = nullptr; + struct data_offer_t *data_offer_dnd; /* Drag & Drop. */ + struct data_offer_t *data_offer_copy_paste; /* Copy & Paste. */ + + struct data_source_t *data_source; +}; + +struct display_t { + GHOST_SystemWayland *system; + + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor = nullptr; + struct xdg_wm_base *xdg_shell = nullptr; + struct wl_shm *shm = nullptr; + std::vector<output_t *> outputs; + std::vector<input_t *> inputs; + struct wl_cursor_theme *cursor_theme = nullptr; + struct wl_data_device_manager *data_device_manager = nullptr; + struct zwp_relative_pointer_manager_v1 *relative_pointer_manager = nullptr; + struct zwp_pointer_constraints_v1 *pointer_constraints = nullptr; + + std::vector<struct wl_surface *> os_surfaces; + std::vector<struct wl_egl_window *> os_egl_windows; +}; + +static void display_destroy(display_t *d) +{ + if (d->data_device_manager) { + wl_data_device_manager_destroy(d->data_device_manager); + } + + for (output_t *output : d->outputs) { + wl_output_destroy(output->output); + delete output; + } + + for (input_t *input : d->inputs) { + if (input->data_source) { + free(input->data_source->buffer_out); + if (input->data_source->data_source) { + wl_data_source_destroy(input->data_source->data_source); + } + delete input->data_source; + } + if (input->data_offer_copy_paste) { + wl_data_offer_destroy(input->data_offer_copy_paste->id); + delete input->data_offer_copy_paste; + } + if (input->data_device) { + wl_data_device_release(input->data_device); + } + if (input->pointer) { + if (input->cursor.file_buffer) { + munmap(input->cursor.file_buffer->data, input->cursor.file_buffer->size); + delete input->cursor.file_buffer; + } + if (input->cursor.surface) { + wl_surface_destroy(input->cursor.surface); + } + if (input->pointer) { + wl_pointer_destroy(input->pointer); + } + } + if (input->keyboard) { + wl_keyboard_destroy(input->keyboard); + } + if (input->xkb_state) { + xkb_state_unref(input->xkb_state); + } + if (input->xkb_context) { + xkb_context_unref(input->xkb_context); + } + wl_seat_destroy(input->seat); + delete input; + } + + if (d->cursor_theme) { + wl_cursor_theme_destroy(d->cursor_theme); + } + + if (d->shm) { + wl_shm_destroy(d->shm); + } + + if (d->relative_pointer_manager) { + zwp_relative_pointer_manager_v1_destroy(d->relative_pointer_manager); + } + + if (d->pointer_constraints) { + zwp_pointer_constraints_v1_destroy(d->pointer_constraints); + } + + for (wl_egl_window *os_egl_window : d->os_egl_windows) { + wl_egl_window_destroy(os_egl_window); + } + + for (wl_surface *os_surface : d->os_surfaces) { + wl_surface_destroy(os_surface); + } + + if (d->compositor) { + wl_compositor_destroy(d->compositor); + } + + if (d->xdg_shell) { + xdg_wm_base_destroy(d->xdg_shell); + } + + if (eglGetDisplay) { + ::eglTerminate(eglGetDisplay(EGLNativeDisplayType(d->display))); + } + + if (d->display) { + wl_display_disconnect(d->display); + } + + delete d; +} + +static GHOST_TKey xkb_map_gkey(const xkb_keysym_t &sym) +{ + + GHOST_TKey gkey; + if (sym >= XKB_KEY_0 && sym <= XKB_KEY_9) { + gkey = GHOST_TKey(sym); + } + else if (sym >= XKB_KEY_KP_0 && sym <= XKB_KEY_KP_9) { + gkey = GHOST_TKey(GHOST_kKeyNumpad0 + sym - XKB_KEY_KP_0); + } + else if (sym >= XKB_KEY_A && sym <= XKB_KEY_Z) { + gkey = GHOST_TKey(sym); + } + else if (sym >= XKB_KEY_a && sym <= XKB_KEY_z) { + gkey = GHOST_TKey(sym - XKB_KEY_a + XKB_KEY_A); + } + else if (sym >= XKB_KEY_F1 && sym <= XKB_KEY_F24) { + gkey = GHOST_TKey(GHOST_kKeyF1 + sym - XKB_KEY_F1); + } + else { + +#define GXMAP(k, x, y) \ + case x: \ + k = y; \ + break + + switch (sym) { + GXMAP(gkey, XKB_KEY_BackSpace, GHOST_kKeyBackSpace); + GXMAP(gkey, XKB_KEY_Tab, GHOST_kKeyTab); + GXMAP(gkey, XKB_KEY_Linefeed, GHOST_kKeyLinefeed); + GXMAP(gkey, XKB_KEY_Clear, GHOST_kKeyClear); + GXMAP(gkey, XKB_KEY_Return, GHOST_kKeyEnter); + + GXMAP(gkey, XKB_KEY_Escape, GHOST_kKeyEsc); + GXMAP(gkey, XKB_KEY_space, GHOST_kKeySpace); + GXMAP(gkey, XKB_KEY_comma, GHOST_kKeyComma); + GXMAP(gkey, XKB_KEY_minus, GHOST_kKeyMinus); + GXMAP(gkey, XKB_KEY_plus, GHOST_kKeyPlus); + GXMAP(gkey, XKB_KEY_period, GHOST_kKeyPeriod); + GXMAP(gkey, XKB_KEY_slash, GHOST_kKeySlash); + + GXMAP(gkey, XKB_KEY_semicolon, GHOST_kKeySemicolon); + GXMAP(gkey, XKB_KEY_equal, GHOST_kKeyEqual); + + GXMAP(gkey, XKB_KEY_bracketleft, GHOST_kKeyLeftBracket); + GXMAP(gkey, XKB_KEY_bracketright, GHOST_kKeyRightBracket); + GXMAP(gkey, XKB_KEY_backslash, GHOST_kKeyBackslash); + GXMAP(gkey, XKB_KEY_grave, GHOST_kKeyAccentGrave); + + GXMAP(gkey, XKB_KEY_Shift_L, GHOST_kKeyLeftShift); + GXMAP(gkey, XKB_KEY_Shift_R, GHOST_kKeyRightShift); + GXMAP(gkey, XKB_KEY_Control_L, GHOST_kKeyLeftControl); + GXMAP(gkey, XKB_KEY_Control_R, GHOST_kKeyRightControl); + GXMAP(gkey, XKB_KEY_Alt_L, GHOST_kKeyLeftAlt); + GXMAP(gkey, XKB_KEY_Alt_R, GHOST_kKeyRightAlt); + GXMAP(gkey, XKB_KEY_Super_L, GHOST_kKeyOS); + GXMAP(gkey, XKB_KEY_Super_R, GHOST_kKeyOS); + GXMAP(gkey, XKB_KEY_Menu, GHOST_kKeyApp); + + GXMAP(gkey, XKB_KEY_Caps_Lock, GHOST_kKeyCapsLock); + GXMAP(gkey, XKB_KEY_Num_Lock, GHOST_kKeyNumLock); + GXMAP(gkey, XKB_KEY_Scroll_Lock, GHOST_kKeyScrollLock); + + GXMAP(gkey, XKB_KEY_Left, GHOST_kKeyLeftArrow); + GXMAP(gkey, XKB_KEY_KP_Left, GHOST_kKeyLeftArrow); + GXMAP(gkey, XKB_KEY_Right, GHOST_kKeyRightArrow); + GXMAP(gkey, XKB_KEY_KP_Right, GHOST_kKeyRightArrow); + GXMAP(gkey, XKB_KEY_Up, GHOST_kKeyUpArrow); + GXMAP(gkey, XKB_KEY_KP_Up, GHOST_kKeyUpArrow); + GXMAP(gkey, XKB_KEY_Down, GHOST_kKeyDownArrow); + GXMAP(gkey, XKB_KEY_KP_Down, GHOST_kKeyDownArrow); + + GXMAP(gkey, XKB_KEY_Print, GHOST_kKeyPrintScreen); + GXMAP(gkey, XKB_KEY_Pause, GHOST_kKeyPause); + + GXMAP(gkey, XKB_KEY_Insert, GHOST_kKeyInsert); + GXMAP(gkey, XKB_KEY_KP_Insert, GHOST_kKeyInsert); + GXMAP(gkey, XKB_KEY_Delete, GHOST_kKeyDelete); + GXMAP(gkey, XKB_KEY_KP_Delete, GHOST_kKeyDelete); + GXMAP(gkey, XKB_KEY_Home, GHOST_kKeyHome); + GXMAP(gkey, XKB_KEY_KP_Home, GHOST_kKeyHome); + GXMAP(gkey, XKB_KEY_End, GHOST_kKeyEnd); + GXMAP(gkey, XKB_KEY_KP_End, GHOST_kKeyEnd); + GXMAP(gkey, XKB_KEY_Page_Up, GHOST_kKeyUpPage); + GXMAP(gkey, XKB_KEY_KP_Page_Up, GHOST_kKeyUpPage); + GXMAP(gkey, XKB_KEY_Page_Down, GHOST_kKeyDownPage); + GXMAP(gkey, XKB_KEY_KP_Page_Down, GHOST_kKeyDownPage); + + GXMAP(gkey, XKB_KEY_KP_Decimal, GHOST_kKeyNumpadPeriod); + GXMAP(gkey, XKB_KEY_KP_Enter, GHOST_kKeyNumpadEnter); + GXMAP(gkey, XKB_KEY_KP_Add, GHOST_kKeyNumpadPlus); + GXMAP(gkey, XKB_KEY_KP_Subtract, GHOST_kKeyNumpadMinus); + GXMAP(gkey, XKB_KEY_KP_Multiply, GHOST_kKeyNumpadAsterisk); + GXMAP(gkey, XKB_KEY_KP_Divide, GHOST_kKeyNumpadSlash); + + GXMAP(gkey, XKB_KEY_XF86AudioPlay, GHOST_kKeyMediaPlay); + GXMAP(gkey, XKB_KEY_XF86AudioStop, GHOST_kKeyMediaStop); + GXMAP(gkey, XKB_KEY_XF86AudioPrev, GHOST_kKeyMediaFirst); + GXMAP(gkey, XKB_KEY_XF86AudioNext, GHOST_kKeyMediaLast); + default: + GHOST_PRINT("unhandled key: " << sym << std::endl); + gkey = GHOST_kKeyUnknown; + } +#undef GXMAP + } + + return gkey; +} + +static const int default_cursor_size = 24; + +static const std::unordered_map<GHOST_TStandardCursor, std::string> cursors = { + {GHOST_kStandardCursorDefault, "left_ptr"}, + {GHOST_kStandardCursorRightArrow, "right_ptr"}, + {GHOST_kStandardCursorLeftArrow, "left_ptr"}, + {GHOST_kStandardCursorInfo, ""}, + {GHOST_kStandardCursorDestroy, ""}, + {GHOST_kStandardCursorHelp, "question_arrow"}, + {GHOST_kStandardCursorWait, "watch"}, + {GHOST_kStandardCursorText, "xterm"}, + {GHOST_kStandardCursorCrosshair, "crosshair"}, + {GHOST_kStandardCursorCrosshairA, ""}, + {GHOST_kStandardCursorCrosshairB, ""}, + {GHOST_kStandardCursorCrosshairC, ""}, + {GHOST_kStandardCursorPencil, ""}, + {GHOST_kStandardCursorUpArrow, "sb_up_arrow"}, + {GHOST_kStandardCursorDownArrow, "sb_down_arrow"}, + {GHOST_kStandardCursorVerticalSplit, ""}, + {GHOST_kStandardCursorHorizontalSplit, ""}, + {GHOST_kStandardCursorEraser, ""}, + {GHOST_kStandardCursorKnife, ""}, + {GHOST_kStandardCursorEyedropper, ""}, + {GHOST_kStandardCursorZoomIn, ""}, + {GHOST_kStandardCursorZoomOut, ""}, + {GHOST_kStandardCursorMove, "move"}, + {GHOST_kStandardCursorNSEWScroll, ""}, + {GHOST_kStandardCursorNSScroll, ""}, + {GHOST_kStandardCursorEWScroll, ""}, + {GHOST_kStandardCursorStop, ""}, + {GHOST_kStandardCursorUpDown, "sb_v_double_arrow"}, + {GHOST_kStandardCursorLeftRight, "sb_h_double_arrow"}, + {GHOST_kStandardCursorTopSide, "top_side"}, + {GHOST_kStandardCursorBottomSide, "bottom_side"}, + {GHOST_kStandardCursorLeftSide, "left_side"}, + {GHOST_kStandardCursorRightSide, "right_side"}, + {GHOST_kStandardCursorTopLeftCorner, "top_left_corner"}, + {GHOST_kStandardCursorTopRightCorner, "top_right_corner"}, + {GHOST_kStandardCursorBottomRightCorner, "bottom_right_corner"}, + {GHOST_kStandardCursorBottomLeftCorner, "bottom_left_corner"}, + {GHOST_kStandardCursorCopy, "copy"}, +}; + +static constexpr const char *mime_text_plain = "text/plain"; +static constexpr const char *mime_text_utf8 = "text/plain;charset=utf-8"; +static constexpr const char *mime_text_uri = "text/uri-list"; + +static const std::unordered_map<std::string, GHOST_TDragnDropTypes> mime_dnd = { + {mime_text_plain, GHOST_kDragnDropTypeString}, + {mime_text_utf8, GHOST_kDragnDropTypeString}, + {mime_text_uri, GHOST_kDragnDropTypeFilenames}, +}; + +static const std::vector<std::string> mime_preference_order = { + mime_text_uri, + mime_text_utf8, + mime_text_plain, +}; + +static const std::vector<std::string> mime_send = { + "UTF8_STRING", + "COMPOUND_TEXT", + "TEXT", + "STRING", + "text/plain;charset=utf-8", + "text/plain", +}; + +/* -------------------------------------------------------------------- */ +/** \name Interface Callbacks + * + * These callbacks are registered for Wayland interfaces and called when + * an event is received from the compositor. + * \{ */ + +static void relative_pointer_relative_motion( + void *data, + struct zwp_relative_pointer_v1 * /*zwp_relative_pointer_v1*/, + uint32_t /*utime_hi*/, + uint32_t /*utime_lo*/, + wl_fixed_t dx, + wl_fixed_t dy, + wl_fixed_t /*dx_unaccel*/, + wl_fixed_t /*dy_unaccel*/) +{ + input_t *input = static_cast<input_t *>(data); + + input->x += wl_fixed_to_int(dx); + input->y += wl_fixed_to_int(dy); + + input->system->pushEvent( + new GHOST_EventCursor(input->system->getMilliSeconds(), + GHOST_kEventCursorMove, + input->system->getWindowManager()->getActiveWindow(), + input->x, + input->y, + GHOST_TABLET_DATA_NONE)); +} + +static const zwp_relative_pointer_v1_listener relative_pointer_listener = { + relative_pointer_relative_motion}; + +static void dnd_events(const input_t *const input, const GHOST_TEventType event) +{ + const GHOST_TUns64 time = input->system->getMilliSeconds(); + GHOST_IWindow *const window = input->system->getWindowManager()->getActiveWindow(); + for (const std::string &type : mime_preference_order) { + input->system->pushEvent(new GHOST_EventDragnDrop(time, + event, + mime_dnd.at(type), + window, + input->data_offer_dnd->dnd.x, + input->data_offer_dnd->dnd.y, + nullptr)); + } +} + +static std::string read_pipe(data_offer_t *data_offer, const std::string mime_receive) +{ + int pipefd[2]; + pipe(pipefd); + wl_data_offer_receive(data_offer->id, mime_receive.c_str(), pipefd[1]); + close(pipefd[1]); + + std::string data; + ssize_t len; + char buffer[4096]; + while ((len = read(pipefd[0], buffer, sizeof(buffer))) > 0) { + data.insert(data.end(), buffer, buffer + len); + } + close(pipefd[0]); + data_offer->in_use.store(false); + + return data; +} + +/** + * A target accepts an offered mime type. + * + * Sent when a target accepts pointer_focus or motion events. If + * a target does not accept any of the offered types, type is NULL. + */ +static void data_source_target(void * /*data*/, + struct wl_data_source * /*wl_data_source*/, + const char * /*mime_type*/) +{ + /* pass */ +} + +static void data_source_send(void *data, + struct wl_data_source * /*wl_data_source*/, + const char * /*mime_type*/, + int32_t fd) +{ + const char *const buffer = static_cast<char *>(data); + write(fd, buffer, strlen(buffer) + 1); + close(fd); +} + +static void data_source_cancelled(void * /*data*/, struct wl_data_source *wl_data_source) +{ + wl_data_source_destroy(wl_data_source); +} + +/** + * The drag-and-drop operation physically finished. + * + * The user performed the drop action. This event does not + * indicate acceptance, #wl_data_source.cancelled may still be + * emitted afterwards if the drop destination does not accept any mime type. + */ +static void data_source_dnd_drop_performed(void * /*data*/, + struct wl_data_source * /*wl_data_source*/) +{ + /* pass */ +} + +/** + * The drag-and-drop operation concluded. + * + * The drop destination finished interoperating with this data + * source, so the client is now free to destroy this data source + * and free all associated data. + */ +static void data_source_dnd_finished(void * /*data*/, struct wl_data_source * /*wl_data_source*/) +{ + /* pass */ +} + +/** + * Notify the selected action. + * + * This event indicates the action selected by the compositor + * after matching the source/destination side actions. Only one + * action (or none) will be offered here. + */ +static void data_source_action(void * /*data*/, + struct wl_data_source * /*wl_data_source*/, + uint32_t /*dnd_action*/) +{ + /* pass */ +} + +static const struct wl_data_source_listener data_source_listener = { + data_source_target, + data_source_send, + data_source_cancelled, + data_source_dnd_drop_performed, + data_source_dnd_finished, + data_source_action, +}; + +static void data_offer_offer(void *data, + struct wl_data_offer * /*wl_data_offer*/, + const char *mime_type) +{ + static_cast<data_offer_t *>(data)->types.insert(mime_type); +} + +static void data_offer_source_actions(void *data, + struct wl_data_offer * /*wl_data_offer*/, + uint32_t source_actions) +{ + static_cast<data_offer_t *>(data)->source_actions = source_actions; +} + +static void data_offer_action(void *data, + struct wl_data_offer * /*wl_data_offer*/, + uint32_t dnd_action) +{ + static_cast<data_offer_t *>(data)->dnd_action = dnd_action; +} + +static const struct wl_data_offer_listener data_offer_listener = { + data_offer_offer, + data_offer_source_actions, + data_offer_action, +}; + +static void data_device_data_offer(void * /*data*/, + struct wl_data_device * /*wl_data_device*/, + struct wl_data_offer *id) +{ + data_offer_t *data_offer = new data_offer_t; + data_offer->id = id; + wl_data_offer_add_listener(id, &data_offer_listener, data_offer); +} + +static void data_device_enter(void *data, + struct wl_data_device * /*wl_data_device*/, + uint32_t serial, + struct wl_surface * /*surface*/, + wl_fixed_t x, + wl_fixed_t y, + struct wl_data_offer *id) +{ + input_t *input = static_cast<input_t *>(data); + input->data_offer_dnd = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id)); + data_offer_t *data_offer = input->data_offer_dnd; + + data_offer->in_use.store(true); + data_offer->dnd.x = wl_fixed_to_int(x); + data_offer->dnd.y = wl_fixed_to_int(y); + + wl_data_offer_set_actions(id, + WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | + WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE, + WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY); + + for (const std::string &type : mime_preference_order) { + wl_data_offer_accept(id, serial, type.c_str()); + } + + dnd_events(input, GHOST_kEventDraggingEntered); +} + +static void data_device_leave(void *data, struct wl_data_device * /*wl_data_device*/) +{ + input_t *input = static_cast<input_t *>(data); + + dnd_events(input, GHOST_kEventDraggingExited); + + if (input->data_offer_dnd && !input->data_offer_dnd->in_use.load()) { + wl_data_offer_destroy(input->data_offer_dnd->id); + delete input->data_offer_dnd; + input->data_offer_dnd = nullptr; + } +} + +static void data_device_motion(void *data, + struct wl_data_device * /*wl_data_device*/, + uint32_t /*time*/, + wl_fixed_t x, + wl_fixed_t y) +{ + input_t *input = static_cast<input_t *>(data); + input->data_offer_dnd->dnd.x = wl_fixed_to_int(x); + input->data_offer_dnd->dnd.y = wl_fixed_to_int(y); + dnd_events(input, GHOST_kEventDraggingUpdated); +} + +static void data_device_drop(void *data, struct wl_data_device * /*wl_data_device*/) +{ + input_t *input = static_cast<input_t *>(data); + data_offer_t *data_offer = input->data_offer_dnd; + + const std::string mime_receive = *std::find_first_of(mime_preference_order.begin(), + mime_preference_order.end(), + data_offer->types.begin(), + data_offer->types.end()); + + auto read_uris = [](GHOST_SystemWayland *const system, + data_offer_t *data_offer, + const std::string mime_receive) { + const int x = data_offer->dnd.x; + const int y = data_offer->dnd.y; + + const std::string data = read_pipe(data_offer, mime_receive); + + wl_data_offer_finish(data_offer->id); + wl_data_offer_destroy(data_offer->id); + + delete data_offer; + data_offer = nullptr; + + if (mime_receive == mime_text_uri) { + static constexpr const char *file_proto = "file://"; + static constexpr const char *crlf = "\r\n"; + + std::vector<std::string> uris; + + size_t pos = 0; + while (true) { + pos = data.find(file_proto, pos); + const size_t start = pos + sizeof(file_proto) - 1; + pos = data.find(crlf, pos); + const size_t end = pos; + + if (pos == std::string::npos) { + break; + } + uris.push_back(data.substr(start, end - start)); + } + + GHOST_TStringArray *flist = static_cast<GHOST_TStringArray *>( + malloc(sizeof(GHOST_TStringArray))); + flist->count = int(uris.size()); + flist->strings = static_cast<GHOST_TUns8 **>(malloc(uris.size() * sizeof(GHOST_TUns8 *))); + for (size_t i = 0; i < uris.size(); i++) { + flist->strings[i] = static_cast<GHOST_TUns8 *>( + malloc((uris[i].size() + 1) * sizeof(GHOST_TUns8))); + memcpy(flist->strings[i], uris[i].data(), uris[i].size() + 1); + } + system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(), + GHOST_kEventDraggingDropDone, + GHOST_kDragnDropTypeFilenames, + system->getWindowManager()->getActiveWindow(), + x, + y, + flist)); + } + else if (mime_receive == mime_text_plain || mime_receive == mime_text_utf8) { + /* TODO: enable use of internal functions 'txt_insert_buf' and + * 'text_update_edited' to behave like dropped text was pasted. */ + } + wl_display_roundtrip(system->display()); + }; + + std::thread read_thread(read_uris, input->system, data_offer, mime_receive); + read_thread.detach(); +} + +static void data_device_selection(void *data, + struct wl_data_device * /*wl_data_device*/, + struct wl_data_offer *id) +{ + input_t *input = static_cast<input_t *>(data); + data_offer_t *data_offer = input->data_offer_copy_paste; + + /* Delete old data offer. */ + if (data_offer != nullptr) { + wl_data_offer_destroy(data_offer->id); + delete data_offer; + data_offer = nullptr; + } + + if (id == nullptr) { + return; + } + + /* Get new data offer. */ + data_offer = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id)); + input->data_offer_copy_paste = data_offer; + + std::string mime_receive; + for (const std::string &type : {mime_text_utf8, mime_text_plain}) { + if (data_offer->types.count(type)) { + mime_receive = type; + break; + } + } + + auto read_selection = [](GHOST_SystemWayland *const system, + data_offer_t *data_offer, + const std::string mime_receive) { + const std::string data = read_pipe(data_offer, mime_receive); + system->setSelection(data); + }; + + std::thread read_thread(read_selection, input->system, data_offer, mime_receive); + read_thread.detach(); +} + +static const struct wl_data_device_listener data_device_listener = { + data_device_data_offer, + data_device_enter, + data_device_leave, + data_device_motion, + data_device_drop, + data_device_selection, +}; + +static void buffer_release(void * /*data*/, struct wl_buffer *wl_buffer) +{ + wl_buffer_destroy(wl_buffer); +} + +const struct wl_buffer_listener buffer_listener = { + buffer_release, +}; + +static void pointer_enter(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t serial, + struct wl_surface *surface, + wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + if (!surface) { + return; + } + input_t *input = static_cast<input_t *>(data); + input->pointer_serial = serial; + input->x = wl_fixed_to_int(surface_x); + input->y = wl_fixed_to_int(surface_y); + + static_cast<GHOST_WindowWayland *>(wl_surface_get_user_data(surface))->activate(); +} + +static void pointer_leave(void * /*data*/, + struct wl_pointer * /*wl_pointer*/, + uint32_t /*serial*/, + struct wl_surface *surface) +{ + if (surface != nullptr) { + static_cast<GHOST_WindowWayland *>(wl_surface_get_user_data(surface))->deactivate(); + } +} + +static void pointer_motion(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t /*time*/, + wl_fixed_t surface_x, + wl_fixed_t surface_y) +{ + input_t *input = static_cast<input_t *>(data); + + input->x = wl_fixed_to_int(surface_x); + input->y = wl_fixed_to_int(surface_y); + + input->system->pushEvent( + new GHOST_EventCursor(input->system->getMilliSeconds(), + GHOST_kEventCursorMove, + input->system->getWindowManager()->getActiveWindow(), + wl_fixed_to_int(surface_x), + wl_fixed_to_int(surface_y), + GHOST_TABLET_DATA_NONE)); +} + +static void pointer_button(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t serial, + uint32_t /*time*/, + uint32_t button, + uint32_t state) +{ + GHOST_TEventType etype = GHOST_kEventUnknown; + switch (state) { + case WL_POINTER_BUTTON_STATE_RELEASED: + etype = GHOST_kEventButtonUp; + break; + case WL_POINTER_BUTTON_STATE_PRESSED: + etype = GHOST_kEventButtonDown; + break; + } + + GHOST_TButtonMask ebutton = GHOST_kButtonMaskLeft; + switch (button) { + case BTN_LEFT: + ebutton = GHOST_kButtonMaskLeft; + break; + case BTN_MIDDLE: + ebutton = GHOST_kButtonMaskMiddle; + break; + case BTN_RIGHT: + ebutton = GHOST_kButtonMaskRight; + break; + } + + input_t *input = static_cast<input_t *>(data); + input->data_source->source_serial = serial; + input->buttons.set(ebutton, state == WL_POINTER_BUTTON_STATE_PRESSED); + input->system->pushEvent( + new GHOST_EventButton(input->system->getMilliSeconds(), + etype, + input->system->getWindowManager()->getActiveWindow(), + ebutton, + GHOST_TABLET_DATA_NONE)); +} + +static void pointer_axis(void *data, + struct wl_pointer * /*wl_pointer*/, + uint32_t /*time*/, + uint32_t axis, + wl_fixed_t value) +{ + if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) { + return; + } + input_t *input = static_cast<input_t *>(data); + input->system->pushEvent( + new GHOST_EventWheel(input->system->getMilliSeconds(), + input->system->getWindowManager()->getActiveWindow(), + std::signbit(value) ? +1 : -1)); +} + +static const struct wl_pointer_listener pointer_listener = { + pointer_enter, + pointer_leave, + pointer_motion, + pointer_button, + pointer_axis, +}; + +static void keyboard_keymap( + void *data, struct wl_keyboard * /*wl_keyboard*/, uint32_t format, int32_t fd, uint32_t size) +{ + input_t *input = static_cast<input_t *>(data); + + if ((!data) || (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)) { + close(fd); + return; + } + + char *map_str = static_cast<char *>(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0)); + if (map_str == MAP_FAILED) { + close(fd); + throw std::runtime_error("keymap mmap failed: " + std::string(std::strerror(errno))); + } + + struct xkb_keymap *keymap = xkb_keymap_new_from_string( + input->xkb_context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(map_str, size); + close(fd); + + if (!keymap) { + return; + } + + input->xkb_state = xkb_state_new(keymap); + + xkb_keymap_unref(keymap); +} + +/** + * Enter event. + * + * Notification that this seat's keyboard focus is on a certain + * surface. + */ +static void keyboard_enter(void * /*data*/, + struct wl_keyboard * /*wl_keyboard*/, + uint32_t /*serial*/, + struct wl_surface * /*surface*/, + struct wl_array * /*keys*/) +{ + /* pass */ +} + +/** + * Leave event. + * + * Notification that this seat's keyboard focus is no longer on a + * certain surface. + */ +static void keyboard_leave(void * /*data*/, + struct wl_keyboard * /*wl_keyboard*/, + uint32_t /*serial*/, + struct wl_surface * /*surface*/) +{ + /* pass */ +} + +static void keyboard_key(void *data, + struct wl_keyboard * /*wl_keyboard*/, + uint32_t serial, + uint32_t /*time*/, + uint32_t key, + uint32_t state) +{ + input_t *input = static_cast<input_t *>(data); + + GHOST_TEventType etype = GHOST_kEventUnknown; + switch (state) { + case WL_KEYBOARD_KEY_STATE_RELEASED: + etype = GHOST_kEventKeyUp; + break; + case WL_KEYBOARD_KEY_STATE_PRESSED: + etype = GHOST_kEventKeyDown; + break; + } + + const xkb_keysym_t sym = xkb_state_key_get_one_sym(input->xkb_state, key + 8); + if (sym == XKB_KEY_NoSymbol) { + return; + } + const GHOST_TKey gkey = xkb_map_gkey(sym); + + GHOST_TEventKeyData key_data; + + if (etype == GHOST_kEventKeyDown) { + xkb_state_key_get_utf8( + input->xkb_state, key + 8, key_data.utf8_buf, sizeof(GHOST_TEventKeyData::utf8_buf)); + } + else { + key_data.utf8_buf[0] = '\0'; + } + + input->data_source->source_serial = serial; + input->system->pushEvent(new GHOST_EventKey(input->system->getMilliSeconds(), + etype, + input->system->getWindowManager()->getActiveWindow(), + gkey, + '\0', + key_data.utf8_buf, + false)); +} + +static void keyboard_modifiers(void *data, + struct wl_keyboard * /*wl_keyboard*/, + uint32_t /*serial*/, + uint32_t mods_depressed, + uint32_t mods_latched, + uint32_t mods_locked, + uint32_t group) +{ + xkb_state_update_mask(static_cast<input_t *>(data)->xkb_state, + mods_depressed, + mods_latched, + mods_locked, + 0, + 0, + group); +} + +static const struct wl_keyboard_listener keyboard_listener = { + keyboard_keymap, + keyboard_enter, + keyboard_leave, + keyboard_key, + keyboard_modifiers, +}; + +static void seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) +{ + input_t *input = static_cast<input_t *>(data); + input->pointer = nullptr; + input->keyboard = nullptr; + + if (capabilities & WL_SEAT_CAPABILITY_POINTER) { + input->pointer = wl_seat_get_pointer(wl_seat); + input->cursor.surface = wl_compositor_create_surface(input->system->compositor()); + input->cursor.visible = true; + input->cursor.buffer = nullptr; + input->cursor.file_buffer = new buffer_t; + wl_pointer_add_listener(input->pointer, &pointer_listener, data); + } + + if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { + input->keyboard = wl_seat_get_keyboard(wl_seat); + wl_keyboard_add_listener(input->keyboard, &keyboard_listener, data); + } +} + +static void seat_name(void *data, struct wl_seat * /*wl_seat*/, const char *name) +{ + static_cast<input_t *>(data)->name = std::string(name); +} + +static const struct wl_seat_listener seat_listener = { + seat_capabilities, + seat_name, +}; + +static void output_geometry(void *data, + struct wl_output * /*wl_output*/, + int32_t /*x*/, + int32_t /*y*/, + int32_t /*physical_width*/, + int32_t /*physical_height*/, + int32_t /*subpixel*/, + const char *make, + const char *model, + int32_t transform) +{ + output_t *output = static_cast<output_t *>(data); + output->transform = transform; + output->make = std::string(make); + output->model = std::string(model); +} + +static void output_mode(void *data, + struct wl_output * /*wl_output*/, + uint32_t /*flags*/, + int32_t width, + int32_t height, + int32_t /*refresh*/) +{ + output_t *output = static_cast<output_t *>(data); + output->width = width; + output->height = height; +} + +/** + * Sent all information about output. + * + * This event is sent after all other properties have been sent + * after binding to the output object and after any other property + * changes done after that. This allows changes to the output + * properties to be seen as atomic, even if they happen via multiple events. + */ +static void output_done(void * /*data*/, struct wl_output * /*wl_output*/) +{ +} + +static void output_scale(void *data, struct wl_output * /*wl_output*/, int32_t factor) +{ + static_cast<output_t *>(data)->scale = factor; +} + +static const struct wl_output_listener output_listener = { + output_geometry, + output_mode, + output_done, + output_scale, +}; + +static void shell_ping(void * /*data*/, struct xdg_wm_base *xdg_wm_base, uint32_t serial) +{ + xdg_wm_base_pong(xdg_wm_base, serial); +} + +static const struct xdg_wm_base_listener shell_listener = { + shell_ping, +}; + +static void global_add(void *data, + struct wl_registry *wl_registry, + uint32_t name, + const char *interface, + uint32_t /*version*/) +{ + struct display_t *display = static_cast<struct display_t *>(data); + if (!strcmp(interface, wl_compositor_interface.name)) { + display->compositor = static_cast<wl_compositor *>( + wl_registry_bind(wl_registry, name, &wl_compositor_interface, 1)); + } + else if (!strcmp(interface, xdg_wm_base_interface.name)) { + display->xdg_shell = static_cast<xdg_wm_base *>( + wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 1)); + xdg_wm_base_add_listener(display->xdg_shell, &shell_listener, nullptr); + } + else if (!strcmp(interface, wl_output_interface.name)) { + output_t *output = new output_t; + output->scale = 1; + output->output = static_cast<wl_output *>( + wl_registry_bind(wl_registry, name, &wl_output_interface, 2)); + display->outputs.push_back(output); + wl_output_add_listener(output->output, &output_listener, output); + } + else if (!strcmp(interface, wl_seat_interface.name)) { + input_t *input = new input_t; + input->system = display->system; + input->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + input->xkb_state = nullptr; + input->data_offer_dnd = nullptr; + input->data_offer_copy_paste = nullptr; + input->data_source = new data_source_t; + input->data_source->data_source = nullptr; + input->data_source->buffer_out = nullptr; + input->relative_pointer = nullptr; + input->locked_pointer = nullptr; + input->seat = static_cast<wl_seat *>( + wl_registry_bind(wl_registry, name, &wl_seat_interface, 2)); + display->inputs.push_back(input); + wl_seat_add_listener(input->seat, &seat_listener, input); + } + else if (!strcmp(interface, wl_shm_interface.name)) { + display->shm = static_cast<wl_shm *>( + wl_registry_bind(wl_registry, name, &wl_shm_interface, 1)); + } + else if (!strcmp(interface, wl_data_device_manager_interface.name)) { + display->data_device_manager = static_cast<wl_data_device_manager *>( + wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, 1)); + } + else if (!strcmp(interface, zwp_relative_pointer_manager_v1_interface.name)) { + display->relative_pointer_manager = static_cast<zwp_relative_pointer_manager_v1 *>( + wl_registry_bind(wl_registry, name, &zwp_relative_pointer_manager_v1_interface, 1)); + } + else if (!strcmp(interface, zwp_pointer_constraints_v1_interface.name)) { + display->pointer_constraints = static_cast<zwp_pointer_constraints_v1 *>( + wl_registry_bind(wl_registry, name, &zwp_pointer_constraints_v1_interface, 1)); + } +} + +/** + * Announce removal of global object. + * + * Notify the client of removed global objects. + * + * This event notifies the client that the global identified by + * name is no longer available. If the client bound to the global + * using the bind request, the client should now destroy that object. + */ +static void global_remove(void * /*data*/, struct wl_registry * /*wl_registry*/, uint32_t /*name*/) +{ +} + +static const struct wl_registry_listener registry_listener = { + global_add, + global_remove, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Ghost Implementation + * + * Wayland specific implementation of the GHOST_System interface. + * \{ */ + +GHOST_SystemWayland::GHOST_SystemWayland() : GHOST_System(), d(new display_t) +{ + d->system = this; + /* Connect to the Wayland server. */ + d->display = wl_display_connect(nullptr); + if (!d->display) { + display_destroy(d); + throw std::runtime_error("Wayland: unable to connect to display!"); + } + + /* Register interfaces. */ + struct wl_registry *registry = wl_display_get_registry(d->display); + wl_registry_add_listener(registry, ®istry_listener, d); + /* Call callback for registry listener. */ + wl_display_roundtrip(d->display); + /* Call callbacks for registered listeners. */ + wl_display_roundtrip(d->display); + wl_registry_destroy(registry); + + if (!d->xdg_shell) { + display_destroy(d); + throw std::runtime_error("Wayland: unable to access xdg_shell!"); + } + + /* Register data device per seat for IPC between Wayland clients. */ + if (d->data_device_manager) { + for (input_t *input : d->inputs) { + input->data_device = wl_data_device_manager_get_data_device(d->data_device_manager, + input->seat); + wl_data_device_add_listener(input->data_device, &data_device_listener, input); + } + } + + const char *theme = std::getenv("XCURSOR_THEME"); + const char *size = std::getenv("XCURSOR_SIZE"); + const int sizei = size ? std::stoi(size) : default_cursor_size; + + d->cursor_theme = wl_cursor_theme_load(theme, sizei, d->shm); + if (!d->cursor_theme) { + display_destroy(d); + throw std::runtime_error("Wayland: unable to access cursor themes!"); + } +} + +GHOST_SystemWayland::~GHOST_SystemWayland() +{ + display_destroy(d); +} + +bool GHOST_SystemWayland::processEvents(bool /*waitForEvent*/) +{ + wl_display_dispatch(d->display); + return true; +} + +int GHOST_SystemWayland::toggleConsole(int /*action*/) +{ + return 0; +} + +GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) const +{ + if (!d->inputs.empty()) { + static const xkb_state_component mods_all = xkb_state_component( + XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_MODS_LOCKED | + XKB_STATE_MODS_EFFECTIVE); + + keys.set(GHOST_kModifierKeyLeftShift, + xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_SHIFT, mods_all) == + 1); + keys.set(GHOST_kModifierKeyRightShift, + xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_SHIFT, mods_all) == + 1); + keys.set(GHOST_kModifierKeyLeftAlt, + xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "LAlt", mods_all) == 1); + keys.set(GHOST_kModifierKeyRightAlt, + xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "RAlt", mods_all) == 1); + keys.set(GHOST_kModifierKeyLeftControl, + xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "LControl", mods_all) == 1); + keys.set(GHOST_kModifierKeyRightControl, + xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "RControl", mods_all) == 1); + keys.set(GHOST_kModifierKeyOS, + xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "Super", mods_all) == 1); + keys.set(GHOST_kModifierKeyNumMasks, + xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, "NumLock", mods_all) == 1); + + return GHOST_kSuccess; + } + return GHOST_kFailure; +} + +GHOST_TSuccess GHOST_SystemWayland::getButtons(GHOST_Buttons &buttons) const +{ + if (!d->inputs.empty()) { + buttons = d->inputs[0]->buttons; + return GHOST_kSuccess; + } + return GHOST_kFailure; +} + +GHOST_TUns8 *GHOST_SystemWayland::getClipboard(bool /*selection*/) const +{ + GHOST_TUns8 *clipboard = static_cast<GHOST_TUns8 *>(malloc((selection.size() + 1))); + memcpy(clipboard, selection.data(), selection.size() + 1); + return clipboard; +} + +void GHOST_SystemWayland::putClipboard(GHOST_TInt8 *buffer, bool /*selection*/) const +{ + if (!d->data_device_manager || d->inputs.empty()) { + return; + } + + data_source_t *data_source = d->inputs[0]->data_source; + + /* Copy buffer. */ + data_source->buffer_out = static_cast<char *>(malloc(strlen(buffer) + 1)); + std::strcpy(data_source->buffer_out, buffer); + + data_source->data_source = wl_data_device_manager_create_data_source(d->data_device_manager); + + wl_data_source_add_listener( + data_source->data_source, &data_source_listener, data_source->buffer_out); + + for (const std::string &type : mime_send) { + wl_data_source_offer(data_source->data_source, type.c_str()); + } + + if (!d->inputs.empty() && d->inputs[0]->data_device) { + wl_data_device_set_selection( + d->inputs[0]->data_device, data_source->data_source, data_source->source_serial); + } +} + +GHOST_TUns8 GHOST_SystemWayland::getNumDisplays() const +{ + return d ? GHOST_TUns8(d->outputs.size()) : 0; +} + +GHOST_TSuccess GHOST_SystemWayland::getCursorPosition(GHOST_TInt32 &x, GHOST_TInt32 &y) const +{ + if (getWindowManager()->getActiveWindow() != nullptr && !d->inputs.empty()) { + x = d->inputs[0]->x; + y = d->inputs[0]->y; + return GHOST_kSuccess; + } + else { + return GHOST_kFailure; + } +} + +GHOST_TSuccess GHOST_SystemWayland::setCursorPosition(GHOST_TInt32 /*x*/, GHOST_TInt32 /*y*/) +{ + return GHOST_kFailure; +} + +void GHOST_SystemWayland::getMainDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const +{ + if (getNumDisplays() > 0) { + /* We assume first output as main. */ + width = uint32_t(d->outputs[0]->width); + height = uint32_t(d->outputs[0]->height); + } +} + +void GHOST_SystemWayland::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const +{ + getMainDisplayDimensions(width, height); +} + +GHOST_IContext *GHOST_SystemWayland::createOffscreenContext() +{ + /* Create new off-screen window. */ + wl_surface *os_surface = wl_compositor_create_surface(compositor()); + wl_egl_window *os_egl_window = wl_egl_window_create(os_surface, int(1), int(1)); + + d->os_surfaces.push_back(os_surface); + d->os_egl_windows.push_back(os_egl_window); + + GHOST_Context *context = new GHOST_ContextEGL(false, + EGLNativeWindowType(os_egl_window), + EGLNativeDisplayType(d->display), + EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + 3, + 3, + GHOST_OPENGL_EGL_CONTEXT_FLAGS, + GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, + EGL_OPENGL_API); + + if (context->initializeDrawingContext()) { + return context; + } + else { + delete context; + } + + GHOST_PRINT("Cannot create off-screen EGL context" << std::endl); + + return nullptr; +} + +GHOST_TSuccess GHOST_SystemWayland::disposeContext(GHOST_IContext *context) +{ + delete context; + return GHOST_kSuccess; +} + +GHOST_IWindow *GHOST_SystemWayland::createWindow(const char *title, + GHOST_TInt32 left, + GHOST_TInt32 top, + GHOST_TUns32 width, + GHOST_TUns32 height, + GHOST_TWindowState state, + GHOST_TDrawingContextType type, + GHOST_GLSettings glSettings, + const bool exclusive, + const bool /*is_dialog*/, + const GHOST_IWindow *parentWindow) +{ + GHOST_WindowWayland *window = new GHOST_WindowWayland( + this, + title, + left, + top, + width, + height, + state, + parentWindow, + type, + ((glSettings.flags & GHOST_glStereoVisual) != 0), + exclusive); + + if (window) { + if (window->getValid()) { + m_windowManager->addWindow(window); + m_windowManager->setActiveWindow(window); + pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window)); + } + else { + delete window; + window = nullptr; + } + } + + return window; +} + +wl_display *GHOST_SystemWayland::display() +{ + return d->display; +} + +wl_compositor *GHOST_SystemWayland::compositor() +{ + return d->compositor; +} + +xdg_wm_base *GHOST_SystemWayland::shell() +{ + return d->xdg_shell; +} + +void GHOST_SystemWayland::setSelection(const std::string &selection) +{ + this->selection = selection; +} + +static void set_cursor_buffer(input_t *input, wl_buffer *buffer) +{ + input->cursor.visible = (buffer != nullptr); + + wl_surface_attach(input->cursor.surface, buffer, 0, 0); + wl_surface_commit(input->cursor.surface); + + if (input->cursor.visible) { + wl_surface_damage(input->cursor.surface, + 0, + 0, + int32_t(input->cursor.image.width), + int32_t(input->cursor.image.height)); + wl_pointer_set_cursor(input->pointer, + input->pointer_serial, + input->cursor.surface, + int32_t(input->cursor.image.hotspot_x), + int32_t(input->cursor.image.hotspot_y)); + } +} + +GHOST_TSuccess GHOST_SystemWayland::setCursorShape(GHOST_TStandardCursor shape) +{ + if (d->inputs.empty()) { + return GHOST_kFailure; + } + const std::string cursor_name = cursors.count(shape) ? cursors.at(shape) : + cursors.at(GHOST_kStandardCursorDefault); + + wl_cursor *cursor = wl_cursor_theme_get_cursor(d->cursor_theme, cursor_name.c_str()); + + if (!cursor) { + GHOST_PRINT("cursor '" << cursor_name << "' does not exist" << std::endl); + return GHOST_kFailure; + } + + struct wl_cursor_image *image = cursor->images[0]; + struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); + if (!buffer) { + return GHOST_kFailure; + } + cursor_t *c = &d->inputs[0]->cursor; + c->buffer = buffer; + c->image = *image; + + set_cursor_buffer(d->inputs[0], buffer); + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_SystemWayland::hasCursorShape(GHOST_TStandardCursor cursorShape) +{ + return GHOST_TSuccess(cursors.count(cursorShape) && !cursors.at(cursorShape).empty()); +} + +GHOST_TSuccess GHOST_SystemWayland::setCustomCursorShape(GHOST_TUns8 *bitmap, + GHOST_TUns8 *mask, + int sizex, + int sizey, + int hotX, + int hotY, + bool /*canInvertColor*/) +{ + if (d->inputs.empty()) { + return GHOST_kFailure; + } + + cursor_t *cursor = &d->inputs[0]->cursor; + + static const int32_t stride = sizex * 4; /* ARGB */ + cursor->file_buffer->size = size_t(stride * sizey); + + const int fd = memfd_create("blender-cursor-custom", MFD_CLOEXEC | MFD_ALLOW_SEALING); + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK); + posix_fallocate(fd, 0, int32_t(cursor->file_buffer->size)); + + cursor->file_buffer->data = mmap( + nullptr, cursor->file_buffer->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + struct wl_shm_pool *pool = wl_shm_create_pool(d->shm, fd, int32_t(cursor->file_buffer->size)); + + wl_buffer *buffer = wl_shm_pool_create_buffer( + pool, 0, sizex, sizey, stride, WL_SHM_FORMAT_ARGB8888); + + wl_shm_pool_destroy(pool); + close(fd); + + wl_buffer_add_listener(buffer, &buffer_listener, nullptr); + + static constexpr uint32_t black = 0xFF000000; + static constexpr uint32_t white = 0xFFFFFFFF; + static constexpr uint32_t transparent = 0x00000000; + + uint8_t datab = 0, maskb = 0; + uint32_t *pixel; + + for (int y = 0; y < sizey; ++y) { + pixel = &static_cast<uint32_t *>(cursor->file_buffer->data)[y * sizex]; + for (int x = 0; x < sizex; ++x) { + if ((x % 8) == 0) { + datab = *bitmap++; + maskb = *mask++; + + /* Reverse bit order. */ + datab = uint8_t((datab * 0x0202020202ULL & 0x010884422010ULL) % 1023); + maskb = uint8_t((maskb * 0x0202020202ULL & 0x010884422010ULL) % 1023); + } + + if (maskb & 0x80) { + *pixel++ = (datab & 0x80) ? white : black; + } + else { + *pixel++ = (datab & 0x80) ? white : transparent; + } + datab <<= 1; + maskb <<= 1; + } + } + + cursor->buffer = buffer; + cursor->image.width = uint32_t(sizex); + cursor->image.height = uint32_t(sizey); + cursor->image.hotspot_x = uint32_t(hotX); + cursor->image.hotspot_y = uint32_t(hotY); + + set_cursor_buffer(d->inputs[0], buffer); + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_SystemWayland::setCursorVisibility(bool visible) +{ + if (d->inputs.empty()) { + return GHOST_kFailure; + } + + cursor_t *cursor = &d->inputs[0]->cursor; + if (visible) { + if (!cursor->visible) { + set_cursor_buffer(d->inputs[0], d->inputs[0]->cursor.buffer); + } + } + else { + if (cursor->visible) { + set_cursor_buffer(d->inputs[0], nullptr); + } + } + + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_SystemWayland::setCursorGrab(const GHOST_TGrabCursorMode mode, + wl_surface *surface) +{ + if (d->inputs.empty()) { + return GHOST_kFailure; + } + + input_t *input = d->inputs[0]; + + switch (mode) { + case GHOST_kGrabDisable: + if (input->relative_pointer) { + zwp_relative_pointer_v1_destroy(input->relative_pointer); + input->relative_pointer = nullptr; + } + if (input->locked_pointer) { + zwp_locked_pointer_v1_destroy(input->locked_pointer); + input->locked_pointer = nullptr; + } + break; + + case GHOST_kGrabNormal: + case GHOST_kGrabWrap: + input->relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer( + d->relative_pointer_manager, input->pointer); + zwp_relative_pointer_v1_add_listener( + input->relative_pointer, &relative_pointer_listener, input); + input->locked_pointer = zwp_pointer_constraints_v1_lock_pointer( + d->pointer_constraints, + surface, + input->pointer, + nullptr, + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); + break; + + case GHOST_kGrabHide: + setCursorVisibility(false); + break; + } + + return GHOST_kSuccess; +} + +/** \} */ diff --git a/intern/ghost/intern/GHOST_SystemWayland.h b/intern/ghost/intern/GHOST_SystemWayland.h new file mode 100644 index 00000000000..89cd3406b69 --- /dev/null +++ b/intern/ghost/intern/GHOST_SystemWayland.h @@ -0,0 +1,111 @@ +/* + * 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 GHOST + * Declaration of GHOST_SystemWayland class. + */ + +#ifndef __GHOST_SYSTEMWAYLAND_H__ +#define __GHOST_SYSTEMWAYLAND_H__ + +#include "../GHOST_Types.h" +#include "GHOST_System.h" +#include "GHOST_WindowWayland.h" + +#include <wayland-client.h> +#include <xdg-shell-client-protocol.h> + +#include <string> + +class GHOST_WindowWayland; + +struct display_t; + +class GHOST_SystemWayland : public GHOST_System { + public: + GHOST_SystemWayland(); + + ~GHOST_SystemWayland() override; + + bool processEvents(bool waitForEvent) override; + + int toggleConsole(int action) override; + + GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const override; + + GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const override; + + GHOST_TUns8 *getClipboard(bool selection) const override; + + void putClipboard(GHOST_TInt8 *buffer, bool selection) const override; + + GHOST_TUns8 getNumDisplays() const override; + + GHOST_TSuccess getCursorPosition(GHOST_TInt32 &x, GHOST_TInt32 &y) const override; + + GHOST_TSuccess setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y) override; + + void getMainDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const override; + + void getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const override; + + GHOST_IContext *createOffscreenContext() override; + + GHOST_TSuccess disposeContext(GHOST_IContext *context) override; + + GHOST_IWindow *createWindow(const char *title, + GHOST_TInt32 left, + GHOST_TInt32 top, + GHOST_TUns32 width, + GHOST_TUns32 height, + GHOST_TWindowState state, + GHOST_TDrawingContextType type, + GHOST_GLSettings glSettings, + const bool exclusive, + const bool is_dialog, + const GHOST_IWindow *parentWindow) override; + + wl_display *display(); + + wl_compositor *compositor(); + + xdg_wm_base *shell(); + + void setSelection(const std::string &selection); + + GHOST_TSuccess setCursorShape(GHOST_TStandardCursor shape); + + GHOST_TSuccess hasCursorShape(GHOST_TStandardCursor cursorShape); + + GHOST_TSuccess setCustomCursorShape(GHOST_TUns8 *bitmap, + GHOST_TUns8 *mask, + int sizex, + int sizey, + int hotX, + int hotY, + bool canInvertColor); + + GHOST_TSuccess setCursorVisibility(bool visible); + + GHOST_TSuccess setCursorGrab(const GHOST_TGrabCursorMode mode, wl_surface *surface); + + private: + struct display_t *d; + std::string selection; +}; + +#endif /* __GHOST_SYSTEMWAYLAND_H__ */ diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index fdd022e44ac..849aa5a96f5 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -282,7 +282,7 @@ void GHOST_SystemWin32::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns3 height = ::GetSystemMetrics(SM_CYVIRTUALSCREEN); } -GHOST_IWindow *GHOST_SystemWin32::createWindow(const STR_String &title, +GHOST_IWindow *GHOST_SystemWin32::createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h index c6d810d2a38..b23f907608c 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.h +++ b/intern/ghost/intern/GHOST_SystemWin32.h @@ -126,7 +126,7 @@ class GHOST_SystemWin32 : public GHOST_System { * \param parentWindow Parent window * \return The new window (or 0 if creation failed). */ - GHOST_IWindow *createWindow(const STR_String &title, + GHOST_IWindow *createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 825b93de36f..91c63a3fb76 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -338,7 +338,7 @@ void GHOST_SystemX11::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 * \param parentWindow Parent window * \return The new window (or 0 if creation failed). */ -GHOST_IWindow *GHOST_SystemX11::createWindow(const STR_String &title, +GHOST_IWindow *GHOST_SystemX11::createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -1881,7 +1881,7 @@ static GHOST_TKey ghost_key_from_keysym(const KeySym key) # endif #endif default: -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG printf("%s: unknown key: %lu / 0x%lx\n", __func__, key, key); #endif type = GHOST_kKeyUnknown; @@ -1905,7 +1905,7 @@ static GHOST_TKey ghost_key_from_keycode(const XkbDescPtr xkb_descr, const KeyCo switch (id) { case MAKE_ID('T', 'L', 'D', 'E'): return GHOST_kKeyAccentGrave; -#ifdef GHOST_DEBUG +#ifdef WITH_GHOST_DEBUG default: printf("%s unhandled keycode: %.*s\n", __func__, XkbKeyNameLength, id_str); break; @@ -2456,7 +2456,7 @@ GHOST_TSuccess GHOST_SystemX11::showMessageBox(const char *title, string cmd = "xdg-open \"" + string(link) + "\""; if (system(cmd.c_str()) != 0) { GHOST_PRINTF("GHOST_SystemX11::showMessageBox: Unable to run system command [%s]", - cmd); + cmd.c_str()); } } break; diff --git a/intern/ghost/intern/GHOST_SystemX11.h b/intern/ghost/intern/GHOST_SystemX11.h index d0e0506e77b..bb01ef7e0cc 100644 --- a/intern/ghost/intern/GHOST_SystemX11.h +++ b/intern/ghost/intern/GHOST_SystemX11.h @@ -137,7 +137,7 @@ class GHOST_SystemX11 : public GHOST_System { * \param parentWindow Parent (embedder) window * \return The new window (or 0 if creation failed). */ - GHOST_IWindow *createWindow(const STR_String &title, + GHOST_IWindow *createWindow(const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, diff --git a/intern/ghost/intern/GHOST_Window.h b/intern/ghost/intern/GHOST_Window.h index 553a7d89df4..472149148e6 100644 --- a/intern/ghost/intern/GHOST_Window.h +++ b/intern/ghost/intern/GHOST_Window.h @@ -27,7 +27,6 @@ #include "GHOST_IWindow.h" -class STR_String; class GHOST_Context; /** @@ -61,8 +60,8 @@ class GHOST_Window : public GHOST_IWindow { * \section Interface inherited from GHOST_IWindow left for derived class * implementation. * virtual bool getValid() const = 0; - * virtual void setTitle(const STR_String& title) = 0; - * virtual void getTitle(STR_String& title) const = 0; + * virtual void setTitle(const char * title) = 0; + * virtual std::string getTitle() const = 0; * virtual void getWindowBounds(GHOST_Rect& bounds) const = 0; * virtual void getClientBounds(GHOST_Rect& bounds) const = 0; * virtual GHOST_TSuccess setClientWidth(GHOST_TUns32 width) = 0; diff --git a/intern/ghost/intern/GHOST_WindowCocoa.h b/intern/ghost/intern/GHOST_WindowCocoa.h index efa418deee2..15429eab5db 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.h +++ b/intern/ghost/intern/GHOST_WindowCocoa.h @@ -30,7 +30,6 @@ #endif // __APPLE__ #include "GHOST_Window.h" -#include "STR_String.h" @class CAMetalLayer; @class CocoaMetalView; @@ -58,7 +57,7 @@ class GHOST_WindowCocoa : public GHOST_Window { * \param stereoVisual Stereo visual for quad buffered stereo. */ GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 bottom, GHOST_TUns32 width, @@ -92,13 +91,12 @@ class GHOST_WindowCocoa : public GHOST_Window { * Sets the title displayed in the title bar. * \param title The title to display in the title bar. */ - void setTitle(const STR_String &title); - + void setTitle(const char *title); /** * Returns the title displayed in the title bar. * \param title The title displayed in the title bar. */ - void getTitle(STR_String &title) const; + std::string getTitle() const; /** * Returns the window rectangle dimensions. diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm index 10ab05a0de1..05adc41cb8e 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.mm +++ b/intern/ghost/intern/GHOST_WindowCocoa.mm @@ -291,7 +291,7 @@ /* clang-format on */ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 bottom, GHOST_TUns32 width, @@ -482,7 +482,7 @@ void *GHOST_WindowCocoa::getOSWindow() const return (void *)m_window; } -void GHOST_WindowCocoa::setTitle(const STR_String &title) +void GHOST_WindowCocoa::setTitle(const char *title) { GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::setTitle(): window invalid"); NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; @@ -524,7 +524,7 @@ void GHOST_WindowCocoa::setTitle(const STR_String &title) [pool drain]; } -void GHOST_WindowCocoa::getTitle(STR_String &title) const +std::string GHOST_WindowCocoa::getTitle() const { GHOST_ASSERT(getValid(), "GHOST_WindowCocoa::getTitle(): window invalid"); @@ -532,11 +532,14 @@ void GHOST_WindowCocoa::getTitle(STR_String &title) const NSString *windowTitle = [m_window title]; + std::string title; if (windowTitle != nil) { title = [windowTitle UTF8String]; } [pool drain]; + + return title; } void GHOST_WindowCocoa::getWindowBounds(GHOST_Rect &bounds) const diff --git a/intern/ghost/intern/GHOST_WindowNULL.h b/intern/ghost/intern/GHOST_WindowNULL.h index db40075e6ca..e1aa0cb7f13 100644 --- a/intern/ghost/intern/GHOST_WindowNULL.h +++ b/intern/ghost/intern/GHOST_WindowNULL.h @@ -26,7 +26,6 @@ #include <map> -class STR_String; class GHOST_SystemNULL; class GHOST_WindowNULL : public GHOST_Window { @@ -37,7 +36,7 @@ class GHOST_WindowNULL : public GHOST_Window { } GHOST_WindowNULL(GHOST_SystemNULL *system, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -83,12 +82,12 @@ class GHOST_WindowNULL : public GHOST_Window { { return true; } - void setTitle(const STR_String &title) + void setTitle(const char *title) { /* nothing */ } - void getTitle(STR_String &title) const + std::string getTitle() const { - title = "untitled"; + return "untitled"; } void getWindowBounds(GHOST_Rect &bounds) const { diff --git a/intern/ghost/intern/GHOST_WindowSDL.cpp b/intern/ghost/intern/GHOST_WindowSDL.cpp index e8d129f45fe..dcb1ab8c78c 100644 --- a/intern/ghost/intern/GHOST_WindowSDL.cpp +++ b/intern/ghost/intern/GHOST_WindowSDL.cpp @@ -27,7 +27,7 @@ #include <assert.h> GHOST_WindowSDL::GHOST_WindowSDL(GHOST_SystemSDL *system, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -148,14 +148,14 @@ bool GHOST_WindowSDL::getValid() const return GHOST_Window::getValid() && m_valid_setup; } -void GHOST_WindowSDL::setTitle(const STR_String &title) +void GHOST_WindowSDL::setTitle(const char *title) { - SDL_SetWindowTitle(m_sdl_win, title.ReadPtr()); + SDL_SetWindowTitle(m_sdl_win, title); } -void GHOST_WindowSDL::getTitle(STR_String &title) const +std::string GHOST_WindowSDL::getTitle() const { - title = SDL_GetWindowTitle(m_sdl_win); + return SDL_GetWindowTitle(m_sdl_win); } void GHOST_WindowSDL::getWindowBounds(GHOST_Rect &bounds) const diff --git a/intern/ghost/intern/GHOST_WindowSDL.h b/intern/ghost/intern/GHOST_WindowSDL.h index eadd1b7df9d..5039c742c9d 100644 --- a/intern/ghost/intern/GHOST_WindowSDL.h +++ b/intern/ghost/intern/GHOST_WindowSDL.h @@ -35,7 +35,6 @@ extern "C" { # error "SDL 2.0 or newer is needed to build with Ghost" #endif -class STR_String; class GHOST_SystemSDL; class GHOST_WindowSDL : public GHOST_Window { @@ -49,7 +48,7 @@ class GHOST_WindowSDL : public GHOST_Window { public: GHOST_WindowSDL(GHOST_SystemSDL *system, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -107,9 +106,9 @@ class GHOST_WindowSDL : public GHOST_Window { GHOST_TSuccess setWindowCursorVisibility(bool visible); - void setTitle(const STR_String &title); + void setTitle(const char *title); - void getTitle(STR_String &title) const; + std::string getTitle() const; GHOST_TSuccess setClientWidth(GHOST_TUns32 width); diff --git a/intern/ghost/intern/GHOST_WindowWayland.cpp b/intern/ghost/intern/GHOST_WindowWayland.cpp new file mode 100644 index 00000000000..0ea6f5f8ecb --- /dev/null +++ b/intern/ghost/intern/GHOST_WindowWayland.cpp @@ -0,0 +1,406 @@ +/* + * 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 GHOST + */ + +#include "GHOST_WindowWayland.h" +#include "GHOST_SystemWayland.h" +#include "GHOST_WindowManager.h" + +#include "GHOST_Event.h" + +#include "GHOST_ContextEGL.h" +#include "GHOST_ContextNone.h" + +#include <wayland-egl.h> + +struct window_t { + GHOST_WindowWayland *w; + wl_surface *surface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *xdg_toplevel; + wl_egl_window *egl_window; + int32_t pending_width, pending_height; + bool is_maximised; + bool is_fullscreen; + bool is_active; + int32_t width, height; +}; + +/* -------------------------------------------------------------------- */ +/** \name Wayland Interface Callbacks + * + * These callbacks are registered for Wayland interfaces and called when + * an event is received from the compositor. + * \{ */ + +static void toplevel_configure( + void *data, xdg_toplevel * /*xdg_toplevel*/, int32_t width, int32_t height, wl_array *states) +{ + window_t *win = static_cast<window_t *>(data); + win->pending_width = width; + win->pending_height = height; + + win->is_maximised = false; + win->is_fullscreen = false; + win->is_active = false; + + /* Note that the macro 'wl_array_for_each' would typically be used to simplify this logic, + * however it's not compatible with C++, so perform casts instead. + * If this needs to be done more often we could define our own C++ compatible macro. */ + for (enum xdg_toplevel_state *state = static_cast<xdg_toplevel_state *>(states->data); + reinterpret_cast<uint8_t *>(state) < (static_cast<uint8_t *>(states->data) + states->size); + state++) { + switch (*state) { + case XDG_TOPLEVEL_STATE_MAXIMIZED: + win->is_maximised = true; + break; + case XDG_TOPLEVEL_STATE_FULLSCREEN: + win->is_fullscreen = true; + break; + case XDG_TOPLEVEL_STATE_ACTIVATED: + win->is_active = true; + break; + default: + break; + } + } +} + +static void toplevel_close(void *data, xdg_toplevel * /*xdg_toplevel*/) +{ + static_cast<window_t *>(data)->w->close(); +} + +static const xdg_toplevel_listener toplevel_listener = { + toplevel_configure, + toplevel_close, +}; + +static void surface_configure(void *data, xdg_surface *xdg_surface, uint32_t serial) +{ + window_t *win = static_cast<window_t *>(data); + + int w, h; + wl_egl_window_get_attached_size(win->egl_window, &w, &h); + if (win->pending_width != 0 && win->pending_height != 0 && win->pending_width != w && + win->pending_height != h) { + win->width = win->pending_width; + win->height = win->pending_height; + wl_egl_window_resize(win->egl_window, win->pending_width, win->pending_height, 0, 0); + win->pending_width = 0; + win->pending_height = 0; + win->w->notify_size(); + } + + if (win->is_active) { + win->w->activate(); + } + else { + win->w->deactivate(); + } + + xdg_surface_ack_configure(xdg_surface, serial); +} + +static const xdg_surface_listener surface_listener = { + surface_configure, +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Ghost Implementation + * + * Wayland specific implementation of the GHOST_Window interface. + * \{ */ + +GHOST_TSuccess GHOST_WindowWayland::hasCursorShape(GHOST_TStandardCursor cursorShape) +{ + return m_system->hasCursorShape(cursorShape); +} + +GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system, + const char *title, + GHOST_TInt32 /*left*/, + GHOST_TInt32 /*top*/, + GHOST_TUns32 width, + GHOST_TUns32 height, + GHOST_TWindowState state, + const GHOST_IWindow *parentWindow, + GHOST_TDrawingContextType type, + const bool stereoVisual, + const bool exclusive) + : GHOST_Window(width, height, state, stereoVisual, exclusive), + m_system(system), + w(new window_t) +{ + w->w = this; + + w->width = int32_t(width); + w->height = int32_t(height); + + /* Window surfaces. */ + w->surface = wl_compositor_create_surface(m_system->compositor()); + w->egl_window = wl_egl_window_create(w->surface, int(width), int(height)); + + w->xdg_surface = xdg_wm_base_get_xdg_surface(m_system->shell(), w->surface); + w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface); + + wl_surface_set_user_data(w->surface, this); + + xdg_surface_add_listener(w->xdg_surface, &surface_listener, w); + xdg_toplevel_add_listener(w->xdg_toplevel, &toplevel_listener, w); + + if (parentWindow) { + xdg_toplevel_set_parent( + w->xdg_toplevel, dynamic_cast<const GHOST_WindowWayland *>(parentWindow)->w->xdg_toplevel); + } + + /* Call top-level callbacks. */ + wl_surface_commit(w->surface); + wl_display_roundtrip(m_system->display()); + + setState(state); + + setTitle(title); + + /* EGL context. */ + if (setDrawingContextType(type) == GHOST_kFailure) { + GHOST_PRINT("Failed to create EGL context" << std::endl); + } +} + +GHOST_TSuccess GHOST_WindowWayland::close() +{ + return m_system->pushEvent( + new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowClose, this)); +} + +GHOST_TSuccess GHOST_WindowWayland::activate() +{ + if (m_system->getWindowManager()->setActiveWindow(this) == GHOST_kFailure) { + return GHOST_kFailure; + } + return m_system->pushEvent( + new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowActivate, this)); +} + +GHOST_TSuccess GHOST_WindowWayland::deactivate() +{ + m_system->getWindowManager()->setWindowInactive(this); + return m_system->pushEvent( + new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowDeactivate, this)); +} + +GHOST_TSuccess GHOST_WindowWayland::notify_size() +{ + return m_system->pushEvent( + new GHOST_Event(m_system->getMilliSeconds(), GHOST_kEventWindowSize, this)); +} + +GHOST_TSuccess GHOST_WindowWayland::setWindowCursorGrab(GHOST_TGrabCursorMode mode) +{ + return m_system->setCursorGrab(mode, w->surface); +} + +GHOST_TSuccess GHOST_WindowWayland::setWindowCursorShape(GHOST_TStandardCursor shape) +{ + const GHOST_TSuccess ok = m_system->setCursorShape(shape); + m_cursorShape = (ok == GHOST_kSuccess) ? shape : GHOST_kStandardCursorDefault; + return ok; +} + +GHOST_TSuccess GHOST_WindowWayland::setWindowCustomCursorShape(GHOST_TUns8 *bitmap, + GHOST_TUns8 *mask, + int sizex, + int sizey, + int hotX, + int hotY, + bool canInvertColor) +{ + return m_system->setCustomCursorShape(bitmap, mask, sizex, sizey, hotX, hotY, canInvertColor); +} + +void GHOST_WindowWayland::setTitle(const char *title) +{ + xdg_toplevel_set_title(w->xdg_toplevel, title); + xdg_toplevel_set_app_id(w->xdg_toplevel, title); + this->title = title; +} + +std::string GHOST_WindowWayland::getTitle() const +{ + return this->title.empty() ? "untitled" : this->title; +} + +void GHOST_WindowWayland::getWindowBounds(GHOST_Rect &bounds) const +{ + getClientBounds(bounds); +} + +void GHOST_WindowWayland::getClientBounds(GHOST_Rect &bounds) const +{ + bounds.set(0, 0, w->width, w->height); +} + +GHOST_TSuccess GHOST_WindowWayland::setClientWidth(GHOST_TUns32 width) +{ + return setClientSize(width, GHOST_TUns32(w->height)); +} + +GHOST_TSuccess GHOST_WindowWayland::setClientHeight(GHOST_TUns32 height) +{ + return setClientSize(GHOST_TUns32(w->width), height); +} + +GHOST_TSuccess GHOST_WindowWayland::setClientSize(GHOST_TUns32 width, GHOST_TUns32 height) +{ + wl_egl_window_resize(w->egl_window, int(width), int(height), 0, 0); + return GHOST_kSuccess; +} + +void GHOST_WindowWayland::screenToClient(GHOST_TInt32 inX, + GHOST_TInt32 inY, + GHOST_TInt32 &outX, + GHOST_TInt32 &outY) const +{ + outX = inX; + outY = inY; +} + +void GHOST_WindowWayland::clientToScreen(GHOST_TInt32 inX, + GHOST_TInt32 inY, + GHOST_TInt32 &outX, + GHOST_TInt32 &outY) const +{ + outX = inX; + outY = inY; +} + +GHOST_WindowWayland::~GHOST_WindowWayland() +{ + releaseNativeHandles(); + + wl_egl_window_destroy(w->egl_window); + xdg_toplevel_destroy(w->xdg_toplevel); + xdg_surface_destroy(w->xdg_surface); + wl_surface_destroy(w->surface); + + delete w; +} + +GHOST_TSuccess GHOST_WindowWayland::setWindowCursorVisibility(bool visible) +{ + return m_system->setCursorVisibility(visible); +} + +GHOST_TSuccess GHOST_WindowWayland::setState(GHOST_TWindowState state) +{ + switch (state) { + case GHOST_kWindowStateNormal: + /* Unset states. */ + switch (getState()) { + case GHOST_kWindowStateMaximized: + xdg_toplevel_unset_maximized(w->xdg_toplevel); + break; + case GHOST_kWindowStateFullScreen: + xdg_toplevel_unset_fullscreen(w->xdg_toplevel); + break; + default: + break; + } + break; + case GHOST_kWindowStateMaximized: + xdg_toplevel_set_maximized(w->xdg_toplevel); + break; + case GHOST_kWindowStateMinimized: + xdg_toplevel_set_minimized(w->xdg_toplevel); + break; + case GHOST_kWindowStateFullScreen: + xdg_toplevel_set_fullscreen(w->xdg_toplevel, nullptr); + break; + case GHOST_kWindowStateEmbedded: + return GHOST_kFailure; + } + return GHOST_kSuccess; +} + +GHOST_TWindowState GHOST_WindowWayland::getState() const +{ + if (w->is_fullscreen) { + return GHOST_kWindowStateFullScreen; + } + else if (w->is_maximised) { + return GHOST_kWindowStateMaximized; + } + else { + return GHOST_kWindowStateNormal; + } +} + +GHOST_TSuccess GHOST_WindowWayland::invalidate() +{ + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_WindowWayland::setOrder(GHOST_TWindowOrder /*order*/) +{ + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_WindowWayland::beginFullScreen() const +{ + xdg_toplevel_set_fullscreen(w->xdg_toplevel, nullptr); + return GHOST_kSuccess; +} + +GHOST_TSuccess GHOST_WindowWayland::endFullScreen() const +{ + xdg_toplevel_unset_fullscreen(w->xdg_toplevel); + return GHOST_kSuccess; +} + +/** + * \param type The type of rendering context create. + * \return Indication of success. + */ +GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType type) +{ + GHOST_Context *context; + switch (type) { + case GHOST_kDrawingContextTypeNone: + context = new GHOST_ContextNone(m_wantStereoVisual); + break; + case GHOST_kDrawingContextTypeOpenGL: + context = new GHOST_ContextEGL(m_wantStereoVisual, + EGLNativeWindowType(w->egl_window), + EGLNativeDisplayType(m_system->display()), + EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + 3, + 3, + GHOST_OPENGL_EGL_CONTEXT_FLAGS, + GHOST_OPENGL_EGL_RESET_NOTIFICATION_STRATEGY, + EGL_OPENGL_API); + break; + } + + return (context->initializeDrawingContext() == GHOST_kSuccess) ? context : nullptr; +} + +/** \} */ diff --git a/intern/ghost/intern/GHOST_WindowWayland.h b/intern/ghost/intern/GHOST_WindowWayland.h new file mode 100644 index 00000000000..39c35f77d7d --- /dev/null +++ b/intern/ghost/intern/GHOST_WindowWayland.h @@ -0,0 +1,121 @@ +/* + * 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 GHOST + * + * Declaration of GHOST_WindowWayland class. + */ + +#ifndef __GHOST_WINDOWWAYLAND_H__ +#define __GHOST_WINDOWWAYLAND_H__ + +#include "GHOST_Window.h" + +class GHOST_SystemWayland; + +struct window_t; + +class GHOST_WindowWayland : public GHOST_Window { + public: + GHOST_TSuccess hasCursorShape(GHOST_TStandardCursor cursorShape) override; + + GHOST_WindowWayland(GHOST_SystemWayland *system, + const char *title, + GHOST_TInt32 left, + GHOST_TInt32 top, + GHOST_TUns32 width, + GHOST_TUns32 height, + GHOST_TWindowState state, + const GHOST_IWindow *parentWindow, + GHOST_TDrawingContextType type, + const bool stereoVisual, + const bool exclusive); + + ~GHOST_WindowWayland() override; + + GHOST_TSuccess close(); + + GHOST_TSuccess activate(); + + GHOST_TSuccess deactivate(); + + GHOST_TSuccess notify_size(); + + protected: + GHOST_TSuccess setWindowCursorGrab(GHOST_TGrabCursorMode mode) override; + + GHOST_TSuccess setWindowCursorShape(GHOST_TStandardCursor shape) override; + + GHOST_TSuccess setWindowCustomCursorShape(GHOST_TUns8 *bitmap, + GHOST_TUns8 *mask, + int sizex, + int sizey, + int hotX, + int hotY, + bool canInvertColor) override; + + void setTitle(const char *title) override; + + std::string getTitle() const override; + + void getWindowBounds(GHOST_Rect &bounds) const override; + + void getClientBounds(GHOST_Rect &bounds) const override; + + GHOST_TSuccess setClientWidth(GHOST_TUns32 width) override; + + GHOST_TSuccess setClientHeight(GHOST_TUns32 height) override; + + GHOST_TSuccess setClientSize(GHOST_TUns32 width, GHOST_TUns32 height) override; + + void screenToClient(GHOST_TInt32 inX, + GHOST_TInt32 inY, + GHOST_TInt32 &outX, + GHOST_TInt32 &outY) const override; + + void clientToScreen(GHOST_TInt32 inX, + GHOST_TInt32 inY, + GHOST_TInt32 &outX, + GHOST_TInt32 &outY) const override; + + GHOST_TSuccess setWindowCursorVisibility(bool visible) override; + + GHOST_TSuccess setState(GHOST_TWindowState state) override; + + GHOST_TWindowState getState() const override; + + GHOST_TSuccess invalidate() override; + + GHOST_TSuccess setOrder(GHOST_TWindowOrder order) override; + + GHOST_TSuccess beginFullScreen() const override; + + GHOST_TSuccess endFullScreen() const override; + + private: + GHOST_SystemWayland *m_system; + struct window_t *w; + std::string title; + + /** + * \param type The type of rendering context create. + * \return Indication of success. + */ + GHOST_Context *newDrawingContext(GHOST_TDrawingContextType type) override; +}; + +#endif // __GHOST_WINDOWWAYLAND_H__ diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index 8b95cdd6351..55525157753 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -59,7 +59,7 @@ __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; } GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -158,7 +158,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, height = rect.bottom - rect.top; } - wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0); + wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0); m_hWnd = ::CreateWindowW(s_windowClassName, // pointer to registered class name title_16, // pointer to window name wintype, // window style @@ -173,7 +173,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, free(title_16); } else { - wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0); + wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0); m_hWnd = ::CreateWindowW(s_windowClassName, // pointer to registered class name title_16, // pointer to window name WS_MAXIMIZE, // window style @@ -430,19 +430,18 @@ HWND GHOST_WindowWin32::getHWND() const return m_hWnd; } -void GHOST_WindowWin32::setTitle(const STR_String &title) +void GHOST_WindowWin32::setTitle(const char *title) { - wchar_t *title_16 = alloc_utf16_from_8((char *)(const char *)title, 0); + wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0); ::SetWindowTextW(m_hWnd, (wchar_t *)title_16); free(title_16); } -void GHOST_WindowWin32::getTitle(STR_String &title) const +std::string GHOST_WindowWin32::getTitle() const { char buf[s_maxTitleLength]; /*CHANGE + never used yet*/ ::GetWindowText(m_hWnd, buf, s_maxTitleLength); - STR_String temp(buf); - title = buf; + return std::string(buf); } void GHOST_WindowWin32::getWindowBounds(GHOST_Rect &bounds) const diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index 8c43eca0dc2..dbed7c5ee5f 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -35,6 +35,8 @@ # include "GHOST_ImeWin32.h" #endif +#include <vector> + #include <wintab.h> #define PACKETDATA (PK_BUTTONS | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_CURSOR) #define PACKETMODE PK_BUTTONS @@ -259,7 +261,7 @@ class GHOST_WindowWin32 : public GHOST_Window { * \param parentWindowHwnd */ GHOST_WindowWin32(GHOST_SystemWin32 *system, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -294,13 +296,13 @@ class GHOST_WindowWin32 : public GHOST_Window { * Sets the title displayed in the title bar. * \param title The title to display in the title bar. */ - void setTitle(const STR_String &title); + void setTitle(const char *title); /** * Returns the title displayed in the title bar. - * \param title The title displayed in the title bar. + * \return The title displayed in the title bar. */ - void getTitle(STR_String &title) const; + std::string getTitle() const; /** * Returns the window rectangle dimensions. diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp index db9f6846b11..691f1790a2d 100644 --- a/intern/ghost/intern/GHOST_WindowX11.cpp +++ b/intern/ghost/intern/GHOST_WindowX11.cpp @@ -33,7 +33,6 @@ #include "GHOST_IconX11.h" #include "GHOST_SystemX11.h" #include "GHOST_WindowX11.h" -#include "STR_String.h" #ifdef WITH_XDND # include "GHOST_DropTargetX11.h" @@ -61,6 +60,7 @@ #include <unistd.h> #include <algorithm> +#include <limits.h> #include <math.h> #include <string> @@ -212,7 +212,7 @@ static XVisualInfo *x11_visualinfo_from_glx(Display *display, GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system, Display *display, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -414,9 +414,9 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system, /* XClassHint, title */ { XClassHint *xclasshint = XAllocClassHint(); - const int len = title.Length() + 1; + const int len = strlen(title) + 1; char *wmclass = (char *)malloc(sizeof(char) * len); - memcpy(wmclass, title.ReadPtr(), len * sizeof(char)); + memcpy(wmclass, title, len * sizeof(char)); xclasshint->res_name = wmclass; xclasshint->res_class = wmclass; XSetClassHint(m_display, m_window, xclasshint); @@ -617,7 +617,7 @@ bool GHOST_WindowX11::getValid() const return GHOST_Window::getValid() && m_valid_setup; } -void GHOST_WindowX11::setTitle(const STR_String &title) +void GHOST_WindowX11::setTitle(const char *title) { Atom name = XInternAtom(m_display, "_NET_WM_NAME", 0); Atom utf8str = XInternAtom(m_display, "UTF8_STRING", 0); @@ -627,8 +627,8 @@ void GHOST_WindowX11::setTitle(const STR_String &title) utf8str, 8, PropModeReplace, - (const unsigned char *)title.ReadPtr(), - title.Length()); + (const unsigned char *)title, + strlen(title)); /* This should convert to valid x11 string * and getTitle would need matching change */ @@ -637,13 +637,14 @@ void GHOST_WindowX11::setTitle(const STR_String &title) XFlush(m_display); } -void GHOST_WindowX11::getTitle(STR_String &title) const +std::string GHOST_WindowX11::getTitle() const { char *name = NULL; XFetchName(m_display, m_window, &name); - title = name ? name : "untitled"; + std::string title = name ? name : "untitled"; XFree(name); + return title; } void GHOST_WindowX11::getWindowBounds(GHOST_Rect &bounds) const diff --git a/intern/ghost/intern/GHOST_WindowX11.h b/intern/ghost/intern/GHOST_WindowX11.h index 4704cb45e58..4232ff40b52 100644 --- a/intern/ghost/intern/GHOST_WindowX11.h +++ b/intern/ghost/intern/GHOST_WindowX11.h @@ -37,7 +37,6 @@ #include <map> -class STR_String; class GHOST_SystemX11; #ifdef WITH_XDND @@ -69,7 +68,7 @@ class GHOST_WindowX11 : public GHOST_Window { */ GHOST_WindowX11(GHOST_SystemX11 *system, Display *display, - const STR_String &title, + const char *title, GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, @@ -85,9 +84,9 @@ class GHOST_WindowX11 : public GHOST_Window { bool getValid() const; - void setTitle(const STR_String &title); + void setTitle(const char *title); - void getTitle(STR_String &title) const; + std::string getTitle() const; void getWindowBounds(GHOST_Rect &bounds) const; diff --git a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp index 60b3b06f6a8..5e30502f7ff 100644 --- a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp +++ b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp @@ -22,7 +22,7 @@ #include <list> #include <sstream> -#if defined(WITH_X11) +#if defined(WITH_GHOST_X11) # include "GHOST_ContextGLX.h" #elif defined(WIN32) # include "GHOST_ContextD3D.h" @@ -68,7 +68,7 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { XrSystemId system_id, std::string *r_requirement_info) const override { -#if defined(WITH_X11) +#if defined(WITH_GHOST_X11) GHOST_ContextGLX *ctx_gl = static_cast<GHOST_ContextGLX *>(ghost_ctx); #else GHOST_ContextWGL *ctx_gl = static_cast<GHOST_ContextWGL *>(ghost_ctx); @@ -107,7 +107,7 @@ class GHOST_XrGraphicsBindingOpenGL : public GHOST_IXrGraphicsBinding { void initFromGhostContext(GHOST_Context *ghost_ctx) override { -#if defined(WITH_X11) +#if defined(WITH_GHOST_X11) GHOST_ContextGLX *ctx_glx = static_cast<GHOST_ContextGLX *>(ghost_ctx); XVisualInfo *visual_info = glXGetVisualFromFBConfig(ctx_glx->m_display, ctx_glx->m_fbconfig); diff --git a/intern/ghost/intern/GHOST_Xr_openxr_includes.h b/intern/ghost/intern/GHOST_Xr_openxr_includes.h index 925d6037750..9cac43b1549 100644 --- a/intern/ghost/intern/GHOST_Xr_openxr_includes.h +++ b/intern/ghost/intern/GHOST_Xr_openxr_includes.h @@ -42,7 +42,7 @@ #ifdef XR_USE_GRAPHICS_API_D3D12 # include <d3d12.h> #endif -#ifdef WITH_X11 +#ifdef WITH_GHOST_X11 # include <GL/glxew.h> #endif diff --git a/intern/ghost/test/CMakeLists.txt b/intern/ghost/test/CMakeLists.txt index eda41eb9c1a..b6e3c0ecf5f 100644 --- a/intern/ghost/test/CMakeLists.txt +++ b/intern/ghost/test/CMakeLists.txt @@ -85,8 +85,9 @@ endif() # Libraries if(UNIX AND NOT APPLE) - set(WITH_X11 ON) + set(WITH_GHOST_X11 ON) endif() + # for now... default to this add_definitions(-DWITH_GL_PROFILE_COMPAT) # BLF needs this to ignore GPU library diff --git a/intern/ghost/test/gears/GHOST_Test.cpp b/intern/ghost/test/gears/GHOST_Test.cpp index a554bfeebef..c9c497aacb4 100644 --- a/intern/ghost/test/gears/GHOST_Test.cpp +++ b/intern/ghost/test/gears/GHOST_Test.cpp @@ -27,6 +27,7 @@ #include <iostream> #include <math.h> +#include <string> #if defined(WIN32) || defined(__APPLE__) # ifdef WIN32 @@ -43,7 +44,6 @@ #endif // defined(WIN32) || defined(__APPLE__) #include "GHOST_Rect.h" -#include "STR_String.h" #include "GHOST_IEvent.h" #include "GHOST_IEventConsumer.h" @@ -427,8 +427,7 @@ Application::Application(GHOST_ISystem *system) fApp = this; // Create the main window - STR_String title1("gears - main window"); - m_mainWindow = system->createWindow(title1, + m_mainWindow = system->createWindow("gears - main window", 10, 64, 320, @@ -443,8 +442,7 @@ Application::Application(GHOST_ISystem *system) } // Create a secondary window - STR_String title2("gears - secondary window"); - m_secondaryWindow = system->createWindow(title2, + m_secondaryWindow = system->createWindow("gears - secondary window", 340, 64, 320, @@ -598,8 +596,7 @@ bool Application::processEvent(GHOST_IEvent *event) case GHOST_kKeyW: if (m_mainWindow) { - STR_String title; - m_mainWindow->getTitle(title); + std::string title = m_mainWindow->getTitle(); title += "-"; m_mainWindow->setTitle(title); } diff --git a/intern/libmv/intern/camera_intrinsics.cc b/intern/libmv/intern/camera_intrinsics.cc index 89e3d0d1178..554c4350b0a 100644 --- a/intern/libmv/intern/camera_intrinsics.cc +++ b/intern/libmv/intern/camera_intrinsics.cc @@ -24,6 +24,7 @@ using libmv::CameraIntrinsics; using libmv::DivisionCameraIntrinsics; using libmv::PolynomialCameraIntrinsics; +using libmv::NukeCameraIntrinsics; libmv_CameraIntrinsics *libmv_cameraIntrinsicsNew( const libmv_CameraIntrinsicsOptions* libmv_camera_intrinsics_options) { @@ -55,6 +56,14 @@ libmv_CameraIntrinsics *libmv_cameraIntrinsicsCopy( *division_intrinsics); break; } + case libmv::DISTORTION_MODEL_NUKE: + { + const NukeCameraIntrinsics *nuke_intrinsics = + static_cast<const NukeCameraIntrinsics*>(orig_intrinsics); + new_intrinsics = LIBMV_OBJECT_NEW(NukeCameraIntrinsics, + *nuke_intrinsics); + break; + } default: assert(!"Unknown distortion model"); } @@ -136,6 +145,25 @@ void libmv_cameraIntrinsicsUpdate( break; } + case LIBMV_DISTORTION_MODEL_NUKE: + { + assert(camera_intrinsics->GetDistortionModelType() == + libmv::DISTORTION_MODEL_NUKE); + + NukeCameraIntrinsics *nuke_intrinsics = + (NukeCameraIntrinsics *) camera_intrinsics; + + double k1 = libmv_camera_intrinsics_options->nuke_k1; + double k2 = libmv_camera_intrinsics_options->nuke_k2; + + if (nuke_intrinsics->k1() != k1 || + nuke_intrinsics->k2() != k2) { + nuke_intrinsics->SetDistortion(k1, k2); + } + + break; + } + default: assert(!"Unknown distortion model"); } @@ -189,6 +217,17 @@ void libmv_cameraIntrinsicsExtractOptions( break; } + case libmv::DISTORTION_MODEL_NUKE: + { + const NukeCameraIntrinsics *nuke_intrinsics = + static_cast<const NukeCameraIntrinsics *>(camera_intrinsics); + camera_intrinsics_options->distortion_model = + LIBMV_DISTORTION_MODEL_NUKE; + camera_intrinsics_options->nuke_k1 = nuke_intrinsics->k1(); + camera_intrinsics_options->nuke_k2 = nuke_intrinsics->k2(); + break; + } + default: assert(!"Unknown distortion model"); } @@ -316,6 +355,17 @@ static void libmv_cameraIntrinsicsFillFromOptions( break; } + case LIBMV_DISTORTION_MODEL_NUKE: + { + NukeCameraIntrinsics *nuke_intrinsics = + static_cast<NukeCameraIntrinsics*>(camera_intrinsics); + + nuke_intrinsics->SetDistortion( + camera_intrinsics_options->nuke_k1, + camera_intrinsics_options->nuke_k2); + break; + } + default: assert(!"Unknown distortion model"); } @@ -331,6 +381,9 @@ CameraIntrinsics* libmv_cameraIntrinsicsCreateFromOptions( case LIBMV_DISTORTION_MODEL_DIVISION: camera_intrinsics = LIBMV_OBJECT_NEW(DivisionCameraIntrinsics); break; + case LIBMV_DISTORTION_MODEL_NUKE: + camera_intrinsics = LIBMV_OBJECT_NEW(NukeCameraIntrinsics); + break; default: assert(!"Unknown distortion model"); } diff --git a/intern/libmv/intern/camera_intrinsics.h b/intern/libmv/intern/camera_intrinsics.h index 40a5826a9c4..b3d259893bd 100644 --- a/intern/libmv/intern/camera_intrinsics.h +++ b/intern/libmv/intern/camera_intrinsics.h @@ -29,6 +29,7 @@ typedef struct libmv_CameraIntrinsics libmv_CameraIntrinsics; enum { LIBMV_DISTORTION_MODEL_POLYNOMIAL = 0, LIBMV_DISTORTION_MODEL_DIVISION = 1, + LIBMV_DISTORTION_MODEL_NUKE = 2, }; typedef struct libmv_CameraIntrinsicsOptions { @@ -45,6 +46,9 @@ typedef struct libmv_CameraIntrinsicsOptions { // Division distortion model. double division_k1, division_k2; + + // Nuke distortion model. + double nuke_k1, nuke_k2; } libmv_CameraIntrinsicsOptions; libmv_CameraIntrinsics *libmv_cameraIntrinsicsNew( diff --git a/intern/libmv/libmv/simple_pipeline/bundle.cc b/intern/libmv/libmv/simple_pipeline/bundle.cc index 2976dd5053f..a70fdbc9888 100644 --- a/intern/libmv/libmv/simple_pipeline/bundle.cc +++ b/intern/libmv/libmv/simple_pipeline/bundle.cc @@ -66,6 +66,12 @@ enum { namespace { +bool NeedUseInvertIntrinsicsPipeline(const CameraIntrinsics *intrinsics) { + const DistortionModelType distortion_model = + intrinsics->GetDistortionModelType(); + return (distortion_model == DISTORTION_MODEL_NUKE); +} + // Apply distortion model (distort the input) on the input point in the // normalized space to get distorted coordinate in the image space. // @@ -89,8 +95,6 @@ void ApplyDistortionModelUsingIntrinsicsBlock( const T& principal_point_x = intrinsics_block[OFFSET_PRINCIPAL_POINT_X]; const T& principal_point_y = intrinsics_block[OFFSET_PRINCIPAL_POINT_Y]; - // Apply distortion to the normalized points to get (xd, yd). - // // TODO(keir): Do early bailouts for zero distortion; these are expensive // jet operations. switch (invariant_intrinsics->GetDistortionModelType()) { @@ -127,11 +131,82 @@ void ApplyDistortionModelUsingIntrinsicsBlock( distorted_x, distorted_y); return; } + + case DISTORTION_MODEL_NUKE: + { + LOG(FATAL) << "Unsupported distortion model."; + return; + } + } + + LOG(FATAL) << "Unknown distortion model."; +} + +// Invert distortion model (undistort the input) on the input point in the +// image space to get undistorted coordinate in the normalized space. +// +// Using intrinsics values from the parameter block, which makes this function +// suitable for use from a cost functor. +// +// Only use for distortion models which are analytically defined for their +// Invert() function. +// +// The invariant_intrinsics are used to access intrinsics which are never +// packed into parameter block: for example, distortion model type and image +// dimension. +template<typename T> +void InvertDistortionModelUsingIntrinsicsBlock( + const CameraIntrinsics *invariant_intrinsics, + const T* const intrinsics_block, + const T& image_x, const T& image_y, + T* normalized_x, T* normalized_y) { + // Unpack the intrinsics. + const T& focal_length = intrinsics_block[OFFSET_FOCAL_LENGTH]; + const T& principal_point_x = intrinsics_block[OFFSET_PRINCIPAL_POINT_X]; + const T& principal_point_y = intrinsics_block[OFFSET_PRINCIPAL_POINT_Y]; + + // TODO(keir): Do early bailouts for zero distortion; these are expensive + // jet operations. + switch (invariant_intrinsics->GetDistortionModelType()) { + case DISTORTION_MODEL_POLYNOMIAL: + case DISTORTION_MODEL_DIVISION: + LOG(FATAL) << "Unsupported distortion model."; + return; + + case DISTORTION_MODEL_NUKE: + { + const T& k1 = intrinsics_block[OFFSET_K1]; + const T& k2 = intrinsics_block[OFFSET_K2]; + + InvertNukeDistortionModel(focal_length, + focal_length, + principal_point_x, + principal_point_y, + invariant_intrinsics->image_width(), + invariant_intrinsics->image_height(), + k1, k2, + image_x, image_y, + normalized_x, normalized_y); + return; + } } LOG(FATAL) << "Unknown distortion model."; } +template<typename T> +void NormalizedToImageSpace(const T* const intrinsics_block, + const T& normalized_x, const T& normalized_y, + T* image_x, T* image_y) { + // Unpack the intrinsics. + const T& focal_length = intrinsics_block[OFFSET_FOCAL_LENGTH]; + const T& principal_point_x = intrinsics_block[OFFSET_PRINCIPAL_POINT_X]; + const T& principal_point_y = intrinsics_block[OFFSET_PRINCIPAL_POINT_Y]; + + *image_x = normalized_x * focal_length + principal_point_x; + *image_y = normalized_y * focal_length + principal_point_y; +} + // Cost functor which computes reprojection error of 3D point X on camera // defined by angle-axis rotation and it's translation (which are in the same // block due to optimization reasons). @@ -191,6 +266,81 @@ struct ReprojectionErrorApplyIntrinsics { const double weight_; }; +// Cost functor which computes reprojection error of 3D point X on camera +// defined by angle-axis rotation and it's translation (which are in the same +// block due to optimization reasons). +// +// This functor can only be used for distortion models which have analytically +// defined Invert() function. +struct ReprojectionErrorInvertIntrinsics { + ReprojectionErrorInvertIntrinsics( + const CameraIntrinsics *invariant_intrinsics, + const double observed_distorted_x, + const double observed_distorted_y, + const double weight) + : invariant_intrinsics_(invariant_intrinsics), + observed_distorted_x_(observed_distorted_x), + observed_distorted_y_(observed_distorted_y), + weight_(weight) {} + + template <typename T> + bool operator()(const T* const intrinsics, + const T* const R_t, // Rotation denoted by angle axis + // followed with translation + const T* const X, // Point coordinates 3x1. + T* residuals) const { + // Unpack the intrinsics. + const T& focal_length = intrinsics[OFFSET_FOCAL_LENGTH]; + const T& principal_point_x = intrinsics[OFFSET_PRINCIPAL_POINT_X]; + const T& principal_point_y = intrinsics[OFFSET_PRINCIPAL_POINT_Y]; + + // Compute projective coordinates: x = RX + t. + T x[3]; + + ceres::AngleAxisRotatePoint(R_t, X, x); + x[0] += R_t[3]; + x[1] += R_t[4]; + x[2] += R_t[5]; + + // Prevent points from going behind the camera. + if (x[2] < T(0)) { + return false; + } + + // Compute normalized coordinates: x /= x[2]. + T xn = x[0] / x[2]; + T yn = x[1] / x[2]; + + // Compute image space coordinate from normalized. + T predicted_x = focal_length * xn + principal_point_x; + T predicted_y = focal_length * yn + principal_point_y; + + T observed_undistorted_normalized_x, observed_undistorted_normalized_y; + InvertDistortionModelUsingIntrinsicsBlock( + invariant_intrinsics_, + intrinsics, + T(observed_distorted_x_), T(observed_distorted_y_), + &observed_undistorted_normalized_x, &observed_undistorted_normalized_y); + + T observed_undistorted_image_x, observed_undistorted_image_y; + NormalizedToImageSpace( + intrinsics, + observed_undistorted_normalized_x, observed_undistorted_normalized_y, + &observed_undistorted_image_x, &observed_undistorted_image_y); + + // The error is the difference between the predicted and observed position. + residuals[0] = (predicted_x - observed_undistorted_image_x) * weight_; + residuals[1] = (predicted_y - observed_undistorted_image_y) * weight_; + + return true; + } + + const CameraIntrinsics *invariant_intrinsics_; + const double observed_distorted_x_; + const double observed_distorted_y_; + const double weight_; +}; + // Print a message to the log which camera intrinsics are gonna to be optimized. void BundleIntrinsicsLogMessage(const int bundle_intrinsics) { if (bundle_intrinsics == BUNDLE_NO_INTRINSICS) { @@ -421,14 +571,25 @@ void AddResidualBlockToProblem(const CameraIntrinsics *invariant_intrinsics, double *camera_R_t, EuclideanPoint *point, ceres::Problem* problem) { - AddResidualBlockToProblemImpl<ReprojectionErrorApplyIntrinsics>( - invariant_intrinsics, - marker.x, marker.y, - marker_weight, - intrinsics_block, - camera_R_t, - point, - problem); + if (NeedUseInvertIntrinsicsPipeline(invariant_intrinsics)) { + AddResidualBlockToProblemImpl<ReprojectionErrorInvertIntrinsics>( + invariant_intrinsics, + marker.x, marker.y, + marker_weight, + intrinsics_block, + camera_R_t, + point, + problem); + } else { + AddResidualBlockToProblemImpl<ReprojectionErrorApplyIntrinsics>( + invariant_intrinsics, + marker.x, marker.y, + marker_weight, + intrinsics_block, + camera_R_t, + point, + problem); + } } // This is an utility function to only bundle 3D position of diff --git a/intern/libmv/libmv/simple_pipeline/camera_intrinsics.cc b/intern/libmv/libmv/simple_pipeline/camera_intrinsics.cc index 5e4e07b3c4c..a95b394ad06 100644 --- a/intern/libmv/libmv/simple_pipeline/camera_intrinsics.cc +++ b/intern/libmv/libmv/simple_pipeline/camera_intrinsics.cc @@ -131,6 +131,8 @@ void CameraIntrinsics::ResetLookupGrids() { undistort_.Reset(); } +// Polynomial model. + PolynomialCameraIntrinsics::PolynomialCameraIntrinsics() : CameraIntrinsics() { SetRadialDistortion(0.0, 0.0, 0.0); @@ -193,6 +195,8 @@ void PolynomialCameraIntrinsics::InvertIntrinsics( normalized_y); } +// Division model. + DivisionCameraIntrinsics::DivisionCameraIntrinsics() : CameraIntrinsics() { SetDistortion(0.0, 0.0); @@ -241,6 +245,57 @@ void DivisionCameraIntrinsics::InvertIntrinsics(double image_x, normalized_y); } +// Nuke model. + +NukeCameraIntrinsics::NukeCameraIntrinsics() + : CameraIntrinsics() { + SetDistortion(0.0, 0.0); +} + +NukeCameraIntrinsics::NukeCameraIntrinsics( + const NukeCameraIntrinsics &from) + : CameraIntrinsics(from) { + SetDistortion(from.k1(), from.k1()); +} + +void NukeCameraIntrinsics::SetDistortion(double k1, double k2) { + parameters_[OFFSET_K1] = k1; + parameters_[OFFSET_K2] = k2; + ResetLookupGrids(); +} + +void NukeCameraIntrinsics::ApplyIntrinsics(double normalized_x, + double normalized_y, + double *image_x, + double *image_y) const { + ApplyNukeDistortionModel(focal_length_x(), + focal_length_y(), + principal_point_x(), + principal_point_y(), + image_width(), image_height(), + k1(), k2(), + normalized_x, + normalized_y, + image_x, + image_y); +} + +void NukeCameraIntrinsics::InvertIntrinsics(double image_x, + double image_y, + double *normalized_x, + double *normalized_y) const { + InvertNukeDistortionModel(focal_length_x(), + focal_length_y(), + principal_point_x(), + principal_point_y(), + image_width(), image_height(), + k1(), k2(), + image_x, + image_y, + normalized_x, + normalized_y); +} + std::ostream& operator <<(std::ostream &os, const CameraIntrinsics &intrinsics) { if (intrinsics.focal_length_x() == intrinsics.focal_length_x()) { @@ -281,6 +336,14 @@ std::ostream& operator <<(std::ostream &os, PRINT_NONZERO_COEFFICIENT(division_intrinsics, k2); break; } + case DISTORTION_MODEL_NUKE: + { + const NukeCameraIntrinsics *nuke_intrinsics = + static_cast<const NukeCameraIntrinsics *>(&intrinsics); + PRINT_NONZERO_COEFFICIENT(nuke_intrinsics, k1); + PRINT_NONZERO_COEFFICIENT(nuke_intrinsics, k2); + break; + } default: LOG(FATAL) << "Unknown distortion model."; } diff --git a/intern/libmv/libmv/simple_pipeline/camera_intrinsics.h b/intern/libmv/libmv/simple_pipeline/camera_intrinsics.h index 6a3ade81089..782fd56c54c 100644 --- a/intern/libmv/libmv/simple_pipeline/camera_intrinsics.h +++ b/intern/libmv/libmv/simple_pipeline/camera_intrinsics.h @@ -276,7 +276,7 @@ class CameraIntrinsics { class PolynomialCameraIntrinsics : public CameraIntrinsics { public: // This constants defines an offset of corresponding coefficients - // in the arameters_ array. + // in the parameters_ array. enum { OFFSET_K1, OFFSET_K2, @@ -342,7 +342,7 @@ class PolynomialCameraIntrinsics : public CameraIntrinsics { class DivisionCameraIntrinsics : public CameraIntrinsics { public: // This constants defines an offset of corresponding coefficients - // in the arameters_ array. + // in the parameters_ array. enum { OFFSET_K1, OFFSET_K2, @@ -393,6 +393,60 @@ class DivisionCameraIntrinsics : public CameraIntrinsics { double parameters_[NUM_PARAMETERS]; }; +class NukeCameraIntrinsics : public CameraIntrinsics { + public: + // This constants defines an offset of corresponding coefficients + // in the parameters_ array. + enum { + OFFSET_K1, + OFFSET_K2, + + // This defines the size of array which we need to have in order + // to store all the coefficients. + NUM_PARAMETERS, + }; + + NukeCameraIntrinsics(); + NukeCameraIntrinsics(const NukeCameraIntrinsics &from); + + DistortionModelType GetDistortionModelType() const { + return DISTORTION_MODEL_NUKE; + } + + int num_distortion_parameters() const { return NUM_PARAMETERS; } + double *distortion_parameters() { return parameters_; }; + const double *distortion_parameters() const { return parameters_; }; + + double k1() const { return parameters_[OFFSET_K1]; } + double k2() const { return parameters_[OFFSET_K2]; } + + // Set radial distortion coeffcients. + void SetDistortion(double k1, double k2); + + // Apply camera intrinsics to the normalized point to get image coordinates. + // + // This applies the lens distortion to a point which is in normalized + // camera coordinates (i.e. the principal point is at (0, 0)) to get image + // coordinates in pixels. + void ApplyIntrinsics(double normalized_x, + double normalized_y, + double *image_x, + double *image_y) const; + + // Invert camera intrinsics on the image point to get normalized coordinates. + // + // This reverses the effect of lens distortion on a point which is in image + // coordinates to get normalized camera coordinates. + void InvertIntrinsics(double image_x, + double image_y, + double *normalized_x, + double *normalized_y) const; + + private: + // Double-parameter division distortion model. + double parameters_[NUM_PARAMETERS]; +}; + /// A human-readable representation of the camera intrinsic parameters. std::ostream& operator <<(std::ostream &os, const CameraIntrinsics &intrinsics); diff --git a/intern/libmv/libmv/simple_pipeline/distortion_models.cc b/intern/libmv/libmv/simple_pipeline/distortion_models.cc index 9b6dca2678a..c069fc6f623 100644 --- a/intern/libmv/libmv/simple_pipeline/distortion_models.cc +++ b/intern/libmv/libmv/simple_pipeline/distortion_models.cc @@ -194,4 +194,96 @@ void InvertDivisionDistortionModel(const double focal_length_x, *normalized_y = normalized(1); } +struct ApplyNukeIntrinsicsCostFunction { + public: + typedef Vec2 FMatrixType; + typedef Vec2 XMatrixType; + + ApplyNukeIntrinsicsCostFunction(const double focal_length_x, + const double focal_length_y, + const double principal_point_x, + const double principal_point_y, + const int image_width, + const int image_height, + const double k1, + const double k2, + const double expected_normalized_x, + const double expected_normalized_y) + : focal_length_x_(focal_length_x), + focal_length_y_(focal_length_y), + principal_point_x_(principal_point_x), + principal_point_y_(principal_point_y), + image_width_(image_width), + image_height_(image_height), + k1_(k1), k2_(k2), + expected_normalized_x_(expected_normalized_x), + expected_normalized_y_(expected_normalized_y) {} + + Vec2 operator()(const Vec2 &image_coordinate) const { + double actual_normalized_x, actual_normalized_y; + + InvertNukeDistortionModel(focal_length_x_, + focal_length_y_, + principal_point_x_, + principal_point_y_, + image_width_, image_height_, + k1_, k2_, + image_coordinate(0), image_coordinate(1), + &actual_normalized_x, &actual_normalized_y); + + Vec2 fx; + fx << (actual_normalized_x - expected_normalized_x_), + (actual_normalized_y - expected_normalized_y_); + return fx; + } + double focal_length_x_; + double focal_length_y_; + double principal_point_x_; + double principal_point_y_; + int image_width_; + int image_height_; + double k1_, k2_; + double expected_normalized_x_, expected_normalized_y_; +}; + +void ApplyNukeDistortionModel(const double focal_length_x, + const double focal_length_y, + const double principal_point_x, + const double principal_point_y, + const int image_width, + const int image_height, + const double k1, + const double k2, + const double normalized_x, + const double normalized_y, + double *image_x, + double *image_y) { + // Compute the initial guess. For a camera with no distortion, this will also + // be the final answer; the LM iteration will terminate immediately. + Vec2 image; + image(0) = normalized_x * focal_length_x + principal_point_x; + image(1) = normalized_y * focal_length_y + principal_point_y; + + // TODO(sergey): Use Ceres minimizer instead. + typedef LevenbergMarquardt<ApplyNukeIntrinsicsCostFunction> Solver; + + ApplyNukeIntrinsicsCostFunction intrinsics_cost(focal_length_x, + focal_length_y, + principal_point_x, + principal_point_y, + image_width, + image_height, + k1, k2, + normalized_x, normalized_y); + Solver::SolverParameters params; + Solver solver(intrinsics_cost); + + /*Solver::Results results =*/ solver.minimize(params, &image); + + // TODO(keir): Better error handling. + + *image_x = image(0); + *image_y = image(1); +} + } // namespace libmv diff --git a/intern/libmv/libmv/simple_pipeline/distortion_models.h b/intern/libmv/libmv/simple_pipeline/distortion_models.h index 4f8e2295a0e..6ba351d729d 100644 --- a/intern/libmv/libmv/simple_pipeline/distortion_models.h +++ b/intern/libmv/libmv/simple_pipeline/distortion_models.h @@ -21,11 +21,14 @@ #ifndef LIBMV_SIMPLE_PIPELINE_DISTORTION_MODELS_H_ #define LIBMV_SIMPLE_PIPELINE_DISTORTION_MODELS_H_ +#include <algorithm> + namespace libmv { enum DistortionModelType { DISTORTION_MODEL_POLYNOMIAL, - DISTORTION_MODEL_DIVISION + DISTORTION_MODEL_DIVISION, + DISTORTION_MODEL_NUKE, }; // Invert camera intrinsics on the image point to get normalized coordinates. @@ -126,6 +129,79 @@ inline void ApplyDivisionDistortionModel(const T &focal_length_x, *image_y = focal_length_y * yd + principal_point_y; } +// Invert camera intrinsics on the image point to get normalized coordinates. +// This inverts the radial lens distortion to a point which is in image pixel +// coordinates to get normalized coordinates. +// +// Uses Nuke distortion model. +template <typename T> +void InvertNukeDistortionModel(const T &focal_length_x, + const T &focal_length_y, + const T &principal_point_x, + const T &principal_point_y, + const int image_width, + const int image_height, + const T &k1, + const T &k2, + const T &image_x, + const T &image_y, + T *normalized_x, + T *normalized_y) { + // According to the documentation: + // + // xu = xd / (1 + k0 * rd^2 + k1 * rd^4) + // yu = yd / (1 + k0 * rd^2 + k1 * rd^4) + // + // Legend: + // (xd, yd) are the distorted cartesian coordinates, + // (rd, phid) are the distorted polar coordinates, + // (xu, yu) are the undistorted cartesian coordinates, + // (ru, phiu) are the undistorted polar coordinates, + // the k-values are the distortion coefficients. + // + // The coordinate systems are relative to the distortion centre. + + const int max_image_size = std::max(image_width, image_height); + const double max_half_image_size = max_image_size * 0.5; + + if (max_half_image_size == 0.0) { + *normalized_x = image_x * max_half_image_size / focal_length_x; + *normalized_y = image_y * max_half_image_size / focal_length_y; + return; + } + + const T xd = (image_x - principal_point_x) / max_half_image_size; + const T yd = (image_y - principal_point_y) / max_half_image_size; + + T rd2 = xd*xd + yd*yd; + T rd4 = rd2 * rd2; + T r_coeff = T(1) / (T(1) + k1*rd2 + k2*rd4); + T xu = xd * r_coeff; + T yu = yd * r_coeff; + + *normalized_x = xu * max_half_image_size / focal_length_x; + *normalized_y = yu * max_half_image_size / focal_length_y; +} + +// Apply camera intrinsics to the normalized point to get image coordinates. +// This applies the radial lens distortion to a point which is in normalized +// camera coordinates (i.e. the principal point is at (0, 0)) to get image +// coordinates in pixels. Templated for use with autodifferentiation. +// +// Uses Nuke distortion model. +void ApplyNukeDistortionModel(const double focal_length_x, + const double focal_length_y, + const double principal_point_x, + const double principal_point_y, + const int image_width, + const int image_height, + const double k1, + const double k2, + const double normalized_x, + const double normalized_y, + double *image_x, + double *image_y); + } // namespace libmv #endif // LIBMV_SIMPLE_PIPELINE_DISTORTION_MODELS_H_ diff --git a/intern/mantaflow/intern/MANTA_main.cpp b/intern/mantaflow/intern/MANTA_main.cpp index a56ce4c1f60..d59c7464934 100644 --- a/intern/mantaflow/intern/MANTA_main.cpp +++ b/intern/mantaflow/intern/MANTA_main.cpp @@ -48,7 +48,16 @@ #include "MEM_guardedalloc.h" -std::atomic<int> MANTA::solverID(0); +using std::cerr; +using std::cout; +using std::endl; +using std::ifstream; +using std::istringstream; +using std::ofstream; +using std::ostringstream; +using std::to_string; + +atomic<int> MANTA::solverID(0); int MANTA::with_debug(0); /* Number of particles that the cache reads at once (with zlib). */ @@ -61,29 +70,30 @@ int MANTA::with_debug(0); MANTA::MANTA(int *res, FluidModifierData *mmd) : mCurrentID(++solverID) { if (with_debug) - std::cout << "FLUID: " << mCurrentID << " with res(" << res[0] << ", " << res[1] << ", " - << res[2] << ")" << std::endl; - - mmd->domain->fluid = this; - - mUsingLiquid = (mmd->domain->type == FLUID_DOMAIN_TYPE_LIQUID); - mUsingSmoke = (mmd->domain->type == FLUID_DOMAIN_TYPE_GAS); - mUsingNoise = (mmd->domain->flags & FLUID_DOMAIN_USE_NOISE) && mUsingSmoke; - mUsingFractions = (mmd->domain->flags & FLUID_DOMAIN_USE_FRACTIONS) && mUsingLiquid; - mUsingMesh = (mmd->domain->flags & FLUID_DOMAIN_USE_MESH) && mUsingLiquid; - mUsingMVel = (mmd->domain->flags & FLUID_DOMAIN_USE_SPEED_VECTORS) && mUsingLiquid; - mUsingGuiding = (mmd->domain->flags & FLUID_DOMAIN_USE_GUIDE); - mUsingDrops = (mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) && mUsingLiquid; - mUsingBubbles = (mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) && mUsingLiquid; - mUsingFloats = (mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) && mUsingLiquid; - mUsingTracers = (mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_TRACER) && mUsingLiquid; - - mUsingHeat = (mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_HEAT) && mUsingSmoke; - mUsingFire = (mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_FIRE) && mUsingSmoke; - mUsingColors = (mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) && mUsingSmoke; - mUsingObstacle = (mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE); - mUsingInvel = (mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_INVEL); - mUsingOutflow = (mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW); + cout << "FLUID: " << mCurrentID << " with res(" << res[0] << ", " << res[1] << ", " << res[2] + << ")" << endl; + + FluidDomainSettings *mds = mmd->domain; + mds->fluid = this; + + mUsingLiquid = (mds->type == FLUID_DOMAIN_TYPE_LIQUID); + mUsingSmoke = (mds->type == FLUID_DOMAIN_TYPE_GAS); + mUsingNoise = (mds->flags & FLUID_DOMAIN_USE_NOISE) && mUsingSmoke; + mUsingFractions = (mds->flags & FLUID_DOMAIN_USE_FRACTIONS) && mUsingLiquid; + mUsingMesh = (mds->flags & FLUID_DOMAIN_USE_MESH) && mUsingLiquid; + mUsingMVel = (mds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS) && mUsingLiquid; + mUsingGuiding = (mds->flags & FLUID_DOMAIN_USE_GUIDE); + mUsingDrops = (mds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) && mUsingLiquid; + mUsingBubbles = (mds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) && mUsingLiquid; + mUsingFloats = (mds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) && mUsingLiquid; + mUsingTracers = (mds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER) && mUsingLiquid; + + mUsingHeat = (mds->active_fields & FLUID_DOMAIN_ACTIVE_HEAT) && mUsingSmoke; + mUsingFire = (mds->active_fields & FLUID_DOMAIN_ACTIVE_FIRE) && mUsingSmoke; + mUsingColors = (mds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) && mUsingSmoke; + mUsingObstacle = (mds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE); + mUsingInvel = (mds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL); + mUsingOutflow = (mds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW); // Simulation constants mTempAmb = 0; // TODO: Maybe use this later for buoyancy calculation @@ -91,10 +101,8 @@ MANTA::MANTA(int *res, FluidModifierData *mmd) : mCurrentID(++solverID) mResY = res[1]; mResZ = res[2]; mMaxRes = MAX3(mResX, mResY, mResZ); - mConstantScaling = 64.0f / mMaxRes; - mConstantScaling = (mConstantScaling < 1.0f) ? 1.0f : mConstantScaling; mTotalCells = mResX * mResY * mResZ; - mResGuiding = mmd->domain->res; + mResGuiding = mds->res; // Smoke low res grids mDensity = nullptr; @@ -184,86 +192,89 @@ MANTA::MANTA(int *res, FluidModifierData *mmd) : mCurrentID(++solverID) // Setup Mantaflow in Python initializeMantaflow(); + // Initializa RNA map with values that Python will need + initializeRNAMap(mmd); + // Initialize Mantaflow variables in Python // Liquid if (mUsingLiquid) { - initDomain(mmd); - initLiquid(mmd); + initDomain(); + initLiquid(); if (mUsingObstacle) - initObstacle(mmd); + initObstacle(); if (mUsingInvel) - initInVelocity(mmd); + initInVelocity(); if (mUsingOutflow) - initOutflow(mmd); + initOutflow(); if (mUsingDrops || mUsingBubbles || mUsingFloats || mUsingTracers) { - mUpresParticle = mmd->domain->particle_scale; + mUpresParticle = mds->particle_scale; mResXParticle = mUpresParticle * mResX; mResYParticle = mUpresParticle * mResY; mResZParticle = mUpresParticle * mResZ; mTotalCellsParticles = mResXParticle * mResYParticle * mResZParticle; - initSndParts(mmd); - initLiquidSndParts(mmd); + initSndParts(); + initLiquidSndParts(); } if (mUsingMesh) { - mUpresMesh = mmd->domain->mesh_scale; + mUpresMesh = mds->mesh_scale; mResXMesh = mUpresMesh * mResX; mResYMesh = mUpresMesh * mResY; mResZMesh = mUpresMesh * mResZ; mTotalCellsMesh = mResXMesh * mResYMesh * mResZMesh; // Initialize Mantaflow variables in Python - initMesh(mmd); - initLiquidMesh(mmd); + initMesh(); + initLiquidMesh(); } if (mUsingGuiding) { - mResGuiding = (mmd->domain->guide_parent) ? mmd->domain->guide_res : mmd->domain->res; - initGuiding(mmd); + mResGuiding = (mds->guide_parent) ? mds->guide_res : mds->res; + initGuiding(); } if (mUsingFractions) { - initFractions(mmd); + initFractions(); } } // Smoke if (mUsingSmoke) { - initDomain(mmd); - initSmoke(mmd); + initDomain(); + initSmoke(); if (mUsingHeat) - initHeat(mmd); + initHeat(); if (mUsingFire) - initFire(mmd); + initFire(); if (mUsingColors) - initColors(mmd); + initColors(); if (mUsingObstacle) - initObstacle(mmd); + initObstacle(); if (mUsingInvel) - initInVelocity(mmd); + initInVelocity(); if (mUsingOutflow) - initOutflow(mmd); + initOutflow(); if (mUsingGuiding) { - mResGuiding = (mmd->domain->guide_parent) ? mmd->domain->guide_res : mmd->domain->res; - initGuiding(mmd); + mResGuiding = (mds->guide_parent) ? mds->guide_res : mds->res; + initGuiding(); } if (mUsingNoise) { - int amplify = mmd->domain->noise_scale; + int amplify = mds->noise_scale; mResXNoise = amplify * mResX; mResYNoise = amplify * mResY; mResZNoise = amplify * mResZ; mTotalCellsHigh = mResXNoise * mResYNoise * mResZNoise; // Initialize Mantaflow variables in Python - initNoise(mmd); - initSmokeNoise(mmd); + initNoise(); + initSmokeNoise(); if (mUsingFire) - initFireHigh(mmd); + initFireHigh(); if (mUsingColors) - initColorsHigh(mmd); + initColorsHigh(); } } updatePointers(); @@ -272,32 +283,32 @@ MANTA::MANTA(int *res, FluidModifierData *mmd) : mCurrentID(++solverID) void MANTA::initDomain(FluidModifierData *mmd) { // Vector will hold all python commands that are to be executed - std::vector<std::string> pythonCommands; + vector<string> pythonCommands; // Set manta debug level first pythonCommands.push_back(manta_import + manta_debuglevel); - std::ostringstream ss; + ostringstream ss; ss << "set_manta_debuglevel(" << with_debug << ")"; pythonCommands.push_back(ss.str()); // Now init basic fluid domain - std::string tmpString = fluid_variables + fluid_solver + fluid_alloc + fluid_cache_helper + - fluid_bake_multiprocessing + fluid_bake_data + fluid_bake_noise + - fluid_bake_mesh + fluid_bake_particles + fluid_bake_guiding + - fluid_file_import + fluid_file_export + fluid_save_data + - fluid_load_data + fluid_pre_step + fluid_post_step + - fluid_adapt_time_step + fluid_time_stepping; - std::string finalString = parseScript(tmpString, mmd); + string tmpString = fluid_variables + fluid_solver + fluid_alloc + fluid_cache_helper + + fluid_bake_multiprocessing + fluid_bake_data + fluid_bake_noise + + fluid_bake_mesh + fluid_bake_particles + fluid_bake_guiding + + fluid_file_import + fluid_file_export + fluid_save_data + fluid_load_data + + fluid_pre_step + fluid_post_step + fluid_adapt_time_step + + fluid_time_stepping; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); } void MANTA::initNoise(FluidModifierData *mmd) { - std::vector<std::string> pythonCommands; - std::string tmpString = fluid_variables_noise + fluid_solver_noise; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = fluid_variables_noise + fluid_solver_noise; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -305,10 +316,10 @@ void MANTA::initNoise(FluidModifierData *mmd) void MANTA::initSmoke(FluidModifierData *mmd) { - std::vector<std::string> pythonCommands; - std::string tmpString = smoke_variables + smoke_alloc + smoke_adaptive_step + smoke_save_data + - smoke_load_data + smoke_step; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = smoke_variables + smoke_alloc + smoke_adaptive_step + smoke_save_data + + smoke_load_data + smoke_step; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -316,10 +327,10 @@ void MANTA::initSmoke(FluidModifierData *mmd) void MANTA::initSmokeNoise(FluidModifierData *mmd) { - std::vector<std::string> pythonCommands; - std::string tmpString = smoke_variables_noise + smoke_alloc_noise + smoke_wavelet_noise + - smoke_save_noise + smoke_load_noise + smoke_step_noise; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = smoke_variables_noise + smoke_alloc_noise + smoke_wavelet_noise + + smoke_save_noise + smoke_load_noise + smoke_step_noise; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -329,9 +340,9 @@ void MANTA::initSmokeNoise(FluidModifierData *mmd) void MANTA::initHeat(FluidModifierData *mmd) { if (!mHeat) { - std::vector<std::string> pythonCommands; - std::string tmpString = smoke_alloc_heat + smoke_with_heat; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = smoke_alloc_heat + smoke_with_heat; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -342,9 +353,9 @@ void MANTA::initHeat(FluidModifierData *mmd) void MANTA::initFire(FluidModifierData *mmd) { if (!mFuel) { - std::vector<std::string> pythonCommands; - std::string tmpString = smoke_alloc_fire + smoke_with_fire; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = smoke_alloc_fire + smoke_with_fire; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -355,9 +366,9 @@ void MANTA::initFire(FluidModifierData *mmd) void MANTA::initFireHigh(FluidModifierData *mmd) { if (!mFuelHigh) { - std::vector<std::string> pythonCommands; - std::string tmpString = smoke_alloc_fire_noise + smoke_with_fire; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = smoke_alloc_fire_noise + smoke_with_fire; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -368,9 +379,9 @@ void MANTA::initFireHigh(FluidModifierData *mmd) void MANTA::initColors(FluidModifierData *mmd) { if (!mColorR) { - std::vector<std::string> pythonCommands; - std::string tmpString = smoke_alloc_colors + smoke_init_colors + smoke_with_colors; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = smoke_alloc_colors + smoke_init_colors + smoke_with_colors; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -381,9 +392,9 @@ void MANTA::initColors(FluidModifierData *mmd) void MANTA::initColorsHigh(FluidModifierData *mmd) { if (!mColorRHigh) { - std::vector<std::string> pythonCommands; - std::string tmpString = smoke_alloc_colors_noise + smoke_init_colors_noise + smoke_with_colors; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = smoke_alloc_colors_noise + smoke_init_colors_noise + smoke_with_colors; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -394,10 +405,10 @@ void MANTA::initColorsHigh(FluidModifierData *mmd) void MANTA::initLiquid(FluidModifierData *mmd) { if (!mPhiIn) { - std::vector<std::string> pythonCommands; - std::string tmpString = liquid_variables + liquid_alloc + liquid_init_phi + liquid_save_data + - liquid_load_data + liquid_adaptive_step + liquid_step; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = liquid_variables + liquid_alloc + liquid_init_phi + liquid_save_data + + liquid_load_data + liquid_adaptive_step + liquid_step; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -407,9 +418,9 @@ void MANTA::initLiquid(FluidModifierData *mmd) void MANTA::initMesh(FluidModifierData *mmd) { - std::vector<std::string> pythonCommands; - std::string tmpString = fluid_variables_mesh + fluid_solver_mesh + liquid_load_mesh; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = fluid_variables_mesh + fluid_solver_mesh + liquid_load_mesh; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -418,9 +429,9 @@ void MANTA::initMesh(FluidModifierData *mmd) void MANTA::initLiquidMesh(FluidModifierData *mmd) { - std::vector<std::string> pythonCommands; - std::string tmpString = liquid_alloc_mesh + liquid_step_mesh + liquid_save_mesh; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = liquid_alloc_mesh + liquid_step_mesh + liquid_save_mesh; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -430,9 +441,9 @@ void MANTA::initLiquidMesh(FluidModifierData *mmd) void MANTA::initObstacle(FluidModifierData *mmd) { if (!mPhiObsIn) { - std::vector<std::string> pythonCommands; - std::string tmpString = fluid_alloc_obstacle + fluid_with_obstacle; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = fluid_alloc_obstacle + fluid_with_obstacle; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -443,10 +454,10 @@ void MANTA::initObstacle(FluidModifierData *mmd) void MANTA::initGuiding(FluidModifierData *mmd) { if (!mPhiGuideIn) { - std::vector<std::string> pythonCommands; - std::string tmpString = fluid_variables_guiding + fluid_solver_guiding + fluid_alloc_guiding + - fluid_save_guiding + fluid_load_vel + fluid_load_guiding; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = fluid_variables_guiding + fluid_solver_guiding + fluid_alloc_guiding + + fluid_save_guiding + fluid_load_vel + fluid_load_guiding; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -456,9 +467,9 @@ void MANTA::initGuiding(FluidModifierData *mmd) void MANTA::initFractions(FluidModifierData *mmd) { - std::vector<std::string> pythonCommands; - std::string tmpString = fluid_alloc_fractions + fluid_with_fractions; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = fluid_alloc_fractions + fluid_with_fractions; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -468,9 +479,9 @@ void MANTA::initFractions(FluidModifierData *mmd) void MANTA::initInVelocity(FluidModifierData *mmd) { if (!mInVelocityX) { - std::vector<std::string> pythonCommands; - std::string tmpString = fluid_alloc_invel + fluid_with_invel; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = fluid_alloc_invel + fluid_with_invel; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -481,9 +492,9 @@ void MANTA::initInVelocity(FluidModifierData *mmd) void MANTA::initOutflow(FluidModifierData *mmd) { if (!mPhiOutIn) { - std::vector<std::string> pythonCommands; - std::string tmpString = fluid_alloc_outflow + fluid_with_outflow; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = fluid_alloc_outflow + fluid_with_outflow; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -493,9 +504,9 @@ void MANTA::initOutflow(FluidModifierData *mmd) void MANTA::initSndParts(FluidModifierData *mmd) { - std::vector<std::string> pythonCommands; - std::string tmpString = fluid_variables_particles + fluid_solver_particles; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = fluid_variables_particles + fluid_solver_particles; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -504,11 +515,11 @@ void MANTA::initSndParts(FluidModifierData *mmd) void MANTA::initLiquidSndParts(FluidModifierData *mmd) { if (!mSndParticleData) { - std::vector<std::string> pythonCommands; - std::string tmpString = liquid_alloc_particles + liquid_variables_particles + - liquid_step_particles + fluid_with_sndparts + liquid_load_particles + - liquid_save_particles; - std::string finalString = parseScript(tmpString, mmd); + vector<string> pythonCommands; + string tmpString = liquid_alloc_particles + liquid_variables_particles + + liquid_step_particles + fluid_with_sndparts + liquid_load_particles + + liquid_save_particles; + string finalString = parseScript(tmpString, mmd); pythonCommands.push_back(finalString); runPythonString(pythonCommands); @@ -518,19 +529,22 @@ void MANTA::initLiquidSndParts(FluidModifierData *mmd) MANTA::~MANTA() { if (with_debug) - std::cout << "~FLUID: " << mCurrentID << " with res(" << mResX << ", " << mResY << ", " - << mResZ << ")" << std::endl; + cout << "~FLUID: " << mCurrentID << " with res(" << mResX << ", " << mResY << ", " << mResZ + << ")" << endl; // Destruction string for Python - std::string tmpString = ""; - std::vector<std::string> pythonCommands; + string tmpString = ""; + vector<string> pythonCommands; bool result = false; tmpString += manta_import; tmpString += fluid_delete_all; + // Initializa RNA map with values that Python will need + initializeRNAMap(); + // Leave out mmd argument in parseScript since only looking up IDs - std::string finalString = parseScript(tmpString); + string finalString = parseScript(tmpString); pythonCommands.push_back(finalString); result = runPythonString(pythonCommands); @@ -538,12 +552,12 @@ MANTA::~MANTA() (void)result; // not needed in release } -bool MANTA::runPythonString(std::vector<std::string> commands) +bool MANTA::runPythonString(vector<string> commands) { int success = -1; PyGILState_STATE gilstate = PyGILState_Ensure(); - for (std::vector<std::string>::iterator it = commands.begin(); it != commands.end(); ++it) { - std::string command = *it; + for (vector<string>::iterator it = commands.begin(); it != commands.end(); ++it) { + string command = *it; #ifdef WIN32 // special treatment for windows when running python code @@ -568,10 +582,10 @@ bool MANTA::runPythonString(std::vector<std::string> commands) void MANTA::initializeMantaflow() { if (with_debug) - std::cout << "Fluid: Initializing Mantaflow framework" << std::endl; + cout << "Fluid: Initializing Mantaflow framework" << endl; - std::string filename = "manta_scene_" + std::to_string(mCurrentID) + ".py"; - std::vector<std::string> fill = std::vector<std::string>(); + string filename = "manta_scene_" + to_string(mCurrentID) + ".py"; + vector<string> fill = vector<string>(); // Initialize extension classes and wrappers srand(0); @@ -583,17 +597,17 @@ void MANTA::initializeMantaflow() void MANTA::terminateMantaflow() { if (with_debug) - std::cout << "Fluid: Releasing Mantaflow framework" << std::endl; + cout << "Fluid: Releasing Mantaflow framework" << endl; PyGILState_STATE gilstate = PyGILState_Ensure(); Pb::finalize(); // Namespace from Mantaflow (registry) PyGILState_Release(gilstate); } -static std::string getCacheFileEnding(char cache_format) +static string getCacheFileEnding(char cache_format) { if (MANTA::with_debug) - std::cout << "MANTA::getCacheFileEnding()" << std::endl; + cout << "MANTA::getCacheFileEnding()" << endl; switch (cache_format) { case FLUID_DOMAIN_FILE_UNI: @@ -607,409 +621,234 @@ static std::string getCacheFileEnding(char cache_format) case FLUID_DOMAIN_FILE_OBJECT: return FLUID_DOMAIN_EXTENSION_OBJ; default: - std::cerr << "Fluid Error -- Could not find file extension. Using default file extension." - << std::endl; + cerr << "Fluid Error -- Could not find file extension. Using default file extension." + << endl; return FLUID_DOMAIN_EXTENSION_UNI; } } -std::string MANTA::getRealValue(const std::string &varName, FluidModifierData *mmd) +void MANTA::initializeRNAMap(FluidModifierData *mmd) { - std::ostringstream ss; - bool is2D = false; - int tmpVar; - float tmpFloat; + if (with_debug) + cout << "MANTA::initializeRNAMap()" << endl; - if (varName == "ID") { - ss << mCurrentID; - return ss.str(); - } + mRNAMap["ID"] = to_string(mCurrentID); if (!mmd) { - std::cerr << "Fluid Error -- Invalid modifier data." << std::endl; - ss << "ERROR - INVALID MODIFIER DATA"; - return ss.str(); - } - - is2D = (mmd->domain->solver_res == 2); - - if (varName == "USING_SMOKE") - ss << ((mmd->domain->type == FLUID_DOMAIN_TYPE_GAS) ? "True" : "False"); - else if (varName == "USING_LIQUID") - ss << ((mmd->domain->type == FLUID_DOMAIN_TYPE_LIQUID) ? "True" : "False"); - else if (varName == "USING_COLORS") - ss << (mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_COLORS ? "True" : "False"); - else if (varName == "USING_HEAT") - ss << (mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_HEAT ? "True" : "False"); - else if (varName == "USING_FIRE") - ss << (mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_FIRE ? "True" : "False"); - else if (varName == "USING_NOISE") - ss << (mmd->domain->flags & FLUID_DOMAIN_USE_NOISE ? "True" : "False"); - else if (varName == "USING_OBSTACLE") - ss << (mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE ? "True" : "False"); - else if (varName == "USING_GUIDING") - ss << (mmd->domain->flags & FLUID_DOMAIN_USE_GUIDE ? "True" : "False"); - else if (varName == "USING_INVEL") - ss << (mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_INVEL ? "True" : "False"); - else if (varName == "USING_OUTFLOW") - ss << (mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW ? "True" : "False"); - else if (varName == "USING_LOG_DISSOLVE") - ss << (mmd->domain->flags & FLUID_DOMAIN_USE_DISSOLVE_LOG ? "True" : "False"); - else if (varName == "USING_DISSOLVE") - ss << (mmd->domain->flags & FLUID_DOMAIN_USE_DISSOLVE ? "True" : "False"); - else if (varName == "SOLVER_DIM") - ss << mmd->domain->solver_res; - else if (varName == "DO_OPEN") { - tmpVar = (FLUID_DOMAIN_BORDER_BACK | FLUID_DOMAIN_BORDER_FRONT | FLUID_DOMAIN_BORDER_LEFT | - FLUID_DOMAIN_BORDER_RIGHT | FLUID_DOMAIN_BORDER_BOTTOM | FLUID_DOMAIN_BORDER_TOP); - ss << (((mmd->domain->border_collisions & tmpVar) == tmpVar) ? "False" : "True"); - } - else if (varName == "BOUND_CONDITIONS") { - if (mmd->domain->solver_res == 2) { - if ((mmd->domain->border_collisions & FLUID_DOMAIN_BORDER_LEFT) == 0) - ss << "x"; - if ((mmd->domain->border_collisions & FLUID_DOMAIN_BORDER_RIGHT) == 0) - ss << "X"; - if ((mmd->domain->border_collisions & FLUID_DOMAIN_BORDER_FRONT) == 0) - ss << "y"; - if ((mmd->domain->border_collisions & FLUID_DOMAIN_BORDER_BACK) == 0) - ss << "Y"; - } - if (mmd->domain->solver_res == 3) { - if ((mmd->domain->border_collisions & FLUID_DOMAIN_BORDER_LEFT) == 0) - ss << "x"; - if ((mmd->domain->border_collisions & FLUID_DOMAIN_BORDER_RIGHT) == 0) - ss << "X"; - if ((mmd->domain->border_collisions & FLUID_DOMAIN_BORDER_FRONT) == 0) - ss << "y"; - if ((mmd->domain->border_collisions & FLUID_DOMAIN_BORDER_BACK) == 0) - ss << "Y"; - if ((mmd->domain->border_collisions & FLUID_DOMAIN_BORDER_BOTTOM) == 0) - ss << "z"; - if ((mmd->domain->border_collisions & FLUID_DOMAIN_BORDER_TOP) == 0) - ss << "Z"; - } - } - else if (varName == "BOUNDARY_WIDTH") - ss << mmd->domain->boundary_width; - else if (varName == "RES") - ss << mMaxRes; - else if (varName == "RESX") - ss << mResX; - else if (varName == "RESY") - if (is2D) { - ss << mResZ; - } - else { - ss << mResY; - } - else if (varName == "RESZ") { - if (is2D) { - ss << 1; - } - else { - ss << mResZ; - } - } - else if (varName == "FRAME_LENGTH") - ss << mmd->domain->frame_length; - else if (varName == "CFL") - ss << mmd->domain->cfl_condition; - else if (varName == "DT") - ss << mmd->domain->dt; - else if (varName == "TIMESTEPS_MIN") - ss << mmd->domain->timesteps_minimum; - else if (varName == "TIMESTEPS_MAX") - ss << mmd->domain->timesteps_maximum; - else if (varName == "TIME_TOTAL") - ss << mmd->domain->time_total; - else if (varName == "TIME_PER_FRAME") - ss << mmd->domain->time_per_frame; - else if (varName == "VORTICITY") - ss << mmd->domain->vorticity / mConstantScaling; - else if (varName == "FLAME_VORTICITY") - ss << mmd->domain->flame_vorticity / mConstantScaling; - else if (varName == "NOISE_SCALE") - ss << mmd->domain->noise_scale; - else if (varName == "MESH_SCALE") - ss << mmd->domain->mesh_scale; - else if (varName == "PARTICLE_SCALE") - ss << mmd->domain->particle_scale; - else if (varName == "NOISE_RESX") - ss << mResXNoise; - else if (varName == "NOISE_RESY") { - if (is2D) { - ss << mResZNoise; - } - else { - ss << mResYNoise; - } - } - else if (varName == "NOISE_RESZ") { - if (is2D) { - ss << 1; - } - else { - ss << mResZNoise; - } - } - else if (varName == "MESH_RESX") - ss << mResXMesh; - else if (varName == "MESH_RESY") { - if (is2D) { - ss << mResZMesh; - } - else { - ss << mResYMesh; - } - } - else if (varName == "MESH_RESZ") { - if (is2D) { - ss << 1; - } - else { - ss << mResZMesh; - } - } - else if (varName == "PARTICLE_RESX") - ss << mResXParticle; - else if (varName == "PARTICLE_RESY") { - if (is2D) { - ss << mResZParticle; - } - else { - ss << mResYParticle; - } - } - else if (varName == "PARTICLE_RESZ") { - if (is2D) { - ss << 1; - } - else { - ss << mResZParticle; - } - } - else if (varName == "GUIDING_RESX") - ss << mResGuiding[0]; - else if (varName == "GUIDING_RESY") { - if (is2D) { - ss << mResGuiding[2]; - } - else { - ss << mResGuiding[1]; - } + if (with_debug) + cout << "No modifier data given in RNA map setup - returning early" << endl; + return; } - else if (varName == "GUIDING_RESZ") { - if (is2D) { - ss << 1; - } - else { - ss << mResGuiding[2]; - } + + FluidDomainSettings *mds = mmd->domain; + bool is2D = (mds->solver_res == 2); + + int openDomain = (FLUID_DOMAIN_BORDER_BACK | FLUID_DOMAIN_BORDER_FRONT | + FLUID_DOMAIN_BORDER_LEFT | FLUID_DOMAIN_BORDER_RIGHT | + FLUID_DOMAIN_BORDER_BOTTOM | FLUID_DOMAIN_BORDER_TOP); + + string borderCollisions = ""; + if ((mds->border_collisions & FLUID_DOMAIN_BORDER_LEFT) == 0) + borderCollisions += "x"; + if ((mds->border_collisions & FLUID_DOMAIN_BORDER_RIGHT) == 0) + borderCollisions += "X"; + if ((mds->border_collisions & FLUID_DOMAIN_BORDER_FRONT) == 0) + borderCollisions += "y"; + if ((mds->border_collisions & FLUID_DOMAIN_BORDER_BACK) == 0) + borderCollisions += "Y"; + if ((mds->border_collisions & FLUID_DOMAIN_BORDER_BOTTOM) == 0) + borderCollisions += "z"; + if ((mds->border_collisions & FLUID_DOMAIN_BORDER_TOP) == 0) + borderCollisions += "Z"; + + string simulationMethod = ""; + if (mds->simulation_method & FLUID_DOMAIN_METHOD_FLIP) + simulationMethod += "'FLIP'"; + else if (mds->simulation_method & FLUID_DOMAIN_METHOD_APIC) + simulationMethod += "'APIC'"; + + string particleTypesStr = ""; + if (mds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) + particleTypesStr += "PtypeSpray"; + if (mds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) { + if (!particleTypesStr.empty()) + particleTypesStr += "|"; + particleTypesStr += "PtypeBubble"; + } + if (mds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) { + if (!particleTypesStr.empty()) + particleTypesStr += "|"; + particleTypesStr += "PtypeFoam"; + } + if (mds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER) { + if (!particleTypesStr.empty()) + particleTypesStr += "|"; + particleTypesStr += "PtypeTracer"; + } + if (particleTypesStr.empty()) + particleTypesStr = "0"; + + int particleTypes = (FLUID_DOMAIN_PARTICLE_SPRAY | FLUID_DOMAIN_PARTICLE_BUBBLE | + FLUID_DOMAIN_PARTICLE_FOAM | FLUID_DOMAIN_PARTICLE_TRACER); + + string cacheDirectory(mds->cache_directory); + + mRNAMap["USING_SMOKE"] = to_string((mds->type == FLUID_DOMAIN_TYPE_GAS) != 0); + mRNAMap["USING_LIQUID"] = to_string((mds->type == FLUID_DOMAIN_TYPE_LIQUID) != 0); + mRNAMap["USING_COLORS"] = to_string((mds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS) != 0); + mRNAMap["USING_HEAT"] = to_string((mds->active_fields & FLUID_DOMAIN_ACTIVE_HEAT) != 0); + mRNAMap["USING_FIRE"] = to_string((mds->active_fields & FLUID_DOMAIN_ACTIVE_FIRE) != 0); + mRNAMap["USING_NOISE"] = to_string((mds->flags & FLUID_DOMAIN_USE_NOISE) != 0); + mRNAMap["USING_OBSTACLE"] = to_string((mds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE) != 0); + mRNAMap["USING_GUIDING"] = to_string((mds->flags & FLUID_DOMAIN_USE_GUIDE) != 0); + mRNAMap["USING_INVEL"] = to_string((mds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL) != 0); + mRNAMap["USING_OUTFLOW"] = to_string((mds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW) != 0); + mRNAMap["USING_LOG_DISSOLVE"] = to_string((mds->flags & FLUID_DOMAIN_USE_DISSOLVE_LOG) != 0); + mRNAMap["USING_DISSOLVE"] = to_string((mds->flags & FLUID_DOMAIN_USE_DISSOLVE) != 0); + mRNAMap["SOLVER_DIM"] = to_string(mds->solver_res); + mRNAMap["DO_OPEN"] = to_string(((mds->border_collisions & openDomain) == openDomain) == 0); + mRNAMap["BOUND_CONDITIONS"] = borderCollisions; + mRNAMap["BOUNDARY_WIDTH"] = to_string(mds->boundary_width); + mRNAMap["RES"] = to_string(mMaxRes); + mRNAMap["RESX"] = to_string(mResX); + mRNAMap["RESY"] = (is2D) ? to_string(mResZ) : to_string(mResY); + mRNAMap["RESZ"] = (is2D) ? to_string(1) : to_string(mResZ); + mRNAMap["TIME_SCALE"] = to_string(mds->time_scale); + mRNAMap["FRAME_LENGTH"] = to_string(mds->frame_length); + mRNAMap["CFL"] = to_string(mds->cfl_condition); + mRNAMap["DT"] = to_string(mds->dt); + mRNAMap["TIMESTEPS_MIN"] = to_string(mds->timesteps_minimum); + mRNAMap["TIMESTEPS_MAX"] = to_string(mds->timesteps_maximum); + mRNAMap["TIME_TOTAL"] = to_string(mds->time_total); + mRNAMap["TIME_PER_FRAME"] = to_string(mds->time_per_frame); + mRNAMap["VORTICITY"] = to_string(mds->vorticity); + mRNAMap["FLAME_VORTICITY"] = to_string(mds->flame_vorticity); + mRNAMap["NOISE_SCALE"] = to_string(mds->noise_scale); + mRNAMap["MESH_SCALE"] = to_string(mds->mesh_scale); + mRNAMap["PARTICLE_SCALE"] = to_string(mds->particle_scale); + mRNAMap["NOISE_RESX"] = to_string(mResXNoise); + mRNAMap["NOISE_RESY"] = (is2D) ? to_string(mResZNoise) : to_string(mResYNoise); + mRNAMap["NOISE_RESZ"] = (is2D) ? to_string(1) : to_string(mResZNoise); + mRNAMap["MESH_RESX"] = to_string(mResXMesh); + mRNAMap["MESH_RESY"] = (is2D) ? to_string(mResZMesh) : to_string(mResYMesh); + mRNAMap["MESH_RESZ"] = (is2D) ? to_string(1) : to_string(mResZMesh); + mRNAMap["PARTICLE_RESX"] = to_string(mResXParticle); + mRNAMap["PARTICLE_RESY"] = (is2D) ? to_string(mResZParticle) : to_string(mResYParticle); + mRNAMap["PARTICLE_RESZ"] = (is2D) ? to_string(1) : to_string(mResZParticle); + mRNAMap["GUIDING_RESX"] = to_string(mResGuiding[0]); + mRNAMap["GUIDING_RESY"] = (is2D) ? to_string(mResGuiding[2]) : to_string(mResGuiding[1]); + mRNAMap["GUIDING_RESZ"] = (is2D) ? to_string(1) : to_string(mResGuiding[2]); + mRNAMap["MIN_RESX"] = to_string(mds->res_min[0]); + mRNAMap["MIN_RESY"] = to_string(mds->res_min[1]); + mRNAMap["MIN_RESZ"] = to_string(mds->res_min[2]); + mRNAMap["BASE_RESX"] = to_string(mds->base_res[0]); + mRNAMap["BASE_RESY"] = to_string(mds->base_res[1]); + mRNAMap["BASE_RESZ"] = to_string(mds->base_res[2]); + mRNAMap["WLT_STR"] = to_string(mds->noise_strength); + mRNAMap["NOISE_POSSCALE"] = to_string(mds->noise_pos_scale); + mRNAMap["NOISE_TIMEANIM"] = to_string(mds->noise_time_anim); + mRNAMap["COLOR_R"] = to_string(mds->active_color[0]); + mRNAMap["COLOR_G"] = to_string(mds->active_color[1]); + mRNAMap["COLOR_B"] = to_string(mds->active_color[2]); + mRNAMap["BUOYANCY_ALPHA"] = to_string(mds->alpha); + mRNAMap["BUOYANCY_BETA"] = to_string(mds->beta); + mRNAMap["DISSOLVE_SPEED"] = to_string(mds->diss_speed); + mRNAMap["BURNING_RATE"] = to_string(mds->burning_rate); + mRNAMap["FLAME_SMOKE"] = to_string(mds->flame_smoke); + mRNAMap["IGNITION_TEMP"] = to_string(mds->flame_ignition); + mRNAMap["MAX_TEMP"] = to_string(mds->flame_max_temp); + mRNAMap["FLAME_SMOKE_COLOR_X"] = to_string(mds->flame_smoke_color[0]); + mRNAMap["FLAME_SMOKE_COLOR_Y"] = to_string(mds->flame_smoke_color[1]); + mRNAMap["FLAME_SMOKE_COLOR_Z"] = to_string(mds->flame_smoke_color[2]); + mRNAMap["CURRENT_FRAME"] = to_string(mmd->time); + mRNAMap["START_FRAME"] = to_string(mds->cache_frame_start); + mRNAMap["END_FRAME"] = to_string(mds->cache_frame_end); + mRNAMap["CACHE_DATA_FORMAT"] = to_string(mds->cache_data_format); + mRNAMap["CACHE_MESH_FORMAT"] = to_string(mds->cache_mesh_format); + mRNAMap["CACHE_NOISE_FORMAT"] = to_string(mds->cache_noise_format); + mRNAMap["CACHE_PARTICLE_FORMAT"] = to_string(mds->cache_particle_format); + mRNAMap["SIMULATION_METHOD"] = simulationMethod; + mRNAMap["FLIP_RATIO"] = to_string(mds->flip_ratio); + mRNAMap["PARTICLE_RANDOMNESS"] = to_string(mds->particle_randomness); + mRNAMap["PARTICLE_NUMBER"] = to_string(mds->particle_number); + mRNAMap["PARTICLE_MINIMUM"] = to_string(mds->particle_minimum); + mRNAMap["PARTICLE_MAXIMUM"] = to_string(mds->particle_maximum); + mRNAMap["PARTICLE_RADIUS"] = to_string(mds->particle_radius); + mRNAMap["FRACTIONS_THRESHOLD"] = to_string(mds->fractions_threshold); + mRNAMap["MESH_CONCAVE_UPPER"] = to_string(mds->mesh_concave_upper); + mRNAMap["MESH_CONCAVE_LOWER"] = to_string(mds->mesh_concave_lower); + mRNAMap["MESH_PARTICLE_RADIUS"] = to_string(mds->mesh_particle_radius); + mRNAMap["MESH_SMOOTHEN_POS"] = to_string(mds->mesh_smoothen_pos); + mRNAMap["MESH_SMOOTHEN_NEG"] = to_string(mds->mesh_smoothen_neg); + mRNAMap["USING_MESH"] = to_string((mds->flags & FLUID_DOMAIN_USE_MESH) != 0); + mRNAMap["USING_IMPROVED_MESH"] = to_string((mds->mesh_generator == FLUID_DOMAIN_MESH_IMPROVED) != + 0); + mRNAMap["PARTICLE_BAND_WIDTH"] = to_string(mds->particle_band_width); + mRNAMap["SNDPARTICLE_TAU_MIN_WC"] = to_string(mds->sndparticle_tau_min_wc); + mRNAMap["SNDPARTICLE_TAU_MAX_WC"] = to_string(mds->sndparticle_tau_max_wc); + mRNAMap["SNDPARTICLE_TAU_MIN_TA"] = to_string(mds->sndparticle_tau_min_ta); + mRNAMap["SNDPARTICLE_TAU_MAX_TA"] = to_string(mds->sndparticle_tau_max_ta); + mRNAMap["SNDPARTICLE_TAU_MIN_K"] = to_string(mds->sndparticle_tau_min_k); + mRNAMap["SNDPARTICLE_TAU_MAX_K"] = to_string(mds->sndparticle_tau_max_k); + mRNAMap["SNDPARTICLE_K_WC"] = to_string(mds->sndparticle_k_wc); + mRNAMap["SNDPARTICLE_K_TA"] = to_string(mds->sndparticle_k_ta); + mRNAMap["SNDPARTICLE_K_B"] = to_string(mds->sndparticle_k_b); + mRNAMap["SNDPARTICLE_K_D"] = to_string(mds->sndparticle_k_d); + mRNAMap["SNDPARTICLE_L_MIN"] = to_string(mds->sndparticle_l_min); + mRNAMap["SNDPARTICLE_L_MAX"] = to_string(mds->sndparticle_l_max); + mRNAMap["SNDPARTICLE_BOUNDARY_DELETE"] = to_string( + (mds->sndparticle_boundary == SNDPARTICLE_BOUNDARY_DELETE) != 0); + mRNAMap["SNDPARTICLE_BOUNDARY_PUSHOUT"] = to_string( + (mds->sndparticle_boundary == SNDPARTICLE_BOUNDARY_PUSHOUT) != 0); + mRNAMap["SNDPARTICLE_POTENTIAL_RADIUS"] = to_string(mds->sndparticle_potential_radius); + mRNAMap["SNDPARTICLE_UPDATE_RADIUS"] = to_string(mds->sndparticle_update_radius); + mRNAMap["LIQUID_SURFACE_TENSION"] = to_string(mds->surface_tension); + mRNAMap["FLUID_VISCOSITY"] = to_string(mds->viscosity_base * + pow(10.0f, -mds->viscosity_exponent)); + mRNAMap["FLUID_DOMAIN_SIZE"] = to_string( + MAX3(mds->global_size[0], mds->global_size[1], mds->global_size[2])); + mRNAMap["SNDPARTICLE_TYPES"] = particleTypesStr; + mRNAMap["USING_SNDPARTS"] = to_string((mds->particle_type & particleTypes) != 0); + mRNAMap["GUIDING_ALPHA"] = to_string(mds->guide_alpha); + mRNAMap["GUIDING_BETA"] = to_string(mds->guide_beta); + mRNAMap["GUIDING_FACTOR"] = to_string(mds->guide_vel_factor); + mRNAMap["GRAVITY_X"] = to_string(mds->gravity[0]); + mRNAMap["GRAVITY_Y"] = to_string(mds->gravity[1]); + mRNAMap["GRAVITY_Z"] = to_string(mds->gravity[2]); + mRNAMap["CACHE_DIR"] = cacheDirectory; + mRNAMap["CACHE_RESUMABLE"] = to_string((mds->cache_type == FLUID_DOMAIN_CACHE_FINAL) == 0); + mRNAMap["USING_ADAPTIVETIME"] = to_string((mds->flags & FLUID_DOMAIN_USE_ADAPTIVE_TIME) != 0); + mRNAMap["USING_SPEEDVECTORS"] = to_string((mds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS) != 0); + mRNAMap["USING_FRACTIONS"] = to_string((mds->flags & FLUID_DOMAIN_USE_FRACTIONS) != 0); + mRNAMap["DELETE_IN_OBSTACLE"] = to_string((mds->flags & FLUID_DOMAIN_DELETE_IN_OBSTACLE) != 0); + mRNAMap["USING_DIFFUSION"] = to_string((mds->flags & FLUID_DOMAIN_USE_DIFFUSION) != 0); +} + +string MANTA::getRealValue(const string &varName) +{ + if (with_debug) + cout << "MANTA::getRealValue()" << endl; + + unordered_map<string, string>::iterator it; + it = mRNAMap.find(varName); + + if (it == mRNAMap.end()) { + cerr << "Fluid Error -- variable " << varName << " not found in RNA map " << it->second + << endl; + return ""; } - else if (varName == "MIN_RESX") - ss << mmd->domain->res_min[0]; - else if (varName == "MIN_RESY") - ss << mmd->domain->res_min[1]; - else if (varName == "MIN_RESZ") - ss << mmd->domain->res_min[2]; - else if (varName == "BASE_RESX") - ss << mmd->domain->base_res[0]; - else if (varName == "BASE_RESY") - ss << mmd->domain->base_res[1]; - else if (varName == "BASE_RESZ") - ss << mmd->domain->base_res[2]; - else if (varName == "WLT_STR") - ss << mmd->domain->noise_strength; - else if (varName == "NOISE_POSSCALE") - ss << mmd->domain->noise_pos_scale; - else if (varName == "NOISE_TIMEANIM") - ss << mmd->domain->noise_time_anim; - else if (varName == "COLOR_R") - ss << mmd->domain->active_color[0]; - else if (varName == "COLOR_G") - ss << mmd->domain->active_color[1]; - else if (varName == "COLOR_B") - ss << mmd->domain->active_color[2]; - else if (varName == "BUOYANCY_ALPHA") - ss << mmd->domain->alpha; - else if (varName == "BUOYANCY_BETA") - ss << mmd->domain->beta; - else if (varName == "DISSOLVE_SPEED") - ss << mmd->domain->diss_speed; - else if (varName == "BURNING_RATE") - ss << mmd->domain->burning_rate; - else if (varName == "FLAME_SMOKE") - ss << mmd->domain->flame_smoke; - else if (varName == "IGNITION_TEMP") - ss << mmd->domain->flame_ignition; - else if (varName == "MAX_TEMP") - ss << mmd->domain->flame_max_temp; - else if (varName == "FLAME_SMOKE_COLOR_X") - ss << mmd->domain->flame_smoke_color[0]; - else if (varName == "FLAME_SMOKE_COLOR_Y") - ss << mmd->domain->flame_smoke_color[1]; - else if (varName == "FLAME_SMOKE_COLOR_Z") - ss << mmd->domain->flame_smoke_color[2]; - else if (varName == "CURRENT_FRAME") - ss << mmd->time; - else if (varName == "START_FRAME") - ss << mmd->domain->cache_frame_start; - else if (varName == "END_FRAME") - ss << mmd->domain->cache_frame_end; - else if (varName == "CACHE_DATA_FORMAT") - ss << getCacheFileEnding(mmd->domain->cache_data_format); - else if (varName == "CACHE_MESH_FORMAT") - ss << getCacheFileEnding(mmd->domain->cache_mesh_format); - else if (varName == "CACHE_NOISE_FORMAT") - ss << getCacheFileEnding(mmd->domain->cache_noise_format); - else if (varName == "CACHE_PARTICLE_FORMAT") - ss << getCacheFileEnding(mmd->domain->cache_particle_format); - else if (varName == "SIMULATION_METHOD") { - if (mmd->domain->simulation_method & FLUID_DOMAIN_METHOD_FLIP) { - ss << "'FLIP'"; - } - else if (mmd->domain->simulation_method & FLUID_DOMAIN_METHOD_APIC) { - ss << "'APIC'"; - } - else { - ss << "'NONE'"; - } + if (with_debug) { + cout << "Found variable " << varName << " with value " << it->second << endl; } - else if (varName == "FLIP_RATIO") - ss << mmd->domain->flip_ratio; - else if (varName == "PARTICLE_RANDOMNESS") - ss << mmd->domain->particle_randomness; - else if (varName == "PARTICLE_NUMBER") - ss << mmd->domain->particle_number; - else if (varName == "PARTICLE_MINIMUM") - ss << mmd->domain->particle_minimum; - else if (varName == "PARTICLE_MAXIMUM") - ss << mmd->domain->particle_maximum; - else if (varName == "PARTICLE_RADIUS") - ss << mmd->domain->particle_radius; - else if (varName == "FRACTIONS_THRESHOLD") - ss << mmd->domain->fractions_threshold; - else if (varName == "MESH_CONCAVE_UPPER") - ss << mmd->domain->mesh_concave_upper; - else if (varName == "MESH_CONCAVE_LOWER") - ss << mmd->domain->mesh_concave_lower; - else if (varName == "MESH_PARTICLE_RADIUS") - ss << mmd->domain->mesh_particle_radius; - else if (varName == "MESH_SMOOTHEN_POS") - ss << mmd->domain->mesh_smoothen_pos; - else if (varName == "MESH_SMOOTHEN_NEG") - ss << mmd->domain->mesh_smoothen_neg; - else if (varName == "USING_MESH") - ss << (mmd->domain->flags & FLUID_DOMAIN_USE_MESH ? "True" : "False"); - else if (varName == "USING_IMPROVED_MESH") - ss << (mmd->domain->mesh_generator == FLUID_DOMAIN_MESH_IMPROVED ? "True" : "False"); - else if (varName == "PARTICLE_BAND_WIDTH") - ss << mmd->domain->particle_band_width; - else if (varName == "SNDPARTICLE_TAU_MIN_WC") - ss << mmd->domain->sndparticle_tau_min_wc; - else if (varName == "SNDPARTICLE_TAU_MAX_WC") - ss << mmd->domain->sndparticle_tau_max_wc; - else if (varName == "SNDPARTICLE_TAU_MIN_TA") - ss << mmd->domain->sndparticle_tau_min_ta; - else if (varName == "SNDPARTICLE_TAU_MAX_TA") - ss << mmd->domain->sndparticle_tau_max_ta; - else if (varName == "SNDPARTICLE_TAU_MIN_K") - ss << mmd->domain->sndparticle_tau_min_k; - else if (varName == "SNDPARTICLE_TAU_MAX_K") - ss << mmd->domain->sndparticle_tau_max_k; - else if (varName == "SNDPARTICLE_K_WC") - ss << mmd->domain->sndparticle_k_wc; - else if (varName == "SNDPARTICLE_K_TA") - ss << mmd->domain->sndparticle_k_ta; - else if (varName == "SNDPARTICLE_K_B") - ss << mmd->domain->sndparticle_k_b; - else if (varName == "SNDPARTICLE_K_D") - ss << mmd->domain->sndparticle_k_d; - else if (varName == "SNDPARTICLE_L_MIN") - ss << mmd->domain->sndparticle_l_min; - else if (varName == "SNDPARTICLE_L_MAX") - ss << mmd->domain->sndparticle_l_max; - else if (varName == "SNDPARTICLE_BOUNDARY_DELETE") - ss << (mmd->domain->sndparticle_boundary == SNDPARTICLE_BOUNDARY_DELETE); - else if (varName == "SNDPARTICLE_BOUNDARY_PUSHOUT") - ss << (mmd->domain->sndparticle_boundary == SNDPARTICLE_BOUNDARY_PUSHOUT); - else if (varName == "SNDPARTICLE_POTENTIAL_RADIUS") - ss << mmd->domain->sndparticle_potential_radius; - else if (varName == "SNDPARTICLE_UPDATE_RADIUS") - ss << mmd->domain->sndparticle_update_radius; - else if (varName == "LIQUID_SURFACE_TENSION") - ss << mmd->domain->surface_tension; - else if (varName == "FLUID_VISCOSITY") - ss << mmd->domain->viscosity_base * pow(10.0f, -mmd->domain->viscosity_exponent); - else if (varName == "FLUID_DOMAIN_SIZE") { - tmpFloat = MAX3( - mmd->domain->global_size[0], mmd->domain->global_size[1], mmd->domain->global_size[2]); - ss << tmpFloat; - } - else if (varName == "SNDPARTICLE_TYPES") { - if (mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) { - ss << "PtypeSpray"; - } - if (mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE) { - if (!ss.str().empty()) - ss << "|"; - ss << "PtypeBubble"; - } - if (mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_FOAM) { - if (!ss.str().empty()) - ss << "|"; - ss << "PtypeFoam"; - } - if (mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_TRACER) { - if (!ss.str().empty()) - ss << "|"; - ss << "PtypeTracer"; - } - if (ss.str().empty()) - ss << "0"; - } - else if (varName == "USING_SNDPARTS") { - tmpVar = (FLUID_DOMAIN_PARTICLE_SPRAY | FLUID_DOMAIN_PARTICLE_BUBBLE | - FLUID_DOMAIN_PARTICLE_FOAM | FLUID_DOMAIN_PARTICLE_TRACER); - ss << (((mmd->domain->particle_type & tmpVar)) ? "True" : "False"); - } - else if (varName == "GUIDING_ALPHA") - ss << mmd->domain->guide_alpha; - else if (varName == "GUIDING_BETA") - ss << mmd->domain->guide_beta; - else if (varName == "GUIDING_FACTOR") - ss << mmd->domain->guide_vel_factor; - else if (varName == "GRAVITY_X") - ss << mmd->domain->gravity[0]; - else if (varName == "GRAVITY_Y") - ss << mmd->domain->gravity[1]; - else if (varName == "GRAVITY_Z") - ss << mmd->domain->gravity[2]; - else if (varName == "CACHE_DIR") - ss << mmd->domain->cache_directory; - else if (varName == "CACHE_RESUMABLE") - ss << (mmd->domain->cache_type == FLUID_DOMAIN_CACHE_FINAL ? "False" : "True"); - else if (varName == "USING_ADAPTIVETIME") - ss << (mmd->domain->flags & FLUID_DOMAIN_USE_ADAPTIVE_TIME ? "True" : "False"); - else if (varName == "USING_SPEEDVECTORS") - ss << (mmd->domain->flags & FLUID_DOMAIN_USE_SPEED_VECTORS ? "True" : "False"); - else if (varName == "USING_FRACTIONS") - ss << (mmd->domain->flags & FLUID_DOMAIN_USE_FRACTIONS ? "True" : "False"); - else if (varName == "DELETE_IN_OBSTACLE") - ss << (mmd->domain->flags & FLUID_DOMAIN_DELETE_IN_OBSTACLE ? "True" : "False"); - else if (varName == "USING_DIFFUSION") - ss << (mmd->domain->flags & FLUID_DOMAIN_USE_DIFFUSION ? "True" : "False"); - else - std::cerr << "Fluid Error -- Unknown option: " << varName << std::endl; - return ss.str(); -} - -std::string MANTA::parseLine(const std::string &line, FluidModifierData *mmd) + + return it->second; +} + +string MANTA::parseLine(const string &line) { if (line.size() == 0) return ""; - std::string res = ""; + string res = ""; int currPos = 0, start_del = 0, end_del = -1; bool readingVar = false; const char delimiter = '$'; @@ -1022,7 +861,7 @@ std::string MANTA::parseLine(const std::string &line, FluidModifierData *mmd) else if (line[currPos] == delimiter && readingVar) { readingVar = false; end_del = currPos; - res += getRealValue(line.substr(start_del, currPos - start_del), mmd); + res += getRealValue(line.substr(start_del, currPos - start_del)); } currPos++; } @@ -1030,13 +869,21 @@ std::string MANTA::parseLine(const std::string &line, FluidModifierData *mmd) return res; } -std::string MANTA::parseScript(const std::string &setup_string, FluidModifierData *mmd) +string MANTA::parseScript(const string &setup_string, FluidModifierData *mmd) { - std::istringstream f(setup_string); - std::ostringstream res; - std::string line = ""; + if (MANTA::with_debug) + cout << "MANTA::parseScript()" << endl; + + istringstream f(setup_string); + ostringstream res; + string line = ""; + + // Update RNA map if modifier data is handed over + if (mmd) { + initializeRNAMap(mmd); + } while (getline(f, line)) { - res << parseLine(line, mmd) << "\n"; + res << parseLine(line) << "\n"; } return res.str(); } @@ -1044,13 +891,14 @@ std::string MANTA::parseScript(const std::string &setup_string, FluidModifierDat bool MANTA::updateFlipStructures(FluidModifierData *mmd, int framenr) { if (MANTA::with_debug) - std::cout << "MANTA::updateFlipStructures()" << std::endl; + cout << "MANTA::updateFlipStructures()" << endl; + FluidDomainSettings *mds = mmd->domain; mFlipFromFile = false; if (!mUsingLiquid) return false; - if (BLI_path_is_rel(mmd->domain->cache_directory)) + if (BLI_path_is_rel(mds->cache_directory)) return false; int result = 0; @@ -1063,8 +911,8 @@ bool MANTA::updateFlipStructures(FluidModifierData *mmd, int framenr) mFlipParticleData->clear(); mFlipParticleVelocity->clear(); - std::string pformat = getCacheFileEnding(mmd->domain->cache_particle_format); - std::string file = getFile( + string pformat = getCacheFileEnding(mds->cache_particle_format); + string file = getFile( mmd, FLUID_DOMAIN_DIR_DATA, FLUID_DOMAIN_FILE_PP, pformat.c_str(), framenr); expected += 1; @@ -1086,13 +934,14 @@ bool MANTA::updateFlipStructures(FluidModifierData *mmd, int framenr) bool MANTA::updateMeshStructures(FluidModifierData *mmd, int framenr) { if (MANTA::with_debug) - std::cout << "MANTA::updateMeshStructures()" << std::endl; + cout << "MANTA::updateMeshStructures()" << endl; + FluidDomainSettings *mds = mmd->domain; mMeshFromFile = false; if (!mUsingMesh) return false; - if (BLI_path_is_rel(mmd->domain->cache_directory)) + if (BLI_path_is_rel(mds->cache_directory)) return false; int result = 0; @@ -1108,9 +957,9 @@ bool MANTA::updateMeshStructures(FluidModifierData *mmd, int framenr) if (mMeshVelocities) mMeshVelocities->clear(); - std::string mformat = getCacheFileEnding(mmd->domain->cache_mesh_format); - std::string dformat = getCacheFileEnding(mmd->domain->cache_data_format); - std::string file = getFile(mmd, FLUID_DOMAIN_DIR_MESH, FLUID_DOMAIN_FILE_MESH, mformat, framenr); + string mformat = getCacheFileEnding(mds->cache_mesh_format); + string dformat = getCacheFileEnding(mds->cache_data_format); + string file = getFile(mmd, FLUID_DOMAIN_DIR_MESH, FLUID_DOMAIN_FILE_MESH, mformat, framenr); expected += 1; if (BLI_exists(file.c_str())) { @@ -1133,13 +982,14 @@ bool MANTA::updateMeshStructures(FluidModifierData *mmd, int framenr) bool MANTA::updateParticleStructures(FluidModifierData *mmd, int framenr) { if (MANTA::with_debug) - std::cout << "MANTA::updateParticleStructures()" << std::endl; + cout << "MANTA::updateParticleStructures()" << endl; + FluidDomainSettings *mds = mmd->domain; mParticlesFromFile = false; if (!mUsingDrops && !mUsingBubbles && !mUsingFloats && !mUsingTracers) return false; - if (BLI_path_is_rel(mmd->domain->cache_directory)) + if (BLI_path_is_rel(mds->cache_directory)) return false; int result = 0; @@ -1153,8 +1003,8 @@ bool MANTA::updateParticleStructures(FluidModifierData *mmd, int framenr) mSndParticleVelocity->clear(); mSndParticleLife->clear(); - std::string pformat = getCacheFileEnding(mmd->domain->cache_particle_format); - std::string file = getFile( + string pformat = getCacheFileEnding(mds->cache_particle_format); + string file = getFile( mmd, FLUID_DOMAIN_DIR_PARTICLES, FLUID_DOMAIN_FILE_PPSND, pformat, framenr); expected += 1; @@ -1183,21 +1033,21 @@ bool MANTA::updateParticleStructures(FluidModifierData *mmd, int framenr) bool MANTA::updateSmokeStructures(FluidModifierData *mmd, int framenr) { if (MANTA::with_debug) - std::cout << "MANTA::updateGridStructures()" << std::endl; + cout << "MANTA::updateGridStructures()" << endl; + FluidDomainSettings *mds = mmd->domain; mSmokeFromFile = false; if (!mUsingSmoke) return false; - if (BLI_path_is_rel(mmd->domain->cache_directory)) + if (BLI_path_is_rel(mds->cache_directory)) return false; int result = 0; int expected = 0; /* Expected number of read successes for this frame. */ - std::string dformat = getCacheFileEnding(mmd->domain->cache_data_format); - std::string file = getFile( - mmd, FLUID_DOMAIN_DIR_DATA, FLUID_DOMAIN_FILE_DENSITY, dformat, framenr); + string dformat = getCacheFileEnding(mds->cache_data_format); + string file = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_DOMAIN_FILE_DENSITY, dformat, framenr); expected += 1; if (BLI_exists(file.c_str())) { @@ -1273,21 +1123,22 @@ bool MANTA::updateSmokeStructures(FluidModifierData *mmd, int framenr) bool MANTA::updateNoiseStructures(FluidModifierData *mmd, int framenr) { if (MANTA::with_debug) - std::cout << "MANTA::updateNoiseStructures()" << std::endl; + cout << "MANTA::updateNoiseStructures()" << endl; + FluidDomainSettings *mds = mmd->domain; mNoiseFromFile = false; if (!mUsingSmoke || !mUsingNoise) return false; - if (BLI_path_is_rel(mmd->domain->cache_directory)) + if (BLI_path_is_rel(mds->cache_directory)) return false; int result = 0; int expected = 0; /* Expected number of read successes for this frame. */ - std::string dformat = getCacheFileEnding(mmd->domain->cache_data_format); - std::string nformat = getCacheFileEnding(mmd->domain->cache_noise_format); - std::string file = getFile( + string dformat = getCacheFileEnding(mds->cache_data_format); + string nformat = getCacheFileEnding(mds->cache_noise_format); + string file = getFile( mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_DOMAIN_FILE_DENSITYNOISE, nformat, framenr); expected += 1; @@ -1353,10 +1204,10 @@ bool MANTA::updateNoiseStructures(FluidModifierData *mmd, int framenr) } /* Dirty hack: Needed to format paths from python code that is run via PyRun_SimpleString */ -static std::string escapeSlashes(std::string const &s) +static string escapeSlashes(string const &s) { - std::string result = ""; - for (std::string::const_iterator i = s.begin(), end = s.end(); i != end; ++i) { + string result = ""; + for (string::const_iterator i = s.begin(), end = s.end(); i != end; ++i) { unsigned char c = *i; if (c == '\\') result += "\\\\"; @@ -1369,21 +1220,20 @@ static std::string escapeSlashes(std::string const &s) bool MANTA::writeConfiguration(FluidModifierData *mmd, int framenr) { if (with_debug) - std::cout << "MANTA::writeConfiguration()" << std::endl; + cout << "MANTA::writeConfiguration()" << endl; FluidDomainSettings *mds = mmd->domain; - std::string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_CONFIG); - std::string format = FLUID_DOMAIN_EXTENSION_UNI; - std::string file = getFile( - mmd, FLUID_DOMAIN_DIR_CONFIG, FLUID_DOMAIN_FILE_CONFIG, format, framenr); + string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_CONFIG); + string format = FLUID_DOMAIN_EXTENSION_UNI; + string file = getFile(mmd, FLUID_DOMAIN_DIR_CONFIG, FLUID_DOMAIN_FILE_CONFIG, format, framenr); /* Create 'config' subdir if it does not exist already. */ BLI_dir_create_recursive(directory.c_str()); gzFile gzf = (gzFile)BLI_gzopen(file.c_str(), "wb1"); // do some compression if (!gzf) { - std::cerr << "Fluid Error -- Cannot open file " << file << std::endl; + cerr << "Fluid Error -- Cannot open file " << file << endl; return false; } @@ -1408,17 +1258,18 @@ bool MANTA::writeConfiguration(FluidModifierData *mmd, int framenr) bool MANTA::writeData(FluidModifierData *mmd, int framenr) { if (with_debug) - std::cout << "MANTA::writeData()" << std::endl; + cout << "MANTA::writeData()" << endl; - std::ostringstream ss; - std::vector<std::string> pythonCommands; + ostringstream ss; + vector<string> pythonCommands; + FluidDomainSettings *mds = mmd->domain; - std::string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_DATA); - std::string dformat = getCacheFileEnding(mmd->domain->cache_data_format); - std::string pformat = getCacheFileEnding(mmd->domain->cache_particle_format); + string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_DATA); + string dformat = getCacheFileEnding(mds->cache_data_format); + string pformat = getCacheFileEnding(mds->cache_particle_format); - bool final_cache = (mmd->domain->cache_type == FLUID_DOMAIN_CACHE_FINAL); - std::string resumable_cache = (final_cache) ? "False" : "True"; + bool final_cache = (mds->cache_type == FLUID_DOMAIN_CACHE_FINAL); + string resumable_cache = (final_cache) ? "False" : "True"; ss.str(""); ss << "fluid_save_data_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr @@ -1443,16 +1294,17 @@ bool MANTA::writeData(FluidModifierData *mmd, int framenr) bool MANTA::writeNoise(FluidModifierData *mmd, int framenr) { if (with_debug) - std::cout << "MANTA::writeNoise()" << std::endl; + cout << "MANTA::writeNoise()" << endl; - std::ostringstream ss; - std::vector<std::string> pythonCommands; + ostringstream ss; + vector<string> pythonCommands; + FluidDomainSettings *mds = mmd->domain; - std::string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_NOISE); - std::string nformat = getCacheFileEnding(mmd->domain->cache_noise_format); + string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_NOISE); + string nformat = getCacheFileEnding(mds->cache_noise_format); - bool final_cache = (mmd->domain->cache_type == FLUID_DOMAIN_CACHE_FINAL); - std::string resumable_cache = (final_cache) ? "False" : "True"; + bool final_cache = (mds->cache_type == FLUID_DOMAIN_CACHE_FINAL); + string resumable_cache = (final_cache) ? "False" : "True"; if (mUsingSmoke && mUsingNoise) { ss.str(""); @@ -1466,22 +1318,21 @@ bool MANTA::writeNoise(FluidModifierData *mmd, int framenr) bool MANTA::readConfiguration(FluidModifierData *mmd, int framenr) { if (with_debug) - std::cout << "MANTA::readConfiguration()" << std::endl; + cout << "MANTA::readConfiguration()" << endl; FluidDomainSettings *mds = mmd->domain; float dummy; - std::string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_CONFIG); - std::string format = FLUID_DOMAIN_EXTENSION_UNI; - std::string file = getFile( - mmd, FLUID_DOMAIN_DIR_CONFIG, FLUID_DOMAIN_FILE_CONFIG, format, framenr); + string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_CONFIG); + string format = FLUID_DOMAIN_EXTENSION_UNI; + string file = getFile(mmd, FLUID_DOMAIN_DIR_CONFIG, FLUID_DOMAIN_FILE_CONFIG, format, framenr); if (!hasConfig(mmd, framenr)) return false; gzFile gzf = (gzFile)BLI_gzopen(file.c_str(), "rb"); // do some compression if (!gzf) { - std::cerr << "Fluid Error -- Cannot open file " << file << std::endl; + cerr << "Fluid Error -- Cannot open file " << file << endl; return false; } @@ -1507,21 +1358,22 @@ bool MANTA::readConfiguration(FluidModifierData *mmd, int framenr) bool MANTA::readData(FluidModifierData *mmd, int framenr) { if (with_debug) - std::cout << "MANTA::readData()" << std::endl; + cout << "MANTA::readData()" << endl; if (!mUsingSmoke && !mUsingLiquid) return false; - std::ostringstream ss; - std::vector<std::string> pythonCommands; + ostringstream ss; + vector<string> pythonCommands; + FluidDomainSettings *mds = mmd->domain; bool result = true; - std::string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_DATA); - std::string dformat = getCacheFileEnding(mmd->domain->cache_data_format); - std::string pformat = getCacheFileEnding(mmd->domain->cache_particle_format); + string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_DATA); + string dformat = getCacheFileEnding(mds->cache_data_format); + string pformat = getCacheFileEnding(mds->cache_particle_format); - bool final_cache = (mmd->domain->cache_type == FLUID_DOMAIN_CACHE_FINAL); - std::string resumable_cache = (final_cache) ? "False" : "True"; + bool final_cache = (mds->cache_type == FLUID_DOMAIN_CACHE_FINAL); + string resumable_cache = (final_cache) ? "False" : "True"; /* Sanity check: Are cache files present? */ if (!hasData(mmd, framenr)) @@ -1552,19 +1404,20 @@ bool MANTA::readData(FluidModifierData *mmd, int framenr) bool MANTA::readNoise(FluidModifierData *mmd, int framenr) { if (with_debug) - std::cout << "MANTA::readNoise()" << std::endl; + cout << "MANTA::readNoise()" << endl; if (!mUsingSmoke || !mUsingNoise) return false; - std::ostringstream ss; - std::vector<std::string> pythonCommands; + ostringstream ss; + vector<string> pythonCommands; + FluidDomainSettings *mds = mmd->domain; - std::string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_NOISE); - std::string nformat = getCacheFileEnding(mmd->domain->cache_noise_format); + string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_NOISE); + string nformat = getCacheFileEnding(mds->cache_noise_format); - bool final_cache = (mmd->domain->cache_type == FLUID_DOMAIN_CACHE_FINAL); - std::string resumable_cache = (final_cache) ? "False" : "True"; + bool final_cache = (mds->cache_type == FLUID_DOMAIN_CACHE_FINAL); + string resumable_cache = (final_cache) ? "False" : "True"; /* Sanity check: Are cache files present? */ if (!hasNoise(mmd, framenr)) @@ -1584,17 +1437,18 @@ bool MANTA::readNoise(FluidModifierData *mmd, int framenr) bool MANTA::readMesh(FluidModifierData *mmd, int framenr) { if (with_debug) - std::cout << "MANTA::readMesh()" << std::endl; + cout << "MANTA::readMesh()" << endl; if (!mUsingLiquid || !mUsingMesh) return false; - std::ostringstream ss; - std::vector<std::string> pythonCommands; + ostringstream ss; + vector<string> pythonCommands; + FluidDomainSettings *mds = mmd->domain; - std::string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_MESH); - std::string mformat = getCacheFileEnding(mmd->domain->cache_mesh_format); - std::string dformat = getCacheFileEnding(mmd->domain->cache_data_format); + string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_MESH); + string mformat = getCacheFileEnding(mds->cache_mesh_format); + string dformat = getCacheFileEnding(mds->cache_data_format); /* Sanity check: Are cache files present? */ if (!hasMesh(mmd, framenr)) @@ -1621,21 +1475,22 @@ bool MANTA::readMesh(FluidModifierData *mmd, int framenr) bool MANTA::readParticles(FluidModifierData *mmd, int framenr) { if (with_debug) - std::cout << "MANTA::readParticles()" << std::endl; + cout << "MANTA::readParticles()" << endl; if (!mUsingLiquid) return false; if (!mUsingDrops && !mUsingBubbles && !mUsingFloats && !mUsingTracers) return false; - std::ostringstream ss; - std::vector<std::string> pythonCommands; + ostringstream ss; + vector<string> pythonCommands; + FluidDomainSettings *mds = mmd->domain; - std::string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_PARTICLES); - std::string pformat = getCacheFileEnding(mmd->domain->cache_particle_format); + string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_PARTICLES); + string pformat = getCacheFileEnding(mds->cache_particle_format); - bool final_cache = (mmd->domain->cache_type == FLUID_DOMAIN_CACHE_FINAL); - std::string resumable_cache = (final_cache) ? "False" : "True"; + bool final_cache = (mds->cache_type == FLUID_DOMAIN_CACHE_FINAL); + string resumable_cache = (final_cache) ? "False" : "True"; /* Sanity check: Are cache files present? */ if (!hasParticles(mmd, framenr)) @@ -1652,19 +1507,21 @@ bool MANTA::readParticles(FluidModifierData *mmd, int framenr) bool MANTA::readGuiding(FluidModifierData *mmd, int framenr, bool sourceDomain) { if (with_debug) - std::cout << "MANTA::readGuiding()" << std::endl; + cout << "MANTA::readGuiding()" << endl; + + FluidDomainSettings *mds = mmd->domain; if (!mUsingGuiding) return false; - if (!mmd->domain) + if (!mds) return false; - std::ostringstream ss; - std::vector<std::string> pythonCommands; + ostringstream ss; + vector<string> pythonCommands; - std::string directory = (sourceDomain) ? getDirectory(mmd, FLUID_DOMAIN_DIR_DATA) : - getDirectory(mmd, FLUID_DOMAIN_DIR_GUIDE); - std::string gformat = getCacheFileEnding(mmd->domain->cache_data_format); + string directory = (sourceDomain) ? getDirectory(mmd, FLUID_DOMAIN_DIR_DATA) : + getDirectory(mmd, FLUID_DOMAIN_DIR_GUIDE); + string gformat = getCacheFileEnding(mds->cache_data_format); /* Sanity check: Are cache files present? */ if (!hasGuiding(mmd, framenr, sourceDomain)) @@ -1688,28 +1545,26 @@ bool MANTA::readGuiding(FluidModifierData *mmd, int framenr, bool sourceDomain) bool MANTA::bakeData(FluidModifierData *mmd, int framenr) { if (with_debug) - std::cout << "MANTA::bakeData()" << std::endl; + cout << "MANTA::bakeData()" << endl; - std::string tmpString, finalString; - std::ostringstream ss; - std::vector<std::string> pythonCommands; + string tmpString, finalString; + ostringstream ss; + vector<string> pythonCommands; + FluidDomainSettings *mds = mmd->domain; char cacheDirData[FILE_MAX], cacheDirGuiding[FILE_MAX]; cacheDirData[0] = '\0'; cacheDirGuiding[0] = '\0'; - std::string dformat = getCacheFileEnding(mmd->domain->cache_data_format); - std::string pformat = getCacheFileEnding(mmd->domain->cache_particle_format); - std::string gformat = dformat; // Use same data format for guiding format + string dformat = getCacheFileEnding(mds->cache_data_format); + string pformat = getCacheFileEnding(mds->cache_particle_format); + string gformat = dformat; // Use same data format for guiding format - BLI_path_join(cacheDirData, - sizeof(cacheDirData), - mmd->domain->cache_directory, - FLUID_DOMAIN_DIR_DATA, - nullptr); + BLI_path_join( + cacheDirData, sizeof(cacheDirData), mds->cache_directory, FLUID_DOMAIN_DIR_DATA, nullptr); BLI_path_join(cacheDirGuiding, sizeof(cacheDirGuiding), - mmd->domain->cache_directory, + mds->cache_directory, FLUID_DOMAIN_DIR_GUIDE, nullptr); BLI_path_make_safe(cacheDirData); @@ -1727,31 +1582,26 @@ bool MANTA::bakeData(FluidModifierData *mmd, int framenr) bool MANTA::bakeNoise(FluidModifierData *mmd, int framenr) { if (with_debug) - std::cout << "MANTA::bakeNoise()" << std::endl; + cout << "MANTA::bakeNoise()" << endl; - std::ostringstream ss; - std::vector<std::string> pythonCommands; + ostringstream ss; + vector<string> pythonCommands; + FluidDomainSettings *mds = mmd->domain; char cacheDirData[FILE_MAX], cacheDirNoise[FILE_MAX]; cacheDirData[0] = '\0'; cacheDirNoise[0] = '\0'; - std::string dformat = getCacheFileEnding(mmd->domain->cache_data_format); - std::string nformat = getCacheFileEnding(mmd->domain->cache_noise_format); + string dformat = getCacheFileEnding(mds->cache_data_format); + string nformat = getCacheFileEnding(mds->cache_noise_format); - bool final_cache = (mmd->domain->cache_type == FLUID_DOMAIN_CACHE_FINAL); - std::string resumable_cache = (final_cache) ? "False" : "True"; + bool final_cache = (mds->cache_type == FLUID_DOMAIN_CACHE_FINAL); + string resumable_cache = (final_cache) ? "False" : "True"; - BLI_path_join(cacheDirData, - sizeof(cacheDirData), - mmd->domain->cache_directory, - FLUID_DOMAIN_DIR_DATA, - nullptr); - BLI_path_join(cacheDirNoise, - sizeof(cacheDirNoise), - mmd->domain->cache_directory, - FLUID_DOMAIN_DIR_NOISE, - nullptr); + BLI_path_join( + cacheDirData, sizeof(cacheDirData), mds->cache_directory, FLUID_DOMAIN_DIR_DATA, nullptr); + BLI_path_join( + cacheDirNoise, sizeof(cacheDirNoise), mds->cache_directory, FLUID_DOMAIN_DIR_NOISE, nullptr); BLI_path_make_safe(cacheDirData); BLI_path_make_safe(cacheDirNoise); @@ -1767,29 +1617,24 @@ bool MANTA::bakeNoise(FluidModifierData *mmd, int framenr) bool MANTA::bakeMesh(FluidModifierData *mmd, int framenr) { if (with_debug) - std::cout << "MANTA::bakeMesh()" << std::endl; + cout << "MANTA::bakeMesh()" << endl; - std::ostringstream ss; - std::vector<std::string> pythonCommands; + ostringstream ss; + vector<string> pythonCommands; + FluidDomainSettings *mds = mmd->domain; char cacheDirData[FILE_MAX], cacheDirMesh[FILE_MAX]; cacheDirData[0] = '\0'; cacheDirMesh[0] = '\0'; - std::string dformat = getCacheFileEnding(mmd->domain->cache_data_format); - std::string mformat = getCacheFileEnding(mmd->domain->cache_mesh_format); - std::string pformat = getCacheFileEnding(mmd->domain->cache_particle_format); + string dformat = getCacheFileEnding(mds->cache_data_format); + string mformat = getCacheFileEnding(mds->cache_mesh_format); + string pformat = getCacheFileEnding(mds->cache_particle_format); - BLI_path_join(cacheDirData, - sizeof(cacheDirData), - mmd->domain->cache_directory, - FLUID_DOMAIN_DIR_DATA, - nullptr); - BLI_path_join(cacheDirMesh, - sizeof(cacheDirMesh), - mmd->domain->cache_directory, - FLUID_DOMAIN_DIR_MESH, - nullptr); + BLI_path_join( + cacheDirData, sizeof(cacheDirData), mds->cache_directory, FLUID_DOMAIN_DIR_DATA, nullptr); + BLI_path_join( + cacheDirMesh, sizeof(cacheDirMesh), mds->cache_directory, FLUID_DOMAIN_DIR_MESH, nullptr); BLI_path_make_safe(cacheDirData); BLI_path_make_safe(cacheDirMesh); @@ -1805,29 +1650,27 @@ bool MANTA::bakeMesh(FluidModifierData *mmd, int framenr) bool MANTA::bakeParticles(FluidModifierData *mmd, int framenr) { if (with_debug) - std::cout << "MANTA::bakeParticles()" << std::endl; + cout << "MANTA::bakeParticles()" << endl; - std::ostringstream ss; - std::vector<std::string> pythonCommands; + ostringstream ss; + vector<string> pythonCommands; + FluidDomainSettings *mds = mmd->domain; char cacheDirData[FILE_MAX], cacheDirParticles[FILE_MAX]; cacheDirData[0] = '\0'; cacheDirParticles[0] = '\0'; - std::string dformat = getCacheFileEnding(mmd->domain->cache_data_format); - std::string pformat = getCacheFileEnding(mmd->domain->cache_particle_format); + string dformat = getCacheFileEnding(mds->cache_data_format); + string pformat = getCacheFileEnding(mds->cache_particle_format); - bool final_cache = (mmd->domain->cache_type == FLUID_DOMAIN_CACHE_FINAL); - std::string resumable_cache = (final_cache) ? "False" : "True"; + bool final_cache = (mds->cache_type == FLUID_DOMAIN_CACHE_FINAL); + string resumable_cache = (final_cache) ? "False" : "True"; - BLI_path_join(cacheDirData, - sizeof(cacheDirData), - mmd->domain->cache_directory, - FLUID_DOMAIN_DIR_DATA, - nullptr); + BLI_path_join( + cacheDirData, sizeof(cacheDirData), mds->cache_directory, FLUID_DOMAIN_DIR_DATA, nullptr); BLI_path_join(cacheDirParticles, sizeof(cacheDirParticles), - mmd->domain->cache_directory, + mds->cache_directory, FLUID_DOMAIN_DIR_PARTICLES, nullptr); BLI_path_make_safe(cacheDirData); @@ -1845,22 +1688,23 @@ bool MANTA::bakeParticles(FluidModifierData *mmd, int framenr) bool MANTA::bakeGuiding(FluidModifierData *mmd, int framenr) { if (with_debug) - std::cout << "MANTA::bakeGuiding()" << std::endl; + cout << "MANTA::bakeGuiding()" << endl; - std::ostringstream ss; - std::vector<std::string> pythonCommands; + ostringstream ss; + vector<string> pythonCommands; + FluidDomainSettings *mds = mmd->domain; char cacheDirGuiding[FILE_MAX]; cacheDirGuiding[0] = '\0'; - std::string gformat = getCacheFileEnding(mmd->domain->cache_data_format); + string gformat = getCacheFileEnding(mds->cache_data_format); - bool final_cache = (mmd->domain->cache_type == FLUID_DOMAIN_CACHE_FINAL); - std::string resumable_cache = (final_cache) ? "False" : "True"; + bool final_cache = (mds->cache_type == FLUID_DOMAIN_CACHE_FINAL); + string resumable_cache = (final_cache) ? "False" : "True"; BLI_path_join(cacheDirGuiding, sizeof(cacheDirGuiding), - mmd->domain->cache_directory, + mds->cache_directory, FLUID_DOMAIN_DIR_GUIDE, nullptr); BLI_path_make_safe(cacheDirGuiding); @@ -1875,8 +1719,8 @@ bool MANTA::bakeGuiding(FluidModifierData *mmd, int framenr) bool MANTA::updateVariables(FluidModifierData *mmd) { - std::string tmpString, finalString; - std::vector<std::string> pythonCommands; + string tmpString, finalString; + vector<string> pythonCommands; tmpString += fluid_variables; if (mUsingSmoke) @@ -1906,13 +1750,15 @@ bool MANTA::updateVariables(FluidModifierData *mmd) void MANTA::exportSmokeScript(FluidModifierData *mmd) { if (with_debug) - std::cout << "MANTA::exportSmokeScript()" << std::endl; + cout << "MANTA::exportSmokeScript()" << endl; char cacheDir[FILE_MAX] = "\0"; char cacheDirScript[FILE_MAX] = "\0"; + FluidDomainSettings *mds = mmd->domain; + BLI_path_join( - cacheDir, sizeof(cacheDir), mmd->domain->cache_directory, FLUID_DOMAIN_DIR_SCRIPT, nullptr); + cacheDir, sizeof(cacheDir), mds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT, nullptr); BLI_path_make_safe(cacheDir); /* Create 'script' subdir if it does not exist already */ BLI_dir_create_recursive(cacheDir); @@ -1920,16 +1766,16 @@ void MANTA::exportSmokeScript(FluidModifierData *mmd) cacheDirScript, sizeof(cacheDirScript), cacheDir, FLUID_DOMAIN_SMOKE_SCRIPT, nullptr); BLI_path_make_safe(cacheDir); - bool noise = mmd->domain->flags & FLUID_DOMAIN_USE_NOISE; - bool heat = mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_HEAT; - bool colors = mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_COLORS; - bool fire = mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_FIRE; - bool obstacle = mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE; - bool guiding = mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE; - bool invel = mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_INVEL; - bool outflow = mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW; + bool noise = mds->flags & FLUID_DOMAIN_USE_NOISE; + bool heat = mds->active_fields & FLUID_DOMAIN_ACTIVE_HEAT; + bool colors = mds->active_fields & FLUID_DOMAIN_ACTIVE_COLORS; + bool fire = mds->active_fields & FLUID_DOMAIN_ACTIVE_FIRE; + bool obstacle = mds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE; + bool guiding = mds->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE; + bool invel = mds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL; + bool outflow = mds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW; - std::string manta_script; + string manta_script; // Libraries manta_script += header_libraries + manta_import; @@ -2001,10 +1847,10 @@ void MANTA::exportSmokeScript(FluidModifierData *mmd) manta_script += header_main + smoke_standalone + fluid_standalone; // Fill in missing variables in script - std::string final_script = MANTA::parseScript(manta_script, mmd); + string final_script = MANTA::parseScript(manta_script, mmd); // Write script - std::ofstream myfile; + ofstream myfile; myfile.open(cacheDirScript); myfile << final_script; myfile.close(); @@ -2013,13 +1859,15 @@ void MANTA::exportSmokeScript(FluidModifierData *mmd) void MANTA::exportLiquidScript(FluidModifierData *mmd) { if (with_debug) - std::cout << "MANTA::exportLiquidScript()" << std::endl; + cout << "MANTA::exportLiquidScript()" << endl; char cacheDir[FILE_MAX] = "\0"; char cacheDirScript[FILE_MAX] = "\0"; + FluidDomainSettings *mds = mmd->domain; + BLI_path_join( - cacheDir, sizeof(cacheDir), mmd->domain->cache_directory, FLUID_DOMAIN_DIR_SCRIPT, nullptr); + cacheDir, sizeof(cacheDir), mds->cache_directory, FLUID_DOMAIN_DIR_SCRIPT, nullptr); BLI_path_make_safe(cacheDir); /* Create 'script' subdir if it does not exist already */ BLI_dir_create_recursive(cacheDir); @@ -2027,18 +1875,18 @@ void MANTA::exportLiquidScript(FluidModifierData *mmd) cacheDirScript, sizeof(cacheDirScript), cacheDir, FLUID_DOMAIN_LIQUID_SCRIPT, nullptr); BLI_path_make_safe(cacheDirScript); - bool mesh = mmd->domain->flags & FLUID_DOMAIN_USE_MESH; - bool drops = mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY; - bool bubble = mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE; - bool floater = mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_FOAM; - bool tracer = mmd->domain->particle_type & FLUID_DOMAIN_PARTICLE_TRACER; - bool obstacle = mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE; - bool fractions = mmd->domain->flags & FLUID_DOMAIN_USE_FRACTIONS; - bool guiding = mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE; - bool invel = mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_INVEL; - bool outflow = mmd->domain->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW; + bool mesh = mds->flags & FLUID_DOMAIN_USE_MESH; + bool drops = mds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY; + bool bubble = mds->particle_type & FLUID_DOMAIN_PARTICLE_BUBBLE; + bool floater = mds->particle_type & FLUID_DOMAIN_PARTICLE_FOAM; + bool tracer = mds->particle_type & FLUID_DOMAIN_PARTICLE_TRACER; + bool obstacle = mds->active_fields & FLUID_DOMAIN_ACTIVE_OBSTACLE; + bool fractions = mds->flags & FLUID_DOMAIN_USE_FRACTIONS; + bool guiding = mds->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE; + bool invel = mds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL; + bool outflow = mds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW; - std::string manta_script; + string manta_script; // Libraries manta_script += header_libraries + manta_import; @@ -2108,10 +1956,10 @@ void MANTA::exportLiquidScript(FluidModifierData *mmd) manta_script += header_main + liquid_standalone + fluid_standalone; // Fill in missing variables in script - std::string final_script = MANTA::parseScript(manta_script, mmd); + string final_script = MANTA::parseScript(manta_script, mmd); // Write script - std::ofstream myfile; + ofstream myfile; myfile.open(cacheDirScript); myfile << final_script; myfile.close(); @@ -2124,14 +1972,12 @@ void MANTA::exportLiquidScript(FluidModifierData *mmd) * * Important! Return value: New reference or nullptr * Caller of this function needs to handle reference count of returned object. */ -static PyObject *callPythonFunction(std::string varName, - std::string functionName, - bool isAttribute = false) +static PyObject *callPythonFunction(string varName, string functionName, bool isAttribute = false) { if ((varName == "") || (functionName == "")) { if (MANTA::with_debug) - std::cout << "Missing Python variable name and/or function name -- name is: " << varName - << ", function name is: " << functionName << std::endl; + cout << "Missing Python variable name and/or function name -- name is: " << varName + << ", function name is: " << functionName << endl; return nullptr; } @@ -2185,8 +2031,8 @@ static void *pyObjectToPointer(PyObject *inputObject) Py_DECREF(inputObject); - std::string str(result); - std::istringstream in(str); + string str(result); + istringstream in(str); void *dataPointer = nullptr; in >> dataPointer; @@ -2233,11 +2079,11 @@ static long pyObjectToLong(PyObject *inputObject) int MANTA::getFrame() { if (with_debug) - std::cout << "MANTA::getFrame()" << std::endl; + cout << "MANTA::getFrame()" << endl; - std::string func = "frame"; - std::string id = std::to_string(mCurrentID); - std::string solver = "s" + id; + string func = "frame"; + string id = to_string(mCurrentID); + string solver = "s" + id; return pyObjectToLong(callPythonFunction(solver, func, true)); } @@ -2245,11 +2091,11 @@ int MANTA::getFrame() float MANTA::getTimestep() { if (with_debug) - std::cout << "MANTA::getTimestep()" << std::endl; + cout << "MANTA::getTimestep()" << endl; - std::string func = "timestep"; - std::string id = std::to_string(mCurrentID); - std::string solver = "s" + id; + string func = "timestep"; + string id = to_string(mCurrentID); + string solver = "s" + id; return (float)pyObjectToDouble(callPythonFunction(solver, func, true)); } @@ -2263,10 +2109,10 @@ bool MANTA::needsRealloc(FluidModifierData *mmd) void MANTA::adaptTimestep() { if (with_debug) - std::cout << "MANTA::adaptTimestep()" << std::endl; + cout << "MANTA::adaptTimestep()" << endl; - std::vector<std::string> pythonCommands; - std::ostringstream ss; + vector<string> pythonCommands; + ostringstream ss; ss << "fluid_adapt_time_step_" << mCurrentID << "()"; pythonCommands.push_back(ss.str()); @@ -2274,14 +2120,14 @@ void MANTA::adaptTimestep() runPythonString(pythonCommands); } -bool MANTA::updateMeshFromFile(std::string filename) +bool MANTA::updateMeshFromFile(string filename) { - std::string fname(filename); - std::string::size_type idx; + string fname(filename); + string::size_type idx; idx = fname.rfind('.'); - if (idx != std::string::npos) { - std::string extension = fname.substr(idx + 1); + if (idx != string::npos) { + string extension = fname.substr(idx + 1); if (extension.compare("gz") == 0) return updateMeshFromBobj(filename); @@ -2290,27 +2136,25 @@ bool MANTA::updateMeshFromFile(std::string filename) else if (extension.compare("uni") == 0) return updateMeshFromUni(filename); else - std::cerr << "Fluid Error -- updateMeshFromFile(): Invalid file extension in file: " - << filename << std::endl; + cerr << "Fluid Error -- updateMeshFromFile(): Invalid file extension in file: " << filename + << endl; } else { - std::cerr << "Fluid Error -- updateMeshFromFile(): Unable to open file: " << filename - << std::endl; + cerr << "Fluid Error -- updateMeshFromFile(): Unable to open file: " << filename << endl; } return false; } -bool MANTA::updateMeshFromBobj(std::string filename) +bool MANTA::updateMeshFromBobj(string filename) { if (with_debug) - std::cout << "MANTA::updateMeshFromBobj()" << std::endl; + cout << "MANTA::updateMeshFromBobj()" << endl; gzFile gzf; gzf = (gzFile)BLI_gzopen(filename.c_str(), "rb1"); // do some compression if (!gzf) { - std::cerr << "Fluid Error -- updateMeshFromBobj(): Unable to open file: " << filename - << std::endl; + cerr << "Fluid Error -- updateMeshFromBobj(): Unable to open file: " << filename << endl; return false; } @@ -2319,15 +2163,14 @@ bool MANTA::updateMeshFromBobj(std::string filename) // Num vertices readBytes = gzread(gzf, &numBuffer, sizeof(int)); if (!readBytes) { - std::cerr - << "Fluid Error -- updateMeshFromBobj(): Unable to read number of mesh vertices from " - << filename << std::endl; + cerr << "Fluid Error -- updateMeshFromBobj(): Unable to read number of mesh vertices from " + << filename << endl; gzclose(gzf); return false; } if (with_debug) - std::cout << "read mesh , num verts: " << numBuffer << " , in file: " << filename << std::endl; + cout << "read mesh , num verts: " << numBuffer << " , in file: " << filename << endl; int numChunks = (int)(ceil((float)numBuffer / NODE_CHUNK)); int readLen, readStart, readEnd, k; @@ -2348,8 +2191,8 @@ bool MANTA::updateMeshFromBobj(std::string filename) readBytes = gzread(gzf, bufferVerts, readLen * sizeof(float) * 3); if (!readBytes) { - std::cerr << "Fluid Error -- updateMeshFromBobj(): Unable to read mesh vertices from " - << filename << std::endl; + cerr << "Fluid Error -- updateMeshFromBobj(): Unable to read mesh vertices from " + << filename << endl; MEM_freeN(bufferVerts); gzclose(gzf); return false; @@ -2361,7 +2204,7 @@ bool MANTA::updateMeshFromBobj(std::string filename) CLAMP(readEnd, 0, numBuffer); k = 0; - for (std::vector<MANTA::Node>::size_type j = readStart; j < readEnd; j++, k += 3) { + for (vector<MANTA::Node>::size_type j = readStart; j < readEnd; j++, k += 3) { mMeshNodes->at(j).pos[0] = bufferVerts[k]; mMeshNodes->at(j).pos[1] = bufferVerts[k + 1]; mMeshNodes->at(j).pos[2] = bufferVerts[k + 2]; @@ -2374,15 +2217,14 @@ bool MANTA::updateMeshFromBobj(std::string filename) // Num normals readBytes = gzread(gzf, &numBuffer, sizeof(int)); if (!readBytes) { - std::cerr << "Fluid Error -- updateMeshFromBobj(): Unable to read number of mesh normals from " - << filename << std::endl; + cerr << "Fluid Error -- updateMeshFromBobj(): Unable to read number of mesh normals from " + << filename << endl; gzclose(gzf); return false; } if (with_debug) - std::cout << "read mesh , num normals : " << numBuffer << " , in file: " << filename - << std::endl; + cout << "read mesh , num normals : " << numBuffer << " , in file: " << filename << endl; if (numBuffer) { // Normals @@ -2401,8 +2243,8 @@ bool MANTA::updateMeshFromBobj(std::string filename) readBytes = gzread(gzf, bufferNormals, readLen * sizeof(float) * 3); if (!readBytes) { - std::cerr << "Fluid Error -- updateMeshFromBobj(): Unable to read mesh normals from " - << filename << std::endl; + cerr << "Fluid Error -- updateMeshFromBobj(): Unable to read mesh normals from " + << filename << endl; MEM_freeN(bufferNormals); gzclose(gzf); return false; @@ -2414,7 +2256,7 @@ bool MANTA::updateMeshFromBobj(std::string filename) CLAMP(readEnd, 0, numBuffer); k = 0; - for (std::vector<MANTA::Node>::size_type j = readStart; j < readEnd; j++, k += 3) { + for (vector<MANTA::Node>::size_type j = readStart; j < readEnd; j++, k += 3) { mMeshNodes->at(j).normal[0] = bufferNormals[k]; mMeshNodes->at(j).normal[1] = bufferNormals[k + 1]; mMeshNodes->at(j).normal[2] = bufferNormals[k + 2]; @@ -2427,16 +2269,15 @@ bool MANTA::updateMeshFromBobj(std::string filename) // Num triangles readBytes = gzread(gzf, &numBuffer, sizeof(int)); if (!readBytes) { - std::cerr - << "Fluid Error -- updateMeshFromBobj(): Unable to read number of mesh triangles from " - << filename << std::endl; + cerr << "Fluid Error -- updateMeshFromBobj(): Unable to read number of mesh triangles from " + << filename << endl; gzclose(gzf); return false; } if (with_debug) - std::cout << "Fluid: Read mesh , num triangles : " << numBuffer << " , in file: " << filename - << std::endl; + cout << "Fluid: Read mesh , num triangles : " << numBuffer << " , in file: " << filename + << endl; numChunks = (int)(ceil((float)numBuffer / TRIANGLE_CHUNK)); @@ -2456,8 +2297,8 @@ bool MANTA::updateMeshFromBobj(std::string filename) readBytes = gzread(gzf, bufferTriangles, readLen * sizeof(int) * 3); if (!readBytes) { - std::cerr << "Fluid Error -- updateMeshFromBobj(): Unable to read mesh triangles from " - << filename << std::endl; + cerr << "Fluid Error -- updateMeshFromBobj(): Unable to read mesh triangles from " + << filename << endl; MEM_freeN(bufferTriangles); gzclose(gzf); return false; @@ -2469,7 +2310,7 @@ bool MANTA::updateMeshFromBobj(std::string filename) CLAMP(readEnd, 0, numBuffer); k = 0; - for (std::vector<MANTA::Triangle>::size_type j = readStart; j < readEnd; j++, k += 3) { + for (vector<MANTA::Triangle>::size_type j = readStart; j < readEnd; j++, k += 3) { mMeshTriangles->at(j).c[0] = bufferTriangles[k]; mMeshTriangles->at(j).c[1] = bufferTriangles[k + 1]; mMeshTriangles->at(j).c[2] = bufferTriangles[k + 2]; @@ -2481,24 +2322,23 @@ bool MANTA::updateMeshFromBobj(std::string filename) return (gzclose(gzf) == Z_OK); } -bool MANTA::updateMeshFromObj(std::string filename) +bool MANTA::updateMeshFromObj(string filename) { if (with_debug) - std::cout << "MANTA::updateMeshFromObj()" << std::endl; + cout << "MANTA::updateMeshFromObj()" << endl; - std::ifstream ifs(filename); + ifstream ifs(filename); float fbuffer[3]; int ibuffer[3]; int cntVerts = 0, cntNormals = 0, cntTris = 0; if (!ifs.good()) { - std::cerr << "Fluid Error -- updateMeshFromObj(): Unable to open file: " << filename - << std::endl; + cerr << "Fluid Error -- updateMeshFromObj(): Unable to open file: " << filename << endl; return false; } while (ifs.good() && !ifs.eof()) { - std::string id; + string id; ifs >> id; if (id[0] == '#') { @@ -2512,8 +2352,8 @@ bool MANTA::updateMeshFromObj(std::string filename) else if (id == "vn") { // normals if (getNumVertices() != cntVerts) { - std::cerr << "Fluid Error -- updateMeshFromObj(): Invalid number of mesh nodes in file: " - << filename << std::endl; + cerr << "Fluid Error -- updateMeshFromObj(): Invalid number of mesh nodes in file: " + << filename << endl; return false; } @@ -2536,20 +2376,20 @@ bool MANTA::updateMeshFromObj(std::string filename) } else if (id == "g") { // group - std::string group; + string group; ifs >> group; } else if (id == "f") { // face - std::string face; + string face; for (int i = 0; i < 3; i++) { ifs >> face; - if (face.find('/') != std::string::npos) + if (face.find('/') != string::npos) face = face.substr(0, face.find('/')); // ignore other indices int idx = atoi(face.c_str()) - 1; if (idx < 0) { - std::cerr << "Fluid Error -- updateMeshFromObj(): Invalid face encountered in file: " - << filename << std::endl; + cerr << "Fluid Error -- updateMeshFromObj(): Invalid face encountered in file: " + << filename << endl; return false; } ibuffer[i] = idx; @@ -2571,10 +2411,10 @@ bool MANTA::updateMeshFromObj(std::string filename) return true; } -bool MANTA::updateMeshFromUni(std::string filename) +bool MANTA::updateMeshFromUni(string filename) { if (with_debug) - std::cout << "MANTA::updateMeshFromUni()" << std::endl; + cout << "MANTA::updateMeshFromUni()" << endl; gzFile gzf; float fbuffer[4]; @@ -2582,8 +2422,7 @@ bool MANTA::updateMeshFromUni(std::string filename) gzf = (gzFile)BLI_gzopen(filename.c_str(), "rb1"); // do some compression if (!gzf) { - std::cerr << "Fluid Error -- updateMeshFromUni(): Unable to open file: " << filename - << std::endl; + cerr << "Fluid Error -- updateMeshFromUni(): Unable to open file: " << filename << endl; return false; } @@ -2591,13 +2430,13 @@ bool MANTA::updateMeshFromUni(std::string filename) char file_magic[5] = {0, 0, 0, 0, 0}; readBytes = gzread(gzf, file_magic, 4); if (!readBytes) { - std::cerr << "Fluid Error -- updateMeshFromUni(): Unable to read header in file: " << filename - << std::endl; + cerr << "Fluid Error -- updateMeshFromUni(): Unable to read header in file: " << filename + << endl; gzclose(gzf); return false; } - std::vector<pVel> *velocityPointer = mMeshVelocities; + vector<pVel> *velocityPointer = mMeshVelocities; // mdata uni header const int STR_LEN_PDATA = 256; @@ -2613,19 +2452,18 @@ bool MANTA::updateMeshFromUni(std::string filename) gzread(gzf, ×tamp, sizeof(unsigned long long)); if (with_debug) - std::cout << "Fluid: Read " << ibuffer[0] << " vertices in file: " << filename << std::endl; + cout << "Fluid: Read " << ibuffer[0] << " vertices in file: " << filename << endl; // Sanity checks const int meshSize = sizeof(float) * 3 + sizeof(int); if (!(bytesPerElement == meshSize) && (elementType == 0)) { - std::cerr << "Fluid Error -- updateMeshFromUni(): Invalid header in file: " << filename - << std::endl; + cerr << "Fluid Error -- updateMeshFromUni(): Invalid header in file: " << filename << endl; gzclose(gzf); return false; } if (!ibuffer[0]) { // Any vertices present? - std::cerr << "Fluid Error -- updateMeshFromUni(): No vertices present in file: " << filename - << std::endl; + cerr << "Fluid Error -- updateMeshFromUni(): No vertices present in file: " << filename + << endl; gzclose(gzf); return false; } @@ -2640,7 +2478,7 @@ bool MANTA::updateMeshFromUni(std::string filename) velocityPointer->resize(numParticles); MANTA::pVel *bufferPVel; - for (std::vector<pVel>::iterator it = velocityPointer->begin(); it != velocityPointer->end(); + for (vector<pVel>::iterator it = velocityPointer->begin(); it != velocityPointer->end(); ++it) { gzread(gzf, fbuffer, sizeof(float) * 3); bufferPVel = (MANTA::pVel *)fbuffer; @@ -2652,44 +2490,42 @@ bool MANTA::updateMeshFromUni(std::string filename) return (gzclose(gzf) == Z_OK); } -bool MANTA::updateParticlesFromFile(std::string filename, bool isSecondarySys, bool isVelData) +bool MANTA::updateParticlesFromFile(string filename, bool isSecondarySys, bool isVelData) { if (with_debug) - std::cout << "MANTA::updateParticlesFromFile()" << std::endl; + cout << "MANTA::updateParticlesFromFile()" << endl; - std::string fname(filename); - std::string::size_type idx; + string fname(filename); + string::size_type idx; idx = fname.rfind('.'); - if (idx != std::string::npos) { - std::string extension = fname.substr(idx + 1); + if (idx != string::npos) { + string extension = fname.substr(idx + 1); if (extension.compare("uni") == 0) return updateParticlesFromUni(filename, isSecondarySys, isVelData); else - std::cerr << "Fluid Error -- updateParticlesFromFile(): Invalid file extension in file: " - << filename << std::endl; + cerr << "Fluid Error -- updateParticlesFromFile(): Invalid file extension in file: " + << filename << endl; return false; } else { - std::cerr << "Fluid Error -- updateParticlesFromFile(): Unable to open file: " << filename - << std::endl; + cerr << "Fluid Error -- updateParticlesFromFile(): Unable to open file: " << filename << endl; return false; } } -bool MANTA::updateParticlesFromUni(std::string filename, bool isSecondarySys, bool isVelData) +bool MANTA::updateParticlesFromUni(string filename, bool isSecondarySys, bool isVelData) { if (with_debug) - std::cout << "MANTA::updateParticlesFromUni()" << std::endl; + cout << "MANTA::updateParticlesFromUni()" << endl; gzFile gzf; int ibuffer[4]; gzf = (gzFile)BLI_gzopen(filename.c_str(), "rb1"); // do some compression if (!gzf) { - std::cerr << "Fluid Error -- updateParticlesFromUni(): Unable to open file: " << filename - << std::endl; + cerr << "Fluid Error -- updateParticlesFromUni(): Unable to open file: " << filename << endl; return false; } @@ -2697,24 +2533,24 @@ bool MANTA::updateParticlesFromUni(std::string filename, bool isSecondarySys, bo char file_magic[5] = {0, 0, 0, 0, 0}; readBytes = gzread(gzf, file_magic, 4); if (!readBytes) { - std::cerr << "Fluid Error -- updateParticlesFromUni(): Unable to read header in file: " - << filename << std::endl; + cerr << "Fluid Error -- updateParticlesFromUni(): Unable to read header in file: " << filename + << endl; gzclose(gzf); return false; } if (!strcmp(file_magic, "PB01")) { - std::cerr << "Fluid Error -- updateParticlesFromUni(): Particle uni file format v01 not " - "supported anymore." - << std::endl; + cerr << "Fluid Error -- updateParticlesFromUni(): Particle uni file format v01 not " + "supported anymore." + << endl; gzclose(gzf); return false; } // Pointer to FLIP system or to secondary particle system - std::vector<pData> *dataPointer = nullptr; - std::vector<pVel> *velocityPointer = nullptr; - std::vector<float> *lifePointer = nullptr; + vector<pData> *dataPointer = nullptr; + vector<pVel> *velocityPointer = nullptr; + vector<float> *lifePointer = nullptr; if (isSecondarySys) { dataPointer = mSndParticleData; @@ -2740,19 +2576,19 @@ bool MANTA::updateParticlesFromUni(std::string filename, bool isSecondarySys, bo gzread(gzf, ×tamp, sizeof(unsigned long long)); if (with_debug) - std::cout << "Fluid: Read " << ibuffer[0] << " particles in file: " << filename << std::endl; + cout << "Fluid: Read " << ibuffer[0] << " particles in file: " << filename << endl; // Sanity checks const int partSysSize = sizeof(float) * 3 + sizeof(int); if (!(bytesPerElement == partSysSize) && (elementType == 0)) { - std::cerr << "Fluid Error -- updateParticlesFromUni(): Invalid header in file: " << filename - << std::endl; + cerr << "Fluid Error -- updateParticlesFromUni(): Invalid header in file: " << filename + << endl; gzclose(gzf); return false; } if (!ibuffer[0]) { // Any particles present? if (with_debug) - std::cout << "Fluid: No particles present in file: " << filename << std::endl; + cout << "Fluid: No particles present in file: " << filename << endl; gzclose(gzf); return true; // return true since having no particles in a cache file is valid } @@ -2780,9 +2616,8 @@ bool MANTA::updateParticlesFromUni(std::string filename, bool isSecondarySys, bo readBytes = gzread(gzf, bufferPData, readLen * sizeof(pData)); if (!readBytes) { - std::cerr - << "Fluid Error -- updateParticlesFromUni(): Unable to read particle data in file: " - << filename << std::endl; + cerr << "Fluid Error -- updateParticlesFromUni(): Unable to read particle data in file: " + << filename << endl; MEM_freeN(bufferPData); gzclose(gzf); return false; @@ -2794,7 +2629,7 @@ bool MANTA::updateParticlesFromUni(std::string filename, bool isSecondarySys, bo CLAMP(readEnd, 0, numParticles); int k = 0; - for (std::vector<MANTA::pData>::size_type j = readStart; j < readEnd; j++, k++) { + for (vector<MANTA::pData>::size_type j = readStart; j < readEnd; j++, k++) { dataPointer->at(j).pos[0] = bufferPData[k].pos[0]; dataPointer->at(j).pos[1] = bufferPData[k].pos[1]; dataPointer->at(j).pos[2] = bufferPData[k].pos[2]; @@ -2821,9 +2656,9 @@ bool MANTA::updateParticlesFromUni(std::string filename, bool isSecondarySys, bo readBytes = gzread(gzf, bufferPVel, readLen * sizeof(pVel)); if (!readBytes) { - std::cerr << "Fluid Error -- updateParticlesFromUni(): Unable to read particle velocities " - "in file: " - << filename << std::endl; + cerr << "Fluid Error -- updateParticlesFromUni(): Unable to read particle velocities " + "in file: " + << filename << endl; MEM_freeN(bufferPVel); gzclose(gzf); return false; @@ -2835,7 +2670,7 @@ bool MANTA::updateParticlesFromUni(std::string filename, bool isSecondarySys, bo CLAMP(readEnd, 0, numParticles); int k = 0; - for (std::vector<MANTA::pVel>::size_type j = readStart; j < readEnd; j++, k++) { + for (vector<MANTA::pVel>::size_type j = readStart; j < readEnd; j++, k++) { velocityPointer->at(j).pos[0] = bufferPVel[k].pos[0]; velocityPointer->at(j).pos[1] = bufferPVel[k].pos[1]; velocityPointer->at(j).pos[2] = bufferPVel[k].pos[2]; @@ -2860,9 +2695,8 @@ bool MANTA::updateParticlesFromUni(std::string filename, bool isSecondarySys, bo readBytes = gzread(gzf, bufferPLife, readLen * sizeof(float)); if (!readBytes) { - std::cerr - << "Fluid Error -- updateParticlesFromUni(): Unable to read particle life in file: " - << filename << std::endl; + cerr << "Fluid Error -- updateParticlesFromUni(): Unable to read particle life in file: " + << filename << endl; MEM_freeN(bufferPLife); gzclose(gzf); return false; @@ -2874,7 +2708,7 @@ bool MANTA::updateParticlesFromUni(std::string filename, bool isSecondarySys, bo CLAMP(readEnd, 0, numParticles); int k = 0; - for (std::vector<float>::size_type j = readStart; j < readEnd; j++, k++) { + for (vector<float>::size_type j = readStart; j < readEnd; j++, k++) { lifePointer->at(j) = bufferPLife[k]; } todoParticles -= readLen; @@ -2884,24 +2718,24 @@ bool MANTA::updateParticlesFromUni(std::string filename, bool isSecondarySys, bo return (gzclose(gzf) == Z_OK); } -bool MANTA::updateGridFromFile(std::string filename, float *grid, bool isNoise) +bool MANTA::updateGridFromFile(string filename, float *grid, bool isNoise) { if (with_debug) - std::cout << "MANTA::updateGridFromFile()" << std::endl; + cout << "MANTA::updateGridFromFile()" << endl; if (!grid) { - std::cerr << "Fluid Error -- updateGridFromFile(): Cannot read into uninitialized grid (grid " - "is null)." - << std::endl; + cerr << "Fluid Error -- updateGridFromFile(): Cannot read into uninitialized grid (grid " + "is null)." + << endl; return false; } - std::string fname(filename); - std::string::size_type idx; + string fname(filename); + string::size_type idx; idx = fname.rfind('.'); - if (idx != std::string::npos) { - std::string extension = fname.substr(idx + 1); + if (idx != string::npos) { + string extension = fname.substr(idx + 1); if (extension.compare("uni") == 0) return updateGridFromUni(filename, grid, isNoise); @@ -2912,29 +2746,27 @@ bool MANTA::updateGridFromFile(std::string filename, float *grid, bool isNoise) else if (extension.compare("raw") == 0) return updateGridFromRaw(filename, grid, isNoise); else - std::cerr << "Fluid Error -- updateGridFromFile(): Invalid file extension in file: " - << filename << std::endl; + cerr << "Fluid Error -- updateGridFromFile(): Invalid file extension in file: " << filename + << endl; return false; } else { - std::cerr << "Fluid Error -- updateGridFromFile(): Unable to open file: " << filename - << std::endl; + cerr << "Fluid Error -- updateGridFromFile(): Unable to open file: " << filename << endl; return false; } } -bool MANTA::updateGridFromUni(std::string filename, float *grid, bool isNoise) +bool MANTA::updateGridFromUni(string filename, float *grid, bool isNoise) { if (with_debug) - std::cout << "MANTA::updateGridFromUni()" << std::endl; + cout << "MANTA::updateGridFromUni()" << endl; gzFile gzf; int ibuffer[4]; gzf = (gzFile)BLI_gzopen(filename.c_str(), "rb1"); if (!gzf) { - std::cerr << "Fluid Error -- updateGridFromUni(): Unable to open file: " << filename - << std::endl; + cerr << "Fluid Error -- updateGridFromUni(): Unable to open file: " << filename << endl; return false; } @@ -2942,32 +2774,29 @@ bool MANTA::updateGridFromUni(std::string filename, float *grid, bool isNoise) char file_magic[5] = {0, 0, 0, 0, 0}; readBytes = gzread(gzf, file_magic, 4); if (!readBytes) { - std::cerr << "Fluid Error -- updateGridFromUni(): Unable to read header in file: " << filename - << std::endl; + cerr << "Fluid Error -- updateGridFromUni(): Unable to read header in file: " << filename + << endl; gzclose(gzf); return false; } if (!strcmp(file_magic, "DDF2")) { - std::cerr - << "Fluid Error -- updateGridFromUni(): Grid uni file format DDF2 not supported anymore." - << std::endl; + cerr << "Fluid Error -- updateGridFromUni(): Grid uni file format DDF2 not supported anymore." + << endl; gzclose(gzf); return false; } if (!strcmp(file_magic, "MNT1")) { - std::cerr - << "Fluid Error -- updateGridFromUni(): Grid uni file format MNT1 not supported anymore." - << std::endl; + cerr << "Fluid Error -- updateGridFromUni(): Grid uni file format MNT1 not supported anymore." + << endl; gzclose(gzf); return false; } if (!strcmp(file_magic, "MNT2")) { - std::cerr - << "Fluid Error -- updateGridFromUni(): Grid uni file format MNT2 not supported anymore." - << std::endl; + cerr << "Fluid Error -- updateGridFromUni(): Grid uni file format MNT2 not supported anymore." + << endl; gzclose(gzf); return false; } @@ -2992,13 +2821,12 @@ bool MANTA::updateGridFromUni(std::string filename, float *grid, bool isNoise) int resZ = (isNoise) ? mResZNoise : mResZ; if (with_debug) - std::cout << "Fluid: Read " << ibuffer[3] << " grid type in file: " << filename << std::endl; + cout << "Fluid: Read " << ibuffer[3] << " grid type in file: " << filename << endl; // Sanity checks if (ibuffer[0] != resX || ibuffer[1] != resY || ibuffer[2] != resZ) { - std::cout << "Fluid: Grid dim doesn't match, read: (" << ibuffer[0] << ", " << ibuffer[1] - << ", " << ibuffer[2] << ") vs setup: (" << resX << ", " << resY << ", " << resZ - << ")" << std::endl; + cout << "Fluid: Grid dim doesn't match, read: (" << ibuffer[0] << ", " << ibuffer[1] << ", " + << ibuffer[2] << ") vs setup: (" << resX << ", " << resY << ", " << resZ << ")" << endl; gzclose(gzf); return false; } @@ -3009,16 +2837,16 @@ bool MANTA::updateGridFromUni(std::string filename, float *grid, bool isNoise) } if (with_debug) - std::cout << "Fluid: Read successfully: " << filename << std::endl; + cout << "Fluid: Read successfully: " << filename << endl; return (gzclose(gzf) == Z_OK); } #if OPENVDB == 1 -bool MANTA::updateGridFromVDB(std::string filename, float *grid, bool isNoise) +bool MANTA::updateGridFromVDB(string filename, float *grid, bool isNoise) { if (with_debug) - std::cout << "MANTA::updateGridFromVDB()" << std::endl; + cout << "MANTA::updateGridFromVDB()" << endl; openvdb::initialize(); openvdb::io::File file(filename); @@ -3026,8 +2854,8 @@ bool MANTA::updateGridFromVDB(std::string filename, float *grid, bool isNoise) file.open(); } catch (const openvdb::IoError &) { - std::cerr << "Fluid Error -- updateGridFromVDB(): IOError, invalid OpenVDB file: " << filename - << std::endl; + cerr << "Fluid Error -- updateGridFromVDB(): IOError, invalid OpenVDB file: " << filename + << endl; return false; } @@ -3059,17 +2887,17 @@ bool MANTA::updateGridFromVDB(std::string filename, float *grid, bool isNoise) } #endif -bool MANTA::updateGridFromRaw(std::string filename, float *grid, bool isNoise) +bool MANTA::updateGridFromRaw(string filename, float *grid, bool isNoise) { if (with_debug) - std::cout << "MANTA::updateGridFromRaw()" << std::endl; + cout << "MANTA::updateGridFromRaw()" << endl; gzFile gzf; int expectedBytes, readBytes; gzf = (gzFile)BLI_gzopen(filename.c_str(), "rb"); if (!gzf) { - std::cout << "MANTA::updateGridFromRaw(): unable to open file" << std::endl; + cout << "MANTA::updateGridFromRaw(): unable to open file" << endl; return false; } @@ -3080,8 +2908,7 @@ bool MANTA::updateGridFromRaw(std::string filename, float *grid, bool isNoise) expectedBytes = sizeof(float) * resX * resY * resZ; readBytes = gzread(gzf, grid, expectedBytes); if (!readBytes) { - std::cerr << "Fluid Error -- updateGridFromRaw(): Unable to read raw file: " << filename - << std::endl; + cerr << "Fluid Error -- updateGridFromRaw(): Unable to read raw file: " << filename << endl; gzclose(gzf); return false; } @@ -3094,25 +2921,25 @@ bool MANTA::updateGridFromRaw(std::string filename, float *grid, bool isNoise) void MANTA::updatePointers() { if (with_debug) - std::cout << "MANTA::updatePointers()" << std::endl; - - std::string func = "getDataPointer"; - std::string funcNodes = "getNodesDataPointer"; - std::string funcTris = "getTrisDataPointer"; - - std::string id = std::to_string(mCurrentID); - std::string solver = "s" + id; - std::string parts = "pp" + id; - std::string snd = "sp" + id; - std::string mesh = "sm" + id; - std::string mesh2 = "mesh" + id; - std::string noise = "sn" + id; - std::string solver_ext = "_" + solver; - std::string parts_ext = "_" + parts; - std::string snd_ext = "_" + snd; - std::string mesh_ext = "_" + mesh; - std::string mesh_ext2 = "_" + mesh2; - std::string noise_ext = "_" + noise; + cout << "MANTA::updatePointers()" << endl; + + string func = "getDataPointer"; + string funcNodes = "getNodesDataPointer"; + string funcTris = "getTrisDataPointer"; + + string id = to_string(mCurrentID); + string solver = "s" + id; + string parts = "pp" + id; + string snd = "sp" + id; + string mesh = "sm" + id; + string mesh2 = "mesh" + id; + string noise = "sn" + id; + string solver_ext = "_" + solver; + string parts_ext = "_" + parts; + string snd_ext = "_" + snd; + string mesh_ext = "_" + mesh; + string mesh_ext2 = "_" + mesh2; + string noise_ext = "_" + noise; mFlags = (int *)pyObjectToPointer(callPythonFunction("flags" + solver_ext, func)); mPhiIn = (float *)pyObjectToPointer(callPythonFunction("phiIn" + solver_ext, func)); @@ -3199,27 +3026,27 @@ void MANTA::updatePointers() } if (mUsingLiquid) { mPhi = (float *)pyObjectToPointer(callPythonFunction("phi" + solver_ext, func)); - mFlipParticleData = (std::vector<pData> *)pyObjectToPointer( + mFlipParticleData = (vector<pData> *)pyObjectToPointer( callPythonFunction("pp" + solver_ext, func)); - mFlipParticleVelocity = (std::vector<pVel> *)pyObjectToPointer( + mFlipParticleVelocity = (vector<pVel> *)pyObjectToPointer( callPythonFunction("pVel" + parts_ext, func)); } if (mUsingLiquid && mUsingMesh) { - mMeshNodes = (std::vector<Node> *)pyObjectToPointer( + mMeshNodes = (vector<Node> *)pyObjectToPointer( callPythonFunction("mesh" + mesh_ext, funcNodes)); - mMeshTriangles = (std::vector<Triangle> *)pyObjectToPointer( + mMeshTriangles = (vector<Triangle> *)pyObjectToPointer( callPythonFunction("mesh" + mesh_ext, funcTris)); } if (mUsingLiquid && mUsingMVel) { - mMeshVelocities = (std::vector<pVel> *)pyObjectToPointer( + mMeshVelocities = (vector<pVel> *)pyObjectToPointer( callPythonFunction("mVel" + mesh_ext2, func)); } if (mUsingLiquid && (mUsingDrops | mUsingBubbles | mUsingFloats | mUsingTracers)) { - mSndParticleData = (std::vector<pData> *)pyObjectToPointer( + mSndParticleData = (vector<pData> *)pyObjectToPointer( callPythonFunction("ppSnd" + snd_ext, func)); - mSndParticleVelocity = (std::vector<pVel> *)pyObjectToPointer( + mSndParticleVelocity = (vector<pVel> *)pyObjectToPointer( callPythonFunction("pVelSnd" + parts_ext, func)); - mSndParticleLife = (std::vector<float> *)pyObjectToPointer( + mSndParticleLife = (vector<float> *)pyObjectToPointer( callPythonFunction("pLifeSnd" + parts_ext, func)); } @@ -3232,21 +3059,21 @@ void MANTA::updatePointers() bool MANTA::hasConfig(FluidModifierData *mmd, int framenr) { - std::string extension = getCacheFileEnding(mmd->domain->cache_data_format); + string extension = getCacheFileEnding(mmd->domain->cache_data_format); return BLI_exists( getFile(mmd, FLUID_DOMAIN_DIR_CONFIG, FLUID_DOMAIN_FILE_CONFIG, extension, framenr).c_str()); } bool MANTA::hasData(FluidModifierData *mmd, int framenr) { - std::string filename = (mUsingSmoke) ? FLUID_DOMAIN_FILE_DENSITY : FLUID_DOMAIN_FILE_PP; - std::string extension = getCacheFileEnding(mmd->domain->cache_data_format); + string filename = (mUsingSmoke) ? FLUID_DOMAIN_FILE_DENSITY : FLUID_DOMAIN_FILE_PP; + string extension = getCacheFileEnding(mmd->domain->cache_data_format); return BLI_exists(getFile(mmd, FLUID_DOMAIN_DIR_DATA, filename, extension, framenr).c_str()); } bool MANTA::hasNoise(FluidModifierData *mmd, int framenr) { - std::string extension = getCacheFileEnding(mmd->domain->cache_noise_format); + string extension = getCacheFileEnding(mmd->domain->cache_noise_format); return BLI_exists( getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_DOMAIN_FILE_DENSITYNOISE, extension, framenr) .c_str()); @@ -3254,14 +3081,14 @@ bool MANTA::hasNoise(FluidModifierData *mmd, int framenr) bool MANTA::hasMesh(FluidModifierData *mmd, int framenr) { - std::string extension = getCacheFileEnding(mmd->domain->cache_mesh_format); + string extension = getCacheFileEnding(mmd->domain->cache_mesh_format); return BLI_exists( getFile(mmd, FLUID_DOMAIN_DIR_MESH, FLUID_DOMAIN_FILE_MESH, extension, framenr).c_str()); } bool MANTA::hasParticles(FluidModifierData *mmd, int framenr) { - std::string extension = getCacheFileEnding(mmd->domain->cache_particle_format); + string extension = getCacheFileEnding(mmd->domain->cache_particle_format); return BLI_exists( getFile(mmd, FLUID_DOMAIN_DIR_PARTICLES, FLUID_DOMAIN_FILE_PPSND, extension, framenr) .c_str()); @@ -3269,13 +3096,13 @@ bool MANTA::hasParticles(FluidModifierData *mmd, int framenr) bool MANTA::hasGuiding(FluidModifierData *mmd, int framenr, bool sourceDomain) { - std::string subdirectory = (sourceDomain) ? FLUID_DOMAIN_DIR_DATA : FLUID_DOMAIN_DIR_GUIDE; - std::string filename = (sourceDomain) ? FLUID_DOMAIN_FILE_VEL : FLUID_DOMAIN_FILE_GUIDEVEL; - std::string extension = getCacheFileEnding(mmd->domain->cache_data_format); + string subdirectory = (sourceDomain) ? FLUID_DOMAIN_DIR_DATA : FLUID_DOMAIN_DIR_GUIDE; + string filename = (sourceDomain) ? FLUID_DOMAIN_FILE_VEL : FLUID_DOMAIN_FILE_GUIDEVEL; + string extension = getCacheFileEnding(mmd->domain->cache_data_format); return BLI_exists(getFile(mmd, subdirectory, filename, extension, framenr).c_str()); } -std::string MANTA::getDirectory(FluidModifierData *mmd, std::string subdirectory) +string MANTA::getDirectory(FluidModifierData *mmd, string subdirectory) { char directory[FILE_MAX]; BLI_path_join( @@ -3284,15 +3111,12 @@ std::string MANTA::getDirectory(FluidModifierData *mmd, std::string subdirectory return directory; } -std::string MANTA::getFile(FluidModifierData *mmd, - std::string subdirectory, - std::string fname, - std::string extension, - int framenr) +string MANTA::getFile( + FluidModifierData *mmd, string subdirectory, string fname, string extension, int framenr) { char targetFile[FILE_MAX]; - std::string path = getDirectory(mmd, subdirectory); - std::string filename = fname + extension; + string path = getDirectory(mmd, subdirectory); + string filename = fname + extension; BLI_join_dirfile(targetFile, sizeof(targetFile), path.c_str(), filename.c_str()); BLI_path_frame(targetFile, framenr, 0); return targetFile; diff --git a/intern/mantaflow/intern/MANTA_main.h b/intern/mantaflow/intern/MANTA_main.h index 21946431f28..9b3fd6aa141 100644 --- a/intern/mantaflow/intern/MANTA_main.h +++ b/intern/mantaflow/intern/MANTA_main.h @@ -27,8 +27,14 @@ #include <atomic> #include <cassert> #include <string> +#include <unordered_map> #include <vector> +using std::atomic; +using std::string; +using std::unordered_map; +using std::vector; + struct MANTA { public: MANTA(int *res, struct FluidModifierData *mmd); @@ -58,20 +64,20 @@ struct MANTA { void step(struct FluidModifierData *mmd, int startFrame); // Grid initialization functions - void initHeat(struct FluidModifierData *mmd); - void initFire(struct FluidModifierData *mmd); - void initColors(struct FluidModifierData *mmd); - void initFireHigh(struct FluidModifierData *mmd); - void initColorsHigh(struct FluidModifierData *mmd); - void initLiquid(FluidModifierData *mmd); - void initLiquidMesh(FluidModifierData *mmd); - void initObstacle(FluidModifierData *mmd); - void initGuiding(FluidModifierData *mmd); - void initFractions(FluidModifierData *mmd); - void initInVelocity(FluidModifierData *mmd); - void initOutflow(FluidModifierData *mmd); - void initSndParts(FluidModifierData *mmd); - void initLiquidSndParts(FluidModifierData *mmd); + void initHeat(struct FluidModifierData *mmd = NULL); + void initFire(struct FluidModifierData *mmd = NULL); + void initColors(struct FluidModifierData *mmd = NULL); + void initFireHigh(struct FluidModifierData *mmd = NULL); + void initColorsHigh(struct FluidModifierData *mmd = NULL); + void initLiquid(FluidModifierData *mmd = NULL); + void initLiquidMesh(FluidModifierData *mmd = NULL); + void initObstacle(FluidModifierData *mmd = NULL); + void initGuiding(FluidModifierData *mmd = NULL); + void initFractions(FluidModifierData *mmd = NULL); + void initInVelocity(FluidModifierData *mmd = NULL); + void initOutflow(FluidModifierData *mmd = NULL); + void initSndParts(FluidModifierData *mmd = NULL); + void initLiquidSndParts(FluidModifierData *mmd = NULL); // Pointer transfer: Mantaflow -> Blender void updatePointers(); @@ -414,7 +420,7 @@ struct MANTA { return mPhi; } - static std::atomic<int> solverID; + static atomic<int> solverID; static int with_debug; // on or off (1 or 0), also sets manta debug level // Mesh getters @@ -741,6 +747,8 @@ struct MANTA { size_t mTotalCellsMesh; size_t mTotalCellsParticles; + unordered_map<string, string> mRNAMap; + int mCurrentID; bool mUsingHeat; @@ -852,45 +860,46 @@ struct MANTA { float *mPhi; // Mesh fields - std::vector<Node> *mMeshNodes; - std::vector<Triangle> *mMeshTriangles; - std::vector<pVel> *mMeshVelocities; + vector<Node> *mMeshNodes; + vector<Triangle> *mMeshTriangles; + vector<pVel> *mMeshVelocities; // Particle fields - std::vector<pData> *mFlipParticleData; - std::vector<pVel> *mFlipParticleVelocity; - - std::vector<pData> *mSndParticleData; - std::vector<pVel> *mSndParticleVelocity; - std::vector<float> *mSndParticleLife; - - void initDomain(struct FluidModifierData *mmd); - void initNoise(struct FluidModifierData *mmd); - void initMesh(struct FluidModifierData *mmd); - void initSmoke(struct FluidModifierData *mmd); - void initSmokeNoise(struct FluidModifierData *mmd); + vector<pData> *mFlipParticleData; + vector<pVel> *mFlipParticleVelocity; + + vector<pData> *mSndParticleData; + vector<pVel> *mSndParticleVelocity; + vector<float> *mSndParticleLife; + + void initializeRNAMap(struct FluidModifierData *mmd = NULL); + void initDomain(struct FluidModifierData *mmd = NULL); + void initNoise(struct FluidModifierData *mmd = NULL); + void initMesh(struct FluidModifierData *mmd = NULL); + void initSmoke(struct FluidModifierData *mmd = NULL); + void initSmokeNoise(struct FluidModifierData *mmd = NULL); void initializeMantaflow(); void terminateMantaflow(); - bool runPythonString(std::vector<std::string> commands); - std::string getRealValue(const std::string &varName, FluidModifierData *mmd); - std::string parseLine(const std::string &line, FluidModifierData *mmd); - std::string parseScript(const std::string &setup_string, FluidModifierData *mmd = NULL); - bool updateMeshFromBobj(std::string filename); - bool updateMeshFromObj(std::string filename); - bool updateMeshFromUni(std::string filename); - bool updateParticlesFromUni(std::string filename, bool isSecondarySys, bool isVelData); - bool updateGridFromUni(std::string filename, float *grid, bool isNoise); - bool updateGridFromVDB(std::string filename, float *grid, bool isNoise); - bool updateGridFromRaw(std::string filename, float *grid, bool isNoise); - bool updateMeshFromFile(std::string filename); - bool updateParticlesFromFile(std::string filename, bool isSecondarySys, bool isVelData); - bool updateGridFromFile(std::string filename, float *grid, bool isNoise); - std::string getDirectory(struct FluidModifierData *mmd, std::string subdirectory); - std::string getFile(struct FluidModifierData *mmd, - std::string subdirectory, - std::string fname, - std::string extension, - int framenr); + bool runPythonString(vector<string> commands); + string getRealValue(const string &varName); + string parseLine(const string &line); + string parseScript(const string &setup_string, FluidModifierData *mmd = NULL); + bool updateMeshFromBobj(string filename); + bool updateMeshFromObj(string filename); + bool updateMeshFromUni(string filename); + bool updateParticlesFromUni(string filename, bool isSecondarySys, bool isVelData); + bool updateGridFromUni(string filename, float *grid, bool isNoise); + bool updateGridFromVDB(string filename, float *grid, bool isNoise); + bool updateGridFromRaw(string filename, float *grid, bool isNoise); + bool updateMeshFromFile(string filename); + bool updateParticlesFromFile(string filename, bool isSecondarySys, bool isVelData); + bool updateGridFromFile(string filename, float *grid, bool isNoise); + string getDirectory(struct FluidModifierData *mmd, string subdirectory); + string getFile(struct FluidModifierData *mmd, + string subdirectory, + string fname, + string extension, + int framenr); }; #endif diff --git a/intern/mantaflow/intern/strings/fluid_script.h b/intern/mantaflow/intern/strings/fluid_script.h index dd2111db7d7..c0a23f77d7a 100644 --- a/intern/mantaflow/intern/strings/fluid_script.h +++ b/intern/mantaflow/intern/strings/fluid_script.h @@ -92,7 +92,7 @@ const std::string fluid_variables = mantaMsg('Fluid variables')\n\ dim_s$ID$ = $SOLVER_DIM$\n\ res_s$ID$ = $RES$\n\ -gravity_s$ID$ = vec3($GRAVITY_X$, $GRAVITY_Y$, $GRAVITY_Z$)\n\ +gravity_s$ID$ = vec3($GRAVITY_X$, $GRAVITY_Y$, $GRAVITY_Z$) # in SI unit (e.g. m/s^2)\n\ gs_s$ID$ = vec3($RESX$, $RESY$, $RESZ$)\n\ maxVel_s$ID$ = 0\n\ \n\ @@ -115,6 +115,7 @@ using_speedvectors_s$ID$ = $USING_SPEEDVECTORS$\n\ using_diffusion_s$ID$ = $USING_DIFFUSION$\n\ \n\ # Fluid time params\n\ +timeScale_s$ID$ = $TIME_SCALE$\n\ timeTotal_s$ID$ = $TIME_TOTAL$\n\ timePerFrame_s$ID$ = $TIME_PER_FRAME$\n\ frameLength_s$ID$ = $FRAME_LENGTH$\n\ @@ -132,8 +133,29 @@ end_frame_s$ID$ = $END_FRAME$\n\ domainSize_s$ID$ = $FLUID_DOMAIN_SIZE$ # longest domain side in meters\n\ viscosity_s$ID$ = $FLUID_VISCOSITY$ / (domainSize_s$ID$*domainSize_s$ID$) # kinematic viscosity in m^2/s\n\ \n\ -# Factor to convert blender velocities to manta velocities\n\ -toMantaUnitsFac_s$ID$ = (1.0 / (1.0 / res_s$ID$))\n # = dt/dx * 1/dt "; +# Factors to convert Blender units to Manta units\n\ +ratioMetersToRes_s$ID$ = float(domainSize_s$ID$) / float(res_s$ID$) # [meters / cells]\n\ +mantaMsg('1 Mantaflow cell is ' + str(ratioMetersToRes_s$ID$) + ' Blender length units long.')\n\ +\n\ +ratioResToBLength_s$ID$ = float(res_s$ID$) / float(domainSize_s$ID$) # [cells / blength] (blength: cm, m, or km, ... )\n\ +mantaMsg('1 Blender length unit is ' + str(ratioResToBLength_s$ID$) + ' Mantaflow cells long.')\n\ +\n\ +ratioBTimeToTimstep_s$ID$ = float(1) / float(0.1 * 25 * timeScale_s$ID$) # the time within 1 blender time unit, see also fluid.c\n\ +mantaMsg('1 Blender time unit is ' + str(ratioBTimeToTimstep_s$ID$) + ' Mantaflow time units long.')\n\ +\n\ +ratioFrameToFramelength_s$ID$ = float(1) / float(frameLength_s$ID$) # the time within 1 frame\n\ +mantaMsg('frame / frameLength is ' + str(ratioFrameToFramelength_s$ID$) + ' Mantaflow time units long.')\n\ +\n\ +scaleAcceleration_s$ID$ = ratioResToBLength_s$ID$ * (ratioBTimeToTimstep_s$ID$**2)# [meters/btime^2] to [cells/timestep^2] (btime: sec, min, or h, ...)\n\ +mantaMsg('scaleAcceleration is ' + str(scaleAcceleration_s$ID$))\n\ +\n\ +scaleSpeedFrames_s$ID$ = ratioResToBLength_s$ID$ * ratioFrameToFramelength_s$ID$ # [blength/frame] to [cells/frameLength]\n\ +mantaMsg('scaleSpeed is ' + str(scaleSpeedFrames_s$ID$))\n\ +\n\ +scaleSpeedTime_s$ID$ = ratioResToBLength_s$ID$ * ratioBTimeToTimstep_s$ID$ # [blength/btime] to [cells/frameLength]\n\ +mantaMsg('scaleSpeedTime is ' + str(scaleSpeedTime_s$ID$))\n\ +\n\ +gravity_s$ID$ *= scaleAcceleration_s$ID$ # scale from world acceleration to cell based acceleration\n"; const std::string fluid_variables_noise = "\n\ @@ -342,17 +364,16 @@ def fluid_pre_step_$ID$():\n\ y_obvel_s$ID$.safeDivide(numObs_s$ID$)\n\ z_obvel_s$ID$.safeDivide(numObs_s$ID$)\n\ \n\ - x_obvel_s$ID$.multConst(toMantaUnitsFac_s$ID$)\n\ - y_obvel_s$ID$.multConst(toMantaUnitsFac_s$ID$)\n\ - z_obvel_s$ID$.multConst(toMantaUnitsFac_s$ID$)\n\ - \n\ + x_obvel_s$ID$.multConst(scaleSpeedFrames_s$ID$)\n\ + y_obvel_s$ID$.multConst(scaleSpeedFrames_s$ID$)\n\ + z_obvel_s$ID$.multConst(scaleSpeedFrames_s$ID$)\n\ copyRealToVec3(sourceX=x_obvel_s$ID$, sourceY=y_obvel_s$ID$, sourceZ=z_obvel_s$ID$, target=obvelC_s$ID$)\n\ \n\ # translate invels (world space) to grid space\n\ if using_invel_s$ID$:\n\ - x_invel_s$ID$.multConst(toMantaUnitsFac_s$ID$)\n\ - y_invel_s$ID$.multConst(toMantaUnitsFac_s$ID$)\n\ - z_invel_s$ID$.multConst(toMantaUnitsFac_s$ID$)\n\ + x_invel_s$ID$.multConst(scaleSpeedTime_s$ID$)\n\ + y_invel_s$ID$.multConst(scaleSpeedTime_s$ID$)\n\ + z_invel_s$ID$.multConst(scaleSpeedTime_s$ID$)\n\ copyRealToVec3(sourceX=x_invel_s$ID$, sourceY=y_invel_s$ID$, sourceZ=z_invel_s$ID$, target=invelC_s$ID$)\n\ \n\ if using_guiding_s$ID$:\n\ @@ -362,9 +383,9 @@ def fluid_pre_step_$ID$():\n\ velT_s$ID$.multConst(vec3(gamma_sg$ID$))\n\ \n\ # translate external forces (world space) to grid space\n\ - x_force_s$ID$.multConst(toMantaUnitsFac_s$ID$)\n\ - y_force_s$ID$.multConst(toMantaUnitsFac_s$ID$)\n\ - z_force_s$ID$.multConst(toMantaUnitsFac_s$ID$)\n\ + x_force_s$ID$.multConst(scaleSpeedFrames_s$ID$)\n\ + y_force_s$ID$.multConst(scaleSpeedFrames_s$ID$)\n\ + z_force_s$ID$.multConst(scaleSpeedFrames_s$ID$)\n\ copyRealToVec3(sourceX=x_force_s$ID$, sourceY=y_force_s$ID$, sourceZ=z_force_s$ID$, target=forces_s$ID$)\n\ \n\ # If obstacle has velocity, i.e. is a moving obstacle, switch to dynamic preconditioner\n\ @@ -598,10 +619,9 @@ def bake_guiding_process_$ID$(framenr, format_guiding, path_guiding, resumable): y_guidevel_s$ID$.safeDivide(numGuides_s$ID$)\n\ z_guidevel_s$ID$.safeDivide(numGuides_s$ID$)\n\ \n\ - x_guidevel_s$ID$.multConst(Real(toMantaUnitsFac_s$ID$))\n\ - y_guidevel_s$ID$.multConst(Real(toMantaUnitsFac_s$ID$))\n\ - z_guidevel_s$ID$.multConst(Real(toMantaUnitsFac_s$ID$))\n\ - \n\ + x_guidevel_s$ID$.multConst(scaleSpeedFrames_s$ID$)\n\ + y_guidevel_s$ID$.multConst(scaleSpeedFrames_s$ID$)\n\ + z_guidevel_s$ID$.multConst(scaleSpeedFrames_s$ID$)\n\ copyRealToVec3(sourceX=x_guidevel_s$ID$, sourceY=y_guidevel_s$ID$, sourceZ=z_guidevel_s$ID$, target=guidevelC_s$ID$)\n\ \n\ mantaMsg('Extrapolating guiding velocity')\n\ diff --git a/intern/mantaflow/intern/strings/liquid_script.h b/intern/mantaflow/intern/strings/liquid_script.h index f20b218427c..3aedde6753f 100644 --- a/intern/mantaflow/intern/strings/liquid_script.h +++ b/intern/mantaflow/intern/strings/liquid_script.h @@ -67,8 +67,7 @@ lMax_sp$ID$ = $SNDPARTICLE_L_MAX$\n\ c_s_sp$ID$ = 0.4 # classification constant for snd parts\n\ c_b_sp$ID$ = 0.77 # classification constant for snd parts\n\ pot_radius_sp$ID$ = $SNDPARTICLE_POTENTIAL_RADIUS$\n\ -update_radius_sp$ID$ = $SNDPARTICLE_UPDATE_RADIUS$\n\ -scaleFromManta_sp$ID$ = $FLUID_DOMAIN_SIZE$ / float(res_s$ID$) # resize factor for snd parts\n"; +update_radius_sp$ID$ = $SNDPARTICLE_UPDATE_RADIUS$\n"; ////////////////////////////////////////////////////////////////////// // GRIDS & MESH & PARTICLESYSTEM @@ -263,7 +262,7 @@ def liquid_step_$ID$():\n\ velOld_s$ID$.copyFrom(vel_s$ID$)\n\ \n\ # forces & pressure solve\n\ - addGravity(flags=flags_s$ID$, vel=vel_s$ID$, gravity=gravity_s$ID$)\n\ + addGravity(flags=flags_s$ID$, vel=vel_s$ID$, gravity=gravity_s$ID$, scale=False)\n\ \n\ mantaMsg('Adding external forces')\n\ addForceField(flags=flags_s$ID$, vel=vel_s$ID$, force=forces_s$ID$)\n\ @@ -371,7 +370,7 @@ def liquid_step_particles_$ID$():\n\ flags_sp$ID$.updateFromLevelset(levelset=phi_sp$ID$)\n\ \n\ # Actual secondary particle simulation\n\ - flipComputeSecondaryParticlePotentials(potTA=trappedAir_sp$ID$, potWC=waveCrest_sp$ID$, potKE=kineticEnergy_sp$ID$, neighborRatio=neighborRatio_sp$ID$, flags=flags_sp$ID$, v=vel_sp$ID$, normal=normal_sp$ID$, phi=phi_sp$ID$, radius=pot_radius_sp$ID$, tauMinTA=tauMin_ta_sp$ID$, tauMaxTA=tauMax_ta_sp$ID$, tauMinWC=tauMin_wc_sp$ID$, tauMaxWC=tauMax_wc_sp$ID$, tauMinKE=tauMin_k_sp$ID$, tauMaxKE=tauMax_k_sp$ID$, scaleFromManta=scaleFromManta_sp$ID$)\n\ + flipComputeSecondaryParticlePotentials(potTA=trappedAir_sp$ID$, potWC=waveCrest_sp$ID$, potKE=kineticEnergy_sp$ID$, neighborRatio=neighborRatio_sp$ID$, flags=flags_sp$ID$, v=vel_sp$ID$, normal=normal_sp$ID$, phi=phi_sp$ID$, radius=pot_radius_sp$ID$, tauMinTA=tauMin_ta_sp$ID$, tauMaxTA=tauMax_ta_sp$ID$, tauMinWC=tauMin_wc_sp$ID$, tauMaxWC=tauMax_wc_sp$ID$, tauMinKE=tauMin_k_sp$ID$, tauMaxKE=tauMax_k_sp$ID$, scaleFromManta=ratioMetersToRes_s$ID$)\n\ flipSampleSecondaryParticles(mode='single', flags=flags_sp$ID$, v=vel_sp$ID$, pts_sec=ppSnd_sp$ID$, v_sec=pVelSnd_pp$ID$, l_sec=pLifeSnd_pp$ID$, lMin=lMin_sp$ID$, lMax=lMax_sp$ID$, potTA=trappedAir_sp$ID$, potWC=waveCrest_sp$ID$, potKE=kineticEnergy_sp$ID$, neighborRatio=neighborRatio_sp$ID$, c_s=c_s_sp$ID$, c_b=c_b_sp$ID$, k_ta=k_ta_sp$ID$, k_wc=k_wc_sp$ID$, dt=sp$ID$.timestep)\n\ flipUpdateSecondaryParticles(mode='linear', pts_sec=ppSnd_sp$ID$, v_sec=pVelSnd_pp$ID$, l_sec=pLifeSnd_pp$ID$, f_sec=pForceSnd_pp$ID$, flags=flags_sp$ID$, v=vel_sp$ID$, neighborRatio=neighborRatio_sp$ID$, radius=update_radius_sp$ID$, gravity=gravity_s$ID$, k_b=k_b_sp$ID$, k_d=k_d_sp$ID$, c_s=c_s_sp$ID$, c_b=c_b_sp$ID$, dt=sp$ID$.timestep)\n\ if $SNDPARTICLE_BOUNDARY_PUSHOUT$:\n\ diff --git a/intern/mantaflow/intern/strings/smoke_script.h b/intern/mantaflow/intern/strings/smoke_script.h index 146106fd147..ea2b1e9d848 100644 --- a/intern/mantaflow/intern/strings/smoke_script.h +++ b/intern/mantaflow/intern/strings/smoke_script.h @@ -302,6 +302,9 @@ def smoke_adaptive_step_$ID$(framenr):\n\ setObstacleFlags(flags=flags_s$ID$, phiObs=phiObs_s$ID$, phiOut=phiOut_s$ID$, phiIn=phiIn_s$ID$, boundaryWidth=1)\n\ flags_s$ID$.fillGrid()\n\ \n\ + # reset emission accumulation at the beginning of an adaptive frame\n\ + if not s$ID$.timePerFrame:\n\ + emission_s$ID$.setConst(0.)\n\ # accumulate emission value per adaptive step for later use in noise computation\n\ emission_s$ID$.join(emissionIn_s$ID$)\n\ \n\ @@ -373,9 +376,9 @@ def smoke_step_$ID$():\n\ \n\ if using_heat_s$ID$:\n\ mantaMsg('Adding heat buoyancy')\n\ - addBuoyancy(flags=flags_s$ID$, density=heat_s$ID$, vel=vel_s$ID$, gravity=gravity_s$ID$, coefficient=buoyancy_heat_s$ID$)\n\ + addBuoyancy(flags=flags_s$ID$, density=heat_s$ID$, vel=vel_s$ID$, gravity=gravity_s$ID$, coefficient=buoyancy_heat_s$ID$, scale=False)\n\ mantaMsg('Adding buoyancy')\n\ - addBuoyancy(flags=flags_s$ID$, density=density_s$ID$, vel=vel_s$ID$, gravity=gravity_s$ID$, coefficient=buoyancy_dens_s$ID$)\n\ + addBuoyancy(flags=flags_s$ID$, density=density_s$ID$, vel=vel_s$ID$, gravity=gravity_s$ID$, coefficient=buoyancy_dens_s$ID$, scale=False)\n\ \n\ mantaMsg('Adding forces')\n\ addForceField(flags=flags_s$ID$, vel=vel_s$ID$, force=forces_s$ID$)\n\ diff --git a/intern/string/CMakeLists.txt b/intern/string/CMakeLists.txt deleted file mode 100644 index 8c400f320ae..00000000000 --- a/intern/string/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -# ***** 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. -# -# The Original Code is Copyright (C) 2006, Blender Foundation -# All rights reserved. -# ***** END GPL LICENSE BLOCK ***** - -set(INC - . -) - -set(INC_SYS - -) - -set(SRC - intern/STR_String.cpp - - STR_String.h -) - -set(LIB -) - -blender_add_lib(bf_intern_string "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/intern/string/STR_String.h b/intern/string/STR_String.h deleted file mode 100644 index 97b23345f91..00000000000 --- a/intern/string/STR_String.h +++ /dev/null @@ -1,366 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup string - */ - -#ifndef __STR_STRING_H__ -#define __STR_STRING_H__ - -#ifndef STR_NO_ASSERTD -# undef assertd -# define assertd(exp) ((void)NULL) -#endif - -#include <limits.h> -#include <vector> - -#include <cstdlib> -#include <cstring> - -#ifdef WITH_CXX_GUARDEDALLOC -# include "MEM_guardedalloc.h" -#endif - -#ifdef _WIN32 -# define stricmp _stricmp -#endif - -class STR_String; - -typedef unsigned long dword; -typedef const STR_String &rcSTR_String; -typedef unsigned char byte; - -/** - * Smart String Value class. Is used by parser when an expression tree is build containing string. - */ - -class STR_String { - public: - // Initialization - STR_String(); - STR_String(char c); - STR_String(char c, int len); - STR_String(const char *str); - STR_String(const char *str, int len); - STR_String(const STR_String &str); - STR_String(const STR_String &str, int len); - STR_String(const char *src1, int src1_len, const char *src2, int src2_len); - explicit STR_String(int val); - explicit STR_String(dword val); - explicit STR_String(float val); - explicit STR_String(double val); - inline ~STR_String() - { - delete[] this->m_data; - } - - // Operations - STR_String &Format(const char *fmt, ...) // Set formatted text to string -#ifdef __GNUC__ - __attribute__((format(printf, 2, 3))) -#endif - ; - STR_String &FormatAdd(const char *fmt, ...) // Add formatted text to string -#ifdef __GNUC__ - __attribute__((format(printf, 2, 3))) -#endif - ; - inline void Clear() - { - this->m_len = this->m_data[0] = 0; - } - inline const STR_String &Reverse() - { - for (int i1 = 0, i2 = this->m_len - 1; i1 < i2; i1++, i2--) { - std::swap(this->m_data[i1], this->m_data[i2]); - } - return *this; - } - - // Properties - bool IsUpper() const; - bool IsLower() const; - inline bool IsEmpty() const - { - return this->m_len == 0; - } - inline int Length() const - { - return this->m_len; - } - - // Data access - inline STR_String &SetLength(int len) - { - AllocBuffer(len, true); - this->m_len = len; - this->m_data[len] = 0; - return *this; - } - inline char GetAt(int pos) const - { - assertd(pos < this->m_len); - return this->m_data[pos]; - } - inline void SetAt(int pos, char c) - { - assertd(pos < this->m_len); - this->m_data[pos] = c; - } - inline void SetAt(int pos, rcSTR_String str); - inline void SetAt(int pos, int num, rcSTR_String str); - void Replace(int pos, rcSTR_String str); - void Replace(int pos, int num, rcSTR_String str); - - // Substrings - inline STR_String Left(int num) const - { - num = (num < this->m_len ? num : this->m_len); - return STR_String(this->m_data, num); - } - inline STR_String Right(int num) const - { - num = (num < this->m_len ? num : this->m_len); - return STR_String(this->m_data + this->m_len - num, num); - } - inline STR_String Mid(int pos, int num = INT_MAX) const - { - pos = (pos < this->m_len ? pos : this->m_len); - num = (num < (this->m_len - pos) ? num : (this->m_len - pos)); - return STR_String(this->m_data + pos, num); - } - - // Comparison - int Compare(rcSTR_String rhs) const; - int CompareNoCase(rcSTR_String rhs) const; - inline bool IsEqual(rcSTR_String rhs) const - { - return (Compare(rhs) == 0); - } - inline bool IsEqualNoCase(rcSTR_String rhs) const - { - return (CompareNoCase(rhs) == 0); - } - - // Search/replace - int Find(char c, int pos = 0) const; - int Find(const char *str, int pos = 0) const; - int Find(rcSTR_String str, int pos = 0) const; - int RFind(char c) const; - int FindOneOf(const char *set, int pos = 0) const; - int RFindOneOf(const char *set, int pos = 0) const; - - std::vector<STR_String> Explode(char c) const; - - // Formatting - STR_String &Upper(); - STR_String &Lower(); - STR_String &Capitalize(); - STR_String &TrimLeft(); - STR_String &TrimLeft(char *set); - STR_String &TrimRight(); - STR_String &TrimRight(char *set); - STR_String &Trim(); - STR_String &Trim(char *set); - STR_String &TrimQuotes(); - - // Conversions - // inline operator char*() { return this->m_data; } - inline operator const char *() const - { - return this->m_data; - } - inline char *Ptr() - { - return this->m_data; - } - inline const char *ReadPtr() const - { - return this->m_data; - } - inline float ToFloat() const - { - float x = (float)(atof(this->m_data)); - return x; - } - inline int ToInt() const - { - return atoi(this->m_data); - } - - // Operators - inline rcSTR_String operator=(const byte *rhs) - { - return Copy((const char *)rhs, strlen((const char *)rhs)); - } - inline rcSTR_String operator=(rcSTR_String rhs) - { - return Copy(rhs.ReadPtr(), rhs.Length()); - } - inline rcSTR_String operator=(char rhs) - { - return Copy(&rhs, 1); - } - inline rcSTR_String operator=(const char *rhs) - { - return Copy(rhs, strlen(rhs)); - } - - inline rcSTR_String operator+=(const char *rhs) - { - return Concat(rhs, strlen(rhs)); - } - inline rcSTR_String operator+=(rcSTR_String rhs) - { - return Concat(rhs.ReadPtr(), rhs.Length()); - } - inline rcSTR_String operator+=(char rhs) - { - return Concat(&rhs, 1); - } - - inline friend bool operator<(rcSTR_String lhs, rcSTR_String rhs) - { - return (strcmp(lhs, rhs) < 0); - } - inline friend bool operator<(rcSTR_String lhs, const char *rhs) - { - return (strcmp(lhs, rhs) < 0); - } - inline friend bool operator<(const char *lhs, rcSTR_String rhs) - { - return (strcmp(lhs, rhs) < 0); - } - inline friend bool operator>(rcSTR_String lhs, rcSTR_String rhs) - { - return (strcmp(lhs, rhs) > 0); - } - inline friend bool operator>(rcSTR_String lhs, const char *rhs) - { - return (strcmp(lhs, rhs) > 0); - } - inline friend bool operator>(const char *lhs, rcSTR_String rhs) - { - return (strcmp(lhs, rhs) > 0); - } - inline friend bool operator<=(rcSTR_String lhs, rcSTR_String rhs) - { - return (strcmp(lhs, rhs) <= 0); - } - inline friend bool operator<=(rcSTR_String lhs, const char *rhs) - { - return (strcmp(lhs, rhs) <= 0); - } - inline friend bool operator<=(const char *lhs, rcSTR_String rhs) - { - return (strcmp(lhs, rhs) <= 0); - } - inline friend bool operator>=(rcSTR_String lhs, rcSTR_String rhs) - { - return (strcmp(lhs, rhs) >= 0); - } - inline friend bool operator>=(rcSTR_String lhs, const char *rhs) - { - return (strcmp(lhs, rhs) >= 0); - } - inline friend bool operator>=(const char *lhs, rcSTR_String rhs) - { - return (strcmp(lhs, rhs) >= 0); - } - inline friend bool operator==(rcSTR_String lhs, rcSTR_String rhs) - { - return ((lhs.Length() == rhs.Length()) && (memcmp(lhs, rhs, lhs.Length()) == 0)); - } - inline friend bool operator==(rcSTR_String lhs, const char *rhs) - { - return (strncmp(lhs, rhs, lhs.Length() + 1) == 0); - } - inline friend bool operator==(const char *lhs, rcSTR_String rhs) - { - return (strncmp(lhs, rhs, rhs.Length() + 1) == 0); - } - inline friend bool operator!=(rcSTR_String lhs, rcSTR_String rhs) - { - return ((lhs.Length() != rhs.Length()) || (memcmp(lhs, rhs, lhs.Length()) != 0)); - } - inline friend bool operator!=(rcSTR_String lhs, const char *rhs) - { - return (strncmp(lhs, rhs, lhs.Length() + 1) != 0); - } - inline friend bool operator!=(const char *lhs, rcSTR_String rhs) - { - return (strncmp(lhs, rhs, rhs.Length() + 1) != 0); - } - - // serializing - // int Serialize(pCStream stream); - - protected: - // Implementation - void AllocBuffer(int len, bool keep_contents); - rcSTR_String Copy(const char *src, int len); - rcSTR_String Concat(const char *data, int len); - - static bool isLower(char c) - { - return !isUpper(c); - } - static bool isUpper(char c) - { - return (c >= 'A') && (c <= 'Z'); - } - static bool isSpace(char c) - { - return (c == ' ') || (c == '\t'); - } - - char *m_data; // -> STR_String data - int m_len; // z Data length - int m_max; // Space in data buffer - -#ifdef WITH_CXX_GUARDEDALLOC - MEM_CXX_CLASS_ALLOC_FUNCS("CXX:STR_String") -#endif -}; - -inline STR_String operator+(rcSTR_String lhs, rcSTR_String rhs) -{ - return STR_String(lhs.ReadPtr(), lhs.Length(), rhs.ReadPtr(), rhs.Length()); -} -inline STR_String operator+(rcSTR_String lhs, char rhs) -{ - return STR_String(lhs.ReadPtr(), lhs.Length(), &rhs, 1); -} -inline STR_String operator+(char lhs, rcSTR_String rhs) -{ - return STR_String(&lhs, 1, rhs.ReadPtr(), rhs.Length()); -} -inline STR_String operator+(rcSTR_String lhs, const char *rhs) -{ - return STR_String(lhs.ReadPtr(), lhs.Length(), rhs, strlen(rhs)); -} -inline STR_String operator+(const char *lhs, rcSTR_String rhs) -{ - return STR_String(lhs, strlen(lhs), rhs.ReadPtr(), rhs.Length()); -} - -#endif //__STR_STRING_H__ diff --git a/intern/string/intern/STR_String.cpp b/intern/string/intern/STR_String.cpp deleted file mode 100644 index 306e786969b..00000000000 --- a/intern/string/intern/STR_String.cpp +++ /dev/null @@ -1,637 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup string - * - * Copyright (C) 2001 NaN Technologies B.V. - * This file was formerly known as: GEN_StdString.cpp. - */ - -#include "STR_String.h" -#include <ctype.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -/*------------------------------------------------------------------------------------------------- - Construction / destruction --------------------------------------------------------------------------------------------------*/ - -#define STR_STRING_SIZE_DEFAULT_WORD 32 /* default size for a new word */ -#define STR_STRING_SIZE_DEFAULT_CHAR 9 /* default size for a new char */ - -// -// Construct an empty string -// -STR_String::STR_String() - : m_data(new char[STR_STRING_SIZE_DEFAULT_WORD]), m_len(0), m_max(STR_STRING_SIZE_DEFAULT_WORD) -{ - this->m_data[0] = 0; -} - -// -// Construct a string of one character -// -STR_String::STR_String(char c) - : m_data(new char[STR_STRING_SIZE_DEFAULT_CHAR]), m_len(1), m_max(STR_STRING_SIZE_DEFAULT_CHAR) -{ - this->m_data[0] = c; - this->m_data[1] = 0; -} - -// -// Construct a string of multiple repeating characters -// -STR_String::STR_String(char c, int len) : m_data(new char[len + 8]), m_len(len), m_max(len + 8) -{ - assertd(this->m_data != NULL); - memset(this->m_data, c, len); - this->m_data[len] = 0; -} - -// -// Construct a string from a pointer-to-ASCIIZ-string -// -// MAART: Changed to test for null strings -STR_String::STR_String(const char *str) -{ - if (str) { - this->m_len = ::strlen(str); - this->m_max = this->m_len + 8; - this->m_data = new char[this->m_max]; - assertd(this->m_data != NULL); - ::memcpy(this->m_data, str, this->m_len); - this->m_data[this->m_len] = 0; - } - else { - this->m_data = NULL; - this->m_len = 0; - this->m_max = 8; - } -} - -// -// Construct a string from a pointer-to-ASCII-string and a length -// -STR_String::STR_String(const char *str, int len) - : m_data(new char[len + 8]), m_len(len), m_max(len + 8) -{ - assertd(this->m_data != NULL); - memcpy(this->m_data, str, len); - this->m_data[len] = 0; -} - -// -// Construct a string from another string -// -STR_String::STR_String(rcSTR_String str) - : m_data(new char[str.Length() + 8]), m_len(str.Length()), m_max(str.Length() + 8) -{ - assertd(this->m_data != NULL); - assertd(str.this->m_data != NULL); - memcpy(this->m_data, str.ReadPtr(), str.Length()); - this->m_data[str.Length()] = 0; -} - -// -// Construct a string from the first number of characters in another string -// -STR_String::STR_String(rcSTR_String str, int len) - : m_data(new char[len + 8]), m_len(len), m_max(len + 8) -{ - assertd(this->m_data != NULL); - assertd(str.this->m_data != NULL); - memcpy(this->m_data, str.ReadPtr(), str.Length()); - this->m_data[str.Length()] = 0; -} - -// -// Create a string by concatenating two sources -// -STR_String::STR_String(const char *src1, int len1, const char *src2, int len2) - : m_data(new char[len1 + len2 + 8]), m_len(len1 + len2), m_max(len1 + len2 + 8) -{ - assertd(this->m_data != NULL); - memcpy(this->m_data, src1, len1); - memcpy(this->m_data + len1, src2, len2); - this->m_data[len1 + len2] = 0; -} - -// -// Create a string with an integer value -// -STR_String::STR_String(int val) - : m_data(new char[STR_STRING_SIZE_DEFAULT_WORD]), m_max(STR_STRING_SIZE_DEFAULT_WORD) -{ - assertd(this->m_data != NULL); - this->m_len = sprintf(this->m_data, "%d", val); -} - -// -// Create a string with a dword value -// -STR_String::STR_String(dword val) - : m_data(new char[STR_STRING_SIZE_DEFAULT_WORD]), m_max(STR_STRING_SIZE_DEFAULT_WORD) -{ - assertd(this->m_data != NULL); - this->m_len = sprintf(this->m_data, "%lu", val); -} - -// -// Create a string with a floating point value -// -STR_String::STR_String(float val) - : m_data(new char[STR_STRING_SIZE_DEFAULT_WORD]), m_max(STR_STRING_SIZE_DEFAULT_WORD) -{ - assertd(this->m_data != NULL); - this->m_len = sprintf(this->m_data, "%g", val); -} - -// -// Create a string with a double value -// -STR_String::STR_String(double val) - : m_data(new char[STR_STRING_SIZE_DEFAULT_WORD]), m_max(STR_STRING_SIZE_DEFAULT_WORD) -{ - assertd(this->m_data != NULL); - this->m_len = sprintf(this->m_data, "%g", val); -} - -/*------------------------------------------------------------------------------------------------- - Buffer management --------------------------------------------------------------------------------------------------*/ - -// -// Make sure that the allocated buffer is at least <len> in size -// -void STR_String::AllocBuffer(int len, bool keep_contents) -{ - // Check if we have enough space - if (len + 1 <= this->m_max) - return; - - // Reallocate string - char *new_data = new char[len + 8]; - if (keep_contents) { - memcpy(new_data, this->m_data, this->m_len); - } - delete[] this->m_data; - - // Accept new data - this->m_max = len + 8; - this->m_data = new_data; - assertd(this->m_data != NULL); -} - -/*------------------------------------------------------------------------------------------------- - Basic string operations --------------------------------------------------------------------------------------------------*/ - -// -// Format string (as does sprintf) -// -STR_String &STR_String::Format(const char *fmt, ...) -{ - AllocBuffer(2048, false); - - assertd(this->m_data != NULL); - // Expand arguments and format to string - va_list args; - va_start(args, fmt); - this->m_len = vsprintf(this->m_data, fmt, args); - assertd(this->m_len <= 2048); - va_end(args); - - return *this; -} - -// -// Format string (as does sprintf) -// -STR_String &STR_String::FormatAdd(const char *fmt, ...) -{ - AllocBuffer(2048, false); - - assertd(this->m_data != NULL); - // Expand arguments and format to string - va_list args; - va_start(args, fmt); - this->m_len += vsprintf(this->m_data + this->m_len, fmt, args); - assertd(this->m_len <= 2048); - va_end(args); - - return *this; -} - -/*------------------------------------------------------------------------------------------------- - Properties --------------------------------------------------------------------------------------------------*/ - -// -// Check if string is entirely in UPPERCase -// -bool STR_String::IsUpper() const -{ - for (int i = 0; i < this->m_len; i++) - if (isLower(this->m_data[i])) - return false; - - return true; -} - -// -// Check if string is entirely in lowerCase -// -bool STR_String::IsLower() const -{ - for (int i = 0; i < this->m_len; i++) - if (isUpper(this->m_data[i])) - return false; - - return true; -} - -/*------------------------------------------------------------------------------------------------- - Search/Replace --------------------------------------------------------------------------------------------------*/ - -// -// Find the first orccurence of <c> in the string -// -int STR_String::Find(char c, int pos) const -{ - assertd(pos >= 0); - assertd(this->m_len == 0 || pos < this->m_len); - assertd(this->m_data != NULL); - char *find_pos = strchr(this->m_data + pos, c); - return (find_pos) ? (find_pos - this->m_data) : -1; -} - -// -// Find the first occurrence of <str> in the string -// -int STR_String::Find(const char *str, int pos) const -{ - assertd(pos >= 0); - assertd(this->m_len == 0 || pos < this->m_len); - assertd(this->m_data != NULL); - char *find_pos = strstr(this->m_data + pos, str); - return (find_pos) ? (find_pos - this->m_data) : -1; -} - -// -// Find the first occurrence of <str> in the string -// -int STR_String::Find(rcSTR_String str, int pos) const -{ - assertd(pos >= 0); - assertd(this->m_len == 0 || pos < this->m_len); - assertd(this->m_data != NULL); - char *find_pos = strstr(this->m_data + pos, str.ReadPtr()); - return (find_pos) ? (find_pos - this->m_data) : -1; -} - -// -// Find the last occurrence of <c> in the string -// -int STR_String::RFind(char c) const -{ - assertd(this->m_data != NULL); - char *pos = strrchr(this->m_data, c); - return (pos) ? (pos - this->m_data) : -1; -} - -// -// Find the first occurrence of any character in character set <set> in the string -// -int STR_String::FindOneOf(const char *set, int pos) const -{ - assertd(pos >= 0); - assertd(this->m_len == 0 || pos < this->m_len); - assertd(this->m_data != NULL); - char *find_pos = strpbrk(this->m_data + pos, set); - return (find_pos) ? (find_pos - this->m_data) : -1; -} - -// -// Replace a character in this string with another string -// -void STR_String::Replace(int pos, rcSTR_String str) -{ - // bounds(pos, 0, Length()-1); - - if (str.Length() < 1) { - // Remove one character from the string - memcpy(this->m_data + pos, this->m_data + pos + 1, this->m_len - pos); - } - else { - // Insert zero or more characters into the string - AllocBuffer(this->m_len + str.Length() - 1, true); - if (str.Length() != 1) - memcpy(this->m_data + pos + str.Length(), this->m_data + pos + 1, Length() - pos); - memcpy(this->m_data + pos, str.ReadPtr(), str.Length()); - } - - this->m_len += str.Length() - 1; -} - -// -// Replace a substring of this string with another string -// -void STR_String::Replace(int pos, int num, rcSTR_String str) -{ - // bounds(pos, 0, Length()-1); - // bounds(pos+num, 0, Length()); - assertd(num >= 1); - - if (str.Length() < num) { - // Remove some data from the string by replacement - memcpy( - this->m_data + pos + str.Length(), this->m_data + pos + num, this->m_len - pos - num + 1); - memcpy(this->m_data + pos, str.ReadPtr(), str.Length()); - } - else { - // Insert zero or more characters into the string - AllocBuffer(this->m_len + str.Length() - num, true); - if (str.Length() != num) - memcpy( - this->m_data + pos + str.Length(), this->m_data + pos + num, Length() - pos - num + 1); - memcpy(this->m_data + pos, str.ReadPtr(), str.Length()); - } - - this->m_len += str.Length() - num; -} - -/*------------------------------------------------------------------------------------------------- - Comparison --------------------------------------------------------------------------------------------------*/ - -// -// Compare two strings and return the result, -// <0 if *this<rhs, >0 if *this>rhs or 0 if *this==rhs -// -int STR_String::Compare(rcSTR_String rhs) const -{ - return strcmp(this->ReadPtr(), rhs.ReadPtr()); -} - -// -// Compare two strings without respecting case and return the result, -// <0 if *this<rhs, >0 if *this>rhs or 0 if *this==rhs -// -int STR_String::CompareNoCase(rcSTR_String rhs) const -{ -#ifdef WIN32 - return stricmp(this->ReadPtr(), rhs.ReadPtr()); -#else - return strcasecmp(this->ReadPtr(), rhs.ReadPtr()); -#endif -} - -/*------------------------------------------------------------------------------------------------- - Formatting --------------------------------------------------------------------------------------------------*/ - -// -// Capitalize string, "heLLo" -> "HELLO" -// -STR_String &STR_String::Upper() -{ - assertd(this->m_data != NULL); -#ifdef WIN32 - _strupr(this->m_data); -#else - for (int i = 0; i < this->m_len; i++) - this->m_data[i] = (this->m_data[i] >= 'a' && this->m_data[i] <= 'z') ? - this->m_data[i] + 'A' - 'a' : - this->m_data[i]; -#endif - return *this; -} - -// -// Lower string, "heLLo" -> "hello" -// -STR_String &STR_String::Lower() -{ - assertd(this->m_data != NULL); -#ifdef WIN32 - _strlwr(this->m_data); -#else - for (int i = 0; i < this->m_len; i++) - this->m_data[i] = (this->m_data[i] >= 'A' && this->m_data[i] <= 'Z') ? - this->m_data[i] + 'a' - 'A' : - this->m_data[i]; -#endif - return *this; -} - -// -// Capitalize string, "heLLo" -> "Hello" -// -STR_String &STR_String::Capitalize() -{ - assertd(this->m_data != NULL); -#ifdef WIN32 - if (this->m_len > 0) - this->m_data[0] = toupper(this->m_data[0]); - if (this->m_len > 1) - _strlwr(this->m_data + 1); -#else - if (this->m_len > 0) - this->m_data[0] = (this->m_data[0] >= 'a' && this->m_data[0] <= 'z') ? - this->m_data[0] + 'A' - 'a' : - this->m_data[0]; - for (int i = 1; i < this->m_len; i++) - this->m_data[i] = (this->m_data[i] >= 'A' && this->m_data[i] <= 'Z') ? - this->m_data[i] + 'a' - 'A' : - this->m_data[i]; -#endif - return *this; -} - -// -// Trim whitespace from the left side of the string -// -STR_String &STR_String::TrimLeft() -{ - int skip; - assertd(this->m_data != NULL); - for (skip = 0; isSpace(this->m_data[skip]); skip++, this->m_len--) { - /* pass */ - } - memmove(this->m_data, this->m_data + skip, this->m_len + 1); - return *this; -} - -// -// Trim whitespaces from the right side of the string -// -STR_String &STR_String::TrimRight() -{ - assertd(this->m_data != NULL); - while (this->m_len && isSpace(this->m_data[this->m_len - 1])) - this->m_len--; - this->m_data[this->m_len] = 0; - return *this; -} - -// -// Trim spaces from both sides of the character set -// -STR_String &STR_String::Trim() -{ - TrimRight(); - TrimLeft(); - return *this; -} - -// -// Trim characters from the character set <set> from the left side of the string -// -STR_String &STR_String::TrimLeft(char *set) -{ - int skip; - assertd(this->m_data != NULL); - for (skip = 0; this->m_len && strchr(set, this->m_data[skip]); skip++, this->m_len--) { - /* pass */ - } - memmove(this->m_data, this->m_data + skip, this->m_len + 1); - return *this; -} - -// -// Trim characters from the character set <set> from the right side of the string -// -STR_String &STR_String::TrimRight(char *set) -{ - assertd(this->m_data != NULL); - while (this->m_len && strchr(set, this->m_data[this->m_len - 1])) - this->m_len--; - this->m_data[this->m_len] = 0; - return *this; -} - -// -// Trim characters from the character set <set> from both sides of the character set -// -STR_String &STR_String::Trim(char *set) -{ - TrimRight(set); - TrimLeft(set); - return *this; -} - -// -// Trim quotes from both sides of the string -// -STR_String &STR_String::TrimQuotes() -{ - // Trim quotes if they are on both sides of the string - assertd(this->m_data != NULL); - if ((this->m_len >= 2) && (this->m_data[0] == '\"') && (this->m_data[this->m_len - 1] == '\"')) { - memmove(this->m_data, this->m_data + 1, this->m_len - 2 + 1); - this->m_len -= 2; - } - return *this; -} - -/*------------------------------------------------------------------------------------------------- - Assignment/Concatenation --------------------------------------------------------------------------------------------------*/ - -// -// Set the string's conents to a copy of <src> with length <len> -// -rcSTR_String STR_String::Copy(const char *src, int len) -{ - assertd(len >= 0); - assertd(src); - assertd(this->m_data != NULL); - - AllocBuffer(len, false); - this->m_len = len; - memcpy(this->m_data, src, len); - this->m_data[this->m_len] = 0; - - return *this; -} - -// -// Concate a number of bytes to the current string -// -rcSTR_String STR_String::Concat(const char *data, int len) -{ - assertd(this->m_len >= 0); - assertd(len >= 0); - assertd(data); - assertd(this->m_data != NULL); - - AllocBuffer(this->m_len + len, true); - memcpy(this->m_data + this->m_len, data, len); - this->m_len += len; - this->m_data[this->m_len] = 0; - - return *this; -} - -std::vector<STR_String> STR_String::Explode(char c) const -{ - STR_String lcv = *this; - std::vector<STR_String> uc; - - while (lcv.Length()) { - int pos = lcv.Find(c); - if (pos < 0) { - uc.push_back(lcv); - lcv.Clear(); - } - else { - uc.push_back(lcv.Left(pos)); - lcv = lcv.Mid(pos + 1); - } - } - - // uc. -= STR_String(""); - - return uc; -} - -#if 0 - -int STR_String::Serialize(pCStream stream) -{ - if (stream->GetAccess() == CStream::Access_Read) { - int ln; - stream->Read(&ln, sizeof(ln)); - AllocBuffer(ln, false); - stream->Read(this->m_data, ln); - this->m_data[ln] = '\0'; - this->m_len = ln; - } - else { - stream->Write(&this->m_len, sizeof(this->m_len)); - stream->Write(this->m_data, this->m_len); - } - - return this->m_len + sizeof(this->m_len); -} -#endif diff --git a/release/scripts/modules/bl_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py index c736f78d501..c233681dc41 100644 --- a/release/scripts/modules/bl_i18n_utils/settings.py +++ b/release/scripts/modules/bl_i18n_utils/settings.py @@ -377,7 +377,6 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "normal", "right", "the lazy dog", - "trunc(A)", "unable to load movie clip", "unable to load text", "unable to open the file", diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 09e9556cf2c..604c5231df2 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -798,8 +798,8 @@ def km_outliner(params): ("outliner.drivers_add_selected", {"type": 'D', "value": 'PRESS', "ctrl": True}, None), ("outliner.drivers_delete_selected", {"type": 'D', "value": 'PRESS', "ctrl": True, "alt": True}, None), ("outliner.collection_new", {"type": 'C', "value": 'PRESS'}, None), - ("outliner.collection_delete", {"type": 'X', "value": 'PRESS'}, None), - ("outliner.collection_delete", {"type": 'DEL', "value": 'PRESS'}, None), + ("outliner.delete", {"type": 'X', "value": 'PRESS'}, None), + ("outliner.delete", {"type": 'DEL', "value": 'PRESS'}, None), ("object.move_to_collection", {"type": 'M', "value": 'PRESS'}, None), ("object.link_to_collection", {"type": 'M', "value": 'PRESS', "shift": True}, None), ("outliner.collection_exclude_set", {"type": 'E', "value": 'PRESS'}, None), @@ -3226,6 +3226,8 @@ def km_grease_pencil_stroke_paint_mode(params): {"properties": [("unselected", True)]}), # Active layer op_menu("GPENCIL_MT_layer_active", {"type": 'Y', "value": 'PRESS'}), + # Active material + op_menu("GPENCIL_MT_material_active", {"type": 'U', "value": 'PRESS'}), # Keyframe menu op_menu("VIEW3D_MT_gpencil_animation", {"type": 'I', "value": 'PRESS'}), # Draw context menu @@ -3859,7 +3861,8 @@ def km_pose(params): {"properties": [("direction", 'CHILD'), ("extend", False)]}), ("pose.select_hierarchy", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "shift": True}, {"properties": [("direction", 'CHILD'), ("extend", True)]}), - ("pose.select_linked", {"type": 'L', "value": 'PRESS'}, None), + ("pose.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None), + ("pose.select_linked_pick", {"type": 'L', "value": 'PRESS'}, None), ("pose.select_grouped", {"type": 'G', "value": 'PRESS', "shift": True}, None), ("pose.select_mirror", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None), ("pose.constraint_add_with_targets", {"type": 'C', "value": 'PRESS', "shift": True, "ctrl": True}, None), diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 38070d1c400..842a12ed249 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -520,10 +520,8 @@ def km_outliner(params): ("anim.keyframe_delete", {"type": 'S', "value": 'PRESS', "alt": True}, None), ("outliner.drivers_add_selected", {"type": 'D', "value": 'PRESS', "ctrl": True}, None), ("outliner.drivers_delete_selected", {"type": 'D', "value": 'PRESS', "ctrl": True, "alt": True}, None), - ("outliner.collection_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None), - ("outliner.collection_delete", {"type": 'DEL', "value": 'PRESS'}, None), - ("outliner.object_operation", {"type": 'BACK_SPACE', "value": 'PRESS'}, {"properties": [("type", 'DELETE')]}), - ("outliner.object_operation", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("type", 'DELETE')]}), + ("outliner.delete", {"type": 'BACK_SPACE', "value": 'PRESS'}, None), + ("outliner.delete", {"type": 'DEL', "value": 'PRESS'}, None), ("object.move_to_collection", {"type": 'G', "value": 'PRESS', "ctrl": True}, None), ("object.link_to_collection", {"type": 'M', "value": 'PRESS', "shift": True, "ctrl": True}, None), ("outliner.collection_exclude_set", {"type": 'E', "value": 'PRESS'}, None), @@ -2376,7 +2374,7 @@ def km_grease_pencil_stroke_paint_mode(params): op_tool_cycle("builtin.cutter", {"type": 'K', "value": 'PRESS'}), op_tool_cycle("builtin.cursor", {"type": 'C', "value": 'PRESS'}), # Active layer - op_menu("GPENCIL_MT_layer_active", {"type": 'M', "value": 'PRESS'}), + op_menu("GPENCIL_MT_layer_active", {"type": 'Y', "value": 'PRESS'}), # Keyframe menu op_menu("VIEW3D_MT_gpencil_animation", {"type": 'I', "value": 'PRESS'}), ]) diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index f900b0ee3df..9e2ce8b83ad 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -87,8 +87,10 @@ def context_path_validate(context, data_path): # One of the items in the rna path is None, just ignore this value = Ellipsis else: - # We have a real error in the rna path, don't ignore that - raise + # Print invalid path, but don't show error to the users and fully + # break the UI if the operator is bound to an event like left click. + print("context_path_validate error: context.%s not found (invalid keymap entry?)" % data_path) + value = Ellipsis return value diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index df6281646b4..9efb2e693ee 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -686,8 +686,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): split = layout.split() col = split.column() col.prop(md, "levels", text="Preview") - # TODO(sergey): Expose it again after T58473 is solved. - # col.prop(md, "sculpt_levels", text="Sculpt") + col.prop(md, "sculpt_levels", text="Sculpt") col.prop(md, "render_levels", text="Render") row = col.row() @@ -697,10 +696,20 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col = split.column() col.enabled = ob.mode != 'EDIT' - col.operator("object.multires_subdivide", text="Subdivide") + op = col.operator("object.multires_subdivide", text="Subdivide") + op.mode = 'CATMULL_CLARK' + + op = col.operator("object.multires_subdivide", text="Subdivide Simple") + op.mode = 'SIMPLE' + + op = col.operator("object.multires_subdivide", text="Subdivide Linear") + op.mode = 'LINEAR' + col.operator("object.multires_higher_levels_delete", text="Delete Higher") + col.operator("object.multires_unsubdivide", text="Unsubdivide") col.operator("object.multires_reshape", text="Reshape") col.operator("object.multires_base_apply", text="Apply Base") + col.operator("object.multires_rebuild_subdiv", text="Rebuild Subdivisions") col.prop(md, "uv_smooth", text="") col.prop(md, "show_only_control_edges") col.prop(md, "use_creases") @@ -1985,7 +1994,7 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): col.prop(md, "mode") if md.mode in {'STROKE', 'STROKE_AND_FILL'}: - col.label(text="Stroke Texture Control:") + col.label(text="Stroke Texture:") col.prop(md, "fit_method") col.prop(md, "uv_offset") col.prop(md, "uv_scale") @@ -1994,7 +2003,7 @@ class DATA_PT_gpencil_modifiers(ModifierButtonsPanel, Panel): col.separator() if md.mode in {'FILL', 'STROKE_AND_FILL'}: - col.label(text="Fill Texture Control:") + col.label(text="Fill Texture:") col.prop(md, "fill_rotation", text="Rotation") col.prop(md, "fill_offset", text="Location") col.prop(md, "fill_scale", text="Scale") diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 0f6a51ad2d0..fea4406cb8c 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -361,6 +361,35 @@ class GPENCIL_MT_layer_active(Menu): layout.operator("gpencil.layer_add", text="New Layer", icon='ADD') +class GPENCIL_MT_material_active(Menu): + bl_label = "Change Active Material" + + @classmethod + def poll(cls, context): + ob = context.active_object + tool_settings = context.scene.tool_settings + mode = tool_settings.gpencil_paint.color_mode + if mode != 'MATERIAL': + return False + + if ob is None or len(ob.material_slots) == 0: + return False + + return True + + def draw(self, context): + layout = self.layout + layout.operator_context = 'INVOKE_REGION_WIN' + ob = context.active_object + mat_active = ob.active_material + + for slot in ob.material_slots: + mat = slot.material + if mat: + icon = mat.id_data.preview.icon_id + layout.operator("gpencil.material_set", text=mat.name, icon_value=icon).slot = mat.name + + class GPENCIL_MT_gpencil_draw_delete(Menu): bl_label = "Delete" @@ -632,8 +661,8 @@ class GreasePencilMaterialsPanel: if ob.data.use_stroke_edit_mode: row = layout.row(align=True) row.operator("gpencil.stroke_change_color", text="Assign") - row.operator("gpencil.select_material", text="Select").deselect = False - row.operator("gpencil.select_material", text="Deselect").deselect = True + row.operator("gpencil.material_select", text="Select").deselect = False + row.operator("gpencil.material_select", text="Deselect").deselect = True # stroke color ma = None if is_view3d and brush is not None: @@ -931,6 +960,7 @@ classes = ( GPENCIL_MT_cleanup, GPENCIL_MT_move_to_layer, GPENCIL_MT_layer_active, + GPENCIL_MT_material_active, GPENCIL_MT_gpencil_draw_delete, GPENCIL_MT_layer_mask_menu, diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py index ab70c4c25c0..6aaec9940e8 100644 --- a/release/scripts/startup/bl_ui/properties_material.py +++ b/release/scripts/startup/bl_ui/properties_material.py @@ -172,10 +172,11 @@ class EEVEE_MATERIAL_PT_surface(MaterialButtonsPanel, Panel): layout.prop(mat, "use_nodes", icon='NODETREE') layout.separator() + layout.use_property_split = True + if mat.use_nodes: panel_node_draw(layout, mat.node_tree, 'OUTPUT_MATERIAL', "Surface") else: - layout.use_property_split = True layout.prop(mat, "diffuse_color", text="Base Color") layout.prop(mat, "metallic") layout.prop(mat, "specular_intensity", text="Specular") @@ -197,6 +198,8 @@ class EEVEE_MATERIAL_PT_volume(MaterialButtonsPanel, Panel): def draw(self, context): layout = self.layout + layout.use_property_split = True + mat = context.material panel_node_draw(layout, mat.node_tree, 'OUTPUT_MATERIAL', "Volume") diff --git a/release/scripts/startup/bl_ui/properties_physics_fluid.py b/release/scripts/startup/bl_ui/properties_physics_fluid.py index d6d2526384a..de526506f68 100644 --- a/release/scripts/startup/bl_ui/properties_physics_fluid.py +++ b/release/scripts/startup/bl_ui/properties_physics_fluid.py @@ -1002,7 +1002,6 @@ class PHYSICS_PT_diffusion(PhysicButtonsPanel, Panel): col.prop(domain, "viscosity_exponent", text="Exponent", slider=True) col = flow.column() - col.prop(domain, "domain_size", text="Real World Size") col.prop(domain, "surface_tension", text="Surface Tension") diff --git a/release/scripts/startup/bl_ui/properties_texture.py b/release/scripts/startup/bl_ui/properties_texture.py index 40a630ff834..5af8bc2aaa7 100644 --- a/release/scripts/startup/bl_ui/properties_texture.py +++ b/release/scripts/startup/bl_ui/properties_texture.py @@ -163,6 +163,8 @@ class TEXTURE_PT_node(TextureButtonsPanel, Panel): def draw(self, context): layout = self.layout + layout.use_property_split = True + node = context.texture_node ntree = node.id_data layout.template_node_view(ntree, node, None) diff --git a/release/scripts/startup/bl_ui/properties_world.py b/release/scripts/startup/bl_ui/properties_world.py index 705be66ecc1..6f00e521e58 100644 --- a/release/scripts/startup/bl_ui/properties_world.py +++ b/release/scripts/startup/bl_ui/properties_world.py @@ -105,6 +105,8 @@ class EEVEE_WORLD_PT_surface(WorldButtonsPanel, Panel): layout.prop(world, "use_nodes", icon='NODETREE') layout.separator() + layout.use_property_split = True + if world.use_nodes: ntree = world.node_tree node = ntree.get_output_node('EEVEE') @@ -139,6 +141,8 @@ class EEVEE_WORLD_PT_volume(WorldButtonsPanel, Panel): ntree = world.node_tree node = ntree.get_output_node('EEVEE') + layout.use_property_split = True + if node: input = find_node_input(node, 'Volume') if input: diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index f93629a4f03..5b6cc6609e0 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -918,6 +918,10 @@ class CLIP_PT_tracking_lens(Panel): col = layout.column(align=True) col.prop(camera, "division_k1") col.prop(camera, "division_k2") + elif camera.distortion_model == 'NUKE': + col = layout.column(align=True) + col.prop(camera, "nuke_k1") + col.prop(camera, "nuke_k2") class CLIP_PT_marker(CLIP_PT_tracking_panel, Panel): diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index a74d9cc9547..ee8015df273 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -212,8 +212,8 @@ class OUTLINER_MT_collection(Menu): layout.separator() - layout.operator("outliner.collection_delete", text="Delete", icon='X').hierarchy = False - layout.operator("outliner.collection_delete", text="Delete Hierarchy").hierarchy = True + layout.operator("outliner.delete", text="Delete", icon='X') + layout.operator("outliner.collection_hierarchy_delete") layout.separator() @@ -278,7 +278,7 @@ class OUTLINER_MT_object(Menu): layout.separator() - layout.operator("outliner.object_operation", text="Delete", icon='X').type = 'DELETE' + layout.operator("outliner.delete", text="Delete", icon='X') if space.display_mode == 'VIEW_LAYER' and not space.use_filter_collection: layout.operator("outliner.object_operation", text="Delete Hierarchy").type = 'DELETE_HIERARCHY' diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index e4539ec7ca7..e1f675871ef 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -1139,6 +1139,8 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel): col = layout.column(align=True) if strip_type == 'SPEED': col.prop(strip, "multiply_speed") + col.prop(strip, "frame_interpolation_mode") + elif 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: diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 6c769e3b013..3e4a3f5de4c 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -1469,6 +1469,7 @@ class VIEW3D_MT_select_pose(Menu): layout.separator() layout.operator("pose.select_constraint_target", text="Constraint Target") + layout.operator("pose.select_linked", text="Linked") layout.separator() @@ -7059,6 +7060,18 @@ def draw_gpencil_layer_active(context, layout): row.operator("gpencil.layer_remove", text="", icon='X') +def draw_gpencil_material_active(context, layout): + ob = context.active_object + if ob and len(ob.material_slots) > 0 and ob.active_material_index >= 0: + ma = ob.material_slots[ob.active_material_index].material + if ma: + layout.label(text="Active Material") + row = layout.row(align=True) + row.operator_context = 'EXEC_REGION_WIN' + row.operator_menu_enum("gpencil.material_set", "slot", text="", icon='MATERIAL') + row.prop(ma, "name", text="") + + class VIEW3D_PT_gpencil_sculpt_context_menu(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'WINDOW' @@ -7132,6 +7145,9 @@ class VIEW3D_PT_gpencil_draw_context_menu(Panel): # Layers draw_gpencil_layer_active(context, layout) + # Material + if not is_vertex: + draw_gpencil_material_active(context, layout) class VIEW3D_PT_gpencil_vertex_context_menu(Panel): diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index f6f1393bd21..ddb88cf61ed 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -117,9 +117,11 @@ void BLF_draw_ascii(int fontid, const char *str, size_t len) ATTR_NONNULL(2); int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth) ATTR_NONNULL(2); typedef bool (*BLF_GlyphBoundsFn)(const char *str, - const size_t str_ofs, - const struct rcti *glyph_bounds, + const size_t str_step_ofs, + const struct rcti *glyph_step_bounds, const int glyph_advance_x, + const struct rctf *glyph_bounds, + const int glyph_bearing[2], void *user_data); void BLF_boundbox_foreach_glyph_ex(int fontid, diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index 394704e1c20..2f7d5a60a6f 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -946,8 +946,8 @@ void BLF_buffer(int fontid, if (font) { font->buf_info.fbuf = fbuf; font->buf_info.cbuf = cbuf; - font->buf_info.w = w; - font->buf_info.h = h; + font->buf_info.dims[0] = w; + font->buf_info.dims[1] = h; font->buf_info.ch = nch; font->buf_info.display = display; } diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index ed92e9aaff2..e5e03418073 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -613,28 +613,28 @@ static void blf_font_draw_buffer_ex(FontBLF *font, BLF_KERNING_STEP_FAST(font, kern_mode, g_prev, g, c_prev, c, pen_x); } - chx = pen_x + ((int)g->pos_x); - chy = pen_y_basis + g->height; + chx = pen_x + ((int)g->pos[0]); + chy = pen_y_basis + g->dims[1]; if (g->pitch < 0) { - pen_y = pen_y_basis + (g->height - (int)g->pos_y); + pen_y = pen_y_basis + (g->dims[1] - g->pos[1]); } else { - pen_y = pen_y_basis - (g->height - (int)g->pos_y); + pen_y = pen_y_basis - (g->dims[1] - g->pos[1]); } - if ((chx + g->width) >= 0 && chx < buf_info->w && (pen_y + g->height) >= 0 && - pen_y < buf_info->h) { + if ((chx + g->dims[0]) >= 0 && chx < buf_info->dims[0] && (pen_y + g->dims[1]) >= 0 && + pen_y < buf_info->dims[1]) { /* don't draw beyond the buffer bounds */ - int width_clip = g->width; - int height_clip = g->height; - int yb_start = g->pitch < 0 ? 0 : g->height - 1; + int width_clip = g->dims[0]; + int height_clip = g->dims[1]; + int yb_start = g->pitch < 0 ? 0 : g->dims[1] - 1; - if (width_clip + chx > buf_info->w) { - width_clip -= chx + width_clip - buf_info->w; + if (width_clip + chx > buf_info->dims[0]) { + width_clip -= chx + width_clip - buf_info->dims[0]; } - if (height_clip + pen_y > buf_info->h) { - height_clip -= pen_y + height_clip - buf_info->h; + if (height_clip + pen_y > buf_info->dims[1]) { + height_clip -= pen_y + height_clip - buf_info->dims[1]; } /* drawing below the image? */ @@ -652,7 +652,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font, if (a_byte) { const float a = (a_byte / 255.0f) * b_col_float[3]; const size_t buf_ofs = (((size_t)(chx + x) + - ((size_t)(pen_y + y) * (size_t)buf_info->w)) * + ((size_t)(pen_y + y) * (size_t)buf_info->dims[0])) * (size_t)buf_info->ch); float *fbuf = buf_info->fbuf + buf_ofs; @@ -689,7 +689,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font, if (a_byte) { const float a = (a_byte / 255.0f) * b_col_float[3]; const size_t buf_ofs = (((size_t)(chx + x) + - ((size_t)(pen_y + y) * (size_t)buf_info->w)) * + ((size_t)(pen_y + y) * (size_t)buf_info->dims[0])) * (size_t)buf_info->ch); unsigned char *cbuf = buf_info->cbuf + buf_ofs; @@ -1246,13 +1246,13 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, } gbox.xmin = pen_x; - gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->width); + gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]); gbox.ymin = pen_y; - gbox.ymax = gbox.ymin - g->height; + gbox.ymax = gbox.ymin - g->dims[1]; pen_x += g->advance_i; - if (user_fn(str, i_curr, &gbox, g->advance_i, user_data) == false) { + if (user_fn(str, i_curr, &gbox, g->advance_i, &g->box, g->pos, user_data) == false) { break; } @@ -1365,8 +1365,8 @@ static void blf_font_fill(FontBLF *font) font->buf_info.fbuf = NULL; font->buf_info.cbuf = NULL; - font->buf_info.w = 0; - font->buf_info.h = 0; + font->buf_info.dims[0] = 0; + font->buf_info.dims[1] = 0; font->buf_info.ch = 0; font->buf_info.col_init[0] = 0; font->buf_info.col_init[1] = 0; diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index a38cb323777..ce17069e53f 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -212,7 +212,7 @@ void blf_glyph_cache_free(GlyphCacheBLF *gc) GlyphBLF *g; unsigned int i; - for (i = 0; i < 257; i++) { + for (i = 0; i < ARRAY_SIZE(gc->bucket); i++) { while ((g = BLI_pophead(&gc->bucket[i]))) { blf_glyph_free(g); } @@ -327,26 +327,27 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un g->c = c; g->idx = (FT_UInt)index; bitmap = slot->bitmap; - g->width = (int)bitmap.width; - g->height = (int)bitmap.rows; + g->dims[0] = (int)bitmap.width; + g->dims[1] = (int)bitmap.rows; - if (g->width && g->height) { + const int buffer_size = g->dims[0] * g->dims[1]; + + if (buffer_size != 0) { if (font->flags & BLF_MONOCHROME) { /* Font buffer uses only 0 or 1 values, Blender expects full 0..255 range */ - int i; - for (i = 0; i < (g->width * g->height); i++) { + for (int i = 0; i < buffer_size; i++) { bitmap.buffer[i] = bitmap.buffer[i] ? 255 : 0; } } - g->bitmap = (unsigned char *)MEM_mallocN((size_t)g->width * (size_t)g->height, "glyph bitmap"); - memcpy((void *)g->bitmap, (void *)bitmap.buffer, (size_t)g->width * (size_t)g->height); + g->bitmap = MEM_mallocN((size_t)buffer_size, "glyph bitmap"); + memcpy(g->bitmap, bitmap.buffer, (size_t)buffer_size); } g->advance = ((float)slot->advance.x) / 64.0f; g->advance_i = (int)g->advance; - g->pos_x = (float)slot->bitmap_left; - g->pos_y = (float)slot->bitmap_top; + g->pos[0] = slot->bitmap_left; + g->pos[1] = slot->bitmap_top; g->pitch = slot->bitmap.pitch; FT_Outline_Get_CBox(&(slot->outline), &bbox); @@ -431,10 +432,10 @@ static void blf_texture3_draw(const unsigned char color_in[4], static void blf_glyph_calc_rect(rctf *rect, GlyphBLF *g, float x, float y) { - rect->xmin = floorf(x + g->pos_x); - rect->xmax = rect->xmin + (float)g->width; - rect->ymin = floorf(y + g->pos_y); - rect->ymax = rect->ymin - (float)g->height; + rect->xmin = floorf(x + (float)g->pos[0]); + rect->xmax = rect->xmin + (float)g->dims[0]; + rect->ymin = floorf(y + (float)g->pos[1]); + rect->ymax = rect->ymin - (float)g->dims[1]; } static void blf_glyph_calc_rect_test(rctf *rect, GlyphBLF *g, float x, float y) @@ -443,9 +444,9 @@ static void blf_glyph_calc_rect_test(rctf *rect, GlyphBLF *g, float x, float y) * width used by BLF_width. This allows that the text slightly * overlaps the clipping border to achieve better alignment. */ rect->xmin = floorf(x); - rect->xmax = rect->xmin + MIN2(g->advance, (float)g->width); + rect->xmax = rect->xmin + MIN2(g->advance, (float)g->dims[0]); rect->ymin = floorf(y); - rect->ymax = rect->ymin - (float)g->height; + rect->ymax = rect->ymin - (float)g->dims[1]; } static void blf_glyph_calc_rect_shadow(rctf *rect, GlyphBLF *g, float x, float y, FontBLF *font) @@ -455,7 +456,7 @@ static void blf_glyph_calc_rect_shadow(rctf *rect, GlyphBLF *g, float x, float y void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, float y) { - if ((!g->width) || (!g->height)) { + if ((!g->dims[0]) || (!g->dims[1])) { return; } @@ -466,7 +467,7 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl g->offset = gc->bitmap_len; - int buff_size = g->width * g->height; + int buff_size = g->dims[0] * g->dims[1]; int bitmap_len = gc->bitmap_len + buff_size; if (bitmap_len > gc->bitmap_len_alloc) { @@ -514,7 +515,7 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl if (font->shadow == 0) { blf_texture_draw(font->shadow_color, - (int[2]){g->width, g->height}, + g->dims, g->offset, rect_ofs.xmin, rect_ofs.ymin, @@ -523,7 +524,7 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl } else if (font->shadow <= 4) { blf_texture3_draw(font->shadow_color, - (int[2]){g->width, g->height}, + g->dims, g->offset, rect_ofs.xmin, rect_ofs.ymin, @@ -532,7 +533,7 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl } else { blf_texture5_draw(font->shadow_color, - (int[2]){g->width, g->height}, + g->dims, g->offset, rect_ofs.xmin, rect_ofs.ymin, @@ -547,39 +548,18 @@ void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, fl #if BLF_BLUR_ENABLE switch (font->blur) { case 3: - blf_texture3_draw(font->color, - (int[2]){g->width, g->height}, - g->offset, - rect.xmin, - rect.ymin, - rect.xmax, - rect.ymax); + blf_texture3_draw( + font->color, g->dims, g->offset, rect.xmin, rect.ymin, rect.xmax, rect.ymax); break; case 5: - blf_texture5_draw(font->color, - (int[2]){g->width, g->height}, - g->offset, - rect.xmin, - rect.ymin, - rect.xmax, - rect.ymax); + blf_texture5_draw( + font->color, g->dims, g->offset, rect.xmin, rect.ymin, rect.xmax, rect.ymax); break; default: - blf_texture_draw(font->color, - (int[2]){g->width, g->height}, - g->offset, - rect.xmin, - rect.ymin, - rect.xmax, - rect.ymax); + blf_texture_draw( + font->color, g->dims, g->offset, rect.xmin, rect.ymin, rect.xmax, rect.ymax); } #else - blf_texture_draw(font->color, - (int[2]){g->width, g->height}, - g->offset, - rect.xmin, - rect.ymin, - rect.xmax, - rect.ymax); + blf_texture_draw(font->color, g->dims, g->offset, rect.xmin, rect.ymin, rect.xmax, rect.ymax); #endif } diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 98ada87d16d..4ae592d323f 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -103,9 +103,11 @@ void blf_font_boundbox_foreach_glyph(struct FontBLF *font, const char *str, size_t len, bool (*user_fn)(const char *str, - const size_t str_ofs, - const struct rcti *glyph_bounds, + const size_t str_step_ofs, + const struct rcti *glyph_step_bounds, const int glyph_advance_x, + const struct rctf *glyph_bounds, + const int glyph_bearing[2], void *user_data), void *user_data, struct ResultBLF *r_info); diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 6fae3eb4376..362cbf6730f 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -119,17 +119,16 @@ typedef struct GlyphBLF { */ unsigned char *bitmap; - /* glyph width and height. */ - int width; - int height; + /* Glyph width and height. */ + int dims[2]; int pitch; - /* X and Y bearing of the glyph. + /** + * X and Y bearing of the glyph. * The X bearing is from the origin to the glyph left bbox edge. * The Y bearing is from the baseline to the top of the glyph edge. */ - float pos_x; - float pos_y; + int pos[2]; struct GlyphCacheBLF *glyph_cache; } GlyphBLF; @@ -141,9 +140,8 @@ typedef struct FontBufInfoBLF { /* the same but unsigned char */ unsigned char *cbuf; - /* buffer size, keep signed so comparisons with negative values work */ - int w; - int h; + /** Buffer size, keep signed so comparisons with negative values work. */ + int dims[2]; /* number of channels. */ int ch; diff --git a/source/blender/blenfont/intern/blf_thumbs.c b/source/blender/blenfont/intern/blf_thumbs.c index b7308d47d71..37eed29f6fe 100644 --- a/source/blender/blenfont/intern/blf_thumbs.c +++ b/source/blender/blenfont/intern/blf_thumbs.c @@ -78,8 +78,8 @@ void BLF_thumb_preview(const char *filename, /* Would be done via the BLF API, but we're not using a fontid here */ font->buf_info.cbuf = buf; font->buf_info.ch = channels; - font->buf_info.w = w; - font->buf_info.h = h; + font->buf_info.dims[0] = w; + font->buf_info.dims[1] = h; /* Always create the image with a white font, * the caller can theme how it likes */ diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h index adc94a833c3..3524be99d0a 100644 --- a/source/blender/blenkernel/BKE_curve.h +++ b/source/blender/blenkernel/BKE_curve.h @@ -111,7 +111,7 @@ void BKE_curve_material_index_clear(struct Curve *cu); bool BKE_curve_material_index_validate(struct Curve *cu); void BKE_curve_material_remap(struct Curve *cu, const unsigned int *remap, unsigned int remap_len); -void BKE_curve_smooth_flag_set(struct Curve *cu, bool set); +void BKE_curve_smooth_flag_set(struct Curve *cu, const bool use_smooth); ListBase *BKE_curve_nurbs_get(struct Curve *cu); diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index d389b557503..e620beb6ccc 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -29,8 +29,6 @@ extern "C" { #endif struct ChannelDriver; -struct DriverTarget; -struct DriverVar; struct FCM_EnvelopeData; struct FCurve; struct FModifier; @@ -56,67 +54,6 @@ typedef struct CfraElem { void bezt_add_to_cfra_elem(ListBase *lb, struct BezTriple *bezt); -/* ************** F-Curve Drivers ***************** */ - -/* With these iterators for convenience, the variables "tarIndex" and "dtar" can be - * accessed directly from the code using them, but it is not recommended that their - * values be changed to point at other slots... - */ - -/* convenience looper over ALL driver targets for a given variable (even the unused ones) */ -#define DRIVER_TARGETS_LOOPER_BEGIN(dvar) \ - { \ - DriverTarget *dtar = &dvar->targets[0]; \ - int tarIndex = 0; \ - for (; tarIndex < MAX_DRIVER_TARGETS; tarIndex++, dtar++) - -/* convenience looper over USED driver targets only */ -#define DRIVER_TARGETS_USED_LOOPER_BEGIN(dvar) \ - { \ - DriverTarget *dtar = &dvar->targets[0]; \ - int tarIndex = 0; \ - for (; tarIndex < dvar->num_targets; tarIndex++, dtar++) - -/* tidy up for driver targets loopers */ -#define DRIVER_TARGETS_LOOPER_END \ - } \ - ((void)0) - -/* ---------------------- */ - -void fcurve_free_driver(struct FCurve *fcu); -struct ChannelDriver *fcurve_copy_driver(const struct ChannelDriver *driver); - -void driver_variables_copy(struct ListBase *dst_vars, const struct ListBase *src_vars); - -void BKE_driver_target_matrix_to_rot_channels( - float mat[4][4], int auto_order, int rotation_mode, int channel, bool angles, float r_buf[4]); - -void driver_free_variable(struct ListBase *variables, struct DriverVar *dvar); -void driver_free_variable_ex(struct ChannelDriver *driver, struct DriverVar *dvar); - -void driver_change_variable_type(struct DriverVar *dvar, int type); -void driver_variable_name_validate(struct DriverVar *dvar); -struct DriverVar *driver_add_new_variable(struct ChannelDriver *driver); - -float driver_get_variable_value(struct ChannelDriver *driver, struct DriverVar *dvar); -bool driver_get_variable_property(struct ChannelDriver *driver, - struct DriverTarget *dtar, - struct PointerRNA *r_ptr, - struct PropertyRNA **r_prop, - int *r_index); - -bool BKE_driver_has_simple_expression(struct ChannelDriver *driver); -bool BKE_driver_expression_depends_on_time(struct ChannelDriver *driver); -void BKE_driver_invalidate_expression(struct ChannelDriver *driver, - bool expr_changed, - bool varname_changed); - -float evaluate_driver(struct PathResolvedRNA *anim_rna, - struct ChannelDriver *driver, - struct ChannelDriver *driver_orig, - const float evaltime); - /* ************** F-Curve Modifiers *************** */ /* F-Curve Modifier Type-Info (fmi): diff --git a/source/blender/blenkernel/BKE_fcurve_driver.h b/source/blender/blenkernel/BKE_fcurve_driver.h new file mode 100644 index 00000000000..ee531abfb72 --- /dev/null +++ b/source/blender/blenkernel/BKE_fcurve_driver.h @@ -0,0 +1,106 @@ +/* + * 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. + */ + +#ifndef __BKE_FCURVE_DRIVER_H__ +#define __BKE_FCURVE_DRIVER_H__ + +/** \file + * \ingroup bke + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct ChannelDriver; +struct DriverTarget; +struct DriverVar; +struct FCurve; +struct PathResolvedRNA; +struct PointerRNA; +struct PropertyRNA; + +#include "DNA_curve_types.h" + +/* ************** F-Curve Drivers ***************** */ + +/* With these iterators for convenience, the variables "tarIndex" and "dtar" can be + * accessed directly from the code using them, but it is not recommended that their + * values be changed to point at other slots... + */ + +/* convenience looper over ALL driver targets for a given variable (even the unused ones) */ +#define DRIVER_TARGETS_LOOPER_BEGIN(dvar) \ + { \ + DriverTarget *dtar = &dvar->targets[0]; \ + int tarIndex = 0; \ + for (; tarIndex < MAX_DRIVER_TARGETS; tarIndex++, dtar++) + +/* convenience looper over USED driver targets only */ +#define DRIVER_TARGETS_USED_LOOPER_BEGIN(dvar) \ + { \ + DriverTarget *dtar = &dvar->targets[0]; \ + int tarIndex = 0; \ + for (; tarIndex < dvar->num_targets; tarIndex++, dtar++) + +/* tidy up for driver targets loopers */ +#define DRIVER_TARGETS_LOOPER_END \ + } \ + ((void)0) + +/* ---------------------- */ + +void fcurve_free_driver(struct FCurve *fcu); +struct ChannelDriver *fcurve_copy_driver(const struct ChannelDriver *driver); + +void driver_variables_copy(struct ListBase *dst_vars, const struct ListBase *src_vars); + +void BKE_driver_target_matrix_to_rot_channels( + float mat[4][4], int auto_order, int rotation_mode, int channel, bool angles, float r_buf[4]); + +void driver_free_variable(struct ListBase *variables, struct DriverVar *dvar); +void driver_free_variable_ex(struct ChannelDriver *driver, struct DriverVar *dvar); + +void driver_change_variable_type(struct DriverVar *dvar, int type); +void driver_variable_name_validate(struct DriverVar *dvar); +struct DriverVar *driver_add_new_variable(struct ChannelDriver *driver); + +float driver_get_variable_value(struct ChannelDriver *driver, struct DriverVar *dvar); +bool driver_get_variable_property(struct ChannelDriver *driver, + struct DriverTarget *dtar, + struct PointerRNA *r_ptr, + struct PropertyRNA **r_prop, + int *r_index); + +bool BKE_driver_has_simple_expression(struct ChannelDriver *driver); +bool BKE_driver_expression_depends_on_time(struct ChannelDriver *driver); +void BKE_driver_invalidate_expression(struct ChannelDriver *driver, + bool expr_changed, + bool varname_changed); + +float evaluate_driver(struct PathResolvedRNA *anim_rna, + struct ChannelDriver *driver, + struct ChannelDriver *driver_orig, + const float evaltime); + +#ifdef __cplusplus +} +#endif + +#endif /* __BKE_FCURVE_DRIVER_H__*/ diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h index 2b02895043f..1272127daa0 100644 --- a/source/blender/blenkernel/BKE_idprop.h +++ b/source/blender/blenkernel/BKE_idprop.h @@ -179,6 +179,17 @@ void IDP_Reset(IDProperty *prop, const IDProperty *reference); # define IDP_Id(prop) ((ID *)(prop)->data.pointer) #endif +/** + * Call a callback for each idproperty in the hierarchy under given root one (included). + * + */ +typedef void (*IDPForeachPropertyCallback)(IDProperty *id_property, void *user_data); + +void IDP_foreach_property(struct IDProperty *id_property_root, + const int type_filter, + IDPForeachPropertyCallback callback, + void *user_data); + /* Format IDProperty as strings */ char *IDP_reprN(const struct IDProperty *prop, uint *r_len); void IDP_repr_fn(const IDProperty *prop, diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 0d8b6efb4b1..37082947687 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -318,7 +318,7 @@ bool BKE_image_fill_tile(struct Image *ima, bool is_float); struct ImageTile *BKE_image_get_tile(struct Image *ima, int tile_number); -struct ImageTile *BKE_image_get_tile_from_iuser(struct Image *ima, struct ImageUser *iuser); +struct ImageTile *BKE_image_get_tile_from_iuser(struct Image *ima, const struct ImageUser *iuser); int BKE_image_get_tile_from_pos(struct Image *ima, const float uv[2], diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h index fe5b8cff31c..927aad287a6 100644 --- a/source/blender/blenkernel/BKE_multires.h +++ b/source/blender/blenkernel/BKE_multires.h @@ -110,6 +110,11 @@ void multiresModifier_del_levels(struct MultiresModifierData *mmd, void multiresModifier_base_apply(struct Depsgraph *depsgraph, struct Object *object, struct MultiresModifierData *mmd); +int multiresModifier_rebuild_subdiv(struct Depsgraph *depsgraph, + struct Object *object, + struct MultiresModifierData *mmd, + int rebuild_limit, + bool switch_view_to_lower_level); void multiresModifier_subdivide_legacy(struct MultiresModifierData *mmd, struct Scene *scene, struct Object *ob, @@ -175,13 +180,25 @@ bool multiresModifier_reshapeFromCCG(const int tot_level, struct SubdivCCG *subdiv_ccg); /* Subdivide multires displacement once. */ -void multiresModifier_subdivide(struct Object *object, struct MultiresModifierData *mmd); + +typedef enum eMultiresSubdivideModeType { + MULTIRES_SUBDIVIDE_CATMULL_CLARK, + MULTIRES_SUBDIVIDE_SIMPLE, + MULTIRES_SUBDIVIDE_LINEAR, +} eMultiresSubdivideModeType; + +void multiresModifier_subdivide(struct Object *object, + struct MultiresModifierData *mmd, + const eMultiresSubdivideModeType mode); +void multires_subdivide_create_tangent_displacement_linear_grids(struct Object *object, + struct MultiresModifierData *mmd); /* Subdivide displacement to the given level. * If level is lower than the current top level nothing happens. */ void multiresModifier_subdivide_to_level(struct Object *object, struct MultiresModifierData *mmd, - const int top_level); + const int top_level, + const eMultiresSubdivideModeType mode); /* Subdivision integration, defined in multires_subdiv.c */ @@ -213,8 +230,6 @@ BLI_INLINE void BKE_multires_construct_tangent_matrix(float tangent_matrix[3][3] const float dPdv[3], const int corner); -int BKE_multires_sculpt_level_get(const struct MultiresModifierData *mmd); - #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 10175a2c45f..7c77d57bc69 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1276,7 +1276,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, float dyt[3], int osatex, const short thread, - struct Tex *tex, + const struct Tex *tex, short which_output, int cfra, int preview, diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 94f0e544a6b..35dd241c173 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -48,6 +48,7 @@ struct Mesh; struct PBVH; struct PBVHNode; struct SubdivCCG; +struct TaskParallelSettings; struct TaskParallelTLS; typedef struct PBVH PBVH; @@ -456,30 +457,10 @@ bool pbvh_has_face_sets(PBVH *bvh); void pbvh_show_face_sets_set(PBVH *bvh, bool show_face_sets); /* Parallelization */ -typedef void (*PBVHParallelRangeFunc)(void *__restrict userdata, - const int iter, - const struct TaskParallelTLS *__restrict tls); -typedef void (*PBVHParallelReduceFunc)(const void *__restrict userdata, - void *__restrict chunk_join, - void *__restrict chunk); - -typedef struct PBVHParallelSettings { - bool use_threading; - void *userdata_chunk; - size_t userdata_chunk_size; - PBVHParallelReduceFunc func_reduce; -} PBVHParallelSettings; - -void BKE_pbvh_parallel_range_settings(struct PBVHParallelSettings *settings, +void BKE_pbvh_parallel_range_settings(struct TaskParallelSettings *settings, bool use_threading, int totnode); -void BKE_pbvh_parallel_range(const int start, - const int stop, - void *userdata, - PBVHParallelRangeFunc func, - const struct PBVHParallelSettings *settings); - struct MVert *BKE_pbvh_get_verts(const PBVH *bvh); #ifdef __cplusplus diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index 939fcb33b2d..8b7be11e852 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -218,6 +218,15 @@ struct ImBuf *BKE_sequencer_give_ibuf_seqbase(const SeqRenderData *context, float cfra, int chan_shown, struct ListBase *seqbasep); +struct ImBuf *BKE_sequencer_effect_execute_threaded(struct SeqEffectHandle *sh, + const SeqRenderData *context, + struct Sequence *seq, + float cfra, + float facf0, + float facf1, + struct ImBuf *ibuf1, + struct ImBuf *ibuf2, + struct ImBuf *ibuf3); /* ********************************************************************** * sequencer.c @@ -374,6 +383,10 @@ struct Sequence *BKE_sequencer_prefetch_get_original_sequence(struct Sequence *s /* intern */ struct SeqEffectHandle BKE_sequence_get_blend(struct Sequence *seq); void BKE_sequence_effect_speed_rebuild_map(struct Scene *scene, struct Sequence *seq, bool force); +float BKE_sequencer_speed_effect_target_frame_get(const SeqRenderData *context, + struct Sequence *seq, + float cfra, + int input); /* extern */ struct SeqEffectHandle BKE_sequence_get_effect(struct Sequence *seq); @@ -507,6 +520,7 @@ void BKE_sequence_alpha_mode_from_extension(struct Sequence *seq); void BKE_sequence_init_colorspace(struct Sequence *seq); float BKE_sequence_get_fps(struct Scene *scene, struct Sequence *seq); +float BKE_sequencer_give_stripelem_index(struct Sequence *seq, float cfra); /* RNA enums, just to be more readable */ enum { diff --git a/source/blender/blenkernel/BKE_sound.h b/source/blender/blenkernel/BKE_sound.h index 685582bcecf..b93591b7b60 100644 --- a/source/blender/blenkernel/BKE_sound.h +++ b/source/blender/blenkernel/BKE_sound.h @@ -164,7 +164,7 @@ void BKE_sound_stop_scene(struct Scene *scene); void BKE_sound_seek_scene(struct Main *bmain, struct Scene *scene); -float BKE_sound_sync_scene(struct Scene *scene); +double BKE_sound_sync_scene(struct Scene *scene); int BKE_sound_scene_playing(struct Scene *scene); @@ -180,10 +180,10 @@ float BKE_sound_get_length(struct Main *bmain, struct bSound *sound); char **BKE_sound_get_device_names(void); -typedef void (*SoundJackSyncCallback)(struct Main *bmain, int mode, float time); +typedef void (*SoundJackSyncCallback)(struct Main *bmain, int mode, double time); void BKE_sound_jack_sync_callback_set(SoundJackSyncCallback callback); -void BKE_sound_jack_scene_update(struct Scene *scene, int mode, float time); +void BKE_sound_jack_scene_update(struct Scene *scene, int mode, double time); /* Dependency graph evaluation. */ diff --git a/source/blender/blenkernel/BKE_subdiv_ccg.h b/source/blender/blenkernel/BKE_subdiv_ccg.h index 7c7638e65d2..8d2565c31f7 100644 --- a/source/blender/blenkernel/BKE_subdiv_ccg.h +++ b/source/blender/blenkernel/BKE_subdiv_ccg.h @@ -40,7 +40,7 @@ struct DMFlagMat; struct Mesh; struct Subdiv; -/* ============================================================================= +/* -------------------------------------------------------------------- * Masks. */ @@ -61,7 +61,7 @@ typedef struct SubdivCCGMaskEvaluator { bool BKE_subdiv_ccg_mask_init_from_paint(SubdivCCGMaskEvaluator *mask_evaluator, const struct Mesh *mesh); -/* ============================================================================= +/* -------------------------------------------------------------------- * Materials. */ @@ -80,7 +80,7 @@ typedef struct SubdivCCGMaterialFlagsEvaluator { void BKE_subdiv_ccg_material_flags_init_from_mesh( SubdivCCGMaterialFlagsEvaluator *material_flags_evaluator, const struct Mesh *mesh); -/* ============================================================================= +/* -------------------------------------------------------------------- * SubdivCCG. */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index bdf8778651d..8e73afea3ac 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -116,6 +116,7 @@ set(SRC intern/editmesh_tangent.c intern/effect.c intern/fcurve.c + intern/fcurve_driver.c intern/fluid.c intern/fmodifier.c intern/font.c @@ -175,9 +176,11 @@ set(SRC intern/multires_reshape_apply_base.c intern/multires_reshape_ccg.c intern/multires_reshape_smooth.c + intern/multires_reshape_subdivide.c intern/multires_reshape_util.c intern/multires_reshape_vertcos.c intern/multires_subdiv.c + intern/multires_unsubdivide.c intern/nla.c intern/node.c intern/object.c @@ -197,7 +200,6 @@ set(SRC intern/particle_system.c intern/pbvh.c intern/pbvh_bmesh.c - intern/pbvh_parallel.cc intern/pointcache.c intern/pointcloud.c intern/report.c @@ -300,6 +302,7 @@ set(SRC BKE_editmesh_tangent.h BKE_effect.h BKE_fcurve.h + BKE_fcurve_driver.h BKE_fluid.h BKE_font.h BKE_freestyle.h @@ -398,6 +401,7 @@ set(SRC intern/lib_intern.h intern/multires_inline.h intern/multires_reshape.h + intern/multires_unsubdivide.h intern/pbvh_intern.h intern/subdiv_converter.h intern/subdiv_inline.h diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index f67e49aef06..b8226c57f80 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -901,7 +901,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* Sculpt can skip certain modifiers. */ MultiresModifierData *mmd = get_multires_modifier(scene, ob, 0); - const bool has_multires = (mmd && BKE_multires_sculpt_level_get(mmd) != 0); + const bool has_multires = (mmd && mmd->sculptlvl != 0); bool multires_applied = false; const bool sculpt_mode = ob->mode & OB_MODE_SCULPT && ob->sculpt && !use_render; const bool sculpt_dyntopo = (sculpt_mode && ob->sculpt->bm) && !use_render; @@ -1020,8 +1020,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, if (sculpt_mode && (!has_multires || multires_applied || sculpt_dyntopo)) { bool unsupported = false; - if (md->type == eModifierType_Multires && - BKE_multires_sculpt_level_get((MultiresModifierData *)md) == 0) { + if (md->type == eModifierType_Multires && ((MultiresModifierData *)md)->sculptlvl == 0) { /* If multires is on level 0 skip it silently without warning message. */ if (!sculpt_dyntopo) { continue; diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index f77e2ea1e0d..02b7763a9b4 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -29,6 +29,7 @@ #include "BKE_animsys.h" #include "BKE_context.h" #include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_global.h" #include "BKE_lib_id.h" #include "BKE_main.h" diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 2cc1d869e4c..f0ea820a1ac 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -49,7 +49,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" -#include "RE_render_ext.h" /* externtex */ +#include "RE_render_ext.h" /* RE_texture_evaluate */ static void brush_init_data(ID *id) { @@ -344,7 +344,8 @@ typedef enum eGPCurveMappingPreset { GPCURVE_PRESET_INK = 1, GPCURVE_PRESET_INKNOISE = 2, GPCURVE_PRESET_MARKER = 3, - GPCURVE_PRESET_CHISEL = 4, + GPCURVE_PRESET_CHISEL_SENSIVITY = 4, + GPCURVE_PRESET_CHISEL_STRENGTH = 5, } eGPCurveMappingPreset; static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, int preset) @@ -391,11 +392,25 @@ static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, int preset) cuma->curve[3].x = 1.0f; cuma->curve[3].y = 1.0f; break; - case GPCURVE_PRESET_CHISEL: + case GPCURVE_PRESET_CHISEL_SENSIVITY: cuma->curve[0].x = 0.0f; cuma->curve[0].y = 0.0f; - cuma->curve[1].x = 0.8f; - cuma->curve[1].y = 1.0f; + cuma->curve[1].x = 0.25f; + cuma->curve[1].y = 0.40f; + cuma->curve[2].x = 1.0f; + cuma->curve[2].y = 1.0f; + break; + case GPCURVE_PRESET_CHISEL_STRENGTH: + cuma->curve[0].x = 0.0f; + cuma->curve[0].y = 0.0f; + cuma->curve[1].x = 0.31f; + cuma->curve[1].y = 0.22f; + cuma->curve[2].x = 0.61f; + cuma->curve[2].y = 0.88f; + cuma->curve[3].x = 1.0f; + cuma->curve[3].y = 1.0f; + break; + default: break; } @@ -587,9 +602,9 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) brush->gpencil_settings->draw_strength = 1.0f; brush->gpencil_settings->input_samples = 10; - brush->gpencil_settings->active_smooth = ACTIVE_SMOOTH; - brush->gpencil_settings->draw_angle = DEG2RAD(20.0f); - brush->gpencil_settings->draw_angle_factor = 1.0f; + brush->gpencil_settings->active_smooth = 0.3f; + brush->gpencil_settings->draw_angle = DEG2RAD(35.0f); + brush->gpencil_settings->draw_angle_factor = 0.5f; brush->gpencil_settings->hardeness = 1.0f; copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.0f); @@ -608,7 +623,12 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type) custom_curve = brush->gpencil_settings->curve_sensitivity; BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); BKE_curvemapping_initialize(custom_curve); - brush_gpencil_curvemap_reset(custom_curve->cm, 2, GPCURVE_PRESET_CHISEL); + brush_gpencil_curvemap_reset(custom_curve->cm, 3, GPCURVE_PRESET_CHISEL_SENSIVITY); + + custom_curve = brush->gpencil_settings->curve_strength; + BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f); + BKE_curvemapping_initialize(custom_curve); + brush_gpencil_curvemap_reset(custom_curve->cm, 4, GPCURVE_PRESET_CHISEL_STRENGTH); brush->gpencil_settings->icon_id = GP_BRUSH_ICON_CHISEL; brush->gpencil_tool = GPAINT_TOOL_DRAW; @@ -1552,8 +1572,7 @@ float BKE_brush_sample_tex_3d(const Scene *scene, else if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) { /* Get strength by feeding the vertex * location directly into a texture */ - hasrgb = externtex( - mtex, point, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false); + hasrgb = RE_texture_evaluate(mtex, point, thread, pool, false, false, &intensity, rgba); } else if (mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) { float rotation = -mtex->rot; @@ -1583,8 +1602,7 @@ float BKE_brush_sample_tex_3d(const Scene *scene, co[1] = y; co[2] = 0.0f; - hasrgb = externtex( - mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false); + hasrgb = RE_texture_evaluate(mtex, co, thread, pool, false, false, &intensity, rgba); } else { float rotation = -mtex->rot; @@ -1640,8 +1658,7 @@ float BKE_brush_sample_tex_3d(const Scene *scene, co[1] = y; co[2] = 0.0f; - hasrgb = externtex( - mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false); + hasrgb = RE_texture_evaluate(mtex, co, thread, pool, false, false, &intensity, rgba); } intensity += br->texture_sample_bias; @@ -1698,8 +1715,7 @@ float BKE_brush_sample_masktex( co[1] = y; co[2] = 0.0f; - externtex( - mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false); + RE_texture_evaluate(mtex, co, thread, pool, false, false, &intensity, rgba); } else { float rotation = -mtex->rot; @@ -1755,8 +1771,7 @@ float BKE_brush_sample_masktex( co[1] = y; co[2] = 0.0f; - externtex( - mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false); + RE_texture_evaluate(mtex, co, thread, pool, false, false, &intensity, rgba); } CLAMP(intensity, 0.0f, 1.0f); @@ -2060,7 +2075,7 @@ unsigned int *BKE_brush_gen_texture_cache(Brush *br, int half_side, bool use_sec unsigned int *texcache = NULL; MTex *mtex = (use_secondary) ? &br->mask_mtex : &br->mtex; float intensity; - float rgba[4]; + float rgba_dummy[4]; int ix, iy; int side = half_side * 2; @@ -2078,11 +2093,8 @@ unsigned int *BKE_brush_gen_texture_cache(Brush *br, int half_side, bool use_sec /* This is copied from displace modifier code */ /* TODO(sergey): brush are always caching with CM enabled for now. */ - externtex(mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL, false, false); - - ((char *)texcache)[(iy * side + ix) * 4] = ((char *)texcache)[(iy * side + ix) * 4 + 1] = - ((char *)texcache)[(iy * side + ix) * 4 + 2] = (( - char *)texcache)[(iy * side + ix) * 4 + 3] = (char)(intensity * 255.0f); + RE_texture_evaluate(mtex, co, 0, NULL, false, false, &intensity, rgba_dummy); + copy_v4_uchar((uchar *)&texcache[iy * side + ix], (char)(intensity * 255.0f)); } } } diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index d7bc7b41ca5..3c68753cbf9 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -62,7 +62,7 @@ #include "BKE_deform.h" #include "BKE_displist.h" #include "BKE_editmesh.h" -#include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_global.h" #include "BKE_idprop.h" #include "BKE_lib_id.h" diff --git a/source/blender/blenkernel/intern/crazyspace.c b/source/blender/blenkernel/intern/crazyspace.c index 7ec1da8eab4..3c47615b071 100644 --- a/source/blender/blenkernel/intern/crazyspace.c +++ b/source/blender/blenkernel/intern/crazyspace.c @@ -386,7 +386,7 @@ int BKE_sculpt_get_first_deform_matrices(struct Depsgraph *depsgraph, crazyspace_init_object_for_eval(depsgraph, object, &object_eval); MultiresModifierData *mmd = get_multires_modifier(scene, &object_eval, 0); const bool is_sculpt_mode = (object->mode & OB_MODE_SCULPT) != 0; - const bool has_multires = mmd != NULL && BKE_multires_sculpt_level_get(mmd) > 0; + const bool has_multires = mmd != NULL && mmd->sculptlvl > 0; const ModifierEvalContext mectx = {depsgraph, &object_eval, 0}; if (is_sculpt_mode && has_multires) { diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.c index a18579f9402..d35dc32738b 100644 --- a/source/blender/blenkernel/intern/displist.c +++ b/source/blender/blenkernel/intern/displist.c @@ -848,7 +848,8 @@ static ModifierData *curve_get_tessellate_point(Scene *scene, return pretessellatePoint; } -static void curve_calc_modifiers_pre( +/* Return true if any modifier was applied. */ +static bool curve_calc_modifiers_pre( Depsgraph *depsgraph, Scene *scene, Object *ob, ListBase *nurb, const bool for_render) { VirtualModifierData virtualModifierData; @@ -861,6 +862,7 @@ static void curve_calc_modifiers_pre( float(*deformedVerts)[3] = NULL; float *keyVerts = NULL; int required_mode; + bool modified = false; modifiers_clearErrors(ob); @@ -913,6 +915,7 @@ static void curve_calc_modifiers_pre( } mti->deformVerts(md, &mectx, NULL, deformedVerts, numVerts); + modified = true; if (md == pretessellatePoint) { break; @@ -931,6 +934,7 @@ static void curve_calc_modifiers_pre( if (keyVerts) { MEM_freeN(keyVerts); } + return modified; } static float (*displist_vert_coords_alloc(ListBase *dispbase, int *r_vert_len))[3] @@ -974,7 +978,8 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, ListBase *nurb, ListBase *dispbase, Mesh **r_final, - const bool for_render) + const bool for_render, + const bool force_mesh_conversion) { VirtualModifierData virtualModifierData; ModifierData *md = modifiers_getVirtualModifierList(ob, &virtualModifierData); @@ -1128,6 +1133,22 @@ static void curve_calc_modifiers_post(Depsgraph *depsgraph, } if (r_final) { + if (force_mesh_conversion && !modified) { + /* XXX 2.8 : This is a workaround for by some deeper technical depts: + * - DRW Batch cache is stored inside the ob->data. + * - Curve data is not COWed for instances that use different modifiers. + * This can causes the modifiers to be applied on all user of the same datablock (see T71055) + * + * The easy workaround is to force to generate a Mesh that will be used for display data + * since a Mesh output is already used for generative modifiers. + * However it does not fix problems with actual edit data still being shared. + * + * The right solution would be to COW the Curve data block at the input of the modifer stack + * just like what the mesh modifier does. + * */ + modified = BKE_mesh_new_nomain_from_curve_displist(ob, dispbase); + } + if (modified) { /* XXX2.8(Sybren): make sure the face normals are recalculated as well */ @@ -1202,6 +1223,7 @@ void BKE_displist_make_surf(Depsgraph *depsgraph, DispList *dl; float *data; int len; + bool force_mesh_conversion = false; if (!for_render && cu->editnurb) { BKE_nurbList_duplicate(&nubase, BKE_curve_editNurbs_get(cu)); @@ -1211,7 +1233,7 @@ void BKE_displist_make_surf(Depsgraph *depsgraph, } if (!for_orco) { - curve_calc_modifiers_pre(depsgraph, scene, ob, &nubase, for_render); + force_mesh_conversion = curve_calc_modifiers_pre(depsgraph, scene, ob, &nubase, for_render); } for (nu = nubase.first; nu; nu = nu->next) { @@ -1289,7 +1311,8 @@ void BKE_displist_make_surf(Depsgraph *depsgraph, if (!for_orco) { BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase); - curve_calc_modifiers_post(depsgraph, scene, ob, &nubase, dispbase, r_final, for_render); + curve_calc_modifiers_post( + depsgraph, scene, ob, &nubase, dispbase, r_final, for_render, force_mesh_conversion); } BKE_nurbList_free(&nubase); @@ -1537,6 +1560,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, else if (ELEM(ob->type, OB_CURVE, OB_FONT)) { ListBase dlbev; ListBase nubase = {NULL, NULL}; + bool force_mesh_conversion = false; BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev); @@ -1559,7 +1583,7 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, } if (!for_orco) { - curve_calc_modifiers_pre(depsgraph, scene, ob, &nubase, for_render); + force_mesh_conversion = curve_calc_modifiers_pre(depsgraph, scene, ob, &nubase, for_render); } BKE_curve_bevelList_make(ob, &nubase, for_render); @@ -1769,7 +1793,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph, if (!for_orco) { BKE_nurbList_duplicate(&ob->runtime.curve_cache->deformed_nurbs, &nubase); - curve_calc_modifiers_post(depsgraph, scene, ob, &nubase, dispbase, r_final, for_render); + curve_calc_modifiers_post( + depsgraph, scene, ob, &nubase, dispbase, r_final, for_render, force_mesh_conversion); } if (cu->flag & CU_DEFORM_FILL && !ob->runtime.data_eval) { diff --git a/source/blender/blenkernel/intern/editmesh_tangent.c b/source/blender/blenkernel/intern/editmesh_tangent.c index c3ae2a54e13..6fcaf84d4ca 100644 --- a/source/blender/blenkernel/intern/editmesh_tangent.c +++ b/source/blender/blenkernel/intern/editmesh_tangent.c @@ -251,9 +251,7 @@ finally: pRes[3] = fSign; } -static void emDM_calc_loop_tangents_thread(TaskPool *__restrict UNUSED(pool), - void *taskdata, - int UNUSED(threadid)) +static void emDM_calc_loop_tangents_thread(TaskPool *__restrict UNUSED(pool), void *taskdata) { struct SGLSLEditMeshToTangent *mesh2tangent = taskdata; /* new computation method */ @@ -362,9 +360,8 @@ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em, #endif /* Calculation */ if (em->tottri != 0) { - TaskScheduler *scheduler = BLI_task_scheduler_get(); TaskPool *task_pool; - task_pool = BLI_task_pool_create(scheduler, NULL, TASK_PRIORITY_LOW); + task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW); tangent_mask_curr = 0; /* Calculate tangent layers */ diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 439992a4113..3f55831cfeb 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -30,49 +30,28 @@ #include "MEM_guardedalloc.h" #include "DNA_anim_types.h" -#include "DNA_constraint_types.h" #include "DNA_object_types.h" -#include "BLI_alloca.h" #include "BLI_blenlib.h" #include "BLI_easing.h" -#include "BLI_expr_pylike_eval.h" #include "BLI_math.h" -#include "BLI_string_utils.h" -#include "BLI_threads.h" -#include "BLI_utildefines.h" -#include "BLT_translation.h" - -#include "BKE_action.h" #include "BKE_anim_data.h" #include "BKE_animsys.h" -#include "BKE_armature.h" -#include "BKE_constraint.h" #include "BKE_context.h" #include "BKE_curve.h" #include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_global.h" #include "BKE_nla.h" -#include "BKE_object.h" #include "RNA_access.h" -#include "atomic_ops.h" - #include "CLG_log.h" -#ifdef WITH_PYTHON -# include "BPY_extern.h" -#endif - #define SMALL -1.0e-10 #define SELECT 1 -#ifdef WITH_PYTHON -static ThreadMutex python_driver_lock = BLI_MUTEX_INITIALIZER; -#endif - static CLG_LogRef LOG = {"bke.fcurve"}; /* ************************** Data-Level Functions ************************* */ @@ -1256,1236 +1235,6 @@ short test_time_fcurve(FCurve *fcu) return 0; } -/* ***************************** Drivers ********************************* */ - -/* Driver Variables --------------------------- */ - -/* TypeInfo for Driver Variables (dvti) */ -typedef struct DriverVarTypeInfo { - /* evaluation callback */ - float (*get_value)(ChannelDriver *driver, DriverVar *dvar); - - /* allocation of target slots */ - int num_targets; /* number of target slots required */ - const char *target_names[MAX_DRIVER_TARGETS]; /* UI names that should be given to the slots */ - short target_flags[MAX_DRIVER_TARGETS]; /* flags defining the requirements for each slot */ -} DriverVarTypeInfo; - -/* Macro to begin definitions */ -#define BEGIN_DVAR_TYPEDEF(type) { - -/* Macro to end definitions */ -#define END_DVAR_TYPEDEF } - -/* ......... */ - -static ID *dtar_id_ensure_proxy_from(ID *id) -{ - if (id && GS(id->name) == ID_OB && ((Object *)id)->proxy_from) { - return (ID *)(((Object *)id)->proxy_from); - } - return id; -} - -/** - * Helper function to obtain a value using RNA from the specified source - * (for evaluating drivers). - */ -static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) -{ - PointerRNA id_ptr, ptr; - PropertyRNA *prop; - ID *id; - int index = -1; - float value = 0.0f; - - /* sanity check */ - if (ELEM(NULL, driver, dtar)) { - return 0.0f; - } - - id = dtar_id_ensure_proxy_from(dtar->id); - - /* error check for missing pointer... */ - if (id == NULL) { - if (G.debug & G_DEBUG) { - CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path); - } - - driver->flag |= DRIVER_FLAG_INVALID; - dtar->flag |= DTAR_FLAG_INVALID; - return 0.0f; - } - - /* get RNA-pointer for the ID-block given in target */ - RNA_id_pointer_create(id, &id_ptr); - - /* get property to read from, and get value as appropriate */ - if (!RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) { - /* path couldn't be resolved */ - if (G.debug & G_DEBUG) { - CLOG_ERROR(&LOG, - "Driver Evaluation Error: cannot resolve target for %s -> %s", - id->name, - dtar->rna_path); - } - - driver->flag |= DRIVER_FLAG_INVALID; - dtar->flag |= DTAR_FLAG_INVALID; - return 0.0f; - } - - if (RNA_property_array_check(prop)) { - /* array */ - if (index < 0 || index >= RNA_property_array_length(&ptr, prop)) { - /* out of bounds */ - if (G.debug & G_DEBUG) { - CLOG_ERROR(&LOG, - "Driver Evaluation Error: array index is out of bounds for %s -> %s (%d)", - id->name, - dtar->rna_path, - index); - } - - driver->flag |= DRIVER_FLAG_INVALID; - dtar->flag |= DTAR_FLAG_INVALID; - return 0.0f; - } - - switch (RNA_property_type(prop)) { - case PROP_BOOLEAN: - value = (float)RNA_property_boolean_get_index(&ptr, prop, index); - break; - case PROP_INT: - value = (float)RNA_property_int_get_index(&ptr, prop, index); - break; - case PROP_FLOAT: - value = RNA_property_float_get_index(&ptr, prop, index); - break; - default: - break; - } - } - else { - /* not an array */ - switch (RNA_property_type(prop)) { - case PROP_BOOLEAN: - value = (float)RNA_property_boolean_get(&ptr, prop); - break; - case PROP_INT: - value = (float)RNA_property_int_get(&ptr, prop); - break; - case PROP_FLOAT: - value = RNA_property_float_get(&ptr, prop); - break; - case PROP_ENUM: - value = (float)RNA_property_enum_get(&ptr, prop); - break; - default: - break; - } - } - - /* if we're still here, we should be ok... */ - dtar->flag &= ~DTAR_FLAG_INVALID; - return value; -} - -/** - * Same as 'dtar_get_prop_val'. but get the RNA property. - */ -bool driver_get_variable_property(ChannelDriver *driver, - DriverTarget *dtar, - PointerRNA *r_ptr, - PropertyRNA **r_prop, - int *r_index) -{ - PointerRNA id_ptr; - PointerRNA ptr; - PropertyRNA *prop; - ID *id; - int index = -1; - - /* sanity check */ - if (ELEM(NULL, driver, dtar)) { - return false; - } - - id = dtar_id_ensure_proxy_from(dtar->id); - - /* error check for missing pointer... */ - if (id == NULL) { - if (G.debug & G_DEBUG) { - CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path); - } - - driver->flag |= DRIVER_FLAG_INVALID; - dtar->flag |= DTAR_FLAG_INVALID; - return false; - } - - /* get RNA-pointer for the ID-block given in target */ - RNA_id_pointer_create(id, &id_ptr); - - /* get property to read from, and get value as appropriate */ - if (dtar->rna_path == NULL || dtar->rna_path[0] == '\0') { - ptr = PointerRNA_NULL; - prop = NULL; /* ok */ - } - else if (RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) { - /* ok */ - } - else { - /* path couldn't be resolved */ - if (G.debug & G_DEBUG) { - CLOG_ERROR(&LOG, - "Driver Evaluation Error: cannot resolve target for %s -> %s", - id->name, - dtar->rna_path); - } - - ptr = PointerRNA_NULL; - *r_prop = NULL; - *r_index = -1; - - driver->flag |= DRIVER_FLAG_INVALID; - dtar->flag |= DTAR_FLAG_INVALID; - return false; - } - - *r_ptr = ptr; - *r_prop = prop; - *r_index = index; - - /* if we're still here, we should be ok... */ - dtar->flag &= ~DTAR_FLAG_INVALID; - return true; -} - -static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar) -{ - short valid_targets = 0; - - DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { - Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); - - /* check if this target has valid data */ - if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { - /* invalid target, so will not have enough targets */ - driver->flag |= DRIVER_FLAG_INVALID; - dtar->flag |= DTAR_FLAG_INVALID; - } - else { - /* target seems to be OK now... */ - dtar->flag &= ~DTAR_FLAG_INVALID; - valid_targets++; - } - } - DRIVER_TARGETS_LOOPER_END; - - return valid_targets; -} - -/* ......... */ - -/* evaluate 'single prop' driver variable */ -static float dvar_eval_singleProp(ChannelDriver *driver, DriverVar *dvar) -{ - /* just evaluate the first target slot */ - return dtar_get_prop_val(driver, &dvar->targets[0]); -} - -/* evaluate 'rotation difference' driver variable */ -static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar) -{ - short valid_targets = driver_check_valid_targets(driver, dvar); - - /* make sure we have enough valid targets to use - all or nothing for now... */ - if (driver_check_valid_targets(driver, dvar) != 2) { - if (G.debug & G_DEBUG) { - CLOG_WARN(&LOG, - "RotDiff DVar: not enough valid targets (n = %d) (a = %p, b = %p)", - valid_targets, - dvar->targets[0].id, - dvar->targets[1].id); - } - return 0.0f; - } - - float(*mat[2])[4]; - - /* NOTE: for now, these are all just worldspace */ - for (int i = 0; i < 2; i++) { - /* get pointer to loc values to store in */ - DriverTarget *dtar = &dvar->targets[i]; - Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); - bPoseChannel *pchan; - - /* after the checks above, the targets should be valid here... */ - BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB)); - - /* try to get posechannel */ - pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); - - /* check if object or bone */ - if (pchan) { - /* bone */ - mat[i] = pchan->pose_mat; - } - else { - /* object */ - mat[i] = ob->obmat; - } - } - - float q1[4], q2[4], quat[4], angle; - - /* use the final posed locations */ - mat4_to_quat(q1, mat[0]); - mat4_to_quat(q2, mat[1]); - - invert_qt_normalized(q1); - mul_qt_qtqt(quat, q1, q2); - angle = 2.0f * (saacos(quat[0])); - angle = fabsf(angle); - - return (angle > (float)M_PI) ? (float)((2.0f * (float)M_PI) - angle) : (float)(angle); -} - -/* evaluate 'location difference' driver variable */ -/* TODO: this needs to take into account space conversions... */ -static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) -{ - float loc1[3] = {0.0f, 0.0f, 0.0f}; - float loc2[3] = {0.0f, 0.0f, 0.0f}; - short valid_targets = driver_check_valid_targets(driver, dvar); - - /* make sure we have enough valid targets to use - all or nothing for now... */ - if (valid_targets < dvar->num_targets) { - if (G.debug & G_DEBUG) { - CLOG_WARN(&LOG, - "LocDiff DVar: not enough valid targets (n = %d) (a = %p, b = %p)", - valid_targets, - dvar->targets[0].id, - dvar->targets[1].id); - } - return 0.0f; - } - - /* SECOND PASS: get two location values */ - /* NOTE: for now, these are all just worldspace */ - DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { - /* get pointer to loc values to store in */ - Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); - bPoseChannel *pchan; - float tmp_loc[3]; - - /* after the checks above, the targets should be valid here... */ - BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB)); - - /* try to get posechannel */ - pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); - - /* check if object or bone */ - if (pchan) { - /* bone */ - if (dtar->flag & DTAR_FLAG_LOCALSPACE) { - if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { - float mat[4][4]; - - /* extract transform just like how the constraints do it! */ - copy_m4_m4(mat, pchan->pose_mat); - BKE_constraint_mat_convertspace( - ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false); - - /* ... and from that, we get our transform */ - copy_v3_v3(tmp_loc, mat[3]); - } - else { - /* transform space (use transform values directly) */ - copy_v3_v3(tmp_loc, pchan->loc); - } - } - else { - /* convert to worldspace */ - copy_v3_v3(tmp_loc, pchan->pose_head); - mul_m4_v3(ob->obmat, tmp_loc); - } - } - else { - /* object */ - if (dtar->flag & DTAR_FLAG_LOCALSPACE) { - if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { - /* XXX: this should practically be the same as transform space... */ - float mat[4][4]; - - /* extract transform just like how the constraints do it! */ - copy_m4_m4(mat, ob->obmat); - BKE_constraint_mat_convertspace( - ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false); - - /* ... and from that, we get our transform */ - copy_v3_v3(tmp_loc, mat[3]); - } - else { - /* transform space (use transform values directly) */ - copy_v3_v3(tmp_loc, ob->loc); - } - } - else { - /* worldspace */ - copy_v3_v3(tmp_loc, ob->obmat[3]); - } - } - - /* copy the location to the right place */ - if (tarIndex) { - copy_v3_v3(loc2, tmp_loc); - } - else { - copy_v3_v3(loc1, tmp_loc); - } - } - DRIVER_TARGETS_LOOPER_END; - - /* if we're still here, there should now be two targets to use, - * so just take the length of the vector between these points - */ - return len_v3v3(loc1, loc2); -} - -/* evaluate 'transform channel' driver variable */ -static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) -{ - DriverTarget *dtar = &dvar->targets[0]; - Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); - bPoseChannel *pchan; - float mat[4][4]; - float oldEul[3] = {0.0f, 0.0f, 0.0f}; - bool use_eulers = false; - short rot_order = ROT_MODE_EUL; - - /* check if this target has valid data */ - if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { - /* invalid target, so will not have enough targets */ - driver->flag |= DRIVER_FLAG_INVALID; - dtar->flag |= DTAR_FLAG_INVALID; - return 0.0f; - } - else { - /* target should be valid now */ - dtar->flag &= ~DTAR_FLAG_INVALID; - } - - /* try to get posechannel */ - pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); - - /* check if object or bone, and get transform matrix accordingly - * - "useEulers" code is used to prevent the problems associated with non-uniqueness - * of euler decomposition from matrices [#20870] - * - localspace is for [#21384], where parent results are not wanted - * but local-consts is for all the common "corrective-shapes-for-limbs" situations - */ - if (pchan) { - /* bone */ - if (pchan->rotmode > 0) { - copy_v3_v3(oldEul, pchan->eul); - rot_order = pchan->rotmode; - use_eulers = true; - } - - if (dtar->flag & DTAR_FLAG_LOCALSPACE) { - if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { - /* just like how the constraints do it! */ - copy_m4_m4(mat, pchan->pose_mat); - BKE_constraint_mat_convertspace( - ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false); - } - else { - /* specially calculate local matrix, since chan_mat is not valid - * since it stores delta transform of pose_mat so that deforms work - * so it cannot be used here for "transform" space - */ - BKE_pchan_to_mat4(pchan, mat); - } - } - else { - /* worldspace matrix */ - mul_m4_m4m4(mat, ob->obmat, pchan->pose_mat); - } - } - else { - /* object */ - if (ob->rotmode > 0) { - copy_v3_v3(oldEul, ob->rot); - rot_order = ob->rotmode; - use_eulers = true; - } - - if (dtar->flag & DTAR_FLAG_LOCALSPACE) { - if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { - /* just like how the constraints do it! */ - copy_m4_m4(mat, ob->obmat); - BKE_constraint_mat_convertspace( - ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false); - } - else { - /* transforms to matrix */ - BKE_object_to_mat4(ob, mat); - } - } - else { - /* worldspace matrix - just the good-old one */ - copy_m4_m4(mat, ob->obmat); - } - } - - /* check which transform */ - if (dtar->transChan >= MAX_DTAR_TRANSCHAN_TYPES) { - /* not valid channel */ - return 0.0f; - } - else if (dtar->transChan == DTAR_TRANSCHAN_SCALE_AVG) { - /* Cubic root of the change in volume, equal to the geometric mean - * of scale over all three axes unless the matrix includes shear. */ - return cbrtf(mat4_to_volume_scale(mat)); - } - else if (ELEM(dtar->transChan, - DTAR_TRANSCHAN_SCALEX, - DTAR_TRANSCHAN_SCALEY, - DTAR_TRANSCHAN_SCALEZ)) { - /* Extract scale, and choose the right axis, - * inline 'mat4_to_size'. */ - return len_v3(mat[dtar->transChan - DTAR_TRANSCHAN_SCALEX]); - } - else if (dtar->transChan >= DTAR_TRANSCHAN_ROTX) { - /* extract rotation as eulers (if needed) - * - definitely if rotation order isn't eulers already - * - if eulers, then we have 2 options: - * a) decompose transform matrix as required, then try to make eulers from - * there compatible with original values - * b) [NOT USED] directly use the original values (no decomposition) - * - only an option for "transform space", if quality is really bad with a) - */ - float quat[4]; - int channel; - - if (dtar->transChan == DTAR_TRANSCHAN_ROTW) { - channel = 0; - } - else { - channel = 1 + dtar->transChan - DTAR_TRANSCHAN_ROTX; - BLI_assert(channel < 4); - } - - BKE_driver_target_matrix_to_rot_channels( - mat, rot_order, dtar->rotation_mode, channel, false, quat); - - if (use_eulers && dtar->rotation_mode == DTAR_ROTMODE_AUTO) { - compatible_eul(quat + 1, oldEul); - } - - return quat[channel]; - } - else { - /* extract location and choose right axis */ - return mat[3][dtar->transChan]; - } -} - -/* Convert a quaternion to pseudo-angles representing the weighted amount of rotation. */ -static void quaternion_to_angles(float quat[4], int channel) -{ - if (channel < 0) { - quat[0] = 2.0f * saacosf(quat[0]); - - for (int i = 1; i < 4; i++) { - quat[i] = 2.0f * saasinf(quat[i]); - } - } - else if (channel == 0) { - quat[0] = 2.0f * saacosf(quat[0]); - } - else { - quat[channel] = 2.0f * saasinf(quat[channel]); - } -} - -/* Compute channel values for a rotational Transform Channel driver variable. */ -void BKE_driver_target_matrix_to_rot_channels( - float mat[4][4], int auto_order, int rotation_mode, int channel, bool angles, float r_buf[4]) -{ - float *const quat = r_buf; - float *const eul = r_buf + 1; - - zero_v4(r_buf); - - if (rotation_mode == DTAR_ROTMODE_AUTO) { - mat4_to_eulO(eul, auto_order, mat); - } - else if (rotation_mode >= DTAR_ROTMODE_EULER_MIN && rotation_mode <= DTAR_ROTMODE_EULER_MAX) { - mat4_to_eulO(eul, rotation_mode, mat); - } - else if (rotation_mode == DTAR_ROTMODE_QUATERNION) { - mat4_to_quat(quat, mat); - - /* For Transformation constraint convenience, convert to pseudo-angles. */ - if (angles) { - quaternion_to_angles(quat, channel); - } - } - else if (rotation_mode >= DTAR_ROTMODE_SWING_TWIST_X && - rotation_mode <= DTAR_ROTMODE_SWING_TWIST_Z) { - int axis = rotation_mode - DTAR_ROTMODE_SWING_TWIST_X; - float raw_quat[4], twist; - - mat4_to_quat(raw_quat, mat); - - if (channel == axis + 1) { - /* If only the twist angle is needed, skip computing swing. */ - twist = quat_split_swing_and_twist(raw_quat, axis, NULL, NULL); - } - else { - twist = quat_split_swing_and_twist(raw_quat, axis, quat, NULL); - - quaternion_to_angles(quat, channel); - } - - quat[axis + 1] = twist; - } - else { - BLI_assert(false); - } -} - -/* ......... */ - -/* Table of Driver Variable Type Info Data */ -static DriverVarTypeInfo dvar_types[MAX_DVAR_TYPES] = { - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_SINGLE_PROP) dvar_eval_singleProp, /* eval callback */ - 1, /* number of targets used */ - {"Property"}, /* UI names for targets */ - {0} /* flags */ - END_DVAR_TYPEDEF, - - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* eval callback */ - 2, /* number of targets used */ - {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ - {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, - DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ - END_DVAR_TYPEDEF, - - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* eval callback */ - 2, /* number of targets used */ - {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ - {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, - DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ - END_DVAR_TYPEDEF, - - BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) dvar_eval_transChan, /* eval callback */ - 1, /* number of targets used */ - {"Object/Bone"}, /* UI names for targets */ - {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ - END_DVAR_TYPEDEF, -}; - -/* Get driver variable typeinfo */ -static const DriverVarTypeInfo *get_dvar_typeinfo(int type) -{ - /* check if valid type */ - if ((type >= 0) && (type < MAX_DVAR_TYPES)) { - return &dvar_types[type]; - } - else { - return NULL; - } -} - -/* Driver API --------------------------------- */ - -/* Perform actual freeing driver variable and remove it from the given list */ -void driver_free_variable(ListBase *variables, DriverVar *dvar) -{ - /* sanity checks */ - if (dvar == NULL) { - return; - } - - /* free target vars - * - need to go over all of them, not just up to the ones that are used - * currently, since there may be some lingering RNA paths from - * previous users needing freeing - */ - DRIVER_TARGETS_LOOPER_BEGIN (dvar) { - /* free RNA path if applicable */ - if (dtar->rna_path) { - MEM_freeN(dtar->rna_path); - } - } - DRIVER_TARGETS_LOOPER_END; - - /* remove the variable from the driver */ - BLI_freelinkN(variables, dvar); -} - -/* Free the driver variable and do extra updates */ -void driver_free_variable_ex(ChannelDriver *driver, DriverVar *dvar) -{ - /* remove and free the driver variable */ - driver_free_variable(&driver->variables, dvar); - - /* since driver variables are cached, the expression needs re-compiling too */ - BKE_driver_invalidate_expression(driver, false, true); -} - -/* Copy driver variables from src_vars list to dst_vars list */ -void driver_variables_copy(ListBase *dst_vars, const ListBase *src_vars) -{ - BLI_assert(BLI_listbase_is_empty(dst_vars)); - BLI_duplicatelist(dst_vars, src_vars); - - LISTBASE_FOREACH (DriverVar *, dvar, dst_vars) { - /* need to go over all targets so that we don't leave any dangling paths */ - DRIVER_TARGETS_LOOPER_BEGIN (dvar) { - /* make a copy of target's rna path if available */ - if (dtar->rna_path) { - dtar->rna_path = MEM_dupallocN(dtar->rna_path); - } - } - DRIVER_TARGETS_LOOPER_END; - } -} - -/* Change the type of driver variable */ -void driver_change_variable_type(DriverVar *dvar, int type) -{ - const DriverVarTypeInfo *dvti = get_dvar_typeinfo(type); - - /* sanity check */ - if (ELEM(NULL, dvar, dvti)) { - return; - } - - /* set the new settings */ - dvar->type = type; - dvar->num_targets = dvti->num_targets; - - /* make changes to the targets based on the defines for these types - * NOTE: only need to make sure the ones we're using here are valid... - */ - DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { - short flags = dvti->target_flags[tarIndex]; - - /* store the flags */ - dtar->flag = flags; - - /* object ID types only, or idtype not yet initialized */ - if ((flags & DTAR_FLAG_ID_OB_ONLY) || (dtar->idtype == 0)) { - dtar->idtype = ID_OB; - } - } - DRIVER_TARGETS_LOOPER_END; -} - -/* Validate driver name (after being renamed) */ -void driver_variable_name_validate(DriverVar *dvar) -{ - /* Special character blacklist */ - const char special_char_blacklist[] = { - '~', '`', '!', '@', '#', '$', '%', '^', '&', '*', '+', '=', '-', '/', '\\', - '?', ':', ';', '<', '>', '{', '}', '[', ']', '|', ' ', '.', '\t', '\n', '\r', - }; - - /* sanity checks */ - if (dvar == NULL) { - return; - } - - /* clear all invalid-name flags */ - dvar->flag &= ~DVAR_ALL_INVALID_FLAGS; - - /* 0) Zero-length identifiers are not allowed */ - if (dvar->name[0] == '\0') { - dvar->flag |= DVAR_FLAG_INVALID_EMPTY; - } - - /* 1) Must start with a letter */ - /* XXX: We assume that valid unicode letters in other languages are ok too, - * hence the blacklisting. */ - if (IN_RANGE_INCL(dvar->name[0], '0', '9')) { - dvar->flag |= DVAR_FLAG_INVALID_START_NUM; - } - else if (dvar->name[0] == '_') { - /* NOTE: We don't allow names to start with underscores - * (i.e. it helps when ruling out security risks) */ - dvar->flag |= DVAR_FLAG_INVALID_START_CHAR; - } - - /* 2) Must not contain invalid stuff in the middle of the string */ - if (strchr(dvar->name, ' ')) { - dvar->flag |= DVAR_FLAG_INVALID_HAS_SPACE; - } - if (strchr(dvar->name, '.')) { - dvar->flag |= DVAR_FLAG_INVALID_HAS_DOT; - } - - /* 3) Check for special characters - Either at start, or in the middle */ - for (int i = 0; i < sizeof(special_char_blacklist); i++) { - char *match = strchr(dvar->name, special_char_blacklist[i]); - - if (match == dvar->name) { - dvar->flag |= DVAR_FLAG_INVALID_START_CHAR; - } - else if (match != NULL) { - dvar->flag |= DVAR_FLAG_INVALID_HAS_SPECIAL; - } - } - - /* 4) Check if the name is a reserved keyword - * NOTE: These won't confuse Python, but it will be impossible to use the variable - * in an expression without Python misinterpreting what these are for - */ -#ifdef WITH_PYTHON - if (BPY_string_is_keyword(dvar->name)) { - dvar->flag |= DVAR_FLAG_INVALID_PY_KEYWORD; - } -#endif - - /* If any these conditions match, the name is invalid */ - if (dvar->flag & DVAR_ALL_INVALID_FLAGS) { - dvar->flag |= DVAR_FLAG_INVALID_NAME; - } -} - -/* Add a new driver variable */ -DriverVar *driver_add_new_variable(ChannelDriver *driver) -{ - DriverVar *dvar; - - /* sanity checks */ - if (driver == NULL) { - return NULL; - } - - /* make a new variable */ - dvar = MEM_callocN(sizeof(DriverVar), "DriverVar"); - BLI_addtail(&driver->variables, dvar); - - /* give the variable a 'unique' name */ - strcpy(dvar->name, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var")); - BLI_uniquename(&driver->variables, - dvar, - CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var"), - '_', - offsetof(DriverVar, name), - sizeof(dvar->name)); - - /* set the default type to 'single prop' */ - driver_change_variable_type(dvar, DVAR_TYPE_SINGLE_PROP); - - /* since driver variables are cached, the expression needs re-compiling too */ - BKE_driver_invalidate_expression(driver, false, true); - - /* return the target */ - return dvar; -} - -/* This frees the driver itself */ -void fcurve_free_driver(FCurve *fcu) -{ - ChannelDriver *driver; - DriverVar *dvar, *dvarn; - - /* sanity checks */ - if (ELEM(NULL, fcu, fcu->driver)) { - return; - } - driver = fcu->driver; - - /* free driver targets */ - for (dvar = driver->variables.first; dvar; dvar = dvarn) { - dvarn = dvar->next; - driver_free_variable_ex(driver, dvar); - } - -#ifdef WITH_PYTHON - /* free compiled driver expression */ - if (driver->expr_comp) { - BPY_DECREF(driver->expr_comp); - } -#endif - - BLI_expr_pylike_free(driver->expr_simple); - - /* Free driver itself, then set F-Curve's point to this to NULL - * (as the curve may still be used). */ - MEM_freeN(driver); - fcu->driver = NULL; -} - -/* This makes a copy of the given driver */ -ChannelDriver *fcurve_copy_driver(const ChannelDriver *driver) -{ - ChannelDriver *ndriver; - - /* sanity checks */ - if (driver == NULL) { - return NULL; - } - - /* copy all data */ - ndriver = MEM_dupallocN(driver); - ndriver->expr_comp = NULL; - ndriver->expr_simple = NULL; - - /* copy variables */ - - /* to get rid of refs to non-copied data (that's still used on original) */ - BLI_listbase_clear(&ndriver->variables); - driver_variables_copy(&ndriver->variables, &driver->variables); - - /* return the new driver */ - return ndriver; -} - -/* Driver Expression Evaluation --------------- */ - -/* Index constants for the expression parameter array. */ -enum { - /* Index of the 'frame' variable. */ - VAR_INDEX_FRAME = 0, - /* Index of the first user-defined driver variable. */ - VAR_INDEX_CUSTOM -}; - -static ExprPyLike_Parsed *driver_compile_simple_expr_impl(ChannelDriver *driver) -{ - /* Prepare parameter names. */ - int names_len = BLI_listbase_count(&driver->variables); - const char **names = BLI_array_alloca(names, names_len + VAR_INDEX_CUSTOM); - int i = VAR_INDEX_CUSTOM; - - names[VAR_INDEX_FRAME] = "frame"; - - LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) { - names[i++] = dvar->name; - } - - return BLI_expr_pylike_parse(driver->expression, names, names_len + VAR_INDEX_CUSTOM); -} - -static bool driver_check_simple_expr_depends_on_time(ExprPyLike_Parsed *expr) -{ - /* Check if the 'frame' parameter is actually used. */ - return BLI_expr_pylike_is_using_param(expr, VAR_INDEX_FRAME); -} - -static bool driver_evaluate_simple_expr(ChannelDriver *driver, - ExprPyLike_Parsed *expr, - float *result, - float time) -{ - /* Prepare parameter values. */ - int vars_len = BLI_listbase_count(&driver->variables); - double *vars = BLI_array_alloca(vars, vars_len + VAR_INDEX_CUSTOM); - int i = VAR_INDEX_CUSTOM; - - vars[VAR_INDEX_FRAME] = time; - - LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) { - vars[i++] = driver_get_variable_value(driver, dvar); - } - - /* Evaluate expression. */ - double result_val; - eExprPyLike_EvalStatus status = BLI_expr_pylike_eval( - expr, vars, vars_len + VAR_INDEX_CUSTOM, &result_val); - const char *message; - - switch (status) { - case EXPR_PYLIKE_SUCCESS: - if (isfinite(result_val)) { - *result = (float)result_val; - } - return true; - - case EXPR_PYLIKE_DIV_BY_ZERO: - case EXPR_PYLIKE_MATH_ERROR: - message = (status == EXPR_PYLIKE_DIV_BY_ZERO) ? "Division by Zero" : "Math Domain Error"; - CLOG_ERROR(&LOG, "%s in Driver: '%s'", message, driver->expression); - - driver->flag |= DRIVER_FLAG_INVALID; - return true; - - default: - /* arriving here means a bug, not user error */ - CLOG_ERROR(&LOG, "simple driver expression evaluation failed: '%s'", driver->expression); - return false; - } -} - -/* Compile and cache the driver expression if necessary, with thread safety. */ -static bool driver_compile_simple_expr(ChannelDriver *driver) -{ - if (driver->expr_simple != NULL) { - return true; - } - - if (driver->type != DRIVER_TYPE_PYTHON) { - return false; - } - - /* It's safe to parse in multiple threads; at worst it'll - * waste some effort, but in return avoids mutex contention. */ - ExprPyLike_Parsed *expr = driver_compile_simple_expr_impl(driver); - - /* Store the result if the field is still NULL, or discard - * it if another thread got here first. */ - if (atomic_cas_ptr((void **)&driver->expr_simple, NULL, expr) != NULL) { - BLI_expr_pylike_free(expr); - } - - return true; -} - -/* Try using the simple expression evaluator to compute the result of the driver. - * On success, stores the result and returns true; on failure result is set to 0. */ -static bool driver_try_evaluate_simple_expr(ChannelDriver *driver, - ChannelDriver *driver_orig, - float *result, - float time) -{ - *result = 0.0f; - - return driver_compile_simple_expr(driver_orig) && - BLI_expr_pylike_is_valid(driver_orig->expr_simple) && - driver_evaluate_simple_expr(driver, driver_orig->expr_simple, result, time); -} - -/* Check if the expression in the driver conforms to the simple subset. */ -bool BKE_driver_has_simple_expression(ChannelDriver *driver) -{ - return driver_compile_simple_expr(driver) && BLI_expr_pylike_is_valid(driver->expr_simple); -} - -/* TODO(sergey): This is somewhat weak, but we don't want neither false-positive - * time dependencies nor special exceptions in the depsgraph evaluation. */ -static bool python_driver_exression_depends_on_time(const char *expression) -{ - if (expression[0] == '\0') { - /* Empty expression depends on nothing. */ - return false; - } - if (strchr(expression, '(') != NULL) { - /* Function calls are considered dependent on a time. */ - return true; - } - if (strstr(expression, "frame") != NULL) { - /* Variable `frame` depends on time. */ - /* TODO(sergey): This is a bit weak, but not sure about better way of handling this. */ - return true; - } - /* Possible indirect time relation s should be handled via variable targets. */ - return false; -} - -/* Check if the expression in the driver may depend on the current frame. */ -bool BKE_driver_expression_depends_on_time(ChannelDriver *driver) -{ - if (driver->type != DRIVER_TYPE_PYTHON) { - return false; - } - - if (BKE_driver_has_simple_expression(driver)) { - /* Simple expressions can be checked exactly. */ - return driver_check_simple_expr_depends_on_time(driver->expr_simple); - } - else { - /* Otherwise, heuristically scan the expression string for certain patterns. */ - return python_driver_exression_depends_on_time(driver->expression); - } -} - -/* Reset cached compiled expression data */ -void BKE_driver_invalidate_expression(ChannelDriver *driver, - bool expr_changed, - bool varname_changed) -{ - if (expr_changed || varname_changed) { - BLI_expr_pylike_free(driver->expr_simple); - driver->expr_simple = NULL; - } - -#ifdef WITH_PYTHON - if (expr_changed) { - driver->flag |= DRIVER_FLAG_RECOMPILE; - } - - if (varname_changed) { - driver->flag |= DRIVER_FLAG_RENAMEVAR; - } -#endif -} - -/* Driver Evaluation -------------------------- */ - -/* Evaluate a Driver Variable to get a value that contributes to the final */ -float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar) -{ - const DriverVarTypeInfo *dvti; - - /* sanity check */ - if (ELEM(NULL, driver, dvar)) { - return 0.0f; - } - - /* call the relevant callbacks to get the variable value - * using the variable type info, storing the obtained value - * in dvar->curval so that drivers can be debugged - */ - dvti = get_dvar_typeinfo(dvar->type); - - if (dvti && dvti->get_value) { - dvar->curval = dvti->get_value(driver, dvar); - } - else { - dvar->curval = 0.0f; - } - - return dvar->curval; -} - -static void evaluate_driver_sum(ChannelDriver *driver) -{ - DriverVar *dvar; - - /* check how many variables there are first (i.e. just one?) */ - if (BLI_listbase_is_single(&driver->variables)) { - /* just one target, so just use that */ - dvar = driver->variables.first; - driver->curval = driver_get_variable_value(driver, dvar); - return; - } - - /* more than one target, so average the values of the targets */ - float value = 0.0f; - int tot = 0; - - /* loop through targets, adding (hopefully we don't get any overflow!) */ - for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - value += driver_get_variable_value(driver, dvar); - tot++; - } - - /* perform operations on the total if appropriate */ - if (driver->type == DRIVER_TYPE_AVERAGE) { - driver->curval = tot ? (value / (float)tot) : 0.0f; - } - else { - driver->curval = value; - } -} - -static void evaluate_driver_min_max(ChannelDriver *driver) -{ - DriverVar *dvar; - float value = 0.0f; - - /* loop through the variables, getting the values and comparing them to existing ones */ - for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - /* get value */ - float tmp_val = driver_get_variable_value(driver, dvar); - - /* store this value if appropriate */ - if (dvar->prev) { - /* check if greater/smaller than the baseline */ - if (driver->type == DRIVER_TYPE_MAX) { - /* max? */ - if (tmp_val > value) { - value = tmp_val; - } - } - else { - /* min? */ - if (tmp_val < value) { - value = tmp_val; - } - } - } - else { - /* first item - make this the baseline for comparisons */ - value = tmp_val; - } - } - - /* store value in driver */ - driver->curval = value; -} - -static void evaluate_driver_python(PathResolvedRNA *anim_rna, - ChannelDriver *driver, - ChannelDriver *driver_orig, - const float evaltime) -{ - /* check for empty or invalid expression */ - if ((driver_orig->expression[0] == '\0') || (driver_orig->flag & DRIVER_FLAG_INVALID)) { - driver->curval = 0.0f; - } - else if (!driver_try_evaluate_simple_expr(driver, driver_orig, &driver->curval, evaltime)) { -#ifdef WITH_PYTHON - /* this evaluates the expression using Python, and returns its result: - * - on errors it reports, then returns 0.0f - */ - BLI_mutex_lock(&python_driver_lock); - - driver->curval = BPY_driver_exec(anim_rna, driver, driver_orig, evaltime); - - BLI_mutex_unlock(&python_driver_lock); -#else /* WITH_PYTHON*/ - UNUSED_VARS(anim_rna, evaltime); -#endif /* WITH_PYTHON*/ - } -} - -/* Evaluate an Channel-Driver to get a 'time' value to use instead of "evaltime" - * - "evaltime" is the frame at which F-Curve is being evaluated - * - has to return a float value - * - driver_orig is where we cache Python expressions, in case of COW - */ -float evaluate_driver(PathResolvedRNA *anim_rna, - ChannelDriver *driver, - ChannelDriver *driver_orig, - const float evaltime) -{ - /* check if driver can be evaluated */ - if (driver_orig->flag & DRIVER_FLAG_INVALID) { - return 0.0f; - } - - switch (driver->type) { - case DRIVER_TYPE_AVERAGE: /* average values of driver targets */ - case DRIVER_TYPE_SUM: /* sum values of driver targets */ - evaluate_driver_sum(driver); - break; - case DRIVER_TYPE_MIN: /* smallest value */ - case DRIVER_TYPE_MAX: /* largest value */ - evaluate_driver_min_max(driver); - break; - case DRIVER_TYPE_PYTHON: /* expression */ - evaluate_driver_python(anim_rna, driver, driver_orig, evaltime); - break; - default: - /* special 'hack' - just use stored value - * This is currently used as the mechanism which allows animated settings to be able - * to be changed via the UI. - */ - break; - } - - /* return value for driver */ - return driver->curval; -} - /* ***************************** Curve Calculations ********************************* */ /* The total length of the handles is not allowed to be more @@ -2666,437 +1415,325 @@ static void berekeny(float f1, float f2, float f3, float f4, float *o, int b) /* -------------------------- */ -/* Calculate F-Curve value for 'evaltime' using BezTriple keyframes */ -static float fcurve_eval_keyframes(FCurve *fcu, BezTriple *bezts, float evaltime) +static float fcurve_eval_keyframes_extrapolate( + FCurve *fcu, BezTriple *bezts, float evaltime, int endpoint_offset, int direction_to_neighbor) +{ + BezTriple *endpoint_bezt = bezts + endpoint_offset; /* The first/last keyframe. */ + BezTriple *neighbor_bezt = endpoint_bezt + + direction_to_neighbor; /* The second (to last) keyframe. */ + + if (endpoint_bezt->ipo == BEZT_IPO_CONST || fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT || + (fcu->flag & FCURVE_DISCRETE_VALUES) != 0) { + /* Constant (BEZT_IPO_HORIZ) extrapolation or constant interpolation, so just extend the + * endpoint's value. */ + return endpoint_bezt->vec[1][1]; + } + + if (endpoint_bezt->ipo == BEZT_IPO_LIN) { + /* Use the next center point instead of our own handle for linear interpolated extrapolate. */ + if (fcu->totvert == 1) { + return endpoint_bezt->vec[1][1]; + } + + float dx = endpoint_bezt->vec[1][0] - evaltime; + float fac = neighbor_bezt->vec[1][0] - endpoint_bezt->vec[1][0]; + + /* Prevent division by zero. */ + if (fac == 0.0f) { + return endpoint_bezt->vec[1][1]; + } + + fac = (neighbor_bezt->vec[1][1] - endpoint_bezt->vec[1][1]) / fac; + return endpoint_bezt->vec[1][1] - (fac * dx); + } + + /* Use the gradient of the second handle (later) of neighbour to calculate the gradient and thus + * the value of the curve at evaltime */ + int handle = direction_to_neighbor > 0 ? 0 : 2; + float dx = endpoint_bezt->vec[1][0] - evaltime; + float fac = endpoint_bezt->vec[1][0] - endpoint_bezt->vec[handle][0]; + + /* Prevent division by zero. */ + if (fac == 0.0f) { + return endpoint_bezt->vec[1][1]; + } + + fac = (endpoint_bezt->vec[1][1] - endpoint_bezt->vec[handle][1]) / fac; + return endpoint_bezt->vec[1][1] - (fac * dx); +} + +static float fcurve_eval_keyframes_interpolate(FCurve *fcu, BezTriple *bezts, float evaltime) { const float eps = 1.e-8f; - BezTriple *bezt, *prevbezt, *lastbezt; - float v1[2], v2[2], v3[2], v4[2], opl[32], dx, fac; + BezTriple *bezt, *prevbezt; unsigned int a; - int b; - float cvalue = 0.0f; - /* get pointers */ - a = fcu->totvert - 1; - prevbezt = bezts; - bezt = prevbezt + 1; - lastbezt = prevbezt + a; + /* evaltime occurs somewhere in the middle of the curve */ + bool exact = false; + + /* Use binary search to find appropriate keyframes... + * + * The threshold here has the following constraints: + * - 0.001 is too coarse: + * We get artifacts with 2cm driver movements at 1BU = 1m (see T40332) + * + * - 0.00001 is too fine: + * Weird errors, like selecting the wrong keyframe range (see T39207), occur. + * This lower bound was established in b888a32eee8147b028464336ad2404d8155c64dd. + */ + a = binarysearch_bezt_index_ex(bezts, evaltime, fcu->totvert, 0.0001, &exact); + bezt = bezts + a; - /* evaluation time at or past endpoints? */ - if (prevbezt->vec[1][0] >= evaltime) { - /* before or on first keyframe */ - if ((fcu->extend == FCURVE_EXTRAPOLATE_LINEAR) && (prevbezt->ipo != BEZT_IPO_CONST) && - !(fcu->flag & FCURVE_DISCRETE_VALUES)) { - /* linear or bezier interpolation */ - if (prevbezt->ipo == BEZT_IPO_LIN) { - /* Use the next center point instead of our own handle for - * linear interpolated extrapolate - */ - if (fcu->totvert == 1) { - cvalue = prevbezt->vec[1][1]; - } - else { - bezt = prevbezt + 1; - dx = prevbezt->vec[1][0] - evaltime; - fac = bezt->vec[1][0] - prevbezt->vec[1][0]; - - /* prevent division by zero */ - if (fac) { - fac = (bezt->vec[1][1] - prevbezt->vec[1][1]) / fac; - cvalue = prevbezt->vec[1][1] - (fac * dx); - } - else { - cvalue = prevbezt->vec[1][1]; - } - } - } - else { - /* Use the first handle (earlier) of first BezTriple to calculate the - * gradient and thus the value of the curve at evaltime - */ - dx = prevbezt->vec[1][0] - evaltime; - fac = prevbezt->vec[1][0] - prevbezt->vec[0][0]; + if (exact) { + /* index returned must be interpreted differently when it sits on top of an existing keyframe + * - that keyframe is the start of the segment we need (see action_bug_2.blend in T39207) + */ + return bezt->vec[1][1]; + } - /* prevent division by zero */ - if (fac) { - fac = (prevbezt->vec[1][1] - prevbezt->vec[0][1]) / fac; - cvalue = prevbezt->vec[1][1] - (fac * dx); - } - else { - cvalue = prevbezt->vec[1][1]; - } - } - } - else { - /* constant (BEZT_IPO_HORIZ) extrapolation or constant interpolation, - * so just extend first keyframe's value - */ - cvalue = prevbezt->vec[1][1]; + /* index returned refers to the keyframe that the eval-time occurs *before* + * - hence, that keyframe marks the start of the segment we're dealing with + */ + prevbezt = (a > 0) ? (bezt - 1) : bezt; + + /* Use if the key is directly on the frame, in rare cases this is needed else we get 0.0 instead. + * XXX: consult T39207 for examples of files where failure of these checks can cause issues */ + if (fabsf(bezt->vec[1][0] - evaltime) < eps) { + return bezt->vec[1][1]; + } + + if (evaltime < prevbezt->vec[1][0] || bezt->vec[1][0] < evaltime) { + if (G.debug & G_DEBUG) { + printf(" ERROR: failed eval - p=%f b=%f, t=%f (%f)\n", + prevbezt->vec[1][0], + bezt->vec[1][0], + evaltime, + fabsf(bezt->vec[1][0] - evaltime)); } + return 0.0f; } - else if (lastbezt->vec[1][0] <= evaltime) { - /* after or on last keyframe */ - if ((fcu->extend == FCURVE_EXTRAPOLATE_LINEAR) && (lastbezt->ipo != BEZT_IPO_CONST) && - !(fcu->flag & FCURVE_DISCRETE_VALUES)) { - /* linear or bezier interpolation */ - if (lastbezt->ipo == BEZT_IPO_LIN) { - /* Use the next center point instead of our own handle for - * linear interpolated extrapolate + + /* Evaltime occurs within the interval defined by these two keyframes. */ + const float begin = prevbezt->vec[1][1]; + const float change = bezt->vec[1][1] - prevbezt->vec[1][1]; + const float duration = bezt->vec[1][0] - prevbezt->vec[1][0]; + const float time = evaltime - prevbezt->vec[1][0]; + const float amplitude = prevbezt->amplitude; + const float period = prevbezt->period; + + /* value depends on interpolation mode */ + if ((prevbezt->ipo == BEZT_IPO_CONST) || (fcu->flag & FCURVE_DISCRETE_VALUES) || + (duration == 0)) { + /* constant (evaltime not relevant, so no interpolation needed) */ + return prevbezt->vec[1][1]; + } + + switch (prevbezt->ipo) { + /* interpolation ...................................... */ + case BEZT_IPO_BEZ: { + float v1[2], v2[2], v3[2], v4[2], opl[32]; + + /* bezier interpolation */ + /* (v1, v2) are the first keyframe and its 2nd handle */ + v1[0] = prevbezt->vec[1][0]; + v1[1] = prevbezt->vec[1][1]; + v2[0] = prevbezt->vec[2][0]; + v2[1] = prevbezt->vec[2][1]; + /* (v3, v4) are the last keyframe's 1st handle + the last keyframe */ + v3[0] = bezt->vec[0][0]; + v3[1] = bezt->vec[0][1]; + v4[0] = bezt->vec[1][0]; + v4[1] = bezt->vec[1][1]; + + if (fabsf(v1[1] - v4[1]) < FLT_EPSILON && fabsf(v2[1] - v3[1]) < FLT_EPSILON && + fabsf(v3[1] - v4[1]) < FLT_EPSILON) { + /* Optimization: If all the handles are flat/at the same values, + * the value is simply the shared value (see T40372 -> F91346) */ - if (fcu->totvert == 1) { - cvalue = lastbezt->vec[1][1]; - } - else { - prevbezt = lastbezt - 1; - dx = evaltime - lastbezt->vec[1][0]; - fac = lastbezt->vec[1][0] - prevbezt->vec[1][0]; - - /* prevent division by zero */ - if (fac) { - fac = (lastbezt->vec[1][1] - prevbezt->vec[1][1]) / fac; - cvalue = lastbezt->vec[1][1] + (fac * dx); - } - else { - cvalue = lastbezt->vec[1][1]; - } + return v1[1]; + } + /* adjust handles so that they don't overlap (forming a loop) */ + correct_bezpart(v1, v2, v3, v4); + + /* try to get a value for this position - if failure, try another set of points */ + if (!findzero(evaltime, v1[0], v2[0], v3[0], v4[0], opl)) { + if (G.debug & G_DEBUG) { + printf(" ERROR: findzero() failed at %f with %f %f %f %f\n", + evaltime, + v1[0], + v2[0], + v3[0], + v4[0]); } + return 0.0; } - else { - /* Use the gradient of the second handle (later) of last BezTriple to calculate the - * gradient and thus the value of the curve at evaltime - */ - dx = evaltime - lastbezt->vec[1][0]; - fac = lastbezt->vec[2][0] - lastbezt->vec[1][0]; - /* prevent division by zero */ - if (fac) { - fac = (lastbezt->vec[2][1] - lastbezt->vec[1][1]) / fac; - cvalue = lastbezt->vec[1][1] + (fac * dx); - } - else { - cvalue = lastbezt->vec[1][1]; - } + berekeny(v1[1], v2[1], v3[1], v4[1], opl, 1); + return opl[0]; + } + case BEZT_IPO_LIN: + /* linear - simply linearly interpolate between values of the two keyframes */ + return BLI_easing_linear_ease(time, begin, change, duration); + + /* easing ............................................ */ + case BEZT_IPO_BACK: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + return BLI_easing_back_ease_in(time, begin, change, duration, prevbezt->back); + case BEZT_IPO_EASE_OUT: + return BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back); + case BEZT_IPO_EASE_IN_OUT: + return BLI_easing_back_ease_in_out(time, begin, change, duration, prevbezt->back); + + default: /* default/auto: same as ease out */ + return BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back); } - } - else { - /* constant (BEZT_IPO_HORIZ) extrapolation or constant interpolation, - * so just extend last keyframe's value - */ - cvalue = lastbezt->vec[1][1]; - } - } - else { - /* evaltime occurs somewhere in the middle of the curve */ - bool exact = false; - - /* Use binary search to find appropriate keyframes... - * - * The threshold here has the following constraints: - * - 0.001 is too coarse: - * We get artifacts with 2cm driver movements at 1BU = 1m (see T40332) - * - * - 0.00001 is too fine: - * Weird errors, like selecting the wrong keyframe range (see T39207), occur. - * This lower bound was established in b888a32eee8147b028464336ad2404d8155c64dd. - */ - a = binarysearch_bezt_index_ex(bezts, evaltime, fcu->totvert, 0.0001, &exact); + break; - if (exact) { - /* index returned must be interpreted differently when it sits on top of an existing keyframe - * - that keyframe is the start of the segment we need (see action_bug_2.blend in T39207) - */ - prevbezt = bezts + a; - bezt = (a < fcu->totvert - 1) ? (prevbezt + 1) : prevbezt; - } - else { - /* index returned refers to the keyframe that the eval-time occurs *before* - * - hence, that keyframe marks the start of the segment we're dealing with - */ - bezt = bezts + a; - prevbezt = (a > 0) ? (bezt - 1) : bezt; - } + case BEZT_IPO_BOUNCE: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + return BLI_easing_bounce_ease_in(time, begin, change, duration); + case BEZT_IPO_EASE_OUT: + return BLI_easing_bounce_ease_out(time, begin, change, duration); + case BEZT_IPO_EASE_IN_OUT: + return BLI_easing_bounce_ease_in_out(time, begin, change, duration); + + default: /* default/auto: same as ease out */ + return BLI_easing_bounce_ease_out(time, begin, change, duration); + } + break; - /* use if the key is directly on the frame, - * rare cases this is needed else we get 0.0 instead. */ - /* XXX: consult T39207 for examples of files where failure of these checks can cause issues */ - if (exact) { - cvalue = prevbezt->vec[1][1]; - } - else if (fabsf(bezt->vec[1][0] - evaltime) < eps) { - cvalue = bezt->vec[1][1]; - } - /* evaltime occurs within the interval defined by these two keyframes */ - else if ((prevbezt->vec[1][0] <= evaltime) && (bezt->vec[1][0] >= evaltime)) { - const float begin = prevbezt->vec[1][1]; - const float change = bezt->vec[1][1] - prevbezt->vec[1][1]; - const float duration = bezt->vec[1][0] - prevbezt->vec[1][0]; - const float time = evaltime - prevbezt->vec[1][0]; - const float amplitude = prevbezt->amplitude; - const float period = prevbezt->period; - - /* value depends on interpolation mode */ - if ((prevbezt->ipo == BEZT_IPO_CONST) || (fcu->flag & FCURVE_DISCRETE_VALUES) || - (duration == 0)) { - /* constant (evaltime not relevant, so no interpolation needed) */ - cvalue = prevbezt->vec[1][1]; + case BEZT_IPO_CIRC: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + return BLI_easing_circ_ease_in(time, begin, change, duration); + case BEZT_IPO_EASE_OUT: + return BLI_easing_circ_ease_out(time, begin, change, duration); + case BEZT_IPO_EASE_IN_OUT: + return BLI_easing_circ_ease_in_out(time, begin, change, duration); + + default: /* default/auto: same as ease in */ + return BLI_easing_circ_ease_in(time, begin, change, duration); } - else { - switch (prevbezt->ipo) { - /* interpolation ...................................... */ - case BEZT_IPO_BEZ: - /* bezier interpolation */ - /* (v1, v2) are the first keyframe and its 2nd handle */ - v1[0] = prevbezt->vec[1][0]; - v1[1] = prevbezt->vec[1][1]; - v2[0] = prevbezt->vec[2][0]; - v2[1] = prevbezt->vec[2][1]; - /* (v3, v4) are the last keyframe's 1st handle + the last keyframe */ - v3[0] = bezt->vec[0][0]; - v3[1] = bezt->vec[0][1]; - v4[0] = bezt->vec[1][0]; - v4[1] = bezt->vec[1][1]; - - if (fabsf(v1[1] - v4[1]) < FLT_EPSILON && fabsf(v2[1] - v3[1]) < FLT_EPSILON && - fabsf(v3[1] - v4[1]) < FLT_EPSILON) { - /* Optimization: If all the handles are flat/at the same values, - * the value is simply the shared value (see T40372 -> F91346) - */ - cvalue = v1[1]; - } - else { - /* adjust handles so that they don't overlap (forming a loop) */ - correct_bezpart(v1, v2, v3, v4); - - /* try to get a value for this position - if failure, try another set of points */ - b = findzero(evaltime, v1[0], v2[0], v3[0], v4[0], opl); - if (b) { - berekeny(v1[1], v2[1], v3[1], v4[1], opl, 1); - cvalue = opl[0]; - /* break; */ - } - else { - if (G.debug & G_DEBUG) { - printf(" ERROR: findzero() failed at %f with %f %f %f %f\n", - evaltime, - v1[0], - v2[0], - v3[0], - v4[0]); - } - } - } - break; - - case BEZT_IPO_LIN: - /* linear - simply linearly interpolate between values of the two keyframes */ - cvalue = BLI_easing_linear_ease(time, begin, change, duration); - break; - - /* easing ............................................ */ - case BEZT_IPO_BACK: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_back_ease_in(time, begin, change, duration, prevbezt->back); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_back_ease_in_out( - time, begin, change, duration, prevbezt->back); - break; - - default: /* default/auto: same as ease out */ - cvalue = BLI_easing_back_ease_out(time, begin, change, duration, prevbezt->back); - break; - } - break; - - case BEZT_IPO_BOUNCE: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_bounce_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_bounce_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_bounce_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease out */ - cvalue = BLI_easing_bounce_ease_out(time, begin, change, duration); - break; - } - break; - - case BEZT_IPO_CIRC: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_circ_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_circ_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_circ_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - cvalue = BLI_easing_circ_ease_in(time, begin, change, duration); - break; - } - break; - - case BEZT_IPO_CUBIC: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_cubic_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_cubic_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_cubic_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - cvalue = BLI_easing_cubic_ease_in(time, begin, change, duration); - break; - } - break; - - case BEZT_IPO_ELASTIC: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_elastic_ease_in( - time, begin, change, duration, amplitude, period); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_elastic_ease_out( - time, begin, change, duration, amplitude, period); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_elastic_ease_in_out( - time, begin, change, duration, amplitude, period); - break; - - default: /* default/auto: same as ease out */ - cvalue = BLI_easing_elastic_ease_out( - time, begin, change, duration, amplitude, period); - break; - } - break; - - case BEZT_IPO_EXPO: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_expo_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_expo_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_expo_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - cvalue = BLI_easing_expo_ease_in(time, begin, change, duration); - break; - } - break; - - case BEZT_IPO_QUAD: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_quad_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_quad_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_quad_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - cvalue = BLI_easing_quad_ease_in(time, begin, change, duration); - break; - } - break; - - case BEZT_IPO_QUART: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_quart_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_quart_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_quart_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - cvalue = BLI_easing_quart_ease_in(time, begin, change, duration); - break; - } - break; - - case BEZT_IPO_QUINT: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_quint_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_quint_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_quint_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - cvalue = BLI_easing_quint_ease_in(time, begin, change, duration); - break; - } - break; - - case BEZT_IPO_SINE: - switch (prevbezt->easing) { - case BEZT_IPO_EASE_IN: - cvalue = BLI_easing_sine_ease_in(time, begin, change, duration); - break; - case BEZT_IPO_EASE_OUT: - cvalue = BLI_easing_sine_ease_out(time, begin, change, duration); - break; - case BEZT_IPO_EASE_IN_OUT: - cvalue = BLI_easing_sine_ease_in_out(time, begin, change, duration); - break; - - default: /* default/auto: same as ease in */ - cvalue = BLI_easing_sine_ease_in(time, begin, change, duration); - break; - } - break; + break; - default: - cvalue = prevbezt->vec[1][1]; - break; - } + case BEZT_IPO_CUBIC: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + return BLI_easing_cubic_ease_in(time, begin, change, duration); + case BEZT_IPO_EASE_OUT: + return BLI_easing_cubic_ease_out(time, begin, change, duration); + case BEZT_IPO_EASE_IN_OUT: + return BLI_easing_cubic_ease_in_out(time, begin, change, duration); + + default: /* default/auto: same as ease in */ + return BLI_easing_cubic_ease_in(time, begin, change, duration); } - } - else { - if (G.debug & G_DEBUG) { - printf(" ERROR: failed eval - p=%f b=%f, t=%f (%f)\n", - prevbezt->vec[1][0], - bezt->vec[1][0], - evaltime, - fabsf(bezt->vec[1][0] - evaltime)); + break; + + case BEZT_IPO_ELASTIC: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + return BLI_easing_elastic_ease_in(time, begin, change, duration, amplitude, period); + case BEZT_IPO_EASE_OUT: + return BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); + case BEZT_IPO_EASE_IN_OUT: + return BLI_easing_elastic_ease_in_out(time, begin, change, duration, amplitude, period); + + default: /* default/auto: same as ease out */ + return BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); } - } + break; + + case BEZT_IPO_EXPO: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + return BLI_easing_expo_ease_in(time, begin, change, duration); + case BEZT_IPO_EASE_OUT: + return BLI_easing_expo_ease_out(time, begin, change, duration); + case BEZT_IPO_EASE_IN_OUT: + return BLI_easing_expo_ease_in_out(time, begin, change, duration); + + default: /* default/auto: same as ease in */ + return BLI_easing_expo_ease_in(time, begin, change, duration); + } + break; + + case BEZT_IPO_QUAD: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + return BLI_easing_quad_ease_in(time, begin, change, duration); + case BEZT_IPO_EASE_OUT: + return BLI_easing_quad_ease_out(time, begin, change, duration); + case BEZT_IPO_EASE_IN_OUT: + return BLI_easing_quad_ease_in_out(time, begin, change, duration); + + default: /* default/auto: same as ease in */ + return BLI_easing_quad_ease_in(time, begin, change, duration); + } + break; + + case BEZT_IPO_QUART: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + return BLI_easing_quart_ease_in(time, begin, change, duration); + case BEZT_IPO_EASE_OUT: + return BLI_easing_quart_ease_out(time, begin, change, duration); + case BEZT_IPO_EASE_IN_OUT: + return BLI_easing_quart_ease_in_out(time, begin, change, duration); + + default: /* default/auto: same as ease in */ + return BLI_easing_quart_ease_in(time, begin, change, duration); + } + break; + + case BEZT_IPO_QUINT: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + return BLI_easing_quint_ease_in(time, begin, change, duration); + case BEZT_IPO_EASE_OUT: + return BLI_easing_quint_ease_out(time, begin, change, duration); + case BEZT_IPO_EASE_IN_OUT: + return BLI_easing_quint_ease_in_out(time, begin, change, duration); + + default: /* default/auto: same as ease in */ + return BLI_easing_quint_ease_in(time, begin, change, duration); + } + break; + + case BEZT_IPO_SINE: + switch (prevbezt->easing) { + case BEZT_IPO_EASE_IN: + return BLI_easing_sine_ease_in(time, begin, change, duration); + case BEZT_IPO_EASE_OUT: + return BLI_easing_sine_ease_out(time, begin, change, duration); + case BEZT_IPO_EASE_IN_OUT: + return BLI_easing_sine_ease_in_out(time, begin, change, duration); + + default: /* default/auto: same as ease in */ + return BLI_easing_sine_ease_in(time, begin, change, duration); + } + break; + + default: + return prevbezt->vec[1][1]; + } +} + +/* Calculate F-Curve value for 'evaltime' using BezTriple keyframes */ +static float fcurve_eval_keyframes(FCurve *fcu, BezTriple *bezts, float evaltime) +{ + if (evaltime <= bezts->vec[1][0]) { + return fcurve_eval_keyframes_extrapolate(fcu, bezts, evaltime, 0, +1); } - /* return value */ - return cvalue; + BezTriple *lastbezt = bezts + fcu->totvert - 1; + if (lastbezt->vec[1][0] <= evaltime) { + return fcurve_eval_keyframes_extrapolate(fcu, bezts, evaltime, fcu->totvert - 1, -1); + } + + return fcurve_eval_keyframes_interpolate(fcu, bezts, evaltime); } /* Calculate F-Curve value for 'evaltime' using FPoint samples */ diff --git a/source/blender/blenkernel/intern/fcurve_driver.c b/source/blender/blenkernel/intern/fcurve_driver.c new file mode 100644 index 00000000000..78a6cf28824 --- /dev/null +++ b/source/blender/blenkernel/intern/fcurve_driver.c @@ -0,0 +1,1294 @@ +/* + * 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 bke + */ + +// #include <float.h> +// #include <math.h> +// #include <stddef.h> +// #include <stdio.h> +// #include <string.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_anim_types.h" +#include "DNA_constraint_types.h" +#include "DNA_object_types.h" + +#include "BLI_alloca.h" +#include "BLI_expr_pylike_eval.h" +#include "BLI_math.h" +#include "BLI_string_utils.h" +#include "BLI_threads.h" +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "BKE_action.h" +#include "BKE_armature.h" +#include "BKE_constraint.h" +#include "BKE_fcurve_driver.h" +#include "BKE_global.h" +#include "BKE_object.h" + +#include "RNA_access.h" + +#include "atomic_ops.h" + +#include "CLG_log.h" + +#ifdef WITH_PYTHON +# include "BPY_extern.h" +#endif + +#ifdef WITH_PYTHON +static ThreadMutex python_driver_lock = BLI_MUTEX_INITIALIZER; +#endif + +static CLG_LogRef LOG = {"bke.fcurve"}; + +/* Driver Variables --------------------------- */ + +/* TypeInfo for Driver Variables (dvti) */ +typedef struct DriverVarTypeInfo { + /* evaluation callback */ + float (*get_value)(ChannelDriver *driver, DriverVar *dvar); + + /* allocation of target slots */ + int num_targets; /* number of target slots required */ + const char *target_names[MAX_DRIVER_TARGETS]; /* UI names that should be given to the slots */ + short target_flags[MAX_DRIVER_TARGETS]; /* flags defining the requirements for each slot */ +} DriverVarTypeInfo; + +/* Macro to begin definitions */ +#define BEGIN_DVAR_TYPEDEF(type) { + +/* Macro to end definitions */ +#define END_DVAR_TYPEDEF } + +/* ......... */ + +static ID *dtar_id_ensure_proxy_from(ID *id) +{ + if (id && GS(id->name) == ID_OB && ((Object *)id)->proxy_from) { + return (ID *)(((Object *)id)->proxy_from); + } + return id; +} + +/** + * Helper function to obtain a value using RNA from the specified source + * (for evaluating drivers). + */ +static float dtar_get_prop_val(ChannelDriver *driver, DriverTarget *dtar) +{ + PointerRNA id_ptr, ptr; + PropertyRNA *prop; + ID *id; + int index = -1; + float value = 0.0f; + + /* sanity check */ + if (ELEM(NULL, driver, dtar)) { + return 0.0f; + } + + id = dtar_id_ensure_proxy_from(dtar->id); + + /* error check for missing pointer... */ + if (id == NULL) { + if (G.debug & G_DEBUG) { + CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path); + } + + driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; + return 0.0f; + } + + /* get RNA-pointer for the ID-block given in target */ + RNA_id_pointer_create(id, &id_ptr); + + /* get property to read from, and get value as appropriate */ + if (!RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) { + /* path couldn't be resolved */ + if (G.debug & G_DEBUG) { + CLOG_ERROR(&LOG, + "Driver Evaluation Error: cannot resolve target for %s -> %s", + id->name, + dtar->rna_path); + } + + driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; + return 0.0f; + } + + if (RNA_property_array_check(prop)) { + /* array */ + if (index < 0 || index >= RNA_property_array_length(&ptr, prop)) { + /* out of bounds */ + if (G.debug & G_DEBUG) { + CLOG_ERROR(&LOG, + "Driver Evaluation Error: array index is out of bounds for %s -> %s (%d)", + id->name, + dtar->rna_path, + index); + } + + driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; + return 0.0f; + } + + switch (RNA_property_type(prop)) { + case PROP_BOOLEAN: + value = (float)RNA_property_boolean_get_index(&ptr, prop, index); + break; + case PROP_INT: + value = (float)RNA_property_int_get_index(&ptr, prop, index); + break; + case PROP_FLOAT: + value = RNA_property_float_get_index(&ptr, prop, index); + break; + default: + break; + } + } + else { + /* not an array */ + switch (RNA_property_type(prop)) { + case PROP_BOOLEAN: + value = (float)RNA_property_boolean_get(&ptr, prop); + break; + case PROP_INT: + value = (float)RNA_property_int_get(&ptr, prop); + break; + case PROP_FLOAT: + value = RNA_property_float_get(&ptr, prop); + break; + case PROP_ENUM: + value = (float)RNA_property_enum_get(&ptr, prop); + break; + default: + break; + } + } + + /* if we're still here, we should be ok... */ + dtar->flag &= ~DTAR_FLAG_INVALID; + return value; +} + +/** + * Same as 'dtar_get_prop_val'. but get the RNA property. + */ +bool driver_get_variable_property(ChannelDriver *driver, + DriverTarget *dtar, + PointerRNA *r_ptr, + PropertyRNA **r_prop, + int *r_index) +{ + PointerRNA id_ptr; + PointerRNA ptr; + PropertyRNA *prop; + ID *id; + int index = -1; + + /* sanity check */ + if (ELEM(NULL, driver, dtar)) { + return false; + } + + id = dtar_id_ensure_proxy_from(dtar->id); + + /* error check for missing pointer... */ + if (id == NULL) { + if (G.debug & G_DEBUG) { + CLOG_ERROR(&LOG, "driver has an invalid target to use (path = %s)", dtar->rna_path); + } + + driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; + return false; + } + + /* get RNA-pointer for the ID-block given in target */ + RNA_id_pointer_create(id, &id_ptr); + + /* get property to read from, and get value as appropriate */ + if (dtar->rna_path == NULL || dtar->rna_path[0] == '\0') { + ptr = PointerRNA_NULL; + prop = NULL; /* ok */ + } + else if (RNA_path_resolve_property_full(&id_ptr, dtar->rna_path, &ptr, &prop, &index)) { + /* ok */ + } + else { + /* path couldn't be resolved */ + if (G.debug & G_DEBUG) { + CLOG_ERROR(&LOG, + "Driver Evaluation Error: cannot resolve target for %s -> %s", + id->name, + dtar->rna_path); + } + + ptr = PointerRNA_NULL; + *r_prop = NULL; + *r_index = -1; + + driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; + return false; + } + + *r_ptr = ptr; + *r_prop = prop; + *r_index = index; + + /* if we're still here, we should be ok... */ + dtar->flag &= ~DTAR_FLAG_INVALID; + return true; +} + +static short driver_check_valid_targets(ChannelDriver *driver, DriverVar *dvar) +{ + short valid_targets = 0; + + DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { + Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + + /* check if this target has valid data */ + if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { + /* invalid target, so will not have enough targets */ + driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; + } + else { + /* target seems to be OK now... */ + dtar->flag &= ~DTAR_FLAG_INVALID; + valid_targets++; + } + } + DRIVER_TARGETS_LOOPER_END; + + return valid_targets; +} + +/* ......... */ + +/* evaluate 'single prop' driver variable */ +static float dvar_eval_singleProp(ChannelDriver *driver, DriverVar *dvar) +{ + /* just evaluate the first target slot */ + return dtar_get_prop_val(driver, &dvar->targets[0]); +} + +/* evaluate 'rotation difference' driver variable */ +static float dvar_eval_rotDiff(ChannelDriver *driver, DriverVar *dvar) +{ + short valid_targets = driver_check_valid_targets(driver, dvar); + + /* make sure we have enough valid targets to use - all or nothing for now... */ + if (driver_check_valid_targets(driver, dvar) != 2) { + if (G.debug & G_DEBUG) { + CLOG_WARN(&LOG, + "RotDiff DVar: not enough valid targets (n = %d) (a = %p, b = %p)", + valid_targets, + dvar->targets[0].id, + dvar->targets[1].id); + } + return 0.0f; + } + + float(*mat[2])[4]; + + /* NOTE: for now, these are all just worldspace */ + for (int i = 0; i < 2; i++) { + /* get pointer to loc values to store in */ + DriverTarget *dtar = &dvar->targets[i]; + Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + bPoseChannel *pchan; + + /* after the checks above, the targets should be valid here... */ + BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB)); + + /* try to get posechannel */ + pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); + + /* check if object or bone */ + if (pchan) { + /* bone */ + mat[i] = pchan->pose_mat; + } + else { + /* object */ + mat[i] = ob->obmat; + } + } + + float q1[4], q2[4], quat[4], angle; + + /* use the final posed locations */ + mat4_to_quat(q1, mat[0]); + mat4_to_quat(q2, mat[1]); + + invert_qt_normalized(q1); + mul_qt_qtqt(quat, q1, q2); + angle = 2.0f * (saacos(quat[0])); + angle = fabsf(angle); + + return (angle > (float)M_PI) ? (float)((2.0f * (float)M_PI) - angle) : (float)(angle); +} + +/* evaluate 'location difference' driver variable */ +/* TODO: this needs to take into account space conversions... */ +static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar) +{ + float loc1[3] = {0.0f, 0.0f, 0.0f}; + float loc2[3] = {0.0f, 0.0f, 0.0f}; + short valid_targets = driver_check_valid_targets(driver, dvar); + + /* make sure we have enough valid targets to use - all or nothing for now... */ + if (valid_targets < dvar->num_targets) { + if (G.debug & G_DEBUG) { + CLOG_WARN(&LOG, + "LocDiff DVar: not enough valid targets (n = %d) (a = %p, b = %p)", + valid_targets, + dvar->targets[0].id, + dvar->targets[1].id); + } + return 0.0f; + } + + /* SECOND PASS: get two location values */ + /* NOTE: for now, these are all just worldspace */ + DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { + /* get pointer to loc values to store in */ + Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + bPoseChannel *pchan; + float tmp_loc[3]; + + /* after the checks above, the targets should be valid here... */ + BLI_assert((ob != NULL) && (GS(ob->id.name) == ID_OB)); + + /* try to get posechannel */ + pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); + + /* check if object or bone */ + if (pchan) { + /* bone */ + if (dtar->flag & DTAR_FLAG_LOCALSPACE) { + if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { + float mat[4][4]; + + /* extract transform just like how the constraints do it! */ + copy_m4_m4(mat, pchan->pose_mat); + BKE_constraint_mat_convertspace( + ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false); + + /* ... and from that, we get our transform */ + copy_v3_v3(tmp_loc, mat[3]); + } + else { + /* transform space (use transform values directly) */ + copy_v3_v3(tmp_loc, pchan->loc); + } + } + else { + /* convert to worldspace */ + copy_v3_v3(tmp_loc, pchan->pose_head); + mul_m4_v3(ob->obmat, tmp_loc); + } + } + else { + /* object */ + if (dtar->flag & DTAR_FLAG_LOCALSPACE) { + if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { + /* XXX: this should practically be the same as transform space... */ + float mat[4][4]; + + /* extract transform just like how the constraints do it! */ + copy_m4_m4(mat, ob->obmat); + BKE_constraint_mat_convertspace( + ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false); + + /* ... and from that, we get our transform */ + copy_v3_v3(tmp_loc, mat[3]); + } + else { + /* transform space (use transform values directly) */ + copy_v3_v3(tmp_loc, ob->loc); + } + } + else { + /* worldspace */ + copy_v3_v3(tmp_loc, ob->obmat[3]); + } + } + + /* copy the location to the right place */ + if (tarIndex) { + copy_v3_v3(loc2, tmp_loc); + } + else { + copy_v3_v3(loc1, tmp_loc); + } + } + DRIVER_TARGETS_LOOPER_END; + + /* if we're still here, there should now be two targets to use, + * so just take the length of the vector between these points + */ + return len_v3v3(loc1, loc2); +} + +/* evaluate 'transform channel' driver variable */ +static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar) +{ + DriverTarget *dtar = &dvar->targets[0]; + Object *ob = (Object *)dtar_id_ensure_proxy_from(dtar->id); + bPoseChannel *pchan; + float mat[4][4]; + float oldEul[3] = {0.0f, 0.0f, 0.0f}; + bool use_eulers = false; + short rot_order = ROT_MODE_EUL; + + /* check if this target has valid data */ + if ((ob == NULL) || (GS(ob->id.name) != ID_OB)) { + /* invalid target, so will not have enough targets */ + driver->flag |= DRIVER_FLAG_INVALID; + dtar->flag |= DTAR_FLAG_INVALID; + return 0.0f; + } + else { + /* target should be valid now */ + dtar->flag &= ~DTAR_FLAG_INVALID; + } + + /* try to get posechannel */ + pchan = BKE_pose_channel_find_name(ob->pose, dtar->pchan_name); + + /* check if object or bone, and get transform matrix accordingly + * - "useEulers" code is used to prevent the problems associated with non-uniqueness + * of euler decomposition from matrices [#20870] + * - localspace is for [#21384], where parent results are not wanted + * but local-consts is for all the common "corrective-shapes-for-limbs" situations + */ + if (pchan) { + /* bone */ + if (pchan->rotmode > 0) { + copy_v3_v3(oldEul, pchan->eul); + rot_order = pchan->rotmode; + use_eulers = true; + } + + if (dtar->flag & DTAR_FLAG_LOCALSPACE) { + if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { + /* just like how the constraints do it! */ + copy_m4_m4(mat, pchan->pose_mat); + BKE_constraint_mat_convertspace( + ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false); + } + else { + /* specially calculate local matrix, since chan_mat is not valid + * since it stores delta transform of pose_mat so that deforms work + * so it cannot be used here for "transform" space + */ + BKE_pchan_to_mat4(pchan, mat); + } + } + else { + /* worldspace matrix */ + mul_m4_m4m4(mat, ob->obmat, pchan->pose_mat); + } + } + else { + /* object */ + if (ob->rotmode > 0) { + copy_v3_v3(oldEul, ob->rot); + rot_order = ob->rotmode; + use_eulers = true; + } + + if (dtar->flag & DTAR_FLAG_LOCALSPACE) { + if (dtar->flag & DTAR_FLAG_LOCAL_CONSTS) { + /* just like how the constraints do it! */ + copy_m4_m4(mat, ob->obmat); + BKE_constraint_mat_convertspace( + ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false); + } + else { + /* transforms to matrix */ + BKE_object_to_mat4(ob, mat); + } + } + else { + /* worldspace matrix - just the good-old one */ + copy_m4_m4(mat, ob->obmat); + } + } + + /* check which transform */ + if (dtar->transChan >= MAX_DTAR_TRANSCHAN_TYPES) { + /* not valid channel */ + return 0.0f; + } + else if (dtar->transChan == DTAR_TRANSCHAN_SCALE_AVG) { + /* Cubic root of the change in volume, equal to the geometric mean + * of scale over all three axes unless the matrix includes shear. */ + return cbrtf(mat4_to_volume_scale(mat)); + } + else if (ELEM(dtar->transChan, + DTAR_TRANSCHAN_SCALEX, + DTAR_TRANSCHAN_SCALEY, + DTAR_TRANSCHAN_SCALEZ)) { + /* Extract scale, and choose the right axis, + * inline 'mat4_to_size'. */ + return len_v3(mat[dtar->transChan - DTAR_TRANSCHAN_SCALEX]); + } + else if (dtar->transChan >= DTAR_TRANSCHAN_ROTX) { + /* extract rotation as eulers (if needed) + * - definitely if rotation order isn't eulers already + * - if eulers, then we have 2 options: + * a) decompose transform matrix as required, then try to make eulers from + * there compatible with original values + * b) [NOT USED] directly use the original values (no decomposition) + * - only an option for "transform space", if quality is really bad with a) + */ + float quat[4]; + int channel; + + if (dtar->transChan == DTAR_TRANSCHAN_ROTW) { + channel = 0; + } + else { + channel = 1 + dtar->transChan - DTAR_TRANSCHAN_ROTX; + BLI_assert(channel < 4); + } + + BKE_driver_target_matrix_to_rot_channels( + mat, rot_order, dtar->rotation_mode, channel, false, quat); + + if (use_eulers && dtar->rotation_mode == DTAR_ROTMODE_AUTO) { + compatible_eul(quat + 1, oldEul); + } + + return quat[channel]; + } + else { + /* extract location and choose right axis */ + return mat[3][dtar->transChan]; + } +} + +/* Convert a quaternion to pseudo-angles representing the weighted amount of rotation. */ +static void quaternion_to_angles(float quat[4], int channel) +{ + if (channel < 0) { + quat[0] = 2.0f * saacosf(quat[0]); + + for (int i = 1; i < 4; i++) { + quat[i] = 2.0f * saasinf(quat[i]); + } + } + else if (channel == 0) { + quat[0] = 2.0f * saacosf(quat[0]); + } + else { + quat[channel] = 2.0f * saasinf(quat[channel]); + } +} + +/* Compute channel values for a rotational Transform Channel driver variable. */ +void BKE_driver_target_matrix_to_rot_channels( + float mat[4][4], int auto_order, int rotation_mode, int channel, bool angles, float r_buf[4]) +{ + float *const quat = r_buf; + float *const eul = r_buf + 1; + + zero_v4(r_buf); + + if (rotation_mode == DTAR_ROTMODE_AUTO) { + mat4_to_eulO(eul, auto_order, mat); + } + else if (rotation_mode >= DTAR_ROTMODE_EULER_MIN && rotation_mode <= DTAR_ROTMODE_EULER_MAX) { + mat4_to_eulO(eul, rotation_mode, mat); + } + else if (rotation_mode == DTAR_ROTMODE_QUATERNION) { + mat4_to_quat(quat, mat); + + /* For Transformation constraint convenience, convert to pseudo-angles. */ + if (angles) { + quaternion_to_angles(quat, channel); + } + } + else if (rotation_mode >= DTAR_ROTMODE_SWING_TWIST_X && + rotation_mode <= DTAR_ROTMODE_SWING_TWIST_Z) { + int axis = rotation_mode - DTAR_ROTMODE_SWING_TWIST_X; + float raw_quat[4], twist; + + mat4_to_quat(raw_quat, mat); + + if (channel == axis + 1) { + /* If only the twist angle is needed, skip computing swing. */ + twist = quat_split_swing_and_twist(raw_quat, axis, NULL, NULL); + } + else { + twist = quat_split_swing_and_twist(raw_quat, axis, quat, NULL); + + quaternion_to_angles(quat, channel); + } + + quat[axis + 1] = twist; + } + else { + BLI_assert(false); + } +} + +/* ......... */ + +/* Table of Driver Variable Type Info Data */ +static DriverVarTypeInfo dvar_types[MAX_DVAR_TYPES] = { + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_SINGLE_PROP) dvar_eval_singleProp, /* eval callback */ + 1, /* number of targets used */ + {"Property"}, /* UI names for targets */ + {0} /* flags */ + END_DVAR_TYPEDEF, + + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_ROT_DIFF) dvar_eval_rotDiff, /* eval callback */ + 2, /* number of targets used */ + {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ + {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, + DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ + END_DVAR_TYPEDEF, + + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_LOC_DIFF) dvar_eval_locDiff, /* eval callback */ + 2, /* number of targets used */ + {"Object/Bone 1", "Object/Bone 2"}, /* UI names for targets */ + {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY, + DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ + END_DVAR_TYPEDEF, + + BEGIN_DVAR_TYPEDEF(DVAR_TYPE_TRANSFORM_CHAN) dvar_eval_transChan, /* eval callback */ + 1, /* number of targets used */ + {"Object/Bone"}, /* UI names for targets */ + {DTAR_FLAG_STRUCT_REF | DTAR_FLAG_ID_OB_ONLY} /* flags */ + END_DVAR_TYPEDEF, +}; + +/* Get driver variable typeinfo */ +static const DriverVarTypeInfo *get_dvar_typeinfo(int type) +{ + /* check if valid type */ + if ((type >= 0) && (type < MAX_DVAR_TYPES)) { + return &dvar_types[type]; + } + else { + return NULL; + } +} + +/* Driver API --------------------------------- */ + +/* Perform actual freeing driver variable and remove it from the given list */ +void driver_free_variable(ListBase *variables, DriverVar *dvar) +{ + /* sanity checks */ + if (dvar == NULL) { + return; + } + + /* free target vars + * - need to go over all of them, not just up to the ones that are used + * currently, since there may be some lingering RNA paths from + * previous users needing freeing + */ + DRIVER_TARGETS_LOOPER_BEGIN (dvar) { + /* free RNA path if applicable */ + if (dtar->rna_path) { + MEM_freeN(dtar->rna_path); + } + } + DRIVER_TARGETS_LOOPER_END; + + /* remove the variable from the driver */ + BLI_freelinkN(variables, dvar); +} + +/* Free the driver variable and do extra updates */ +void driver_free_variable_ex(ChannelDriver *driver, DriverVar *dvar) +{ + /* remove and free the driver variable */ + driver_free_variable(&driver->variables, dvar); + + /* since driver variables are cached, the expression needs re-compiling too */ + BKE_driver_invalidate_expression(driver, false, true); +} + +/* Copy driver variables from src_vars list to dst_vars list */ +void driver_variables_copy(ListBase *dst_vars, const ListBase *src_vars) +{ + BLI_assert(BLI_listbase_is_empty(dst_vars)); + BLI_duplicatelist(dst_vars, src_vars); + + LISTBASE_FOREACH (DriverVar *, dvar, dst_vars) { + /* need to go over all targets so that we don't leave any dangling paths */ + DRIVER_TARGETS_LOOPER_BEGIN (dvar) { + /* make a copy of target's rna path if available */ + if (dtar->rna_path) { + dtar->rna_path = MEM_dupallocN(dtar->rna_path); + } + } + DRIVER_TARGETS_LOOPER_END; + } +} + +/* Change the type of driver variable */ +void driver_change_variable_type(DriverVar *dvar, int type) +{ + const DriverVarTypeInfo *dvti = get_dvar_typeinfo(type); + + /* sanity check */ + if (ELEM(NULL, dvar, dvti)) { + return; + } + + /* set the new settings */ + dvar->type = type; + dvar->num_targets = dvti->num_targets; + + /* make changes to the targets based on the defines for these types + * NOTE: only need to make sure the ones we're using here are valid... + */ + DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { + short flags = dvti->target_flags[tarIndex]; + + /* store the flags */ + dtar->flag = flags; + + /* object ID types only, or idtype not yet initialized */ + if ((flags & DTAR_FLAG_ID_OB_ONLY) || (dtar->idtype == 0)) { + dtar->idtype = ID_OB; + } + } + DRIVER_TARGETS_LOOPER_END; +} + +/* Validate driver name (after being renamed) */ +void driver_variable_name_validate(DriverVar *dvar) +{ + /* Special character blacklist */ + const char special_char_blacklist[] = { + '~', '`', '!', '@', '#', '$', '%', '^', '&', '*', '+', '=', '-', '/', '\\', + '?', ':', ';', '<', '>', '{', '}', '[', ']', '|', ' ', '.', '\t', '\n', '\r', + }; + + /* sanity checks */ + if (dvar == NULL) { + return; + } + + /* clear all invalid-name flags */ + dvar->flag &= ~DVAR_ALL_INVALID_FLAGS; + + /* 0) Zero-length identifiers are not allowed */ + if (dvar->name[0] == '\0') { + dvar->flag |= DVAR_FLAG_INVALID_EMPTY; + } + + /* 1) Must start with a letter */ + /* XXX: We assume that valid unicode letters in other languages are ok too, + * hence the blacklisting. */ + if (IN_RANGE_INCL(dvar->name[0], '0', '9')) { + dvar->flag |= DVAR_FLAG_INVALID_START_NUM; + } + else if (dvar->name[0] == '_') { + /* NOTE: We don't allow names to start with underscores + * (i.e. it helps when ruling out security risks) */ + dvar->flag |= DVAR_FLAG_INVALID_START_CHAR; + } + + /* 2) Must not contain invalid stuff in the middle of the string */ + if (strchr(dvar->name, ' ')) { + dvar->flag |= DVAR_FLAG_INVALID_HAS_SPACE; + } + if (strchr(dvar->name, '.')) { + dvar->flag |= DVAR_FLAG_INVALID_HAS_DOT; + } + + /* 3) Check for special characters - Either at start, or in the middle */ + for (int i = 0; i < sizeof(special_char_blacklist); i++) { + char *match = strchr(dvar->name, special_char_blacklist[i]); + + if (match == dvar->name) { + dvar->flag |= DVAR_FLAG_INVALID_START_CHAR; + } + else if (match != NULL) { + dvar->flag |= DVAR_FLAG_INVALID_HAS_SPECIAL; + } + } + + /* 4) Check if the name is a reserved keyword + * NOTE: These won't confuse Python, but it will be impossible to use the variable + * in an expression without Python misinterpreting what these are for + */ +#ifdef WITH_PYTHON + if (BPY_string_is_keyword(dvar->name)) { + dvar->flag |= DVAR_FLAG_INVALID_PY_KEYWORD; + } +#endif + + /* If any these conditions match, the name is invalid */ + if (dvar->flag & DVAR_ALL_INVALID_FLAGS) { + dvar->flag |= DVAR_FLAG_INVALID_NAME; + } +} + +/* Add a new driver variable */ +DriverVar *driver_add_new_variable(ChannelDriver *driver) +{ + DriverVar *dvar; + + /* sanity checks */ + if (driver == NULL) { + return NULL; + } + + /* make a new variable */ + dvar = MEM_callocN(sizeof(DriverVar), "DriverVar"); + BLI_addtail(&driver->variables, dvar); + + /* give the variable a 'unique' name */ + strcpy(dvar->name, CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var")); + BLI_uniquename(&driver->variables, + dvar, + CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "var"), + '_', + offsetof(DriverVar, name), + sizeof(dvar->name)); + + /* set the default type to 'single prop' */ + driver_change_variable_type(dvar, DVAR_TYPE_SINGLE_PROP); + + /* since driver variables are cached, the expression needs re-compiling too */ + BKE_driver_invalidate_expression(driver, false, true); + + /* return the target */ + return dvar; +} + +/* This frees the driver itself */ +void fcurve_free_driver(FCurve *fcu) +{ + ChannelDriver *driver; + DriverVar *dvar, *dvarn; + + /* sanity checks */ + if (ELEM(NULL, fcu, fcu->driver)) { + return; + } + driver = fcu->driver; + + /* free driver targets */ + for (dvar = driver->variables.first; dvar; dvar = dvarn) { + dvarn = dvar->next; + driver_free_variable_ex(driver, dvar); + } + +#ifdef WITH_PYTHON + /* free compiled driver expression */ + if (driver->expr_comp) { + BPY_DECREF(driver->expr_comp); + } +#endif + + BLI_expr_pylike_free(driver->expr_simple); + + /* Free driver itself, then set F-Curve's point to this to NULL + * (as the curve may still be used). */ + MEM_freeN(driver); + fcu->driver = NULL; +} + +/* This makes a copy of the given driver */ +ChannelDriver *fcurve_copy_driver(const ChannelDriver *driver) +{ + ChannelDriver *ndriver; + + /* sanity checks */ + if (driver == NULL) { + return NULL; + } + + /* copy all data */ + ndriver = MEM_dupallocN(driver); + ndriver->expr_comp = NULL; + ndriver->expr_simple = NULL; + + /* copy variables */ + + /* to get rid of refs to non-copied data (that's still used on original) */ + BLI_listbase_clear(&ndriver->variables); + driver_variables_copy(&ndriver->variables, &driver->variables); + + /* return the new driver */ + return ndriver; +} + +/* Driver Expression Evaluation --------------- */ + +/* Index constants for the expression parameter array. */ +enum { + /* Index of the 'frame' variable. */ + VAR_INDEX_FRAME = 0, + /* Index of the first user-defined driver variable. */ + VAR_INDEX_CUSTOM +}; + +static ExprPyLike_Parsed *driver_compile_simple_expr_impl(ChannelDriver *driver) +{ + /* Prepare parameter names. */ + int names_len = BLI_listbase_count(&driver->variables); + const char **names = BLI_array_alloca(names, names_len + VAR_INDEX_CUSTOM); + int i = VAR_INDEX_CUSTOM; + + names[VAR_INDEX_FRAME] = "frame"; + + LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) { + names[i++] = dvar->name; + } + + return BLI_expr_pylike_parse(driver->expression, names, names_len + VAR_INDEX_CUSTOM); +} + +static bool driver_check_simple_expr_depends_on_time(ExprPyLike_Parsed *expr) +{ + /* Check if the 'frame' parameter is actually used. */ + return BLI_expr_pylike_is_using_param(expr, VAR_INDEX_FRAME); +} + +static bool driver_evaluate_simple_expr(ChannelDriver *driver, + ExprPyLike_Parsed *expr, + float *result, + float time) +{ + /* Prepare parameter values. */ + int vars_len = BLI_listbase_count(&driver->variables); + double *vars = BLI_array_alloca(vars, vars_len + VAR_INDEX_CUSTOM); + int i = VAR_INDEX_CUSTOM; + + vars[VAR_INDEX_FRAME] = time; + + LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) { + vars[i++] = driver_get_variable_value(driver, dvar); + } + + /* Evaluate expression. */ + double result_val; + eExprPyLike_EvalStatus status = BLI_expr_pylike_eval( + expr, vars, vars_len + VAR_INDEX_CUSTOM, &result_val); + const char *message; + + switch (status) { + case EXPR_PYLIKE_SUCCESS: + if (isfinite(result_val)) { + *result = (float)result_val; + } + return true; + + case EXPR_PYLIKE_DIV_BY_ZERO: + case EXPR_PYLIKE_MATH_ERROR: + message = (status == EXPR_PYLIKE_DIV_BY_ZERO) ? "Division by Zero" : "Math Domain Error"; + CLOG_ERROR(&LOG, "%s in Driver: '%s'", message, driver->expression); + + driver->flag |= DRIVER_FLAG_INVALID; + return true; + + default: + /* arriving here means a bug, not user error */ + CLOG_ERROR(&LOG, "simple driver expression evaluation failed: '%s'", driver->expression); + return false; + } +} + +/* Compile and cache the driver expression if necessary, with thread safety. */ +static bool driver_compile_simple_expr(ChannelDriver *driver) +{ + if (driver->expr_simple != NULL) { + return true; + } + + if (driver->type != DRIVER_TYPE_PYTHON) { + return false; + } + + /* It's safe to parse in multiple threads; at worst it'll + * waste some effort, but in return avoids mutex contention. */ + ExprPyLike_Parsed *expr = driver_compile_simple_expr_impl(driver); + + /* Store the result if the field is still NULL, or discard + * it if another thread got here first. */ + if (atomic_cas_ptr((void **)&driver->expr_simple, NULL, expr) != NULL) { + BLI_expr_pylike_free(expr); + } + + return true; +} + +/* Try using the simple expression evaluator to compute the result of the driver. + * On success, stores the result and returns true; on failure result is set to 0. */ +static bool driver_try_evaluate_simple_expr(ChannelDriver *driver, + ChannelDriver *driver_orig, + float *result, + float time) +{ + *result = 0.0f; + + return driver_compile_simple_expr(driver_orig) && + BLI_expr_pylike_is_valid(driver_orig->expr_simple) && + driver_evaluate_simple_expr(driver, driver_orig->expr_simple, result, time); +} + +/* Check if the expression in the driver conforms to the simple subset. */ +bool BKE_driver_has_simple_expression(ChannelDriver *driver) +{ + return driver_compile_simple_expr(driver) && BLI_expr_pylike_is_valid(driver->expr_simple); +} + +/* TODO(sergey): This is somewhat weak, but we don't want neither false-positive + * time dependencies nor special exceptions in the depsgraph evaluation. */ +static bool python_driver_exression_depends_on_time(const char *expression) +{ + if (expression[0] == '\0') { + /* Empty expression depends on nothing. */ + return false; + } + if (strchr(expression, '(') != NULL) { + /* Function calls are considered dependent on a time. */ + return true; + } + if (strstr(expression, "frame") != NULL) { + /* Variable `frame` depends on time. */ + /* TODO(sergey): This is a bit weak, but not sure about better way of handling this. */ + return true; + } + /* Possible indirect time relation s should be handled via variable targets. */ + return false; +} + +/* Check if the expression in the driver may depend on the current frame. */ +bool BKE_driver_expression_depends_on_time(ChannelDriver *driver) +{ + if (driver->type != DRIVER_TYPE_PYTHON) { + return false; + } + + if (BKE_driver_has_simple_expression(driver)) { + /* Simple expressions can be checked exactly. */ + return driver_check_simple_expr_depends_on_time(driver->expr_simple); + } + else { + /* Otherwise, heuristically scan the expression string for certain patterns. */ + return python_driver_exression_depends_on_time(driver->expression); + } +} + +/* Reset cached compiled expression data */ +void BKE_driver_invalidate_expression(ChannelDriver *driver, + bool expr_changed, + bool varname_changed) +{ + if (expr_changed || varname_changed) { + BLI_expr_pylike_free(driver->expr_simple); + driver->expr_simple = NULL; + } + +#ifdef WITH_PYTHON + if (expr_changed) { + driver->flag |= DRIVER_FLAG_RECOMPILE; + } + + if (varname_changed) { + driver->flag |= DRIVER_FLAG_RENAMEVAR; + } +#endif +} + +/* Driver Evaluation -------------------------- */ + +/* Evaluate a Driver Variable to get a value that contributes to the final */ +float driver_get_variable_value(ChannelDriver *driver, DriverVar *dvar) +{ + const DriverVarTypeInfo *dvti; + + /* sanity check */ + if (ELEM(NULL, driver, dvar)) { + return 0.0f; + } + + /* call the relevant callbacks to get the variable value + * using the variable type info, storing the obtained value + * in dvar->curval so that drivers can be debugged + */ + dvti = get_dvar_typeinfo(dvar->type); + + if (dvti && dvti->get_value) { + dvar->curval = dvti->get_value(driver, dvar); + } + else { + dvar->curval = 0.0f; + } + + return dvar->curval; +} + +static void evaluate_driver_sum(ChannelDriver *driver) +{ + DriverVar *dvar; + + /* check how many variables there are first (i.e. just one?) */ + if (BLI_listbase_is_single(&driver->variables)) { + /* just one target, so just use that */ + dvar = driver->variables.first; + driver->curval = driver_get_variable_value(driver, dvar); + return; + } + + /* more than one target, so average the values of the targets */ + float value = 0.0f; + int tot = 0; + + /* loop through targets, adding (hopefully we don't get any overflow!) */ + for (dvar = driver->variables.first; dvar; dvar = dvar->next) { + value += driver_get_variable_value(driver, dvar); + tot++; + } + + /* perform operations on the total if appropriate */ + if (driver->type == DRIVER_TYPE_AVERAGE) { + driver->curval = tot ? (value / (float)tot) : 0.0f; + } + else { + driver->curval = value; + } +} + +static void evaluate_driver_min_max(ChannelDriver *driver) +{ + DriverVar *dvar; + float value = 0.0f; + + /* loop through the variables, getting the values and comparing them to existing ones */ + for (dvar = driver->variables.first; dvar; dvar = dvar->next) { + /* get value */ + float tmp_val = driver_get_variable_value(driver, dvar); + + /* store this value if appropriate */ + if (dvar->prev) { + /* check if greater/smaller than the baseline */ + if (driver->type == DRIVER_TYPE_MAX) { + /* max? */ + if (tmp_val > value) { + value = tmp_val; + } + } + else { + /* min? */ + if (tmp_val < value) { + value = tmp_val; + } + } + } + else { + /* first item - make this the baseline for comparisons */ + value = tmp_val; + } + } + + /* store value in driver */ + driver->curval = value; +} + +static void evaluate_driver_python(PathResolvedRNA *anim_rna, + ChannelDriver *driver, + ChannelDriver *driver_orig, + const float evaltime) +{ + /* check for empty or invalid expression */ + if ((driver_orig->expression[0] == '\0') || (driver_orig->flag & DRIVER_FLAG_INVALID)) { + driver->curval = 0.0f; + } + else if (!driver_try_evaluate_simple_expr(driver, driver_orig, &driver->curval, evaltime)) { +#ifdef WITH_PYTHON + /* this evaluates the expression using Python, and returns its result: + * - on errors it reports, then returns 0.0f + */ + BLI_mutex_lock(&python_driver_lock); + + driver->curval = BPY_driver_exec(anim_rna, driver, driver_orig, evaltime); + + BLI_mutex_unlock(&python_driver_lock); +#else /* WITH_PYTHON*/ + UNUSED_VARS(anim_rna, evaltime); +#endif /* WITH_PYTHON*/ + } +} + +/* Evaluate an Channel-Driver to get a 'time' value to use instead of "evaltime" + * - "evaltime" is the frame at which F-Curve is being evaluated + * - has to return a float value + * - driver_orig is where we cache Python expressions, in case of COW + */ +float evaluate_driver(PathResolvedRNA *anim_rna, + ChannelDriver *driver, + ChannelDriver *driver_orig, + const float evaltime) +{ + /* check if driver can be evaluated */ + if (driver_orig->flag & DRIVER_FLAG_INVALID) { + return 0.0f; + } + + switch (driver->type) { + case DRIVER_TYPE_AVERAGE: /* average values of driver targets */ + case DRIVER_TYPE_SUM: /* sum values of driver targets */ + evaluate_driver_sum(driver); + break; + case DRIVER_TYPE_MIN: /* smallest value */ + case DRIVER_TYPE_MAX: /* largest value */ + evaluate_driver_min_max(driver); + break; + case DRIVER_TYPE_PYTHON: /* expression */ + evaluate_driver_python(anim_rna, driver, driver_orig, evaltime); + break; + default: + /* special 'hack' - just use stored value + * This is currently used as the mechanism which allows animated settings to be able + * to be changed via the UI. + */ + break; + } + + /* return value for driver */ + return driver->curval; +} diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index d4f54daa773..5340fa63f38 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -81,6 +81,8 @@ # include "RE_shader_ext.h" +# include "CLG_log.h" + # include "manta_fluid_API.h" #endif /* WITH_FLUID */ @@ -96,6 +98,8 @@ static void BKE_fluid_modifier_reset_ex(struct FluidModifierData *mmd, bool need #ifdef WITH_FLUID // #define DEBUG_PRINT +static CLG_LogRef LOG = {"bke.fluid"}; + /* -------------------------------------------------------------------- */ /** \name Fluid API * \{ */ @@ -487,32 +491,6 @@ static void manta_set_domain_from_mesh(FluidDomainSettings *mds, mds->cell_size[2] /= (float)mds->base_res[2]; } -static void manta_set_domain_gravity(Scene *scene, FluidDomainSettings *mds) -{ - const float normalization_factor = 1.0f / 9.81f; - - /* Use global gravity if enabled. */ - if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) { - float gravity[3]; - copy_v3_v3(gravity, scene->physics_settings.gravity); - /* Map default value to 1.0. */ - mul_v3_fl(gravity, normalization_factor); - - /* Convert gravity to domain space. */ - float gravity_mag = len_v3(gravity); - mul_mat3_m4_v3(mds->imat, gravity); - normalize_v3(gravity); - mul_v3_fl(gravity, gravity_mag); - - copy_v3_v3(mds->gravity, gravity); - } - else { - mul_v3_fl(mds->gravity, normalization_factor); - } - - mul_v3_fl(mds->gravity, mds->effector_weights->global_gravity); -} - static bool BKE_fluid_modifier_init( FluidModifierData *mmd, Depsgraph *depsgraph, Object *ob, Scene *scene, Mesh *me) { @@ -523,8 +501,11 @@ static bool BKE_fluid_modifier_init( int res[3]; /* Set domain dimensions from mesh. */ manta_set_domain_from_mesh(mds, ob, me, true); - /* Set domain gravity. */ - manta_set_domain_gravity(scene, mds); + /* Set domain gravity, use global gravity if enabled. */ + if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) { + copy_v3_v3(mds->gravity, scene->physics_settings.gravity); + } + mul_v3_fl(mds->gravity, mds->effector_weights->global_gravity); /* Reset domain values. */ zero_v3_int(mds->shift); zero_v3(mds->shift_f); @@ -948,11 +929,7 @@ static void sample_effector(FluidEffectorSettings *mes, velocity_map[index * 3 + 2] += hit_vel[2]; # ifdef DEBUG_PRINT /* Debugging: Print object velocities. */ - printf("adding effector object vel: [%f, %f, %f], dx is: %f\n", - hit_vel[0], - hit_vel[1], - hit_vel[2], - mds->dx); + printf("adding effector object vel: [%f, %f, %f]\n", hit_vel[0], hit_vel[1], hit_vel[2]); # endif } } @@ -1996,9 +1973,9 @@ static void sample_mesh(FluidFlowSettings *mfs, normalize_v3(hit_normal); /* Apply normal directional velocity. */ - velocity_map[index * 3] += hit_normal[0] * mfs->vel_normal * 0.25f; - velocity_map[index * 3 + 1] += hit_normal[1] * mfs->vel_normal * 0.25f; - velocity_map[index * 3 + 2] += hit_normal[2] * mfs->vel_normal * 0.25f; + velocity_map[index * 3] += hit_normal[0] * mfs->vel_normal; + velocity_map[index * 3 + 1] += hit_normal[1] * mfs->vel_normal; + velocity_map[index * 3 + 2] += hit_normal[2] * mfs->vel_normal; } /* Apply object velocity. */ if (has_velocity && mfs->vel_multi) { @@ -3773,6 +3750,7 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd, /* Fluid domain init must not fail in order to continue modifier evaluation. */ if (!mds->fluid && !BKE_fluid_modifier_init(mmd, depsgraph, ob, scene, me)) { + CLOG_ERROR(&LOG, "Fluid initialization failed. Should not happen!"); return; } BLI_assert(mds->fluid); @@ -3794,6 +3772,12 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd, /* Get distance between cache start and current frame for total time. */ mds->time_total = abs(scene_framenr - mds->cache_frame_start) * mds->frame_length; + /* Ensure that gravity is copied over every frame (could be keyframed). */ + if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) { + copy_v3_v3(mds->gravity, scene->physics_settings.gravity); + mul_v3_fl(mds->gravity, mds->effector_weights->global_gravity); + } + int next_frame = scene_framenr + 1; int prev_frame = scene_framenr - 1; /* Ensure positivity of previous frame. */ @@ -4889,7 +4873,6 @@ void BKE_fluid_modifier_create_type_data(struct FluidModifierData *mmd) mmd->domain->surface_tension = 0.0f; mmd->domain->viscosity_base = 1.0f; mmd->domain->viscosity_exponent = 6.0f; - mmd->domain->domain_size = 0.5f; /* mesh options */ mmd->domain->mesh_velocities = NULL; @@ -5133,7 +5116,6 @@ void BKE_fluid_modifier_copy(const struct FluidModifierData *mmd, tmds->surface_tension = mds->surface_tension; tmds->viscosity_base = mds->viscosity_base; tmds->viscosity_exponent = mds->viscosity_exponent; - tmds->domain_size = mds->domain_size; /* mesh options */ if (mds->mesh_velocities) { diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c index 874672f4a73..413e28c431b 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.c +++ b/source/blender/blenkernel/intern/gpencil_geom.c @@ -1710,7 +1710,7 @@ static void gpencil_convert_spline(Main *bmain, /* Create Stroke. */ bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "bGPDstroke"); - gps->thickness = 1.0f; + gps->thickness = 10.0f; gps->fill_opacity_fac = 1.0f; gps->hardeness = 1.0f; gps->uv_scale = 1.0f; @@ -1731,7 +1731,7 @@ static void gpencil_convert_spline(Main *bmain, int segments = 0; int resolu = nu->resolu + 1; segments = nu->pntsu; - if (((nu->flagu & CU_NURB_CYCLIC) == 0) || (nu->pntsu == 2)) { + if ((nu->flagu & CU_NURB_CYCLIC) == 0) { segments--; cyclic = false; } @@ -1847,7 +1847,7 @@ static void gpencil_convert_spline(Main *bmain, int init = 0; resolu = nu->resolu + 1; segments = nu->pntsu; - if (((nu->flagu & CU_NURB_CYCLIC) == 0) || (nu->pntsu == 2)) { + if ((nu->flagu & CU_NURB_CYCLIC) == 0) { segments--; } /* Get all interpolated curve points of Beziert */ diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index 5530a126ffc..669539ca574 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -1130,4 +1130,45 @@ void IDP_Reset(IDProperty *prop, const IDProperty *reference) } } +/** + * Loop through all ID properties in hierarchy of given \a id_property_root included. + * + * \note Container types (groups and arrays) are processed after applying the callback on them. + * + * \param type_filter: If not 0, only apply callback on properties of matching types, see + * IDP_TYPE_FILTER_ enum in DNA_ID.h. + */ +void IDP_foreach_property(IDProperty *id_property_root, + const int type_filter, + IDPForeachPropertyCallback callback, + void *user_data) +{ + if (!id_property_root) { + return; + } + + if (type_filter == 0 || (1 << id_property_root->type) & type_filter) { + callback(id_property_root, user_data); + } + + /* Recursive call into container types of ID properties. */ + switch (id_property_root->type) { + case IDP_GROUP: { + LISTBASE_FOREACH (IDProperty *, loop, &id_property_root->data.group) { + IDP_foreach_property(loop, type_filter, callback, user_data); + } + break; + } + case IDP_IDPARRAY: { + IDProperty *loop = IDP_Array(id_property_root); + for (int i = 0; i < id_property_root->len; i++) { + IDP_foreach_property(&loop[i], type_filter, callback, user_data); + } + break; + } + default: + break; /* Nothing to do here with other types of IDProperties... */ + } +} + /** \} */ diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index ee1bd5afa61..f5b28355d85 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -571,7 +571,7 @@ ImageTile *BKE_image_get_tile(Image *ima, int tile_number) return NULL; } -ImageTile *BKE_image_get_tile_from_iuser(Image *ima, ImageUser *iuser) +ImageTile *BKE_image_get_tile_from_iuser(Image *ima, const ImageUser *iuser) { return BKE_image_get_tile(ima, (iuser && iuser->tile) ? iuser->tile : 1001); } @@ -4796,7 +4796,7 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_entry, return ibuf; } -BLI_INLINE bool image_quick_test(Image *ima, ImageUser *iuser) +BLI_INLINE bool image_quick_test(Image *ima, const ImageUser *iuser) { if (ima == NULL) { return false; diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index b0b88a13a75..12c1cf6bafa 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -60,6 +60,7 @@ #include "BKE_action.h" #include "BKE_anim_data.h" #include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_global.h" #include "BKE_idtype.h" #include "BKE_ipo.h" diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index bddf8c0f819..31ac8ca623b 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -68,7 +68,7 @@ #include "BKE_anim_data.h" #include "BKE_collection.h" #include "BKE_constraint.h" -#include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_gpencil_modifier.h" #include "BKE_idprop.h" #include "BKE_lib_id.h" @@ -173,41 +173,20 @@ static void library_foreach_ID_link(Main *bmain, int flag, LibraryForeachIDData *inherit_data); -static void library_foreach_idproperty_ID_link(LibraryForeachIDData *data, - IDProperty *prop, - int flag) +static void library_foreach_idpropertiesForeachIDLink(IDProperty *id_prop, void *user_data) { - if (!prop) { - return; - } + BLI_assert(id_prop->type == IDP_ID); - switch (prop->type) { - case IDP_GROUP: { - LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) { - library_foreach_idproperty_ID_link(data, loop, flag); - } - break; - } - case IDP_IDPARRAY: { - IDProperty *loop = IDP_Array(prop); - for (int i = 0; i < prop->len; i++) { - library_foreach_idproperty_ID_link(data, &loop[i], flag); - } - break; - } - case IDP_ID: - FOREACH_CALLBACK_INVOKE_ID(data, prop->data.pointer, flag); - break; - default: - break; /* Nothing to do here with other types of IDProperties... */ - } + LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; + FOREACH_CALLBACK_INVOKE_ID(data, id_prop->data.pointer, IDWALK_CB_USER); FOREACH_FINALIZE_VOID; } static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket *sock) { - library_foreach_idproperty_ID_link(data, sock->prop, IDWALK_CB_USER); + IDP_foreach_property( + sock->prop, IDP_TYPE_FILTER_ID, library_foreach_idpropertiesForeachIDLink, &data); switch ((eNodeSocketDatatype)sock->type) { case SOCK_OBJECT: { @@ -308,11 +287,9 @@ static void library_foreach_particlesystemsObjectLooper(ParticleSystem *UNUSED(p static void library_foreach_nla_strip(LibraryForeachIDData *data, NlaStrip *strip) { - NlaStrip *substrip; - FOREACH_CALLBACK_INVOKE(data, strip->act, IDWALK_CB_USER); - for (substrip = strip->strips.first; substrip; substrip = substrip->next) { + LISTBASE_FOREACH (NlaStrip *, substrip, &strip->strips) { library_foreach_nla_strip(data, substrip); } @@ -321,15 +298,10 @@ static void library_foreach_nla_strip(LibraryForeachIDData *data, NlaStrip *stri static void library_foreach_animationData(LibraryForeachIDData *data, AnimData *adt) { - FCurve *fcu; - NlaTrack *nla_track; - NlaStrip *nla_strip; - - for (fcu = adt->drivers.first; fcu; fcu = fcu->next) { + LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) { ChannelDriver *driver = fcu->driver; - DriverVar *dvar; - for (dvar = driver->variables.first; dvar; dvar = dvar->next) { + LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) { /* only used targets */ DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { FOREACH_CALLBACK_INVOKE_ID(data, dtar->id, IDWALK_CB_NOP); @@ -341,8 +313,8 @@ static void library_foreach_animationData(LibraryForeachIDData *data, AnimData * FOREACH_CALLBACK_INVOKE(data, adt->action, IDWALK_CB_USER); FOREACH_CALLBACK_INVOKE(data, adt->tmpact, IDWALK_CB_USER); - for (nla_track = adt->nla_tracks.first; nla_track; nla_track = nla_track->next) { - for (nla_strip = nla_track->strips.first; nla_strip; nla_strip = nla_strip->next) { + LISTBASE_FOREACH (NlaTrack *, nla_track, &adt->nla_tracks) { + LISTBASE_FOREACH (NlaStrip *, nla_strip, &nla_track->strips) { library_foreach_nla_strip(data, nla_strip); } } @@ -371,7 +343,8 @@ static void library_foreach_paint(LibraryForeachIDData *data, Paint *paint) static void library_foreach_bone(LibraryForeachIDData *data, Bone *bone) { - library_foreach_idproperty_ID_link(data, bone->prop, IDWALK_CB_USER); + IDP_foreach_property( + bone->prop, IDP_TYPE_FILTER_ID, library_foreach_idpropertiesForeachIDLink, data); LISTBASE_FOREACH (Bone *, curbone, &bone->childbase) { library_foreach_bone(data, curbone); @@ -518,7 +491,6 @@ static void library_foreach_screen_area(LibraryForeachIDData *data, ScrArea *are } case SPACE_NODE: { SpaceNode *snode = (SpaceNode *)sl; - bNodeTreePath *path; const bool is_private_nodetree = snode->id != NULL && ntreeFromID(snode->id) == snode->nodetree; @@ -529,7 +501,7 @@ static void library_foreach_screen_area(LibraryForeachIDData *data, ScrArea *are FOREACH_CALLBACK_INVOKE( data, snode->nodetree, is_private_nodetree ? IDWALK_CB_EMBEDDED : IDWALK_CB_USER_ONE); - for (path = snode->treepath.first; path; path = path->next) { + LISTBASE_FOREACH (bNodeTreePath *, path, &snode->treepath) { if (path == snode->treepath.first) { /* first nodetree in path is same as snode->nodetree */ FOREACH_CALLBACK_INVOKE(data, @@ -563,14 +535,12 @@ static void library_foreach_screen_area(LibraryForeachIDData *data, ScrArea *are FOREACH_FINALIZE_VOID; } -static void library_foreach_ID_as_subdata_link(ID **id_pp, - LibraryIDLinkCallback callback, - void *user_data, - int flag, - LibraryForeachIDData *data) +static void library_foreach_ID_as_subdata_link(ID **id_pp, LibraryForeachIDData *data) { /* Needed e.g. for callbacks handling relationships... This call shall be absolutely readonly. */ ID *id = *id_pp; + const int flag = data->flag; + FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pp, IDWALK_CB_EMBEDDED); BLI_assert(id == *id_pp); @@ -587,7 +557,8 @@ static void library_foreach_ID_as_subdata_link(ID **id_pp, } } else { - library_foreach_ID_link(data->bmain, data->owner_id, id, callback, user_data, flag, data); + library_foreach_ID_link( + data->bmain, data->owner_id, id, data->callback, data->user_data, data->flag, data); } FOREACH_FINALIZE_VOID; @@ -677,7 +648,8 @@ static void library_foreach_ID_link(Main *bmain, IDWALK_CB_USER | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE); } - library_foreach_idproperty_ID_link(&data, id->properties, IDWALK_CB_USER); + IDP_foreach_property( + id->properties, IDP_TYPE_FILTER_ID, library_foreach_idpropertiesForeachIDLink, &data); AnimData *adt = BKE_animdata_from_id(id); if (adt) { @@ -702,8 +674,7 @@ static void library_foreach_ID_link(Main *bmain, CALLBACK_INVOKE(scene->r.bake.cage_object, IDWALK_CB_NOP); if (scene->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link( - (ID **)&scene->nodetree, callback, user_data, flag, &data); + library_foreach_ID_as_subdata_link((ID **)&scene->nodetree, &data); } if (scene->ed) { Sequence *seq; @@ -713,7 +684,8 @@ static void library_foreach_ID_link(Main *bmain, CALLBACK_INVOKE(seq->clip, IDWALK_CB_USER); CALLBACK_INVOKE(seq->mask, IDWALK_CB_USER); CALLBACK_INVOKE(seq->sound, IDWALK_CB_USER); - library_foreach_idproperty_ID_link(&data, seq->prop, IDWALK_CB_USER); + IDP_foreach_property( + seq->prop, IDP_TYPE_FILTER_ID, library_foreach_idpropertiesForeachIDLink, &data); LISTBASE_FOREACH (SequenceModifierData *, smd, &seq->modifiers) { CALLBACK_INVOKE(smd->mask_id, IDWALK_CB_USER); } @@ -731,8 +703,7 @@ static void library_foreach_ID_link(Main *bmain, library_foreach_collection(&data, scene->master_collection); } - ViewLayer *view_layer; - for (view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) { + LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { CALLBACK_INVOKE(view_layer->mat_override, IDWALK_CB_USER); LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { @@ -811,7 +782,6 @@ static void library_foreach_ID_link(Main *bmain, case ID_OB: { Object *object = (Object *)id; - ParticleSystem *psys; /* Object is special, proxies make things hard... */ const int data_cb_flag = data.cb_flag; @@ -867,11 +837,10 @@ static void library_foreach_ID_link(Main *bmain, /* Note that ob->effect is deprecated, so no need to handle it here. */ if (object->pose) { - bPoseChannel *pchan; - data.cb_flag |= proxy_cb_flag; - for (pchan = object->pose->chanbase.first; pchan; pchan = pchan->next) { - library_foreach_idproperty_ID_link(&data, pchan->prop, IDWALK_CB_USER); + LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { + IDP_foreach_property( + pchan->prop, IDP_TYPE_FILTER_ID, library_foreach_idpropertiesForeachIDLink, &data); CALLBACK_INVOKE(pchan->custom, IDWALK_CB_USER); BKE_constraints_id_loop( &pchan->constraints, library_foreach_constraintObjectLooper, &data); @@ -885,8 +854,7 @@ static void library_foreach_ID_link(Main *bmain, } if (object->lodlevels.first) { - LodLevel *level; - for (level = object->lodlevels.first; level; level = level->next) { + LISTBASE_FOREACH (LodLevel *, level, &object->lodlevels) { CALLBACK_INVOKE(level->source, IDWALK_CB_NEVER_SELF); } } @@ -898,7 +866,7 @@ static void library_foreach_ID_link(Main *bmain, &object->constraints, library_foreach_constraintObjectLooper, &data); BKE_shaderfx_foreachIDLink(object, library_foreach_shaderfxForeachIDLink, &data); - for (psys = object->particlesystem.first; psys; psys = psys->next) { + LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) { BKE_particlesystem_id_loop(psys, library_foreach_particlesystemsObjectLooper, &data); } @@ -959,8 +927,7 @@ static void library_foreach_ID_link(Main *bmain, Material *material = (Material *)id; if (material->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link( - (ID **)&material->nodetree, callback, user_data, flag, &data); + library_foreach_ID_as_subdata_link((ID **)&material->nodetree, &data); } if (material->texpaintslot != NULL) { CALLBACK_INVOKE(material->texpaintslot->ima, IDWALK_CB_NOP); @@ -976,8 +943,7 @@ static void library_foreach_ID_link(Main *bmain, Tex *texture = (Tex *)id; if (texture->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link( - (ID **)&texture->nodetree, callback, user_data, flag, &data); + library_foreach_ID_as_subdata_link((ID **)&texture->nodetree, &data); } CALLBACK_INVOKE(texture->ima, IDWALK_CB_USER); break; @@ -993,8 +959,7 @@ static void library_foreach_ID_link(Main *bmain, Light *lamp = (Light *)id; if (lamp->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link( - (ID **)&lamp->nodetree, callback, user_data, flag, &data); + library_foreach_ID_as_subdata_link((ID **)&lamp->nodetree, &data); } break; } @@ -1024,8 +989,7 @@ static void library_foreach_ID_link(Main *bmain, World *world = (World *)id; if (world->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link( - (ID **)&world->nodetree, callback, user_data, flag, &data); + library_foreach_ID_as_subdata_link((ID **)&world->nodetree, &data); } break; } @@ -1051,14 +1015,14 @@ static void library_foreach_ID_link(Main *bmain, case ID_NT: { bNodeTree *ntree = (bNodeTree *)id; - bNode *node; CALLBACK_INVOKE(ntree->gpd, IDWALK_CB_USER); - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { CALLBACK_INVOKE_ID(node->id, IDWALK_CB_USER); - library_foreach_idproperty_ID_link(&data, node->prop, IDWALK_CB_USER); + IDP_foreach_property( + node->prop, IDP_TYPE_FILTER_ID, library_foreach_idpropertiesForeachIDLink, &data); LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { library_foreach_node_socket(&data, sock); } @@ -1116,11 +1080,8 @@ static void library_foreach_ID_link(Main *bmain, } if (psett->boids) { - BoidState *state; - BoidRule *rule; - - for (state = psett->boids->states.first; state; state = state->next) { - for (rule = state->rules.first; rule; rule = rule->next) { + LISTBASE_FOREACH (BoidState *, state, &psett->boids->states) { + LISTBASE_FOREACH (BoidRule *, rule, &state->rules) { if (rule->type == eBoidRuleType_Avoid) { BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid *)rule; CALLBACK_INVOKE(gabr->ob, IDWALK_CB_NOP); @@ -1142,23 +1103,19 @@ static void library_foreach_ID_link(Main *bmain, case ID_MC: { MovieClip *clip = (MovieClip *)id; MovieTracking *tracking = &clip->tracking; - MovieTrackingObject *object; - MovieTrackingTrack *track; - MovieTrackingPlaneTrack *plane_track; CALLBACK_INVOKE(clip->gpd, IDWALK_CB_USER); - for (track = tracking->tracks.first; track; track = track->next) { + LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking->tracks) { CALLBACK_INVOKE(track->gpd, IDWALK_CB_USER); } - for (object = tracking->objects.first; object; object = object->next) { - for (track = object->tracks.first; track; track = track->next) { + LISTBASE_FOREACH (MovieTrackingObject *, object, &tracking->objects) { + LISTBASE_FOREACH (MovieTrackingTrack *, track, &object->tracks) { CALLBACK_INVOKE(track->gpd, IDWALK_CB_USER); } } - for (plane_track = tracking->plane_tracks.first; plane_track; - plane_track = plane_track->next) { + LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, &tracking->plane_tracks) { CALLBACK_INVOKE(plane_track->image, IDWALK_CB_USER); } break; @@ -1166,12 +1123,9 @@ static void library_foreach_ID_link(Main *bmain, case ID_MSK: { Mask *mask = (Mask *)id; - MaskLayer *mask_layer; - for (mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { - MaskSpline *mask_spline; - for (mask_spline = mask_layer->splines.first; mask_spline; - mask_spline = mask_spline->next) { + LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { + LISTBASE_FOREACH (MaskSpline *, mask_spline, &mask_layer->splines) { for (i = 0; i < mask_spline->tot_point; i++) { MaskSplinePoint *point = &mask_spline->points[i]; CALLBACK_INVOKE_ID(point->parent.id, IDWALK_CB_USER); @@ -1183,7 +1137,7 @@ static void library_foreach_ID_link(Main *bmain, case ID_LS: { FreestyleLineStyle *linestyle = (FreestyleLineStyle *)id; - LineStyleModifier *lsm; + for (i = 0; i < MAX_MTEX; i++) { if (linestyle->mtex[i]) { library_foreach_mtex(&data, linestyle->mtex[i]); @@ -1191,11 +1145,10 @@ static void library_foreach_ID_link(Main *bmain, } if (linestyle->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link( - (ID **)&linestyle->nodetree, callback, user_data, flag, &data); + library_foreach_ID_as_subdata_link((ID **)&linestyle->nodetree, &data); } - for (lsm = linestyle->color_modifiers.first; lsm; lsm = lsm->next) { + LISTBASE_FOREACH (LineStyleModifier *, lsm, &linestyle->color_modifiers) { if (lsm->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { LineStyleColorModifier_DistanceFromObject *p = (LineStyleColorModifier_DistanceFromObject *)lsm; @@ -1204,7 +1157,7 @@ static void library_foreach_ID_link(Main *bmain, } } } - for (lsm = linestyle->alpha_modifiers.first; lsm; lsm = lsm->next) { + LISTBASE_FOREACH (LineStyleModifier *, lsm, &linestyle->alpha_modifiers) { if (lsm->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { LineStyleAlphaModifier_DistanceFromObject *p = (LineStyleAlphaModifier_DistanceFromObject *)lsm; @@ -1213,7 +1166,7 @@ static void library_foreach_ID_link(Main *bmain, } } } - for (lsm = linestyle->thickness_modifiers.first; lsm; lsm = lsm->next) { + LISTBASE_FOREACH (LineStyleModifier *, lsm, &linestyle->thickness_modifiers) { if (lsm->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { LineStyleThicknessModifier_DistanceFromObject *p = (LineStyleThicknessModifier_DistanceFromObject *)lsm; @@ -1280,8 +1233,7 @@ static void library_foreach_ID_link(Main *bmain, CALLBACK_INVOKE(gpencil->mat[i], IDWALK_CB_USER); } - for (bGPDlayer *gplayer = gpencil->layers.first; gplayer != NULL; - gplayer = gplayer->next) { + LISTBASE_FOREACH (bGPDlayer *, gplayer, &gpencil->layers) { CALLBACK_INVOKE(gplayer->parent, IDWALK_CB_NOP); } @@ -1323,8 +1275,7 @@ static void library_foreach_ID_link(Main *bmain, Simulation *simulation = (Simulation *)id; if (simulation->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link( - (ID **)&simulation->nodetree, callback, user_data, flag, &data); + library_foreach_ID_as_subdata_link((ID **)&simulation->nodetree, &data); } break; } @@ -1382,14 +1333,11 @@ void BKE_library_update_ID_link_user(ID *id_dst, ID *id_src, const int cb_flag) } /** - * Say whether given \a id_type_owner can use (in any way) a data-block of \a id_type_used. + * Say whether given \a id_owner may use (in any way) a data-block of \a id_type_used. * * This is a 'simplified' abstract version of #BKE_library_foreach_ID_link() above, - * quite useful to reduce* useless iterations in some cases. + * quite useful to reduce useless iterations in some cases. */ -/* XXX This has to be fully rethink, basing check on ID type is not really working anymore - * (and even worth once IDProps will support ID pointers), - * we'll have to do some quick checks on IDs themselves... */ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) { /* any type of ID can be used in custom props. */ diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c index 78991ad063d..f0d19f01aab 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.c +++ b/source/blender/blenkernel/intern/mesh_evaluate.c @@ -1300,7 +1300,7 @@ static void loop_split_worker_do(LoopSplitTaskDataCommon *common_data, } } -static void loop_split_worker(TaskPool *__restrict pool, void *taskdata, int UNUSED(threadid)) +static void loop_split_worker(TaskPool *__restrict pool, void *taskdata) { LoopSplitTaskDataCommon *common_data = BLI_task_pool_user_data(pool); LoopSplitTaskData *data = taskdata; @@ -1704,11 +1704,7 @@ void BKE_mesh_normals_loop_split(const MVert *mverts, loop_split_generator(NULL, &common_data); } else { - TaskScheduler *task_scheduler; - TaskPool *task_pool; - - task_scheduler = BLI_task_scheduler_get(); - task_pool = BLI_task_pool_create(task_scheduler, &common_data, TASK_PRIORITY_HIGH); + TaskPool *task_pool = BLI_task_pool_create(&common_data, TASK_PRIORITY_HIGH); loop_split_generator(task_pool, &common_data); diff --git a/source/blender/blenkernel/intern/mesh_tangent.c b/source/blender/blenkernel/intern/mesh_tangent.c index a2a198cdb0d..d6f945cf34f 100644 --- a/source/blender/blenkernel/intern/mesh_tangent.c +++ b/source/blender/blenkernel/intern/mesh_tangent.c @@ -452,9 +452,7 @@ finally: pRes[3] = fSign; } -static void DM_calc_loop_tangents_thread(TaskPool *__restrict UNUSED(pool), - void *taskdata, - int UNUSED(threadid)) +static void DM_calc_loop_tangents_thread(TaskPool *__restrict UNUSED(pool), void *taskdata) { struct SGLSLMeshToTangent *mesh2tangent = taskdata; /* new computation method */ @@ -658,9 +656,7 @@ void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert, /* Calculation */ if (looptri_len != 0) { - TaskScheduler *scheduler = BLI_task_scheduler_get(); - TaskPool *task_pool; - task_pool = BLI_task_pool_create(scheduler, NULL, TASK_PRIORITY_LOW); + TaskPool *task_pool = BLI_task_pool_create(NULL, TASK_PRIORITY_LOW); tangent_mask_curr = 0; /* Calculate tangent layers */ diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index 7991559d1ce..4c7b791f103 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -460,6 +460,7 @@ typedef struct MovieClipCache { float principal[2]; float polynomial_k[3]; float division_k[2]; + float nuke_k[2]; short distortion_model; bool undistortion_used; @@ -908,6 +909,9 @@ static bool check_undistortion_cache_flags(const MovieClip *clip) if (!equals_v2v2(&camera->division_k1, cache->postprocessed.division_k)) { return false; } + if (!equals_v2v2(&camera->nuke_k1, cache->postprocessed.nuke_k)) { + return false; + } return true; } @@ -1010,6 +1014,7 @@ static void put_postprocessed_frame_to_cache( copy_v2_v2(cache->postprocessed.principal, camera->principal); copy_v3_v3(cache->postprocessed.polynomial_k, &camera->k1); copy_v2_v2(cache->postprocessed.division_k, &camera->division_k1); + copy_v2_v2(cache->postprocessed.nuke_k, &camera->nuke_k1); cache->postprocessed.undistortion_used = true; } else { diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index 6dc03c518fe..d79dbbb7d32 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -407,7 +407,7 @@ int multires_get_level(const Scene *scene, mmd->renderlvl; } else if (ob->mode == OB_MODE_SCULPT) { - return BKE_multires_sculpt_level_get(mmd); + return mmd->sculptlvl; } else if (ignore_simplify) { return mmd->lvl; @@ -2234,7 +2234,14 @@ void multiresModifier_sync_levels_ex(Object *ob_dst, } if (mmd_src->totlvl > mmd_dst->totlvl) { - multiresModifier_subdivide_to_level(ob_dst, mmd_dst, mmd_src->totlvl); + if (mmd_dst->simple) { + multiresModifier_subdivide_to_level( + ob_dst, mmd_dst, mmd_src->totlvl, MULTIRES_SUBDIVIDE_SIMPLE); + } + else { + multiresModifier_subdivide_to_level( + ob_dst, mmd_dst, mmd_src->totlvl, MULTIRES_SUBDIVIDE_CATMULL_CLARK); + } } else { multires_del_higher(mmd_dst, ob_dst, mmd_src->totlvl); @@ -2516,12 +2523,3 @@ int mdisp_rot_face_to_crn(struct MVert *UNUSED(mvert), return S; } - -/* This is a workaround for T58473. - * Force sculpting on the highest level for until the root of the issue is solved. - * - * When that issue is solved simple replace call of this function with mmd->sculptlvl. */ -int BKE_multires_sculpt_level_get(const struct MultiresModifierData *mmd) -{ - return mmd->totlvl; -} diff --git a/source/blender/blenkernel/intern/multires_reshape.c b/source/blender/blenkernel/intern/multires_reshape.c index 02cd5f094a6..480a1d0b2a0 100644 --- a/source/blender/blenkernel/intern/multires_reshape.c +++ b/source/blender/blenkernel/intern/multires_reshape.c @@ -28,8 +28,6 @@ #include "DNA_modifier_types.h" #include "DNA_scene_types.h" -#include "BLI_math_vector.h" - #include "BKE_customdata.h" #include "BKE_lib_id.h" #include "BKE_mesh.h" @@ -37,14 +35,16 @@ #include "BKE_modifier.h" #include "BKE_multires.h" #include "BKE_subdiv.h" +#include "BKE_subsurf.h" +#include "BLI_math_vector.h" #include "DEG_depsgraph_query.h" #include "multires_reshape.h" -/* ================================================================================================ - * Reshape from object. - */ +/* -------------------------------------------------------------------- */ +/** \name Reshape from object + * \{ */ bool multiresModifier_reshapeFromVertcos(struct Depsgraph *depsgraph, struct Object *object, @@ -93,9 +93,11 @@ bool multiresModifier_reshapeFromObject(struct Depsgraph *depsgraph, return result; } -/* ================================================================================================ - * Reshape from modifier. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Reshape from modifier + * \{ */ bool multiresModifier_reshapeFromDeformModifier(struct Depsgraph *depsgraph, struct Object *object, @@ -133,9 +135,11 @@ bool multiresModifier_reshapeFromDeformModifier(struct Depsgraph *depsgraph, return result; } -/* ================================================================================================ - * Reshape from grids. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Reshape from grids + * \{ */ bool multiresModifier_reshapeFromCCG(const int tot_level, Mesh *coarse_mesh, @@ -161,19 +165,24 @@ bool multiresModifier_reshapeFromCCG(const int tot_level, return true; } -/* ================================================================================================ - * Subdivision. - */ +/** \} */ -void multiresModifier_subdivide(Object *object, MultiresModifierData *mmd) +/* -------------------------------------------------------------------- */ +/** \name Subdivision + * \{ */ + +void multiresModifier_subdivide(Object *object, + MultiresModifierData *mmd, + const eMultiresSubdivideModeType mode) { const int top_level = mmd->totlvl + 1; - multiresModifier_subdivide_to_level(object, mmd, top_level); + multiresModifier_subdivide_to_level(object, mmd, top_level, mode); } void multiresModifier_subdivide_to_level(struct Object *object, struct MultiresModifierData *mmd, - const int top_level) + const int top_level, + const eMultiresSubdivideModeType mode) { if (top_level <= mmd->totlvl) { return; @@ -190,7 +199,12 @@ void multiresModifier_subdivide_to_level(struct Object *object, } if (!has_mdisps || top_level == 1) { multires_reshape_ensure_grids(coarse_mesh, top_level); - multires_set_tot_level(object, mmd, top_level); + if (ELEM(mode, MULTIRES_SUBDIVIDE_LINEAR, MULTIRES_SUBDIVIDE_SIMPLE)) { + multires_subdivide_create_tangent_displacement_linear_grids(object, mmd); + } + else { + multires_set_tot_level(object, mmd, top_level); + } return; } @@ -199,25 +213,33 @@ void multiresModifier_subdivide_to_level(struct Object *object, if (!multires_reshape_context_create_from_subdivide(&reshape_context, object, mmd, top_level)) { return; } + multires_reshape_store_original_grids(&reshape_context); multires_reshape_ensure_grids(coarse_mesh, reshape_context.top.level); multires_reshape_assign_final_coords_from_orig_mdisps(&reshape_context); - /* Free original grids which makes it so smoothing with details thinks all the details were - * added against base mesh's limit surface. This is similar behavior to as if we've done all - * displacement in sculpt mode at the old top level and then propagated to the new top level. */ - multires_reshape_free_original_grids(&reshape_context); + if (ELEM(mode, MULTIRES_SUBDIVIDE_LINEAR, MULTIRES_SUBDIVIDE_SIMPLE)) { + multires_reshape_smooth_object_grids(&reshape_context, mode); + } + else { + /* Free original grids which makes it so smoothing with details thinks all the details were + * added against base mesh's limit surface. This is similar behavior to as if we've done all + * displacement in sculpt mode at the old top level and then propagated to the new top level.*/ + multires_reshape_free_original_grids(&reshape_context); - multires_reshape_smooth_object_grids_with_details(&reshape_context); + multires_reshape_smooth_object_grids_with_details(&reshape_context); + } multires_reshape_object_grids_to_tangent_displacement(&reshape_context); multires_reshape_context_free(&reshape_context); multires_set_tot_level(object, mmd, top_level); } -/* ================================================================================================ - * Apply base. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Apply base + * \{ */ void multiresModifier_base_apply(struct Depsgraph *depsgraph, Object *object, @@ -263,3 +285,5 @@ void multiresModifier_base_apply(struct Depsgraph *depsgraph, multires_reshape_context_free(&reshape_context); } + +/** \} */ diff --git a/source/blender/blenkernel/intern/multires_reshape.h b/source/blender/blenkernel/intern/multires_reshape.h index adfa2659661..644404b5cb3 100644 --- a/source/blender/blenkernel/intern/multires_reshape.h +++ b/source/blender/blenkernel/intern/multires_reshape.h @@ -26,6 +26,8 @@ #include "BLI_sys_types.h" +#include "BKE_multires.h" + struct Depsgraph; struct GridPaintMask; struct MDisps; @@ -138,7 +140,7 @@ typedef struct ReshapeConstGridElement { float mask; } ReshapeConstGridElement; -/* ================================================================================================ +/* -------------------------------------------------------------------- * Construct/destruct reshape context. */ @@ -156,6 +158,11 @@ bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape struct Object *object, struct MultiresModifierData *mmd); +bool multires_reshape_context_create_from_base_mesh(MultiresReshapeContext *reshape_context, + struct Depsgraph *depsgraph, + struct Object *object, + struct MultiresModifierData *mmd); + bool multires_reshape_context_create_from_ccg(MultiresReshapeContext *reshape_context, struct SubdivCCG *subdiv_ccg, struct Mesh *base_mesh, @@ -169,7 +176,7 @@ bool multires_reshape_context_create_from_subdivide(MultiresReshapeContext *resh void multires_reshape_free_original_grids(MultiresReshapeContext *reshape_context); void multires_reshape_context_free(MultiresReshapeContext *reshape_context); -/* ================================================================================================ +/* -------------------------------------------------------------------- * Helper accessors. */ @@ -214,7 +221,7 @@ ReshapeGridElement multires_reshape_grid_element_for_ptex_coord( ReshapeConstGridElement multires_reshape_orig_grid_element_for_grid_coord( const MultiresReshapeContext *reshape_context, const GridCoord *grid_coord); -/* ================================================================================================ +/* -------------------------------------------------------------------- * Sample limit surface of the base mesh. */ @@ -225,14 +232,14 @@ void multires_reshape_evaluate_limit_at_grid(const MultiresReshapeContext *resha float r_P[3], float r_tangent_matrix[3][3]); -/* ================================================================================================ +/* -------------------------------------------------------------------- * Custom data preparation. */ /* Make sure custom data is allocated for the given level. */ void multires_reshape_ensure_grids(struct Mesh *mesh, const int level); -/* ================================================================================================ +/* -------------------------------------------------------------------- * Functions specific to reshaping from a set of vertices in a object position. */ @@ -245,7 +252,7 @@ bool multires_reshape_assign_final_coords_from_vertcos( const float (*vert_coords)[3], const int num_vert_coords); -/* ================================================================================================ +/* -------------------------------------------------------------------- * Functions specific to reshaping from CCG. */ @@ -255,7 +262,7 @@ bool multires_reshape_assign_final_coords_from_vertcos( bool multires_reshape_assign_final_coords_from_ccg(const MultiresReshapeContext *reshape_context, struct SubdivCCG *subdiv_ccg); -/* ================================================================================================ +/* -------------------------------------------------------------------- * Functions specific to reshaping from MDISPS. */ @@ -267,7 +274,7 @@ void multires_reshape_assign_final_coords_from_mdisps( void multires_reshape_assign_final_coords_from_orig_mdisps( const MultiresReshapeContext *reshape_context); -/* ================================================================================================ +/* -------------------------------------------------------------------- * Displacement smooth. */ @@ -284,9 +291,10 @@ void multires_reshape_smooth_object_grids_with_details( * * Makes it so surface on top level looks smooth. Details are not preserved */ -void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_context); +void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_context, + const enum eMultiresSubdivideModeType mode); -/* ================================================================================================ +/* -------------------------------------------------------------------- * Displacement, space conversion. */ @@ -297,7 +305,7 @@ void multires_reshape_store_original_grids(MultiresReshapeContext *reshape_conte void multires_reshape_object_grids_to_tangent_displacement( const MultiresReshapeContext *reshape_context); -/* ================================================================================================ +/* -------------------------------------------------------------------- * Apply base. */ @@ -319,5 +327,4 @@ void multires_reshape_apply_base_refine_from_base(MultiresReshapeContext *reshap * * NOTE: Will re-evaluate all leading modifiers, so it's not cheap. */ void multires_reshape_apply_base_refine_from_deform(MultiresReshapeContext *reshape_context); - #endif /* __BKE_INTERN_MULTIRES_RESHAPE_H__ */ diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.c b/source/blender/blenkernel/intern/multires_reshape_smooth.c index 514608a0f1d..04acb95909f 100644 --- a/source/blender/blenkernel/intern/multires_reshape_smooth.c +++ b/source/blender/blenkernel/intern/multires_reshape_smooth.c @@ -48,6 +48,10 @@ #include "atomic_ops.h" #include "subdiv_converter.h" +/* -------------------------------------------------------------------- */ +/** \name Local Structs + * \{ */ + typedef struct SurfacePoint { float P[3]; float tangent_matrix[3][3]; @@ -115,11 +119,21 @@ typedef struct MultiresReshapeSmoothContext { Subdiv *reshape_subdiv; SurfaceGrid *base_surface_grids; + + /* Defines how displacement is interpolated on the higher levels (for example, whether + * displacement is smoothed in Catmull-Clark mode or interpolated linearly preserving sharp edges + * of the current sculpt level). + * + * NOTE: Uses same enumerator type as Subdivide operator, since the values are the same and + * decoupling type just adds extra headache to convert one enumerator to another. */ + eMultiresSubdivideModeType smoothing_type; } MultiresReshapeSmoothContext; -/* ================================================================================================ - * Masks. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Masks + * \{ */ /* Interpolate mask grid at a reshape level. * Will return 0 if there is no masks custom data layer. */ @@ -165,9 +179,11 @@ static float interpolate_masks_grid(const MultiresReshapeSmoothContext *reshape_ mask_elements[2] * weights[2] + mask_elements[3] * weights[3]; } -/* ================================================================================================ - * Surface. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Surface + * \{ */ static void base_surface_grids_allocate(MultiresReshapeSmoothContext *reshape_smooth_context) { @@ -227,9 +243,11 @@ static void base_surface_grids_write(const MultiresReshapeSmoothContext *reshape copy_m3_m3(point->tangent_matrix, tangent_matrix); } -/* ================================================================================================ - * Evaluation of subdivision surface at a reshape level. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Evaluation of subdivision surface at a reshape level + * \{ */ typedef void (*ForeachTopLevelGridCoordCallback)( const MultiresReshapeSmoothContext *reshape_smooth_context, @@ -383,11 +401,14 @@ static void foreach_toplevel_grid_coord(const MultiresReshapeSmoothContext *resh 0, num_faces, &data, foreach_toplevel_grid_coord_task, ¶llel_range_settings); } -/* ================================================================================================ - * Generation of a topology information for OpenSubdiv converter. +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Generation of a topology information for OpenSubdiv converter * * Calculates vertices, their coordinates in the original grids, and connections of them so then - * it's easy to create OpenSubdiv's topology refiner. */ + * it's easy to create OpenSubdiv's topology refiner. + * \{ */ static int get_reshape_level_resolution(const MultiresReshapeContext *reshape_context) { @@ -399,15 +420,17 @@ static int get_reshape_level_resolution(const MultiresReshapeContext *reshape_co static char get_effective_edge_crease_char( const MultiresReshapeSmoothContext *reshape_smooth_context, const MEdge *base_edge) { - const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; - if (reshape_context->subdiv->settings.is_simple) { + if (ELEM(reshape_smooth_context->smoothing_type, + MULTIRES_SUBDIVIDE_LINEAR, + MULTIRES_SUBDIVIDE_SIMPLE)) { return 255; } return base_edge->crease; } static void context_init(MultiresReshapeSmoothContext *reshape_smooth_context, - const MultiresReshapeContext *reshape_context) + const MultiresReshapeContext *reshape_context, + const eMultiresSubdivideModeType mode) { reshape_smooth_context->reshape_context = reshape_context; @@ -427,6 +450,8 @@ static void context_init(MultiresReshapeSmoothContext *reshape_smooth_context, reshape_smooth_context->non_loose_base_edge_map = NULL; reshape_smooth_context->reshape_subdiv = NULL; reshape_smooth_context->base_surface_grids = NULL; + + reshape_smooth_context->smoothing_type = mode; } static void context_free_geometry(MultiresReshapeSmoothContext *reshape_smooth_context) @@ -461,12 +486,14 @@ static void context_free(MultiresReshapeSmoothContext *reshape_smooth_context) static bool foreach_topology_info(const SubdivForeachContext *foreach_context, const int num_vertices, - const int UNUSED(num_edges), + const int num_edges, const int num_loops, const int num_polygons) { MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data; - const int max_edges = reshape_smooth_context->geometry.max_edges; + const int max_edges = reshape_smooth_context->smoothing_type == MULTIRES_SUBDIVIDE_LINEAR ? + num_edges : + reshape_smooth_context->geometry.max_edges; /* NOTE: Calloc so the counters are re-set to 0 "for free". */ reshape_smooth_context->geometry.num_vertices = num_vertices; @@ -659,6 +686,22 @@ static void foreach_vertex_of_loose_edge(const struct SubdivForeachContext *fore } } +static void store_edge(MultiresReshapeSmoothContext *reshape_smooth_context, + const int subdiv_v1, + const int subdiv_v2, + const char crease) +{ + /* This is a bit overhead to use atomics in such a simple function called from many threads, + * but this allows to save quite measurable amount of memory. */ + const int edge_index = atomic_fetch_and_add_z(&reshape_smooth_context->geometry.num_edges, 1); + BLI_assert(edge_index < reshape_smooth_context->geometry.max_edges); + + Edge *edge = &reshape_smooth_context->geometry.edges[edge_index]; + edge->v1 = subdiv_v1; + edge->v2 = subdiv_v2; + edge->sharpness = BKE_subdiv_edge_crease_to_sharpness_char(crease); +} + static void foreach_edge(const struct SubdivForeachContext *foreach_context, void *UNUSED(tls), const int coarse_edge_index, @@ -669,8 +712,15 @@ static void foreach_edge(const struct SubdivForeachContext *foreach_context, MultiresReshapeSmoothContext *reshape_smooth_context = foreach_context->user_data; const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context; - /* Ignore all inner face edges as they have sharpness of zero. */ - if (coarse_edge_index == ORIGINDEX_NONE) { + if (reshape_smooth_context->smoothing_type == MULTIRES_SUBDIVIDE_LINEAR) { + store_edge(reshape_smooth_context, subdiv_v1, subdiv_v2, (char)255); + return; + } + + /* Ignore all inner face edges as they have sharpness of zero when using Catmull-Clark mode. In + * simple mode, all edges have maximum sharpness, so they can't be skipped. */ + if (coarse_edge_index == ORIGINDEX_NONE && + reshape_smooth_context->smoothing_type != MULTIRES_SUBDIVIDE_SIMPLE) { return; } /* Ignore all loose edges as well, as they are not communicated to the OpenSubdiv. */ @@ -684,16 +734,7 @@ static void foreach_edge(const struct SubdivForeachContext *foreach_context, if (crease == 0) { return; } - - /* This is a bit overhead to use atomics in such a simple function called from many threads, - * but this allows to save quite measurable amount of memory. */ - const int edge_index = atomic_fetch_and_add_z(&reshape_smooth_context->geometry.num_edges, 1); - BLI_assert(edge_index < reshape_smooth_context->geometry.max_edges); - - Edge *edge = &reshape_smooth_context->geometry.edges[edge_index]; - edge->v1 = subdiv_v1; - edge->v2 = subdiv_v2; - edge->sharpness = BKE_subdiv_edge_crease_to_sharpness_char(crease); + store_edge(reshape_smooth_context, subdiv_v1, subdiv_v2, crease); } static void geometry_init_loose_information(MultiresReshapeSmoothContext *reshape_smooth_context) @@ -757,9 +798,11 @@ static void geometry_create(MultiresReshapeSmoothContext *reshape_smooth_context reshape_context->subdiv, &foreach_context, &mesh_settings, reshape_context->base_mesh); } -/* ================================================================================================ - * Generation of OpenSubdiv evaluator for topology created form reshape level. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Generation of OpenSubdiv evaluator for topology created form reshape level + * \{ */ static OpenSubdiv_SchemeType get_scheme_type(const OpenSubdiv_Converter *UNUSED(converter)) { @@ -1037,9 +1080,11 @@ static void reshape_subdiv_evaluate_limit_at_grid( BKE_multires_construct_tangent_matrix(r_tangent_matrix, dPdu, dPdv, corner); } -/* ================================================================================================ - * Evaluation of base surface. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Evaluation of base surface + * \{ */ static void evaluate_base_surface_grids_callback( const MultiresReshapeSmoothContext *reshape_smooth_context, @@ -1060,9 +1105,11 @@ static void evaluate_base_surface_grids(const MultiresReshapeSmoothContext *resh foreach_toplevel_grid_coord(reshape_smooth_context, evaluate_base_surface_grids_callback, NULL); } -/* ================================================================================================ - * Evaluation of new surface. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Evaluation of new surface + * \{ */ /* Evaluate final position of the original (pre-sculpt-edit) point position at a given grid * coordinate. */ @@ -1176,9 +1223,12 @@ static void evaluate_higher_grid_positions( foreach_toplevel_grid_coord( reshape_smooth_context, evaluate_higher_grid_positions_callback, NULL); } -/* ================================================================================================ - * Entry point. - */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Entry point + * \{ */ void multires_reshape_smooth_object_grids_with_details( const MultiresReshapeContext *reshape_context) @@ -1190,7 +1240,12 @@ void multires_reshape_smooth_object_grids_with_details( } MultiresReshapeSmoothContext reshape_smooth_context; - context_init(&reshape_smooth_context, reshape_context); + if (reshape_context->subdiv->settings.is_simple) { + context_init(&reshape_smooth_context, reshape_context, MULTIRES_SUBDIVIDE_SIMPLE); + } + else { + context_init(&reshape_smooth_context, reshape_context, MULTIRES_SUBDIVIDE_CATMULL_CLARK); + } geometry_create(&reshape_smooth_context); @@ -1206,7 +1261,8 @@ void multires_reshape_smooth_object_grids_with_details( context_free(&reshape_smooth_context); } -void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_context) +void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_context, + const eMultiresSubdivideModeType mode) { const int level_difference = (reshape_context->top.level - reshape_context->reshape.level); if (level_difference == 0) { @@ -1215,7 +1271,7 @@ void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_ } MultiresReshapeSmoothContext reshape_smooth_context; - context_init(&reshape_smooth_context, reshape_context); + context_init(&reshape_smooth_context, reshape_context, mode); geometry_create(&reshape_smooth_context); @@ -1226,3 +1282,5 @@ void multires_reshape_smooth_object_grids(const MultiresReshapeContext *reshape_ context_free(&reshape_smooth_context); } + +/** \} */ diff --git a/source/blender/blenkernel/intern/multires_reshape_subdivide.c b/source/blender/blenkernel/intern/multires_reshape_subdivide.c new file mode 100644 index 00000000000..5923e0430f2 --- /dev/null +++ b/source/blender/blenkernel/intern/multires_reshape_subdivide.c @@ -0,0 +1,106 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup bke + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_scene_types.h" + +#include "BKE_customdata.h" +#include "BKE_lib_id.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_modifier.h" +#include "BKE_multires.h" +#include "BKE_subdiv.h" +#include "BKE_subsurf.h" +#include "BLI_math_vector.h" + +#include "DEG_depsgraph_query.h" + +#include "multires_reshape.h" + +static void multires_subdivide_create_object_space_linear_grids(Mesh *mesh) +{ + MDisps *mdisps = CustomData_get_layer(&mesh->ldata, CD_MDISPS); + const int totpoly = mesh->totpoly; + for (int p = 0; p < totpoly; p++) { + MPoly *poly = &mesh->mpoly[p]; + float poly_center[3]; + BKE_mesh_calc_poly_center(poly, &mesh->mloop[poly->loopstart], mesh->mvert, poly_center); + for (int l = 0; l < poly->totloop; l++) { + const int loop_index = poly->loopstart + l; + + float(*disps)[3] = mdisps[loop_index].disps; + mdisps[loop_index].totdisp = 4; + mdisps[loop_index].level = 1; + + int prev_loop_index = l - 1 >= 0 ? loop_index - 1 : loop_index + poly->totloop - 1; + int next_loop_index = l + 1 < poly->totloop ? loop_index + 1 : poly->loopstart; + + MLoop *loop = &mesh->mloop[loop_index]; + MLoop *loop_next = &mesh->mloop[next_loop_index]; + MLoop *loop_prev = &mesh->mloop[prev_loop_index]; + + copy_v3_v3(disps[0], poly_center); + mid_v3_v3v3(disps[1], mesh->mvert[loop->v].co, mesh->mvert[loop_next->v].co); + mid_v3_v3v3(disps[2], mesh->mvert[loop->v].co, mesh->mvert[loop_prev->v].co); + copy_v3_v3(disps[3], mesh->mvert[loop->v].co); + } + } +} + +void multires_subdivide_create_tangent_displacement_linear_grids(Object *object, + MultiresModifierData *mmd) +{ + Mesh *coarse_mesh = object->data; + multires_force_sculpt_rebuild(object); + + MultiresReshapeContext reshape_context; + + const int new_top_level = mmd->totlvl + 1; + + const bool has_mdisps = CustomData_has_layer(&coarse_mesh->ldata, CD_MDISPS); + if (!has_mdisps) { + CustomData_add_layer(&coarse_mesh->ldata, CD_MDISPS, CD_CALLOC, NULL, coarse_mesh->totloop); + } + + if (new_top_level == 1) { + /* No MDISPS. Create new grids for level 1 using the edges mid point and poly centers. */ + multires_reshape_ensure_grids(coarse_mesh, 1); + multires_subdivide_create_object_space_linear_grids(coarse_mesh); + } + + /* Convert the new grids to tangent displacement. */ + multires_set_tot_level(object, mmd, new_top_level); + + if (!multires_reshape_context_create_from_subdivide( + &reshape_context, object, mmd, new_top_level)) { + return; + } + + multires_reshape_object_grids_to_tangent_displacement(&reshape_context); + multires_reshape_context_free(&reshape_context); +} diff --git a/source/blender/blenkernel/intern/multires_reshape_util.c b/source/blender/blenkernel/intern/multires_reshape_util.c index 57d790d2c34..4d54b7b6e87 100644 --- a/source/blender/blenkernel/intern/multires_reshape_util.c +++ b/source/blender/blenkernel/intern/multires_reshape_util.c @@ -43,9 +43,9 @@ #include "DEG_depsgraph_query.h" -/* ================================================================================================ - * Construct/destruct reshape context. - */ +/* -------------------------------------------------------------------- */ +/** \name Construct/destruct reshape context + * \{ */ /* Create subdivision surface descriptor which is configured for surface evaluation at a given * multires modifier. */ @@ -152,6 +152,39 @@ static bool context_verify_or_free(MultiresReshapeContext *reshape_context) return is_valid; } +bool multires_reshape_context_create_from_base_mesh(MultiresReshapeContext *reshape_context, + Depsgraph *depsgraph, + Object *object, + MultiresModifierData *mmd) +{ + context_zero(reshape_context); + + const bool use_render_params = false; + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Mesh *base_mesh = (Mesh *)object->data; + + reshape_context->depsgraph = depsgraph; + reshape_context->object = object; + reshape_context->mmd = mmd; + + reshape_context->base_mesh = base_mesh; + + reshape_context->subdiv = multires_reshape_create_subdiv(NULL, object, mmd); + reshape_context->need_free_subdiv = true; + + reshape_context->reshape.level = multires_get_level( + scene_eval, object, mmd, use_render_params, true); + reshape_context->reshape.grid_size = BKE_subdiv_grid_size_from_level( + reshape_context->reshape.level); + + reshape_context->top.level = mmd->totlvl; + reshape_context->top.grid_size = BKE_subdiv_grid_size_from_level(reshape_context->top.level); + + context_init_commoon(reshape_context); + + return context_verify_or_free(reshape_context); +} + bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape_context, Depsgraph *depsgraph, Object *object, @@ -272,14 +305,16 @@ void multires_reshape_context_free(MultiresReshapeContext *reshape_context) multires_reshape_free_original_grids(reshape_context); - MEM_freeN(reshape_context->face_start_grid_index); - MEM_freeN(reshape_context->ptex_start_grid_index); - MEM_freeN(reshape_context->grid_to_face_index); + MEM_SAFE_FREE(reshape_context->face_start_grid_index); + MEM_SAFE_FREE(reshape_context->ptex_start_grid_index); + MEM_SAFE_FREE(reshape_context->grid_to_face_index); } -/* ================================================================================================ - * Helper accessors. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Helper accessors + * \{ */ /* For the given grid index get index of face it was created for. */ int multires_reshape_grid_to_face_index(const MultiresReshapeContext *reshape_context, @@ -453,9 +488,11 @@ ReshapeConstGridElement multires_reshape_orig_grid_element_for_grid_coord( return grid_element; } -/* ================================================================================================ - * Sample limit surface of the base mesh. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sample limit surface of the base mesh + * \{ */ void multires_reshape_evaluate_limit_at_grid(const MultiresReshapeContext *reshape_context, const GridCoord *grid_coord, @@ -475,9 +512,11 @@ void multires_reshape_evaluate_limit_at_grid(const MultiresReshapeContext *resha reshape_context, face_index, corner, dPdu, dPdv, r_tangent_matrix); } -/* ================================================================================================ - * Custom data preparation. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Custom data preparation + * \{ */ static void allocate_displacement_grid(MDisps *displacement_grid, const int level) { @@ -539,9 +578,11 @@ void multires_reshape_ensure_grids(Mesh *mesh, const int level) ensure_mask_grids(mesh, level); } -/* ================================================================================================ - * Displacement, space conversion. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Displacement, space conversion + * \{ */ void multires_reshape_store_original_grids(MultiresReshapeContext *reshape_context) { @@ -678,10 +719,13 @@ void multires_reshape_object_grids_to_tangent_displacement( NULL); } -/* ================================================================================================ - * MDISPS - * - * TODO(sergey): Make foreach_grid_coordinate more accessible and move this functionality to +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name MDISPS + * \{ */ + +/* TODO(sergey): Make foreach_grid_coordinate more accessible and move this functionality to * own file. */ static void assign_final_coords_from_mdisps(const MultiresReshapeContext *reshape_context, @@ -732,3 +776,5 @@ void multires_reshape_assign_final_coords_from_orig_mdisps( foreach_grid_coordinate( reshape_context, reshape_context->top.level, assign_final_coords_from_orig_mdisps, NULL); } + +/** \} */ diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.c b/source/blender/blenkernel/intern/multires_unsubdivide.c new file mode 100644 index 00000000000..49de88db521 --- /dev/null +++ b/source/blender/blenkernel/intern/multires_unsubdivide.c @@ -0,0 +1,1297 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup bke + * + * This implements the un-subdivide algorithm, which generates a lower resolution base mesh and + * its corresponding grids to match a given original mesh. + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_scene_types.h" + +#include "BLI_gsqueue.h" +#include "BLI_math_vector.h" + +#include "BKE_customdata.h" +#include "BKE_lib_id.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_modifier.h" +#include "BKE_multires.h" +#include "BKE_subdiv.h" +#include "BKE_subsurf.h" + +#include "bmesh.h" + +#include "DEG_depsgraph_query.h" + +#include "multires_reshape.h" +#include "multires_unsubdivide.h" + +/* This is done in the following steps: + * + * - If there are already grids in the original mesh, + * convert them from tangent displacement to object space coordinates. + * - Assign data-layers to the original mesh to map vertices to a new base mesh. + * These data-layers store the indices of the elements in the original mesh. + * This way the original indices are + * preserved when doing mesh modifications (removing and dissolving vertices) + * when building the new base mesh. + * - Try to find a lower resolution base mesh. This is done by flood fill operation that tags the + * center vertices of the lower level grid. + * If the algorithm can tag all vertices correctly, + * the lower level base mesh is generated by dissolving the tagged vertices. + * - Use the data-layers to map vertices from the base mesh to the original mesh and original to + * base mesh. + * - Find two adjacent vertices on the base mesh to a given vertex to map that loop from base mesh + * to original mesh + * - Extract the grid from the original mesh from that loop. If there are no grids in the original + * mesh, build the new grid directly from the vertex coordinates by iterating in a grid pattern + * over them. If there are grids in the original mesh, iterate in a grid pattern over the polys, + * reorder all the coordinates of the grid in that poly and copy those coordinates to the new + * base mesh grid. + * - Copy the new grid data over to a new allocated MDISP layer with the appropriate size to store + * the new levels. + * - Convert the grid data from object space to tangent displacement. + */ + +/** + * Used to check if a vertex is in a disconnected element ID. + */ +static bool is_vertex_in_id(BMVert *v, int *elem_id, int elem) +{ + const int v_index = BM_elem_index_get(v); + return elem_id[v_index] == elem; +} + +static bool is_vertex_pole_three(BMVert *v) +{ + return !BM_vert_is_boundary(v) && (BM_vert_edge_count(v) == 3); +} + +static bool is_vertex_pole(BMVert *v) +{ + return !BM_vert_is_boundary(v) && (BM_vert_edge_count(v) == 3 || BM_vert_edge_count(v) >= 5); +} + +/** + * Returns the first pole that is found in an element ID. + * + * Tries to give priority to 3 vert poles as they generally generate better results in cases were + * the un-subdivide solution is ambiguous. + */ +static BMVert *unsubdivide_find_any_pole(BMesh *bm, int *elem_id, int elem) +{ + BMIter iter; + BMVert *v; + BMVert *pole = NULL; + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + if (is_vertex_in_id(v, elem_id, elem) && is_vertex_pole_three(v)) { + return v; + } + else if (is_vertex_in_id(v, elem_id, elem) && is_vertex_pole(v)) { + pole = v; + } + } + return pole; +} + +/** + * Checks if the mesh is all quads. + * + * TODO(pablodp606): This can perform additional checks if they are faster than trying to search + * for an un-subdivide solution. This way it is possible to cancel the operation faster. + */ +static bool unsubdivide_is_all_quads(BMesh *bm) +{ + BMIter iter; + BMIter iter_a; + BMFace *f; + BMVert *v; + int count = 0; + if (bm->totface < 3) { + return false; + } + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + count = 0; + BM_ITER_ELEM (v, &iter_a, f, BM_VERTS_OF_FACE) { + count++; + } + + if (count != 4) { + return false; + } + } + + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + if (BM_vert_is_wire(v)) { + return false; + } + if (BM_vert_edge_count(v) == 0) { + return false; + } + } + + return true; +} + +/** + * Returns true if from_v and to_v, which should be part of the same quad face, are diagonals. + */ +static bool is_vertex_diagonal(BMVert *from_v, BMVert *to_v) +{ + return !BM_edge_exists(from_v, to_v); +} + +/** + * Generates a possible solution for un-subdivision by tagging the (0,0) + * vertices of the possible grids. + * + * This works using a flood fill operation using the quads diagonals to jump to the next vertex. + * + * If initial_vertex is part of the base mesh solution, the flood fill should tag only the (0.0) + * vertices of the grids that need to be dissolved, and nothing else. + */ +static void unsubdivide_face_center_vertex_tag(BMesh *bm, BMVert *initial_vertex) +{ + bool *visited_vertices = MEM_calloc_arrayN(sizeof(bool), bm->totvert, "visited vertices"); + GSQueue *queue; + queue = BLI_gsqueue_new(sizeof(BMVert *)); + + /* Add and tag the vertices connected by a diagonal to initial_vertex to the flood fill queue. If + * initial_vertex is a pole and there is a valid solution, those vertices should be the (0,0) of + * the grids for the loops of initial_vertex. */ + BMIter iter; + BMIter iter_a; + BMFace *f; + BMVert *neighbor_v; + BM_ITER_ELEM (f, &iter, initial_vertex, BM_FACES_OF_VERT) { + BM_ITER_ELEM (neighbor_v, &iter_a, f, BM_VERTS_OF_FACE) { + int neighbor_vertex_index = BM_elem_index_get(neighbor_v); + if (neighbor_v != initial_vertex && is_vertex_diagonal(neighbor_v, initial_vertex)) { + BLI_gsqueue_push(queue, &neighbor_v); + visited_vertices[neighbor_vertex_index] = true; + BM_elem_flag_set(neighbor_v, BM_ELEM_TAG, true); + } + } + } + + /* Repeat a similar operation for all vertices in the queue. */ + /* In this case, add to the queue the vertices connected by 2 steps using the diagonals in any + * direction. If a solution exists and intial_vertex was a pole, this is guaranteed that will tag + * all the (0,0) vertices of the grids, and nothing else. */ + /* If it was not a pole, it may or may not find a solution, even if the solution exists. */ + while (!BLI_gsqueue_is_empty(queue)) { + BMVert *from_v; + BLI_gsqueue_pop(queue, &from_v); + + /* Get the diagonals (first connected step) */ + GSQueue *diagonals; + diagonals = BLI_gsqueue_new(sizeof(BMVert *)); + BM_ITER_ELEM (f, &iter, from_v, BM_FACES_OF_VERT) { + BM_ITER_ELEM (neighbor_v, &iter_a, f, BM_VERTS_OF_FACE) { + if (neighbor_v != from_v && is_vertex_diagonal(neighbor_v, from_v)) { + BLI_gsqueue_push(diagonals, &neighbor_v); + } + } + } + + /* Do the second connected step. This vertices are the ones that are added to the flood fill + * queue. */ + while (!BLI_gsqueue_is_empty(diagonals)) { + BMVert *diagonal_v; + BLI_gsqueue_pop(diagonals, &diagonal_v); + BM_ITER_ELEM (f, &iter, diagonal_v, BM_FACES_OF_VERT) { + BM_ITER_ELEM (neighbor_v, &iter_a, f, BM_VERTS_OF_FACE) { + int neighbor_vertex_index = BM_elem_index_get(neighbor_v); + if (!visited_vertices[neighbor_vertex_index] && neighbor_v != diagonal_v && + is_vertex_diagonal(neighbor_v, diagonal_v)) { + BLI_gsqueue_push(queue, &neighbor_v); + visited_vertices[neighbor_vertex_index] = true; + BM_elem_flag_set(neighbor_v, BM_ELEM_TAG, true); + } + } + } + } + BLI_gsqueue_free(diagonals); + } + + BLI_gsqueue_free(queue); + MEM_freeN(visited_vertices); +} + +/** + * This function checks if the current status of the #BMVert tags + * corresponds to a valid un-subdivide solution. + * + * This means that all vertices corresponding to the (0,0) grid coordinate should be tagged. + * + * On a valid solution, the following things should happen: + * - No boundary vertices should be tagged + * - No vertices connected by an edge or a quad diagonal to a tagged vertex should be tagged + * - All boundary vertices should have one vertex connected by an edge or a diagonal tagged + */ +static bool unsubdivide_is_center_vertex_tag_valid(BMesh *bm, int *elem_id, int elem) +{ + BMVert *v, *neighbor_v; + BMIter iter, iter_a, iter_b; + BMFace *f; + + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + if (is_vertex_in_id(v, elem_id, elem)) { + if (BM_elem_flag_test(v, BM_ELEM_TAG)) { + /* Tagged vertex in boundary */ + if (BM_vert_is_boundary(v)) { + return false; + } + /* Tagged vertex with connected tagged vertex. */ + BM_ITER_ELEM (f, &iter_a, v, BM_FACES_OF_VERT) { + BM_ITER_ELEM (neighbor_v, &iter_b, f, BM_VERTS_OF_FACE) { + if (neighbor_v != v && BM_elem_flag_test(neighbor_v, BM_ELEM_TAG)) { + return false; + } + } + } + } + if (BM_vert_is_boundary(v)) { + /* Un-tagged vertex in boundary without connected tagged vertices. */ + bool any_tagged = false; + BM_ITER_ELEM (f, &iter_a, v, BM_FACES_OF_VERT) { + BM_ITER_ELEM (neighbor_v, &iter_b, f, BM_VERTS_OF_FACE) { + if (neighbor_v != v && BM_elem_flag_test(neighbor_v, BM_ELEM_TAG)) { + any_tagged = true; + } + } + } + if (!any_tagged) { + return false; + } + } + } + } + + return true; +} + +/** + * Search and validates an un-subdivide solution for a given element ID. + */ +static bool unsubdivide_tag_disconnected_mesh_element(BMesh *bm, int *elem_id, int elem) +{ + /* First, get vertex candidates to try to generate possible un-subdivide solution. */ + /* Find a vertex pole. If there is a solution on an all quad base mesh, this vertex should be + * part of the base mesh. If it isn't, then there is no solution. */ + GSQueue *initial_vertex = BLI_gsqueue_new(sizeof(BMVert *)); + BMVert *initial_vertex_pole = unsubdivide_find_any_pole(bm, elem_id, elem); + if (initial_vertex_pole != NULL) { + BLI_gsqueue_push(initial_vertex, &initial_vertex_pole); + } + + /* Also try from the different 4 vertices of a quad in the current + * disconnected element ID. If a solution exists the search should return a valid solution from + * one of these vertices.*/ + BMFace *f, *init_face = NULL; + BMVert *v; + BMIter iter_a, iter_b; + BM_ITER_MESH (f, &iter_a, bm, BM_FACES_OF_MESH) { + BM_ITER_ELEM (v, &iter_b, f, BM_VERTS_OF_FACE) { + if (is_vertex_in_id(v, elem_id, elem)) { + init_face = f; + break; + } + } + if (init_face != NULL) { + break; + } + } + + BM_ITER_ELEM (v, &iter_a, init_face, BM_VERTS_OF_FACE) { + BLI_gsqueue_push(initial_vertex, &v); + } + + bool valid_tag_found = false; + + /* Check all vertex candidates to a solution. */ + while (!BLI_gsqueue_is_empty(initial_vertex)) { + + BMVert *iv; + BLI_gsqueue_pop(initial_vertex, &iv); + + /* Generate a possible solution. */ + unsubdivide_face_center_vertex_tag(bm, iv); + + /* Check if the solution is valid. If it is, stop searching. */ + if (unsubdivide_is_center_vertex_tag_valid(bm, elem_id, elem)) { + valid_tag_found = true; + break; + } + + /* If the solution is not valid, reset the state of all tags in this disconnected element ID + * and try again. */ + BMVert *v_reset; + BMIter iter; + BM_ITER_MESH (v_reset, &iter, bm, BM_VERTS_OF_MESH) { + if (is_vertex_in_id(v_reset, elem_id, elem)) { + BM_elem_flag_set(v_reset, BM_ELEM_TAG, false); + } + } + } + BLI_gsqueue_free(initial_vertex); + return valid_tag_found; +} + +/** + * Uses a flood fill operation to generate a different ID for each disconnected mesh element. + */ +static int unsubdivide_init_elem_ids(BMesh *bm, int *elem_id) +{ + bool *visited_vertices = MEM_calloc_arrayN(sizeof(bool), bm->totvert, "visited vertices"); + int current_id = 0; + for (int i = 0; i < bm->totvert; i++) { + if (!visited_vertices[i]) { + GSQueue *queue; + queue = BLI_gsqueue_new(sizeof(BMVert *)); + + visited_vertices[i] = true; + elem_id[i] = current_id; + BMVert *iv = BM_vert_at_index(bm, i); + BLI_gsqueue_push(queue, &iv); + + while (!BLI_gsqueue_is_empty(queue)) { + BMIter iter; + BMVert *current_v, *neighbor_v; + BMEdge *ed; + BLI_gsqueue_pop(queue, ¤t_v); + BM_ITER_ELEM (ed, &iter, current_v, BM_EDGES_OF_VERT) { + neighbor_v = BM_edge_other_vert(ed, current_v); + const int neighbor_index = BM_elem_index_get(neighbor_v); + if (!visited_vertices[neighbor_index]) { + visited_vertices[neighbor_index] = true; + elem_id[neighbor_index] = current_id; + BLI_gsqueue_push(queue, &neighbor_v); + } + } + } + current_id++; + BLI_gsqueue_free(queue); + } + } + MEM_freeN(visited_vertices); + return current_id; +} + +/** + * Builds a base mesh one subdivision level down from the current original mesh if the original + * mesh has a valid solution stored in the #BMVert tags. + */ +static void unsubdivide_build_base_mesh_from_tags(BMesh *bm) +{ + BMVert *v; + BMIter iter; + + /* Stores the vertices which correspond to (1, 0) and (0, 1) of the grids in the select flag. */ + BM_mesh_elem_hflag_enable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); + BMVert *v_neighbor; + BMIter iter_a; + BMEdge *ed; + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + BM_ITER_ELEM (ed, &iter_a, v, BM_EDGES_OF_VERT) { + v_neighbor = BM_edge_other_vert(ed, v); + if (BM_elem_flag_test(v_neighbor, BM_ELEM_TAG)) { + BM_elem_flag_set(v, BM_ELEM_SELECT, false); + } + } + } + + /* Dissolves the (0,0) vertices of the grids. */ + BMO_op_callf(bm, + (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), + "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b", + BM_ELEM_TAG, + false, + true); + + BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + + /* Copy the select flag to the tag flag. */ + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + if (!BM_elem_flag_test(v, BM_ELEM_SELECT)) { + BM_elem_flag_set(v, BM_ELEM_TAG, true); + } + } + + /* Dissolves the (1,0) and (0,1) vertices of the grids. */ + BMO_op_callf(bm, + (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), + "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b", + BM_ELEM_TAG, + false, + true); +} + +/** + * Main function to get a base mesh one level down from the current original mesh if it exists. + * + * This searches for different un-subdivide solutions and stores them as a combination of #BMVert + * flags for each disconnected mesh element. + * + * If the solution for all elements are valid, it builds a new base mesh based on those tags by + * dissolving and merging vertices. + */ +static bool multires_unsubdivide_single_level(BMesh *bm) +{ + + /* Do a first check to make sure that it makes sense to search for un-subdivision in this mesh. + */ + if (!unsubdivide_is_all_quads(bm)) { + return false; + }; + + /* Initialize the vertex table. */ + BM_mesh_elem_table_init(bm, BM_VERT); + BM_mesh_elem_table_ensure(bm, BM_VERT); + + /* Build disconnected elements IDs. Each disconnected mesh element is evaluated separately. */ + int *elem_id = MEM_calloc_arrayN(sizeof(int), bm->totvert, " ELEM ID"); + const int tot_ids = unsubdivide_init_elem_ids(bm, elem_id); + + bool valid_tag_found = true; + + /* Reset the #BMesh flags as they are used to store data during the un-subdivide process. */ + BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); + + /* For each disconnected mesh element ID, search if an un-subdivide solution is possible. The + * whole un-subdivide process fails if a single disconnected mesh element fails. */ + for (int id = 0; id < tot_ids; id++) { + /* Try to the #BMesh vertex flag tags corresponding to an un-subdivide solution. */ + if (!unsubdivide_tag_disconnected_mesh_element(bm, elem_id, id)) { + valid_tag_found = false; + break; + } + } + + /* If a solution was found for all elements IDs, build the new base mesh using the solution + * stored in the BMVert tags. */ + if (valid_tag_found) { + unsubdivide_build_base_mesh_from_tags(bm); + } + + MEM_freeN(elem_id); + return valid_tag_found; +} + +/** + * Returns the next edge and vertex in the direction of a given edge. + */ +static BMEdge *edge_step(BMVert *v, BMEdge *edge, BMVert **r_next_vertex) +{ + BMIter iter; + BMEdge *test_edge; + if (edge == NULL) { + (*r_next_vertex) = v; + return edge; + } + (*r_next_vertex) = BM_edge_other_vert(edge, v); + BM_ITER_ELEM (test_edge, &iter, (*r_next_vertex), BM_EDGES_OF_VERT) { + if (!BM_edge_share_quad_check(test_edge, edge)) { + return test_edge; + } + } + return NULL; +} + +static BMFace *face_step(BMEdge *edge, BMFace *f) +{ + BMIter iter; + BMFace *face_iter; + + BM_ITER_ELEM (face_iter, &iter, edge, BM_FACES_OF_EDGE) { + if (BM_face_share_edge_check(face_iter, f)) { + return face_iter; + } + } + return f; +} + +/** + * Returns the other edge which belongs to the face f which is different from edge_x and shares + * initial_vertex. + */ +static BMEdge *get_initial_edge_y(BMFace *f, BMEdge *edge_x, BMVert *initial_vertex) +{ + BMIter iter; + BMEdge *test_edge; + BM_ITER_ELEM (test_edge, &iter, f, BM_EDGES_OF_FACE) { + if (edge_x != test_edge) { + if (test_edge->v1 != initial_vertex && test_edge->v2 == initial_vertex) { + return test_edge; + } + if (test_edge->v2 != initial_vertex && test_edge->v1 == initial_vertex) { + return test_edge; + } + } + } + return NULL; +} + +/** + * Writes the current mdisp data into the corresponding area of quad poly giving its corner's loop. + */ +static void write_loop_in_face_grid( + float (*face_grid)[3], MDisps *mdisp, int face_grid_size, int orig_grid_size, int loop) +{ + int origin[2]; + int step_x[2]; + int step_y[2]; + + const int grid_offset = orig_grid_size - 1; + origin[0] = grid_offset; + origin[1] = grid_offset; + + switch (loop) { + case 0: + step_x[0] = -1; + step_x[1] = 0; + + step_y[0] = 0; + step_y[1] = -1; + + break; + case 1: + step_x[0] = 0; + step_x[1] = 1; + + step_y[0] = -1; + step_y[1] = -0; + break; + case 2: + step_x[0] = 1; + step_x[1] = 0; + + step_y[0] = 0; + step_y[1] = 1; + break; + case 3: + step_x[0] = 0; + step_x[1] = -1; + + step_y[0] = 1; + step_y[1] = 0; + break; + default: + BLI_assert(!"Should never happen"); + break; + } + + for (int y = 0; y < orig_grid_size; y++) { + for (int x = 0; x < orig_grid_size; x++) { + const int remap_x = origin[1] + (step_x[1] * x) + (step_y[1] * y); + const int remap_y = origin[0] + (step_x[0] * x) + (step_y[0] * y); + + const int final_index = remap_x + remap_y * face_grid_size; + copy_v3_v3(face_grid[final_index], mdisp->disps[x + y * orig_grid_size]); + } + } +} + +/** + * Writes a buffer containing the 4 grids in the correct orientation of the 4 loops of a face into + * the main #MultiresUnsubdivideGrid that is being extracted. + */ +static void write_face_grid_in_unsubdivide_grid(MultiresUnsubdivideGrid *grid, + float (*face_grid)[3], + int face_grid_size, + int gunsub_x, + int gunsub_y) +{ + const int grid_it = face_grid_size - 1; + for (int y = 0; y < face_grid_size; y++) { + for (int x = 0; x < face_grid_size; x++) { + const int remap_x = (grid_it * gunsub_x) + x; + const int remap_y = (grid_it * gunsub_y) + y; + + const int remap_index_y = grid->grid_size - remap_x - 1; + const int remap_index_x = grid->grid_size - remap_y - 1; + const int grid_index = remap_index_x + (remap_index_y * grid->grid_size); + copy_v3_v3(grid->grid_co[grid_index], face_grid[x + y * face_grid_size]); + } + } +} + +/** + * Stores the data from the mdisps grids of the loops of the face f + * into the new grid for the new base mesh. + * + * Used when there are already grids in the original mesh. + */ +static void store_grid_data(MultiresUnsubdivideContext *context, + MultiresUnsubdivideGrid *grid, + BMVert *v, + BMFace *f, + int grid_x, + int grid_y) +{ + + Mesh *original_mesh = context->original_mesh; + MPoly *poly = &original_mesh->mpoly[BM_elem_index_get(f)]; + + const int corner_vertex_index = BM_elem_index_get(v); + + /* Calculates an offset to write the grids correctly oriented in the main + * #MultiresUnsubdivideGrid. */ + int loop_offset = 0; + for (int i = 0; i < poly->totloop; i++) { + const int loop_index = poly->loopstart + i; + MLoop *l = &original_mesh->mloop[loop_index]; + if (l->v == corner_vertex_index) { + loop_offset = i; + break; + } + } + + /* Write the 4 grids of the current quad with the right orientation into the face_grid buffer. */ + const int grid_size = BKE_ccg_gridsize(context->num_original_levels); + const int face_grid_size = BKE_ccg_gridsize(context->num_original_levels + 1); + const int face_grid_area = face_grid_size * face_grid_size; + float(*face_grid)[3] = MEM_calloc_arrayN(face_grid_area, 3 * sizeof(float), "face_grid"); + + for (int i = 0; i < poly->totloop; i++) { + const int loop_index = poly->loopstart + i; + MDisps *mdisp = &context->original_mdisp[loop_index]; + int quad_loop = i - loop_offset; + if (quad_loop < 0) { + quad_loop += 4; + } + if (quad_loop >= 4) { + quad_loop -= 4; + } + write_loop_in_face_grid(face_grid, mdisp, face_grid_size, grid_size, quad_loop); + } + + /* Write the face_grid buffer in the correct position in the #MultiresUnsubdivideGrids that is + * being extracted. */ + write_face_grid_in_unsubdivide_grid(grid, face_grid, face_grid_size, grid_x, grid_y); + + MEM_freeN(face_grid); +} + +/** + * Stores the data into the new grid from a #BMVert. + * Used when there are no grids in the original mesh. + */ +static void store_vertex_data(MultiresUnsubdivideGrid *grid, BMVert *v, int grid_x, int grid_y) +{ + const int remap_index_y = grid->grid_size - 1 - grid_x; + const int remap_index_x = grid->grid_size - 1 - grid_y; + + const int grid_index = remap_index_x + (remap_index_y * grid->grid_size); + + copy_v3_v3(grid->grid_co[grid_index], v->co); +} + +/** + * Main function to extract data from the original bmesh and MDISPS as grids for the new base mesh. + */ +static void multires_unsubdivide_extract_single_grid_from_face_edge( + MultiresUnsubdivideContext *context, + BMFace *f1, + BMEdge *e1, + bool flip_grid, + MultiresUnsubdivideGrid *grid) +{ + BMVert *initial_vertex; + BMEdge *initial_edge_x; + BMEdge *initial_edge_y; + + const int grid_size = BKE_ccg_gridsize(context->num_new_levels); + const int unsubdiv_grid_size = grid->grid_size = BKE_ccg_gridsize(context->num_total_levels); + grid->grid_size = unsubdiv_grid_size; + grid->grid_co = MEM_calloc_arrayN( + unsubdiv_grid_size * unsubdiv_grid_size, 3 * sizeof(float), "grids coordinates"); + + /* Get the vertex on the corner of the grid. This vertex was tagged previously as it also exist + * on the base mesh. */ + initial_edge_x = e1; + if (BM_elem_flag_test(initial_edge_x->v1, BM_ELEM_TAG)) { + initial_vertex = initial_edge_x->v1; + } + else { + initial_vertex = initial_edge_x->v2; + } + + /* From that vertex, get the edge that defines the grid Y axis for extraction. */ + initial_edge_y = get_initial_edge_y(f1, initial_edge_x, initial_vertex); + + if (flip_grid) { + BMEdge *edge_temp; + edge_temp = initial_edge_x; + initial_edge_x = initial_edge_y; + initial_edge_y = edge_temp; + } + + int grid_x = 0; + int grid_y = 0; + + BMVert *current_vertex_x = initial_vertex; + BMEdge *edge_x = initial_edge_x; + + BMVert *current_vertex_y = initial_vertex; + BMEdge *edge_y = initial_edge_y; + BMEdge *prev_edge_y = initial_edge_y; + + BMFace *current_face = f1; + BMFace *grid_face = f1; + + /* If the data is going to be extracted from the already existing grids, there is no need to go + * to the last vertex of the iteration as that coordinate is also included in the grids + * corresponding to the loop of the face of the previous iteration. */ + int grid_iteration_max_steps = grid_size; + if (context->num_original_levels > 0) { + grid_iteration_max_steps = grid_size - 1; + } + + /* Iterate over the mesh vertices in a grid pattern using the axis defined by the two initial + * edges. */ + while (grid_y < grid_iteration_max_steps) { + + grid_face = current_face; + + while (grid_x < grid_iteration_max_steps) { + if (context->num_original_levels == 0) { + /* If there were no grids on the original mesh, extract the data directly from the + * vertices. */ + store_vertex_data(grid, current_vertex_x, grid_x, grid_y); + edge_x = edge_step(current_vertex_x, edge_x, ¤t_vertex_x); + } + else { + /* If there were grids in the original mesh, extract the data from the grids and iterate + * over the faces. */ + store_grid_data(context, grid, current_vertex_x, grid_face, grid_x, grid_y); + edge_x = edge_step(current_vertex_x, edge_x, ¤t_vertex_x); + grid_face = face_step(edge_x, grid_face); + } + + grid_x++; + } + grid_x = 0; + + edge_y = edge_step(current_vertex_y, edge_y, ¤t_vertex_y); + current_vertex_x = current_vertex_y; + + /* Get the next edge_x to extract the next row of the grid. This needs to be done because there + * may be two edges connected to current_vertex_x that belong to two different grids. */ + BMIter iter; + BMEdge *ed; + BMFace *f; + BM_ITER_ELEM (ed, &iter, current_vertex_x, BM_EDGES_OF_VERT) { + if (ed != prev_edge_y && BM_edge_in_face(ed, current_face)) { + edge_x = ed; + break; + } + } + BM_ITER_ELEM (f, &iter, edge_x, BM_FACES_OF_EDGE) { + if (f != current_face) { + current_face = f; + break; + } + } + + prev_edge_y = edge_y; + grid_y++; + } +} + +/** + * Returns the l+1 and l-1 vertices of the base mesh poly were the grid from the face f1 and edge + * e1 is going to be extracted. + * + * These vertices should always have an corresponding existing vertex on the base mesh. + */ +static void multires_unsubdivide_get_grid_corners_on_base_mesh(BMFace *f1, + BMEdge *e1, + BMVert **r_corner_x, + BMVert **r_corner_y) +{ + BMVert *initial_vertex; + BMEdge *initial_edge_x; + BMEdge *initial_edge_y; + + initial_edge_x = e1; + if (BM_elem_flag_test(initial_edge_x->v1, BM_ELEM_TAG)) { + initial_vertex = initial_edge_x->v1; + } + else { + initial_vertex = initial_edge_x->v2; + } + + /* From that vertex, get the edge that defines the grid Y axis for extraction. */ + initial_edge_y = get_initial_edge_y(f1, initial_edge_x, initial_vertex); + + BMVert *current_vertex_x = initial_vertex; + BMEdge *edge_x = initial_edge_x; + + BMVert *current_vertex_y = initial_vertex; + BMEdge *edge_y = initial_edge_y; + + /* Do an edge step until it finds a tagged vertex, which is part of the base mesh. */ + /* x axis */ + edge_x = edge_step(current_vertex_x, edge_x, ¤t_vertex_x); + while (!BM_elem_flag_test(current_vertex_x, BM_ELEM_TAG)) { + edge_x = edge_step(current_vertex_x, edge_x, ¤t_vertex_x); + } + (*r_corner_x) = current_vertex_x; + + /* Same for y axis */ + edge_y = edge_step(current_vertex_y, edge_y, ¤t_vertex_y); + while (!BM_elem_flag_test(current_vertex_y, BM_ELEM_TAG)) { + edge_y = edge_step(current_vertex_y, edge_y, ¤t_vertex_y); + } + (*r_corner_y) = current_vertex_y; +} + +static BMesh *get_bmesh_from_mesh(Mesh *mesh) +{ + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); + BMesh *bm = BM_mesh_create(&allocsize, + &((struct BMeshCreateParams){ + .use_toolflags = true, + })); + + BM_mesh_bm_from_me(bm, + mesh, + (&(struct BMeshFromMeshParams){ + .calc_face_normal = true, + })); + + return bm; +} + +/* Data-layer names to store the original indices of the elements before modifying the mesh. */ +static const char lname[] = "l_remap_index"; +static const char vname[] = "v_remap_index"; + +static void multires_unsubdivide_free_original_datalayers(Mesh *mesh) +{ + const int l_layer_index = CustomData_get_named_layer_index(&mesh->ldata, CD_PROP_INT, lname); + if (l_layer_index != -1) { + CustomData_free_layer(&mesh->ldata, CD_PROP_INT, mesh->totloop, l_layer_index); + } + + const int v_layer_index = CustomData_get_named_layer_index(&mesh->vdata, CD_PROP_INT, vname); + if (v_layer_index != -1) { + CustomData_free_layer(&mesh->vdata, CD_PROP_INT, mesh->totvert, v_layer_index); + } +} + +/** + * Generates two data-layers to map loops and vertices from base mesh to original mesh after + * dissolving the vertices. + */ +static void multires_unsubdivide_add_original_index_datalayers(Mesh *mesh) +{ + multires_unsubdivide_free_original_datalayers(mesh); + + int *l_index = CustomData_add_layer_named( + &mesh->ldata, CD_PROP_INT, CD_CALLOC, NULL, mesh->totloop, lname); + + int *v_index = CustomData_add_layer_named( + &mesh->vdata, CD_PROP_INT, CD_CALLOC, NULL, mesh->totvert, vname); + + /* Initialize these data-layer with the indices in the current mesh. */ + for (int i = 0; i < mesh->totloop; i++) { + l_index[i] = i; + } + for (int i = 0; i < mesh->totvert; i++) { + v_index[i] = i; + } +} + +static void multires_unsubdivide_prepare_original_bmesh_for_extract( + MultiresUnsubdivideContext *context) +{ + + Mesh *original_mesh = context->original_mesh; + Mesh *base_mesh = context->base_mesh; + + BMesh *bm_original_mesh = context->bm_original_mesh = get_bmesh_from_mesh(original_mesh); + + /* Initialize the elem tables. */ + BM_mesh_elem_table_ensure(bm_original_mesh, BM_EDGE); + BM_mesh_elem_table_ensure(bm_original_mesh, BM_FACE); + BM_mesh_elem_table_ensure(bm_original_mesh, BM_VERT); + + /* Disable all flags. */ + BM_mesh_elem_hflag_disable_all( + bm_original_mesh, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + BM_mesh_elem_hflag_disable_all( + bm_original_mesh, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); + + /* Get the mapping data-layer. */ + context->base_to_orig_vmap = CustomData_get_layer_named(&base_mesh->vdata, CD_PROP_INT, vname); + + /* Tag the base mesh vertices in the original mesh. */ + for (int i = 0; i < base_mesh->totvert; i++) { + int vert_basemesh_index = context->base_to_orig_vmap[i]; + BMVert *v = BM_vert_at_index(bm_original_mesh, vert_basemesh_index); + BM_elem_flag_set(v, BM_ELEM_TAG, true); + } + + /* Create a map from loop index to poly index for the original mesh. */ + context->loop_to_face_map = MEM_calloc_arrayN(sizeof(int), original_mesh->totloop, "loop map"); + + for (int i = 0; i < original_mesh->totpoly; i++) { + MPoly *poly = &original_mesh->mpoly[i]; + for (int l = 0; l < poly->totloop; l++) { + int original_loop_index = l + poly->loopstart; + context->loop_to_face_map[original_loop_index] = i; + } + } +} + +/** + * Checks the orientation of the loops to flip the x and y axis when extracting the grid if + * necessary. + */ +static bool multires_unsubdivide_flip_grid_x_axis(Mesh *mesh, int poly, int loop, int v_x) +{ + MPoly *p = &mesh->mpoly[poly]; + + MLoop *l_first = &mesh->mloop[p->loopstart]; + if ((loop == (p->loopstart + (p->totloop - 1))) && l_first->v == v_x) { + return true; + } + + int next_l_index = loop + 1; + if (next_l_index < p->loopstart + p->totloop) { + MLoop *l_next = &mesh->mloop[next_l_index]; + if (l_next->v == v_x) { + return true; + } + } + + return false; +} + +static void multires_unsubdivide_extract_grids(MultiresUnsubdivideContext *context) +{ + Mesh *original_mesh = context->original_mesh; + Mesh *base_mesh = context->base_mesh; + + BMesh *bm_original_mesh = context->bm_original_mesh; + + context->num_grids = base_mesh->totloop; + context->base_mesh_grids = MEM_calloc_arrayN( + sizeof(MultiresUnsubdivideGrid), base_mesh->totloop, "grids"); + + /* Based on the existing indices in the data-layers, generate two vertex indices maps. */ + /* From vertex index in original to vertex index in base and from vertex index in base to vertex + * index in original. */ + int *orig_to_base_vmap = MEM_calloc_arrayN(sizeof(int), bm_original_mesh->totvert, "orig vmap"); + int *base_to_orig_vmap = MEM_calloc_arrayN(sizeof(int), base_mesh->totvert, "base vmap"); + + context->base_to_orig_vmap = CustomData_get_layer_named(&base_mesh->vdata, CD_PROP_INT, vname); + for (int i = 0; i < base_mesh->totvert; i++) { + base_to_orig_vmap[i] = context->base_to_orig_vmap[i]; + } + + /* If an index in original does not exist in base (it was dissolved when creating the new base + * mesh, return -1. */ + for (int i = 0; i < original_mesh->totvert; i++) { + orig_to_base_vmap[i] = -1; + } + + for (int i = 0; i < base_mesh->totvert; i++) { + const int orig_vertex_index = context->base_to_orig_vmap[i]; + orig_to_base_vmap[orig_vertex_index] = i; + } + + /* Add the original data-layers to the base mesh to have the loop indices stored in a data-layer, + * so they can be used from #BMesh. */ + multires_unsubdivide_add_original_index_datalayers(base_mesh); + + const int base_l_layer_index = CustomData_get_named_layer_index( + &base_mesh->ldata, CD_PROP_INT, lname); + BMesh *bm_base_mesh = get_bmesh_from_mesh(base_mesh); + BMIter iter, iter_a, iter_b; + BMVert *v; + BMLoop *l, *lb; + + BM_mesh_elem_table_ensure(bm_base_mesh, BM_VERT); + BM_mesh_elem_table_ensure(bm_base_mesh, BM_FACE); + + /* Get the data-layer that contains the loops indices. */ + const int base_l_offset = CustomData_get_n_offset( + &bm_base_mesh->ldata, CD_PROP_INT, base_l_layer_index); + + /* Main loop for extracting the grids. Iterates over the base mesh vertices. */ + BM_ITER_MESH (v, &iter, bm_base_mesh, BM_VERTS_OF_MESH) { + + /* For each base mesh vertex, get the corresponding #BMVert of the original mesh using the + * vertex map. */ + const int orig_vertex_index = base_to_orig_vmap[BM_elem_index_get(v)]; + BMVert *vert_original = BM_vert_at_index(bm_original_mesh, orig_vertex_index); + + /* Iterate over the loops of that vertex in the original mesh. */ + BM_ITER_ELEM (l, &iter_a, vert_original, BM_LOOPS_OF_VERT) { + /* For each loop, get the two vertices that should map to the l+1 and l-1 vertices in the + * base mesh of the poly of grid that is going to be extracted. */ + BMVert *corner_x, *corner_y; + multires_unsubdivide_get_grid_corners_on_base_mesh(l->f, l->e, &corner_x, &corner_y); + + /* Map the two obtained vertices to the base mesh. */ + const int corner_x_index = orig_to_base_vmap[BM_elem_index_get(corner_x)]; + const int corner_y_index = orig_to_base_vmap[BM_elem_index_get(corner_y)]; + + /* Iterate over the loops of the same vertex in the base mesh. With the previously obtained + * vertices and the current vertex it is possible to get the index of the loop in the base + * mesh the grid that is going to be extracted belongs to. */ + BM_ITER_ELEM (lb, &iter_b, v, BM_LOOPS_OF_VERT) { + BMFace *base_face = lb->f; + BMVert *base_corner_x = BM_vert_at_index(bm_base_mesh, corner_x_index); + BMVert *base_corner_y = BM_vert_at_index(bm_base_mesh, corner_y_index); + /* If this is the correct loop in the base mesh, the original vertex and the two corners + * should be in the loop's face. */ + if (BM_vert_in_face(base_corner_x, base_face) && + BM_vert_in_face(base_corner_y, base_face)) { + /* Get the index of the loop. */ + const int base_mesh_loop_index = BM_ELEM_CD_GET_INT(lb, base_l_offset); + const int base_mesh_face_index = BM_elem_index_get(base_face); + + /* Check the orientation of the loops in case that is needed to flip the x and y axis + * when extracting the grid. */ + const bool flip_grid = multires_unsubdivide_flip_grid_x_axis( + base_mesh, base_mesh_face_index, base_mesh_loop_index, corner_x_index); + + /* Extract the grid for that loop. */ + context->base_mesh_grids[base_mesh_loop_index].grid_index = base_mesh_loop_index; + multires_unsubdivide_extract_single_grid_from_face_edge( + context, l->f, l->e, !flip_grid, &context->base_mesh_grids[base_mesh_loop_index]); + + break; + } + } + } + } + + MEM_freeN(orig_to_base_vmap); + MEM_freeN(base_to_orig_vmap); + + BM_mesh_free(bm_base_mesh); + multires_unsubdivide_free_original_datalayers(base_mesh); +} + +static void multires_unsubdivide_private_extract_data_free(MultiresUnsubdivideContext *context) +{ + if (context->bm_original_mesh != NULL) { + BM_mesh_free(context->bm_original_mesh); + } + MEM_SAFE_FREE(context->loop_to_face_map); +} + +void multires_unsubdivide_context_init(MultiresUnsubdivideContext *context, + Mesh *original_mesh, + struct MultiresModifierData *mmd) +{ + context->original_mesh = original_mesh; + context->num_new_levels = 0; + context->num_total_levels = 0; + context->num_original_levels = mmd->totlvl; +} + +bool multires_unsubdivide_to_basemesh(MultiresUnsubdivideContext *context) +{ + Mesh *original_mesh = context->original_mesh; + + /* Prepare the data-layers to map base to original. */ + multires_unsubdivide_add_original_index_datalayers(original_mesh); + BMesh *bm_base_mesh = get_bmesh_from_mesh(original_mesh); + + /* Un-subdivide as many iterations as possible. */ + context->num_new_levels = 0; + int num_levels_left = context->max_new_levels; + while (num_levels_left > 0 && multires_unsubdivide_single_level(bm_base_mesh)) { + context->num_new_levels++; + num_levels_left--; + } + + /* If no un-subdivide steps were possible, free the bmesh, the map data-layers and stop. */ + if (context->num_new_levels == 0) { + multires_unsubdivide_free_original_datalayers(original_mesh); + BM_mesh_free(bm_base_mesh); + return false; + } + + /* Calculate the final levels for the new grids over base mesh. */ + context->num_total_levels = context->num_new_levels + context->num_original_levels; + + /* Store the new base-mesh as a mesh in context, free bmesh. */ + context->base_mesh = BKE_mesh_new_nomain(0, 0, 0, 0, 0); + BM_mesh_bm_to_me(NULL, + bm_base_mesh, + context->base_mesh, + (&(struct BMeshToMeshParams){ + .calc_object_remap = true, + })); + BM_mesh_free(bm_base_mesh); + + /* Initialize bmesh and maps for the original mesh and extract the grids. */ + + multires_unsubdivide_prepare_original_bmesh_for_extract(context); + multires_unsubdivide_extract_grids(context); + + return true; +} + +void multires_unsubdivide_context_free(MultiresUnsubdivideContext *context) +{ + multires_unsubdivide_private_extract_data_free(context); + for (int i = 0; i < context->num_grids; i++) { + if (context->base_mesh_grids[i].grid_size > 0) { + MEM_SAFE_FREE(context->base_mesh_grids[i].grid_co); + } + } + MEM_SAFE_FREE(context->base_mesh_grids); +} + +/** + * This function allocates new mdisps with the right size to fit the new extracted grids from the + * base mesh and copies the data to them. + */ +static void multires_create_grids_in_unsubdivided_base_mesh(MultiresUnsubdivideContext *context, + Mesh *base_mesh) +{ + /* Free the current MDISPS and create a new ones. */ + if (CustomData_has_layer(&base_mesh->ldata, CD_MDISPS)) { + CustomData_free_layers(&base_mesh->ldata, CD_MDISPS, base_mesh->totloop); + } + MDisps *mdisps = CustomData_add_layer( + &base_mesh->ldata, CD_MDISPS, CD_CALLOC, NULL, base_mesh->totloop); + + const int totdisp = pow_i(BKE_ccg_gridsize(context->num_total_levels), 2); + const int totloop = base_mesh->totloop; + + BLI_assert(base_mesh->totloop == context->num_grids); + + /* Allocate the MDISPS grids and copy the extracted data from context. */ + for (int i = 0; i < totloop; i++) { + float(*disps)[3] = MEM_calloc_arrayN(totdisp, 3 * sizeof(float), "multires disps"); + + if (mdisps[i].disps) { + MEM_freeN(mdisps[i].disps); + } + + for (int j = 0; j < totdisp; j++) { + if (context->base_mesh_grids[i].grid_co) { + copy_v3_v3(disps[j], context->base_mesh_grids[i].grid_co[j]); + } + } + + mdisps[i].disps = disps; + mdisps[i].totdisp = totdisp; + mdisps[i].level = context->num_total_levels; + } +} + +int multiresModifier_rebuild_subdiv(struct Depsgraph *depsgraph, + struct Object *object, + struct MultiresModifierData *mmd, + int rebuild_limit, + bool switch_view_to_lower_level) +{ + Mesh *mesh = object->data; + + multires_force_sculpt_rebuild(object); + + MultiresUnsubdivideContext unsubdiv_context = {0}; + MultiresReshapeContext reshape_context = {0}; + + multires_unsubdivide_context_init(&unsubdiv_context, mesh, mmd); + + /* Convert and store the existing grids in object space if available. */ + if (mmd->totlvl != 0) { + if (!multires_reshape_context_create_from_object(&reshape_context, depsgraph, object, mmd)) { + return 0; + } + + multires_reshape_store_original_grids(&reshape_context); + multires_reshape_assign_final_coords_from_mdisps(&reshape_context); + unsubdiv_context.original_mdisp = reshape_context.mdisps; + } + + /* Set the limit for the levels that should be rebuild. */ + unsubdiv_context.max_new_levels = rebuild_limit; + + /* Un-subdivide and create the data for the new grids. */ + if (multires_unsubdivide_to_basemesh(&unsubdiv_context) == 0) { + /* If there was no possible to rebuild any level, free the data and return. */ + if (mmd->totlvl != 0) { + multires_reshape_object_grids_to_tangent_displacement(&reshape_context); + multires_unsubdivide_context_free(&unsubdiv_context); + } + multires_reshape_context_free(&reshape_context); + return 0; + } + + /* Free the reshape context used to convert the data from the original grids to object space. */ + if (mmd->totlvl != 0) { + multires_reshape_context_free(&reshape_context); + } + + /* Copy the new base mesh to the original mesh. */ + BKE_mesh_nomain_to_mesh(unsubdiv_context.base_mesh, object->data, object, &CD_MASK_MESH, true); + Mesh *base_mesh = object->data; + multires_create_grids_in_unsubdivided_base_mesh(&unsubdiv_context, base_mesh); + + /* Update the levels in the modifier. Force always to display at level 0 as it contains the new + * created level. */ + mmd->totlvl = (char)unsubdiv_context.num_total_levels; + + if (switch_view_to_lower_level) { + mmd->sculptlvl = 0; + mmd->lvl = 0; + } + else { + mmd->sculptlvl = (char)(mmd->sculptlvl + unsubdiv_context.num_new_levels); + mmd->lvl = (char)(mmd->lvl + unsubdiv_context.num_new_levels); + } + + mmd->renderlvl = (char)(mmd->renderlvl + unsubdiv_context.num_new_levels); + + /* Create a reshape context to convert the MDISPS data to tangent displacement. It can be the + * same as the previous one as a new Subdivision needs to be created for the new base mesh. */ + if (!multires_reshape_context_create_from_base_mesh(&reshape_context, depsgraph, object, mmd)) { + return 0; + } + multires_reshape_object_grids_to_tangent_displacement(&reshape_context); + multires_reshape_context_free(&reshape_context); + + /* Free the un-subdivide context and return the total number of levels that were rebuild. */ + const int rebuild_subdvis = unsubdiv_context.num_new_levels; + multires_unsubdivide_context_free(&unsubdiv_context); + + return rebuild_subdvis; +} diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.h b/source/blender/blenkernel/intern/multires_unsubdivide.h new file mode 100644 index 00000000000..55d425c08bc --- /dev/null +++ b/source/blender/blenkernel/intern/multires_unsubdivide.h @@ -0,0 +1,94 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup bke + */ + +#ifndef __BKE_INTERN_MULTIRES_UNSUBDIVIDE_H__ +#define __BKE_INTERN_MULTIRES_UNSUBDIVIDE_H__ + +#include "BLI_sys_types.h" + +struct BMesh; +struct Depsgraph; +struct Mesh; +struct MultiresModifierData; +struct Object; + +typedef struct MultiresUnsubdivideGrid { + /* For sanity checks. */ + int grid_index; + int grid_size; + + /** Grid coordinates in object space. */ + float (*grid_co)[3]; + +} MultiresUnsubdivideGrid; + +typedef struct MultiresUnsubdivideContext { + /* Input Mesh to un-subdivide. */ + struct Mesh *original_mesh; + struct MDisps *original_mdisp; + + /** Number of subdivision in the grids of the input mesh. */ + int num_original_levels; + + /** Level 0 base mesh after applying the maximum amount of unsubdivisions. */ + struct Mesh *base_mesh; + + /** Limit on how many levels down the unsubdivide operation should create, if possible. */ + int max_new_levels; + + /** New levels that were created after unsubdividing. */ + int num_new_levels; + + /** + * Number of subdivisions that should be applied to the base mesh. + * (num_new_levels + num_original_levels). + */ + int num_total_levels; + + /** Data for the new grids, indexed by base mesh loop index. */ + int num_grids; + struct MultiresUnsubdivideGrid *base_mesh_grids; + + /* Private data. */ + struct BMesh *bm_original_mesh; + int *loop_to_face_map; + int *base_to_orig_vmap; +} MultiresUnsubdivideContext; + +/* -------------------------------------------------------------------- + * Construct/destruct reshape context. + */ + +void multires_unsubdivide_context_init(MultiresUnsubdivideContext *context, + struct Mesh *original_mesh, + struct MultiresModifierData *mmd); +void multires_unsubdivide_context_free(MultiresUnsubdivideContext *context); + +/* -------------------------------------------------------------------- + * Rebuild Lower Subdivisions. + */ + +/* Rebuilds all subdivision to the level 0 base mesh. */ +bool multires_unsubdivide_to_basemesh(MultiresUnsubdivideContext *context); + +#endif /* __BKE_INTERN_MULTIRES_UNSUBDIVIDE_H__ */ diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index a12307b7c18..4df8b6f595a 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -83,6 +83,7 @@ #include "BKE_editmesh.h" #include "BKE_effect.h" #include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_font.h" #include "BKE_global.h" #include "BKE_gpencil.h" diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 4835a4c6a62..8957628c76a 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -453,9 +453,7 @@ static void ocean_compute_htilda(void *__restrict userdata, } } -static void ocean_compute_displacement_y(TaskPool *__restrict pool, - void *UNUSED(taskdata), - int UNUSED(threadid)) +static void ocean_compute_displacement_y(TaskPool *__restrict pool, void *UNUSED(taskdata)) { OceanSimulateData *osd = BLI_task_pool_user_data(pool); const Ocean *o = osd->o; @@ -463,9 +461,7 @@ static void ocean_compute_displacement_y(TaskPool *__restrict pool, fftw_execute(o->_disp_y_plan); } -static void ocean_compute_displacement_x(TaskPool *__restrict pool, - void *UNUSED(taskdata), - int UNUSED(threadid)) +static void ocean_compute_displacement_x(TaskPool *__restrict pool, void *UNUSED(taskdata)) { OceanSimulateData *osd = BLI_task_pool_user_data(pool); const Ocean *o = osd->o; @@ -494,9 +490,7 @@ static void ocean_compute_displacement_x(TaskPool *__restrict pool, fftw_execute(o->_disp_x_plan); } -static void ocean_compute_displacement_z(TaskPool *__restrict pool, - void *UNUSED(taskdata), - int UNUSED(threadid)) +static void ocean_compute_displacement_z(TaskPool *__restrict pool, void *UNUSED(taskdata)) { OceanSimulateData *osd = BLI_task_pool_user_data(pool); const Ocean *o = osd->o; @@ -525,9 +519,7 @@ static void ocean_compute_displacement_z(TaskPool *__restrict pool, fftw_execute(o->_disp_z_plan); } -static void ocean_compute_jacobian_jxx(TaskPool *__restrict pool, - void *UNUSED(taskdata), - int UNUSED(threadid)) +static void ocean_compute_jacobian_jxx(TaskPool *__restrict pool, void *UNUSED(taskdata)) { OceanSimulateData *osd = BLI_task_pool_user_data(pool); const Ocean *o = osd->o; @@ -560,9 +552,7 @@ static void ocean_compute_jacobian_jxx(TaskPool *__restrict pool, } } -static void ocean_compute_jacobian_jzz(TaskPool *__restrict pool, - void *UNUSED(taskdata), - int UNUSED(threadid)) +static void ocean_compute_jacobian_jzz(TaskPool *__restrict pool, void *UNUSED(taskdata)) { OceanSimulateData *osd = BLI_task_pool_user_data(pool); const Ocean *o = osd->o; @@ -595,9 +585,7 @@ static void ocean_compute_jacobian_jzz(TaskPool *__restrict pool, } } -static void ocean_compute_jacobian_jxz(TaskPool *__restrict pool, - void *UNUSED(taskdata), - int UNUSED(threadid)) +static void ocean_compute_jacobian_jxz(TaskPool *__restrict pool, void *UNUSED(taskdata)) { OceanSimulateData *osd = BLI_task_pool_user_data(pool); const Ocean *o = osd->o; @@ -624,9 +612,7 @@ static void ocean_compute_jacobian_jxz(TaskPool *__restrict pool, fftw_execute(o->_Jxz_plan); } -static void ocean_compute_normal_x(TaskPool *__restrict pool, - void *UNUSED(taskdata), - int UNUSED(threadid)) +static void ocean_compute_normal_x(TaskPool *__restrict pool, void *UNUSED(taskdata)) { OceanSimulateData *osd = BLI_task_pool_user_data(pool); const Ocean *o = osd->o; @@ -645,9 +631,7 @@ static void ocean_compute_normal_x(TaskPool *__restrict pool, fftw_execute(o->_N_x_plan); } -static void ocean_compute_normal_z(TaskPool *__restrict pool, - void *UNUSED(taskdata), - int UNUSED(threadid)) +static void ocean_compute_normal_z(TaskPool *__restrict pool, void *UNUSED(taskdata)) { OceanSimulateData *osd = BLI_task_pool_user_data(pool); const Ocean *o = osd->o; @@ -668,7 +652,6 @@ static void ocean_compute_normal_z(TaskPool *__restrict pool, void BKE_ocean_simulate(struct Ocean *o, float t, float scale, float chop_amount) { - TaskScheduler *scheduler = BLI_task_scheduler_get(); TaskPool *pool; OceanSimulateData osd; @@ -680,7 +663,7 @@ void BKE_ocean_simulate(struct Ocean *o, float t, float scale, float chop_amount osd.scale = scale; osd.chop_amount = chop_amount; - pool = BLI_task_pool_create(scheduler, &osd, TASK_PRIORITY_HIGH); + pool = BLI_task_pool_create(&osd, TASK_PRIORITY_HIGH); BLI_rw_mutex_lock(&o->oceanmutex, THREAD_LOCK_WRITE); diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 4bbbae7081d..4b90e320052 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -1414,7 +1414,7 @@ MultiresModifierData *BKE_sculpt_multires_active(Scene *scene, Object *ob) continue; } - if (BKE_multires_sculpt_level_get(mmd) > 0) { + if (mmd->sculptlvl > 0) { return mmd; } else { @@ -1686,7 +1686,7 @@ int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd) * isn't one already */ if (mmd && !CustomData_has_layer(&me->ldata, CD_GRID_PAINT_MASK)) { GridPaintMask *gmask; - int level = max_ii(1, BKE_multires_sculpt_level_get(mmd)); + int level = max_ii(1, mmd->sculptlvl); int gridsize = BKE_ccg_gridsize(level); int gridarea = gridsize * gridsize; int i, j; diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 0e35fa5d19f..e5ebae02558 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -2787,9 +2787,7 @@ static void psys_thread_create_path(ParticleTask *task, } } -static void exec_child_path_cache(TaskPool *__restrict UNUSED(pool), - void *taskdata, - int UNUSED(threadid)) +static void exec_child_path_cache(TaskPool *__restrict UNUSED(pool), void *taskdata) { ParticleTask *task = taskdata; ParticleThreadContext *ctx = task->ctx; @@ -2810,7 +2808,6 @@ void psys_cache_child_paths(ParticleSimulationData *sim, const bool editupdate, const bool use_render_params) { - TaskScheduler *task_scheduler; TaskPool *task_pool; ParticleThreadContext ctx; ParticleTask *tasks_parent, *tasks_child; @@ -2826,8 +2823,7 @@ void psys_cache_child_paths(ParticleSimulationData *sim, return; } - task_scheduler = BLI_task_scheduler_get(); - task_pool = BLI_task_pool_create(task_scheduler, &ctx, TASK_PRIORITY_LOW); + task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW); totchild = ctx.totchild; totparent = ctx.totparent; @@ -3377,7 +3373,6 @@ void psys_cache_edit_paths(Depsgraph *depsgraph, TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); - settings.scheduling_mode = TASK_SCHEDULING_DYNAMIC; BLI_task_parallel_range(0, edit->totpoint, &iter_data, psys_cache_edit_paths_iter, &settings); edit->totcached = totpart; @@ -4009,7 +4004,7 @@ static void get_cpa_texture(Mesh *mesh, break; } - externtex(mtex, texvec, &value, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL, false, false); + RE_texture_evaluate(mtex, texvec, 0, NULL, false, false, &value, rgba); if ((event & mtex->mapto) & PAMAP_ROUGH) { ptex->rough1 = ptex->rough2 = ptex->roughe = texture_value_blend( @@ -4124,7 +4119,7 @@ void psys_get_texture( break; } - externtex(mtex, texvec, &value, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL, false, false); + RE_texture_evaluate(mtex, texvec, 0, NULL, false, false, &value, rgba); if ((event & mtex->mapto) & PAMAP_TIME) { /* the first time has to set the base value for time regardless of blend mode */ diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c index d91e27a92d8..7b9b2484dbe 100644 --- a/source/blender/blenkernel/intern/particle_distribute.c +++ b/source/blender/blenkernel/intern/particle_distribute.c @@ -773,9 +773,7 @@ static void distribute_children_exec(ParticleTask *thread, ChildParticle *cpa, i } } -static void exec_distribute_parent(TaskPool *__restrict UNUSED(pool), - void *taskdata, - int UNUSED(threadid)) +static void exec_distribute_parent(TaskPool *__restrict UNUSED(pool), void *taskdata) { ParticleTask *task = taskdata; ParticleSystem *psys = task->ctx->sim.psys; @@ -804,9 +802,7 @@ static void exec_distribute_parent(TaskPool *__restrict UNUSED(pool), } } -static void exec_distribute_child(TaskPool *__restrict UNUSED(pool), - void *taskdata, - int UNUSED(threadid)) +static void exec_distribute_child(TaskPool *__restrict UNUSED(pool), void *taskdata) { ParticleTask *task = taskdata; ParticleSystem *psys = task->ctx->sim.psys; @@ -1324,7 +1320,6 @@ static void psys_task_init_distribute(ParticleTask *task, ParticleSimulationData static void distribute_particles_on_dm(ParticleSimulationData *sim, int from) { - TaskScheduler *task_scheduler; TaskPool *task_pool; ParticleThreadContext ctx; ParticleTask *tasks; @@ -1336,8 +1331,7 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from) return; } - task_scheduler = BLI_task_scheduler_get(); - task_pool = BLI_task_pool_create(task_scheduler, &ctx, TASK_PRIORITY_LOW); + task_pool = BLI_task_pool_create(&ctx, TASK_PRIORITY_LOW); totpart = (from == PART_FROM_CHILD ? sim->psys->totchild : sim->psys->totpart); psys_tasks_create(&ctx, 0, totpart, &tasks, &numtasks); diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index e6d672ad9d9..aa541b3d715 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -1115,11 +1115,11 @@ static void pbvh_faces_update_normals(PBVH *bvh, PBVHNode **nodes, int totnode) .vnors = vnors, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BKE_pbvh_parallel_range(0, totnode, &data, pbvh_update_normals_accum_task_cb, &settings); - BKE_pbvh_parallel_range(0, totnode, &data, pbvh_update_normals_store_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_accum_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_store_task_cb, &settings); MEM_freeN(vnors); } @@ -1169,9 +1169,9 @@ static void pbvh_update_mask_redraw(PBVH *bvh, PBVHNode **nodes, int totnode, in .flag = flag, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BKE_pbvh_parallel_range(0, totnode, &data, pbvh_update_mask_redraw_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, pbvh_update_mask_redraw_task_cb, &settings); } static void pbvh_update_visibility_redraw_task_cb(void *__restrict userdata, @@ -1207,9 +1207,9 @@ static void pbvh_update_visibility_redraw(PBVH *bvh, PBVHNode **nodes, int totno .flag = flag, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BKE_pbvh_parallel_range(0, totnode, &data, pbvh_update_visibility_redraw_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, pbvh_update_visibility_redraw_task_cb, &settings); } static void pbvh_update_BB_redraw_task_cb(void *__restrict userdata, @@ -1245,9 +1245,9 @@ void pbvh_update_BB_redraw(PBVH *bvh, PBVHNode **nodes, int totnode, int flag) .flag = flag, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BKE_pbvh_parallel_range(0, totnode, &data, pbvh_update_BB_redraw_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, pbvh_update_BB_redraw_task_cb, &settings); } static int pbvh_get_buffers_update_flags(PBVH *bvh, bool show_vcol) @@ -1365,9 +1365,9 @@ static void pbvh_update_draw_buffers( .show_vcol = show_vcol, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BKE_pbvh_parallel_range(0, totnode, &data, pbvh_update_draw_buffer_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, pbvh_update_draw_buffer_cb, &settings); } static int pbvh_flush_bb(PBVH *bvh, PBVHNode *node, int flag) @@ -1558,9 +1558,9 @@ static void pbvh_update_visibility(PBVH *bvh, PBVHNode **nodes, int totnode) .nodes = nodes, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BKE_pbvh_parallel_range(0, totnode, &data, pbvh_update_visibility_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, pbvh_update_visibility_task_cb, &settings); } void BKE_pbvh_update_visibility(PBVH *bvh) @@ -2994,7 +2994,7 @@ void BKE_pbvh_get_frustum_planes(PBVH *bvh, PBVHFrustumPlanes *planes) } } -void BKE_pbvh_parallel_range_settings(PBVHParallelSettings *settings, +void BKE_pbvh_parallel_range_settings(TaskParallelSettings *settings, bool use_threading, int totnode) { diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c index f999a98faac..a08bbf182fa 100644 --- a/source/blender/blenkernel/intern/seqcache.c +++ b/source/blender/blenkernel/intern/seqcache.c @@ -1225,6 +1225,10 @@ struct ImBuf *BKE_sequencer_cache_get( seq = BKE_sequencer_prefetch_get_original_sequence(seq, scene); } + if (!seq) { + return NULL; + } + if (!scene->ed->cache) { seq_cache_create(context->bmain, scene); } @@ -1287,6 +1291,10 @@ bool BKE_sequencer_cache_put_if_possible(const SeqRenderData *context, seq = BKE_sequencer_prefetch_get_original_sequence(seq, scene); } + if (!seq) { + return false; + } + if (BKE_sequencer_cache_recycle_item(scene)) { BKE_sequencer_cache_put(context, seq, cfra, type, ibuf, cost, skip_disk_cache); return true; diff --git a/source/blender/blenkernel/intern/seqeffects.c b/source/blender/blenkernel/intern/seqeffects.c index c1644e39afa..9fa43ed0a5f 100644 --- a/source/blender/blenkernel/intern/seqeffects.c +++ b/source/blender/blenkernel/intern/seqeffects.c @@ -61,6 +61,8 @@ #include "BLF_api.h" +static struct SeqEffectHandle get_sequence_effect_impl(int seq_type); + static void slice_get_byte_buffers(const SeqRenderData *context, const ImBuf *ibuf1, const ImBuf *ibuf2, @@ -3118,7 +3120,7 @@ static void copy_speed_effect(Sequence *dst, Sequence *src, const int UNUSED(fla static int early_out_speed(Sequence *UNUSED(seq), float UNUSED(facf0), float UNUSED(facf1)) { - return EARLY_USE_INPUT_1; + return EARLY_DO_EFFECT; } static void store_icu_yrange_speed(Sequence *seq, short UNUSED(adrcode), float *ymin, float *ymax) @@ -3248,36 +3250,60 @@ void BKE_sequence_effect_speed_rebuild_map(Scene *scene, Sequence *seq, bool for } } +/* Override cfra when rendering speed effect input. */ +float BKE_sequencer_speed_effect_target_frame_get(const SeqRenderData *context, + Sequence *seq, + float cfra, + int input) +{ + int nr = BKE_sequencer_give_stripelem_index(seq, cfra); + SpeedControlVars *s = (SpeedControlVars *)seq->effectdata; + BKE_sequence_effect_speed_rebuild_map(context->scene, seq, false); + + /* No interpolation. */ + if ((s->flags & SEQ_SPEED_USE_INTERPOLATION) == 0) { + return seq->start + s->frameMap[nr]; + } + + /* We need to provide current and next image for interpolation. */ + if (input == 0) { /* Current frame. */ + return floor(seq->start + s->frameMap[nr]); + } + else { /* Next frame. */ + return ceil(seq->start + s->frameMap[nr]); + } +} + +static float speed_effect_interpolation_ratio_get(SpeedControlVars *s, Sequence *seq, float cfra) +{ + int nr = BKE_sequencer_give_stripelem_index(seq, cfra); + return s->frameMap[nr] - floor(s->frameMap[nr]); +} + static ImBuf *do_speed_effect(const SeqRenderData *context, - Sequence *UNUSED(seq), - float UNUSED(cfra), + Sequence *seq, + float cfra, float facf0, float facf1, ImBuf *ibuf1, ImBuf *ibuf2, ImBuf *ibuf3) { - ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3); + SpeedControlVars *s = (SpeedControlVars *)seq->effectdata; + struct SeqEffectHandle cross_effect = get_sequence_effect_impl(SEQ_TYPE_CROSS); + ImBuf *out; - if (out->rect_float) { - do_cross_effect_float(facf0, - facf1, - context->rectx, - context->recty, - ibuf1->rect_float, - ibuf2->rect_float, - out->rect_float); + if (s->flags & SEQ_SPEED_USE_INTERPOLATION) { + out = prepare_effect_imbufs(context, ibuf1, ibuf2, ibuf3); + facf0 = facf1 = speed_effect_interpolation_ratio_get(s, seq, cfra); + /* Current frame is ibuf1, next frame is ibuf2. */ + out = BKE_sequencer_effect_execute_threaded( + &cross_effect, context, NULL, cfra, facf0, facf1, ibuf1, ibuf2, ibuf3); + return out; } - else { - do_cross_effect_byte(facf0, - facf1, - context->rectx, - context->recty, - (unsigned char *)ibuf1->rect, - (unsigned char *)ibuf2->rect, - (unsigned char *)out->rect); - } - return out; + + /* No interpolation. */ + return IMB_dupImBuf(ibuf1); } /*********************** overdrop *************************/ diff --git a/source/blender/blenkernel/intern/seqprefetch.c b/source/blender/blenkernel/intern/seqprefetch.c index bba829529a3..1425bff48a8 100644 --- a/source/blender/blenkernel/intern/seqprefetch.c +++ b/source/blender/blenkernel/intern/seqprefetch.c @@ -134,19 +134,29 @@ static bool seq_prefetch_job_is_waiting(Scene *scene) return pfjob->waiting; } -/* for cache context swapping */ -Sequence *BKE_sequencer_prefetch_get_original_sequence(Sequence *seq, Scene *scene) +static Sequence *sequencer_prefetch_get_original_sequence(Sequence *seq, ListBase *seqbase) { - Editing *ed = scene->ed; - ListBase *seqbase = &ed->seqbase; - Sequence *seq_orig = NULL; - - for (seq_orig = (Sequence *)seqbase->first; seq_orig; seq_orig = seq_orig->next) { + LISTBASE_FOREACH (Sequence *, seq_orig, seqbase) { if (strcmp(seq->name, seq_orig->name) == 0) { - break; + return seq_orig; + } + + if (seq_orig->type == SEQ_TYPE_META) { + Sequence *match = sequencer_prefetch_get_original_sequence(seq, &seq_orig->seqbase); + if (match != NULL) { + return match; + } } } - return seq_orig; + + return NULL; +} + +/* for cache context swapping */ +Sequence *BKE_sequencer_prefetch_get_original_sequence(Sequence *seq, Scene *scene) +{ + Editing *ed = scene->ed; + return sequencer_prefetch_get_original_sequence(seq, &ed->seqbase); } /* for cache context swapping */ diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 5136c526416..b962c99b109 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -1394,7 +1394,7 @@ static void multibuf(ImBuf *ibuf, const float fmul) } } -static float give_stripelem_index(Sequence *seq, float cfra) +float BKE_sequencer_give_stripelem_index(Sequence *seq, float cfra) { float nr; int sta = seq->start; @@ -1452,7 +1452,7 @@ StripElem *BKE_sequencer_give_stripelem(Sequence *seq, int cfra) * all other strips don't use this... */ - int nr = (int)give_stripelem_index(seq, cfra); + int nr = (int)BKE_sequencer_give_stripelem_index(seq, cfra); if (nr == -1 || se == NULL) { return NULL; @@ -1889,7 +1889,7 @@ static bool seq_proxy_get_fname(Editing *ed, frameno = 1; } else { - frameno = (int)give_stripelem_index(seq, cfra) + seq->anim_startofs; + frameno = (int)BKE_sequencer_give_stripelem_index(seq, cfra) + seq->anim_startofs; BLI_snprintf(name, PROXY_MAXFILE, "%s/proxy_misc/%d/####%s", dir, proxy_size_number, suffix); } @@ -1922,7 +1922,7 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c } if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE) { - int frameno = (int)give_stripelem_index(seq, cfra) + seq->anim_startofs; + int frameno = (int)BKE_sequencer_give_stripelem_index(seq, cfra) + seq->anim_startofs; if (proxy->anim == NULL) { if (seq_proxy_get_fname(ed, seq, cfra, psize, name, context->view_id) == 0) { return NULL; @@ -2887,15 +2887,15 @@ static void *render_effect_execute_do_thread(void *thread_data_v) return NULL; } -static ImBuf *seq_render_effect_execute_threaded(struct SeqEffectHandle *sh, - const SeqRenderData *context, - Sequence *seq, - float cfra, - float facf0, - float facf1, - ImBuf *ibuf1, - ImBuf *ibuf2, - ImBuf *ibuf3) +ImBuf *BKE_sequencer_effect_execute_threaded(struct SeqEffectHandle *sh, + const SeqRenderData *context, + Sequence *seq, + float cfra, + float facf0, + float facf1, + ImBuf *ibuf1, + ImBuf *ibuf2, + ImBuf *ibuf3) { RenderEffectInitData init_data; ImBuf *out = sh->init_execution(context, ibuf1, ibuf2, ibuf3); @@ -2969,14 +2969,21 @@ static ImBuf *seq_render_effect_strip_impl(const SeqRenderData *context, break; case EARLY_DO_EFFECT: for (i = 0; i < 3; i++) { - if (input[i]) { - ibuf[i] = seq_render_strip(context, state, input[i], cfra); + /* Speed effect requires time remapping of cfra for input(s). */ + if (input[1] && seq->type == SEQ_TYPE_SPEED) { + float target_frame = BKE_sequencer_speed_effect_target_frame_get(context, seq, cfra, i); + ibuf[i] = seq_render_strip(context, state, input[i], target_frame); + } + else { /* Other effects. */ + if (input[i]) { + ibuf[i] = seq_render_strip(context, state, input[i], cfra); + } } } if (ibuf[0] && ibuf[1]) { if (sh.multithreaded) { - out = seq_render_effect_execute_threaded( + out = BKE_sequencer_effect_execute_threaded( &sh, context, seq, cfra, fac, facf, ibuf[0], ibuf[1], ibuf[2]); } else { @@ -3679,9 +3686,8 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, float cfra) { ImBuf *ibuf = NULL; - float nr = give_stripelem_index(seq, cfra); - int type = (seq->type & SEQ_TYPE_EFFECT && seq->type != SEQ_TYPE_SPEED) ? SEQ_TYPE_EFFECT : - seq->type; + float nr = BKE_sequencer_give_stripelem_index(seq, cfra); + int type = (seq->type & SEQ_TYPE_EFFECT) ? SEQ_TYPE_EFFECT : seq->type; switch (type) { case SEQ_TYPE_META: { ibuf = do_render_strip_seqbase(context, state, seq, nr); @@ -3723,21 +3729,8 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, break; } - case SEQ_TYPE_SPEED: { - float f_cfra; - SpeedControlVars *s = (SpeedControlVars *)seq->effectdata; - - BKE_sequence_effect_speed_rebuild_map(context->scene, seq, false); - - /* weeek! */ - f_cfra = seq->start + s->frameMap[(int)nr]; - ibuf = seq_render_strip(context, state, seq->seq1, f_cfra); - - break; - } - case SEQ_TYPE_EFFECT: { - ibuf = seq_render_effect_strip_impl(context, state, seq, seq->start + nr); + ibuf = seq_render_effect_strip_impl(context, state, seq, cfra); break; } @@ -3931,7 +3924,7 @@ static ImBuf *seq_render_strip_stack_apply_effect( if (swap_input) { if (sh.multithreaded) { - out = seq_render_effect_execute_threaded( + out = BKE_sequencer_effect_execute_threaded( &sh, context, seq, cfra, facf, facf, ibuf2, ibuf1, NULL); } else { @@ -3940,7 +3933,7 @@ static ImBuf *seq_render_strip_stack_apply_effect( } else { if (sh.multithreaded) { - out = seq_render_effect_execute_threaded( + out = BKE_sequencer_effect_execute_threaded( &sh, context, seq, cfra, facf, facf, ibuf1, ibuf2, NULL); } else { diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index 64fe396d6a3..5500918428f 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -751,12 +751,12 @@ static void sound_start_play_scene(Scene *scene) } } -static float get_cur_time(Scene *scene) +static double get_cur_time(Scene *scene) { /* We divide by the current framelen to take into account time remapping. * Otherwise we will get the wrong starting time which will break A/V sync. * See T74111 for further details. */ - return FRA2TIME((CFRA + SUBFRA) / scene->r.framelen); + return FRA2TIME((CFRA + SUBFRA) / (double)scene->r.framelen); } void BKE_sound_play_scene(Scene *scene) @@ -764,7 +764,7 @@ void BKE_sound_play_scene(Scene *scene) sound_verify_evaluated_id(&scene->id); AUD_Status status; - const float cur_time = get_cur_time(scene); + const double cur_time = get_cur_time(scene); AUD_Device_lock(sound_device); @@ -811,8 +811,8 @@ void BKE_sound_seek_scene(Main *bmain, Scene *scene) bScreen *screen; int animation_playing; - const float one_frame = (float)(1.0 / FPS); - const float cur_time = FRA2TIME(CFRA); + const double one_frame = 1.0 / FPS; + const double cur_time = FRA2TIME(CFRA); AUD_Device_lock(sound_device); @@ -869,7 +869,7 @@ void BKE_sound_seek_scene(Main *bmain, Scene *scene) AUD_Device_unlock(sound_device); } -float BKE_sound_sync_scene(Scene *scene) +double BKE_sound_sync_scene(Scene *scene) { sound_verify_evaluated_id(&scene->id); @@ -1230,7 +1230,7 @@ void BKE_sound_stop_scene(Scene *UNUSED(scene)) void BKE_sound_seek_scene(Main *UNUSED(bmain), Scene *UNUSED(scene)) { } -float BKE_sound_sync_scene(Scene *UNUSED(scene)) +double BKE_sound_sync_scene(Scene *UNUSED(scene)) { return NAN_FLT; } @@ -1341,7 +1341,7 @@ void BKE_sound_jack_sync_callback_set(SoundJackSyncCallback callback) #endif } -void BKE_sound_jack_scene_update(Scene *scene, int mode, float time) +void BKE_sound_jack_scene_update(Scene *scene, int mode, double time) { sound_verify_evaluated_id(&scene->id); diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c index 6637b3e9b69..d5d5530c1ce 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.c +++ b/source/blender/blenkernel/intern/subdiv_ccg.c @@ -40,9 +40,9 @@ #include "opensubdiv_topology_refiner_capi.h" -/* ============================================================================= - * Various forward declarations. - */ +/* -------------------------------------------------------------------- */ +/** \name Various forward declarations + * \{ */ static void subdiv_ccg_average_all_boundaries_and_corners(SubdivCCG *subdiv_ccg, CCGKey *key); @@ -50,9 +50,11 @@ static void subdiv_ccg_average_inner_face_grids(SubdivCCG *subdiv_ccg, CCGKey *key, SubdivCCGFace *face); -/* ============================================================================= - * Generally useful internal helpers. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Generally useful internal helpers + * \{ */ /* Number of floats in per-vertex elements. */ static int num_element_float_get(const SubdivCCG *subdiv_ccg) @@ -74,9 +76,11 @@ static int element_size_bytes_get(const SubdivCCG *subdiv_ccg) return sizeof(float) * num_element_float_get(subdiv_ccg); } -/* ============================================================================= - * Internal helpers for CCG creation. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Internal helpers for CCG creation + * \{ */ static void subdiv_ccg_init_layers(SubdivCCG *subdiv_ccg, const SubdivToCCGSettings *settings) { @@ -158,9 +162,11 @@ static void subdiv_ccg_alloc_elements(SubdivCCG *subdiv_ccg, Subdiv *subdiv) } } -/* ============================================================================= - * Grids evaluation. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Grids evaluation + * \{ */ typedef struct CCGEvalGridsData { SubdivCCG *subdiv_ccg; @@ -556,9 +562,11 @@ static void subdiv_ccg_init_faces_neighborhood(SubdivCCG *subdiv_ccg) subdiv_ccg_init_faces_vertex_neighborhood(subdiv_ccg); } -/* ============================================================================= - * Creation / evaluation. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Creation / evaluation + * \{ */ SubdivCCG *BKE_subdiv_to_ccg(Subdiv *subdiv, const SubdivToCCGSettings *settings, @@ -670,9 +678,11 @@ void BKE_subdiv_ccg_key_top_level(CCGKey *key, const SubdivCCG *subdiv_ccg) BKE_subdiv_ccg_key(key, subdiv_ccg, subdiv_ccg->level); } -/* ============================================================================= - * Normals. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Normals + * \{ */ typedef struct RecalcInnerNormalsData { SubdivCCG *subdiv_ccg; @@ -885,9 +895,11 @@ void BKE_subdiv_ccg_update_normals(SubdivCCG *subdiv_ccg, subdiv_ccg_average_all_boundaries_and_corners(subdiv_ccg, &key); } -/* ============================================================================= - * Boundary averaging/stitching. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Boundary averaging/stitching + * \{ */ typedef struct AverageInnerGridsData { SubdivCCG *subdiv_ccg; @@ -1244,9 +1256,11 @@ void BKE_subdiv_ccg_topology_counters(const SubdivCCG *subdiv_ccg, *r_num_loops = *r_num_faces * 4; } -/* ============================================================================= - * Neighbors. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Neighbors + * \{ */ void BKE_subdiv_ccg_print_coord(const char *message, const SubdivCCGCoord *coord) { @@ -1791,3 +1805,5 @@ int BKE_subdiv_ccg_grid_to_face_index(const SubdivCCG *subdiv_ccg, const int gri const int face_index = face - subdiv_ccg->faces; return face_index; } + +/** \} */ diff --git a/source/blender/blenkernel/intern/subdiv_deform.c b/source/blender/blenkernel/intern/subdiv_deform.c index db0a51c390b..f03cf4c4d21 100644 --- a/source/blender/blenkernel/intern/subdiv_deform.c +++ b/source/blender/blenkernel/intern/subdiv_deform.c @@ -39,9 +39,9 @@ #include "MEM_guardedalloc.h" -/* ================================================================================================ - * Subdivision context. - */ +/* -------------------------------------------------------------------- */ +/** \name Subdivision context + * \{ */ typedef struct SubdivDeformContext { const Mesh *coarse_mesh; @@ -77,9 +77,11 @@ static void subdiv_mesh_context_free(SubdivDeformContext *ctx) MEM_SAFE_FREE(ctx->accumulated_counters); } -/* ================================================================================================ - * Accumulation helpers. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Accumulation helpers + * \{ */ static void subdiv_accumulate_vertex_displacement(SubdivDeformContext *ctx, const int ptex_face_index, @@ -105,9 +107,11 @@ static void subdiv_accumulate_vertex_displacement(SubdivDeformContext *ctx, ++ctx->accumulated_counters[vertex_index]; } -/* ================================================================================================ - * Subdivision callbacks. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Subdivision callbacks + * \{ */ static bool subdiv_mesh_topology_info(const SubdivForeachContext *foreach_context, const int UNUSED(num_vertices), @@ -165,9 +169,11 @@ static void subdiv_mesh_vertex_corner(const SubdivForeachContext *foreach_contex add_v3_v3(vertex_co, D); } -/* ================================================================================================ - * Initialization. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Initialization + * \{ */ static void setup_foreach_callbacks(const SubdivDeformContext *subdiv_context, SubdivForeachContext *foreach_context) @@ -182,9 +188,11 @@ static void setup_foreach_callbacks(const SubdivDeformContext *subdiv_context, foreach_context->vertex_corner = subdiv_mesh_vertex_corner; } -/* ================================================================================================ - * Public entry point. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public entry point + * \{ */ void BKE_subdiv_deform_coarse_vertices(struct Subdiv *subdiv, const struct Mesh *coarse_mesh, @@ -234,3 +242,5 @@ void BKE_subdiv_deform_coarse_vertices(struct Subdiv *subdiv, /* Free used memory. */ subdiv_mesh_context_free(&subdiv_context); } + +/** \} */ diff --git a/source/blender/blenkernel/intern/subdiv_foreach.c b/source/blender/blenkernel/intern/subdiv_foreach.c index 0884f40952f..ff7f6fad5f0 100644 --- a/source/blender/blenkernel/intern/subdiv_foreach.c +++ b/source/blender/blenkernel/intern/subdiv_foreach.c @@ -40,9 +40,9 @@ #include "MEM_guardedalloc.h" -/* ============================================================================= - * General helpers. - */ +/* -------------------------------------------------------------------- */ +/** \name General helpers + * \{ */ /* Number of ptex faces for a given polygon. */ BLI_INLINE int num_ptex_faces_per_poly_get(const MPoly *poly) @@ -75,9 +75,11 @@ BLI_INLINE int ptex_face_resolution_get(const MPoly *poly, int resolution) return (poly->totloop == 4) ? (resolution) : ((resolution >> 1) + 1); } -/* ============================================================================= - * Context which is passed to all threaded tasks. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Context which is passed to all threaded tasks + * \{ */ typedef struct SubdivForeachTaskContext { const Mesh *coarse_mesh; @@ -122,9 +124,11 @@ typedef struct SubdivForeachTaskContext { BLI_bitmap *coarse_edges_used_map; } SubdivForeachTaskContext; -/* ============================================================================= - * Threading helpers. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Threading helpers + * \{ */ static void *subdiv_foreach_tls_alloc(SubdivForeachTaskContext *ctx) { @@ -148,9 +152,11 @@ static void subdiv_foreach_tls_free(SubdivForeachTaskContext *ctx, void *tls) MEM_freeN(tls); } -/* ============================================================================= - * Initialization. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Initialization + * \{ */ /* NOTE: Expects edge map to be zeroed. */ static void subdiv_foreach_ctx_count(SubdivForeachTaskContext *ctx) @@ -294,9 +300,11 @@ static void subdiv_foreach_ctx_free(SubdivForeachTaskContext *ctx) MEM_freeN(ctx->subdiv_polygon_offset); } -/* ============================================================================= - * Vertex traversal process. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vertex traversal process + * \{ */ /* Traversal of corner vertices. They are coming from coarse vertices. */ @@ -706,9 +714,11 @@ static void subdiv_foreach_vertices(SubdivForeachTaskContext *ctx, void *tls, co } } -/* ============================================================================= - * Edge traversal process. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Edge traversal process + * \{ */ /* TODO(sergey): Coarse edge are always NONE, consider getting rid of it. */ static int subdiv_foreach_edges_row(SubdivForeachTaskContext *ctx, @@ -1022,9 +1032,11 @@ static void subdiv_foreach_boundary_edges(SubdivForeachTaskContext *ctx, ctx->foreach_context, tls, coarse_edge_index, subdiv_edge_index, v1, v2); } -/* ============================================================================= - * Loops traversal. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Loops traversal + * \{ */ static void rotate_indices(const int rot, int *a, int *b, int *c, int *d) { @@ -1666,9 +1678,11 @@ static void subdiv_foreach_loops(SubdivForeachTaskContext *ctx, void *tls, int p } } -/* ============================================================================= - * Polygons traverse process. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Polygons traverse process + * \{ */ static void subdiv_foreach_polys(SubdivForeachTaskContext *ctx, void *tls, int poly_index) { @@ -1697,9 +1711,11 @@ static void subdiv_foreach_polys(SubdivForeachTaskContext *ctx, void *tls, int p } } -/* ============================================================================= - * Loose elements traverse process. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Loose elements traverse process + * \{ */ static void subdiv_foreach_loose_vertices_task(void *__restrict userdata, const int coarse_vertex_index, @@ -1754,9 +1770,11 @@ static void subdiv_foreach_vertices_of_loose_edges_task(void *__restrict userdat } } -/* ============================================================================= - * Subdivision process entry points. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Subdivision process entry points + * \{ */ static void subdiv_foreach_single_geometry_vertices(SubdivForeachTaskContext *ctx, void *tls) { @@ -1908,3 +1926,5 @@ bool BKE_subdiv_foreach_subdiv_geometry(Subdiv *subdiv, subdiv_foreach_ctx_free(&ctx); return true; } + +/** \} */ diff --git a/source/blender/blenkernel/intern/subdiv_mesh.c b/source/blender/blenkernel/intern/subdiv_mesh.c index bd091108b11..987cc0311c7 100644 --- a/source/blender/blenkernel/intern/subdiv_mesh.c +++ b/source/blender/blenkernel/intern/subdiv_mesh.c @@ -41,9 +41,9 @@ #include "MEM_guardedalloc.h" -/* ============================================================================= - * Subdivision context. - */ +/* -------------------------------------------------------------------- */ +/** \name Subdivision Context + * \{ */ typedef struct SubdivMeshContext { const SubdivToMeshSettings *settings; @@ -119,9 +119,11 @@ static void subdiv_mesh_context_free(SubdivMeshContext *ctx) MEM_SAFE_FREE(ctx->accumulated_counters); } -/* ============================================================================= - * Loop custom data copy helpers. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Loop custom data copy helpers + * \{ */ typedef struct LoopsOfPtex { /* First loop of the ptex, starts at ptex (0, 0) and goes in u direction. */ @@ -159,9 +161,11 @@ static void loops_of_ptex_get(const SubdivMeshContext *ctx, } } -/* ============================================================================= - * Vertex custom data interpolation helpers. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vertex custom data interpolation helpers + * \{ */ /* TODO(sergey): Somehow de-duplicate with loops storage, without too much * exception cases all over the code. */ @@ -295,9 +299,11 @@ static void vertex_interpolation_end(VerticesForInterpolation *vertex_interpolat } } -/* ============================================================================= - * Loop custom data interpolation helpers. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Loop custom data interpolation helpers + * \{ */ typedef struct LoopsForInterpolation { /* This field points to a loop data which is to be used for interpolation. @@ -413,9 +419,11 @@ static void loop_interpolation_end(LoopsForInterpolation *loop_interpolation) } } -/* ============================================================================= - * TLS. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name TLS + * \{ */ typedef struct SubdivMeshTLS { bool vertex_interpolation_initialized; @@ -440,9 +448,11 @@ static void subdiv_mesh_tls_free(void *tls_v) } } -/* ============================================================================= - * Evaluation helper functions. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Evaluation helper functions + * \{ */ static void eval_final_point_and_vertex_normal(Subdiv *subdiv, const int ptex_face_index, @@ -459,9 +469,11 @@ static void eval_final_point_and_vertex_normal(Subdiv *subdiv, } } -/* ============================================================================= - * Accumulation helpers. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Accumulation helpers + * \{ */ static void subdiv_accumulate_vertex_normal_and_displacement(SubdivMeshContext *ctx, const int ptex_face_index, @@ -490,9 +502,11 @@ static void subdiv_accumulate_vertex_normal_and_displacement(SubdivMeshContext * ++ctx->accumulated_counters[subdiv_vertex_index]; } -/* ============================================================================= - * Callbacks. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks + * \{ */ static bool subdiv_mesh_topology_info(const SubdivForeachContext *foreach_context, const int num_vertices, @@ -513,9 +527,11 @@ static bool subdiv_mesh_topology_info(const SubdivForeachContext *foreach_contex return true; } -/* ============================================================================= - * Vertex subdivision process. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Vertex subdivision process + * \{ */ static void subdiv_vertex_data_copy(const SubdivMeshContext *ctx, const MVert *coarse_vertex, @@ -778,9 +794,11 @@ static void subdiv_mesh_vertex_inner(const SubdivForeachContext *foreach_context subdiv_mesh_tag_center_vertex(coarse_poly, subdiv_vert, u, v); } -/* ============================================================================= - * Edge subdivision process. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Edge subdivision process + * \{ */ static void subdiv_copy_edge_data(SubdivMeshContext *ctx, MEdge *subdiv_edge, @@ -827,9 +845,11 @@ static void subdiv_mesh_edge(const SubdivForeachContext *foreach_context, subdiv_edge->v2 = subdiv_v2; } -/* ============================================================================= - * Loops creation/interpolation. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Loops creation/interpolation + * \{ */ static void subdiv_interpolate_loop_data(const SubdivMeshContext *ctx, MLoop *subdiv_loop, @@ -921,9 +941,11 @@ static void subdiv_mesh_loop(const SubdivForeachContext *foreach_context, subdiv_loop->e = subdiv_edge_index; } -/* ============================================================================= - * Polygons subdivision process. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Polygons subdivision process + * \{ */ static void subdiv_copy_poly_data(const SubdivMeshContext *ctx, MPoly *subdiv_poly, @@ -955,9 +977,11 @@ static void subdiv_mesh_poly(const SubdivForeachContext *foreach_context, subdiv_poly->totloop = num_loops; } -/* ============================================================================= - * Loose elements subdivision process. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Loose elements subdivision process + * \{ */ static void subdiv_mesh_vertex_loose(const SubdivForeachContext *foreach_context, void *UNUSED(tls), @@ -1127,9 +1151,11 @@ static void subdiv_mesh_vertex_of_loose_edge(const struct SubdivForeachContext * normal_float_to_short_v3(subdiv_vertex->no, no); } -/* ============================================================================= - * Initialization. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Initialization + * \{ */ static void setup_foreach_callbacks(const SubdivMeshContext *subdiv_context, SubdivForeachContext *foreach_context) @@ -1157,9 +1183,11 @@ static void setup_foreach_callbacks(const SubdivMeshContext *subdiv_context, foreach_context->user_data_tls_free = subdiv_mesh_tls_free; } -/* ============================================================================= - * Public entry point. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public entry point + * \{ */ Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv, const SubdivToMeshSettings *settings, @@ -1206,3 +1234,5 @@ Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv, subdiv_mesh_context_free(&subdiv_context); return result; } + +/** \} */ diff --git a/source/blender/blenkernel/intern/tracking_util.c b/source/blender/blenkernel/intern/tracking_util.c index 5f7452e4775..629c01ec298 100644 --- a/source/blender/blenkernel/intern/tracking_util.c +++ b/source/blender/blenkernel/intern/tracking_util.c @@ -451,6 +451,12 @@ static void distortion_model_parameters_from_tracking( camera_intrinsics_options->division_k1 = camera->division_k1; camera_intrinsics_options->division_k2 = camera->division_k2; return; + + case TRACKING_DISTORTION_MODEL_NUKE: + camera_intrinsics_options->distortion_model = LIBMV_DISTORTION_MODEL_NUKE; + camera_intrinsics_options->nuke_k1 = camera->nuke_k1; + camera_intrinsics_options->nuke_k2 = camera->nuke_k2; + return; } /* Unknown distortion model, which might be due to opening newer file in older Blender. @@ -479,6 +485,12 @@ static void distortion_model_parameters_from_options( camera->division_k1 = camera_intrinsics_options->division_k1; camera->division_k2 = camera_intrinsics_options->division_k2; return; + + case LIBMV_DISTORTION_MODEL_NUKE: + camera->distortion_model = TRACKING_DISTORTION_MODEL_NUKE; + camera->nuke_k1 = camera_intrinsics_options->nuke_k1; + camera->nuke_k2 = camera_intrinsics_options->nuke_k2; + return; } /* Libmv returned distortion model which is not known to Blender. This is a logical error in code diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh index 61d57599619..9dd8341aa76 100644 --- a/source/blender/blenlib/BLI_array.hh +++ b/source/blender/blenlib/BLI_array.hh @@ -80,7 +80,7 @@ class Array { m_allocator = other.m_allocator; m_data = this->get_buffer_for_size(other.size()); - copy_n(other.begin(), m_size, m_data); + uninitialized_copy_n(other.begin(), m_size, m_data); } Array(Array &&other) noexcept @@ -202,6 +202,11 @@ class Array { return IndexRange(m_size); } + Allocator &allocator() + { + return m_allocator; + } + private: T *get_buffer_for_size(uint size) { diff --git a/source/blender/blenlib/BLI_dot_export.hh b/source/blender/blenlib/BLI_dot_export.hh new file mode 100644 index 00000000000..08c37fec01e --- /dev/null +++ b/source/blender/blenlib/BLI_dot_export.hh @@ -0,0 +1,290 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __BLI_DOT_EXPORT_HH__ +#define __BLI_DOT_EXPORT_HH__ + +/** + * Language grammar: https://www.graphviz.org/doc/info/lang.html + * Attributes: https://www.graphviz.org/doc/info/attrs.html + * Node Shapes: https://www.graphviz.org/doc/info/shapes.html + * Preview: https://dreampuf.github.io/GraphvizOnline + */ + +#include "BLI_map.hh" +#include "BLI_optional.hh" +#include "BLI_set.hh" +#include "BLI_string_map.hh" +#include "BLI_utility_mixins.hh" +#include "BLI_vector.hh" + +#include "BLI_dot_export_attribute_enums.hh" + +#include <sstream> + +namespace BLI { +namespace DotExport { + +class Graph; +class DirectedGraph; +class UndirectedGraph; +class Node; +class NodePort; +class DirectedEdge; +class UndirectedEdge; +class Cluster; +class AttributeList; + +class AttributeList { + private: + Map<std::string, std::string> m_attributes; + + public: + void export__as_bracket_list(std::stringstream &ss) const; + + void set(StringRef key, StringRef value) + { + m_attributes.add_override(key, value); + } +}; + +class Graph { + private: + AttributeList m_attributes; + Vector<std::unique_ptr<Node>> m_nodes; + Vector<std::unique_ptr<Cluster>> m_clusters; + + Set<Node *> m_top_level_nodes; + Set<Cluster *> m_top_level_clusters; + + friend Cluster; + friend Node; + + public: + Node &new_node(StringRef label); + Cluster &new_cluster(StringRef label = ""); + + void export__declare_nodes_and_clusters(std::stringstream &ss) const; + + void set_attribute(StringRef key, StringRef value) + { + m_attributes.set(key, value); + } + + void set_rankdir(Attr_rankdir rankdir) + { + this->set_attribute("rankdir", rankdir_to_string(rankdir)); + } + + void set_random_cluster_bgcolors(); +}; + +class Cluster { + private: + AttributeList m_attributes; + Graph &m_graph; + Cluster *m_parent = nullptr; + Set<Cluster *> m_children; + Set<Node *> m_nodes; + + friend Graph; + friend Node; + + Cluster(Graph &graph) : m_graph(graph) + { + } + + public: + void export__declare_nodes_and_clusters(std::stringstream &ss) const; + + void set_attribute(StringRef key, StringRef value) + { + m_attributes.set(key, value); + } + + void set_parent_cluster(Cluster *cluster); + void set_parent_cluster(Cluster &cluster) + { + this->set_parent_cluster(&cluster); + } + + void set_random_cluster_bgcolors(); +}; + +class Node { + private: + AttributeList m_attributes; + Graph &m_graph; + Cluster *m_cluster = nullptr; + + friend Graph; + + Node(Graph &graph) : m_graph(graph) + { + } + + public: + const AttributeList &attributes() const + { + return m_attributes; + } + + AttributeList &attributes() + { + return m_attributes; + } + + void set_parent_cluster(Cluster *cluster); + void set_parent_cluster(Cluster &cluster) + { + this->set_parent_cluster(&cluster); + } + + void set_attribute(StringRef key, StringRef value) + { + m_attributes.set(key, value); + } + + void set_shape(Attr_shape shape) + { + this->set_attribute("shape", shape_to_string(shape)); + } + + /* See https://www.graphviz.org/doc/info/attrs.html#k:color. */ + void set_background_color(StringRef name) + { + this->set_attribute("fillcolor", name); + this->set_attribute("style", "filled"); + } + + void export__as_id(std::stringstream &ss) const; + + void export__as_declaration(std::stringstream &ss) const; +}; + +class UndirectedGraph final : public Graph { + private: + Vector<std::unique_ptr<UndirectedEdge>> m_edges; + + public: + std::string to_dot_string() const; + + UndirectedEdge &new_edge(NodePort a, NodePort b); +}; + +class DirectedGraph final : public Graph { + private: + Vector<std::unique_ptr<DirectedEdge>> m_edges; + + public: + std::string to_dot_string() const; + + DirectedEdge &new_edge(NodePort from, NodePort to); +}; + +class NodePort { + private: + Node *m_node; + Optional<std::string> m_port_name; + + public: + NodePort(Node &node, Optional<std::string> port_name = {}) + : m_node(&node), m_port_name(std::move(port_name)) + { + } + + void to_dot_string(std::stringstream &ss) const; +}; + +class Edge : BLI::NonCopyable, BLI::NonMovable { + protected: + AttributeList m_attributes; + NodePort m_a; + NodePort m_b; + + public: + Edge(NodePort a, NodePort b) : m_a(std::move(a)), m_b(std::move(b)) + { + } + + void set_attribute(StringRef key, StringRef value) + { + m_attributes.set(key, value); + } + + void set_arrowhead(Attr_arrowType type) + { + this->set_attribute("arrowhead", arrowType_to_string(type)); + } + + void set_arrowtail(Attr_arrowType type) + { + this->set_attribute("arrowtail", arrowType_to_string(type)); + } + + void set_dir(Attr_dirType type) + { + this->set_attribute("dir", dirType_to_string(type)); + } +}; + +class DirectedEdge : public Edge { + public: + DirectedEdge(NodePort from, NodePort to) : Edge(std::move(from), std::move(to)) + { + } + + void export__as_edge_statement(std::stringstream &ss) const; +}; + +class UndirectedEdge : public Edge { + public: + UndirectedEdge(NodePort a, NodePort b) : Edge(std::move(a), std::move(b)) + { + } + + void export__as_edge_statement(std::stringstream &ss) const; +}; + +std::string color_attr_from_hsv(float h, float s, float v); + +class NodeWithSocketsRef { + private: + Node *m_node; + + public: + NodeWithSocketsRef(Node &node, + StringRef name, + ArrayRef<std::string> input_names, + ArrayRef<std::string> output_names); + + NodePort input(uint index) const + { + std::string port = "\"in" + std::to_string(index) + "\""; + return NodePort(*m_node, port); + } + + NodePort output(uint index) const + { + std::string port = "\"out" + std::to_string(index) + "\""; + return NodePort(*m_node, port); + } +}; + +} // namespace DotExport +} // namespace BLI + +#endif /* __BLI_DOT_EXPORT_HH__ */ diff --git a/source/blender/blenlib/BLI_dot_export_attribute_enums.hh b/source/blender/blenlib/BLI_dot_export_attribute_enums.hh new file mode 100644 index 00000000000..8e61f46dc12 --- /dev/null +++ b/source/blender/blenlib/BLI_dot_export_attribute_enums.hh @@ -0,0 +1,125 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __BLI_DOT_EXPORT_ATTRIBUTE_ENUMS_HH__ +#define __BLI_DOT_EXPORT_ATTRIBUTE_ENUMS_HH__ + +#include "BLI_string_ref.hh" + +namespace BLI { +namespace DotExport { + +enum class Attr_rankdir { + LeftToRight, + TopToBottom, +}; + +inline StringRef rankdir_to_string(Attr_rankdir value) +{ + switch (value) { + case Attr_rankdir::LeftToRight: + return "LR"; + case Attr_rankdir::TopToBottom: + return "TB"; + } + return ""; +} + +enum class Attr_shape { + Rectangle, + Ellipse, + Circle, + Point, + Diamond, + Square, +}; + +inline StringRef shape_to_string(Attr_shape value) +{ + switch (value) { + case Attr_shape::Rectangle: + return "rectangle"; + case Attr_shape::Ellipse: + return "ellipse"; + case Attr_shape::Circle: + return "circle"; + case Attr_shape::Point: + return "point"; + case Attr_shape::Diamond: + return "diamond"; + case Attr_shape::Square: + return "square"; + } + return ""; +} + +enum class Attr_arrowType { + Normal, + Inv, + Dot, + None, + Empty, + Box, + Vee, +}; + +inline StringRef arrowType_to_string(Attr_arrowType value) +{ + switch (value) { + case Attr_arrowType::Normal: + return "normal"; + case Attr_arrowType::Inv: + return "inv"; + case Attr_arrowType::Dot: + return "dot"; + case Attr_arrowType::None: + return "none"; + case Attr_arrowType::Empty: + return "empty"; + case Attr_arrowType::Box: + return "box"; + case Attr_arrowType::Vee: + return "vee"; + } + return ""; +} + +enum class Attr_dirType { + Forward, + Back, + Both, + None, +}; + +inline StringRef dirType_to_string(Attr_dirType value) +{ + switch (value) { + case Attr_dirType::Forward: + return "forward"; + case Attr_dirType::Back: + return "back"; + case Attr_dirType::Both: + return "both"; + case Attr_dirType::None: + return "none"; + } + return ""; +} + +} // namespace DotExport +} // namespace BLI + +#endif /* __BLI_DOT_EXPORT_ATTRIBUTE_ENUMS_HH__ */ diff --git a/source/blender/blenlib/BLI_gsqueue.h b/source/blender/blenlib/BLI_gsqueue.h index dffb2a165ee..b69bdb7057c 100644 --- a/source/blender/blenlib/BLI_gsqueue.h +++ b/source/blender/blenlib/BLI_gsqueue.h @@ -24,6 +24,8 @@ * \ingroup bli */ +#include "BLI_utildefines.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/source/blender/blenlib/BLI_lasso_2d.h b/source/blender/blenlib/BLI_lasso_2d.h index 56db360dab0..fb661c41784 100644 --- a/source/blender/blenlib/BLI_lasso_2d.h +++ b/source/blender/blenlib/BLI_lasso_2d.h @@ -30,14 +30,14 @@ extern "C" { struct rcti; -void BLI_lasso_boundbox(struct rcti *rect, const int mcords[][2], const unsigned int moves); -bool BLI_lasso_is_point_inside(const int mcords[][2], - const unsigned int moves, +void BLI_lasso_boundbox(struct rcti *rect, const int mcoords[][2], const unsigned int mcoords_len); +bool BLI_lasso_is_point_inside(const int mcoords[][2], + const unsigned int mcoords_len, const int sx, const int sy, const int error_value); -bool BLI_lasso_is_edge_inside(const int mcords[][2], - const unsigned int moves, +bool BLI_lasso_is_edge_inside(const int mcoords[][2], + const unsigned int mcoords_len, int x0, int y0, int x1, diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh index 553175b0395..ea5e5da4099 100644 --- a/source/blender/blenlib/BLI_map.hh +++ b/source/blender/blenlib/BLI_map.hh @@ -408,6 +408,19 @@ class Map { } /** + * Return the value that corresponds to the given key. + * If it does not exist yet, insert a new default constructed value and return that. + */ + ValueT &lookup_or_add_default(const KeyT &key) + { + return this->lookup_or_add(key, []() { return ValueT(); }); + } + ValueT &lookup_or_add_default(const KeyT &&key) + { + return this->lookup_or_add(std::move(key), []() { return ValueT(); }); + } + + /** * Get the number of elements in the map. */ uint32_t size() const @@ -416,6 +429,14 @@ class Map { } /** + * Returns true if there are no elements in the map. + */ + bool is_empty() const + { + return this->size() == 0; + } + + /** * Calls the given function for each key-value-pair. */ template<typename FuncT> void foreach_item(const FuncT &func) const diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index 6cfa2d2ced6..a171ff1bb1c 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -62,6 +62,11 @@ MINLINE void swap_v4_v4(float a[4], float b[4]); MINLINE void copy_v2_v2_uchar(unsigned char r[2], const unsigned char a[2]); MINLINE void copy_v3_v3_uchar(unsigned char r[3], const unsigned char a[3]); MINLINE void copy_v4_v4_uchar(unsigned char r[4], const unsigned char a[4]); + +MINLINE void copy_v2_uchar(unsigned char r[2], const unsigned char a); +MINLINE void copy_v3_uchar(unsigned char r[3], const unsigned char a); +MINLINE void copy_v4_uchar(unsigned char r[4], const unsigned char a); + /* char */ MINLINE void copy_v2_v2_char(char r[2], const char a[2]); MINLINE void copy_v3_v3_char(char r[3], const char a[3]); diff --git a/source/blender/blenlib/BLI_open_addressing.hh b/source/blender/blenlib/BLI_open_addressing.hh index d466a915b2f..3bd932350d0 100644 --- a/source/blender/blenlib/BLI_open_addressing.hh +++ b/source/blender/blenlib/BLI_open_addressing.hh @@ -34,6 +34,7 @@ #include <cmath> #include "BLI_allocator.hh" +#include "BLI_array.hh" #include "BLI_math_base.h" #include "BLI_memory_utils.hh" #include "BLI_utildefines.h" @@ -114,8 +115,6 @@ class OpenAddressingArray { * m_slots_set_or_dummy < m_slots_total */ - /* Array containing the actual hash table. Might be a pointer to the inlined storage. */ - Item *m_items; /* Number of items in the hash table. Must be a power of two. */ uint32_t m_item_amount; /* Exponent of the current item amount. */ @@ -130,9 +129,8 @@ class OpenAddressingArray { uint32_t m_slots_usable; /* Can be used to map a hash value into the range of valid slot indices. */ uint32_t m_slot_mask; - Allocator m_allocator; - AlignedBuffer<(uint)sizeof(Item) * s_items_in_small_storage, (uint)alignof(Item)> - m_local_storage; + + Array<Item, s_items_in_small_storage, Allocator> m_items; public: explicit OpenAddressingArray(uint8_t item_exponent = s_small_storage_item_exponent) @@ -147,51 +145,12 @@ class OpenAddressingArray { (uint64_t)s_max_load_factor_numerator, (uint64_t)s_max_load_factor_denominator); - if (m_item_amount <= s_items_in_small_storage) { - m_items = this->small_storage(); - } - else { - m_items = (Item *)m_allocator.allocate_aligned( - (uint32_t)sizeof(Item) * m_item_amount, std::alignment_of<Item>::value, __func__); - } - - for (uint32_t i = 0; i < m_item_amount; i++) { - new (m_items + i) Item(); - } + m_items = Array<Item, s_items_in_small_storage, Allocator>(m_item_amount); } - ~OpenAddressingArray() - { - if (m_items != nullptr) { - for (uint32_t i = 0; i < m_item_amount; i++) { - m_items[i].~Item(); - } - if (!this->is_in_small_storage()) { - m_allocator.deallocate((void *)m_items); - } - } - } - - OpenAddressingArray(const OpenAddressingArray &other) - { - m_slots_total = other.m_slots_total; - m_slots_set_or_dummy = other.m_slots_set_or_dummy; - m_slots_dummy = other.m_slots_dummy; - m_slots_usable = other.m_slots_usable; - m_slot_mask = other.m_slot_mask; - m_item_amount = other.m_item_amount; - m_item_exponent = other.m_item_exponent; - - if (m_item_amount <= s_items_in_small_storage) { - m_items = this->small_storage(); - } - else { - m_items = (Item *)m_allocator.allocate_aligned( - sizeof(Item) * m_item_amount, std::alignment_of<Item>::value, __func__); - } + ~OpenAddressingArray() = default; - uninitialized_copy_n(other.m_items, m_item_amount, m_items); - } + OpenAddressingArray(const OpenAddressingArray &other) = default; OpenAddressingArray(OpenAddressingArray &&other) noexcept { @@ -202,15 +161,8 @@ class OpenAddressingArray { m_slot_mask = other.m_slot_mask; m_item_amount = other.m_item_amount; m_item_exponent = other.m_item_exponent; - if (other.is_in_small_storage()) { - m_items = this->small_storage(); - uninitialized_relocate_n(other.m_items, m_item_amount, m_items); - } - else { - m_items = other.m_items; - } + m_items = std::move(other.m_items); - other.m_items = nullptr; other.~OpenAddressingArray(); new (&other) OpenAddressingArray(); } @@ -237,7 +189,7 @@ class OpenAddressingArray { Allocator &allocator() { - return m_allocator; + return m_items.allocator(); } /* Prepare a new array that can hold a minimum of min_usable_slots elements. All entries are @@ -340,33 +292,22 @@ class OpenAddressingArray { Item *begin() { - return m_items; + return m_items.begin(); } Item *end() { - return m_items + m_item_amount; + return m_items.end(); } const Item *begin() const { - return m_items; + return m_items.begin(); } const Item *end() const { - return m_items + m_item_amount; - } - - private: - Item *small_storage() const - { - return reinterpret_cast<Item *>((char *)m_local_storage.ptr()); - } - - bool is_in_small_storage() const - { - return m_items == this->small_storage(); + return m_items.end(); } }; diff --git a/source/blender/blenlib/BLI_set.hh b/source/blender/blenlib/BLI_set.hh index b09dd910007..dc9df5d116a 100644 --- a/source/blender/blenlib/BLI_set.hh +++ b/source/blender/blenlib/BLI_set.hh @@ -277,6 +277,20 @@ class Set { } /** + * Return true if this set contains no elements. + */ + bool is_empty() const + { + return this->size() == 0; + } + + void clear() + { + this->~Set(); + new (this) Set(); + } + + /** * Returns true when there is at least one element that is in both sets. * Otherwise false. */ diff --git a/source/blender/blenlib/BLI_system.h b/source/blender/blenlib/BLI_system.h index 8c0c9ad99bf..50f8adc20f6 100644 --- a/source/blender/blenlib/BLI_system.h +++ b/source/blender/blenlib/BLI_system.h @@ -53,6 +53,10 @@ int BLI_system_memory_max_in_megabytes_int(void); /* getpid */ #ifdef WIN32 # define BLI_SYSTEM_PID_H <process.h> + +/* void* since we really do not want to drag Windows.h in to get the proper typedef. */ +void BLI_windows_handle_exception(void *exception); + #else # define BLI_SYSTEM_PID_H <unistd.h> #endif diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h index 42dd47266dc..ee087600a31 100644 --- a/source/blender/blenlib/BLI_task.h +++ b/source/blender/blenlib/BLI_task.h @@ -43,12 +43,9 @@ struct BLI_mempool; * must be called from the main threads. All other scheduler and pool functions * are thread-safe. */ -typedef struct TaskScheduler TaskScheduler; - -TaskScheduler *BLI_task_scheduler_create(int num_threads); -void BLI_task_scheduler_free(TaskScheduler *scheduler); - -int BLI_task_scheduler_num_threads(TaskScheduler *scheduler); +void BLI_task_scheduler_init(void); +void BLI_task_scheduler_exit(void); +int BLI_task_scheduler_num_threads(void); /* Task Pool * @@ -70,16 +67,14 @@ typedef enum TaskPriority { } TaskPriority; typedef struct TaskPool TaskPool; -typedef void (*TaskRunFunction)(TaskPool *__restrict pool, void *taskdata, int threadid); +typedef void (*TaskRunFunction)(TaskPool *__restrict pool, void *taskdata); typedef void (*TaskFreeFunction)(TaskPool *__restrict pool, void *taskdata); -TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata, TaskPriority priority); -TaskPool *BLI_task_pool_create_background(TaskScheduler *scheduler, - void *userdata, - TaskPriority priority); -TaskPool *BLI_task_pool_create_suspended(TaskScheduler *scheduler, - void *userdata, - TaskPriority priority); +TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority); +TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority); +TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority); +TaskPool *BLI_task_pool_create_no_threads(void *userdata); +TaskPool *BLI_task_pool_create_background_serial(void *userdata, TaskPriority priority); void BLI_task_pool_free(TaskPool *pool); void BLI_task_pool_push(TaskPool *pool, @@ -87,17 +82,9 @@ void BLI_task_pool_push(TaskPool *pool, void *taskdata, bool free_taskdata, TaskFreeFunction freedata); -void BLI_task_pool_push_from_thread(TaskPool *pool, - TaskRunFunction run, - void *taskdata, - bool free_taskdata, - TaskFreeFunction freedata, - int thread_id); /* work and wait until all tasks are done */ void BLI_task_pool_work_and_wait(TaskPool *pool); -/* work and wait until all tasks are done, then reset to the initial suspended state */ -void BLI_task_pool_work_wait_and_reset(TaskPool *pool); /* cancel all tasks, keep worker threads running */ void BLI_task_pool_cancel(TaskPool *pool); @@ -110,36 +97,10 @@ void *BLI_task_pool_user_data(TaskPool *pool); /* optional mutex to use from run function */ ThreadMutex *BLI_task_pool_user_mutex(TaskPool *pool); -/* Thread ID of thread that created the task pool. */ -int BLI_task_pool_creator_thread_id(TaskPool *pool); - -/* Delayed push, use that to reduce thread overhead by accumulating - * all new tasks into local queue first and pushing it to scheduler - * from within a single mutex lock. - */ -void BLI_task_pool_delayed_push_begin(TaskPool *pool, int thread_id); -void BLI_task_pool_delayed_push_end(TaskPool *pool, int thread_id); - /* Parallel for routines */ -typedef enum eTaskSchedulingMode { - /* Task scheduler will divide overall work into equal chunks, scheduling - * even chunks to all worker threads. - * Least run time benefit, ideal for cases when each task requires equal - * amount of compute power. - */ - TASK_SCHEDULING_STATIC, - /* Task scheduler will schedule small amount of work to each worker thread. - * Has more run time overhead, but deals much better with cases when each - * part of the work requires totally different amount of compute power. - */ - TASK_SCHEDULING_DYNAMIC, -} eTaskSchedulingMode; - /* Per-thread specific data passed to the callback. */ typedef struct TaskParallelTLS { - /* Identifier of the thread who this data belongs to. */ - int thread_id; /* Copy of user-specifier chunk, which is copied from original chunk to all * worker threads. This is similar to OpenMP's firstprivate. */ @@ -163,8 +124,6 @@ typedef struct TaskParallelSettings { * is higher than a chunk size. As in, threading will always be performed. */ bool use_threading; - /* Scheduling mode to use for this parallel range invocation. */ - eTaskSchedulingMode scheduling_mode; /* Each instance of looping chunks will get a copy of this data * (similar to OpenMP's firstprivate). */ @@ -199,7 +158,7 @@ void BLI_task_parallel_range(const int start, const int stop, void *userdata, TaskParallelRangeFunc func, - TaskParallelSettings *settings); + const TaskParallelSettings *settings); /* This data is shared between all tasks, its access needs thread lock or similar protection. */ @@ -254,11 +213,14 @@ BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *setti { memset(settings, 0, sizeof(*settings)); settings->use_threading = true; - settings->scheduling_mode = TASK_SCHEDULING_STATIC; /* Use default heuristic to define actual chunk size. */ settings->min_iter_per_thread = 0; } +/* Don't use this, store any thread specific data in tls->userdata_chunk instead. + * Ony here for code to be removed. */ +int BLI_task_parallel_thread_id(const TaskParallelTLS *tls); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h index c2127c1ec3a..243efedebf9 100644 --- a/source/blender/blenlib/BLI_threads.h +++ b/source/blender/blenlib/BLI_threads.h @@ -47,8 +47,6 @@ struct TaskScheduler; void BLI_threadapi_init(void); void BLI_threadapi_exit(void); -struct TaskScheduler *BLI_task_scheduler_get(void); - void BLI_threadpool_init(struct ListBase *threadbase, void *(*do_thread)(void *), int tot); int BLI_available_threads(struct ListBase *threadbase); int BLI_threadpool_available_thread_index(struct ListBase *threadbase); diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index 40dc876d5a5..49cf41c2005 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -427,7 +427,7 @@ class Vector { /** * Returns true when the vector contains no elements, otherwise false. */ - bool empty() const + bool is_empty() const { return m_begin == m_end; } @@ -438,7 +438,7 @@ class Vector { */ void remove_last() { - BLI_assert(!this->empty()); + BLI_assert(!this->is_empty()); m_end--; destruct(m_end); UPDATE_VECTOR_SIZE(this); @@ -449,7 +449,7 @@ class Vector { */ T pop_last() { - BLI_assert(!this->empty()); + BLI_assert(!this->is_empty()); m_end--; T value = std::move(*m_end); destruct(m_end); diff --git a/source/blender/blenlib/BLI_vector_set.hh b/source/blender/blenlib/BLI_vector_set.hh index 6e1ab823e86..9f887513816 100644 --- a/source/blender/blenlib/BLI_vector_set.hh +++ b/source/blender/blenlib/BLI_vector_set.hh @@ -317,6 +317,11 @@ template<typename T, typename Allocator = GuardedAllocator> class VectorSet { return m_array.slots_set(); } + bool is_empty() const + { + return this->size() == 0; + } + const T *begin() const { return m_elements; diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 19089447b95..d3bfb553329 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -64,6 +64,7 @@ set(SRC intern/buffer.c intern/convexhull_2d.c intern/delaunay_2d.c + intern/dot_export.cc intern/dynlib.c intern/easing.c intern/edgehash.c @@ -118,8 +119,10 @@ set(SRC intern/string_utf8.c intern/string_utils.c intern/system.c - intern/task_pool.cc intern/task_iterator.c + intern/task_pool.cc + intern/task_range.cc + intern/task_scheduler.cc intern/threads.c intern/time.c intern/timecode.c @@ -161,6 +164,8 @@ set(SRC BLI_delaunay_2d.h BLI_dial_2d.h BLI_dlrbTree.h + BLI_dot_export.hh + BLI_dot_export_attribute_enums.hh BLI_dynlib.h BLI_dynstr.h BLI_easing.h @@ -275,6 +280,14 @@ if(WITH_MEM_VALGRIND) add_definitions(-DWITH_MEM_VALGRIND) endif() +if(WITH_TBB) + add_definitions(-DWITH_TBB) + + list(APPEND INC_SYS + ${TBB_INCLUDE_DIRS} + ) +endif() + if(WIN32) list(APPEND INC ../../../intern/utfconv @@ -282,6 +295,9 @@ if(WIN32) list(APPEND LIB bf_intern_utfconv ) + list(APPEND SRC + intern/system_win32.c + ) endif() diff --git a/source/blender/blenlib/intern/delaunay_2d.c b/source/blender/blenlib/intern/delaunay_2d.c index 836292e0c88..4e0cd3a78dc 100644 --- a/source/blender/blenlib/intern/delaunay_2d.c +++ b/source/blender/blenlib/intern/delaunay_2d.c @@ -1264,6 +1264,7 @@ static void fill_crossdata_for_intersect(CDT_state *cdt, se_vcva = t->next->next; BLI_assert(se_vcva->vert == vc && se_vcva->next->vert == va); BLI_assert(se_vcvb->vert == vc && se_vcvb->next->vert == vb); + UNUSED_VARS_NDEBUG(vc); isect = isect_seg_seg_v2_lambda_mu_db(va->co, vb->co, curco, v2->co, &lambda, &mu); #ifdef DEBUG_CDT if (dbg_level > 0) { diff --git a/source/blender/blenlib/intern/dot_export.cc b/source/blender/blenlib/intern/dot_export.cc new file mode 100644 index 00000000000..96de4056fc5 --- /dev/null +++ b/source/blender/blenlib/intern/dot_export.cc @@ -0,0 +1,305 @@ +/* + * 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 <iomanip> + +#include "BLI_dot_export.hh" + +namespace BLI { +namespace DotExport { + +/* Graph Building + ************************************************/ + +Node &Graph::new_node(StringRef label) +{ + Node *node = new Node(*this); + m_nodes.append(std::unique_ptr<Node>(node)); + m_top_level_nodes.add_new(node); + node->set_attribute("label", label); + return *node; +} + +Cluster &Graph::new_cluster(StringRef label) +{ + Cluster *cluster = new Cluster(*this); + m_clusters.append(std::unique_ptr<Cluster>(cluster)); + m_top_level_clusters.add_new(cluster); + cluster->set_attribute("label", label); + return *cluster; +} + +UndirectedEdge &UndirectedGraph::new_edge(NodePort a, NodePort b) +{ + UndirectedEdge *edge = new UndirectedEdge(a, b); + m_edges.append(std::unique_ptr<UndirectedEdge>(edge)); + return *edge; +} + +DirectedEdge &DirectedGraph::new_edge(NodePort from, NodePort to) +{ + DirectedEdge *edge = new DirectedEdge(from, to); + m_edges.append(std::unique_ptr<DirectedEdge>(edge)); + return *edge; +} + +void Cluster::set_parent_cluster(Cluster *new_parent) +{ + if (m_parent == new_parent) { + return; + } + else if (m_parent == nullptr) { + m_graph.m_top_level_clusters.remove(this); + new_parent->m_children.add_new(this); + } + else if (new_parent == nullptr) { + m_parent->m_children.remove(this); + m_graph.m_top_level_clusters.add_new(this); + } + else { + m_parent->m_children.remove(this); + new_parent->m_children.add_new(this); + } + m_parent = new_parent; +} + +void Node::set_parent_cluster(Cluster *cluster) +{ + if (m_cluster == cluster) { + return; + } + else if (m_cluster == nullptr) { + m_graph.m_top_level_nodes.remove(this); + cluster->m_nodes.add_new(this); + } + else if (cluster == nullptr) { + m_cluster->m_nodes.remove(this); + m_graph.m_top_level_nodes.add_new(this); + } + else { + m_cluster->m_nodes.remove(this); + cluster->m_nodes.add_new(this); + } + m_cluster = cluster; +} + +/* Utility methods + **********************************************/ + +void Graph::set_random_cluster_bgcolors() +{ + for (Cluster *cluster : m_top_level_clusters) { + cluster->set_random_cluster_bgcolors(); + } +} + +void Cluster::set_random_cluster_bgcolors() +{ + float hue = rand() / (float)RAND_MAX; + float staturation = 0.3f; + float value = 0.8f; + this->set_attribute("bgcolor", color_attr_from_hsv(hue, staturation, value)); + + for (Cluster *cluster : m_children) { + cluster->set_random_cluster_bgcolors(); + } +} + +/* Dot Generation + **********************************************/ + +std::string DirectedGraph::to_dot_string() const +{ + std::stringstream ss; + ss << "digraph {\n"; + this->export__declare_nodes_and_clusters(ss); + ss << "\n"; + + for (const std::unique_ptr<DirectedEdge> &edge : m_edges) { + edge->export__as_edge_statement(ss); + ss << "\n"; + } + + ss << "}\n"; + return ss.str(); +} + +std::string UndirectedGraph::to_dot_string() const +{ + std::stringstream ss; + ss << "graph {\n"; + this->export__declare_nodes_and_clusters(ss); + ss << "\n"; + + for (const std::unique_ptr<UndirectedEdge> &edge : m_edges) { + edge->export__as_edge_statement(ss); + ss << "\n"; + } + + ss << "}\n"; + return ss.str(); +} + +void Graph::export__declare_nodes_and_clusters(std::stringstream &ss) const +{ + ss << "graph "; + m_attributes.export__as_bracket_list(ss); + ss << "\n\n"; + + for (Node *node : m_top_level_nodes) { + node->export__as_declaration(ss); + } + + for (Cluster *cluster : m_top_level_clusters) { + cluster->export__declare_nodes_and_clusters(ss); + } +} + +void Cluster::export__declare_nodes_and_clusters(std::stringstream &ss) const +{ + ss << "subgraph cluster_" << (uintptr_t)this << " {\n"; + + ss << "graph "; + m_attributes.export__as_bracket_list(ss); + ss << "\n\n"; + + for (Node *node : m_nodes) { + node->export__as_declaration(ss); + } + + for (Cluster *cluster : m_children) { + cluster->export__declare_nodes_and_clusters(ss); + } + + ss << "}\n"; +} + +void DirectedEdge::export__as_edge_statement(std::stringstream &ss) const +{ + m_a.to_dot_string(ss); + ss << " -> "; + m_b.to_dot_string(ss); + ss << " "; + m_attributes.export__as_bracket_list(ss); +} + +void UndirectedEdge::export__as_edge_statement(std::stringstream &ss) const +{ + m_a.to_dot_string(ss); + ss << " -- "; + m_b.to_dot_string(ss); + ss << " "; + m_attributes.export__as_bracket_list(ss); +} + +void AttributeList::export__as_bracket_list(std::stringstream &ss) const +{ + ss << "["; + m_attributes.foreach_item([&](StringRef key, StringRef value) { + if (StringRef(value).startswith("<")) { + /* Don't draw the quotes, this is an html-like value. */ + ss << key << "=" << value << ", "; + } + else { + ss << key << "=\"" << value << "\", "; + } + }); + ss << "]"; +} + +void Node::export__as_id(std::stringstream &ss) const +{ + ss << '"' << (uintptr_t)this << '"'; +} + +void Node::export__as_declaration(std::stringstream &ss) const +{ + this->export__as_id(ss); + ss << " "; + m_attributes.export__as_bracket_list(ss); + ss << "\n"; +} + +void NodePort::to_dot_string(std::stringstream &ss) const +{ + m_node->export__as_id(ss); + if (m_port_name.has_value()) { + ss << ":" << m_port_name.value(); + } +} + +std::string color_attr_from_hsv(float h, float s, float v) +{ + std::stringstream ss; + ss << std::setprecision(4) << h << ' ' << s << ' ' << v; + return ss.str(); +} + +NodeWithSocketsRef::NodeWithSocketsRef(Node &node, + StringRef name, + ArrayRef<std::string> input_names, + ArrayRef<std::string> output_names) + : m_node(&node) +{ + std::stringstream ss; + + ss << "<<table border=\"0\" cellspacing=\"3\">"; + + /* Header */ + ss << "<tr><td colspan=\"3\" align=\"center\"><b>"; + ss << ((name.size() == 0) ? "No Name" : name); + ss << "</b></td></tr>"; + + /* Sockets */ + uint socket_max_amount = std::max(input_names.size(), output_names.size()); + for (uint i = 0; i < socket_max_amount; i++) { + ss << "<tr>"; + if (i < input_names.size()) { + StringRef name = input_names[i]; + if (name.size() == 0) { + name = "No Name"; + } + ss << "<td align=\"left\" port=\"in" << i << "\">"; + ss << name; + ss << "</td>"; + } + else { + ss << "<td></td>"; + } + ss << "<td></td>"; + if (i < output_names.size()) { + StringRef name = output_names[i]; + if (name.size() == 0) { + name = "No Name"; + } + ss << "<td align=\"right\" port=\"out" << i << "\">"; + ss << name; + ss << "</td>"; + } + else { + ss << "<td></td>"; + } + ss << "</tr>"; + } + + ss << "</table>>"; + + m_node->set_attribute("label", ss.str()); + m_node->set_shape(Attr_shape::Rectangle); +} + +} // namespace DotExport +} // namespace BLI diff --git a/source/blender/blenlib/intern/lasso_2d.c b/source/blender/blenlib/intern/lasso_2d.c index f1e9b1e655f..a01adf4fa6a 100644 --- a/source/blender/blenlib/intern/lasso_2d.c +++ b/source/blender/blenlib/intern/lasso_2d.c @@ -28,47 +28,47 @@ #include "BLI_lasso_2d.h" /* own include */ -void BLI_lasso_boundbox(rcti *rect, const int mcords[][2], const unsigned int moves) +void BLI_lasso_boundbox(rcti *rect, const int mcoords[][2], const unsigned int mcoords_len) { unsigned int a; - rect->xmin = rect->xmax = mcords[0][0]; - rect->ymin = rect->ymax = mcords[0][1]; + rect->xmin = rect->xmax = mcoords[0][0]; + rect->ymin = rect->ymax = mcoords[0][1]; - for (a = 1; a < moves; a++) { - if (mcords[a][0] < rect->xmin) { - rect->xmin = mcords[a][0]; + for (a = 1; a < mcoords_len; a++) { + if (mcoords[a][0] < rect->xmin) { + rect->xmin = mcoords[a][0]; } - else if (mcords[a][0] > rect->xmax) { - rect->xmax = mcords[a][0]; + else if (mcoords[a][0] > rect->xmax) { + rect->xmax = mcoords[a][0]; } - if (mcords[a][1] < rect->ymin) { - rect->ymin = mcords[a][1]; + if (mcoords[a][1] < rect->ymin) { + rect->ymin = mcoords[a][1]; } - else if (mcords[a][1] > rect->ymax) { - rect->ymax = mcords[a][1]; + else if (mcoords[a][1] > rect->ymax) { + rect->ymax = mcoords[a][1]; } } } -bool BLI_lasso_is_point_inside(const int mcords[][2], - const unsigned int moves, +bool BLI_lasso_is_point_inside(const int mcoords[][2], + const unsigned int mcoords_len, const int sx, const int sy, const int error_value) { - if (sx == error_value || moves == 0) { + if (sx == error_value || mcoords_len == 0) { return false; } else { int pt[2] = {sx, sy}; - return isect_point_poly_v2_int(pt, mcords, moves, true); + return isect_point_poly_v2_int(pt, mcoords, mcoords_len, true); } } /* edge version for lasso select. we assume boundbox check was done */ -bool BLI_lasso_is_edge_inside(const int mcords[][2], - const unsigned int moves, +bool BLI_lasso_is_edge_inside(const int mcoords[][2], + const unsigned int mcoords_len, int x0, int y0, int x1, @@ -76,27 +76,27 @@ bool BLI_lasso_is_edge_inside(const int mcords[][2], const int error_value) { - if (x0 == error_value || x1 == error_value || moves == 0) { + if (x0 == error_value || x1 == error_value || mcoords_len == 0) { return false; } const int v1[2] = {x0, y0}, v2[2] = {x1, y1}; /* check points in lasso */ - if (BLI_lasso_is_point_inside(mcords, moves, v1[0], v1[1], error_value)) { + if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v1[0], v1[1], error_value)) { return true; } - if (BLI_lasso_is_point_inside(mcords, moves, v2[0], v2[1], error_value)) { + if (BLI_lasso_is_point_inside(mcoords, mcoords_len, v2[0], v2[1], error_value)) { return true; } /* no points in lasso, so we have to intersect with lasso edge */ - if (isect_seg_seg_v2_int(mcords[0], mcords[moves - 1], v1, v2) > 0) { + if (isect_seg_seg_v2_int(mcoords[0], mcoords[mcoords_len - 1], v1, v2) > 0) { return true; } - for (unsigned int a = 0; a < moves - 1; a++) { - if (isect_seg_seg_v2_int(mcords[a], mcords[a + 1], v1, v2) > 0) { + for (unsigned int a = 0; a < mcoords_len - 1; a++) { + if (isect_seg_seg_v2_int(mcoords[a], mcoords[a + 1], v1, v2) > 0) { return true; } } diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index d2c55233653..ca405907bdd 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -123,6 +123,27 @@ MINLINE void copy_v4_v4_uchar(unsigned char r[4], const unsigned char a[4]) r[3] = a[3]; } +MINLINE void copy_v2_uchar(unsigned char r[2], const unsigned char a) +{ + r[0] = a; + r[1] = a; +} + +MINLINE void copy_v3_uchar(unsigned char r[3], const unsigned char a) +{ + r[0] = a; + r[1] = a; + r[2] = a; +} + +MINLINE void copy_v4_uchar(unsigned char r[4], const unsigned char a) +{ + r[0] = a; + r[1] = a; + r[2] = a; + r[3] = a; +} + /* char */ MINLINE void copy_v2_v2_char(char r[2], const char a[2]) { diff --git a/source/blender/blenlib/intern/system.c b/source/blender/blenlib/intern/system.c index 7d9ed2598a6..53db49aa59c 100644 --- a/source/blender/blenlib/intern/system.c +++ b/source/blender/blenlib/intern/system.c @@ -32,11 +32,8 @@ /* for backtrace and gethostname/GetComputerName */ #if defined(WIN32) # include <intrin.h> -# include <windows.h> -# pragma warning(push) -# pragma warning(disable : 4091) -# include <dbghelp.h> -# pragma warning(pop) + +# include "BLI_winstuff.h" #else # include <execinfo.h> # include <unistd.h> @@ -74,6 +71,8 @@ int BLI_cpu_support_sse2(void) #endif } +/* Windows stackwalk lives in system_win32.c */ +#if !defined(_MSC_VER) /** * Write a backtrace into a file for systems which support it. */ @@ -81,9 +80,9 @@ void BLI_system_backtrace(FILE *fp) { /* ------------- */ /* Linux / Apple */ -#if defined(__linux__) || defined(__APPLE__) +# if defined(__linux__) || defined(__APPLE__) -# define SIZE 100 +# define SIZE 100 void *buffer[SIZE]; int nptrs; char **strings; @@ -98,48 +97,15 @@ void BLI_system_backtrace(FILE *fp) } free(strings); -# undef SIZE - - /* -------- */ - /* Windows */ -#elif defined(_MSC_VER) - -# ifndef NDEBUG -# define MAXSYMBOL 256 -# define SIZE 100 - unsigned short i; - void *stack[SIZE]; - unsigned short nframes; - SYMBOL_INFO *symbolinfo; - HANDLE process; - - process = GetCurrentProcess(); - - SymInitialize(process, NULL, TRUE); - - nframes = CaptureStackBackTrace(0, SIZE, stack, NULL); - symbolinfo = MEM_callocN(sizeof(SYMBOL_INFO) + MAXSYMBOL * sizeof(char), "crash Symbol table"); - symbolinfo->MaxNameLen = MAXSYMBOL - 1; - symbolinfo->SizeOfStruct = sizeof(SYMBOL_INFO); - - for (i = 0; i < nframes; i++) { - SymFromAddr(process, (DWORD64)(stack[i]), 0, symbolinfo); - - fprintf(fp, "%u: %s - 0x%0llX\n", nframes - i - 1, symbolinfo->Name, symbolinfo->Address); - } - - MEM_freeN(symbolinfo); -# undef MAXSYMBOL # undef SIZE + # else - fprintf(fp, "Crash backtrace not supported on release builds\n"); -# endif /* NDEBUG */ -#else /* _MSC_VER */ /* ------------------ */ /* non msvc/osx/linux */ (void)fp; -#endif +# endif } +#endif /* end BLI_system_backtrace */ /* NOTE: The code for CPU brand string is adopted from Cycles. */ diff --git a/source/blender/blenlib/intern/system_win32.c b/source/blender/blenlib/intern/system_win32.c new file mode 100644 index 00000000000..4fea47386ea --- /dev/null +++ b/source/blender/blenlib/intern/system_win32.c @@ -0,0 +1,378 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bli + */ +#include <Windows.h> +#include <stdio.h> + +#include <dbghelp.h> +#include <shlwapi.h> +#include <tlhelp32.h> + +#include "BLI_string.h" + +#include "MEM_guardedalloc.h" + +static EXCEPTION_POINTERS *current_exception; + +static const char *bli_windows_get_exception_description(const DWORD exceptioncode) +{ + switch (exceptioncode) { + case EXCEPTION_ACCESS_VIOLATION: + return "EXCEPTION_ACCESS_VIOLATION"; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; + case EXCEPTION_BREAKPOINT: + return "EXCEPTION_BREAKPOINT"; + case EXCEPTION_DATATYPE_MISALIGNMENT: + return "EXCEPTION_DATATYPE_MISALIGNMENT"; + case EXCEPTION_FLT_DENORMAL_OPERAND: + return "EXCEPTION_FLT_DENORMAL_OPERAND"; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + return "EXCEPTION_FLT_DIVIDE_BY_ZERO"; + case EXCEPTION_FLT_INEXACT_RESULT: + return "EXCEPTION_FLT_INEXACT_RESULT"; + case EXCEPTION_FLT_INVALID_OPERATION: + return "EXCEPTION_FLT_INVALID_OPERATION"; + case EXCEPTION_FLT_OVERFLOW: + return "EXCEPTION_FLT_OVERFLOW"; + case EXCEPTION_FLT_STACK_CHECK: + return "EXCEPTION_FLT_STACK_CHECK"; + case EXCEPTION_FLT_UNDERFLOW: + return "EXCEPTION_FLT_UNDERFLOW"; + case EXCEPTION_ILLEGAL_INSTRUCTION: + return "EXCEPTION_ILLEGAL_INSTRUCTION"; + case EXCEPTION_IN_PAGE_ERROR: + return "EXCEPTION_IN_PAGE_ERROR"; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + return "EXCEPTION_INT_DIVIDE_BY_ZERO"; + case EXCEPTION_INT_OVERFLOW: + return "EXCEPTION_INT_OVERFLOW"; + case EXCEPTION_INVALID_DISPOSITION: + return "EXCEPTION_INVALID_DISPOSITION"; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + return "EXCEPTION_NONCONTINUABLE_EXCEPTION"; + case EXCEPTION_PRIV_INSTRUCTION: + return "EXCEPTION_PRIV_INSTRUCTION"; + case EXCEPTION_SINGLE_STEP: + return "EXCEPTION_SINGLE_STEP"; + case EXCEPTION_STACK_OVERFLOW: + return "EXCEPTION_STACK_OVERFLOW"; + default: + return "UNKNOWN EXCEPTION"; + } +} + +static void bli_windows_get_module_name(LPVOID address, PCHAR buffer, size_t size) +{ + HMODULE mod; + buffer[0] = 0; + if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, address, &mod)) { + if (GetModuleFileName(mod, buffer, size)) { + PathStripPath(buffer); + } + } +} + +static void bli_windows_get_module_version(const char *file, char *buffer, size_t buffersize) +{ + buffer[0] = 0; + DWORD verHandle = 0; + UINT size = 0; + LPBYTE lpBuffer = NULL; + DWORD verSize = GetFileVersionInfoSize(file, &verHandle); + if (verSize != 0) { + LPSTR verData = (LPSTR)MEM_callocN(verSize, "crash module version"); + + if (GetFileVersionInfo(file, verHandle, verSize, verData)) { + if (VerQueryValue(verData, "\\", (VOID FAR * FAR *)&lpBuffer, &size)) { + if (size) { + VS_FIXEDFILEINFO *verInfo = (VS_FIXEDFILEINFO *)lpBuffer; + /* Magic value from + * https://docs.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo + */ + if (verInfo->dwSignature == 0xfeef04bd) { + BLI_snprintf(buffer, + buffersize, + "%d.%d.%d.%d", + (verInfo->dwFileVersionMS >> 16) & 0xffff, + (verInfo->dwFileVersionMS >> 0) & 0xffff, + (verInfo->dwFileVersionLS >> 16) & 0xffff, + (verInfo->dwFileVersionLS >> 0) & 0xffff); + } + } + } + } + MEM_freeN(verData); + } +} + +static void bli_windows_system_backtrace_exception_record(FILE *fp, PEXCEPTION_RECORD record) +{ + char module[MAX_PATH]; + fprintf(fp, "Exception Record:\n\n"); + fprintf(fp, + "ExceptionCode : %s\n", + bli_windows_get_exception_description(record->ExceptionCode)); + fprintf(fp, "Exception Address : 0x%p\n", record->ExceptionAddress); + bli_windows_get_module_name(record->ExceptionAddress, module, sizeof(module)); + fprintf(fp, "Exception Module : %s\n", module); + fprintf(fp, "Exception Flags : 0x%.8x\n", record->ExceptionFlags); + fprintf(fp, "Exception Parameters : 0x%x\n", record->NumberParameters); + for (DWORD idx = 0; idx < record->NumberParameters; idx++) { + fprintf(fp, "\tParameters[%d] : 0x%p\n", idx, (LPVOID *)record->ExceptionInformation[idx]); + } + if (record->ExceptionRecord) { + fprintf(fp, "Nested "); + bli_windows_system_backtrace_exception_record(fp, record->ExceptionRecord); + } + fprintf(fp, "\n\n"); +} + +static bool BLI_windows_system_backtrace_run_trace(FILE *fp, HANDLE hThread, PCONTEXT context) +{ + const int max_symbol_length = 100; + + bool result = true; + + PSYMBOL_INFO symbolinfo = MEM_callocN(sizeof(SYMBOL_INFO) + max_symbol_length * sizeof(char), + "crash Symbol table"); + symbolinfo->MaxNameLen = max_symbol_length - 1; + symbolinfo->SizeOfStruct = sizeof(SYMBOL_INFO); + + STACKFRAME frame = {0}; + frame.AddrPC.Offset = context->Rip; + frame.AddrPC.Mode = AddrModeFlat; + frame.AddrFrame.Offset = context->Rsp; + frame.AddrFrame.Mode = AddrModeFlat; + frame.AddrStack.Offset = context->Rsp; + frame.AddrStack.Mode = AddrModeFlat; + + while (true) { + if (StackWalk64(IMAGE_FILE_MACHINE_AMD64, + GetCurrentProcess(), + hThread, + &frame, + context, + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + 0)) { + if (frame.AddrPC.Offset) { + char module[MAX_PATH]; + + bli_windows_get_module_name((LPVOID)frame.AddrPC.Offset, module, sizeof(module)); + + if (SymFromAddr(GetCurrentProcess(), (DWORD64)(frame.AddrPC.Offset), 0, symbolinfo)) { + fprintf(fp, "%-20s:0x%p %s", module, (LPVOID)symbolinfo->Address, symbolinfo->Name); + IMAGEHLP_LINE lineinfo; + lineinfo.SizeOfStruct = sizeof(lineinfo); + DWORD displacement = 0; + if (SymGetLineFromAddr( + GetCurrentProcess(), (DWORD64)(frame.AddrPC.Offset), &displacement, &lineinfo)) { + fprintf(fp, " %s:%d", lineinfo.FileName, lineinfo.LineNumber); + } + fprintf(fp, "\n"); + } + else { + fprintf(fp, + "%-20s:0x%p %s\n", + module, + (LPVOID)frame.AddrPC.Offset, + "Symbols not available"); + result = false; + break; + } + } + else { + break; + } + } + else { + break; + } + } + MEM_freeN(symbolinfo); + fprintf(fp, "\n\n"); + return result; +} + +static void bli_windows_system_backtrace_stack_thread(FILE *fp, HANDLE hThread) +{ + if (hThread != GetCurrentThread()) { + SuspendThread(hThread); + } + CONTEXT context; + context.ContextFlags = CONTEXT_ALL; + if (!GetThreadContext(hThread, &context)) { + fprintf(fp, "Cannot get thread context : 0x0%.8x\n", GetLastError()); + } + BLI_windows_system_backtrace_run_trace(fp, hThread, &context); + if (hThread != GetCurrentThread()) { + ResumeThread(hThread); + } +} + +static void bli_windows_system_backtrace_modules(FILE *fp) +{ + fprintf(fp, "Loaded Modules :\n"); + HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0); + if (hModuleSnap == INVALID_HANDLE_VALUE) + return; + + MODULEENTRY32 me32; + me32.dwSize = sizeof(MODULEENTRY32); + + if (!Module32First(hModuleSnap, &me32)) { + CloseHandle(hModuleSnap); // Must clean up the snapshot object! + fprintf(fp, " Error getting module list.\n"); + return; + } + + do { + if (me32.th32ProcessID == GetCurrentProcessId()) { + char version[MAX_PATH]; + bli_windows_get_module_version(me32.szExePath, version, sizeof(version)); + fprintf(fp, "0x%p %-20s %s\n", me32.modBaseAddr, version, me32.szModule); + } + } while (Module32Next(hModuleSnap, &me32)); +} + +static void bli_windows_system_backtrace_threads(FILE *fp) +{ + fprintf(fp, "Threads:\n"); + HANDLE hThreadSnap = INVALID_HANDLE_VALUE; + THREADENTRY32 te32; + + hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (hThreadSnap == INVALID_HANDLE_VALUE) { + fprintf(fp, "Unable to retrieve threads list.\n"); + return; + } + + te32.dwSize = sizeof(THREADENTRY32); + + if (!Thread32First(hThreadSnap, &te32)) { + CloseHandle(hThreadSnap); + return; + } + do { + if (te32.th32OwnerProcessID == GetCurrentProcessId()) { + if (GetCurrentThreadId() != te32.th32ThreadID) { + fprintf(fp, "Thread : %.8x\n", te32.th32ThreadID); + HANDLE ht = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID); + bli_windows_system_backtrace_stack_thread(fp, ht); + CloseHandle(ht); + } + } + } while (Thread32Next(hThreadSnap, &te32)); + CloseHandle(hThreadSnap); +} + +static bool BLI_windows_system_backtrace_stack(FILE *fp) +{ + fprintf(fp, "Stack trace:\n"); + CONTEXT TempContext = *current_exception->ContextRecord; + return BLI_windows_system_backtrace_run_trace(fp, GetCurrentThread(), &TempContext); +} + +static bool bli_private_symbols_loaded() +{ + IMAGEHLP_MODULE64 m64; + m64.SizeOfStruct = sizeof(m64); + if (SymGetModuleInfo64(GetCurrentProcess(), (DWORD64)GetModuleHandle(NULL), &m64)) { + PathStripPath(m64.LoadedPdbName); + return BLI_strcasecmp(m64.LoadedPdbName, "blender_private.pdb") == 0; + } + return false; +} + +static void bli_load_symbols() +{ + /* If this is a developer station and the private pdb is already loaded leave it be. */ + if (bli_private_symbols_loaded()) { + return; + } + + char pdb_file[MAX_PATH] = {0}; + + /* get the currently executing image */ + if (GetModuleFileNameA(NULL, pdb_file, sizeof(pdb_file))) { + /* remove the filename */ + PathRemoveFileSpecA(pdb_file); + /* append blender.pdb */ + PathAppendA(pdb_file, "blender.pdb"); + if (PathFileExistsA(pdb_file)) { + HMODULE mod = GetModuleHandle(NULL); + if (mod) { + WIN32_FILE_ATTRIBUTE_DATA file_data; + if (GetFileAttributesExA(pdb_file, GetFileExInfoStandard, &file_data)) { + /* SymInitialize will try to load symbols on its own, so we first must unload whatever it + * did trying to help */ + SymUnloadModule64(GetCurrentProcess(), (DWORD64)mod); + + DWORD64 module_base = SymLoadModule(GetCurrentProcess(), + NULL, + pdb_file, + NULL, + (DWORD64)mod, + (DWORD)file_data.nFileSizeLow); + if (module_base == 0) { + fprintf(stderr, + "Error loading symbols %s\n\terror:0x%.8x\n\tsize = %d\n\tbase=0x%p\n", + pdb_file, + GetLastError(), + file_data.nFileSizeLow, + (LPVOID)mod); + } + } + } + } + } +} + +void BLI_system_backtrace(FILE *fp) +{ + SymInitialize(GetCurrentProcess(), NULL, TRUE); + bli_load_symbols(); + bli_windows_system_backtrace_exception_record(fp, current_exception->ExceptionRecord); + if (BLI_windows_system_backtrace_stack(fp)) { + /* When the blender symbols are missing the stack traces will be unreliable + * so only run if the previous step completed successfully. */ + bli_windows_system_backtrace_threads(fp); + } + bli_windows_system_backtrace_modules(fp); + fputc(0, fp); /* Give our selves a nice zero terminator for later on */ +} + +void BLI_windows_handle_exception(EXCEPTION_POINTERS *exception) +{ + current_exception = exception; + fprintf(stderr, + "Error : %s\n", + bli_windows_get_exception_description(exception->ExceptionRecord->ExceptionCode)); + fflush(stderr); + + LPVOID address = exception->ExceptionRecord->ExceptionAddress; + fprintf(stderr, "Address : 0x%p\n", address); + + CHAR modulename[MAX_PATH]; + bli_windows_get_module_name(address, modulename, sizeof(modulename)); + fprintf(stderr, "Module : %s\n", modulename); + fflush(stderr); +} diff --git a/source/blender/blenlib/intern/task_iterator.c b/source/blender/blenlib/intern/task_iterator.c index 1189ec0d0c0..ee459ac2548 100644 --- a/source/blender/blenlib/intern/task_iterator.c +++ b/source/blender/blenlib/intern/task_iterator.c @@ -17,7 +17,7 @@ /** \file * \ingroup bli * - * A generic task system which can be used for any task based subsystem. + * Parallel tasks over all elements in a container. */ #include <stdlib.h> @@ -34,82 +34,12 @@ #include "atomic_ops.h" -/* Parallel range routines */ - -/** - * - * Main functions: - * - #BLI_task_parallel_range - * - #BLI_task_parallel_listbase (#ListBase - double linked list) - * - * TODO: - * - #BLI_task_parallel_foreach_link (#Link - single linked list) - * - #BLI_task_parallel_foreach_ghash/gset (#GHash/#GSet - hash & set) - * - #BLI_task_parallel_foreach_mempool (#BLI_mempool - iterate over mempools) - */ - /* Allows to avoid using malloc for userdata_chunk in tasks, when small enough. */ #define MALLOCA(_size) ((_size) <= 8192) ? alloca((_size)) : MEM_mallocN((_size), __func__) #define MALLOCA_FREE(_mem, _size) \ if (((_mem) != NULL) && ((_size) > 8192)) \ MEM_freeN((_mem)) -/* Stores all needed data to perform a parallelized iteration, - * with a same operation (callback function). - * It can be chained with other tasks in a single-linked list way. */ -typedef struct TaskParallelRangeState { - struct TaskParallelRangeState *next; - - /* Start and end point of integer value iteration. */ - int start, stop; - - /* User-defined data, shared between all worker threads. */ - void *userdata_shared; - /* User-defined callback function called for each value in [start, stop[ specified range. */ - TaskParallelRangeFunc func; - - /* Each instance of looping chunks will get a copy of this data - * (similar to OpenMP's firstprivate). - */ - void *initial_tls_memory; /* Pointer to actual user-defined 'tls' data. */ - size_t tls_data_size; /* Size of that data. */ - - void *flatten_tls_storage; /* 'tls' copies of initial_tls_memory for each running task. */ - /* Number of 'tls' copies in the array, i.e. number of worker threads. */ - size_t num_elements_in_tls_storage; - - /* Function called to join user data chunk into another, to reduce - * the result to the original userdata_chunk memory. - * The reduce functions should have no side effects, so that they - * can be run on any thread. */ - TaskParallelReduceFunc func_reduce; - /* Function called to free data created by TaskParallelRangeFunc. */ - TaskParallelFreeFunc func_free; - - /* Current value of the iterator, shared between all threads (atomically updated). */ - int iter_value; - int iter_chunk_num; /* Amount of iterations to process in a single step. */ -} TaskParallelRangeState; - -/* Stores all the parallel tasks for a single pool. */ -typedef struct TaskParallelRangePool { - /* The workers' task pool. */ - TaskPool *pool; - /* The number of worker tasks we need to create. */ - int num_tasks; - /* The total number of iterations in all the added ranges. */ - int num_total_iters; - /* The size (number of items) processed at once by a worker task. */ - int chunk_size; - - /* Linked list of range tasks to process. */ - TaskParallelRangeState *parallel_range_states; - /* Current range task beeing processed, swapped atomically. */ - TaskParallelRangeState *current_state; - /* Scheduling settings common to all tasks. */ - TaskParallelSettings *settings; -} TaskParallelRangePool; - BLI_INLINE void task_parallel_calc_chunk_size(const TaskParallelSettings *settings, const int tot_items, int num_tasks, @@ -154,232 +84,7 @@ BLI_INLINE void task_parallel_calc_chunk_size(const TaskParallelSettings *settin } BLI_assert(chunk_size > 0); - - if (tot_items > 0) { - switch (settings->scheduling_mode) { - case TASK_SCHEDULING_STATIC: - *r_chunk_size = max_ii(chunk_size, tot_items / num_tasks); - break; - case TASK_SCHEDULING_DYNAMIC: - *r_chunk_size = chunk_size; - break; - } - } - else { - /* If total amount of items is unknown, we can only use dynamic scheduling. */ - *r_chunk_size = chunk_size; - } -} - -BLI_INLINE void task_parallel_range_calc_chunk_size(TaskParallelRangePool *range_pool) -{ - int num_iters = 0; - int min_num_iters = INT_MAX; - for (TaskParallelRangeState *state = range_pool->parallel_range_states; state != NULL; - state = state->next) { - const int ni = state->stop - state->start; - num_iters += ni; - if (min_num_iters > ni) { - min_num_iters = ni; - } - } - range_pool->num_total_iters = num_iters; - /* Note: Passing min_num_iters here instead of num_iters kind of partially breaks the 'static' - * scheduling, but pooled range iterator is inherently non-static anyway, so adding a small level - * of dynamic scheduling here should be fine. */ - task_parallel_calc_chunk_size( - range_pool->settings, min_num_iters, range_pool->num_tasks, &range_pool->chunk_size); -} - -BLI_INLINE bool parallel_range_next_iter_get(TaskParallelRangePool *__restrict range_pool, - int *__restrict r_iter, - int *__restrict r_count, - TaskParallelRangeState **__restrict r_state) -{ - /* We need an atomic op here as well to fetch the initial state, since some other thread might - * have already updated it. */ - TaskParallelRangeState *current_state = atomic_cas_ptr( - (void **)&range_pool->current_state, NULL, NULL); - - int previter = INT32_MAX; - - while (current_state != NULL && previter >= current_state->stop) { - previter = atomic_fetch_and_add_int32(¤t_state->iter_value, range_pool->chunk_size); - *r_iter = previter; - *r_count = max_ii(0, min_ii(range_pool->chunk_size, current_state->stop - previter)); - - if (previter >= current_state->stop) { - /* At this point the state we got is done, we need to go to the next one. In case some other - * thread already did it, then this does nothing, and we'll just get current valid state - * at start of the next loop. */ - TaskParallelRangeState *current_state_from_atomic_cas = atomic_cas_ptr( - (void **)&range_pool->current_state, current_state, current_state->next); - - if (current_state == current_state_from_atomic_cas) { - /* The atomic CAS operation was successful, we did update range_pool->current_state, so we - * can safely switch to next state. */ - current_state = current_state->next; - } - else { - /* The atomic CAS operation failed, but we still got range_pool->current_state value out of - * it, just use it as our new current state. */ - current_state = current_state_from_atomic_cas; - } - } - } - - *r_state = current_state; - return (current_state != NULL && previter < current_state->stop); -} - -static void parallel_range_func(TaskPool *__restrict pool, void *tls_data_idx, int thread_id) -{ - TaskParallelRangePool *__restrict range_pool = BLI_task_pool_user_data(pool); - TaskParallelTLS tls = { - .thread_id = thread_id, - .userdata_chunk = NULL, - }; - TaskParallelRangeState *state; - int iter, count; - while (parallel_range_next_iter_get(range_pool, &iter, &count, &state)) { - tls.userdata_chunk = (char *)state->flatten_tls_storage + - (((size_t)POINTER_AS_INT(tls_data_idx)) * state->tls_data_size); - for (int i = 0; i < count; i++) { - state->func(state->userdata_shared, iter + i, &tls); - } - } -} - -static void parallel_range_single_thread(TaskParallelRangePool *range_pool) -{ - for (TaskParallelRangeState *state = range_pool->parallel_range_states; state != NULL; - state = state->next) { - const int start = state->start; - const int stop = state->stop; - void *userdata = state->userdata_shared; - TaskParallelRangeFunc func = state->func; - - void *initial_tls_memory = state->initial_tls_memory; - const size_t tls_data_size = state->tls_data_size; - const bool use_tls_data = (tls_data_size != 0) && (initial_tls_memory != NULL); - TaskParallelTLS tls = { - .thread_id = 0, - .userdata_chunk = initial_tls_memory, - }; - for (int i = start; i < stop; i++) { - func(userdata, i, &tls); - } - if (use_tls_data && state->func_free != NULL) { - /* `func_free` should only free data that was created during execution of `func`. */ - state->func_free(userdata, initial_tls_memory); - } - } -} - -/** - * This function allows to parallelized for loops in a similar way to OpenMP's - * 'parallel for' statement. - * - * See public API doc of ParallelRangeSettings for description of all settings. - */ -void BLI_task_parallel_range(const int start, - const int stop, - void *userdata, - TaskParallelRangeFunc func, - TaskParallelSettings *settings) -{ - if (start == stop) { - return; - } - - BLI_assert(start < stop); - - TaskParallelRangeState state = { - .next = NULL, - .start = start, - .stop = stop, - .userdata_shared = userdata, - .func = func, - .iter_value = start, - .initial_tls_memory = settings->userdata_chunk, - .tls_data_size = settings->userdata_chunk_size, - .func_free = settings->func_free, - }; - TaskParallelRangePool range_pool = { - .pool = NULL, .parallel_range_states = &state, .current_state = NULL, .settings = settings}; - int i, num_threads, num_tasks; - - void *tls_data = settings->userdata_chunk; - const size_t tls_data_size = settings->userdata_chunk_size; - if (tls_data_size != 0) { - BLI_assert(tls_data != NULL); - } - const bool use_tls_data = (tls_data_size != 0) && (tls_data != NULL); - void *flatten_tls_storage = NULL; - - /* If it's not enough data to be crunched, don't bother with tasks at all, - * do everything from the current thread. - */ - if (!settings->use_threading) { - parallel_range_single_thread(&range_pool); - return; - } - - TaskScheduler *task_scheduler = BLI_task_scheduler_get(); - num_threads = BLI_task_scheduler_num_threads(task_scheduler); - - /* The idea here is to prevent creating task for each of the loop iterations - * and instead have tasks which are evenly distributed across CPU cores and - * pull next iter to be crunched using the queue. - */ - range_pool.num_tasks = num_tasks = num_threads + 2; - - task_parallel_range_calc_chunk_size(&range_pool); - range_pool.num_tasks = num_tasks = min_ii(num_tasks, - max_ii(1, (stop - start) / range_pool.chunk_size)); - - if (num_tasks == 1) { - parallel_range_single_thread(&range_pool); - return; - } - - TaskPool *task_pool = range_pool.pool = BLI_task_pool_create_suspended( - task_scheduler, &range_pool, TASK_PRIORITY_HIGH); - - range_pool.current_state = &state; - - if (use_tls_data) { - state.flatten_tls_storage = flatten_tls_storage = MALLOCA(tls_data_size * (size_t)num_tasks); - state.tls_data_size = tls_data_size; - } - - const int thread_id = BLI_task_pool_creator_thread_id(task_pool); - for (i = 0; i < num_tasks; i++) { - if (use_tls_data) { - void *userdata_chunk_local = (char *)flatten_tls_storage + (tls_data_size * (size_t)i); - memcpy(userdata_chunk_local, tls_data, tls_data_size); - } - /* Use this pool's pre-allocated tasks. */ - BLI_task_pool_push_from_thread( - task_pool, parallel_range_func, POINTER_FROM_INT(i), false, NULL, thread_id); - } - - BLI_task_pool_work_and_wait(task_pool); - BLI_task_pool_free(task_pool); - - if (use_tls_data && (settings->func_free != NULL || settings->func_reduce != NULL)) { - for (i = 0; i < num_tasks; i++) { - void *userdata_chunk_local = (char *)flatten_tls_storage + (tls_data_size * (size_t)i); - if (settings->func_reduce) { - settings->func_reduce(userdata, tls_data, userdata_chunk_local); - } - if (settings->func_free) { - /* `func_free` should only free data that was created during execution of `func`. */ - settings->func_free(userdata, userdata_chunk_local); - } - } - MALLOCA_FREE(flatten_tls_storage, tls_data_size * (size_t)num_tasks); - } + *r_chunk_size = chunk_size; } typedef struct TaskParallelIteratorState { @@ -394,20 +99,10 @@ typedef struct TaskParallelIteratorState { int tot_items; } TaskParallelIteratorState; -BLI_INLINE void task_parallel_iterator_calc_chunk_size(const TaskParallelSettings *settings, - const int num_tasks, - TaskParallelIteratorState *state) -{ - task_parallel_calc_chunk_size( - settings, state->tot_items, num_tasks, &state->iter_shared.chunk_size); -} - static void parallel_iterator_func_do(TaskParallelIteratorState *__restrict state, - void *userdata_chunk, - int threadid) + void *userdata_chunk) { TaskParallelTLS tls = { - .thread_id = threadid, .userdata_chunk = userdata_chunk, }; @@ -460,11 +155,11 @@ static void parallel_iterator_func_do(TaskParallelIteratorState *__restrict stat MALLOCA_FREE(current_chunk_indices, indices_size); } -static void parallel_iterator_func(TaskPool *__restrict pool, void *userdata_chunk, int threadid) +static void parallel_iterator_func(TaskPool *__restrict pool, void *userdata_chunk) { TaskParallelIteratorState *__restrict state = BLI_task_pool_user_data(pool); - parallel_iterator_func_do(state, userdata_chunk, threadid); + parallel_iterator_func_do(state, userdata_chunk); } static void task_parallel_iterator_no_threads(const TaskParallelSettings *settings, @@ -483,7 +178,7 @@ static void task_parallel_iterator_no_threads(const TaskParallelSettings *settin /* Also marking it as non-threaded for the iterator callback. */ state->iter_shared.spin_lock = NULL; - parallel_iterator_func_do(state, userdata_chunk, 0); + parallel_iterator_func_do(state, userdata_chunk); if (use_userdata_chunk && settings->func_free != NULL) { /* `func_free` should only free data that was created during execution of `func`. */ @@ -494,10 +189,10 @@ static void task_parallel_iterator_no_threads(const TaskParallelSettings *settin static void task_parallel_iterator_do(const TaskParallelSettings *settings, TaskParallelIteratorState *state) { - TaskScheduler *task_scheduler = BLI_task_scheduler_get(); - const int num_threads = BLI_task_scheduler_num_threads(task_scheduler); + const int num_threads = BLI_task_scheduler_num_threads(); - task_parallel_iterator_calc_chunk_size(settings, num_threads, state); + task_parallel_calc_chunk_size( + settings, state->tot_items, num_threads, &state->iter_shared.chunk_size); if (!settings->use_threading) { task_parallel_iterator_no_threads(settings, state); @@ -526,21 +221,19 @@ static void task_parallel_iterator_do(const TaskParallelSettings *settings, void *userdata_chunk_array = NULL; const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL); - TaskPool *task_pool = BLI_task_pool_create_suspended(task_scheduler, state, TASK_PRIORITY_HIGH); + TaskPool *task_pool = BLI_task_pool_create(state, TASK_PRIORITY_HIGH); if (use_userdata_chunk) { userdata_chunk_array = MALLOCA(userdata_chunk_size * num_tasks); } - const int thread_id = BLI_task_pool_creator_thread_id(task_pool); for (size_t i = 0; i < num_tasks; i++) { if (use_userdata_chunk) { userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i); memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size); } /* Use this pool's pre-allocated tasks. */ - BLI_task_pool_push_from_thread( - task_pool, parallel_iterator_func, userdata_chunk_local, false, NULL, thread_id); + BLI_task_pool_push(task_pool, parallel_iterator_func, userdata_chunk_local, false, NULL); } BLI_task_pool_work_and_wait(task_pool); @@ -656,7 +349,7 @@ typedef struct ParallelMempoolState { TaskParallelMempoolFunc func; } ParallelMempoolState; -static void parallel_mempool_func(TaskPool *__restrict pool, void *taskdata, int UNUSED(threadid)) +static void parallel_mempool_func(TaskPool *__restrict pool, void *taskdata) { ParallelMempoolState *__restrict state = BLI_task_pool_user_data(pool); BLI_mempool_iter *iter = taskdata; @@ -684,7 +377,6 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool, TaskParallelMempoolFunc func, const bool use_threading) { - TaskScheduler *task_scheduler; TaskPool *task_pool; ParallelMempoolState state; int i, num_threads, num_tasks; @@ -704,9 +396,8 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool, return; } - task_scheduler = BLI_task_scheduler_get(); - task_pool = BLI_task_pool_create_suspended(task_scheduler, &state, TASK_PRIORITY_HIGH); - num_threads = BLI_task_scheduler_num_threads(task_scheduler); + task_pool = BLI_task_pool_create(&state, TASK_PRIORITY_HIGH); + num_threads = BLI_task_scheduler_num_threads(); /* The idea here is to prevent creating task for each of the loop iterations * and instead have tasks which are evenly distributed across CPU cores and @@ -720,11 +411,9 @@ void BLI_task_parallel_mempool(BLI_mempool *mempool, BLI_mempool_iter *mempool_iterators = BLI_mempool_iter_threadsafe_create(mempool, (size_t)num_tasks); - const int thread_id = BLI_task_pool_creator_thread_id(task_pool); for (i = 0; i < num_tasks; i++) { /* Use this pool's pre-allocated tasks. */ - BLI_task_pool_push_from_thread( - task_pool, parallel_mempool_func, &mempool_iterators[i], false, NULL, thread_id); + BLI_task_pool_push(task_pool, parallel_mempool_func, &mempool_iterators[i], false, NULL); } BLI_task_pool_work_and_wait(task_pool); diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc index 60ed156105c..b0d7df92343 100644 --- a/source/blender/blenlib/intern/task_pool.cc +++ b/source/blender/blenlib/intern/task_pool.cc @@ -17,731 +17,374 @@ /** \file * \ingroup bli * - * A generic task system which can be used for any task based subsystem. + * Task pool to run tasks in parallel. */ +#include <memory> #include <stdlib.h> +#include <utility> #include "MEM_guardedalloc.h" #include "DNA_listBase.h" -#include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_mempool.h" #include "BLI_task.h" #include "BLI_threads.h" -#include "atomic_ops.h" - -/* Define this to enable some detailed statistic print. */ -#undef DEBUG_STATS - -/* Types */ - -/* Number of per-thread pre-allocated tasks. - * - * For more details see description of TaskMemPool. - */ -#define MEMPOOL_SIZE 256 - -/* Number of tasks which are pushed directly to local thread queue. - * - * This allows thread to fetch next task without locking the whole queue. - */ -#define LOCAL_QUEUE_SIZE 1 - -/* Number of tasks which are allowed to be scheduled in a delayed manner. - * - * This allows to use less locks per graph node children schedule. More details - * could be found at TaskThreadLocalStorage::do_delayed_push. - */ -#define DELAYED_QUEUE_SIZE 4096 - -#ifndef NDEBUG -# define ASSERT_THREAD_ID(scheduler, thread_id) \ - do { \ - if (!BLI_thread_is_main()) { \ - TaskThread *thread = (TaskThread *)pthread_getspecific(scheduler->tls_id_key); \ - if (thread == NULL) { \ - BLI_assert(thread_id == 0); \ - } \ - else { \ - BLI_assert(thread_id == thread->id); \ - } \ - } \ - else { \ - BLI_assert(thread_id == 0); \ - } \ - } while (false) -#else -# define ASSERT_THREAD_ID(scheduler, thread_id) +#ifdef WITH_TBB +/* Quiet top level deprecation message, unrelated to API usage here. */ +# define TBB_SUPPRESS_DEPRECATED_MESSAGES 1 +# include <tbb/tbb.h> #endif -typedef struct Task { - struct Task *next, *prev; +/* Task + * + * Unit of work to execute. This is a C++ class to work with TBB. */ +class Task { + public: + TaskPool *pool; TaskRunFunction run; void *taskdata; bool free_taskdata; TaskFreeFunction freedata; - TaskPool *pool; -} Task; -/* This is a per-thread storage of pre-allocated tasks. - * - * The idea behind this is simple: reduce amount of malloc() calls when pushing - * new task to the pool. This is done by keeping memory from the tasks which - * were finished already, so instead of freeing that memory we put it to the - * pool for the later re-use. - * - * The tricky part here is to avoid any inter-thread synchronization, hence no - * lock must exist around this pool. The pool will become an owner of the pointer - * from freed task, and only corresponding thread will be able to use this pool - * (no memory stealing and such). - * - * This leads to the following use of the pool: - * - * - task_push() should provide proper thread ID from which the task is being - * pushed from. - * - * - Task allocation function which check corresponding memory pool and if there - * is any memory in there it'll mark memory as re-used, remove it from the pool - * and use that memory for the new task. - * - * At this moment task queue owns the memory. - * - * - When task is done and task_free() is called the memory will be put to the - * pool which corresponds to a thread which handled the task. - */ -typedef struct TaskMemPool { - /* Number of pre-allocated tasks in the pool. */ - int num_tasks; - /* Pre-allocated task memory pointers. */ - Task *tasks[MEMPOOL_SIZE]; -} TaskMemPool; - -#ifdef DEBUG_STATS -typedef struct TaskMemPoolStats { - /* Number of allocations. */ - int num_alloc; - /* Number of avoided allocations (pointer was re-used from the pool). */ - int num_reuse; - /* Number of discarded memory due to pool saturation, */ - int num_discard; -} TaskMemPoolStats; -#endif - -typedef struct TaskThreadLocalStorage { - /* Memory pool for faster task allocation. - * The idea is to re-use memory of finished/discarded tasks by this thread. - */ - TaskMemPool task_mempool; - - /* Local queue keeps thread alive by keeping small amount of tasks ready - * to be picked up without causing global thread locks for synchronization. - */ - int num_local_queue; - Task *local_queue[LOCAL_QUEUE_SIZE]; - - /* Thread can be marked for delayed tasks push. This is helpful when it's - * know that lots of subsequent task pushed will happen from the same thread - * without "interrupting" for task execution. - * - * We try to accumulate as much tasks as possible in a local queue without - * any locks first, and then we push all of them into a scheduler's queue - * from within a single mutex lock. - */ - bool do_delayed_push; - int num_delayed_queue; - Task *delayed_queue[DELAYED_QUEUE_SIZE]; -} TaskThreadLocalStorage; - -struct TaskPool { - TaskScheduler *scheduler; - - volatile size_t num; - ThreadMutex num_mutex; - ThreadCondition num_cond; - - void *userdata; - ThreadMutex user_mutex; + Task(TaskPool *pool, + TaskRunFunction run, + void *taskdata, + bool free_taskdata, + TaskFreeFunction freedata) + : pool(pool), run(run), taskdata(taskdata), free_taskdata(free_taskdata), freedata(freedata) + { + } - volatile bool do_cancel; - volatile bool do_work; + ~Task() + { + if (free_taskdata) { + if (freedata) { + freedata(pool, taskdata); + } + else { + MEM_freeN(taskdata); + } + } + } - volatile bool is_suspended; - bool start_suspended; - ListBase suspended_queue; - size_t num_suspended; + /* Move constructor. */ + Task(Task &&other) + : pool(other.pool), + run(other.run), + taskdata(other.taskdata), + free_taskdata(other.free_taskdata), + freedata(other.freedata) + { + other.pool = NULL; + other.run = NULL; + other.taskdata = NULL; + other.free_taskdata = false; + other.freedata = NULL; + } - TaskPriority priority; + /* Execute task. */ + void operator()() const + { + run(pool, taskdata); + } - /* If set, this pool may never be work_and_wait'ed, which means TaskScheduler - * has to use its special background fallback thread in case we are in - * single-threaded situation. - */ - bool run_in_background; + /* For performance, ensure we never copy the task and only move it. */ + Task(const Task &other) = delete; + Task &operator=(const Task &other) = delete; + Task &operator=(Task &&other) = delete; +}; - /* This is a task scheduler's ID of a thread at which pool was constructed. - * It will be used to access task TLS. - */ - int thread_id; +/* TBB Task Group. + * + * Subclass since there seems to be no other way to set priority. */ + +#ifdef WITH_TBB +class TBBTaskGroup : public tbb::task_group { + public: + TBBTaskGroup(TaskPriority priority) + { + switch (priority) { + case TASK_PRIORITY_LOW: + my_context.set_priority(tbb::priority_low); + break; + case TASK_PRIORITY_HIGH: + my_context.set_priority(tbb::priority_normal); + break; + } + } - /* For the pools which are created from non-main thread which is not a - * scheduler worker thread we can't re-use any of scheduler's threads TLS - * and have to use our own one. - */ - bool use_local_tls; - TaskThreadLocalStorage local_tls; -#ifndef NDEBUG - pthread_t creator_thread_id; + ~TBBTaskGroup() + { + } +}; #endif -#ifdef DEBUG_STATS - TaskMemPoolStats *mempool_stats; -#endif -}; +/* Task Pool */ -struct TaskScheduler { - pthread_t *threads; - struct TaskThread *task_threads; - int num_threads; - bool background_thread_only; +typedef enum TaskPoolType { + TASK_POOL_TBB, + TASK_POOL_TBB_SUSPENDED, + TASK_POOL_NO_THREADS, + TASK_POOL_BACKGROUND, + TASK_POOL_BACKGROUND_SERIAL, +} TaskPoolType; - ListBase queue; - ThreadMutex queue_mutex; - ThreadCondition queue_cond; +struct TaskPool { + TaskPoolType type; + bool use_threads; - ThreadMutex startup_mutex; - ThreadCondition startup_cond; - volatile int num_thread_started; + ThreadMutex user_mutex; + void *userdata; - volatile bool do_exit; + /* TBB task pool. */ +#ifdef WITH_TBB + TBBTaskGroup tbb_group; +#endif + volatile bool is_suspended; + BLI_mempool *suspended_mempool; - /* NOTE: In pthread's TLS we store the whole TaskThread structure. */ - pthread_key_t tls_id_key; + /* Background task pool. */ + ListBase background_threads; + ThreadQueue *background_queue; + volatile bool background_is_canceling; }; -typedef struct TaskThread { - TaskScheduler *scheduler; - int id; - TaskThreadLocalStorage tls; -} TaskThread; - -/* Helper */ -BLI_INLINE void task_data_free(Task *task, const int UNUSED(thread_id)) -{ - if (task->free_taskdata) { - if (task->freedata) { - task->freedata(task->pool, task->taskdata); - } - else { - MEM_freeN(task->taskdata); - } - } -} - -BLI_INLINE void initialize_task_tls(TaskThreadLocalStorage *tls) -{ - memset(tls, 0, sizeof(TaskThreadLocalStorage)); -} +/* TBB Task Pool. + * + * Task pool using the TBB scheduler for tasks. When building without TBB + * support or running Blender with -t 1, this reverts to single threaded. + * + * Tasks may be suspended until in all are created, to make it possible to + * initialize data structures and create tasks in a single pass. */ -BLI_INLINE TaskThreadLocalStorage *get_task_tls(TaskPool *pool, const int thread_id) +static void tbb_task_pool_create(TaskPool *pool, TaskPriority priority) { - TaskScheduler *scheduler = pool->scheduler; - BLI_assert(thread_id >= 0); - BLI_assert(thread_id <= scheduler->num_threads); - if (pool->use_local_tls && thread_id == 0) { - BLI_assert(pool->thread_id == 0); - BLI_assert(!BLI_thread_is_main()); - BLI_assert(pthread_equal(pthread_self(), pool->creator_thread_id)); - return &pool->local_tls; - } - if (thread_id == 0) { - BLI_assert(BLI_thread_is_main()); - return &scheduler->task_threads[pool->thread_id].tls; + if (pool->type == TASK_POOL_TBB_SUSPENDED) { + pool->is_suspended = true; + pool->suspended_mempool = BLI_mempool_create(sizeof(Task), 512, 512, BLI_MEMPOOL_ALLOW_ITER); } - return &scheduler->task_threads[thread_id].tls; -} -BLI_INLINE void free_task_tls(TaskThreadLocalStorage *tls) -{ - TaskMemPool *task_mempool = &tls->task_mempool; - for (int i = 0; i < task_mempool->num_tasks; i++) { - MEM_freeN(task_mempool->tasks[i]); +#ifdef WITH_TBB + if (pool->use_threads) { + new (&pool->tbb_group) TBBTaskGroup(priority); } -} - -static Task *task_alloc(TaskPool *pool, const int thread_id) -{ - BLI_assert(thread_id <= pool->scheduler->num_threads); - if (thread_id != -1) { - BLI_assert(thread_id >= 0); - BLI_assert(thread_id <= pool->scheduler->num_threads); - TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id); - TaskMemPool *task_mempool = &tls->task_mempool; - /* Try to re-use task memory from a thread local storage. */ - if (task_mempool->num_tasks > 0) { - --task_mempool->num_tasks; - /* Success! We've just avoided task allocation. */ -#ifdef DEBUG_STATS - pool->mempool_stats[thread_id].num_reuse++; -#endif - return task_mempool->tasks[task_mempool->num_tasks]; - } - /* We are doomed to allocate new task data. */ -#ifdef DEBUG_STATS - pool->mempool_stats[thread_id].num_alloc++; +#else + UNUSED_VARS(priority); #endif - } - return (Task *)MEM_mallocN(sizeof(Task), "New task"); } -static void task_free(TaskPool *pool, Task *task, const int thread_id) +static void tbb_task_pool_run(TaskPool *pool, Task &&task) { - task_data_free(task, thread_id); - BLI_assert(thread_id >= 0); - BLI_assert(thread_id <= pool->scheduler->num_threads); - if (thread_id == 0) { - BLI_assert(pool->use_local_tls || BLI_thread_is_main()); + if (pool->is_suspended) { + /* Suspended task that will be executed in work_and_wait(). */ + Task *task_mem = (Task *)BLI_mempool_alloc(pool->suspended_mempool); + new (task_mem) Task(std::move(task)); +#ifdef __GNUC__ + /* Work around apparent compiler bug where task is not properly copied + * to task_mem. This appears unrelated to the use of placement new or + * move semantics, happens even writing to a plain C struct. Rather the + * call into TBB seems to have some indirect effect. */ + std::atomic_thread_fence(std::memory_order_release); +#endif } - TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id); - TaskMemPool *task_mempool = &tls->task_mempool; - if (task_mempool->num_tasks < MEMPOOL_SIZE - 1) { - /* Successfully allowed the task to be re-used later. */ - task_mempool->tasks[task_mempool->num_tasks] = task; - ++task_mempool->num_tasks; +#ifdef WITH_TBB + else if (pool->use_threads) { + /* Execute in TBB task group. */ + pool->tbb_group.run(std::move(task)); } - else { - /* Local storage saturated, no other way than just discard - * the memory. - * - * TODO(sergey): We can perhaps store such pointer in a global - * scheduler pool, maybe it'll be faster than discarding and - * allocating again. - */ - MEM_freeN(task); -#ifdef DEBUG_STATS - pool->mempool_stats[thread_id].num_discard++; #endif + else { + /* Execute immediately. */ + task(); } } -/* Task Scheduler */ - -static void task_pool_num_decrease(TaskPool *pool, size_t done) +static void tbb_task_pool_work_and_wait(TaskPool *pool) { - BLI_mutex_lock(&pool->num_mutex); + /* Start any suspended task now. */ + if (pool->suspended_mempool) { + pool->is_suspended = false; - BLI_assert(pool->num >= done); - - pool->num -= done; + BLI_mempool_iter iter; + BLI_mempool_iternew(pool->suspended_mempool, &iter); + while (Task *task = (Task *)BLI_mempool_iterstep(&iter)) { + tbb_task_pool_run(pool, std::move(*task)); + } - if (pool->num == 0) { - BLI_condition_notify_all(&pool->num_cond); + BLI_mempool_clear(pool->suspended_mempool); } - BLI_mutex_unlock(&pool->num_mutex); +#ifdef WITH_TBB + if (pool->use_threads) { + /* This is called wait(), but internally it can actually do work. This + * matters because we don't want recursive usage of task pools to run + * out of threads and get stuck. */ + pool->tbb_group.wait(); + } +#endif } -static void task_pool_num_increase(TaskPool *pool, size_t new_num) +static void tbb_task_pool_cancel(TaskPool *pool) { - BLI_mutex_lock(&pool->num_mutex); - - pool->num += new_num; - BLI_condition_notify_all(&pool->num_cond); - - BLI_mutex_unlock(&pool->num_mutex); +#ifdef WITH_TBB + if (pool->use_threads) { + pool->tbb_group.cancel(); + pool->tbb_group.wait(); + } +#else + UNUSED_VARS(pool); +#endif } -static bool task_scheduler_thread_wait_pop(TaskScheduler *scheduler, Task **task) +static bool tbb_task_pool_canceled(TaskPool *pool) { - bool found_task = false; - BLI_mutex_lock(&scheduler->queue_mutex); - - while (!scheduler->queue.first && !scheduler->do_exit) { - BLI_condition_wait(&scheduler->queue_cond, &scheduler->queue_mutex); +#ifdef WITH_TBB + if (pool->use_threads) { + return pool->tbb_group.is_canceling(); } +#else + UNUSED_VARS(pool); +#endif - do { - Task *current_task; - - /* Assuming we can only have a void queue in 'exit' case here seems logical - * (we should only be here after our worker thread has been woken up from a - * condition_wait(), which only happens after a new task was added to the queue), - * but it is wrong. - * Waiting on condition may wake up the thread even if condition is not signaled - * (spurious wake-ups), and some race condition may also empty the queue **after** - * condition has been signaled, but **before** awoken thread reaches this point... - * See http://stackoverflow.com/questions/8594591 - * - * So we only abort here if do_exit is set. - */ - if (scheduler->do_exit) { - BLI_mutex_unlock(&scheduler->queue_mutex); - return false; - } - - for (current_task = (Task *)scheduler->queue.first; current_task != NULL; - current_task = current_task->next) { - TaskPool *pool = current_task->pool; - - if (scheduler->background_thread_only && !pool->run_in_background) { - continue; - } - - *task = current_task; - found_task = true; - BLI_remlink(&scheduler->queue, *task); - break; - } - if (!found_task) { - BLI_condition_wait(&scheduler->queue_cond, &scheduler->queue_mutex); - } - } while (!found_task); - - BLI_mutex_unlock(&scheduler->queue_mutex); - - return true; + return false; } -BLI_INLINE void handle_local_queue(TaskThreadLocalStorage *tls, const int thread_id) +static void tbb_task_pool_free(TaskPool *pool) { - BLI_assert(!tls->do_delayed_push); - while (tls->num_local_queue > 0) { - /* We pop task from queue before handling it so handler of the task can - * push next job to the local queue. - */ - tls->num_local_queue--; - Task *local_task = tls->local_queue[tls->num_local_queue]; - /* TODO(sergey): Double-check work_and_wait() doesn't handle other's - * pool tasks. - */ - TaskPool *local_pool = local_task->pool; - local_task->run(local_pool, local_task->taskdata, thread_id); - task_free(local_pool, local_task, thread_id); +#ifdef WITH_TBB + if (pool->use_threads) { + pool->tbb_group.~TBBTaskGroup(); } - BLI_assert(!tls->do_delayed_push); -} +#endif -static void *task_scheduler_thread_run(void *thread_p) -{ - TaskThread *thread = (TaskThread *)thread_p; - TaskThreadLocalStorage *tls = &thread->tls; - TaskScheduler *scheduler = thread->scheduler; - int thread_id = thread->id; - Task *task; - - pthread_setspecific(scheduler->tls_id_key, thread); - - /* signal the main thread when all threads have started */ - BLI_mutex_lock(&scheduler->startup_mutex); - scheduler->num_thread_started++; - if (scheduler->num_thread_started == scheduler->num_threads) { - BLI_condition_notify_one(&scheduler->startup_cond); + if (pool->suspended_mempool) { + BLI_mempool_destroy(pool->suspended_mempool); } - BLI_mutex_unlock(&scheduler->startup_mutex); - - /* keep popping off tasks */ - while (task_scheduler_thread_wait_pop(scheduler, &task)) { - TaskPool *pool = task->pool; - - /* run task */ - BLI_assert(!tls->do_delayed_push); - task->run(pool, task->taskdata, thread_id); - BLI_assert(!tls->do_delayed_push); - - /* delete task */ - task_free(pool, task, thread_id); +} - /* Handle all tasks from local queue. */ - handle_local_queue(tls, thread_id); +/* Background Task Pool. + * + * Fallback for running background tasks when building without TBB. */ - /* notify pool task was done */ - task_pool_num_decrease(pool, 1); +static void *background_task_run(void *userdata) +{ + TaskPool *pool = (TaskPool *)userdata; + while (Task *task = (Task *)BLI_thread_queue_pop(pool->background_queue)) { + (*task)(); + task->~Task(); + MEM_freeN(task); } - return NULL; } -TaskScheduler *BLI_task_scheduler_create(int num_threads) +static void background_task_pool_create(TaskPool *pool) { - TaskScheduler *scheduler = (TaskScheduler *)MEM_callocN(sizeof(TaskScheduler), "TaskScheduler"); - - /* multiple places can use this task scheduler, sharing the same - * threads, so we keep track of the number of users. */ - scheduler->do_exit = false; - - BLI_listbase_clear(&scheduler->queue); - BLI_mutex_init(&scheduler->queue_mutex); - BLI_condition_init(&scheduler->queue_cond); - - BLI_mutex_init(&scheduler->startup_mutex); - BLI_condition_init(&scheduler->startup_cond); - scheduler->num_thread_started = 0; - - if (num_threads == 0) { - /* automatic number of threads will be main thread + num cores */ - num_threads = BLI_system_thread_count(); - } - - /* main thread will also work, so we count it too */ - num_threads -= 1; - - /* Add background-only thread if needed. */ - if (num_threads == 0) { - scheduler->background_thread_only = true; - num_threads = 1; - } - - scheduler->task_threads = (TaskThread *)MEM_mallocN(sizeof(TaskThread) * (num_threads + 1), - "TaskScheduler task threads"); - - /* Initialize TLS for main thread. */ - initialize_task_tls(&scheduler->task_threads[0].tls); - - pthread_key_create(&scheduler->tls_id_key, NULL); - - /* launch threads that will be waiting for work */ - if (num_threads > 0) { - int i; - - scheduler->num_threads = num_threads; - scheduler->threads = (pthread_t *)MEM_callocN(sizeof(pthread_t) * num_threads, - "TaskScheduler threads"); - - for (i = 0; i < num_threads; i++) { - TaskThread *thread = &scheduler->task_threads[i + 1]; - thread->scheduler = scheduler; - thread->id = i + 1; - initialize_task_tls(&thread->tls); - - if (pthread_create(&scheduler->threads[i], NULL, task_scheduler_thread_run, thread) != 0) { - fprintf(stderr, "TaskScheduler failed to launch thread %d/%d\n", i, num_threads); - } - } - } - - /* Wait for all worker threads to start before returning to caller to prevent the case where - * threads are still starting and pthread_join is called, which causes a deadlock on pthreads4w. - */ - BLI_mutex_lock(&scheduler->startup_mutex); - /* NOTE: Use loop here to avoid false-positive everything-is-ready caused by spontaneous thread - * wake up. */ - while (scheduler->num_thread_started != num_threads) { - BLI_condition_wait(&scheduler->startup_cond, &scheduler->startup_mutex); - } - BLI_mutex_unlock(&scheduler->startup_mutex); - - return scheduler; + pool->background_queue = BLI_thread_queue_init(); + BLI_threadpool_init(&pool->background_threads, background_task_run, 1); + BLI_threadpool_insert(&pool->background_threads, pool); } -void BLI_task_scheduler_free(TaskScheduler *scheduler) +static void background_task_pool_run(TaskPool *pool, Task &&task) { - Task *task; - - /* stop all waiting threads */ - BLI_mutex_lock(&scheduler->queue_mutex); - scheduler->do_exit = true; - BLI_condition_notify_all(&scheduler->queue_cond); - BLI_mutex_unlock(&scheduler->queue_mutex); - - pthread_key_delete(scheduler->tls_id_key); - - /* delete threads */ - if (scheduler->threads) { - int i; - - for (i = 0; i < scheduler->num_threads; i++) { - if (pthread_join(scheduler->threads[i], NULL) != 0) { - fprintf(stderr, "TaskScheduler failed to join thread %d/%d\n", i, scheduler->num_threads); - } - } - - MEM_freeN(scheduler->threads); - } - - /* Delete task thread data */ - if (scheduler->task_threads) { - for (int i = 0; i < scheduler->num_threads + 1; i++) { - TaskThreadLocalStorage *tls = &scheduler->task_threads[i].tls; - free_task_tls(tls); - } - - MEM_freeN(scheduler->task_threads); - } - - /* delete leftover tasks */ - for (task = (Task *)scheduler->queue.first; task; task = task->next) { - task_data_free(task, 0); - } - BLI_freelistN(&scheduler->queue); - - /* delete mutex/condition */ - BLI_mutex_end(&scheduler->queue_mutex); - BLI_condition_end(&scheduler->queue_cond); - BLI_mutex_end(&scheduler->startup_mutex); - BLI_condition_end(&scheduler->startup_cond); - - MEM_freeN(scheduler); + Task *task_mem = (Task *)MEM_mallocN(sizeof(Task), __func__); + new (task_mem) Task(std::move(task)); + BLI_thread_queue_push(pool->background_queue, task_mem); } -int BLI_task_scheduler_num_threads(TaskScheduler *scheduler) +static void background_task_pool_work_and_wait(TaskPool *pool) { - return scheduler->num_threads + 1; + /* Signal background thread to stop waiting for new tasks if none are + * left, and wait for tasks and thread to finish. */ + BLI_thread_queue_nowait(pool->background_queue); + BLI_thread_queue_wait_finish(pool->background_queue); + BLI_threadpool_remove(&pool->background_threads, pool); } -static void task_scheduler_push(TaskScheduler *scheduler, Task *task, TaskPriority priority) +static void background_task_pool_cancel(TaskPool *pool) { - task_pool_num_increase(task->pool, 1); + pool->background_is_canceling = true; - /* add task to queue */ - BLI_mutex_lock(&scheduler->queue_mutex); - - if (priority == TASK_PRIORITY_HIGH) { - BLI_addhead(&scheduler->queue, task); - } - else { - BLI_addtail(&scheduler->queue, task); + /* Remove tasks not yet started by background thread. */ + BLI_thread_queue_nowait(pool->background_queue); + while (Task *task = (Task *)BLI_thread_queue_pop(pool->background_queue)) { + task->~Task(); + MEM_freeN(task); } - BLI_condition_notify_one(&scheduler->queue_cond); - BLI_mutex_unlock(&scheduler->queue_mutex); + /* Let background thread finish or cancel task it is working on. */ + BLI_threadpool_remove(&pool->background_threads, pool); + pool->background_is_canceling = false; } -static void task_scheduler_push_all(TaskScheduler *scheduler, - TaskPool *pool, - Task **tasks, - int num_tasks) +static bool background_task_pool_canceled(TaskPool *pool) { - if (num_tasks == 0) { - return; - } - - task_pool_num_increase(pool, num_tasks); - - BLI_mutex_lock(&scheduler->queue_mutex); - - for (int i = 0; i < num_tasks; i++) { - BLI_addhead(&scheduler->queue, tasks[i]); - } - - BLI_condition_notify_all(&scheduler->queue_cond); - BLI_mutex_unlock(&scheduler->queue_mutex); + return pool->background_is_canceling; } -static void task_scheduler_clear(TaskScheduler *scheduler, TaskPool *pool) +static void background_task_pool_free(TaskPool *pool) { - Task *task, *nexttask; - size_t done = 0; - - BLI_mutex_lock(&scheduler->queue_mutex); - - /* free all tasks from this pool from the queue */ - for (task = (Task *)scheduler->queue.first; task; task = nexttask) { - nexttask = task->next; - - if (task->pool == pool) { - task_data_free(task, pool->thread_id); - BLI_freelinkN(&scheduler->queue, task); + background_task_pool_work_and_wait(pool); - done++; - } - } - - BLI_mutex_unlock(&scheduler->queue_mutex); - - /* notify done */ - task_pool_num_decrease(pool, done); + BLI_threadpool_end(&pool->background_threads); + BLI_thread_queue_free(pool->background_queue); } /* Task Pool */ -static TaskPool *task_pool_create_ex(TaskScheduler *scheduler, - void *userdata, - const bool is_background, - const bool is_suspended, - TaskPriority priority) +static TaskPool *task_pool_create_ex(void *userdata, TaskPoolType type, TaskPriority priority) { - TaskPool *pool = (TaskPool *)MEM_mallocN(sizeof(TaskPool), "TaskPool"); + /* Ensure malloc will go fine from threads, + * + * This is needed because we could be in main thread here + * and malloc could be non-thread safe at this point because + * no other jobs are running. + */ + BLI_threaded_malloc_begin(); -#ifndef NDEBUG - /* Assert we do not try to create a background pool from some parent task - - * those only work OK from main thread. */ - if (is_background) { - const pthread_t thread_id = pthread_self(); - int i = scheduler->num_threads; + const bool use_threads = BLI_task_scheduler_num_threads() > 1 && type != TASK_POOL_NO_THREADS; - while (i--) { - BLI_assert(!pthread_equal(scheduler->threads[i], thread_id)); - } + /* Background task pool uses regular TBB scheduling if available. Only when + * building without TBB or running with -t 1 do we need to ensure these tasks + * do not block the main thread. */ + if (type == TASK_POOL_BACKGROUND && use_threads) { + type = TASK_POOL_TBB; } -#endif - pool->scheduler = scheduler; - pool->num = 0; - pool->do_cancel = false; - pool->do_work = false; - pool->is_suspended = is_suspended; - pool->start_suspended = is_suspended; - pool->num_suspended = 0; - pool->suspended_queue.first = pool->suspended_queue.last = NULL; - pool->priority = priority; - pool->run_in_background = is_background; - pool->use_local_tls = false; - - BLI_mutex_init(&pool->num_mutex); - BLI_condition_init(&pool->num_cond); + /* Allocate task pool. */ + TaskPool *pool = (TaskPool *)MEM_callocN(sizeof(TaskPool), "TaskPool"); + + pool->type = type; + pool->use_threads = use_threads; pool->userdata = userdata; BLI_mutex_init(&pool->user_mutex); - if (BLI_thread_is_main()) { - pool->thread_id = 0; - } - else { - TaskThread *thread = (TaskThread *)pthread_getspecific(scheduler->tls_id_key); - if (thread == NULL) { - /* NOTE: Task pool is created from non-main thread which is not - * managed by the task scheduler. We identify ourselves as thread ID - * 0 but we do not use scheduler's TLS storage and use our own - * instead to avoid any possible threading conflicts. - */ - pool->thread_id = 0; - pool->use_local_tls = true; -#ifndef NDEBUG - pool->creator_thread_id = pthread_self(); -#endif - initialize_task_tls(&pool->local_tls); - } - else { - pool->thread_id = thread->id; - } + switch (type) { + case TASK_POOL_TBB: + case TASK_POOL_TBB_SUSPENDED: + case TASK_POOL_NO_THREADS: + tbb_task_pool_create(pool, priority); + break; + case TASK_POOL_BACKGROUND: + case TASK_POOL_BACKGROUND_SERIAL: + background_task_pool_create(pool); + break; } -#ifdef DEBUG_STATS - pool->mempool_stats = (TaskMemPoolStats *)MEM_callocN( - sizeof(*pool->mempool_stats) * (scheduler->num_threads + 1), "per-taskpool mempool stats"); -#endif - - /* Ensure malloc will go fine from threads, - * - * This is needed because we could be in main thread here - * and malloc could be non-thread safe at this point because - * no other jobs are running. - */ - BLI_threaded_malloc_begin(); - return pool; } /** * Create a normal task pool. Tasks will be executed as soon as they are added. */ -TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata, TaskPriority priority) +TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority) { - return task_pool_create_ex(scheduler, userdata, false, false, priority); + return task_pool_create_ex(userdata, TASK_POOL_TBB, priority); } /** @@ -756,11 +399,9 @@ TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata, TaskPri * they could end never being executed, since the 'fallback' background thread is already * busy with parent task in single-threaded context). */ -TaskPool *BLI_task_pool_create_background(TaskScheduler *scheduler, - void *userdata, - TaskPriority priority) +TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority) { - return task_pool_create_ex(scheduler, userdata, true, false, priority); + return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND, priority); } /** @@ -768,228 +409,114 @@ TaskPool *BLI_task_pool_create_background(TaskScheduler *scheduler, * for until BLI_task_pool_work_and_wait() is called. This helps reducing threading * overhead when pushing huge amount of small initial tasks from the main thread. */ -TaskPool *BLI_task_pool_create_suspended(TaskScheduler *scheduler, - void *userdata, - TaskPriority priority) +TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority) { - return task_pool_create_ex(scheduler, userdata, false, true, priority); + return task_pool_create_ex(userdata, TASK_POOL_TBB_SUSPENDED, priority); } -void BLI_task_pool_free(TaskPool *pool) +/** + * Single threaded task pool that executes pushed task immediately, for + * debugging purposes. + */ +TaskPool *BLI_task_pool_create_no_threads(void *userdata) { - BLI_task_pool_cancel(pool); - - BLI_mutex_end(&pool->num_mutex); - BLI_condition_end(&pool->num_cond); + return task_pool_create_ex(userdata, TASK_POOL_NO_THREADS, TASK_PRIORITY_HIGH); +} - BLI_mutex_end(&pool->user_mutex); +/** + * Task pool that executeds one task after the other, possibly on different threads + * but never in parallel. + */ +TaskPool *BLI_task_pool_create_background_serial(void *userdata, TaskPriority priority) +{ + return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND_SERIAL, priority); +} -#ifdef DEBUG_STATS - printf("Thread ID Allocated Reused Discarded\n"); - for (int i = 0; i < pool->scheduler->num_threads + 1; i++) { - printf("%02d %05d %05d %05d\n", - i, - pool->mempool_stats[i].num_alloc, - pool->mempool_stats[i].num_reuse, - pool->mempool_stats[i].num_discard); +void BLI_task_pool_free(TaskPool *pool) +{ + switch (pool->type) { + case TASK_POOL_TBB: + case TASK_POOL_TBB_SUSPENDED: + case TASK_POOL_NO_THREADS: + tbb_task_pool_free(pool); + break; + case TASK_POOL_BACKGROUND: + case TASK_POOL_BACKGROUND_SERIAL: + background_task_pool_free(pool); + break; } - MEM_freeN(pool->mempool_stats); -#endif - if (pool->use_local_tls) { - free_task_tls(&pool->local_tls); - } + BLI_mutex_end(&pool->user_mutex); MEM_freeN(pool); BLI_threaded_malloc_end(); } -BLI_INLINE bool task_can_use_local_queues(TaskPool *pool, int thread_id) -{ - return (thread_id != -1 && (thread_id != pool->thread_id || pool->do_work)); -} - -static void task_pool_push(TaskPool *pool, - TaskRunFunction run, - void *taskdata, - bool free_taskdata, - TaskFreeFunction freedata, - int thread_id) -{ - /* Allocate task and fill it's properties. */ - Task *task = task_alloc(pool, thread_id); - task->run = run; - task->taskdata = taskdata; - task->free_taskdata = free_taskdata; - task->freedata = freedata; - task->pool = pool; - /* For suspended pools we put everything yo a global queue first - * and exit as soon as possible. - * - * This tasks will be moved to actual execution when pool is - * activated by work_and_wait(). - */ - if (pool->is_suspended) { - BLI_addhead(&pool->suspended_queue, task); - atomic_fetch_and_add_z(&pool->num_suspended, 1); - return; - } - /* Populate to any local queue first, this is cheapest push ever. */ - if (task_can_use_local_queues(pool, thread_id)) { - ASSERT_THREAD_ID(pool->scheduler, thread_id); - TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id); - /* Try to push to a local execution queue. - * These tasks will be picked up next. - */ - if (tls->num_local_queue < LOCAL_QUEUE_SIZE) { - tls->local_queue[tls->num_local_queue] = task; - tls->num_local_queue++; - return; - } - /* If we are in the delayed tasks push mode, we push tasks to a - * temporary local queue first without any locks, and then move them - * to global execution queue with a single lock. - */ - if (tls->do_delayed_push && tls->num_delayed_queue < DELAYED_QUEUE_SIZE) { - tls->delayed_queue[tls->num_delayed_queue] = task; - tls->num_delayed_queue++; - return; - } - } - /* Do push to a global execution pool, slowest possible method, - * causes quite reasonable amount of threading overhead. - */ - task_scheduler_push(pool->scheduler, task, pool->priority); -} - void BLI_task_pool_push(TaskPool *pool, TaskRunFunction run, void *taskdata, bool free_taskdata, TaskFreeFunction freedata) { - task_pool_push(pool, run, taskdata, free_taskdata, freedata, -1); -} + Task task(pool, run, taskdata, free_taskdata, freedata); -void BLI_task_pool_push_from_thread(TaskPool *pool, - TaskRunFunction run, - void *taskdata, - bool free_taskdata, - TaskFreeFunction freedata, - int thread_id) -{ - task_pool_push(pool, run, taskdata, free_taskdata, freedata, thread_id); + switch (pool->type) { + case TASK_POOL_TBB: + case TASK_POOL_TBB_SUSPENDED: + case TASK_POOL_NO_THREADS: + tbb_task_pool_run(pool, std::move(task)); + break; + case TASK_POOL_BACKGROUND: + case TASK_POOL_BACKGROUND_SERIAL: + background_task_pool_run(pool, std::move(task)); + break; + } } void BLI_task_pool_work_and_wait(TaskPool *pool) { - TaskThreadLocalStorage *tls = get_task_tls(pool, pool->thread_id); - TaskScheduler *scheduler = pool->scheduler; - - if (atomic_fetch_and_and_uint8((uint8_t *)&pool->is_suspended, 0)) { - if (pool->num_suspended) { - task_pool_num_increase(pool, pool->num_suspended); - BLI_mutex_lock(&scheduler->queue_mutex); - - BLI_movelisttolist(&scheduler->queue, &pool->suspended_queue); - - BLI_condition_notify_all(&scheduler->queue_cond); - BLI_mutex_unlock(&scheduler->queue_mutex); - - pool->num_suspended = 0; - } - } - - pool->do_work = true; - - ASSERT_THREAD_ID(pool->scheduler, pool->thread_id); - - handle_local_queue(tls, pool->thread_id); - - BLI_mutex_lock(&pool->num_mutex); - - while (pool->num != 0) { - Task *task, *work_task = NULL; - bool found_task = false; - - BLI_mutex_unlock(&pool->num_mutex); - - BLI_mutex_lock(&scheduler->queue_mutex); - - /* find task from this pool. if we get a task from another pool, - * we can get into deadlock */ - - for (task = (Task *)scheduler->queue.first; task; task = task->next) { - if (task->pool == pool) { - work_task = task; - found_task = true; - BLI_remlink(&scheduler->queue, task); - break; - } - } - - BLI_mutex_unlock(&scheduler->queue_mutex); - - /* if found task, do it, otherwise wait until other tasks are done */ - if (found_task) { - /* run task */ - BLI_assert(!tls->do_delayed_push); - work_task->run(pool, work_task->taskdata, pool->thread_id); - BLI_assert(!tls->do_delayed_push); - - /* delete task */ - task_free(pool, task, pool->thread_id); - - /* Handle all tasks from local queue. */ - handle_local_queue(tls, pool->thread_id); - - /* notify pool task was done */ - task_pool_num_decrease(pool, 1); - } - - BLI_mutex_lock(&pool->num_mutex); - if (pool->num == 0) { + switch (pool->type) { + case TASK_POOL_TBB: + case TASK_POOL_TBB_SUSPENDED: + case TASK_POOL_NO_THREADS: + tbb_task_pool_work_and_wait(pool); + break; + case TASK_POOL_BACKGROUND: + case TASK_POOL_BACKGROUND_SERIAL: + background_task_pool_work_and_wait(pool); break; - } - - if (!found_task) { - BLI_condition_wait(&pool->num_cond, &pool->num_mutex); - } } - - BLI_mutex_unlock(&pool->num_mutex); - - BLI_assert(tls->num_local_queue == 0); -} - -void BLI_task_pool_work_wait_and_reset(TaskPool *pool) -{ - BLI_task_pool_work_and_wait(pool); - - pool->do_work = false; - pool->is_suspended = pool->start_suspended; } void BLI_task_pool_cancel(TaskPool *pool) { - pool->do_cancel = true; - - task_scheduler_clear(pool->scheduler, pool); - - /* wait until all entries are cleared */ - BLI_mutex_lock(&pool->num_mutex); - while (pool->num) { - BLI_condition_wait(&pool->num_cond, &pool->num_mutex); + switch (pool->type) { + case TASK_POOL_TBB: + case TASK_POOL_TBB_SUSPENDED: + case TASK_POOL_NO_THREADS: + tbb_task_pool_cancel(pool); + break; + case TASK_POOL_BACKGROUND: + case TASK_POOL_BACKGROUND_SERIAL: + background_task_pool_cancel(pool); + break; } - BLI_mutex_unlock(&pool->num_mutex); - - pool->do_cancel = false; } bool BLI_task_pool_canceled(TaskPool *pool) { - return pool->do_cancel; + switch (pool->type) { + case TASK_POOL_TBB: + case TASK_POOL_TBB_SUSPENDED: + case TASK_POOL_NO_THREADS: + return tbb_task_pool_canceled(pool); + case TASK_POOL_BACKGROUND: + case TASK_POOL_BACKGROUND_SERIAL: + return background_task_pool_canceled(pool); + } + BLI_assert("BLI_task_pool_canceled: Control flow should not come here!"); + return false; } void *BLI_task_pool_user_data(TaskPool *pool) @@ -1001,29 +528,3 @@ ThreadMutex *BLI_task_pool_user_mutex(TaskPool *pool) { return &pool->user_mutex; } - -int BLI_task_pool_creator_thread_id(TaskPool *pool) -{ - return pool->thread_id; -} - -void BLI_task_pool_delayed_push_begin(TaskPool *pool, int thread_id) -{ - if (task_can_use_local_queues(pool, thread_id)) { - ASSERT_THREAD_ID(pool->scheduler, thread_id); - TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id); - tls->do_delayed_push = true; - } -} - -void BLI_task_pool_delayed_push_end(TaskPool *pool, int thread_id) -{ - if (task_can_use_local_queues(pool, thread_id)) { - ASSERT_THREAD_ID(pool->scheduler, thread_id); - TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id); - BLI_assert(tls->do_delayed_push); - task_scheduler_push_all(pool->scheduler, pool, tls->delayed_queue, tls->num_delayed_queue); - tls->do_delayed_push = false; - tls->num_delayed_queue = 0; - } -} diff --git a/source/blender/blenkernel/intern/pbvh_parallel.cc b/source/blender/blenlib/intern/task_range.cc index 2534fdea3ee..a8447c305e0 100644 --- a/source/blender/blenkernel/intern/pbvh_parallel.cc +++ b/source/blender/blenlib/intern/task_range.cc @@ -14,60 +14,72 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/** \file + * \ingroup bli + * + * Task parallel range functions. + */ + +#include <stdlib.h> + #include "MEM_guardedalloc.h" +#include "DNA_listBase.h" + #include "BLI_task.h" #include "BLI_threads.h" -#include "BKE_pbvh.h" - #include "atomic_ops.h" #ifdef WITH_TBB - /* Quiet top level deprecation message, unrelated to API usage here. */ # define TBB_SUPPRESS_DEPRECATED_MESSAGES 1 - # include <tbb/tbb.h> +#endif + +#ifdef WITH_TBB /* Functor for running TBB parallel_for and parallel_reduce. */ -struct PBVHTask { - PBVHParallelRangeFunc func; +struct RangeTask { + TaskParallelRangeFunc func; void *userdata; - const PBVHParallelSettings *settings; + const TaskParallelSettings *settings; void *userdata_chunk; /* Root constructor. */ - PBVHTask(PBVHParallelRangeFunc func, void *userdata, const PBVHParallelSettings *settings) + RangeTask(TaskParallelRangeFunc func, void *userdata, const TaskParallelSettings *settings) : func(func), userdata(userdata), settings(settings) { init_chunk(settings->userdata_chunk); } /* Copy constructor. */ - PBVHTask(const PBVHTask &other) + RangeTask(const RangeTask &other) : func(other.func), userdata(other.userdata), settings(other.settings) { - init_chunk(other.userdata_chunk); + init_chunk(settings->userdata_chunk); } /* Splitting constructor for parallel reduce. */ - PBVHTask(PBVHTask &other, tbb::split) + RangeTask(RangeTask &other, tbb::split) : func(other.func), userdata(other.userdata), settings(other.settings) { init_chunk(settings->userdata_chunk); } - ~PBVHTask() + ~RangeTask() { + if (settings->func_free != NULL) { + settings->func_free(userdata, userdata_chunk); + } MEM_SAFE_FREE(userdata_chunk); } void init_chunk(void *from_chunk) { if (from_chunk) { - userdata_chunk = MEM_mallocN(settings->userdata_chunk_size, "PBVHTask"); + userdata_chunk = MEM_mallocN(settings->userdata_chunk_size, "RangeTask"); memcpy(userdata_chunk, from_chunk, settings->userdata_chunk_size); } else { @@ -78,59 +90,41 @@ struct PBVHTask { void operator()(const tbb::blocked_range<int> &r) const { TaskParallelTLS tls; - tls.thread_id = get_thread_id(); tls.userdata_chunk = userdata_chunk; for (int i = r.begin(); i != r.end(); ++i) { func(userdata, i, &tls); } } - void join(const PBVHTask &other) + void join(const RangeTask &other) { settings->func_reduce(userdata, userdata_chunk, other.userdata_chunk); } - - int get_thread_id() const - { - /* Get a unique thread ID for texture nodes. In the future we should get rid - * of the thread ID and change texture evaluation to not require per-thread - * storage that can't be efficiently allocated on the stack. */ - static tbb::enumerable_thread_specific<int> pbvh_thread_id(-1); - static int pbvh_thread_id_counter = 0; - - int &thread_id = pbvh_thread_id.local(); - if (thread_id == -1) { - thread_id = atomic_fetch_and_add_int32(&pbvh_thread_id_counter, 1); - if (thread_id >= BLENDER_MAX_THREADS) { - BLI_assert(!"Maximum number of threads exceeded for sculpting"); - thread_id = thread_id % BLENDER_MAX_THREADS; - } - } - return thread_id; - } }; #endif -void BKE_pbvh_parallel_range(const int start, +void BLI_task_parallel_range(const int start, const int stop, void *userdata, - PBVHParallelRangeFunc func, - const struct PBVHParallelSettings *settings) + TaskParallelRangeFunc func, + const TaskParallelSettings *settings) { #ifdef WITH_TBB /* Multithreading. */ - if (settings->use_threading) { - PBVHTask task(func, userdata, settings); + if (settings->use_threading && BLI_task_scheduler_num_threads() > 1) { + RangeTask task(func, userdata, settings); + const size_t grainsize = MAX2(settings->min_iter_per_thread, 1); + const tbb::blocked_range<int> range(start, stop, grainsize); if (settings->func_reduce) { - parallel_reduce(tbb::blocked_range<int>(start, stop), task); + parallel_reduce(range, task); if (settings->userdata_chunk) { memcpy(settings->userdata_chunk, task.userdata_chunk, settings->userdata_chunk_size); } } else { - parallel_for(tbb::blocked_range<int>(start, stop), task); + parallel_for(range, task); } return; @@ -140,9 +134,34 @@ void BKE_pbvh_parallel_range(const int start, /* Single threaded. Nothing to reduce as everything is accumulated into the * main userdata chunk directly. */ TaskParallelTLS tls; - tls.thread_id = 0; tls.userdata_chunk = settings->userdata_chunk; for (int i = start; i < stop; i++) { func(userdata, i, &tls); } + if (settings->func_free != NULL) { + settings->func_free(userdata, settings->userdata_chunk); + } +} + +int BLI_task_parallel_thread_id(const TaskParallelTLS *UNUSED(tls)) +{ +#ifdef WITH_TBB + /* Get a unique thread ID for texture nodes. In the future we should get rid + * of the thread ID and change texture evaluation to not require per-thread + * storage that can't be efficiently allocated on the stack. */ + static tbb::enumerable_thread_specific<int> tbb_thread_id(-1); + static int tbb_thread_id_counter = 0; + + int &thread_id = tbb_thread_id.local(); + if (thread_id == -1) { + thread_id = atomic_fetch_and_add_int32(&tbb_thread_id_counter, 1); + if (thread_id >= BLENDER_MAX_THREADS) { + BLI_assert(!"Maximum number of threads exceeded for sculpting"); + thread_id = thread_id % BLENDER_MAX_THREADS; + } + } + return thread_id; +#else + return 0; +#endif } diff --git a/source/blender/blenlib/intern/task_scheduler.cc b/source/blender/blenlib/intern/task_scheduler.cc new file mode 100644 index 00000000000..682fee5c46d --- /dev/null +++ b/source/blender/blenlib/intern/task_scheduler.cc @@ -0,0 +1,73 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bli + * + * Task scheduler initialization. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_task.h" +#include "BLI_threads.h" + +#ifdef WITH_TBB +/* Quiet top level deprecation message, unrelated to API usage here. */ +# define TBB_SUPPRESS_DEPRECATED_MESSAGES 1 +# include <tbb/tbb.h> +#endif + +/* Task Scheduler */ + +static int task_scheduler_num_threads = 1; +#ifdef WITH_TBB +static tbb::global_control *task_scheduler_global_control = nullptr; +#endif + +void BLI_task_scheduler_init() +{ +#ifdef WITH_TBB + const int num_threads_override = BLI_system_num_threads_override_get(); + + if (num_threads_override > 0) { + /* Override number of threads. This settings is used within the lifetime + * of tbb::global_control, so we allocate it on the heap. */ + task_scheduler_global_control = OBJECT_GUARDED_NEW( + tbb::global_control, tbb::global_control::max_allowed_parallelism, num_threads_override); + task_scheduler_num_threads = num_threads_override; + } + else { + /* Let TBB choose the number of threads. For (legacy) code that calss + * BLI_task_scheduler_num_threads() we provide the system thread count. + * Ideally such code should be rewritten not to use the number of threads + * at all. */ + task_scheduler_num_threads = BLI_system_thread_count(); + } +#endif +} + +void BLI_task_scheduler_exit() +{ +#ifdef WITH_TBB + OBJECT_GUARDED_DELETE(task_scheduler_global_control, tbb::global_control); +#endif +} + +int BLI_task_scheduler_num_threads() +{ + return task_scheduler_num_threads; +} diff --git a/source/blender/blenlib/intern/threads.c b/source/blender/blenlib/intern/threads.c index 31e8581590a..f535798f86d 100644 --- a/source/blender/blenlib/intern/threads.c +++ b/source/blender/blenlib/intern/threads.c @@ -61,9 +61,6 @@ extern pthread_key_t gomp_tls_key; static void *thread_tls_data; #endif -/* We're using one global task scheduler for all kind of tasks. */ -static TaskScheduler *task_scheduler = NULL; - /* ********** basic thread control API ************ * * Many thread cases have an X amount of jobs, and only an Y amount of @@ -157,27 +154,9 @@ void BLI_threadapi_init(void) void BLI_threadapi_exit(void) { - if (task_scheduler) { - BLI_task_scheduler_free(task_scheduler); - task_scheduler = NULL; - } BLI_spin_end(&_malloc_lock); } -TaskScheduler *BLI_task_scheduler_get(void) -{ - if (task_scheduler == NULL) { - int tot_thread = BLI_system_thread_count(); - - /* Do a lazy initialization, so it happens after - * command line arguments parsing - */ - task_scheduler = BLI_task_scheduler_create(tot_thread); - } - - return task_scheduler; -} - /* tot = 0 only initializes malloc mutex in a safe way (see sequence.c) * problem otherwise: scene render will kill of the mutex! */ @@ -839,11 +818,6 @@ void BLI_threaded_malloc_begin(void) unsigned int level = atomic_fetch_and_add_u(&thread_levels, 1); if (level == 0) { MEM_set_lock_callback(BLI_lock_malloc_thread, BLI_unlock_malloc_thread); - /* There is a little chance that two threads will need to access to a - * scheduler which was not yet created from main thread. which could - * cause scheduler created multiple times. - */ - BLI_task_scheduler_get(); } } diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index ac1d315434d..418750a8df2 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -118,7 +118,7 @@ #include "BKE_constraint.h" #include "BKE_curve.h" #include "BKE_effect.h" -#include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_fluid.h" #include "BKE_global.h" // for G #include "BKE_gpencil_modifier.h" @@ -9715,7 +9715,9 @@ static bool read_libblock_undo_restore( /* Do not add LIB_TAG_NEW here, this should not be needed/used in undo case anyway (as * this is only for do_version-like code), but for sake of consistency, and also because * it will tell us which ID is re-used from old Main, and which one is actually new. */ - const int id_tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_UNDO_OLD_ID_REUSED; + /* Also do not add LIB_TAG_NEED_LINK, those IDs will never be re-liblinked, hence that tag will + * never be cleared, leading to critical issue in link/appemd code. */ + const int id_tag = tag | LIB_TAG_UNDO_OLD_ID_REUSED; read_libblock_undo_restore_identical(fd, main, id, id_old, id_tag); /* Insert into library map for lookup by newly read datablocks (with pointer value bhead->old). @@ -10195,6 +10197,15 @@ static void lib_link_all(FileData *fd, Main *bmain) * 'permanently' in our data structures... */ BKE_main_collections_parent_relations_rebuild(bmain); } + +#ifndef NDEBUG + /* Double check we do not have any 'need link' tag remaining, this should never be the case once + * this function has run. */ + FOREACH_MAIN_ID_BEGIN (bmain, id) { + BLI_assert((id->tag & LIB_TAG_NEED_LINK) == 0); + } + FOREACH_MAIN_ID_END; +#endif } /** \} */ diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 98d2acbd938..989efa9a1db 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -55,7 +55,7 @@ #include "BKE_anim_data.h" #include "BKE_animsys.h" #include "BKE_colortools.h" -#include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_main.h" #include "BKE_mask.h" #include "BKE_modifier.h" diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index d246eadb381..1879ab10fef 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -75,6 +75,7 @@ #include "BKE_curveprofile.h" #include "BKE_customdata.h" #include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_freestyle.h" #include "BKE_global.h" #include "BKE_gpencil.h" diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 9adcceb1b6b..3015a0e1b8a 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -157,6 +157,7 @@ #include "BKE_constraint.h" #include "BKE_curve.h" #include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_global.h" // for G #include "BKE_gpencil_modifier.h" #include "BKE_idtype.h" diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h index c94a8876ab0..d735d3b89bc 100644 --- a/source/blender/depsgraph/DEG_depsgraph.h +++ b/source/blender/depsgraph/DEG_depsgraph.h @@ -184,7 +184,7 @@ void DEG_editors_set_update_cb(DEG_EditorUpdateIDCb id_func, DEG_EditorUpdateSce /* Evaluation ----------------------------------- */ -bool DEG_is_evaluating(struct Depsgraph *depsgraph); +bool DEG_is_evaluating(const struct Depsgraph *depsgraph); bool DEG_is_active(const struct Depsgraph *depsgraph); void DEG_make_active(struct Depsgraph *depsgraph); diff --git a/source/blender/depsgraph/DEG_depsgraph_query.h b/source/blender/depsgraph/DEG_depsgraph_query.h index 26b46376a0a..3d570536223 100644 --- a/source/blender/depsgraph/DEG_depsgraph_query.h +++ b/source/blender/depsgraph/DEG_depsgraph_query.h @@ -111,14 +111,14 @@ struct ID *DEG_get_original_id(struct ID *id); * * Original IDs are considered all the IDs which are not covered by copy-on-write system and are * not out-of-main localized data-blocks. */ -bool DEG_is_original_id(struct ID *id); -bool DEG_is_original_object(struct Object *object); +bool DEG_is_original_id(const struct ID *id); +bool DEG_is_original_object(const struct Object *object); /* Opposite of the above. * * If the data-block is not original it must be evaluated, and vice versa. */ -bool DEG_is_evaluated_id(struct ID *id); -bool DEG_is_evaluated_object(struct Object *object); +bool DEG_is_evaluated_id(const struct ID *id); +bool DEG_is_evaluated_object(const struct Object *object); /* Check whether depsgraph os fully evaluated. This includes the following checks: * - Relations are up-to-date. diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc index d880618753c..82f3ea7d182 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -31,7 +31,6 @@ #include "DNA_layer_types.h" #include "DNA_object_types.h" -#include "BLI_ghash.h" #include "BLI_stack.h" #include "BLI_utildefines.h" @@ -157,10 +156,9 @@ void deg_graph_build_flush_visibility(Depsgraph *graph) BLI_Stack *stack = BLI_stack_new(sizeof(OperationNode *), "DEG flush layers stack"); for (IDNode *id_node : graph->id_nodes) { - GHASH_FOREACH_BEGIN (ComponentNode *, comp_node, id_node->components) { + for (ComponentNode *comp_node : id_node->components.values()) { comp_node->affects_directly_visible |= id_node->is_directly_visible; } - GHASH_FOREACH_END(); } for (OperationNode *op_node : graph->operations) { op_node->custom_flags = 0; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 25037fbd1b5..5ae6201227b 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -75,9 +75,10 @@ extern "C" { #include "BKE_constraint.h" #include "BKE_curve.h" #include "BKE_effect.h" -#include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_gpencil.h" #include "BKE_gpencil_modifier.h" +#include "BKE_idprop.h" #include "BKE_idtype.h" #include "BKE_image.h" #include "BKE_key.h" @@ -121,20 +122,6 @@ extern "C" { namespace DEG { -namespace { - -void free_copy_on_write_datablock(void *id_info_v) -{ - DepsgraphNodeBuilder::IDInfo *id_info = (DepsgraphNodeBuilder::IDInfo *)id_info_v; - if (id_info->id_cow != nullptr) { - deg_free_copy_on_write_datablock(id_info->id_cow); - MEM_freeN(id_info->id_cow); - } - MEM_freeN(id_info); -} - -} /* namespace */ - /* ************ */ /* Node Builder */ @@ -148,15 +135,18 @@ DepsgraphNodeBuilder::DepsgraphNodeBuilder(Main *bmain, view_layer_(nullptr), view_layer_index_(-1), collection_(nullptr), - is_parent_collection_visible_(true), - id_info_hash_(nullptr) + is_parent_collection_visible_(true) { } DepsgraphNodeBuilder::~DepsgraphNodeBuilder() { - if (id_info_hash_ != nullptr) { - BLI_ghash_free(id_info_hash_, nullptr, free_copy_on_write_datablock); + for (IDInfo *id_info : id_info_hash_.values()) { + if (id_info->id_cow != nullptr) { + deg_free_copy_on_write_datablock(id_info->id_cow); + MEM_freeN(id_info->id_cow); + } + MEM_freeN(id_info); } } @@ -167,7 +157,7 @@ IDNode *DepsgraphNodeBuilder::add_id_node(ID *id) IDComponentsMask previously_visible_components_mask = 0; uint32_t previous_eval_flags = 0; DEGCustomDataMeshMasks previous_customdata_masks; - IDInfo *id_info = (IDInfo *)BLI_ghash_lookup(id_info_hash_, id); + IDInfo *id_info = id_info_hash_.lookup_default(id, nullptr); if (id_info != nullptr) { id_cow = id_info->id_cow; previously_visible_components_mask = id_info->previously_visible_components_mask; @@ -183,7 +173,7 @@ IDNode *DepsgraphNodeBuilder::add_id_node(ID *id) /* Currently all ID nodes are supposed to have copy-on-write logic. * * NOTE: Zero number of components indicates that ID node was just created. */ - if (BLI_ghash_len(id_node->components) == 0) { + if (id_node->components.is_empty()) { ComponentNode *comp_cow = id_node->add_component(NodeType::COPY_ON_WRITE); OperationNode *op_cow = comp_cow->add_operation( function_bind(deg_evaluate_copy_on_write, _1, id_node), @@ -321,7 +311,6 @@ void DepsgraphNodeBuilder::begin_build() { /* Store existing copy-on-write versions of datablock, so we can re-use * them for new ID nodes. */ - id_info_hash_ = BLI_ghash_ptr_new("Depsgraph id hash"); for (IDNode *id_node : graph_->id_nodes) { /* It is possible that the ID does not need to have CoW version in which case id_cow is the * same as id_orig. Additionally, such ID might have been removed, which makes the check @@ -345,11 +334,11 @@ void DepsgraphNodeBuilder::begin_build() id_info->previously_visible_components_mask = id_node->visible_components_mask; id_info->previous_eval_flags = id_node->eval_flags; id_info->previous_customdata_masks = id_node->customdata_masks; - BLI_ghash_insert(id_info_hash_, id_node->id_orig, id_info); + id_info_hash_.add_new(id_node->id_orig, id_info); id_node->id_cow = nullptr; } - GSET_FOREACH_BEGIN (OperationNode *, op_node, graph_->entry_tags) { + for (OperationNode *op_node : graph_->entry_tags) { ComponentNode *comp_node = op_node->owner; IDNode *id_node = comp_node->owner; @@ -361,12 +350,11 @@ void DepsgraphNodeBuilder::begin_build() entry_tag.name_tag = op_node->name_tag; saved_entry_tags_.push_back(entry_tag); } - GSET_FOREACH_END(); /* Make sure graph has no nodes left from previous state. */ graph_->clear_all_nodes(); graph_->operations.clear(); - BLI_gset_clear(graph_->entry_tags, nullptr); + graph_->entry_tags.clear(); } void DepsgraphNodeBuilder::end_build() @@ -493,6 +481,18 @@ void DepsgraphNodeBuilder::build_id(ID *id) } } +static void build_idproperties_callback(IDProperty *id_property, void *user_data) +{ + DepsgraphNodeBuilder *builder = reinterpret_cast<DepsgraphNodeBuilder *>(user_data); + BLI_assert(id_property->type == IDP_ID); + builder->build_id(reinterpret_cast<ID *>(id_property->data.pointer)); +} + +void DepsgraphNodeBuilder::build_idproperties(IDProperty *id_property) +{ + IDP_foreach_property(id_property, IDP_TYPE_FILTER_ID, build_idproperties_callback, this); +} + void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collection, Collection *collection) { @@ -521,6 +521,8 @@ void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collecti /* Collection itself. */ id_node = add_id_node(&collection->id); id_node->is_directly_visible = is_collection_visible; + + build_idproperties(collection->id.properties); } if (from_layer_collection != nullptr) { /* If we came from layer collection we don't go deeper, view layer @@ -633,6 +635,7 @@ void DepsgraphNodeBuilder::build_object(int base_index, /* Parameters, used by both drivers/animation and also to inform dependency * from object's data. */ build_parameters(&object->id); + build_idproperties(object->id.properties); /* Build animation data, * * Do it now because it's possible object data will affect @@ -947,6 +950,7 @@ void DepsgraphNodeBuilder::build_action(bAction *action) if (built_map_.checkIsBuiltAndTag(action)) { return; } + build_idproperties(action->id.properties); add_operation_node(&action->id, NodeType::ANIMATION, OperationCode::ANIMATION_EVAL); } @@ -1052,6 +1056,7 @@ void DepsgraphNodeBuilder::build_world(World *world) NodeType::SHADING, OperationCode::WORLD_UPDATE, function_bind(BKE_world_eval, _1, world_cow)); + build_idproperties(world->id.properties); /* Animation. */ build_animdata(&world->id); build_parameters(&world->id); @@ -1234,6 +1239,7 @@ void DepsgraphNodeBuilder::build_shapekeys(Key *key) if (built_map_.checkIsBuiltAndTag(key)) { return; } + build_idproperties(key->id.properties); build_animdata(&key->id); build_parameters(&key->id); /* This is an exit operation for the entire key datablock, is what is used @@ -1288,6 +1294,7 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata, bool /* Make sure we've got an ID node before requesting CoW pointer. */ (void)add_id_node((ID *)obdata); ID *obdata_cow = get_cow_id(obdata); + build_idproperties(obdata->properties); /* Animation. */ build_animdata(obdata); /* ShapeKeys */ @@ -1393,10 +1400,20 @@ void DepsgraphNodeBuilder::build_armature(bArmature *armature) if (built_map_.checkIsBuiltAndTag(armature)) { return; } + build_idproperties(armature->id.properties); build_animdata(&armature->id); build_parameters(&armature->id); /* Make sure pose is up-to-date with armature updates. */ add_operation_node(&armature->id, NodeType::ARMATURE, OperationCode::ARMATURE_EVAL); + build_armature_bones(&armature->bonebase); +} + +void DepsgraphNodeBuilder::build_armature_bones(ListBase *bones) +{ + LISTBASE_FOREACH (Bone *, bone, bones) { + build_idproperties(bone->prop); + build_armature_bones(&bone->childbase); + } } void DepsgraphNodeBuilder::build_camera(Camera *camera) @@ -1404,6 +1421,7 @@ void DepsgraphNodeBuilder::build_camera(Camera *camera) if (built_map_.checkIsBuiltAndTag(camera)) { return; } + build_idproperties(camera->id.properties); build_animdata(&camera->id); build_parameters(&camera->id); if (camera->dof.focus_object != nullptr) { @@ -1416,6 +1434,7 @@ void DepsgraphNodeBuilder::build_light(Light *lamp) if (built_map_.checkIsBuiltAndTag(lamp)) { return; } + build_idproperties(lamp->id.properties); build_animdata(&lamp->id); build_parameters(&lamp->id); /* light's nodetree */ @@ -1435,6 +1454,7 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) bNodeTree *ntree_cow = get_cow_datablock(ntree); /* General parameters. */ build_parameters(&ntree->id); + build_idproperties(ntree->id.properties); /* Animation, */ build_animdata(&ntree->id); /* Shading update. */ @@ -1447,6 +1467,14 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) function_bind(BKE_nodetree_shading_params_eval, _1, ntree_cow, ntree)); /* nodetree's nodes... */ LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) { + build_idproperties(bnode->prop); + LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->inputs) { + build_idproperties(socket->prop); + } + LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->outputs) { + build_idproperties(socket->prop); + } + ID *id = bnode->id; if (id == nullptr) { continue; @@ -1495,6 +1523,13 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) } } + LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs) { + build_idproperties(socket->prop); + } + LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs) { + build_idproperties(socket->prop); + } + // TODO: link from nodetree to owner_component? } @@ -1512,6 +1547,7 @@ void DepsgraphNodeBuilder::build_material(Material *material) NodeType::SHADING, OperationCode::MATERIAL_UPDATE, function_bind(BKE_material_eval, _1, material_cow)); + build_idproperties(material->id.properties); /* Material animation. */ build_animdata(&material->id); build_parameters(&material->id); @@ -1536,6 +1572,7 @@ void DepsgraphNodeBuilder::build_texture(Tex *texture) return; } /* Texture itself. */ + build_idproperties(texture->id.properties); build_animdata(&texture->id); build_parameters(&texture->id); /* Texture's nodetree. */ @@ -1556,6 +1593,7 @@ void DepsgraphNodeBuilder::build_image(Image *image) return; } build_parameters(&image->id); + build_idproperties(image->id.properties); add_operation_node( &image->id, NodeType::GENERIC_DATABLOCK, OperationCode::GENERIC_DATABLOCK_UPDATE); } @@ -1584,6 +1622,7 @@ void DepsgraphNodeBuilder::build_cachefile(CacheFile *cache_file) ID *cache_file_id = &cache_file->id; add_id_node(cache_file_id); CacheFile *cache_file_cow = get_cow_datablock(cache_file); + build_idproperties(cache_file_id->properties); /* Animation, */ build_animdata(cache_file_id); build_parameters(cache_file_id); @@ -1601,6 +1640,7 @@ void DepsgraphNodeBuilder::build_mask(Mask *mask) } ID *mask_id = &mask->id; Mask *mask_cow = (Mask *)ensure_cow_id(mask_id); + build_idproperties(mask->id.properties); /* F-Curve based animation. */ build_animdata(mask_id); build_parameters(mask_id); @@ -1637,6 +1677,7 @@ void DepsgraphNodeBuilder::build_freestyle_linestyle(FreestyleLineStyle *linesty ID *linestyle_id = &linestyle->id; build_parameters(linestyle_id); + build_idproperties(linestyle->id.properties); build_animdata(linestyle_id); build_nodetree(linestyle->nodetree); } @@ -1648,6 +1689,7 @@ void DepsgraphNodeBuilder::build_movieclip(MovieClip *clip) } ID *clip_id = &clip->id; MovieClip *clip_cow = (MovieClip *)ensure_cow_id(clip_id); + build_idproperties(clip_id->properties); /* Animation. */ build_animdata(clip_id); build_parameters(clip_id); @@ -1670,6 +1712,7 @@ void DepsgraphNodeBuilder::build_lightprobe(LightProbe *probe) } /* Placeholder so we can add relations and tag ID node for update. */ add_operation_node(&probe->id, NodeType::PARAMETERS, OperationCode::LIGHT_PROBE_EVAL); + build_idproperties(probe->id.properties); build_animdata(&probe->id); build_parameters(&probe->id); } @@ -1681,6 +1724,7 @@ void DepsgraphNodeBuilder::build_speaker(Speaker *speaker) } /* Placeholder so we can add relations and tag ID node for update. */ add_operation_node(&speaker->id, NodeType::AUDIO, OperationCode::SPEAKER_EVAL); + build_idproperties(speaker->id.properties); build_animdata(&speaker->id); build_parameters(&speaker->id); if (speaker->sound != nullptr) { @@ -1699,6 +1743,7 @@ void DepsgraphNodeBuilder::build_sound(bSound *sound) NodeType::AUDIO, OperationCode::SOUND_EVAL, function_bind(BKE_sound_evaluate, _1, bmain_, sound_cow)); + build_idproperties(sound->id.properties); build_animdata(&sound->id); build_parameters(&sound->id); } @@ -1732,6 +1777,7 @@ void DepsgraphNodeBuilder::build_scene_sequencer(Scene *scene) /* Make sure data for sequences is in the graph. */ Sequence *seq; SEQ_BEGIN (scene->ed, seq) { + build_idproperties(seq->prop); if (seq->sound != nullptr) { build_sound(seq->sound); } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 15c1a42574f..5cd7f7449a2 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -37,8 +37,8 @@ struct Collection; struct FCurve; struct FreestyleLineSet; struct FreestyleLineStyle; -struct GHash; struct ID; +struct IDProperty; struct Image; struct Key; struct LayerCollection; @@ -150,6 +150,8 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { virtual void build_id(ID *id); + virtual void build_idproperties(IDProperty *id_property); + virtual void build_scene_render(Scene *scene, ViewLayer *view_layer); virtual void build_scene_parameters(Scene *scene); virtual void build_scene_compositor(Scene *scene); @@ -200,6 +202,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { virtual void build_rig(Object *object, bool is_object_visible); virtual void build_proxy_rig(Object *object); virtual void build_armature(bArmature *armature); + virtual void build_armature_bones(ListBase *bones); virtual void build_shapekeys(Key *key); virtual void build_camera(Camera *camera); virtual void build_light(Light *lamp); @@ -277,7 +280,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { bool is_parent_collection_visible_; /* Indexed by original ID, values are IDInfo. */ - GHash *id_info_hash_; + Map<const ID *, IDInfo *> id_info_hash_; /* Set of IDs which were already build. Makes it easier to keep track of * what was already built and what was not. */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc index 5383e26bbd4..7a05ae36ea4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc @@ -244,6 +244,7 @@ void DepsgraphNodeBuilder::build_rig(Object *object, bool is_object_visible) /* Custom properties. */ if (pchan->prop != nullptr) { + build_idproperties(pchan->prop); add_operation_node( &object->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, nullptr, pchan->name); } @@ -323,6 +324,7 @@ void DepsgraphNodeBuilder::build_proxy_rig(Object *object) /* Custom properties. */ if (pchan->prop != nullptr) { + build_idproperties(pchan->prop); add_operation_node( &object->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, nullptr, pchan->name); } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc index 1edf9826208..60e843f9124 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc @@ -57,6 +57,7 @@ void DepsgraphNodeBuilder::build_scene_parameters(Scene *scene) return; } build_parameters(&scene->id); + build_idproperties(scene->id.properties); add_operation_node(&scene->id, NodeType::PARAMETERS, OperationCode::SCENE_EVAL); /* NOTE: This is a bit overkill and can potentially pull a bit too much into the graph, but: * diff --git a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc index ecacfcf7ee9..a0179181866 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.cc @@ -26,104 +26,55 @@ #include <stdio.h> #include <string.h> -#include "BLI_ghash.h" #include "BLI_utildefines.h" namespace DEG { -static void free_rootpchanmap_valueset(void *val) -{ - /* Just need to free the set itself - the names stored are all references. */ - GSet *values = (GSet *)val; - BLI_gset_free(values, nullptr); -} - RootPChanMap::RootPChanMap() { - /* Just create empty map. */ - map_ = BLI_ghash_str_new("RootPChanMap"); } RootPChanMap::~RootPChanMap() { - /* Free the map, and all the value sets. */ - BLI_ghash_free(map_, nullptr, free_rootpchanmap_valueset); } /* Debug contents of map */ void RootPChanMap::print_debug() { - GHashIterator it1; - GSetIterator it2; - - printf("Root PChan Map:\n"); - GHASH_ITER (it1, map_) { - const char *item = (const char *)BLI_ghashIterator_getKey(&it1); - GSet *values = (GSet *)BLI_ghashIterator_getValue(&it1); - - printf(" %s : { ", item); - GSET_ITER (it2, values) { - const char *val = (const char *)BLI_gsetIterator_getKey(&it2); - printf("%s, ", val); + map_.foreach_item([](StringRefNull key, const Set<StringRefNull> &values) { + printf(" %s : { ", key.data()); + for (StringRefNull val : values) { + printf("%s, ", val.data()); } printf("}\n"); - } + }); } /* Add a mapping. */ void RootPChanMap::add_bone(const char *bone, const char *root) { - if (BLI_ghash_haskey(map_, bone)) { - /* Add new entry, but only add the root if it doesn't already - * exist in there. */ - GSet *values = (GSet *)BLI_ghash_lookup(map_, bone); - BLI_gset_add(values, (void *)root); - } - else { - /* Create new set and mapping. */ - GSet *values = BLI_gset_new( - BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "RootPChanMap Value Set"); - BLI_ghash_insert(map_, (void *)bone, (void *)values); - - /* Add new entry now. */ - BLI_gset_insert(values, (void *)root); - } + map_.lookup_or_add_default(bone).add(root); } /* Check if there's a common root bone between two bones. */ bool RootPChanMap::has_common_root(const char *bone1, const char *bone2) const { - /* Ensure that both are in the map... */ - if (BLI_ghash_haskey(map_, bone1) == false) { + const Set<StringRefNull> *bone1_roots = map_.lookup_ptr(bone1); + const Set<StringRefNull> *bone2_roots = map_.lookup_ptr(bone2); + + if (bone1_roots == nullptr) { // fprintf("RootPChanMap: bone1 '%s' not found (%s => %s)\n", bone1, bone1, bone2); // print_debug(); return false; } - if (BLI_ghash_haskey(map_, bone2) == false) { + if (bone2_roots == nullptr) { // fprintf("RootPChanMap: bone2 '%s' not found (%s => %s)\n", bone2, bone1, bone2); // print_debug(); return false; } - GSet *bone1_roots = (GSet *)BLI_ghash_lookup(map_, (void *)bone1); - GSet *bone2_roots = (GSet *)BLI_ghash_lookup(map_, (void *)bone2); - - GSetIterator it1, it2; - GSET_ITER (it1, bone1_roots) { - GSET_ITER (it2, bone2_roots) { - const char *v1 = (const char *)BLI_gsetIterator_getKey(&it1); - const char *v2 = (const char *)BLI_gsetIterator_getKey(&it2); - - if (strcmp(v1, v2) == 0) { - // fprintf("RootPchanMap: %s in common for %s => %s\n", v1, bone1, bone2); - return true; - } - } - } - - // fprintf("RootPChanMap: No common root found (%s => %s)\n", bone1, bone2); - return false; + return Set<StringRefNull>::Intersects(*bone1_roots, *bone2_roots); } } // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h index 1442f547b08..c3c90e5aae4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h @@ -23,7 +23,7 @@ #pragma once -struct GHash; +#include "intern/depsgraph_type.h" namespace DEG { @@ -42,13 +42,11 @@ struct RootPChanMap { bool has_common_root(const char *bone1, const char *bone2) const; protected: - /* The actual map: - * - Keys are "strings" (const char *) - not dynamically allocated. - * - Values are "sets" (const char *) - not dynamically allocated. - * - * We don't use the C++ maps here, as it's more convenient to use - * Blender's GHash and be able to compare by-value instead of by-ref. */ - struct GHash *map_; + /** + * The strings are only referenced by this map. Users of RootPChanMap have to make sure that the + * life-time of the strings is long enough. + */ + Map<StringRefNull, Set<StringRefNull>> map_; }; } // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 11ec205eaf5..1ecae6b5d8b 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -76,8 +76,9 @@ extern "C" { #include "BKE_constraint.h" #include "BKE_curve.h" #include "BKE_effect.h" -#include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_gpencil_modifier.h" +#include "BKE_idprop.h" #include "BKE_image.h" #include "BKE_key.h" #include "BKE_layer.h" @@ -566,6 +567,18 @@ void DepsgraphRelationBuilder::build_id(ID *id) } } +static void build_idproperties_callback(IDProperty *id_property, void *user_data) +{ + DepsgraphRelationBuilder *builder = reinterpret_cast<DepsgraphRelationBuilder *>(user_data); + BLI_assert(id_property->type == IDP_ID); + builder->build_id(reinterpret_cast<ID *>(id_property->data.pointer)); +} + +void DepsgraphRelationBuilder::build_idproperties(IDProperty *id_property) +{ + IDP_foreach_property(id_property, IDP_TYPE_FILTER_ID, build_idproperties_callback, this); +} + void DepsgraphRelationBuilder::build_collection(LayerCollection *from_layer_collection, Object *object, Collection *collection) @@ -579,6 +592,7 @@ void DepsgraphRelationBuilder::build_collection(LayerCollection *from_layer_coll * recurses into all the nested objects and collections. */ return; } + build_idproperties(collection->id.properties); const bool group_done = built_map_.checkIsBuiltAndTag(collection); OperationKey object_transform_final_key(object != nullptr ? &object->id : nullptr, NodeType::TRANSFORM, @@ -692,6 +706,7 @@ void DepsgraphRelationBuilder::build_object(Base *base, Object *object) final_transform_key, "Simulation -> Final Transform"); } + build_idproperties(object->id.properties); /* Animation data */ build_animdata(&object->id); /* Object data. */ @@ -1368,6 +1383,7 @@ void DepsgraphRelationBuilder::build_action(bAction *action) if (built_map_.checkIsBuiltAndTag(action)) { return; } + build_idproperties(action->id.properties); if (!BLI_listbase_is_empty(&action->curves)) { TimeSourceKey time_src_key; ComponentKey animation_key(&action->id, NodeType::ANIMATION); @@ -1613,6 +1629,7 @@ void DepsgraphRelationBuilder::build_world(World *world) if (built_map_.checkIsBuiltAndTag(world)) { return; } + build_idproperties(world->id.properties); /* animation */ build_animdata(&world->id); build_parameters(&world->id); @@ -1891,6 +1908,7 @@ void DepsgraphRelationBuilder::build_shapekeys(Key *key) if (built_map_.checkIsBuiltAndTag(key)) { return; } + build_idproperties(key->id.properties); /* Attach animdata to geometry. */ build_animdata(&key->id); build_parameters(&key->id); @@ -2070,6 +2088,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata) if (built_map_.checkIsBuiltAndTag(obdata)) { return; } + build_idproperties(obdata->properties); /* Animation. */ build_animdata(obdata); build_parameters(obdata); @@ -2182,8 +2201,18 @@ void DepsgraphRelationBuilder::build_armature(bArmature *armature) if (built_map_.checkIsBuiltAndTag(armature)) { return; } + build_idproperties(armature->id.properties); build_animdata(&armature->id); build_parameters(&armature->id); + build_armature_bones(&armature->bonebase); +} + +void DepsgraphRelationBuilder::build_armature_bones(ListBase *bones) +{ + LISTBASE_FOREACH (Bone *, bone, bones) { + build_idproperties(bone->prop); + build_armature_bones(&bone->childbase); + } } void DepsgraphRelationBuilder::build_camera(Camera *camera) @@ -2191,6 +2220,7 @@ void DepsgraphRelationBuilder::build_camera(Camera *camera) if (built_map_.checkIsBuiltAndTag(camera)) { return; } + build_idproperties(camera->id.properties); build_animdata(&camera->id); build_parameters(&camera->id); if (camera->dof.focus_object != nullptr) { @@ -2207,6 +2237,7 @@ void DepsgraphRelationBuilder::build_light(Light *lamp) if (built_map_.checkIsBuiltAndTag(lamp)) { return; } + build_idproperties(lamp->id.properties); build_animdata(&lamp->id); build_parameters(&lamp->id); /* light's nodetree */ @@ -2227,11 +2258,20 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) if (built_map_.checkIsBuiltAndTag(ntree)) { return; } + build_idproperties(ntree->id.properties); build_animdata(&ntree->id); build_parameters(&ntree->id); ComponentKey shading_key(&ntree->id, NodeType::SHADING); /* nodetree's nodes... */ LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) { + build_idproperties(bnode->prop); + LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->inputs) { + build_idproperties(socket->prop); + } + LISTBASE_FOREACH (bNodeSocket *, socket, &bnode->outputs) { + build_idproperties(socket->prop); + } + ID *id = bnode->id; if (id == nullptr) { continue; @@ -2296,6 +2336,13 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) } } + LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs) { + build_idproperties(socket->prop); + } + LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs) { + build_idproperties(socket->prop); + } + OperationKey shading_update_key(&ntree->id, NodeType::SHADING, OperationCode::MATERIAL_UPDATE); OperationKey shading_parameters_key( &ntree->id, NodeType::SHADING_PARAMETERS, OperationCode::MATERIAL_UPDATE); @@ -2315,6 +2362,7 @@ void DepsgraphRelationBuilder::build_material(Material *material) if (built_map_.checkIsBuiltAndTag(material)) { return; } + build_idproperties(material->id.properties); /* animation */ build_animdata(&material->id); build_parameters(&material->id); @@ -2347,6 +2395,7 @@ void DepsgraphRelationBuilder::build_texture(Tex *texture) } /* texture itself */ ComponentKey texture_key(&texture->id, NodeType::GENERIC_DATABLOCK); + build_idproperties(texture->id.properties); build_animdata(&texture->id); build_parameters(&texture->id); @@ -2375,6 +2424,7 @@ void DepsgraphRelationBuilder::build_image(Image *image) if (built_map_.checkIsBuiltAndTag(image)) { return; } + build_idproperties(image->id.properties); build_parameters(&image->id); } @@ -2395,6 +2445,7 @@ void DepsgraphRelationBuilder::build_cachefile(CacheFile *cache_file) if (built_map_.checkIsBuiltAndTag(cache_file)) { return; } + build_idproperties(cache_file->id.properties); /* Animation. */ build_animdata(&cache_file->id); build_parameters(&cache_file->id); @@ -2424,6 +2475,7 @@ void DepsgraphRelationBuilder::build_mask(Mask *mask) return; } ID *mask_id = &mask->id; + build_idproperties(mask_id->properties); /* F-Curve animation. */ build_animdata(mask_id); build_parameters(mask_id); @@ -2462,6 +2514,7 @@ void DepsgraphRelationBuilder::build_freestyle_linestyle(FreestyleLineStyle *lin ID *linestyle_id = &linestyle->id; build_parameters(linestyle_id); + build_idproperties(linestyle_id->properties); build_animdata(linestyle_id); build_nodetree(linestyle->nodetree); } @@ -2472,6 +2525,7 @@ void DepsgraphRelationBuilder::build_movieclip(MovieClip *clip) return; } /* Animation. */ + build_idproperties(clip->id.properties); build_animdata(&clip->id); build_parameters(&clip->id); } @@ -2481,6 +2535,7 @@ void DepsgraphRelationBuilder::build_lightprobe(LightProbe *probe) if (built_map_.checkIsBuiltAndTag(probe)) { return; } + build_idproperties(probe->id.properties); build_animdata(&probe->id); build_parameters(&probe->id); } @@ -2490,6 +2545,7 @@ void DepsgraphRelationBuilder::build_speaker(Speaker *speaker) if (built_map_.checkIsBuiltAndTag(speaker)) { return; } + build_idproperties(speaker->id.properties); build_animdata(&speaker->id); build_parameters(&speaker->id); if (speaker->sound != nullptr) { @@ -2505,6 +2561,7 @@ void DepsgraphRelationBuilder::build_sound(bSound *sound) if (built_map_.checkIsBuiltAndTag(sound)) { return; } + build_idproperties(sound->id.properties); build_animdata(&sound->id); build_parameters(&sound->id); } @@ -2535,6 +2592,7 @@ void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene) Sequence *seq; bool has_audio_strips = false; SEQ_BEGIN (scene->ed, seq) { + build_idproperties(seq->prop); if (seq->sound != nullptr) { build_sound(seq->sound); ComponentKey sound_key(&seq->sound->id, NodeType::AUDIO); @@ -2637,7 +2695,7 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDNode *id_node) Node *node_cow = find_node(copy_on_write_key); OperationNode *op_cow = node_cow->get_exit_operation(); /* Plug any other components to this one. */ - GHASH_FOREACH_BEGIN (ComponentNode *, comp_node, id_node->components) { + for (ComponentNode *comp_node : id_node->components.values()) { if (comp_node->type == NodeType::COPY_ON_WRITE) { /* Copy-on-write component never depends on itself. */ continue; @@ -2684,7 +2742,7 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDNode *id_node) if (op_node == op_entry) { continue; } - if (op_node->inlinks.size() == 0) { + if (op_node->inlinks.is_empty()) { Relation *rel = graph_->add_new_relation(op_cow, op_node, "CoW Dependency"); rel->flag |= rel_flag; } @@ -2714,7 +2772,6 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDNode *id_node) * evaluation step needs geometry, it will have transitive dependency * to Mesh copy-on-write already. */ } - GHASH_FOREACH_END(); /* TODO(sergey): This solves crash for now, but causes too many * updates potentially. */ if (GS(id_orig->name) == ID_OB) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index caec7cc1496..aa6d8ababd3 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -54,6 +54,7 @@ struct FCurve; struct FreestyleLineSet; struct FreestyleLineStyle; struct ID; +struct IDProperty; struct Image; struct Key; struct LayerCollection; @@ -196,6 +197,8 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { virtual void build_id(ID *id); + virtual void build_idproperties(IDProperty *id_property); + virtual void build_scene_render(Scene *scene, ViewLayer *view_layer); virtual void build_scene_parameters(Scene *scene); virtual void build_scene_compositor(Scene *scene); @@ -267,6 +270,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { virtual void build_proxy_rig(Object *object); virtual void build_shapekeys(Key *key); virtual void build_armature(bArmature *armature); + virtual void build_armature_bones(ListBase *bones); virtual void build_camera(Camera *camera); virtual void build_light(Light *lamp); virtual void build_nodetree(bNodeTree *ntree); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc index 7fe7695f195..b75252aa325 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc @@ -356,6 +356,7 @@ void DepsgraphRelationBuilder::build_rig(Object *object) } /* Links between operations for each bone. */ LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { + build_idproperties(pchan->prop); OperationKey bone_local_key( &object->id, NodeType::BONE, pchan->name, OperationCode::BONE_LOCAL); OperationKey bone_pose_key( @@ -464,6 +465,7 @@ void DepsgraphRelationBuilder::build_proxy_rig(Object *object) OperationKey pose_done_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_DONE); OperationKey pose_cleanup_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_CLEANUP); LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { + build_idproperties(pchan->prop); OperationKey bone_local_key( &object->id, NodeType::BONE, pchan->name, OperationCode::BONE_LOCAL); OperationKey bone_ready_key( diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc index 08191bcecc8..410df839875 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc @@ -52,6 +52,7 @@ void DepsgraphRelationBuilder::build_scene_parameters(Scene *scene) if (built_map_.checkIsBuiltAndTag(scene, BuilderMap::TAG_PARAMETERS)) { return; } + build_idproperties(scene->id.properties); build_parameters(&scene->id); OperationKey parameters_eval_key( &scene->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EXIT); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.cc b/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.cc index 2ce1f1f1c1d..c6545362bb1 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.cc @@ -43,7 +43,7 @@ static inline bool is_unused_noop(OperationNode *op_node) if (op_node->flag & OperationFlag::DEPSOP_FLAG_PINNED) { return false; } - return op_node->is_noop() && op_node->outlinks.empty(); + return op_node->is_noop() && op_node->outlinks.is_empty(); } void deg_graph_remove_unused_noops(Depsgraph *graph) @@ -61,7 +61,7 @@ void deg_graph_remove_unused_noops(Depsgraph *graph) OperationNode *to_remove = queue.front(); queue.pop_front(); - while (!to_remove->inlinks.empty()) { + while (!to_remove->inlinks.is_empty()) { Relation *rel_in = to_remove->inlinks[0]; Node *dependency = rel_in->from; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc index 73f6730be03..a35b69df60d 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc @@ -27,7 +27,6 @@ #include "MEM_guardedalloc.h" -#include "BLI_ghash.h" #include "BLI_listbase.h" #include "BLI_utildefines.h" @@ -57,37 +56,33 @@ namespace DEG { class RNANodeQueryIDData { public: - explicit RNANodeQueryIDData(const ID *id) : id_(id), contraint_to_pchan_map_(nullptr) + explicit RNANodeQueryIDData(const ID *id) : id_(id), constraint_to_pchan_map_(nullptr) { } ~RNANodeQueryIDData() { - if (contraint_to_pchan_map_ != nullptr) { - BLI_ghash_free(contraint_to_pchan_map_, nullptr, nullptr); - } + delete constraint_to_pchan_map_; } const bPoseChannel *get_pchan_for_constraint(const bConstraint *constraint) { ensure_constraint_to_pchan_map(); - return static_cast<bPoseChannel *>(BLI_ghash_lookup(contraint_to_pchan_map_, constraint)); + return constraint_to_pchan_map_->lookup_default(constraint, nullptr); } void ensure_constraint_to_pchan_map() { - if (contraint_to_pchan_map_ != nullptr) { + if (constraint_to_pchan_map_ != nullptr) { return; } BLI_assert(GS(id_->name) == ID_OB); const Object *object = reinterpret_cast<const Object *>(id_); - contraint_to_pchan_map_ = BLI_ghash_ptr_new("id data pchan constraint map"); + constraint_to_pchan_map_ = new Map<const bConstraint *, const bPoseChannel *>(); if (object->pose != nullptr) { LISTBASE_FOREACH (const bPoseChannel *, pchan, &object->pose->chanbase) { LISTBASE_FOREACH (const bConstraint *, constraint, &pchan->constraints) { - BLI_ghash_insert(contraint_to_pchan_map_, - const_cast<bConstraint *>(constraint), - const_cast<bPoseChannel *>(pchan)); + constraint_to_pchan_map_->add_new(constraint, pchan); } } } @@ -99,7 +94,7 @@ class RNANodeQueryIDData { /* indexed by bConstraint*, returns pose channel which contains that * constraint. */ - GHash *contraint_to_pchan_map_; + Map<const bConstraint *, const bPoseChannel *> *constraint_to_pchan_map_; }; /* ***************************** Node Identifier **************************** */ @@ -121,26 +116,13 @@ bool RNANodeIdentifier::is_valid() const /* ********************************** Query ********************************* */ -namespace { - -void ghash_id_data_free_func(void *value) -{ - RNANodeQueryIDData *id_data = static_cast<RNANodeQueryIDData *>(value); - OBJECT_GUARDED_DELETE(id_data, RNANodeQueryIDData); -} - -} // namespace - RNANodeQuery::RNANodeQuery(Depsgraph *depsgraph, DepsgraphBuilder *builder) - : depsgraph_(depsgraph), - builder_(builder), - id_data_map_(BLI_ghash_ptr_new("rna node query id data hash")) + : depsgraph_(depsgraph), builder_(builder) { } RNANodeQuery::~RNANodeQuery() { - BLI_ghash_free(id_data_map_, nullptr, ghash_id_data_free_func); } Node *RNANodeQuery::find_node(const PointerRNA *ptr, @@ -384,12 +366,9 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, RNANodeQueryIDData *RNANodeQuery::ensure_id_data(const ID *id) { - RNANodeQueryIDData **id_data_ptr; - if (!BLI_ghash_ensure_p( - id_data_map_, const_cast<ID *>(id), reinterpret_cast<void ***>(&id_data_ptr))) { - *id_data_ptr = OBJECT_GUARDED_NEW(RNANodeQueryIDData, id); - } - return *id_data_ptr; + unique_ptr<RNANodeQueryIDData> &id_data = id_data_map_.lookup_or_add( + id, [&]() { return BLI::make_unique<RNANodeQueryIDData>(id); }); + return id_data.get(); } } // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.h b/source/blender/depsgraph/intern/builder/deg_builder_rna.h index 8a79d9abef9..bd806ce058a 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.h @@ -26,7 +26,6 @@ #include "intern/node/deg_node.h" #include "intern/node/deg_node_operation.h" -struct GHash; struct ID; struct PointerRNA; struct PropertyRNA; @@ -83,7 +82,7 @@ class RNANodeQuery { DepsgraphBuilder *builder_; /* Indexed by an ID, returns RNANodeQueryIDData associated with that ID. */ - GHash *id_data_map_; + Map<const ID *, unique_ptr<RNANodeQueryIDData>> id_data_map_; /* Construct identifier of the node which corresponds given configuration * of RNA property. */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc b/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc index 3f828ef5bb0..22ceac899b8 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_transitive.cc @@ -72,6 +72,8 @@ static void deg_graph_tag_paths_recursive(Node *node) void deg_graph_transitive_reduction(Depsgraph *graph) { int num_removed_relations = 0; + Vector<Relation *> relations_to_remove; + for (OperationNode *target : graph->operations) { /* Clear tags. */ for (OperationNode *node : graph->operations) { @@ -85,25 +87,24 @@ void deg_graph_transitive_reduction(Depsgraph *graph) deg_graph_tag_paths_recursive(rel->from); } /* Remove redundant paths to the target. */ - for (Node::Relations::const_iterator it_rel = target->inlinks.begin(); - it_rel != target->inlinks.end();) { - Relation *rel = *it_rel; + for (Relation *rel : target->inlinks) { if (rel->from->type == NodeType::TIMESOURCE) { /* HACK: time source nodes don't get "custom_flags" flag * set/cleared. */ /* TODO: there will be other types in future, so iterators above * need modifying. */ - ++it_rel; + continue; } else if (rel->from->custom_flags & OP_REACHABLE) { - rel->unlink(); - OBJECT_GUARDED_DELETE(rel, Relation); - num_removed_relations++; - } - else { - ++it_rel; + relations_to_remove.append(rel); } } + for (Relation *rel : relations_to_remove) { + rel->unlink(); + OBJECT_GUARDED_DELETE(rel, Relation); + } + num_removed_relations += relations_to_remove.size(); + relations_to_remove.clear(); } DEG_DEBUG_PRINTF((::Depsgraph *)graph, BUILD, "Removed %d relations\n", num_removed_relations); } diff --git a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc index 701419d24e1..08f373d486a 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc +++ b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc @@ -25,7 +25,6 @@ #include <cstdarg> -#include "BLI_ghash.h" #include "BLI_utildefines.h" extern "C" { @@ -407,15 +406,14 @@ static void deg_debug_graphviz_node(const DebugContext &ctx, const Node *node) switch (node->type) { case NodeType::ID_REF: { const IDNode *id_node = (const IDNode *)node; - if (BLI_ghash_len(id_node->components) == 0) { + if (id_node->components.is_empty()) { deg_debug_graphviz_node_single(ctx, node); } else { deg_debug_graphviz_node_cluster_begin(ctx, node); - GHASH_FOREACH_BEGIN (const ComponentNode *, comp, id_node->components) { + for (const ComponentNode *comp : id_node->components.values()) { deg_debug_graphviz_node(ctx, comp); } - GHASH_FOREACH_END(); deg_debug_graphviz_node_cluster_end(ctx); } break; @@ -473,7 +471,7 @@ static bool deg_debug_graphviz_is_cluster(const Node *node) switch (node->type) { case NodeType::ID_REF: { const IDNode *id_node = (const IDNode *)node; - return BLI_ghash_len(id_node->components) > 0; + return !id_node->components.is_empty(); } case NodeType::PARAMETERS: case NodeType::ANIMATION: @@ -569,12 +567,11 @@ static void deg_debug_graphviz_graph_nodes(const DebugContext &ctx, const Depsgr static void deg_debug_graphviz_graph_relations(const DebugContext &ctx, const Depsgraph *graph) { for (IDNode *id_node : graph->id_nodes) { - GHASH_FOREACH_BEGIN (ComponentNode *, comp_node, id_node->components) { + for (ComponentNode *comp_node : id_node->components.values()) { for (OperationNode *op_node : comp_node->operations) { deg_debug_graphviz_node_relations(ctx, op_node); } } - GHASH_FOREACH_END(); } TimeSourceNode *time_source = graph->find_time_source(); diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index 6d88782d68c..faf375155b5 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -31,7 +31,6 @@ #include "MEM_guardedalloc.h" #include "BLI_console.h" -#include "BLI_ghash.h" #include "BLI_hash.h" #include "BLI_utildefines.h" @@ -60,12 +59,6 @@ extern "C" { namespace DEG { -/* TODO(sergey): Find a better place for this. */ -template<typename T> static void remove_from_vector(vector<T> *vector, const T &value) -{ - vector->erase(std::remove(vector->begin(), vector->end(), value), vector->end()); -} - Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode) : time_source(nullptr), need_update(true), @@ -81,8 +74,6 @@ Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluati is_render_pipeline_depsgraph(false) { BLI_spin_init(&lock); - id_hash = BLI_ghash_ptr_new("Depsgraph id hash"); - entry_tags = BLI_gset_ptr_new("Depsgraph entry_tags"); memset(id_type_updated, 0, sizeof(id_type_updated)); memset(id_type_exist, 0, sizeof(id_type_exist)); memset(physics_relations, 0, sizeof(physics_relations)); @@ -91,8 +82,6 @@ Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluati Depsgraph::~Depsgraph() { clear_id_nodes(); - BLI_ghash_free(id_hash, nullptr, nullptr); - BLI_gset_free(entry_tags, nullptr); if (time_source != nullptr) { OBJECT_GUARDED_DELETE(time_source, TimeSourceNode); } @@ -117,7 +106,7 @@ TimeSourceNode *Depsgraph::find_time_source() const IDNode *Depsgraph::find_id_node(const ID *id) const { - return reinterpret_cast<IDNode *>(BLI_ghash_lookup(id_hash, id)); + return id_hash.lookup_default(id, nullptr); } IDNode *Depsgraph::add_id_node(ID *id, ID *id_cow_hint) @@ -132,7 +121,7 @@ IDNode *Depsgraph::add_id_node(ID *id, ID *id_cow_hint) * * NOTE: We address ID nodes by the original ID pointer they are * referencing to. */ - BLI_ghash_insert(id_hash, id, id_node); + id_hash.add_new(id, id_node); id_nodes.push_back(id_node); id_type_exist[BKE_idtype_idcode_to_index(GS(id->name))] = 1; @@ -170,7 +159,7 @@ void Depsgraph::clear_id_nodes() OBJECT_GUARDED_DELETE(id_node, IDNode); } /* Clear containers. */ - BLI_ghash_clear(id_hash, nullptr, nullptr); + id_hash.clear(); id_nodes.clear(); /* Clear physics relation caches. */ clear_physics_relations(this); @@ -233,7 +222,7 @@ void Depsgraph::add_entry_tag(OperationNode *node) * from. * NOTE: this is necessary since we have several thousand nodes to play * with. */ - BLI_gset_insert(entry_tags, node); + entry_tags.add(node); } void Depsgraph::clear_all_nodes() @@ -320,9 +309,9 @@ void DEG_graph_free(Depsgraph *graph) OBJECT_GUARDED_DELETE(deg_depsgraph, Depsgraph); } -bool DEG_is_evaluating(struct Depsgraph *depsgraph) +bool DEG_is_evaluating(const struct Depsgraph *depsgraph) { - DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(depsgraph); + const DEG::Depsgraph *deg_graph = reinterpret_cast<const DEG::Depsgraph *>(depsgraph); return deg_graph->is_evaluating; } diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h index 7801f95e008..672f202338e 100644 --- a/source/blender/depsgraph/intern/depsgraph.h +++ b/source/blender/depsgraph/intern/depsgraph.h @@ -43,8 +43,6 @@ #include "intern/debug/deg_debug.h" #include "intern/depsgraph_type.h" -struct GHash; -struct GSet; struct ID; struct Scene; struct ViewLayer; @@ -97,7 +95,7 @@ struct Depsgraph { /* <ID : IDNode> mapping from ID blocks to nodes representing these * blocks, used for quick lookups. */ - GHash *id_hash; + Map<const ID *, IDNode *> id_hash; /* Ordered list of ID nodes, order matches ID allocation order. * Used for faster iteration, especially for areas which are critical to @@ -119,7 +117,7 @@ struct Depsgraph { /* Quick-Access Temp Data ............. */ /* Nodes which have been tagged as "directly modified". */ - GSet *entry_tags; + Set<OperationNode *> entry_tags; /* Special entry tag for time source. Allows to tag invisible dependency graphs for update when * scene frame changes, so then when dependency graph becomes visible it is on a proper state. */ @@ -169,7 +167,7 @@ struct Depsgraph { /* Cached list of colliders/effectors for collections and the scene * created along with relations, for fast lookup during evaluation. */ - GHash *physics_relations[DEG_PHYSICS_RELATIONS_NUM]; + Map<const ID *, ListBase *> *physics_relations[DEG_PHYSICS_RELATIONS_NUM]; }; } // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index ff65ff5b050..542065d2fb3 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -25,7 +25,6 @@ #include "MEM_guardedalloc.h" -#include "BLI_ghash.h" #include "BLI_listbase.h" #include "BLI_utildefines.h" diff --git a/source/blender/depsgraph/intern/depsgraph_debug.cc b/source/blender/depsgraph/intern/depsgraph_debug.cc index c713a4a3fb1..8b6984c9870 100644 --- a/source/blender/depsgraph/intern/depsgraph_debug.cc +++ b/source/blender/depsgraph/intern/depsgraph_debug.cc @@ -23,7 +23,6 @@ * Implementation of tools for debugging the depsgraph */ -#include "BLI_ghash.h" #include "BLI_utildefines.h" extern "C" { @@ -224,13 +223,12 @@ void DEG_stats_simple(const Depsgraph *graph, for (DEG::IDNode *id_node : deg_graph->id_nodes) { tot_outer++; - GHASH_FOREACH_BEGIN (DEG::ComponentNode *, comp_node, id_node->components) { + for (DEG::ComponentNode *comp_node : id_node->components.values()) { tot_outer++; for (DEG::OperationNode *op_node : comp_node->operations) { tot_rels += op_node->inlinks.size(); } } - GHASH_FOREACH_END(); } DEG::TimeSourceNode *time_source = deg_graph->find_time_source(); diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc index 9251d975125..c5628a4f8b9 100644 --- a/source/blender/depsgraph/intern/depsgraph_eval.cc +++ b/source/blender/depsgraph/intern/depsgraph_eval.cc @@ -25,7 +25,6 @@ #include "MEM_guardedalloc.h" -#include "BLI_ghash.h" #include "BLI_listbase.h" #include "BLI_utildefines.h" @@ -87,5 +86,5 @@ void DEG_evaluate_on_framechange(Main *bmain, Depsgraph *graph, float ctime) bool DEG_needs_eval(Depsgraph *graph) { DEG::Depsgraph *deg_graph = reinterpret_cast<DEG::Depsgraph *>(graph); - return BLI_gset_len(deg_graph->entry_tags) != 0 || deg_graph->need_update_time; + return !deg_graph->entry_tags.is_empty() || deg_graph->need_update_time; } diff --git a/source/blender/depsgraph/intern/depsgraph_physics.cc b/source/blender/depsgraph/intern/depsgraph_physics.cc index d7c09780845..20375e447c3 100644 --- a/source/blender/depsgraph/intern/depsgraph_physics.cc +++ b/source/blender/depsgraph/intern/depsgraph_physics.cc @@ -28,7 +28,6 @@ #include "MEM_guardedalloc.h" #include "BLI_compiler_compat.h" -#include "BLI_ghash.h" #include "BLI_listbase.h" extern "C" { @@ -72,8 +71,8 @@ ListBase *DEG_get_effector_relations(const Depsgraph *graph, Collection *collect } ID *collection_orig = DEG_get_original_id(&collection->id); - return (ListBase *)BLI_ghash_lookup(deg_graph->physics_relations[DEG_PHYSICS_EFFECTOR], - collection_orig); + return deg_graph->physics_relations[DEG_PHYSICS_EFFECTOR]->lookup_default(collection_orig, + nullptr); } ListBase *DEG_get_collision_relations(const Depsgraph *graph, @@ -86,7 +85,7 @@ ListBase *DEG_get_collision_relations(const Depsgraph *graph, return nullptr; } ID *collection_orig = DEG_get_original_id(&collection->id); - return (ListBase *)BLI_ghash_lookup(deg_graph->physics_relations[type], collection_orig); + return deg_graph->physics_relations[type]->lookup_default(collection_orig, nullptr); } /********************** Depsgraph Building API ************************/ @@ -165,19 +164,15 @@ namespace DEG { ListBase *build_effector_relations(Depsgraph *graph, Collection *collection) { - GHash *hash = graph->physics_relations[DEG_PHYSICS_EFFECTOR]; + Map<const ID *, ListBase *> *hash = graph->physics_relations[DEG_PHYSICS_EFFECTOR]; if (hash == nullptr) { - graph->physics_relations[DEG_PHYSICS_EFFECTOR] = BLI_ghash_ptr_new( - "Depsgraph physics relations hash"); + graph->physics_relations[DEG_PHYSICS_EFFECTOR] = new Map<const ID *, ListBase *>(); hash = graph->physics_relations[DEG_PHYSICS_EFFECTOR]; } - ListBase *relations = reinterpret_cast<ListBase *>(BLI_ghash_lookup(hash, collection)); - if (relations == nullptr) { + return hash->lookup_or_add(&collection->id, [&]() { ::Depsgraph *depsgraph = reinterpret_cast<::Depsgraph *>(graph); - relations = BKE_effector_relations_create(depsgraph, graph->view_layer, collection); - BLI_ghash_insert(hash, &collection->id, relations); - } - return relations; + return BKE_effector_relations_create(depsgraph, graph->view_layer, collection); + }); } ListBase *build_collision_relations(Depsgraph *graph, @@ -185,48 +180,38 @@ ListBase *build_collision_relations(Depsgraph *graph, unsigned int modifier_type) { const ePhysicsRelationType type = modifier_to_relation_type(modifier_type); - GHash *hash = graph->physics_relations[type]; + Map<const ID *, ListBase *> *hash = graph->physics_relations[type]; if (hash == nullptr) { - graph->physics_relations[type] = BLI_ghash_ptr_new("Depsgraph physics relations hash"); + graph->physics_relations[type] = new Map<const ID *, ListBase *>(); hash = graph->physics_relations[type]; } - ListBase *relations = reinterpret_cast<ListBase *>(BLI_ghash_lookup(hash, collection)); - if (relations == nullptr) { + return hash->lookup_or_add(&collection->id, [&]() { ::Depsgraph *depsgraph = reinterpret_cast<::Depsgraph *>(graph); - relations = BKE_collision_relations_create(depsgraph, collection, modifier_type); - BLI_ghash_insert(hash, &collection->id, relations); - } - return relations; -} - -namespace { - -void free_effector_relations(void *value) -{ - BKE_effector_relations_free(reinterpret_cast<ListBase *>(value)); + return BKE_collision_relations_create(depsgraph, collection, modifier_type); + }); } -void free_collision_relations(void *value) -{ - BKE_collision_relations_free(reinterpret_cast<ListBase *>(value)); -} - -} // namespace - void clear_physics_relations(Depsgraph *graph) { for (int i = 0; i < DEG_PHYSICS_RELATIONS_NUM; i++) { - if (graph->physics_relations[i]) { + Map<const ID *, ListBase *> *hash = graph->physics_relations[i]; + if (hash) { const ePhysicsRelationType type = (ePhysicsRelationType)i; switch (type) { case DEG_PHYSICS_EFFECTOR: - BLI_ghash_free(graph->physics_relations[i], nullptr, free_effector_relations); + for (ListBase *list : hash->values()) { + BKE_effector_relations_free(list); + } + hash->clear(); break; case DEG_PHYSICS_COLLISION: case DEG_PHYSICS_SMOKE_COLLISION: case DEG_PHYSICS_DYNAMIC_BRUSH: - BLI_ghash_free(graph->physics_relations[i], nullptr, free_collision_relations); + for (ListBase *list : hash->values()) { + BKE_collision_relations_free(list); + } + hash->clear(); break; case DEG_PHYSICS_RELATIONS_NUM: break; diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc index 6e85b2e8bd9..b64b6c0e715 100644 --- a/source/blender/depsgraph/intern/depsgraph_query.cc +++ b/source/blender/depsgraph/intern/depsgraph_query.cc @@ -28,7 +28,6 @@ extern "C" { #include <string.h> // XXX: memcpy -#include "BLI_ghash.h" #include "BLI_listbase.h" #include "BLI_utildefines.h" @@ -271,7 +270,7 @@ ID *DEG_get_original_id(ID *id) return (ID *)id->orig_id; } -bool DEG_is_original_id(ID *id) +bool DEG_is_original_id(const ID *id) { /* Some explanation of the logic. * @@ -296,17 +295,17 @@ bool DEG_is_original_id(ID *id) return true; } -bool DEG_is_original_object(Object *object) +bool DEG_is_original_object(const Object *object) { return DEG_is_original_id(&object->id); } -bool DEG_is_evaluated_id(ID *id) +bool DEG_is_evaluated_id(const ID *id) { return !DEG_is_original_id(id); } -bool DEG_is_evaluated_object(Object *object) +bool DEG_is_evaluated_object(const Object *object) { return !DEG_is_original_object(object); } @@ -319,7 +318,7 @@ bool DEG_is_fully_evaluated(const struct Depsgraph *depsgraph) return false; } /* Check whether IDs are up to date. */ - if (BLI_gset_len(deg_graph->entry_tags) > 0) { + if (!deg_graph->entry_tags.is_empty()) { return false; } return true; diff --git a/source/blender/depsgraph/intern/depsgraph_query_foreach.cc b/source/blender/depsgraph/intern/depsgraph_query_foreach.cc index bc8012158e1..b68c4b91fcc 100644 --- a/source/blender/depsgraph/intern/depsgraph_query_foreach.cc +++ b/source/blender/depsgraph/intern/depsgraph_query_foreach.cc @@ -25,7 +25,6 @@ #include "MEM_guardedalloc.h" -#include "BLI_ghash.h" #include "BLI_utildefines.h" #include "DNA_object_types.h" @@ -75,7 +74,7 @@ void deg_foreach_dependent_operation(const Depsgraph *UNUSED(graph), /* Start with scheduling all operations from ID node. */ TraversalQueue queue; Set<OperationNode *> scheduled; - GHASH_FOREACH_BEGIN (ComponentNode *, comp_node, target_id_node->components) { + for (ComponentNode *comp_node : target_id_node->components.values()) { if (source_component_type != DEG_OB_COMP_ANY && nodeTypeToObjectComponent(comp_node->type) != source_component_type) { continue; @@ -88,7 +87,6 @@ void deg_foreach_dependent_operation(const Depsgraph *UNUSED(graph), scheduled.add(op_node); } } - GHASH_FOREACH_END(); /* Process the queue. */ while (!queue.empty()) { /* get next operation node to process. */ @@ -205,13 +203,12 @@ void deg_foreach_ancestor_ID(const Depsgraph *graph, /* Start with scheduling all operations from ID node. */ TraversalQueue queue; Set<OperationNode *> scheduled; - GHASH_FOREACH_BEGIN (ComponentNode *, comp_node, target_id_node->components) { + for (ComponentNode *comp_node : target_id_node->components.values()) { for (OperationNode *op_node : comp_node->operations) { queue.push_back(op_node); scheduled.add(op_node); } } - GHASH_FOREACH_END(); Set<IDNode *> visited; visited.add_new(target_id_node); /* Process the queue. */ diff --git a/source/blender/depsgraph/intern/depsgraph_registry.cc b/source/blender/depsgraph/intern/depsgraph_registry.cc index ad60b1bc4cf..3b0a0b3ea19 100644 --- a/source/blender/depsgraph/intern/depsgraph_registry.cc +++ b/source/blender/depsgraph/intern/depsgraph_registry.cc @@ -29,46 +29,33 @@ namespace DEG { -typedef set<Depsgraph *> DepsgraphStorage; -typedef map<Main *, DepsgraphStorage> MainDepsgraphMap; - -static MainDepsgraphMap g_graph_registry; +static Map<Main *, VectorSet<Depsgraph *>> g_graph_registry; void register_graph(Depsgraph *depsgraph) { Main *bmain = depsgraph->bmain; - MainDepsgraphMap::iterator it = g_graph_registry.find(bmain); - if (it == g_graph_registry.end()) { - it = g_graph_registry.insert(make_pair(bmain, DepsgraphStorage())).first; - } - DepsgraphStorage &storage = it->second; - storage.insert(depsgraph); + g_graph_registry.lookup_or_add_default(bmain).add_new(depsgraph); } void unregister_graph(Depsgraph *depsgraph) { Main *bmain = depsgraph->bmain; - MainDepsgraphMap::iterator it = g_graph_registry.find(bmain); - BLI_assert(it != g_graph_registry.end()); - - // Remove dependency graph from storage. - DepsgraphStorage &storage = it->second; - storage.erase(depsgraph); + VectorSet<Depsgraph *> &graphs = g_graph_registry.lookup(bmain); + graphs.remove(depsgraph); // If this was the last depsgraph associated with the main, remove the main entry as well. - if (storage.empty()) { - g_graph_registry.erase(bmain); + if (graphs.is_empty()) { + g_graph_registry.remove(bmain); } } -const set<Depsgraph *> &get_all_registered_graphs(Main *bmain) +ArrayRef<Depsgraph *> get_all_registered_graphs(Main *bmain) { - MainDepsgraphMap::iterator it = g_graph_registry.find(bmain); - if (it == g_graph_registry.end()) { - static DepsgraphStorage empty_storage; - return empty_storage; + VectorSet<Depsgraph *> *graphs = g_graph_registry.lookup_ptr(bmain); + if (graphs != nullptr) { + return *graphs; } - return it->second; + return {}; } } // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_registry.h b/source/blender/depsgraph/intern/depsgraph_registry.h index 7517b6a0b2a..f8e5b9543f2 100644 --- a/source/blender/depsgraph/intern/depsgraph_registry.h +++ b/source/blender/depsgraph/intern/depsgraph_registry.h @@ -33,6 +33,6 @@ struct Depsgraph; void register_graph(Depsgraph *depsgraph); void unregister_graph(Depsgraph *depsgraph); -const set<Depsgraph *> &get_all_registered_graphs(Main *bmain); +ArrayRef<Depsgraph *> get_all_registered_graphs(Main *bmain); } // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_relation.cc b/source/blender/depsgraph/intern/depsgraph_relation.cc index cff62540ca8..a4ec48658f5 100644 --- a/source/blender/depsgraph/intern/depsgraph_relation.cc +++ b/source/blender/depsgraph/intern/depsgraph_relation.cc @@ -30,12 +30,6 @@ namespace DEG { -/* TODO(sergey): Find a better place for this. */ -template<typename T> static void remove_from_vector(vector<T> *vector, const T &value) -{ - vector->erase(std::remove(vector->begin(), vector->end(), value), vector->end()); -} - Relation::Relation(Node *from, Node *to, const char *description) : from(from), to(to), name(description), flag(0) { @@ -52,8 +46,8 @@ Relation::Relation(Node *from, Node *to, const char *description) * * - Un-registering relation is not a cheap operation, so better to have it * as an explicit call if we need this. */ - from->outlinks.push_back(this); - to->inlinks.push_back(this); + from->outlinks.append(this); + to->inlinks.append(this); } Relation::~Relation() @@ -66,8 +60,8 @@ void Relation::unlink() { /* Sanity check. */ BLI_assert(from != nullptr && to != nullptr); - remove_from_vector(&from->outlinks, this); - remove_from_vector(&to->inlinks, this); + from->outlinks.remove_first_occurrence_and_reorder(this); + to->inlinks.remove_first_occurrence_and_reorder(this); } } // namespace DEG diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 1c56808ea82..ff7d9ee0170 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -495,13 +495,13 @@ void deg_graph_node_tag_zero(Main *bmain, ID *id = id_node->id_orig; /* TODO(sergey): Which recalc flags to set here? */ id_node->id_cow->recalc |= deg_recalc_flags_for_legacy_zero(); - GHASH_FOREACH_BEGIN (ComponentNode *, comp_node, id_node->components) { + + for (ComponentNode *comp_node : id_node->components.values()) { if (comp_node->type == NodeType::ANIMATION) { continue; } comp_node->tag_update(graph, update_source); } - GHASH_FOREACH_END(); deg_graph_id_tag_legacy_compat(bmain, graph, id, (IDRecalcFlag)0, update_source); } diff --git a/source/blender/depsgraph/intern/depsgraph_type.cc b/source/blender/depsgraph/intern/depsgraph_type.cc index 559bf8dfdab..92d775b0ae4 100644 --- a/source/blender/depsgraph/intern/depsgraph_type.cc +++ b/source/blender/depsgraph/intern/depsgraph_type.cc @@ -25,7 +25,6 @@ #include <cstdlib> // for BLI_assert() -#include "BLI_ghash.h" #include "BLI_utildefines.h" #include "DNA_customdata_types.h" diff --git a/source/blender/depsgraph/intern/depsgraph_type.h b/source/blender/depsgraph/intern/depsgraph_type.h index a3dc2135f78..7016d07ae72 100644 --- a/source/blender/depsgraph/intern/depsgraph_type.h +++ b/source/blender/depsgraph/intern/depsgraph_type.h @@ -38,11 +38,13 @@ #include <map> #include <set> #include <string> -#include <unordered_map> #include <vector> #include "BLI_map.hh" #include "BLI_set.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" +#include "BLI_vector_set.hh" struct Depsgraph; @@ -51,14 +53,19 @@ struct CustomData_MeshMasks; namespace DEG { /* Commonly used types. */ +using BLI::ArrayRef; using BLI::Map; using BLI::Set; +using BLI::StringRef; +using BLI::StringRefNull; +using BLI::Vector; +using BLI::VectorSet; using std::deque; using std::map; using std::pair; using std::set; using std::string; -using std::unordered_map; +using std::unique_ptr; using std::vector; /* Commonly used functions. */ diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index e61bcb961ae..189beb506b3 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -28,7 +28,6 @@ #include "PIL_time.h" #include "BLI_compiler_attrs.h" -#include "BLI_ghash.h" #include "BLI_gsqueue.h" #include "BLI_task.h" #include "BLI_utildefines.h" @@ -61,18 +60,17 @@ namespace { struct DepsgraphEvalState; -void deg_task_run_func(TaskPool *pool, void *taskdata, int thread_id); +void deg_task_run_func(TaskPool *pool, void *taskdata); template<typename ScheduleFunction, typename... ScheduleFunctionArgs> void schedule_children(DepsgraphEvalState *state, OperationNode *node, - const int thread_id, ScheduleFunction *schedule_function, ScheduleFunctionArgs... schedule_function_args); -void schedule_node_to_pool(OperationNode *node, const int thread_id, TaskPool *pool) +void schedule_node_to_pool(OperationNode *node, const int UNUSED(thread_id), TaskPool *pool) { - BLI_task_pool_push_from_thread(pool, deg_task_run_func, node, false, NULL, thread_id); + BLI_task_pool_push(pool, deg_task_run_func, node, false, NULL); } /* Denotes which part of dependency graph is being evaluated. */ @@ -116,7 +114,7 @@ void evaluate_node(const DepsgraphEvalState *state, OperationNode *operation_nod } } -void deg_task_run_func(TaskPool *pool, void *taskdata, int thread_id) +void deg_task_run_func(TaskPool *pool, void *taskdata) { void *userdata_v = BLI_task_pool_user_data(pool); DepsgraphEvalState *state = (DepsgraphEvalState *)userdata_v; @@ -126,9 +124,7 @@ void deg_task_run_func(TaskPool *pool, void *taskdata, int thread_id) evaluate_node(state, operation_node); /* Schedule children. */ - BLI_task_pool_delayed_push_begin(pool, thread_id); - schedule_children(state, operation_node, thread_id, schedule_node_to_pool, pool); - BLI_task_pool_delayed_push_end(pool, thread_id); + schedule_children(state, operation_node, schedule_node_to_pool, pool); } bool check_operation_node_visible(OperationNode *op_node) @@ -238,7 +234,6 @@ template<typename ScheduleFunction, typename... ScheduleFunctionArgs> void schedule_node(DepsgraphEvalState *state, OperationNode *node, bool dec_parents, - const int thread_id, ScheduleFunction *schedule_function, ScheduleFunctionArgs... schedule_function_args) { @@ -271,11 +266,11 @@ void schedule_node(DepsgraphEvalState *state, if (!is_scheduled) { if (node->is_noop()) { /* skip NOOP node, schedule children right away */ - schedule_children(state, node, thread_id, schedule_function, schedule_function_args...); + schedule_children(state, node, schedule_function, schedule_function_args...); } else { /* children are scheduled once this task is completed */ - schedule_function(node, thread_id, schedule_function_args...); + schedule_function(node, 0, schedule_function_args...); } } } @@ -286,14 +281,13 @@ void schedule_graph(DepsgraphEvalState *state, ScheduleFunctionArgs... schedule_function_args) { for (OperationNode *node : state->graph->operations) { - schedule_node(state, node, false, -1, schedule_function, schedule_function_args...); + schedule_node(state, node, false, schedule_function, schedule_function_args...); } } template<typename ScheduleFunction, typename... ScheduleFunctionArgs> void schedule_children(DepsgraphEvalState *state, OperationNode *node, - const int thread_id, ScheduleFunction *schedule_function, ScheduleFunctionArgs... schedule_function_args) { @@ -307,7 +301,6 @@ void schedule_children(DepsgraphEvalState *state, schedule_node(state, child, (rel->flag & RELATION_FLAG_CYCLIC) == 0, - thread_id, schedule_function, schedule_function_args...); } @@ -330,7 +323,7 @@ void evaluate_graph_single_threaded(DepsgraphEvalState *state) BLI_gsqueue_pop(evaluation_queue, &operation_node); evaluate_node(state, operation_node); - schedule_children(state, operation_node, 0, schedule_node_to_queue, evaluation_queue); + schedule_children(state, operation_node, schedule_node_to_queue, evaluation_queue); } BLI_gsqueue_free(evaluation_queue); @@ -354,6 +347,16 @@ void depsgraph_ensure_view_layer(Depsgraph *graph) } // namespace +static TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state) +{ + if (G.debug & G_DEBUG_DEPSGRAPH_NO_THREADS) { + return BLI_task_pool_create_no_threads(state); + } + else { + return BLI_task_pool_create_suspended(state, TASK_PRIORITY_HIGH); + } +} + /** * Evaluate all nodes tagged for updating, * \warning This is usually done as part of main loop, but may also be @@ -364,7 +367,7 @@ void depsgraph_ensure_view_layer(Depsgraph *graph) void deg_evaluate_on_refresh(Depsgraph *graph) { /* Nothing to update, early out. */ - if (BLI_gset_len(graph->entry_tags) == 0) { + if (graph->entry_tags.is_empty()) { return; } @@ -377,30 +380,20 @@ void deg_evaluate_on_refresh(Depsgraph *graph) state.graph = graph; state.do_stats = graph->debug.do_time_debug(); state.need_single_thread_pass = false; - /* Set up task scheduler and pull for threaded evaluation. */ - TaskScheduler *task_scheduler; - bool need_free_scheduler; - if (G.debug & G_DEBUG_DEPSGRAPH_NO_THREADS) { - task_scheduler = BLI_task_scheduler_create(1); - need_free_scheduler = true; - } - else { - task_scheduler = BLI_task_scheduler_get(); - need_free_scheduler = false; - } - TaskPool *task_pool = BLI_task_pool_create_suspended(task_scheduler, &state, TASK_PRIORITY_HIGH); /* Prepare all nodes for evaluation. */ initialize_execution(&state, graph); /* Do actual evaluation now. */ - /* First, process all Copy-On-Write nodes. */ state.stage = EvaluationStage::COPY_ON_WRITE; + TaskPool *task_pool = deg_evaluate_task_pool_create(&state); schedule_graph(&state, schedule_node_to_pool, task_pool); - BLI_task_pool_work_wait_and_reset(task_pool); + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); /* After that, process all other nodes. */ state.stage = EvaluationStage::THREADED_EVALUATION; + task_pool = deg_evaluate_task_pool_create(&state); schedule_graph(&state, schedule_node_to_pool, task_pool); BLI_task_pool_work_and_wait(task_pool); BLI_task_pool_free(task_pool); @@ -418,9 +411,6 @@ void deg_evaluate_on_refresh(Depsgraph *graph) } /* Clear any uncleared tags - just in case. */ deg_graph_clear_tags(graph); - if (need_free_scheduler) { - BLI_task_scheduler_free(task_scheduler); - } graph->is_evaluating = false; graph->debug.end_graph_evaluation(); diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc index 990002cf2e8..5ff1b31b07c 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -27,7 +27,6 @@ #include <cmath> -#include "BLI_ghash.h" #include "BLI_listbase.h" #include "BLI_math_vector.h" #include "BLI_task.h" @@ -94,9 +93,9 @@ void flush_init_id_node_func(void *__restrict data_v, Depsgraph *graph = (Depsgraph *)data_v; IDNode *id_node = graph->id_nodes[i]; id_node->custom_flags = ID_STATE_NONE; - GHASH_FOREACH_BEGIN (ComponentNode *, comp_node, id_node->components) + for (ComponentNode *comp_node : id_node->components.values()) { comp_node->custom_flags = COMPONENT_STATE_NONE; - GHASH_FOREACH_END(); + } } BLI_INLINE void flush_prepare(Depsgraph *graph) @@ -116,7 +115,7 @@ BLI_INLINE void flush_prepare(Depsgraph *graph) BLI_INLINE void flush_schedule_entrypoints(Depsgraph *graph, FlushQueue *queue) { - GSET_FOREACH_BEGIN (OperationNode *, op_node, graph->entry_tags) { + for (OperationNode *op_node : graph->entry_tags) { queue->push_back(op_node); op_node->scheduled = true; DEG_DEBUG_PRINTF((::Depsgraph *)graph, @@ -124,7 +123,6 @@ BLI_INLINE void flush_schedule_entrypoints(Depsgraph *graph, FlushQueue *queue) "Operation is entry point for update: %s\n", op_node->identifier().c_str()); } - GSET_FOREACH_END(); } BLI_INLINE void flush_handle_id_node(IDNode *id_node) @@ -231,7 +229,7 @@ void flush_editors_id_update(Depsgraph *graph, const DEGEditorUpdateContext *upd ID *id_orig = id_node->id_orig; ID *id_cow = id_node->id_cow; /* Gather recalc flags from all changed components. */ - GHASH_FOREACH_BEGIN (ComponentNode *, comp_node, id_node->components) { + for (DEG::ComponentNode *comp_node : id_node->components.values()) { if (comp_node->custom_flags != COMPONENT_STATE_DONE) { continue; } @@ -239,7 +237,6 @@ void flush_editors_id_update(Depsgraph *graph, const DEGEditorUpdateContext *upd BLI_assert(factory != nullptr); id_cow->recalc |= factory->id_recalc_tag(); } - GHASH_FOREACH_END(); DEG_DEBUG_PRINTF((::Depsgraph *)graph, EVAL, "Accumulated recalc bits for %s: %u\n", @@ -307,7 +304,7 @@ void invalidate_tagged_evaluated_data(Depsgraph *graph) if (!deg_copy_on_write_is_expanded(id_cow)) { continue; } - GHASH_FOREACH_BEGIN (ComponentNode *, comp_node, id_node->components) { + for (ComponentNode *comp_node : id_node->components.values()) { if (comp_node->custom_flags != COMPONENT_STATE_DONE) { continue; } @@ -322,7 +319,6 @@ void invalidate_tagged_evaluated_data(Depsgraph *graph) break; } } - GHASH_FOREACH_END(); } #else (void)graph; @@ -347,7 +343,7 @@ void deg_graph_flush_updates(Main *bmain, Depsgraph *graph) graph->ctime = ctime; time_source->tag_update(graph, DEG::DEG_UPDATE_SOURCE_TIME); } - if (BLI_gset_len(graph->entry_tags) == 0) { + if (graph->entry_tags.is_empty()) { return; } /* Reset all flags, get ready for the flush. */ @@ -393,7 +389,7 @@ void deg_graph_clear_tags(Depsgraph *graph) DEPSOP_FLAG_USER_MODIFIED); } /* Clear any entry tags which haven't been flushed. */ - BLI_gset_clear(graph->entry_tags, nullptr); + graph->entry_tags.clear(); } } // namespace DEG diff --git a/source/blender/depsgraph/intern/eval/deg_eval_stats.cc b/source/blender/depsgraph/intern/eval/deg_eval_stats.cc index f164560ac24..9d3b1356570 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_stats.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_stats.cc @@ -23,7 +23,6 @@ #include "intern/eval/deg_eval_stats.h" -#include "BLI_ghash.h" #include "BLI_utildefines.h" #include "intern/depsgraph.h" @@ -41,10 +40,9 @@ void deg_eval_stats_aggregate(Depsgraph *graph) * Those are not filled in by the evaluation engine. */ for (Node *node : graph->id_nodes) { IDNode *id_node = (IDNode *)node; - GHASH_FOREACH_BEGIN (ComponentNode *, comp_node, id_node->components) { + for (ComponentNode *comp_node : id_node->components.values()) { comp_node->stats.reset_current(); } - GHASH_FOREACH_END(); id_node->stats.reset_current(); } /* Now accumulate operation timings to components and IDs. */ diff --git a/source/blender/depsgraph/intern/node/deg_node.h b/source/blender/depsgraph/intern/node/deg_node.h index 75a3762192d..f0ce38ddeae 100644 --- a/source/blender/depsgraph/intern/node/deg_node.h +++ b/source/blender/depsgraph/intern/node/deg_node.h @@ -165,7 +165,7 @@ struct Node { * The reason why all depsgraph nodes are descended from this type (apart * from basic serialization benefits - from the typeinfo) is that we can * have relationships between these nodes. */ - typedef vector<Relation *> Relations; + typedef Vector<Relation *> Relations; string name; /* Identifier - mainly for debugging purposes. */ NodeType type; /* Structural type of node. */ diff --git a/source/blender/depsgraph/intern/node/deg_node_component.cc b/source/blender/depsgraph/intern/node/deg_node_component.cc index 5fd412f32e6..c751282a087 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.cc +++ b/source/blender/depsgraph/intern/node/deg_node_component.cc @@ -26,7 +26,6 @@ #include <cstring> /* required for STREQ later on. */ #include <stdio.h> -#include "BLI_ghash.h" #include "BLI_utildefines.h" extern "C" { diff --git a/source/blender/depsgraph/intern/node/deg_node_component.h b/source/blender/depsgraph/intern/node/deg_node_component.h index b211ac873a3..ca37401d200 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.h +++ b/source/blender/depsgraph/intern/node/deg_node_component.h @@ -31,7 +31,6 @@ #include "BLI_string.h" #include "BLI_utildefines.h" -struct GHash; struct ID; struct bPoseChannel; diff --git a/source/blender/depsgraph/intern/node/deg_node_id.cc b/source/blender/depsgraph/intern/node/deg_node_id.cc index 4b6120a6985..4e998fb095a 100644 --- a/source/blender/depsgraph/intern/node/deg_node_id.cc +++ b/source/blender/depsgraph/intern/node/deg_node_id.cc @@ -26,7 +26,6 @@ #include <cstring> /* required for STREQ later on. */ #include <stdio.h> -#include "BLI_ghash.h" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -69,34 +68,6 @@ bool IDNode::ComponentIDKey::operator==(const ComponentIDKey &other) const return type == other.type && STREQ(name, other.name); } -static unsigned int id_deps_node_hash_key(const void *key_v) -{ - const IDNode::ComponentIDKey *key = reinterpret_cast<const IDNode::ComponentIDKey *>(key_v); - const int type_as_int = static_cast<int>(key->type); - return BLI_ghashutil_combine_hash(BLI_ghashutil_uinthash(type_as_int), - BLI_ghashutil_strhash_p(key->name)); -} - -static bool id_deps_node_hash_key_cmp(const void *a, const void *b) -{ - const IDNode::ComponentIDKey *key_a = reinterpret_cast<const IDNode::ComponentIDKey *>(a); - const IDNode::ComponentIDKey *key_b = reinterpret_cast<const IDNode::ComponentIDKey *>(b); - return !(*key_a == *key_b); -} - -static void id_deps_node_hash_key_free(void *key_v) -{ - typedef IDNode::ComponentIDKey ComponentIDKey; - ComponentIDKey *key = reinterpret_cast<ComponentIDKey *>(key_v); - OBJECT_GUARDED_DELETE(key, ComponentIDKey); -} - -static void id_deps_node_hash_value_free(void *value_v) -{ - ComponentNode *comp_node = reinterpret_cast<ComponentNode *>(value_v); - OBJECT_GUARDED_DELETE(comp_node, ComponentNode); -} - /* Initialize 'id' node - from pointer data given. */ void IDNode::init(const ID *id, const char *UNUSED(subdata)) { @@ -116,9 +87,6 @@ void IDNode::init(const ID *id, const char *UNUSED(subdata)) visible_components_mask = 0; previously_visible_components_mask = 0; - - components = BLI_ghash_new( - id_deps_node_hash_key, id_deps_node_hash_key_cmp, "Depsgraph id components hash"); } void IDNode::init_copy_on_write(ID *id_cow_hint) @@ -158,7 +126,9 @@ void IDNode::destroy() return; } - BLI_ghash_free(components, id_deps_node_hash_key_free, id_deps_node_hash_value_free); + for (ComponentNode *comp_node : components.values()) { + OBJECT_GUARDED_DELETE(comp_node, ComponentNode); + } /* Free memory used by this CoW ID. */ if (id_cow != id_orig && id_cow != nullptr) { @@ -185,7 +155,7 @@ string IDNode::identifier() const ComponentNode *IDNode::find_component(NodeType type, const char *name) const { ComponentIDKey key(type, name); - return reinterpret_cast<ComponentNode *>(BLI_ghash_lookup(components, &key)); + return components.lookup_default(key, nullptr); } ComponentNode *IDNode::add_component(NodeType type, const char *name) @@ -196,8 +166,8 @@ ComponentNode *IDNode::add_component(NodeType type, const char *name) comp_node = (ComponentNode *)factory->create_node(this->id_orig, "", name); /* Register. */ - ComponentIDKey *key = OBJECT_GUARDED_NEW(ComponentIDKey, type, name); - BLI_ghash_insert(components, key, comp_node); + ComponentIDKey key(type, name); + components.add_new(key, comp_node); comp_node->owner = this; } return comp_node; @@ -205,7 +175,7 @@ ComponentNode *IDNode::add_component(NodeType type, const char *name) void IDNode::tag_update(Depsgraph *graph, eUpdateSource source) { - GHASH_FOREACH_BEGIN (ComponentNode *, comp_node, components) { + for (ComponentNode *comp_node : components.values()) { /* Relations update does explicit animation update when needed. Here we ignore animation * component to avoid loss of possible unkeyed changes. */ if (comp_node->type == NodeType::ANIMATION && source == DEG_UPDATE_SOURCE_RELATIONS) { @@ -213,30 +183,27 @@ void IDNode::tag_update(Depsgraph *graph, eUpdateSource source) } comp_node->tag_update(graph, source); } - GHASH_FOREACH_END(); } void IDNode::finalize_build(Depsgraph *graph) { /* Finalize build of all components. */ - GHASH_FOREACH_BEGIN (ComponentNode *, comp_node, components) { + for (ComponentNode *comp_node : components.values()) { comp_node->finalize_build(graph); } - GHASH_FOREACH_END(); visible_components_mask = get_visible_components_mask(); } IDComponentsMask IDNode::get_visible_components_mask() const { IDComponentsMask result = 0; - GHASH_FOREACH_BEGIN (ComponentNode *, comp_node, components) { + for (ComponentNode *comp_node : components.values()) { if (comp_node->affects_directly_visible) { const int component_type_as_int = static_cast<int>(comp_node->type); BLI_assert(component_type_as_int < 64); result |= (1ULL << component_type_as_int); } } - GHASH_FOREACH_END(); return result; } diff --git a/source/blender/depsgraph/intern/node/deg_node_id.h b/source/blender/depsgraph/intern/node/deg_node_id.h index 80bb67f182f..c7663b50c6f 100644 --- a/source/blender/depsgraph/intern/node/deg_node_id.h +++ b/source/blender/depsgraph/intern/node/deg_node_id.h @@ -23,12 +23,11 @@ #pragma once +#include "BLI_ghash.h" #include "BLI_sys_types.h" #include "DNA_ID.h" #include "intern/node/deg_node.h" -struct GHash; - namespace DEG { struct ComponentNode; @@ -82,7 +81,7 @@ struct IDNode : public Node { ID *id_cow; /* Hash to make it faster to look up components. */ - GHash *components; + Map<ComponentIDKey, ComponentNode *> components; /* Additional flags needed for scene evaluation. * TODO(sergey): Only needed for until really granular updates @@ -116,3 +115,16 @@ struct IDNode : public Node { }; } // namespace DEG + +namespace BLI { + +template<> struct DefaultHash<DEG::IDNode::ComponentIDKey> { + uint32_t operator()(const DEG::IDNode::ComponentIDKey &key) const + { + const int type_as_int = static_cast<int>(key.type); + return BLI_ghashutil_combine_hash(BLI_ghashutil_uinthash(type_as_int), + BLI_ghashutil_strhash_p(key.name)); + } +}; + +} // namespace BLI diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc index c1aa05df987..2381076161f 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.cc +++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc @@ -25,7 +25,6 @@ #include "MEM_guardedalloc.h" -#include "BLI_ghash.h" #include "BLI_utildefines.h" #include "intern/depsgraph.h" diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index 1bdc3bc5a03..6e7654aed02 100644 --- a/source/blender/draw/DRW_engine.h +++ b/source/blender/draw/DRW_engine.h @@ -77,7 +77,7 @@ typedef bool (*DRW_SelectPassFn)(eDRWSelectStage stage, void *user_data); typedef bool (*DRW_ObjectFilterFn)(struct Object *ob, void *user_data); void DRW_draw_view(const struct bContext *C); -void DRW_draw_region_engine_info(int xoffset, int yoffset); +void DRW_draw_region_engine_info(int xoffset, int *yoffset, int line_height); void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, struct RenderEngineType *engine_type, diff --git a/source/blender/draw/engines/overlay/overlay_gpencil.c b/source/blender/draw/engines/overlay/overlay_gpencil.c index 0dbb0542b18..b79d5753686 100644 --- a/source/blender/draw/engines/overlay/overlay_gpencil.c +++ b/source/blender/draw/engines/overlay/overlay_gpencil.c @@ -201,7 +201,9 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) } const bool show_overlays = (v3d->flag2 & V3D_HIDE_OVERLAYS) == 0; - const bool show_grid = (v3d->gp_flag & V3D_GP_SHOW_GRID) != 0; + const bool show_grid = (v3d->gp_flag & V3D_GP_SHOW_GRID) != 0 && + ((ts->gpencil_v3d_align & + (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE)) == 0); if (show_grid && show_overlays) { const char *grid_unit = NULL; @@ -238,6 +240,15 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata) break; } + /* Move the grid to the right location depending of the align type. + * This is required only for 3D Cursor or Origin. */ + if (ts->gpencil_v3d_align & GP_PROJECT_CURSOR) { + copy_v3_v3(mat[3], cursor->location); + } + else if (ts->gpencil_v3d_align & GP_PROJECT_VIEWSPACE) { + copy_v3_v3(mat[3], ob->obmat[3]); + } + translate_m4(mat, gpd->grid.offset[0], gpd->grid.offset[1], 0.0f); mul_v2_v2fl(size, gpd->grid.scale, 2.0f * ED_scene_grid_scale(scene, &grid_unit)); rescale_m4(mat, (float[3]){size[0], size[1], 0.0f}); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index 40687306b4e..401f5c76a02 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -4509,7 +4509,7 @@ BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, } } -static void extract_run(TaskPool *__restrict UNUSED(pool), void *taskdata, int UNUSED(threadid)) +static void extract_run(TaskPool *__restrict UNUSED(pool), void *taskdata) { ExtractTaskData *data = taskdata; mesh_extract_iter( @@ -4595,7 +4595,7 @@ static void extract_task_create(TaskPool *task_pool, else { /* Single threaded extraction. */ (*task_counter)++; - extract_run(NULL, taskdata, -1); + extract_run(NULL, taskdata); MEM_freeN(taskdata); } } @@ -4685,11 +4685,11 @@ void mesh_buffer_cache_create_requested(MeshBatchCache *cache, double rdata_end = PIL_check_seconds_timer(); #endif - TaskScheduler *task_scheduler; TaskPool *task_pool; - task_scheduler = BLI_task_scheduler_get(); - task_pool = BLI_task_pool_create_suspended(task_scheduler, NULL, TASK_PRIORITY_HIGH); + /* Create a suspended pool as the finalize method could be called too early. + * See `extract_run`. */ + task_pool = BLI_task_pool_create_suspended(NULL, TASK_PRIORITY_HIGH); size_t counters_size = (sizeof(mbc) / sizeof(void *)) * sizeof(int32_t); int32_t *task_counters = MEM_callocN(counters_size, __func__); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 28c8570e897..e3c364eef6d 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -1068,7 +1068,7 @@ static void drw_engines_draw_text(void) } /* Draw render engine info. */ -void DRW_draw_region_engine_info(int xoffset, int yoffset) +void DRW_draw_region_engine_info(int xoffset, int *yoffset, int line_height) { LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) { DrawEngineType *engine = link->data; @@ -1091,8 +1091,8 @@ void DRW_draw_region_engine_info(int xoffset, int yoffset) if (*chr_current == '\n') { char info[GPU_INFO_SIZE]; BLI_strncpy(info, chr_start, line_len + 1); - yoffset -= U.widget_unit; - BLF_draw_default(xoffset, yoffset, 0.0f, info, sizeof(info)); + *yoffset -= line_height; + BLF_draw_default(xoffset, *yoffset, 0.0f, info, sizeof(info)); /* Re-start counting. */ chr_start = chr_current + 1; @@ -1102,8 +1102,8 @@ void DRW_draw_region_engine_info(int xoffset, int yoffset) char info[GPU_INFO_SIZE]; BLI_strncpy(info, chr_start, line_len + 1); - yoffset -= U.widget_unit; - BLF_draw_default(xoffset, yoffset, 0.0f, info, sizeof(info)); + *yoffset -= line_height; + BLF_draw_default(xoffset, *yoffset, 0.0f, info, sizeof(info)); BLF_disable(font_id, BLF_SHADOW); } diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index e4da4e66e21..4c7c3a2c253 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -87,6 +87,7 @@ #include "BKE_collection.h" #include "BKE_context.h" #include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_global.h" #include "BKE_key.h" #include "BKE_layer.h" diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index 3ae4e3bf998..82e24eaa6e3 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -40,6 +40,7 @@ #include "BKE_animsys.h" #include "BKE_context.h" #include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_report.h" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c index e22fddc6d67..2dae4e8b4c5 100644 --- a/source/blender/editors/animation/keyframes_edit.c +++ b/source/blender/editors/animation/keyframes_edit.c @@ -633,7 +633,7 @@ bool keyframe_region_lasso_test(const KeyframeEdit_LassoData *data_lasso, const BLI_rctf_transform_pt_v(data_lasso->rectf_view, data_lasso->rectf_scaled, xy_view, xy); if (BLI_lasso_is_point_inside( - data_lasso->mcords, data_lasso->mcords_tot, xy_view[0], xy_view[1], INT_MAX)) { + data_lasso->mcoords, data_lasso->mcoords_len, xy_view[0], xy_view[1], INT_MAX)) { return true; } } diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 49e936d22aa..04061ceea51 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -50,6 +50,7 @@ #include "BKE_armature.h" #include "BKE_context.h" #include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_global.h" #include "BKE_idtype.h" #include "BKE_key.h" diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index dfb274fdefe..d941f8ce95f 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -53,6 +53,7 @@ #include "WM_types.h" #include "ED_armature.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_view3d.h" @@ -222,6 +223,7 @@ static int armature_click_extrude_exec(bContext *C, wmOperator *UNUSED(op)) WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); DEG_id_tag_update(&obedit->id, ID_RECALC_SELECT); + ED_outliner_select_sync_from_edit_bone_tag(C); return OPERATOR_FINISHED; } @@ -1049,6 +1051,8 @@ static int armature_duplicate_selected_exec(bContext *C, wmOperator *op) } MEM_freeN(objects); + ED_outliner_select_sync_from_edit_bone_tag(C); + return OPERATOR_FINISHED; } @@ -1521,7 +1525,13 @@ static int armature_extrude_exec(bContext *C, wmOperator *op) } MEM_freeN(objects); - return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + if (!changed_multi) { + return OPERATOR_CANCELLED; + } + + ED_outliner_select_sync_from_edit_bone_tag(C); + + return OPERATOR_FINISHED; } void ARMATURE_OT_extrude(wmOperatorType *ot) @@ -1592,6 +1602,7 @@ static int armature_bone_primitive_add_exec(bContext *C, wmOperator *op) /* note, notifier might evolve */ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); DEG_id_tag_update(&obedit->id, ID_RECALC_SELECT); + ED_outliner_select_sync_from_edit_bone_tag(C); return OPERATOR_FINISHED; } @@ -1682,6 +1693,7 @@ static int armature_subdivide_exec(bContext *C, wmOperator *op) /* note, notifier might evolve */ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); DEG_id_tag_update(&obedit->id, ID_RECALC_SELECT); + ED_outliner_select_sync_from_edit_bone_tag(C); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c index 8a7afe13a2f..a7a705a6202 100644 --- a/source/blender/editors/armature/armature_edit.c +++ b/source/blender/editors/armature/armature_edit.c @@ -54,6 +54,7 @@ #include "WM_types.h" #include "ED_armature.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_view3d.h" @@ -1283,6 +1284,7 @@ static int armature_delete_selected_exec(bContext *C, wmOperator *UNUSED(op)) BKE_pose_tag_recalc(CTX_data_main(C), obedit->pose); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); + ED_outliner_select_sync_from_edit_bone_tag(C); } } MEM_freeN(objects); @@ -1458,6 +1460,7 @@ static int armature_dissolve_selected_exec(bContext *C, wmOperator *UNUSED(op)) ED_armature_edit_refresh_layer_used(arm); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); + ED_outliner_select_sync_from_edit_bone_tag(C); } } MEM_freeN(objects); diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index 3d41fd5f0c6..08d82bf13c9 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -104,6 +104,7 @@ void POSE_OT_select_all(struct wmOperatorType *ot); void POSE_OT_select_parent(struct wmOperatorType *ot); void POSE_OT_select_hierarchy(struct wmOperatorType *ot); void POSE_OT_select_linked(struct wmOperatorType *ot); +void POSE_OT_select_linked_pick(struct wmOperatorType *ot); void POSE_OT_select_constraint_target(struct wmOperatorType *ot); void POSE_OT_select_grouped(struct wmOperatorType *ot); void POSE_OT_select_mirror(struct wmOperatorType *ot); diff --git a/source/blender/editors/armature/armature_ops.c b/source/blender/editors/armature/armature_ops.c index b304ce92a54..da1b29307b1 100644 --- a/source/blender/editors/armature/armature_ops.c +++ b/source/blender/editors/armature/armature_ops.c @@ -100,6 +100,7 @@ void ED_operatortypes_armature(void) WM_operatortype_append(POSE_OT_select_parent); WM_operatortype_append(POSE_OT_select_hierarchy); WM_operatortype_append(POSE_OT_select_linked); + WM_operatortype_append(POSE_OT_select_linked_pick); WM_operatortype_append(POSE_OT_select_constraint_target); WM_operatortype_append(POSE_OT_select_grouped); WM_operatortype_append(POSE_OT_select_mirror); diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c index 644e466e904..5f3b876efaf 100644 --- a/source/blender/editors/armature/armature_relations.c +++ b/source/blender/editors/armature/armature_relations.c @@ -42,7 +42,7 @@ #include "BKE_armature.h" #include "BKE_constraint.h" #include "BKE_context.h" -#include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_layer.h" #include "BKE_main.h" #include "BKE_report.h" @@ -58,6 +58,7 @@ #include "ED_armature.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "UI_interface.h" @@ -707,6 +708,7 @@ static int separate_armature_exec(bContext *C, wmOperator *op) if (ok) { BKE_report(op->reports, RPT_INFO, "Separated bones"); + ED_outliner_select_sync_from_object_tag(C); } return OPERATOR_FINISHED; diff --git a/source/blender/editors/armature/pose_group.c b/source/blender/editors/armature/pose_group.c index 9cd87f476d4..c10e204e3a4 100644 --- a/source/blender/editors/armature/pose_group.c +++ b/source/blender/editors/armature/pose_group.c @@ -44,6 +44,7 @@ #include "WM_types.h" #include "ED_armature.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "UI_interface.h" @@ -484,6 +485,7 @@ static int pose_group_select_exec(bContext *C, wmOperator *UNUSED(op)) bArmature *arm = ob->data; DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + ED_outliner_select_sync_from_pose_bone_tag(C); return OPERATOR_FINISHED; } @@ -518,6 +520,7 @@ static int pose_group_deselect_exec(bContext *C, wmOperator *UNUSED(op)) bArmature *arm = ob->data; DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + ED_outliner_select_sync_from_pose_bone_tag(C); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index 41ad5433931..ce652b0eaf4 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -454,22 +454,22 @@ static int pose_select_connected_invoke(bContext *C, wmOperator *op, const wmEve return OPERATOR_FINISHED; } -static bool pose_select_linked_poll(bContext *C) +static bool pose_select_linked_pick_poll(bContext *C) { return (ED_operator_view3d_active(C) && ED_operator_posemode(C)); } -void POSE_OT_select_linked(wmOperatorType *ot) +void POSE_OT_select_linked_pick(wmOperatorType *ot) { /* identifiers */ ot->name = "Select Connected"; - ot->idname = "POSE_OT_select_linked"; - ot->description = "Select bones related to selected ones by parent/child relationships"; + ot->idname = "POSE_OT_select_linked_pick"; + ot->description = "Select bones linked by parent/child connections under the mouse cursor"; /* callbacks */ /* leave 'exec' unset */ ot->invoke = pose_select_connected_invoke; - ot->poll = pose_select_linked_poll; + ot->poll = pose_select_linked_pick_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -482,6 +482,62 @@ void POSE_OT_select_linked(wmOperatorType *ot) "Extend selection instead of deselecting everything first"); } +static int pose_select_linked_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Bone *curBone, *next = NULL; + + CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) { + if ((pchan->bone->flag & BONE_SELECTED) == 0) { + continue; + } + + bArmature *arm = ob->data; + + /* Select parents */ + for (curBone = pchan->bone; curBone; curBone = next) { + if (PBONE_SELECTABLE(arm, curBone)) { + curBone->flag |= BONE_SELECTED; + + if (curBone->flag & BONE_CONNECTED) { + next = curBone->parent; + } + else { + next = NULL; + } + } + else { + next = NULL; + } + } + + /* Select children */ + for (curBone = pchan->bone->childbase.first; curBone; curBone = curBone->next) { + selectconnected_posebonechildren(ob, curBone, false); + } + ED_pose_bone_select_tag_update(ob); + } + CTX_DATA_END; + + ED_outliner_select_sync_from_pose_bone_tag(C); + + return OPERATOR_FINISHED; +} + +void POSE_OT_select_linked(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Connected"; + ot->idname = "POSE_OT_select_linked"; + ot->description = "Select all bones linked by parent/child connections to the current selection"; + + /* callbacks */ + ot->exec = pose_select_linked_exec; + ot->poll = ED_operator_posemode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* -------------------------------------- */ static int pose_de_select_all_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index c0e5e11ac22..d6256f67066 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -58,6 +58,7 @@ #include "ED_curve.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_transform.h" #include "ED_transform_snap_object_context.h" @@ -1500,6 +1501,8 @@ static int separate_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + ED_outliner_select_sync_from_object_tag(C); + return OPERATOR_FINISHED; } @@ -3589,6 +3592,7 @@ static void subdividenurb(Object *obedit, View3D *v3d, int number_cuts) memcpy(bpn, nextbp, sizeof(BPoint)); interp_v4_v4v4(bpn->vec, bp->vec, nextbp->vec, factor); + bpn->radius = interpf(bp->radius, nextbp->radius, factor); bpn++; } } diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c index adaf4ab2459..86423d907b5 100644 --- a/source/blender/editors/gpencil/annotate_draw.c +++ b/source/blender/editors/gpencil/annotate_draw.c @@ -524,131 +524,6 @@ static void annotation_draw_strokes(const bGPDframe *gpf, GPU_program_point_size(false); } -/* Draw selected verts for strokes being edited */ -static void annotation_draw_strokes_edit(bGPDlayer *gpl, - const bGPDframe *gpf, - int offsx, - int offsy, - int winx, - int winy, - short dflag, - float alpha) -{ - /* if alpha 0 do not draw */ - if (alpha == 0.0f) { - return; - } - - const bool no_xray = (dflag & GP_DRAWDATA_NO_XRAY) != 0; - int mask_orig = 0; - - /* set up depth masks... */ - if (dflag & GP_DRAWDATA_ONLY3D) { - if (no_xray) { - glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig); - glDepthMask(0); - GPU_depth_test(true); - - /* first arg is normally rv3d->dist, but this isn't - * available here and seems to work quite well without */ - bglPolygonOffset(1.0f, 1.0f); - } - } - - GPU_program_point_size(true); - - /* draw stroke verts */ - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - /* check if stroke can be drawn */ - if (annotation_can_draw_stroke(gps, dflag) == false) { - continue; - } - - /* Optimization: only draw points for selected strokes - * We assume that selected points can only occur in - * strokes that are selected too. - */ - if ((gps->flag & GP_STROKE_SELECT) == 0) { - continue; - } - - /* Get size of verts: - * - The selected state needs to be larger than the unselected state so that - * they stand out more. - * - We use the theme setting for size of the unselected verts - */ - float bsize = UI_GetThemeValuef(TH_GP_VERTEX_SIZE); - float vsize; - if ((int)bsize > 8) { - vsize = 10.0f; - bsize = 8.0f; - } - else { - vsize = bsize + 2; - } - - /* Why? */ - UNUSED_VARS(vsize); - - float selectColor[4]; - UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, selectColor); - selectColor[3] = alpha; - - GPUVertFormat *format = immVertexFormat(); - uint pos; /* specified later */ - uint size = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - - if (gps->flag & GP_STROKE_3DSPACE) { - pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR); - } - else { - pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_POINT_VARYING_SIZE_VARYING_COLOR); - } - - immBegin(GPU_PRIM_POINTS, gps->totpoints); - - /* Draw all the stroke points (selected or not) */ - bGPDspoint *pt = gps->points; - for (int i = 0; i < gps->totpoints; i++, pt++) { - /* size and color first */ - immAttr3fv(color, gpl->color); - immAttr1f(size, bsize); - - /* then position */ - if (gps->flag & GP_STROKE_3DSPACE) { - immVertex3fv(pos, &pt->x); - } - else { - float co[2]; - annotation_calc_2d_stroke_fxy(&pt->x, gps->flag, offsx, offsy, winx, winy, co); - immVertex2fv(pos, co); - } - } - - immEnd(); - immUnbindProgram(); - } - - GPU_program_point_size(false); - - /* clear depth mask */ - if (dflag & GP_DRAWDATA_ONLY3D) { - if (no_xray) { - glDepthMask(mask_orig); - GPU_depth_test(false); - - bglPolygonOffset(0.0, 0.0); -#if 0 - glDisable(GL_POLYGON_OFFSET_LINE); - glPolygonOffset(0, 0); -#endif - } - } -} - /* ----- General Drawing ------ */ /* draw onion-skinning for a layer */ static void annotation_draw_onionskins( @@ -724,7 +599,7 @@ static void annotation_draw_onionskins( /* loop over gpencil data layers, drawing them */ static void annotation_draw_data_layers( - bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag, float alpha) + bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) { float ink[4]; @@ -767,21 +642,6 @@ static void annotation_draw_data_layers( /* draw the strokes already in active frame */ annotation_draw_strokes(gpf, offsx, offsy, winx, winy, dflag, lthick, ink); - /* Draw verts of selected strokes: - * - when doing OpenGL renders, we don't want to be showing these, as that ends up - * flickering - * - locked layers can't be edited, so there's no point showing these verts - * as they will have no bearings on what gets edited - * - only show when in editmode, since operators shouldn't work otherwise - * (NOTE: doing it this way means that the toggling editmode - * shows visible change immediately). - */ - /* XXX: perhaps we don't want to show these when users are drawing... */ - if ((G.f & G_FLAG_RENDER_VIEWPORT) == 0 && (gpl->flag & GP_LAYER_LOCKED) == 0 && - (gpd->flag & GP_DATA_STROKE_EDITMODE)) { - annotation_draw_strokes_edit(gpl, gpf, offsx, offsy, winx, winy, dflag, alpha); - } - /* Check if may need to draw the active stroke cache, only if this layer is the active layer * that is being edited. (Stroke buffer is currently stored in gp-data) */ @@ -803,57 +663,9 @@ static void annotation_draw_data_layers( } } -/* draw a short status message in the top-right corner */ -static void annotation_draw_status_text(const bGPdata *gpd, ARegion *region) -{ - - /* Cannot draw any status text when drawing OpenGL Renders */ - if (G.f & G_FLAG_RENDER_VIEWPORT) { - return; - } - - /* Get bounds of region - Necessary to avoid problems with region overlap */ - const rcti *rect = ED_region_visible_rect(region); - - /* for now, this should only be used to indicate when we are in stroke editmode */ - if (gpd->flag & GP_DATA_STROKE_EDITMODE) { - const char *printable = IFACE_("GPencil Stroke Editing"); - float printable_size[2]; - - int font_id = BLF_default(); - - BLF_width_and_height( - font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]); - - int xco = (rect->xmax - U.widget_unit) - (int)printable_size[0]; - int yco = (rect->ymax - U.widget_unit); - - /* text label */ - UI_FontThemeColor(font_id, TH_TEXT_HI); -#ifdef WITH_INTERNATIONAL - BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); -#else - BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); -#endif - - /* grease pencil icon... */ - // XXX: is this too intrusive? - GPU_blend_set_func_separate( - GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - GPU_blend(true); - - xco -= U.widget_unit; - yco -= (int)printable_size[1] / 2; - - UI_icon_draw(xco, yco, ICON_GREASEPENCIL); - - GPU_blend(false); - } -} - /* draw grease-pencil datablock */ static void annotation_draw_data( - bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag, float alpha) + bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) { /* turn on smooth lines (i.e. anti-aliasing) */ GPU_line_smooth(true); @@ -864,7 +676,7 @@ static void annotation_draw_data( GPU_blend(true); /* draw! */ - annotation_draw_data_layers(gpd, offsx, offsy, winx, winy, cfra, dflag, alpha); + annotation_draw_data_layers(gpd, offsx, offsy, winx, winy, cfra, dflag); /* turn off alpha blending, then smooth lines */ GPU_blend(false); // alpha blending @@ -884,7 +696,6 @@ static void annotation_draw_data_all(Scene *scene, const char spacetype) { bGPdata *gpd_source = NULL; - float alpha = 1.0f; if (scene) { if (spacetype == SPACE_VIEW3D) { @@ -897,14 +708,14 @@ static void annotation_draw_data_all(Scene *scene, } if (gpd_source) { - annotation_draw_data(gpd_source, offsx, offsy, winx, winy, cfra, dflag, alpha); + annotation_draw_data(gpd_source, offsx, offsy, winx, winy, cfra, dflag); } } /* scene/clip data has already been drawn, only object/track data is drawn here * if gpd_source == gpd, we don't have any object/track data and we can skip */ if (gpd_source == NULL || (gpd_source && gpd_source != gpd)) { - annotation_draw_data(gpd, offsx, offsy, winx, winy, cfra, dflag, alpha); + annotation_draw_data(gpd, offsx, offsy, winx, winy, cfra, dflag); } } @@ -1026,11 +837,6 @@ void ED_annotation_draw_view2d(const bContext *C, bool onlyv2d) annotation_draw_data_all( scene, gpd, 0, 0, region->winx, region->winy, CFRA, dflag, area->spacetype); - - /* draw status text (if in screen/pixel-space) */ - if (!onlyv2d) { - annotation_draw_status_text(gpd, region); - } } /* draw annotations sketches to specified 3d-view assuming that matrices are already set diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index c05162510d7..86b08880d57 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -55,7 +55,7 @@ #include "BKE_brush.h" #include "BKE_context.h" #include "BKE_deform.h" -#include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_gpencil.h" #include "BKE_gpencil_modifier.h" #include "BKE_lib_id.h" @@ -3193,7 +3193,7 @@ void GPENCIL_OT_material_unlock_all(wmOperatorType *ot) /* ***************** Select all strokes using color ************************ */ -static int gpencil_select_material_exec(bContext *C, wmOperator *op) +static int gpencil_material_select_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); Object *ob = CTX_data_active_object(C); @@ -3263,15 +3263,15 @@ static int gpencil_select_material_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void GPENCIL_OT_select_material(wmOperatorType *ot) +void GPENCIL_OT_material_select(wmOperatorType *ot) { /* identifiers */ ot->name = "Select Material"; - ot->idname = "GPENCIL_OT_select_material"; + ot->idname = "GPENCIL_OT_material_select"; ot->description = "Select/Deselect all Grease Pencil strokes using current material"; /* callbacks */ - ot->exec = gpencil_select_material_exec; + ot->exec = gpencil_material_select_exec; ot->poll = gpencil_active_material_poll; /* flags */ @@ -3282,6 +3282,48 @@ void GPENCIL_OT_select_material(wmOperatorType *ot) RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE); } +/* ***************** Set active material ************************* */ +static int gpencil_material_set_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + int slot = RNA_enum_get(op->ptr, "slot"); + + /* Try to get material */ + if ((slot < 1) || (slot > ob->totcol)) { + BKE_reportf( + op->reports, RPT_ERROR, "Cannot change to non-existent material (index = %d)", slot); + return OPERATOR_CANCELLED; + } + + /* Set active material. */ + ob->actcol = slot; + + /* updates */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_material_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Material"; + ot->idname = "GPENCIL_OT_material_set"; + ot->description = "Set active material"; + + /* callbacks */ + ot->exec = gpencil_material_set_exec; + ot->poll = gpencil_active_material_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Material to use (dynamic enum) */ + ot->prop = RNA_def_enum(ot->srna, "slot", DummyRNA_DEFAULT_items, 0, "Material Slot", ""); + RNA_def_enum_funcs(ot->prop, ED_gpencil_material_enum_itemf); +} + /* ***************** Set selected stroke material the active material ************************ */ static int gpencil_set_active_material_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 947da9e7877..4a4ade4e98f 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -77,6 +77,7 @@ #include "ED_gpencil.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_select_utils.h" #include "ED_space_api.h" @@ -4365,6 +4366,7 @@ static int gp_stroke_separate_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, NULL); + ED_outliner_select_sync_from_object_tag(C); return OPERATOR_FINISHED; } @@ -4545,8 +4547,8 @@ void GPENCIL_OT_stroke_smooth(wmOperatorType *ot) /* smart stroke cutter for trimming stroke ends */ struct GP_SelectLassoUserData { rcti rect; - const int (*mcords)[2]; - int mcords_len; + const int (*mcoords)[2]; + int mcoords_len; }; static bool gpencil_test_lasso(bGPDstroke *gps, @@ -4562,7 +4564,7 @@ static bool gpencil_test_lasso(bGPDstroke *gps, gp_point_to_xy(gsc, gps, &pt2, &x0, &y0); /* test if in lasso */ return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&data->rect, x0, y0) && - BLI_lasso_is_point_inside(data->mcords, data->mcords_len, x0, y0, INT_MAX)); + BLI_lasso_is_point_inside(data->mcoords, data->mcoords_len, x0, y0, INT_MAX)); } typedef bool (*GPencilTestFn)(bGPDstroke *gps, @@ -4740,19 +4742,19 @@ static int gpencil_cutter_exec(bContext *C, wmOperator *op) } struct GP_SelectLassoUserData data = {0}; - data.mcords = WM_gesture_lasso_path_to_array(C, op, &data.mcords_len); + data.mcoords = WM_gesture_lasso_path_to_array(C, op, &data.mcoords_len); /* Sanity check. */ - if (data.mcords == NULL) { + if (data.mcoords == NULL) { return OPERATOR_PASS_THROUGH; } /* Compute boundbox of lasso (for faster testing later). */ - BLI_lasso_boundbox(&data.rect, data.mcords, data.mcords_len); + BLI_lasso_boundbox(&data.rect, data.mcoords, data.mcoords_len); gpencil_cutter_lasso_select(C, op, gpencil_test_lasso, &data); - MEM_freeN((void *)data.mcords); + MEM_freeN((void *)data.mcoords); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index c5e5a0b79ef..19b53d0662d 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -345,6 +345,10 @@ const struct EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(struct bCon struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); +const struct EnumPropertyItem *ED_gpencil_material_enum_itemf(struct bContext *C, + struct PointerRNA *ptr, + struct PropertyRNA *prop, + bool *r_free); /* ***************************************************** */ /* Operator Defines */ @@ -550,7 +554,8 @@ void GPENCIL_OT_material_reveal(struct wmOperatorType *ot); void GPENCIL_OT_material_lock_all(struct wmOperatorType *ot); void GPENCIL_OT_material_unlock_all(struct wmOperatorType *ot); void GPENCIL_OT_material_lock_unused(struct wmOperatorType *ot); -void GPENCIL_OT_select_material(struct wmOperatorType *ot); +void GPENCIL_OT_material_select(struct wmOperatorType *ot); +void GPENCIL_OT_material_set(struct wmOperatorType *ot); void GPENCIL_OT_set_active_material(struct wmOperatorType *ot); /* convert old 2.7 files to 2.8 */ diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 0171a81f5eb..94c86572fd3 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -653,7 +653,8 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_material_reveal); WM_operatortype_append(GPENCIL_OT_material_lock_all); WM_operatortype_append(GPENCIL_OT_material_unlock_all); - WM_operatortype_append(GPENCIL_OT_select_material); + WM_operatortype_append(GPENCIL_OT_material_select); + WM_operatortype_append(GPENCIL_OT_material_set); /* Editing (Time) --------------- */ diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index bd7dfecf692..961c4e05a28 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -515,7 +515,6 @@ static void gp_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const floa float mvec[2]; float sen = brush->gpencil_settings->draw_angle_factor; /* sensitivity */ float fac; - float mpressure; /* default angle of brush in radians */ float angle = brush->gpencil_settings->draw_angle; @@ -543,9 +542,7 @@ static void gp_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const floa fac = 1.0f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */ /* interpolate with previous point for smoother transitions */ - mpressure = interpf(pt->pressure - (sen * fac), (pt - 1)->pressure, 0.3f); - pt->pressure = mpressure; - + pt->pressure = interpf(pt->pressure - (sen * fac), (pt - 1)->pressure, 0.3f); CLAMP(pt->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f); } } @@ -3151,7 +3148,6 @@ static void gp_brush_angle_segment(tGPsdata *p, tGPspoint *pt_prev, tGPspoint *p float mvec[2]; float fac; - float mpressure; /* angle vector of the brush with full thickness */ float v0[2] = {cos(angle), sin(angle)}; @@ -3159,13 +3155,10 @@ static void gp_brush_angle_segment(tGPsdata *p, tGPspoint *pt_prev, tGPspoint *p mvec[0] = pt->x - pt_prev->x; mvec[1] = pt->y - pt_prev->y; normalize_v2(mvec); - fac = 1.0f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */ /* interpolate with previous point for smoother transitions */ - mpressure = interpf(pt->pressure - (sen * fac), pt_prev->pressure, 0.3f); - pt->pressure = mpressure; - - CLAMP(pt->pressure, pt_prev->pressure * 0.5f, 1.0f); + pt->pressure = interpf(pt->pressure - (sen * fac), pt_prev->pressure, 0.3f); + CLAMP(pt->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f); } /* Add arc points between two mouse events using the previous segment to determine the vertice of @@ -3241,6 +3234,7 @@ static void gpencil_add_arc_points(tGPsdata *p, float mval[2], int segments) corner[0] = midpoint[0] - (cp1[0] - midpoint[0]); corner[1] = midpoint[1] - (cp1[1] - midpoint[1]); + tGPspoint *pt_step = pt_prev; for (int i = 0; i < segments; i++) { pt = &points[idx_prev + i - 1]; pt->x = corner[0] + (end[0] - corner[0]) * sinf(a) + (start[0] - corner[0]) * cosf(a); @@ -3250,9 +3244,12 @@ static void gpencil_add_arc_points(tGPsdata *p, float mval[2], int segments) pt->pressure = pt_prev->pressure; pt->strength = pt_prev->strength; - /* Apply angle of stroke to brush size. */ + /* Apply angle of stroke to brush size to interpolated points but slightly attenuated.. */ if (brush_settings->draw_angle_factor != 0.0f) { - gp_brush_angle_segment(p, pt_prev, pt); + gp_brush_angle_segment(p, pt_step, pt); + CLAMP(pt->pressure, pt_prev->pressure * 0.5f, 1.0f); + /* Use the previous interpolated point for next segment. */ + pt_step = pt; } /* Apply randomness to pressure. */ diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index e25576f32aa..69d22b52ded 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -1327,8 +1327,8 @@ void GPENCIL_OT_select_box(wmOperatorType *ot) struct GP_SelectLassoUserData { rcti rect; - const int (*mcords)[2]; - int mcords_len; + const int (*mcoords)[2]; + int mcoords_len; }; static bool gpencil_test_lasso(bGPDstroke *gps, @@ -1344,25 +1344,25 @@ static bool gpencil_test_lasso(bGPDstroke *gps, gp_point_to_xy(gsc, gps, &pt2, &x0, &y0); /* test if in lasso boundbox + within the lasso noose */ return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&data->rect, x0, y0) && - BLI_lasso_is_point_inside(data->mcords, data->mcords_len, x0, y0, INT_MAX)); + BLI_lasso_is_point_inside(data->mcoords, data->mcoords_len, x0, y0, INT_MAX)); } static int gpencil_lasso_select_exec(bContext *C, wmOperator *op) { struct GP_SelectLassoUserData data = {0}; - data.mcords = WM_gesture_lasso_path_to_array(C, op, &data.mcords_len); + data.mcoords = WM_gesture_lasso_path_to_array(C, op, &data.mcoords_len); /* Sanity check. */ - if (data.mcords == NULL) { + if (data.mcoords == NULL) { return OPERATOR_PASS_THROUGH; } /* Compute boundbox of lasso (for faster testing later). */ - BLI_lasso_boundbox(&data.rect, data.mcords, data.mcords_len); + BLI_lasso_boundbox(&data.rect, data.mcoords, data.mcoords_len); int ret = gpencil_generic_select_exec(C, op, gpencil_test_lasso, &data); - MEM_freeN((void *)data.mcords); + MEM_freeN((void *)data.mcoords); return ret; } diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 3d571773bc8..6a7bb2012a7 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -481,6 +481,40 @@ const EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(bContext *C, return item; } +/* Just existing Materials */ +const EnumPropertyItem *ED_gpencil_material_enum_itemf(bContext *C, + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + Object *ob = CTX_data_active_object(C); + EnumPropertyItem *item = NULL, item_tmp = {0}; + int totitem = 0; + int i = 0; + + if (ELEM(NULL, C, ob)) { + return DummyRNA_DEFAULT_items; + } + + /* Existing materials */ + for (i = 1; i <= ob->totcol; i++) { + Material *ma = BKE_object_material_get(ob, i); + if (ma) { + item_tmp.identifier = ma->id.name + 2; + item_tmp.name = ma->id.name + 2; + item_tmp.value = i; + item_tmp.icon = ma->preview->icon_id; + + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} + /* ******************************************************** */ /* Brush Tool Core */ diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index 910cf362a37..a8476e3d1ca 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -69,7 +69,7 @@ void ED_space_image_get_size(struct SpaceImage *sima, int *r_width, int *r_heigh void ED_space_image_get_size_fl(struct SpaceImage *sima, float r_size[2]); void ED_space_image_get_aspect(struct SpaceImage *sima, float *r_aspx, float *r_aspy); void ED_space_image_get_zoom(struct SpaceImage *sima, - struct ARegion *region, + const struct ARegion *region, float *r_zoomx, float *r_zoomy); void ED_space_image_get_uv_aspect(struct SpaceImage *sima, float *r_aspx, float *r_aspy); @@ -88,14 +88,18 @@ void ED_image_get_uv_aspect(struct Image *ima, float *r_aspx, float *r_aspy); void ED_image_mouse_pos(struct SpaceImage *sima, - struct ARegion *region, + const struct ARegion *region, const int mval[2], float co[2]); void ED_image_view_center_to_point(struct SpaceImage *sima, float x, float y); -void ED_image_point_pos( - struct SpaceImage *sima, struct ARegion *region, float x, float y, float *r_x, float *r_y); +void ED_image_point_pos(struct SpaceImage *sima, + const struct ARegion *region, + float x, + float y, + float *r_x, + float *r_y); void ED_image_point_pos__reverse(struct SpaceImage *sima, - struct ARegion *region, + const struct ARegion *region, const float co[2], float r_co[2]); bool ED_image_slot_cycle(struct Image *image, int direction); diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h index 3ae0c254000..3ae864721e8 100644 --- a/source/blender/editors/include/ED_keyframes_edit.h +++ b/source/blender/editors/include/ED_keyframes_edit.h @@ -106,8 +106,8 @@ typedef enum eEditKeyframes_Mirror { typedef struct KeyframeEdit_LassoData { rctf *rectf_scaled; const rctf *rectf_view; - const int (*mcords)[2]; - int mcords_tot; + const int (*mcoords)[2]; + int mcoords_len; } KeyframeEdit_LassoData; /* use with BEZT_OK_REGION_CIRCLE */ diff --git a/source/blender/editors/include/ED_outliner.h b/source/blender/editors/include/ED_outliner.h index be1ee786a75..d3fc5174dd9 100644 --- a/source/blender/editors/include/ED_outliner.h +++ b/source/blender/editors/include/ED_outliner.h @@ -27,6 +27,7 @@ extern "C" { #endif +struct Base; struct ListBase; struct bContext; @@ -34,7 +35,7 @@ bool ED_outliner_collections_editor_poll(struct bContext *C); void ED_outliner_selected_objects_get(const struct bContext *C, struct ListBase *objects); -Base *ED_outliner_give_base_under_cursor(struct bContext *C, const int mval[2]); +struct Base *ED_outliner_give_base_under_cursor(struct bContext *C, const int mval[2]); void ED_outliner_select_sync_from_object_tag(struct bContext *C); void ED_outliner_select_sync_from_edit_bone_tag(struct bContext *C); diff --git a/source/blender/editors/include/ED_particle.h b/source/blender/editors/include/ED_particle.h index c8a4dc5b49d..6959cac7efe 100644 --- a/source/blender/editors/include/ED_particle.h +++ b/source/blender/editors/include/ED_particle.h @@ -71,7 +71,7 @@ bool PE_mouse_particles( bool PE_box_select(struct bContext *C, const struct rcti *rect, const int sel_op); bool PE_circle_select(struct bContext *C, const int sel_op, const int mval[2], float rad); int PE_lasso_select(struct bContext *C, - const int mcords[][2], + const int mcoords[][2], const short moves, const int sel_op); bool PE_deselect_all_visible_ex(struct PTCacheEdit *edit); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 709e05c18b6..f4dd2da06fc 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -1672,6 +1672,7 @@ void UI_panel_end(const struct ScrArea *area, void UI_panels_scale(struct ARegion *region, float new_width); void UI_panel_label_offset(struct uiBlock *block, int *r_x, int *r_y); int UI_panel_size_y(const struct Panel *panel); +bool UI_panel_is_dragging(const struct Panel *panel); bool UI_panel_category_is_visible(const struct ARegion *region); void UI_panel_category_add(struct ARegion *region, const char *name); diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index d4db1b14074..ffc06e94a90 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -137,9 +137,9 @@ 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(struct View2D *v2d, float step); +void UI_view2d_constant_grid_draw(const struct View2D *v2d, float step); void UI_view2d_multi_grid_draw( - struct View2D *v2d, int colorid, float step, int level_size, int totlevels); + 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); @@ -209,14 +209,17 @@ bool UI_view2d_view_to_region_clip( const struct View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) ATTR_NONNULL(); void UI_view2d_view_to_region( - struct View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) ATTR_NONNULL(); -void UI_view2d_view_to_region_fl( - struct View2D *v2d, float x, float y, float *r_region_x, float *r_region_y) ATTR_NONNULL(); -void UI_view2d_view_to_region_m4(struct View2D *v2d, float matrix[4][4]) ATTR_NONNULL(); -void UI_view2d_view_to_region_rcti(struct View2D *v2d, + const struct View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) ATTR_NONNULL(); +void UI_view2d_view_to_region_fl(const struct View2D *v2d, + float x, + float y, + float *r_region_x, + float *r_region_y) ATTR_NONNULL(); +void UI_view2d_view_to_region_m4(const struct View2D *v2d, float matrix[4][4]) ATTR_NONNULL(); +void UI_view2d_view_to_region_rcti(const struct View2D *v2d, const struct rctf *rect_src, struct rcti *rect_dst) ATTR_NONNULL(); -bool UI_view2d_view_to_region_rcti_clip(struct View2D *v2d, +bool UI_view2d_view_to_region_rcti_clip(const struct View2D *v2d, const struct rctf *rect_src, struct rcti *rect_dst) ATTR_NONNULL(); @@ -228,9 +231,9 @@ void UI_view2d_scroller_size_get(const struct View2D *v2d, float *r_x, float *r_ void UI_view2d_scale_get(struct View2D *v2d, float *r_x, float *r_y); float UI_view2d_scale_get_x(const struct View2D *v2d); float UI_view2d_scale_get_y(const struct View2D *v2d); -void UI_view2d_scale_get_inverse(struct View2D *v2d, float *r_x, float *r_y); +void UI_view2d_scale_get_inverse(const struct View2D *v2d, float *r_x, float *r_y); -void UI_view2d_center_get(struct View2D *v2d, float *r_x, float *r_y); +void UI_view2d_center_get(const struct View2D *v2d, float *r_x, float *r_y); void UI_view2d_center_set(struct View2D *v2d, float x, float y); void UI_view2d_offset(struct View2D *v2d, float xfac, float yfac); diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c index ed591335660..8c9768f523d 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.c @@ -35,6 +35,7 @@ #include "BKE_context.h" #include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_nla.h" diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 9a7e189406c..84be109b38e 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2848,6 +2848,23 @@ static bool ui_textedit_delete_selection(uiBut *but, uiHandleButtonData *data) return changed; } +static bool ui_textedit_set_cursor_pos_foreach_glyph(const char *UNUSED(str), + const size_t str_step_ofs, + const rcti *glyph_step_bounds, + const int UNUSED(glyph_advance_x), + const rctf *glyph_bounds, + const int UNUSED(glyph_bearing[2]), + void *user_data) +{ + int *cursor_data = user_data; + float center = glyph_step_bounds->xmin + (BLI_rctf_size_x(glyph_bounds) / 2.0f); + if (cursor_data[0] < center) { + cursor_data[1] = str_step_ofs; + return false; + } + return true; +} + /** * \param x: Screen space cursor location - #wmEvent.x * @@ -2883,8 +2900,7 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con startx += UI_DPI_ICON_SIZE / aspect; } } - /* But this extra .05 makes clicks in between characters feel nicer. */ - startx += ((UI_TEXT_MARGIN_X + 0.05f) * U.widget_unit) / aspect; + startx += (UI_TEXT_MARGIN_X * U.widget_unit) / aspect; /* mouse dragged outside the widget to the left */ if (x < startx) { @@ -2907,48 +2923,24 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con but->pos = but->ofs; } /* mouse inside the widget, mouse coords mapped in widget space */ - else { /* (x >= startx) */ - int pos_i; - - /* keep track of previous distance from the cursor to the char */ - float cdist, cdist_prev = 0.0f; - short pos_prev; - - str_last = &str[strlen(str)]; - - but->pos = pos_prev = ((str_last - str) - but->ofs); - - while (true) { - cdist = startx + BLF_width(fstyle.uifont_id, str + but->ofs, (str_last - str) - but->ofs); - - /* check if position is found */ - if (cdist < x) { - /* check is previous location was in fact closer */ - if ((x - cdist) > (cdist_prev - x)) { - but->pos = pos_prev; - } - break; - } - cdist_prev = cdist; - pos_prev = but->pos; - /* done with tricky distance checks */ - - pos_i = but->pos; - if (but->pos <= 0) { - break; - } - if (BLI_str_cursor_step_prev_utf8(str + but->ofs, but->ofs, &pos_i)) { - but->pos = pos_i; - str_last = &str[but->pos + but->ofs]; - } - else { - break; /* unlikely but possible */ - } - } - but->pos += but->ofs; - if (but->pos < 0) { - but->pos = 0; - } + else { + str_last = &str[but->ofs]; + const int str_last_len = strlen(str_last); + int x_pos = (int)(x - startx); + int glyph_data[2] = { + x_pos, /* horizontal position to test. */ + -1, /* Write the character offset here. */ + }; + BLF_boundbox_foreach_glyph(fstyle.uifont_id, + str + but->ofs, + INT_MAX, + ui_textedit_set_cursor_pos_foreach_glyph, + glyph_data); + /* If value untouched then we are to the right. */ + if (glyph_data[1] == -1) { + glyph_data[1] = str_last_len; + } + but->pos = glyph_data[1] + but->ofs; } if (fstyle.kerning == 1) { diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index f824670cc57..04179721305 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -102,6 +102,7 @@ typedef struct uiHandlePanelData { double starttime; /* dragging */ + bool is_drag_drop; int startx, starty; int startofsx, startofsy; int startsizex, startsizey; @@ -876,6 +877,16 @@ static int get_panel_real_ofsx(Panel *panel) } } +bool UI_panel_is_dragging(const struct Panel *panel) +{ + uiHandlePanelData *data = panel->activedata; + if (!data) { + return false; + } + + return data->is_drag_drop; +} + typedef struct PanelSort { Panel *panel, *orig; } PanelSort; @@ -2486,6 +2497,8 @@ static void panel_activate_state(const bContext *C, Panel *panel, uiHandlePanelS return; } + bool was_drag_drop = (data && data->state == PANEL_STATE_DRAG); + if (state == PANEL_STATE_EXIT || state == PANEL_STATE_ANIMATION) { if (data && data->state != PANEL_STATE_ANIMATION) { /* XXX: @@ -2537,6 +2550,12 @@ static void panel_activate_state(const bContext *C, Panel *panel, uiHandlePanelS data->startsizex = panel->sizex; data->startsizey = panel->sizey; data->starttime = PIL_check_seconds_timer(); + + /* Remember drag drop state even when animating to the aligned position after dragging. */ + data->is_drag_drop = was_drag_drop; + if (state == PANEL_STATE_DRAG) { + data->is_drag_drop = true; + } } ED_region_tag_redraw(region); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 1df42659bf8..2d1b3f560f7 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -1843,20 +1843,26 @@ static void modifiers_convertToReal(bContext *C, void *ob_v, void *md_v) ED_undo_push(C, "Modifier convert to real"); } -static int modifier_can_delete(ModifierData *md) +static bool modifier_can_delete(ModifierData *md) { /* fluid particle modifier can't be deleted here */ if (md->type == eModifierType_ParticleSystem) { short particle_type = ((ParticleSystemModifierData *)md)->psys->part->type; - if (particle_type == PART_FLUID || particle_type == PART_FLUID_FLIP || - particle_type == PART_FLUID_FOAM || particle_type == PART_FLUID_SPRAY || - particle_type == PART_FLUID_BUBBLE || particle_type == PART_FLUID_TRACER || - particle_type == PART_FLUID_SPRAYFOAM || particle_type == PART_FLUID_SPRAYBUBBLE || - particle_type == PART_FLUID_FOAMBUBBLE || particle_type == PART_FLUID_SPRAYFOAMBUBBLE) { - return 0; + if (ELEM(particle_type, + PART_FLUID, + PART_FLUID_FLIP, + PART_FLUID_FOAM, + PART_FLUID_SPRAY, + PART_FLUID_BUBBLE, + PART_FLUID_TRACER, + PART_FLUID_SPRAYFOAM, + PART_FLUID_SPRAYBUBBLE, + PART_FLUID_FOAMBUBBLE, + PART_FLUID_SPRAYFOAMBUBBLE)) { + return false; } } - return 1; + return true; } /* Check whether Modifier is a simulation or not, @@ -7128,6 +7134,49 @@ static void menu_types_add_from_keymap_items(bContext *C, } } +static void menu_items_from_all_operators(bContext *C, struct MenuSearch_Data *data) +{ + /* Add to temporary list so we can sort them separately. */ + ListBase operator_items = {NULL, NULL}; + + MemArena *memarena = data->memarena; + GHashIterator iter; + for (WM_operatortype_iter(&iter); !BLI_ghashIterator_done(&iter); + BLI_ghashIterator_step(&iter)) { + wmOperatorType *ot = BLI_ghashIterator_getValue(&iter); + + if ((ot->flag & OPTYPE_INTERNAL) && (G.debug & G_DEBUG_WM) == 0) { + continue; + } + + if (WM_operator_poll((bContext *)C, ot)) { + const char *ot_ui_name = CTX_IFACE_(ot->translation_context, ot->name); + + struct MenuSearch_Item *item = NULL; + item = BLI_memarena_calloc(memarena, sizeof(*item)); + item->type = MENU_SEARCH_TYPE_OP; + + item->op.type = ot; + item->op.opcontext = WM_OP_EXEC_DEFAULT; + item->op.context = NULL; + + char idname_as_py[OP_MAX_TYPENAME]; + char uiname[256]; + WM_operator_py_idname(idname_as_py, ot->idname); + + SNPRINTF(uiname, "%s " MENU_SEP "%s", idname_as_py, ot_ui_name); + + item->drawwstr_full = strdup_memarena(memarena, uiname); + + BLI_addtail(&operator_items, item); + } + } + + BLI_listbase_sort(&operator_items, menu_item_sort_by_drawstr_full); + + BLI_movelisttolist(&data->items, &operator_items); +} + /** * Create #MenuSearch_Data by inspecting the current context, this uses two methods: * @@ -7441,6 +7490,10 @@ static struct MenuSearch_Data *menu_items_from_ui_create(bContext *C, data->memarena = memarena; + if (U.flag & USER_DEVELOPER_UI) { + menu_items_from_all_operators(C, data); + } + return data; } diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 0142f3b38e0..0d4eff2ba31 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -2095,15 +2095,18 @@ static void widget_draw_text_ime_underline(const uiFontStyle *fstyle, #endif /* WITH_INPUT_IME */ static bool widget_draw_text_underline_calc_center_x(const char *UNUSED(str), - const size_t str_ofs, - const rcti *glyph_bounds, - const int glyph_advance_x, + const size_t str_step_ofs, + const rcti *glyph_step_bounds, + const int UNUSED(glyph_advance_x), + const rctf *glyph_bounds, + const int glyph_bearing[2], void *user_data) { /* The index of the character to get, set to the x-position. */ int *ul_data = user_data; - if (ul_data[0] == (int)str_ofs) { - ul_data[1] = glyph_bounds->xmin + (glyph_advance_x / 2); + if (ul_data[0] == (int)str_step_ofs) { + ul_data[1] = glyph_step_bounds->xmin + glyph_bearing[0] + + (BLI_rctf_size_x(glyph_bounds) / 2.0f); /* Early exit. */ return false; } diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index 72f6535eadf..f8419ba3eba 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -1234,7 +1234,7 @@ void UI_view2d_view_restore(const bContext *C) * \{ */ /* Draw a constant grid in given 2d-region */ -void UI_view2d_constant_grid_draw(View2D *v2d, float step) +void UI_view2d_constant_grid_draw(const View2D *v2d, float step) { float start_x, start_y; int count_x, count_y; @@ -1306,7 +1306,8 @@ void UI_view2d_constant_grid_draw(View2D *v2d, float step) } /* Draw a multi-level grid in given 2d-region */ -void UI_view2d_multi_grid_draw(View2D *v2d, int colorid, float step, int level_size, int totlevels) +void UI_view2d_multi_grid_draw( + const View2D *v2d, int colorid, float step, int level_size, int totlevels) { /* Exit if there is nothing to draw */ if (totlevels == 0) { @@ -1777,7 +1778,8 @@ bool UI_view2d_view_to_region_clip( * \param x, y: Coordinates to convert. * \param r_region_x, r_region_y: Resultant coordinates. */ -void UI_view2d_view_to_region(View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) +void UI_view2d_view_to_region( + const View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) { /* step 1: express given coordinates as proportional values */ x = (x - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur); @@ -1793,7 +1795,7 @@ void UI_view2d_view_to_region(View2D *v2d, float x, float y, int *r_region_x, in } void UI_view2d_view_to_region_fl( - View2D *v2d, float x, float y, float *r_region_x, float *r_region_y) + const View2D *v2d, float x, float y, float *r_region_x, float *r_region_y) { /* express given coordinates as proportional values */ x = (x - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur); @@ -1804,7 +1806,7 @@ void UI_view2d_view_to_region_fl( *r_region_y = v2d->mask.ymin + (y * BLI_rcti_size_y(&v2d->mask)); } -void UI_view2d_view_to_region_rcti(View2D *v2d, const rctf *rect_src, rcti *rect_dst) +void UI_view2d_view_to_region_rcti(const View2D *v2d, const rctf *rect_src, rcti *rect_dst) { const float cur_size[2] = {BLI_rctf_size_x(&v2d->cur), BLI_rctf_size_y(&v2d->cur)}; const float mask_size[2] = {BLI_rcti_size_x(&v2d->mask), BLI_rcti_size_y(&v2d->mask)}; @@ -1825,7 +1827,7 @@ void UI_view2d_view_to_region_rcti(View2D *v2d, const rctf *rect_src, rcti *rect clamp_rctf_to_rcti(rect_dst, &rect_tmp); } -void UI_view2d_view_to_region_m4(View2D *v2d, float matrix[4][4]) +void UI_view2d_view_to_region_m4(const View2D *v2d, float matrix[4][4]) { rctf mask; unit_m4(matrix); @@ -1833,7 +1835,7 @@ void UI_view2d_view_to_region_m4(View2D *v2d, float matrix[4][4]) BLI_rctf_transform_calc_m4_pivot_min(&v2d->cur, &mask, matrix); } -bool UI_view2d_view_to_region_rcti_clip(View2D *v2d, const rctf *rect_src, rcti *rect_dst) +bool UI_view2d_view_to_region_rcti_clip(const View2D *v2d, const rctf *rect_src, rcti *rect_dst) { const float cur_size[2] = {BLI_rctf_size_x(&v2d->cur), BLI_rctf_size_y(&v2d->cur)}; const float mask_size[2] = {BLI_rcti_size_x(&v2d->mask), BLI_rcti_size_y(&v2d->mask)}; @@ -1959,7 +1961,7 @@ float UI_view2d_scale_get_y(const View2D *v2d) /** * Same as ``UI_view2d_scale_get() - 1.0f / x, y`` */ -void UI_view2d_scale_get_inverse(View2D *v2d, float *r_x, float *r_y) +void UI_view2d_scale_get_inverse(const View2D *v2d, float *r_x, float *r_y) { if (r_x) { *r_x = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask); @@ -1973,7 +1975,7 @@ void UI_view2d_scale_get_inverse(View2D *v2d, float *r_x, float *r_y) * Simple functions for consistent center offset access. * Used by node editor to shift view center for each individual node tree. */ -void UI_view2d_center_get(struct View2D *v2d, float *r_x, float *r_y) +void UI_view2d_center_get(const struct View2D *v2d, float *r_x, float *r_y) { /* get center */ if (r_x) { diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c index a6dece91eb0..416c1dc6e49 100644 --- a/source/blender/editors/mask/mask_select.c +++ b/source/blender/editors/mask/mask_select.c @@ -521,8 +521,8 @@ void MASK_OT_select_box(wmOperatorType *ot) * \{ */ static bool do_lasso_select_mask(bContext *C, - const int mcords[][2], - short moves, + const int mcoords[][2], + short mcoords_len, const eSelectOp sel_op) { ScrArea *area = CTX_wm_area(C); @@ -540,7 +540,7 @@ static bool do_lasso_select_mask(bContext *C, } /* get rectangle from operator */ - BLI_lasso_boundbox(&rect, mcords, moves); + BLI_lasso_boundbox(&rect, mcoords, mcoords_len); /* do actual selection */ LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { @@ -573,7 +573,7 @@ static bool do_lasso_select_mask(bContext *C, &screen_co[1]); if (BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) && - BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], INT_MAX)) { + BLI_lasso_is_point_inside(mcoords, mcoords_len, screen_co[0], screen_co[1], INT_MAX)) { BKE_mask_point_select_set(point, select); BKE_mask_point_select_set_handle(point, MASK_WHICH_HANDLE_BOTH, select); changed = true; @@ -594,14 +594,14 @@ static bool do_lasso_select_mask(bContext *C, static int clip_lasso_select_exec(bContext *C, wmOperator *op) { - int mcords_tot; - const int(*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot); + int mcoords_len; + const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len); - if (mcords) { + if (mcoords) { const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); - do_lasso_select_mask(C, mcords, mcords_tot, sel_op); + do_lasso_select_mask(C, mcoords, mcoords_len, sel_op); - MEM_freeN((void *)mcords); + MEM_freeN((void *)mcoords); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 37b0cd447b6..24c5cbb8573 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -69,6 +69,7 @@ #include "ED_mesh.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_transform.h" #include "ED_uvedit.h" @@ -4404,6 +4405,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) /* delay depsgraph recalc until all objects are duplicated */ DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); + ED_outliner_select_sync_from_object_tag(C); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 48ec41027ff..e4ecfa9c680 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -397,9 +397,7 @@ struct UMArrayData { UndoMesh *um; const UndoMesh *um_ref; /* can be NULL */ }; -static void um_arraystore_compact_cb(TaskPool *__restrict UNUSED(pool), - void *taskdata, - int UNUSED(threadid)) +static void um_arraystore_compact_cb(TaskPool *__restrict UNUSED(pool), void *taskdata) { struct UMArrayData *um_data = taskdata; um_arraystore_compact_with_info(um_data->um, um_data->um_ref); @@ -541,9 +539,7 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key) # ifdef USE_ARRAY_STORE_THREAD if (um_arraystore.task_pool == NULL) { - TaskScheduler *scheduler = BLI_task_scheduler_get(); - um_arraystore.task_pool = BLI_task_pool_create_background( - scheduler, NULL, TASK_PRIORITY_LOW); + um_arraystore.task_pool = BLI_task_pool_create_background(NULL, TASK_PRIORITY_LOW); } struct UMArrayData *um_data = MEM_mallocN(sizeof(*um_data), __func__); diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index de9cc72dfa4..fb273cf49a8 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -17,8 +17,8 @@ set(INC ../include - ../../blenkernel ../../blenfont + ../../blenkernel ../../blenlib ../../blentranslation ../../bmesh diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 9e0a6d51614..64abd01983c 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -2048,6 +2048,7 @@ static int object_duplicates_make_real_exec(bContext *C, wmOperator *op) DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_SCENE, scene); WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL); + ED_outliner_select_sync_from_object_tag(C); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index d8ba270073e..d7a7b4ca110 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -169,6 +169,8 @@ void OBJECT_OT_multires_subdivide(struct wmOperatorType *ot); void OBJECT_OT_multires_reshape(struct wmOperatorType *ot); void OBJECT_OT_multires_higher_levels_delete(struct wmOperatorType *ot); void OBJECT_OT_multires_base_apply(struct wmOperatorType *ot); +void OBJECT_OT_multires_unsubdivide(struct wmOperatorType *ot); +void OBJECT_OT_multires_rebuild_subdiv(struct wmOperatorType *ot); void OBJECT_OT_multires_external_save(struct wmOperatorType *ot); void OBJECT_OT_multires_external_pack(struct wmOperatorType *ot); void OBJECT_OT_correctivesmooth_bind(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 1b662ccc389..bcc5bf3e1ae 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -1433,6 +1433,25 @@ void OBJECT_OT_multires_higher_levels_delete(wmOperatorType *ot) /** \name Multires Subdivide Operator * \{ */ +static EnumPropertyItem prop_multires_subdivide_mode_type[] = { + {MULTIRES_SUBDIVIDE_CATMULL_CLARK, + "CATMULL_CLARK", + 0, + "Catmull-Clark", + "Create a new level using Catmull-Clark subdivisions"}, + {MULTIRES_SUBDIVIDE_SIMPLE, + "SIMPLE", + 0, + "Simple", + "Create a new level using simple subdivisions"}, + {MULTIRES_SUBDIVIDE_LINEAR, + "LINEAR", + 0, + "Linear", + "Create a new level using linear interpolation of the sculpted displacement"}, + {0, NULL, 0, NULL, NULL}, +}; + static int multires_subdivide_exec(bContext *C, wmOperator *op) { Object *object = ED_object_active_context(C); @@ -1443,7 +1462,9 @@ static int multires_subdivide_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - multiresModifier_subdivide(object, mmd); + const eMultiresSubdivideModeType subdivide_mode = (eMultiresSubdivideModeType)( + RNA_enum_get(op->ptr, "mode")); + multiresModifier_subdivide(object, mmd, subdivide_mode); ED_object_iter_other( CTX_data_main(C), object, true, ED_object_multires_update_totlevels_cb, &mmd->totlvl); @@ -1482,6 +1503,12 @@ void OBJECT_OT_multires_subdivide(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; edit_modifier_properties(ot); + RNA_def_enum(ot->srna, + "mode", + prop_multires_subdivide_mode_type, + MULTIRES_SUBDIVIDE_CATMULL_CLARK, + "Subdivision Mode", + "How the mesh is going to be subdivided to create a new level"); } /** \} */ @@ -1739,6 +1766,119 @@ void OBJECT_OT_multires_base_apply(wmOperatorType *ot) /** \} */ /* ------------------------------------------------------------------- */ +/** \name Multires Unsubdivide + * \{ */ + +static int multires_unsubdivide_exec(bContext *C, wmOperator *op) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Object *object = ED_object_active_context(C); + MultiresModifierData *mmd = (MultiresModifierData *)edit_modifier_property_get( + op, object, eModifierType_Multires); + + if (!mmd) { + return OPERATOR_CANCELLED; + } + + int new_levels = multiresModifier_rebuild_subdiv(depsgraph, object, mmd, 1, true); + if (new_levels == 0) { + BKE_report(op->reports, RPT_ERROR, "Not valid subdivisions found to rebuild a lower level"); + return OPERATOR_CANCELLED; + } + + DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object); + + return OPERATOR_FINISHED; +} + +static int multires_unsubdivide_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (edit_modifier_invoke_properties(C, op)) { + return multires_unsubdivide_exec(C, op); + } + else { + return OPERATOR_CANCELLED; + } +} + +void OBJECT_OT_multires_unsubdivide(wmOperatorType *ot) +{ + ot->name = "Unsubdivide"; + ot->description = "Rebuild a lower subdivision level of the current base mesh"; + ot->idname = "OBJECT_OT_multires_unsubdivide"; + + ot->poll = multires_poll; + ot->invoke = multires_unsubdivide_invoke; + ot->exec = multires_unsubdivide_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); +} + +/** \} */ + +/* ------------------------------------------------------------------- */ +/** \name Multires Rebuild Subdivisions + * \{ */ + +static int multires_rebuild_subdiv_exec(bContext *C, wmOperator *op) +{ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Object *object = ED_object_active_context(C); + MultiresModifierData *mmd = (MultiresModifierData *)edit_modifier_property_get( + op, object, eModifierType_Multires); + + if (!mmd) { + return OPERATOR_CANCELLED; + } + + int new_levels = multiresModifier_rebuild_subdiv(depsgraph, object, mmd, INT_MAX, false); + if (new_levels == 0) { + BKE_report(op->reports, RPT_ERROR, "Not valid subdivisions found to rebuild lower levels"); + return OPERATOR_CANCELLED; + } + + BKE_reportf(op->reports, RPT_INFO, "%d new levels rebuilt", new_levels); + + DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object); + + return OPERATOR_FINISHED; +} + +static int multires_rebuild_subdiv_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + if (edit_modifier_invoke_properties(C, op)) { + return multires_rebuild_subdiv_exec(C, op); + } + else { + return OPERATOR_CANCELLED; + } +} + +void OBJECT_OT_multires_rebuild_subdiv(wmOperatorType *ot) +{ + ot->name = "Rebuild Lower Subdivisions"; + ot->description = + "Rebuilds all possible subdivisions levels to generate a lower resolution base mesh"; + ot->idname = "OBJECT_OT_multires_rebuild_subdiv"; + + ot->poll = multires_poll; + ot->invoke = multires_rebuild_subdiv_invoke; + ot->exec = multires_rebuild_subdiv_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); +} + +/** \} */ + +/* ------------------------------------------------------------------- */ /** \name Skin Modifier * \{ */ diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index fef046169a7..819b6c18a44 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -137,6 +137,8 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_multires_reshape); WM_operatortype_append(OBJECT_OT_multires_higher_levels_delete); WM_operatortype_append(OBJECT_OT_multires_base_apply); + WM_operatortype_append(OBJECT_OT_multires_unsubdivide); + WM_operatortype_append(OBJECT_OT_multires_rebuild_subdiv); WM_operatortype_append(OBJECT_OT_multires_external_save); WM_operatortype_append(OBJECT_OT_multires_external_pack); WM_operatortype_append(OBJECT_OT_skin_root_mark); diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 3ce5aeedae8..604f66002c2 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -849,7 +849,6 @@ static void foreach_mouse_hit_key(PEData *data, ForHitKeyMatFunc func, int selec TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); - settings.scheduling_mode = TASK_SCHEDULING_DYNAMIC; BLI_task_parallel_range(0, edit->totpoint, &iter_data, foreach_mouse_hit_key_iter, &settings); } @@ -1229,7 +1228,6 @@ static void pe_deflect_emitter(Scene *scene, Object *ob, PTCacheEdit *edit) TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); - settings.scheduling_mode = TASK_SCHEDULING_DYNAMIC; BLI_task_parallel_range(0, edit->totpoint, &iter_data, deflect_emitter_iter, &settings); } @@ -1278,7 +1276,6 @@ static void PE_apply_lengths(Scene *scene, PTCacheEdit *edit) TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); - settings.scheduling_mode = TASK_SCHEDULING_DYNAMIC; BLI_task_parallel_range(0, edit->totpoint, &iter_data, apply_lengths_iter, &settings); } @@ -1353,7 +1350,6 @@ static void pe_iterate_lengths(Scene *scene, PTCacheEdit *edit) TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); - settings.scheduling_mode = TASK_SCHEDULING_DYNAMIC; BLI_task_parallel_range(0, edit->totpoint, &iter_data, iterate_lengths_iter, &settings); } @@ -2256,7 +2252,7 @@ bool PE_circle_select(bContext *C, const int sel_op, const int mval[2], float ra /************************ lasso select operator ************************/ -int PE_lasso_select(bContext *C, const int mcords[][2], const short moves, const int sel_op) +int PE_lasso_select(bContext *C, const int mcoords[][2], const short mcoords_len, const int sel_op) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); @@ -2300,7 +2296,8 @@ int PE_lasso_select(bContext *C, const int mcords[][2], const short moves, const const bool is_inside = ((ED_view3d_project_int_global(region, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) == V3D_PROJ_RET_OK) && - BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], IS_CLIPPED) && + BLI_lasso_is_point_inside( + mcoords, mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED) && key_test_depth(&data, co, screen_co)); const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); if (sel_op_result != -1) { @@ -2319,7 +2316,8 @@ int PE_lasso_select(bContext *C, const int mcords[][2], const short moves, const const bool is_inside = ((ED_view3d_project_int_global(region, co, screen_co, V3D_PROJ_TEST_CLIP_WIN) == V3D_PROJ_RET_OK) && - BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], IS_CLIPPED) && + BLI_lasso_is_point_inside( + mcoords, mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED) && key_test_depth(&data, co, screen_co)); const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); if (sel_op_result != -1) { @@ -4112,7 +4110,7 @@ static void brush_add_count_iter(void *__restrict iter_data_v, dmy = size; if (tls->rng == NULL) { tls->rng = BLI_rng_new_srandom(psys->seed + data->mval[0] + data->mval[1] + - tls_v->thread_id); + BLI_task_parallel_thread_id(tls_v)); } /* rejection sampling to get points in circle */ while (dmx * dmx + dmy * dmy > size2) { @@ -4257,7 +4255,6 @@ static int brush_add(const bContext *C, PEData *data, short number) TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); - settings.scheduling_mode = TASK_SCHEDULING_DYNAMIC; settings.userdata_chunk = &tls; settings.userdata_chunk_size = sizeof(BrushAddCountIterTLSData); settings.func_reduce = brush_add_count_iter_reduce; diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index b0444f25a8c..f2e8209b099 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -144,7 +144,6 @@ typedef struct OGLRender { wmTimer *timer; /* use to check if running modal or not (invoke'd or exec'd)*/ void **movie_ctx_arr; - TaskScheduler *task_scheduler; TaskPool *task_pool; bool pool_ok; bool is_animation; @@ -856,22 +855,16 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) gather_frames_to_render(C, oglrender); } - TaskScheduler *task_scheduler = BLI_task_scheduler_get(); if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { - task_scheduler = BLI_task_scheduler_create(1); - oglrender->task_scheduler = task_scheduler; - oglrender->task_pool = BLI_task_pool_create_background( - task_scheduler, oglrender, TASK_PRIORITY_LOW); + oglrender->task_pool = BLI_task_pool_create_background_serial(oglrender, TASK_PRIORITY_LOW); } else { - oglrender->task_scheduler = NULL; - oglrender->task_pool = BLI_task_pool_create(task_scheduler, oglrender, TASK_PRIORITY_LOW); + oglrender->task_pool = BLI_task_pool_create(oglrender, TASK_PRIORITY_LOW); } oglrender->pool_ok = true; BLI_spin_init(&oglrender->reports_lock); } else { - oglrender->task_scheduler = NULL; oglrender->task_pool = NULL; } oglrender->num_scheduled_frames = 0; @@ -910,10 +903,6 @@ static void screen_opengl_render_end(bContext *C, OGLRender *oglrender) } BLI_task_pool_work_and_wait(oglrender->task_pool); BLI_task_pool_free(oglrender->task_pool); - /* Depending on various things we might or might not use global scheduler. */ - if (oglrender->task_scheduler != NULL) { - BLI_task_scheduler_free(oglrender->task_scheduler); - } BLI_spin_end(&oglrender->reports_lock); } BLI_mutex_end(&oglrender->task_mutex); @@ -1033,7 +1022,7 @@ typedef struct WriteTaskData { Scene tmp_scene; } WriteTaskData; -static void write_result_func(TaskPool *__restrict pool, void *task_data_v, int UNUSED(thread_id)) +static void write_result_func(TaskPool *__restrict pool, void *task_data_v) { OGLRender *oglrender = (OGLRender *)BLI_task_pool_user_data(pool); WriteTaskData *task_data = (WriteTaskData *)task_data_v; diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 69cfe72308f..bbc6b9e7c86 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -2515,6 +2515,7 @@ void ED_region_panels_layout_ex(const bContext *C, int margin_x = 0; const bool region_layout_based = region->flag & RGN_FLAG_DYNAMIC_SIZE; const bool is_context_new = (contextnr != -1) ? UI_view2d_tab_set(v2d, contextnr) : false; + bool update_tot_size = true; /* before setting the view */ if (vertical) { @@ -2583,6 +2584,11 @@ void ED_region_panels_layout_ex(const bContext *C, } } + if (panel && UI_panel_is_dragging(panel)) { + /* Prevent View2d.tot rectangle size changes while dragging panels. */ + update_tot_size = false; + } + ed_panel_draw(C, area, region, ®ion->panels, pt, panel, w, em, vertical); } @@ -2638,8 +2644,10 @@ void ED_region_panels_layout_ex(const bContext *C, y = -y; } - /* this also changes the 'cur' */ - UI_view2d_totRect_set(v2d, x, y); + if (update_tot_size) { + /* this also changes the 'cur' */ + UI_view2d_totRect_set(v2d, x, y); + } if (use_category_tabs) { region->runtime.category = category; diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index b66964d0e7f..eccc15456ae 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -4388,7 +4388,7 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv wmWindow *window; ScrArea *area; int sync; - float time; + double time; /* sync, don't sync, or follow scene setting */ if (sad->flag & ANIMPLAY_FLAG_SYNC) { @@ -4411,7 +4411,7 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv } else if ((scene->audio.flag & AUDIO_SYNC) && (sad->flag & ANIMPLAY_FLAG_REVERSE) == false && isfinite(time = BKE_sound_sync_scene(scene_eval))) { - double newfra = (double)time * FPS; + double newfra = time * FPS; /* give some space here to avoid jumps */ if (newfra + 0.5 > scene->r.cfra && newfra - 0.5 < scene->r.cfra) { diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 536453ad085..f6f84ad1229 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -171,6 +171,8 @@ static void load_tex_task_cb_ex(void *__restrict userdata, bool convert_to_linear = false; struct ColorSpace *colorspace = NULL; + const int thread_id = BLI_task_parallel_thread_id(tls); + if (mtex->tex && mtex->tex->type == TEX_IMAGE && mtex->tex->ima) { ImBuf *tex_ibuf = BKE_image_pool_acquire_ibuf(mtex->tex->ima, &mtex->tex->iuser, pool); /* For consistency, sampling always returns color in linear space. */ @@ -214,8 +216,7 @@ static void load_tex_task_cb_ex(void *__restrict userdata, if (col) { float rgba[4]; - paint_get_tex_pixel_col( - mtex, x, y, rgba, pool, tls->thread_id, convert_to_linear, colorspace); + paint_get_tex_pixel_col(mtex, x, y, rgba, pool, thread_id, convert_to_linear, colorspace); buffer[index * 4] = rgba[0] * 255; buffer[index * 4 + 1] = rgba[1] * 255; @@ -223,7 +224,7 @@ static void load_tex_task_cb_ex(void *__restrict userdata, buffer[index * 4 + 3] = rgba[3] * 255; } else { - float avg = paint_get_tex_pixel(mtex, x, y, pool, tls->thread_id); + float avg = paint_get_tex_pixel(mtex, x, y, pool, thread_id); avg += br->texture_sample_bias; diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index d913bc2e242..e227db1c644 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -388,6 +388,10 @@ void paint_brush_color_get(struct Scene *scene, break; } } + /* Gradient / Colorband colors are not considered PROP_COLOR_GAMMA. + * Brush colors are expected to be in sRGB though. */ + IMB_colormanagement_scene_linear_to_srgb_v3(color_gr); + copy_v3_v3(color, color_gr); } else { diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 6af9ec01fc3..3dc6305dcf2 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -5168,9 +5168,7 @@ static void copy_original_alpha_channel(ProjPixel *pixel, bool is_floatbuf) } /* Run this for single and multi-threaded painting. */ -static void do_projectpaint_thread(TaskPool *__restrict UNUSED(pool), - void *ph_v, - int UNUSED(threadid)) +static void do_projectpaint_thread(TaskPool *__restrict UNUSED(pool), void *ph_v) { /* First unpack args from the struct */ ProjPaintState *ps = ((ProjectHandle *)ph_v)->ps; @@ -5605,7 +5603,6 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po bool touch_any = false; ProjectHandle handles[BLENDER_MAX_THREADS]; - TaskScheduler *scheduler = NULL; TaskPool *task_pool = NULL; int a, i; @@ -5616,8 +5613,7 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po } if (ps->thread_tot > 1) { - scheduler = BLI_task_scheduler_get(); - task_pool = BLI_task_pool_create_suspended(scheduler, NULL, TASK_PRIORITY_HIGH); + task_pool = BLI_task_pool_create_suspended(NULL, TASK_PRIORITY_HIGH); } image_pool = BKE_image_pool_new(); @@ -5661,7 +5657,7 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po BLI_task_pool_free(task_pool); } else { - do_projectpaint_thread(NULL, &handles[0], 0); + do_projectpaint_thread(NULL, &handles[0]); } BKE_image_pool_free(image_pool); diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index fb8cc3a639b..89cf090b7c6 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -168,9 +168,9 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op) .value = value, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, mask_flood_fill_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, mask_flood_fill_task_cb, &settings); if (multires) { multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); @@ -343,9 +343,9 @@ bool ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti * .clip_planes_final = clip_planes_final, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, mask_box_select_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, mask_box_select_task_cb, &settings); if (nodes) { MEM_freeN(nodes); @@ -456,10 +456,10 @@ static void mask_gesture_lasso_task_cb(void *__restrict userdata, static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) { - int mcords_tot; - const int(*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot); + int mcoords_len; + const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len); - if (mcords) { + if (mcoords) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); float clip_planes[4][4], clip_planes_final[4][4]; BoundBox bb; @@ -485,7 +485,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) ob = vc.obact; ED_view3d_ob_project_mat_get(vc.rv3d, ob, data.projviewobjmat); - BLI_lasso_boundbox(&data.rect, mcords, mcords_tot); + BLI_lasso_boundbox(&data.rect, mcoords, mcoords_len); data.width = data.rect.xmax - data.rect.xmin; data.px = BLI_BITMAP_NEW(data.width * (data.rect.ymax - data.rect.ymin), __func__); @@ -493,8 +493,8 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) data.rect.ymin, data.rect.xmax, data.rect.ymax, - mcords, - mcords_tot, + mcoords, + mcoords_len, mask_lasso_px_cb, &data); @@ -532,9 +532,9 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) data.task_data.mode = mode; data.task_data.value = value; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, mask_gesture_lasso_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, mask_gesture_lasso_task_cb, &settings); if (nodes) { MEM_freeN(nodes); @@ -551,7 +551,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) SCULPT_undo_push_end(); ED_region_tag_redraw(vc.region); - MEM_freeN((void *)mcords); + MEM_freeN((void *)mcoords); MEM_freeN(data.px); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 60b4a2f8e0c..c84a3b9cbfc 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -166,10 +166,11 @@ float paint_calc_object_space_radius(ViewContext *vc, const float center[3], flo float paint_get_tex_pixel(const MTex *mtex, float u, float v, struct ImagePool *pool, int thread) { - float intensity, rgba[4]; + float intensity; + float rgba_dummy[4]; float co[3] = {u, v, 0.0f}; - externtex(mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false); + RE_texture_evaluate(mtex, co, thread, pool, false, false, &intensity, rgba_dummy); return intensity; } @@ -184,11 +185,10 @@ void paint_get_tex_pixel_col(const MTex *mtex, struct ColorSpace *colorspace) { float co[3] = {u, v, 0.0f}; - int hasrgb; float intensity; - hasrgb = externtex( - mtex, co, &intensity, rgba, rgba + 1, rgba + 2, rgba + 3, thread, pool, false, false); + const bool hasrgb = RE_texture_evaluate(mtex, co, thread, pool, false, false, &intensity, rgba); + if (!hasrgb) { rgba[0] = intensity; rgba[1] = intensity; diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index d050a39ce68..5b9f1f600e6 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -2183,9 +2183,9 @@ static void calculate_average_weight(SculptThreadedTaskData *data, struct WPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__); data->custom_data = accum; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (data->sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, data, do_wpaint_brush_calc_average_weight_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, data, do_wpaint_brush_calc_average_weight_cb_ex, &settings); uint accum_len = 0; double accum_weight = 0.0; @@ -2231,22 +2231,22 @@ static void wpaint_paint_leaves(bContext *C, data.strength = BKE_brush_weight_get(scene, brush); /* NOTE: current mirroring code cannot be run in parallel */ - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, !(me->editflag & ME_EDIT_MIRROR_X), totnode); switch ((eBrushWeightPaintTool)brush->weightpaint_tool) { case WPAINT_TOOL_AVERAGE: calculate_average_weight(&data, nodes, totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_wpaint_brush_draw_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_wpaint_brush_draw_task_cb_ex, &settings); break; case WPAINT_TOOL_SMEAR: - BKE_pbvh_parallel_range(0, totnode, &data, do_wpaint_brush_smear_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_wpaint_brush_smear_task_cb_ex, &settings); break; case WPAINT_TOOL_BLUR: - BKE_pbvh_parallel_range(0, totnode, &data, do_wpaint_brush_blur_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_wpaint_brush_blur_task_cb_ex, &settings); break; case WPAINT_TOOL_DRAW: - BKE_pbvh_parallel_range(0, totnode, &data, do_wpaint_brush_draw_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_wpaint_brush_draw_task_cb_ex, &settings); break; } } @@ -3252,9 +3252,9 @@ static void calculate_average_color(SculptThreadedTaskData *data, struct VPaintAverageAccum *accum = MEM_mallocN(sizeof(*accum) * totnode, __func__); data->custom_data = accum; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BKE_pbvh_parallel_range(0, totnode, data, do_vpaint_brush_calc_average_color_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, data, do_vpaint_brush_calc_average_color_cb_ex, &settings); uint accum_len = 0; uint accum_value[3] = {0}; @@ -3298,21 +3298,21 @@ static void vpaint_paint_leaves(bContext *C, .lcol = (uint *)me->mloopcol, .me = me, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); switch ((eBrushVertexPaintTool)brush->vertexpaint_tool) { case VPAINT_TOOL_AVERAGE: calculate_average_color(&data, nodes, totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_vpaint_brush_draw_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_draw_task_cb_ex, &settings); break; case VPAINT_TOOL_BLUR: - BKE_pbvh_parallel_range(0, totnode, &data, do_vpaint_brush_blur_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_blur_task_cb_ex, &settings); break; case VPAINT_TOOL_SMEAR: - BKE_pbvh_parallel_range(0, totnode, &data, do_vpaint_brush_smear_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_smear_task_cb_ex, &settings); break; case VPAINT_TOOL_DRAW: - BKE_pbvh_parallel_range(0, totnode, &data, do_vpaint_brush_draw_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_vpaint_brush_draw_task_cb_ex, &settings); break; } } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 9b00feb1cf8..7bd7714b725 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -806,12 +806,12 @@ int SCULPT_nearest_vertex_get( nvtd.nearest_vertex_index = -1; nvtd.nearest_vertex_distance_squared = FLT_MAX; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); settings.func_reduce = nearest_vertex_get_reduce; settings.userdata_chunk = &nvtd; settings.userdata_chunk_size = sizeof(NearestVertexTLSData); - BKE_pbvh_parallel_range(0, totnode, &task_data, do_nearest_vertex_get_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &task_data, do_nearest_vertex_get_task_cb, &settings); MEM_SAFE_FREE(nodes); @@ -1283,9 +1283,9 @@ static void paint_mesh_restore_co(Sculpt *sd, Object *ob) .nodes = nodes, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP) && !ss->bm, totnode); - BKE_pbvh_parallel_range(0, totnode, &data, paint_mesh_restore_co_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, paint_mesh_restore_co_task_cb, &settings); MEM_SAFE_FREE(nodes); } @@ -1909,12 +1909,12 @@ static void calc_area_center( AreaNormalCenterTLSData anctd = {{{0}}}; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); settings.func_reduce = calc_area_normal_and_center_reduce; settings.userdata_chunk = &anctd; settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); - BKE_pbvh_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); /* For flatten center. */ for (n = 0; n < ARRAY_SIZE(anctd.area_cos); n++) { @@ -1968,12 +1968,12 @@ bool SCULPT_pbvh_calc_area_normal(const Brush *brush, AreaNormalCenterTLSData anctd = {{{0}}}; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, use_threading, totnode); settings.func_reduce = calc_area_normal_and_center_reduce; settings.userdata_chunk = &anctd; settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); - BKE_pbvh_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); /* For area normal. */ for (int i = 0; i < ARRAY_SIZE(anctd.area_nos); i++) { @@ -2009,12 +2009,12 @@ static void calc_area_normal_and_center( AreaNormalCenterTLSData anctd = {{{0}}}; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); settings.func_reduce = calc_area_normal_and_center_reduce; settings.userdata_chunk = &anctd; settings.userdata_chunk_size = sizeof(AreaNormalCenterTLSData); - BKE_pbvh_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); /* For flatten center. */ for (n = 0; n < ARRAY_SIZE(anctd.area_cos); n++) { @@ -2618,22 +2618,17 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = bstrength * - SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - *vd.mask, - vd.index, - tls->thread_id) * - ss->cache->pressure; + const float fade = + bstrength * + SCULPT_brush_strength_factor( + ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.index, thread_id) * + ss->cache->pressure; float avg[3], val[3]; @@ -2675,10 +2670,10 @@ static void bmesh_topology_rake( .nodes = nodes, .strength = factor, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_topology_rake_bmesh_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_topology_rake_bmesh_task_cb_ex, &settings); } } @@ -2696,12 +2691,13 @@ static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (sculpt_brush_test_sq_fn(&test, vd.co)) { const float fade = SCULPT_brush_strength_factor( - ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, tls->thread_id); + ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, thread_id); if (bstrength > 0.0f) { (*vd.mask) += fade * bstrength * (1.0f - *vd.mask); @@ -2731,9 +2727,9 @@ static void do_mask_brush_draw(Sculpt *sd, Object *ob, PBVHNode **nodes, int tot .nodes = nodes, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_mask_brush_draw_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_mask_brush_draw_task_cb_ex, &settings); } static void do_mask_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) @@ -2768,6 +2764,7 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -2781,7 +2778,7 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -2818,9 +2815,9 @@ static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) .offset = offset, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_draw_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_draw_brush_task_cb_ex, &settings); } static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, @@ -2843,6 +2840,7 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -2857,7 +2855,7 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -2894,9 +2892,9 @@ static void do_draw_sharp_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to .offset = offset, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_draw_sharp_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_draw_sharp_brush_task_cb_ex, &settings); } /* -------------------------------------------------------------------- */ @@ -2923,6 +2921,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -2936,7 +2935,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); float current_disp[3]; float current_disp_norm[3]; float final_disp[3]; @@ -3037,6 +3036,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -3050,7 +3050,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); SCULPT_relax_vertex(ss, &vd, fade * bstrength, false, vd.co); if (vd.mvert) { @@ -3079,15 +3079,15 @@ static void do_slide_relax_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t .nodes = nodes, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); if (ss->cache->alt_smooth) { for (int i = 0; i < 4; i++) { - BKE_pbvh_parallel_range(0, totnode, &data, do_topology_relax_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_topology_relax_task_cb_ex, &settings); } } else { - BKE_pbvh_parallel_range(0, totnode, &data, do_topology_slide_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_topology_slide_task_cb_ex, &settings); } } @@ -3200,6 +3200,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -3213,7 +3214,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); float val1[3]; float val2[3]; @@ -3288,9 +3289,9 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod .flippedbstrength = flippedbstrength, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_crease_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_crease_brush_task_cb_ex, &settings); } static void do_pinch_brush_task_cb_ex(void *__restrict userdata, @@ -3311,6 +3312,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); float x_object_space[3]; float z_object_space[3]; @@ -3328,7 +3330,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); float disp_center[3]; float x_disp[3]; float z_disp[3]; @@ -3401,9 +3403,9 @@ static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode .stroke_xz = stroke_xz, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_pinch_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_pinch_brush_task_cb_ex, &settings); } static void do_grab_brush_task_cb_ex(void *__restrict userdata, @@ -3427,6 +3429,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -3441,7 +3444,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); mul_v3_v3fl(proxy[vd.i], grab_delta, fade); @@ -3473,9 +3476,9 @@ static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) .grab_delta = grab_delta, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_grab_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_grab_brush_task_cb_ex, &settings); } static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, @@ -3582,9 +3585,9 @@ static void do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in .grab_delta = grab_delta, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_elastic_deform_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_elastic_deform_brush_task_cb_ex, &settings); } ePaintSymmetryAreas SCULPT_get_vertex_symm_area(const float co[3]) @@ -3744,6 +3747,7 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -3756,7 +3760,7 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); mul_v3_v3fl(proxy[vd.i], cono, fade); @@ -3788,9 +3792,9 @@ static void do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode .cono = cono, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_nudge_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_nudge_brush_task_cb_ex, &settings); } static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, @@ -3817,6 +3821,7 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -3829,7 +3834,7 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); mul_v3_v3fl(proxy[vd.i], grab_delta, fade); @@ -3909,9 +3914,9 @@ static void do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to .grab_delta = grab_delta, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_snake_hook_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_snake_hook_brush_task_cb_ex, &settings); } static void do_thumb_brush_task_cb_ex(void *__restrict userdata, @@ -3935,6 +3940,7 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -3949,7 +3955,7 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); mul_v3_v3fl(proxy[vd.i], cono, fade); @@ -3981,9 +3987,9 @@ static void do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode .cono = cono, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_thumb_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_thumb_brush_task_cb_ex, &settings); } static void do_rotate_brush_task_cb_ex(void *__restrict userdata, @@ -4007,6 +4013,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -4022,7 +4029,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata, NULL, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); sub_v3_v3v3(vec, orig_data.co, ss->cache->location); axis_angle_normalized_to_mat3(rot, ss->cache->sculpt_normal_symm, angle * fade); @@ -4054,9 +4061,9 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod .angle = angle, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_rotate_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_rotate_brush_task_cb_ex, &settings); } static void do_layer_brush_task_cb_ex(void *__restrict userdata, @@ -4078,6 +4085,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -4092,7 +4100,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); const int vi = vd.index; float *disp_factor; @@ -4170,9 +4178,9 @@ static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode .nodes = nodes, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_layer_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_layer_brush_task_cb_ex, &settings); } static void do_inflate_brush_task_cb_ex(void *__restrict userdata, @@ -4192,6 +4200,7 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -4204,7 +4213,7 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); float val[3]; if (vd.fno) { @@ -4236,9 +4245,9 @@ static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno .nodes = nodes, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_inflate_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_inflate_brush_task_cb_ex, &settings); } int SCULPT_plane_trim(const StrokeCache *cache, const Brush *brush, const float val[3]) @@ -4294,6 +4303,7 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); plane_from_point_normal_v3(test.plane_tool, area_co, area_no); @@ -4316,7 +4326,7 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -4360,9 +4370,9 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno .area_co = area_co, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_flatten_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_flatten_brush_task_cb_ex, &settings); } /* -------------------------------------------------------------------- */ @@ -4448,6 +4458,7 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); plane_from_point_normal_v3(test.plane_tool, area_co, area_no); @@ -4468,7 +4479,7 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -4510,13 +4521,13 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) ClaySampleData csd = {{0}}; - PBVHParallelSettings sample_settings; + TaskParallelSettings sample_settings; BKE_pbvh_parallel_range_settings(&sample_settings, (sd->flags & SCULPT_USE_OPENMP), totnode); sample_settings.func_reduce = calc_clay_surface_reduce; sample_settings.userdata_chunk = &csd; sample_settings.userdata_chunk_size = sizeof(ClaySampleData); - BKE_pbvh_parallel_range(0, totnode, &sample_data, calc_clay_surface_task_cb, &sample_settings); + BLI_task_parallel_range(0, totnode, &sample_data, calc_clay_surface_task_cb, &sample_settings); float d_offset = (csd.plane_dist[0] + csd.plane_dist[1]); d_offset = min_ff(radius, d_offset); @@ -4541,9 +4552,9 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) .area_co = area_co, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_clay_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_clay_brush_task_cb_ex, &settings); } static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, @@ -4567,6 +4578,7 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, SCULPT_brush_test_init(ss, &test); plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -4590,7 +4602,7 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -4674,9 +4686,9 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t .mat = mat, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_clay_strips_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_clay_strips_brush_task_cb_ex, &settings); } static void do_fill_brush_task_cb_ex(void *__restrict userdata, @@ -4698,6 +4710,7 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); plane_from_point_normal_v3(test.plane_tool, area_co, area_no); @@ -4721,7 +4734,7 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -4767,9 +4780,9 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) .area_co = area_co, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_fill_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_fill_brush_task_cb_ex, &settings); } static void do_scrape_brush_task_cb_ex(void *__restrict userdata, @@ -4791,6 +4804,7 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); plane_from_point_normal_v3(test.plane_tool, area_co, area_no); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) @@ -4813,7 +4827,7 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -4859,9 +4873,9 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod .area_co = area_co, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_scrape_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_scrape_brush_task_cb_ex, &settings); } /* -------------------------------------------------------------------- */ @@ -4889,6 +4903,7 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); float plane_tilt[4]; float normal_tilt[3]; @@ -4929,7 +4944,7 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -5031,9 +5046,9 @@ static void do_clay_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to .clay_strength = clay_strength, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_clay_thumb_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_clay_thumb_brush_task_cb_ex, &settings); } /** \} */ @@ -5055,6 +5070,7 @@ static void do_gravity_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -5067,7 +5083,7 @@ static void do_gravity_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -5102,9 +5118,9 @@ static void do_gravity(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, fl .offset = offset, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_gravity_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_gravity_task_cb_ex, &settings); } void SCULPT_vertcos_to_key(Object *ob, KeyBlock *kb, const float (*vertCos)[3]) @@ -5294,9 +5310,9 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe .nodes = nodes, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); /* Draw Face Sets in draw mode makes a single undo push, in alt-smooth mode deforms the * vertices and uses regular coords undo. */ @@ -5557,9 +5573,9 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob) .nodes = nodes, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings); } MEM_SAFE_FREE(nodes); @@ -5645,9 +5661,9 @@ void SCULPT_flush_stroke_deform(Sculpt *sd, Object *ob, bool is_proxy_used) .vertCos = vertCos, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, SCULPT_flush_stroke_deform_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, SCULPT_flush_stroke_deform_task_cb, &settings); if (vertCos) { SCULPT_vertcos_to_key(ob, ss->shapekey_active, vertCos); @@ -7501,7 +7517,7 @@ static int ed_object_sculptmode_flush_recalc_flag(Scene *scene, { int flush_recalc = 0; /* Multires in sculpt mode could have different from object mode subdivision level. */ - flush_recalc |= mmd && BKE_multires_sculpt_level_get(mmd) != mmd->lvl; + flush_recalc |= mmd && mmd->sculptlvl != mmd->lvl; /* If object has got active modifiers, it's dm could be different in sculpt mode. */ flush_recalc |= sculpt_has_active_modifiers(scene, ob); return flush_recalc; diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index b1cc6d02bbb..215f4388715 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -221,6 +221,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); /* For Pich Perpendicular Deform Type. */ float x_object_space[3]; @@ -269,7 +270,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); float brush_disp[3]; float normal[3]; @@ -412,7 +413,7 @@ static void cloth_brush_build_nodes_constraints(Sculpt *sd, * storing the constraints per node. */ /* Currently all constrains are added to the same global array which can't be accessed from * different threads. */ - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, false, totnode); SculptThreadedTaskData build_constraints_data = { @@ -421,7 +422,7 @@ static void cloth_brush_build_nodes_constraints(Sculpt *sd, .brush = brush, .nodes = nodes, }; - BKE_pbvh_parallel_range( + BLI_task_parallel_range( 0, totnode, &build_constraints_data, do_cloth_brush_build_constraints_task_cb_ex, &settings); } @@ -490,9 +491,9 @@ static void cloth_brush_do_simulation_step(Sculpt *sd, Object *ob, PBVHNode **no .cloth_time_step = CLOTH_SIMULATION_TIME_STEP, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range( + BLI_task_parallel_range( 0, totnode, &solve_simulation_data, do_cloth_brush_solve_simulation_task_cb_ex, &settings); } @@ -565,9 +566,9 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod } } - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range( + BLI_task_parallel_range( 0, totnode, &apply_forces_data, do_cloth_brush_apply_forces_task_cb_ex, &settings); } diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index c8769523823..e58ffb2c9e6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -87,6 +87,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -107,7 +108,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); if (fade > 0.05f && ss->face_sets[vert_map->indices[j]] > 0) { ss->face_sets[vert_map->indices[j]] = abs(ss->cache->paint_face_set); @@ -127,7 +128,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); if (fade > 0.05f) { SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set); @@ -160,6 +161,8 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, bstrength *= 2.0f; } + const int thread_id = BLI_task_parallel_thread_id(tls); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (sculpt_brush_test_sq_fn(&test, vd.co)) { @@ -172,7 +175,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); SCULPT_relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co); if (vd.mvert) { @@ -211,15 +214,15 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in .nodes = nodes, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); if (ss->cache->alt_smooth) { for (int i = 0; i < 4; i++) { - BKE_pbvh_parallel_range(0, totnode, &data, do_relax_face_sets_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_relax_face_sets_brush_task_cb_ex, &settings); } } else { - BKE_pbvh_parallel_range(0, totnode, &data, do_draw_face_sets_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_draw_face_sets_brush_task_cb_ex, &settings); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c index 38bbd083994..2d47eb3a9c8 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c @@ -246,9 +246,9 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op) .prev_mask = prev_mask, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings); if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) { MEM_freeN(prev_mask); @@ -275,9 +275,9 @@ void SCULPT_mask_filter_smooth_apply( }; for (int i = 0; i < smooth_iterations; i++) { - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, mask_filter_task_cb, &settings); } } @@ -458,17 +458,17 @@ static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op) .max = -FLT_MAX, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); settings.func_reduce = dirty_mask_compute_range_reduce; settings.userdata_chunk = ⦥ settings.userdata_chunk_size = sizeof(DirtyMaskRangeData); - BKE_pbvh_parallel_range(0, totnode, &data, dirty_mask_compute_range_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, dirty_mask_compute_range_task_cb, &settings); data.dirty_mask_min = range.min; data.dirty_mask_max = range.max; - BKE_pbvh_parallel_range(0, totnode, &data, dirty_mask_apply_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, dirty_mask_apply_task_cb, &settings); MEM_SAFE_FREE(nodes); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index 94b6e0eb864..f7b3bf32200 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -112,10 +112,10 @@ void SCULPT_filter_cache_init(Object *ob, Sculpt *sd) .nodes = ss->filter_cache->nodes, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings( &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); - BKE_pbvh_parallel_range( + BLI_task_parallel_range( 0, ss->filter_cache->totnode, &data, filter_cache_init_task_cb, &settings); } @@ -496,13 +496,13 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent * .filter_strength = filter_strength, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings( &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); - BKE_pbvh_parallel_range(0, ss->filter_cache->totnode, &data, mesh_filter_task_cb, &settings); + BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, mesh_filter_task_cb, &settings); if (filter_type == MESH_FILTER_SURFACE_SMOOTH) { - BKE_pbvh_parallel_range(0, + BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, mesh_filter_surface_smooth_displace_task_cb, diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 34ca92acef9..2d71e4df076 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -288,10 +288,10 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * .mask_expand_keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask"), .mask_expand_create_face_set = RNA_boolean_get(op->ptr, "create_face_set"), }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings( &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); - BKE_pbvh_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings); + BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings); ss->filter_cache->mask_update_current_it = mask_expand_update_it; } @@ -458,10 +458,10 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent .mask_expand_keep_prev_mask = RNA_boolean_get(op->ptr, "keep_previous_mask"), .mask_expand_create_face_set = RNA_boolean_get(op->ptr, "create_face_set"), }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings( &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); - BKE_pbvh_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings); + BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, sculpt_expand_task_cb, &settings); const char *status_str = TIP_( "Move the mouse to expand the mask from the active vertex. LMB: confirm mask, ESC/RMB: " diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c index c74a2ba503a..6ca696d4f65 100644 --- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c +++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c @@ -79,6 +79,7 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); /* Apply the brush normal radius to the test before sampling. */ float test_radius = sqrtf(test.radius_squared); @@ -107,7 +108,7 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); /* Sample the normal and area of the +X and -X axis individually. */ if (local_co[0] > 0.0f) { @@ -163,6 +164,7 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -208,7 +210,7 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata, vd.fno, vd.mask ? *vd.mask : 0.0f, vd.index, - tls->thread_id); + thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -301,13 +303,13 @@ void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, MultiplaneScrapeSampleData mssd = {{{0}}}; - PBVHParallelSettings sample_settings; + TaskParallelSettings sample_settings; BKE_pbvh_parallel_range_settings(&sample_settings, (sd->flags & SCULPT_USE_OPENMP), totnode); sample_settings.func_reduce = calc_multiplane_scrape_surface_reduce; sample_settings.userdata_chunk = &mssd; sample_settings.userdata_chunk_size = sizeof(MultiplaneScrapeSampleData); - BKE_pbvh_parallel_range( + BLI_task_parallel_range( 0, totnode, &sample_data, calc_multiplane_scrape_surface_task_cb, &sample_settings); float sampled_plane_normals[2][3]; @@ -392,9 +394,9 @@ void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, normalize_v3(plane_no); plane_from_point_normal_v3(data.multiplane_scrape_planes[0], area_co, plane_no); - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_multiplane_scrape_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_multiplane_scrape_brush_task_cb_ex, &settings); } void SCULPT_multiplane_scrape_preview_draw(const uint gpuattr, diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c index fde4a9d1d23..a90829741bf 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.c +++ b/source/blender/editors/sculpt_paint/sculpt_pose.c @@ -263,7 +263,7 @@ static void sculpt_pose_grow_pose_factor(Sculpt *sd, }; data.pose_initial_co = pose_target; - PBVHParallelSettings settings; + TaskParallelSettings settings; PoseGrowFactorTLSData gftd; gftd.pos_count = 0; zero_v3(gftd.pos_avg); @@ -279,7 +279,7 @@ static void sculpt_pose_grow_pose_factor(Sculpt *sd, zero_v3(gftd.pos_avg); gftd.pos_count = 0; memcpy(data.prev_mask, pose_factor, SCULPT_vertex_count_get(ss) * sizeof(float)); - BKE_pbvh_parallel_range(0, totnode, &data, pose_brush_grow_factor_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, pose_brush_grow_factor_task_cb_ex, &settings); if (gftd.pos_count != 0) { mul_v3_fl(gftd.pos_avg, 1.0f / (float)gftd.pos_count); @@ -793,9 +793,9 @@ void SCULPT_pose_brush_init(Sculpt *sd, Object *ob, SculptSession *ss, Brush *br for (int ik = 0; ik < ss->cache->pose_ik_chain->tot_segments; ik++) { data.pose_factor = ss->cache->pose_ik_chain->segments[ik].weights; for (int i = 0; i < br->pose_smooth_iterations; i++) { - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, pose_brush_init_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, pose_brush_init_task_cb_ex, &settings); } } @@ -885,9 +885,9 @@ void SCULPT_do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) .grab_delta = grab_delta, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range(0, totnode, &data, do_pose_brush_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_pose_brush_task_cb_ex, &settings); } void SCULPT_pose_ik_chain_free(SculptPoseIKChain *ik_chain) diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index 3a09d52d418..0b7057a2cb0 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -245,6 +245,8 @@ static void do_smooth_brush_mesh_task_cb_ex(void *__restrict userdata, SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (sculpt_brush_test_sq_fn(&test, vd.co)) { @@ -257,7 +259,7 @@ static void do_smooth_brush_mesh_task_cb_ex(void *__restrict userdata, vd.fno, smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), vd.index, - tls->thread_id); + thread_id); if (smooth_mask) { float val = SCULPT_neighbor_mask_average(ss, vd.vert_indices[vd.i]) - *vd.mask; val *= fade * bstrength; @@ -301,6 +303,7 @@ static void do_smooth_brush_bmesh_task_cb_ex(void *__restrict userdata, SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -313,7 +316,7 @@ static void do_smooth_brush_bmesh_task_cb_ex(void *__restrict userdata, vd.fno, smooth_mask ? 0.0f : *vd.mask, vd.index, - tls->thread_id); + thread_id); if (smooth_mask) { float val = SCULPT_neighbor_mask_average(ss, vd.index) - *vd.mask; val *= fade * bstrength; @@ -358,6 +361,8 @@ static void do_smooth_brush_multires_task_cb_ex(void *__restrict userdata, SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (sculpt_brush_test_sq_fn(&test, vd.co)) { @@ -370,7 +375,7 @@ static void do_smooth_brush_multires_task_cb_ex(void *__restrict userdata, vd.fno, smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), vd.index, - tls->thread_id); + thread_id); if (smooth_mask) { float val = SCULPT_neighbor_mask_average(ss, vd.index) - *vd.mask; val *= fade * bstrength; @@ -427,18 +432,18 @@ void SCULPT_smooth(Sculpt *sd, .strength = strength, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); switch (type) { case PBVH_GRIDS: - BKE_pbvh_parallel_range(0, totnode, &data, do_smooth_brush_multires_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_multires_task_cb_ex, &settings); break; case PBVH_FACES: - BKE_pbvh_parallel_range(0, totnode, &data, do_smooth_brush_mesh_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_mesh_task_cb_ex, &settings); break; case PBVH_BMESH: - BKE_pbvh_parallel_range(0, totnode, &data, do_smooth_brush_bmesh_task_cb_ex, &settings); + BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_bmesh_task_cb_ex, &settings); break; } } @@ -512,6 +517,7 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); @@ -522,7 +528,7 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( const float fade = bstrength * SCULPT_brush_strength_factor( - ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, tls->thread_id); + ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, thread_id); float disp[3]; SCULPT_surface_smooth_laplacian_step(ss, @@ -555,6 +561,7 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex( SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + const int thread_id = BLI_task_parallel_thread_id(tls); BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -562,7 +569,7 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex( const float fade = bstrength * SCULPT_brush_strength_factor( - ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, tls->thread_id); + ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, thread_id); SCULPT_surface_smooth_displace_step( ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, beta, fade); } @@ -590,12 +597,12 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in .nodes = nodes, }; - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); for (int i = 0; i < brush->surface_smooth_iterations; i++) { - BKE_pbvh_parallel_range( + BLI_task_parallel_range( 0, totnode, &data, SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex, &settings); - BKE_pbvh_parallel_range( + BLI_task_parallel_range( 0, totnode, &data, SCULPT_do_surface_smooth_brush_displace_task_cb_ex, &settings); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index c7cbb6672a4..cf46ca0097e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -177,10 +177,10 @@ void ED_sculpt_update_modal_transform(struct bContext *C) mul_m4_m4m4(data.transform_mats[i], pivot_mat, data.transform_mats[i]); } - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings( &settings, (sd->flags & SCULPT_USE_OPENMP), ss->filter_cache->totnode); - BKE_pbvh_parallel_range( + BLI_task_parallel_range( 0, ss->filter_cache->totnode, &data, sculpt_transform_task_cb, &settings); if (ss->deform_modifiers_active || ss->shapekey_active) { diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 414a6101071..6c8b73723eb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -415,9 +415,9 @@ static void sculpt_undo_bmesh_restore_generic(bContext *C, BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); - PBVHParallelSettings settings; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode); - BKE_pbvh_parallel_range( + BLI_task_parallel_range( 0, totnode, nodes, sculpt_undo_bmesh_restore_generic_task_cb, &settings); if (nodes) { diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 26c29d6cbe7..bbb68f632fb 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -793,8 +793,8 @@ static int actkeys_lassoselect_exec(bContext *C, wmOperator *op) } data_lasso.rectf_view = &rect_fl; - data_lasso.mcords = WM_gesture_lasso_path_to_array(C, op, &data_lasso.mcords_tot); - if (data_lasso.mcords == NULL) { + data_lasso.mcoords = WM_gesture_lasso_path_to_array(C, op, &data_lasso.mcoords_len); + if (data_lasso.mcoords == NULL) { return OPERATOR_CANCELLED; } @@ -805,13 +805,13 @@ static int actkeys_lassoselect_exec(bContext *C, wmOperator *op) } /* get settings from operator */ - BLI_lasso_boundbox(&rect, data_lasso.mcords, data_lasso.mcords_tot); + BLI_lasso_boundbox(&rect, data_lasso.mcoords, data_lasso.mcoords_len); BLI_rctf_rcti_copy(&rect_fl, &rect); /* apply box_select action */ region_select_action_keys(&ac, &rect_fl, BEZT_OK_CHANNEL_LASSO, selectmode, &data_lasso); - MEM_freeN((void *)data_lasso.mcords); + MEM_freeN((void *)data_lasso.mcoords); /* send notifier that keyframe selection has changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c index 4a51b3c8b8a..fe7ae7096a0 100644 --- a/source/blender/editors/space_clip/clip_draw.c +++ b/source/blender/editors/space_clip/clip_draw.c @@ -1730,8 +1730,7 @@ static void draw_distortion(SpaceClip *sc, { float x, y; const int n = 10; - int i, j, a; - float pos[2], tpos[2], grid[11][11][2]; + float tpos[2], grid[11][11][2]; MovieTracking *tracking = &clip->tracking; bGPdata *gpd = NULL; float aspy = 1.0f / tracking->camera.pixel_aspect; @@ -1764,7 +1763,7 @@ static void draw_distortion(SpaceClip *sc, float val[4][2], idx[4][2]; float min[2], max[2]; - for (a = 0; a < 4; a++) { + for (int a = 0; a < 4; a++) { if (a < 2) { val[a][a % 2] = FLT_MAX; } @@ -1773,13 +1772,13 @@ static void draw_distortion(SpaceClip *sc, } } - zero_v2(pos); - for (i = 0; i <= n; i++) { - for (j = 0; j <= n; j++) { + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= n; j++) { if (i == 0 || j == 0 || i == n || j == n) { + const float pos[2] = {dx * j, dy * i}; BKE_tracking_distort_v2(tracking, width, height, pos, tpos); - for (a = 0; a < 4; a++) { + for (int a = 0; a < 4; a++) { int ok; if (a < 2) { @@ -1796,59 +1795,49 @@ static void draw_distortion(SpaceClip *sc, } } } - - pos[0] += dx; } - - pos[0] = 0.0f; - pos[1] += dy; } INIT_MINMAX2(min, max); - for (a = 0; a < 4; a++) { - pos[0] = idx[a][0] * dx; - pos[1] = idx[a][1] * dy; + for (int a = 0; a < 4; a++) { + const float pos[2] = {idx[a][0] * dx, idx[a][1] * dy}; BKE_tracking_undistort_v2(tracking, width, height, pos, tpos); minmax_v2v2_v2(min, max, tpos); } - copy_v2_v2(pos, min); dx = (max[0] - min[0]) / n; dy = (max[1] - min[1]) / n; - for (i = 0; i <= n; i++) { - for (j = 0; j <= n; j++) { + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= n; j++) { + const float pos[2] = {min[0] + dx * j, min[1] + dy * i}; + BKE_tracking_distort_v2(tracking, width, height, pos, grid[i][j]); grid[i][j][0] /= width; grid[i][j][1] /= height * aspy; - - pos[0] += dx; } - - pos[0] = min[0]; - pos[1] += dy; } immUniformColor3f(1.0f, 0.0f, 0.0f); - for (i = 0; i <= n; i++) { + for (int i = 0; i <= n; i++) { immBegin(GPU_PRIM_LINE_STRIP, n + 1); - for (j = 0; j <= n; j++) { + for (int j = 0; j <= n; j++) { immVertex2fv(position, grid[i][j]); } immEnd(); } - for (j = 0; j <= n; j++) { + for (int j = 0; j <= n; j++) { immBegin(GPU_PRIM_LINE_STRIP, n + 1); - for (i = 0; i <= n; i++) { + for (int i = 0; i <= n; i++) { immVertex2fv(position, grid[i][j]); } @@ -1882,8 +1871,8 @@ static void draw_distortion(SpaceClip *sc, while (stroke) { if (stroke->flag & GP_STROKE_2DSPACE) { if (stroke->totpoints > 1) { - for (i = 0; i < stroke->totpoints - 1; i++) { - float npos[2], dpos[2], len; + for (int i = 0; i < stroke->totpoints - 1; i++) { + float pos[2], npos[2], dpos[2], len; int steps; pos[0] = (stroke->points[i].x + offsx) * width; @@ -1906,7 +1895,7 @@ static void draw_distortion(SpaceClip *sc, immBegin(GPU_PRIM_LINE_STRIP, steps + 1); - for (j = 0; j <= steps; j++) { + for (int j = 0; j <= steps; j++) { BKE_tracking_distort_v2(tracking, width, height, pos, tpos); immVertex2f(position, tpos[0] / width, tpos[1] / (height * aspy)); diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c index 657635b845d..5be4b2d5df0 100644 --- a/source/blender/editors/space_clip/clip_editor.c +++ b/source/blender/editors/space_clip/clip_editor.c @@ -885,7 +885,7 @@ static uchar *prefetch_thread_next_frame(PrefetchQueue *queue, return mem; } -static void prefetch_task_func(TaskPool *__restrict pool, void *task_data, int UNUSED(threadid)) +static void prefetch_task_func(TaskPool *__restrict pool, void *task_data) { PrefetchQueue *queue = (PrefetchQueue *)BLI_task_pool_user_data(pool); MovieClip *clip = (MovieClip *)task_data; @@ -942,9 +942,8 @@ static void start_prefetch_threads(MovieClip *clip, float *progress) { PrefetchQueue queue; - TaskScheduler *task_scheduler = BLI_task_scheduler_get(); TaskPool *task_pool; - int i, tot_thread = BLI_task_scheduler_num_threads(task_scheduler); + int i, tot_thread = BLI_task_scheduler_num_threads(); /* initialize queue */ BLI_spin_init(&queue.spin); @@ -961,7 +960,7 @@ static void start_prefetch_threads(MovieClip *clip, queue.do_update = do_update; queue.progress = progress; - task_pool = BLI_task_pool_create(task_scheduler, &queue, TASK_PRIORITY_LOW); + task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW); for (i = 0; i < tot_thread; i++) { BLI_task_pool_push(task_pool, prefetch_task_func, clip, false, NULL); } diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index b849fbd9250..3783a3af96c 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -1367,7 +1367,7 @@ static uchar *proxy_thread_next_frame(ProxyQueue *queue, return mem; } -static void proxy_task_func(TaskPool *__restrict pool, void *task_data, int UNUSED(threadid)) +static void proxy_task_func(TaskPool *__restrict pool, void *task_data) { ProxyThread *data = (ProxyThread *)task_data; ProxyQueue *queue = (ProxyQueue *)BLI_task_pool_user_data(pool); @@ -1413,11 +1413,10 @@ static void do_sequence_proxy(void *pjv, ProxyJob *pj = pjv; MovieClip *clip = pj->clip; Scene *scene = pj->scene; - TaskScheduler *task_scheduler = BLI_task_scheduler_get(); TaskPool *task_pool; int sfra = SFRA, efra = EFRA; ProxyThread *handles; - int i, tot_thread = BLI_task_scheduler_num_threads(task_scheduler); + int i, tot_thread = BLI_task_scheduler_num_threads(); int width, height; ProxyQueue queue; @@ -1434,7 +1433,7 @@ static void do_sequence_proxy(void *pjv, queue.do_update = do_update; queue.progress = progress; - task_pool = BLI_task_pool_create(task_scheduler, &queue, TASK_PRIORITY_LOW); + task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW); handles = MEM_callocN(sizeof(ProxyThread) * tot_thread, "proxy threaded handles"); for (i = 0; i < tot_thread; i++) { ProxyThread *handle = &handles[i]; diff --git a/source/blender/editors/space_clip/tracking_select.c b/source/blender/editors/space_clip/tracking_select.c index c2a4ebdfc63..7f58f30d2d2 100644 --- a/source/blender/editors/space_clip/tracking_select.c +++ b/source/blender/editors/space_clip/tracking_select.c @@ -598,8 +598,8 @@ void CLIP_OT_select_box(wmOperatorType *ot) /********************** lasso select operator *********************/ static int do_lasso_select_marker(bContext *C, - const int mcords[][2], - const short moves, + const int mcoords[][2], + const short mcoords_len, bool select) { SpaceClip *sc = CTX_wm_space_clip(C); @@ -616,7 +616,7 @@ static int do_lasso_select_marker(bContext *C, int framenr = ED_space_clip_get_clip_frame_number(sc); /* get rectangle from operator */ - BLI_lasso_boundbox(&rect, mcords, moves); + BLI_lasso_boundbox(&rect, mcoords, mcoords_len); /* do actual selection */ track = tracksbase->first; @@ -631,7 +631,8 @@ static int do_lasso_select_marker(bContext *C, ED_clip_point_stable_pos__reverse(sc, region, marker->pos, screen_co); if (BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) && - BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], V2D_IS_CLIPPED)) { + BLI_lasso_is_point_inside( + mcoords, mcoords_len, screen_co[0], screen_co[1], V2D_IS_CLIPPED)) { if (select) { BKE_tracking_track_flag_set(track, TRACK_AREA_ALL, SELECT); } @@ -659,7 +660,8 @@ static int do_lasso_select_marker(bContext *C, ED_clip_point_stable_pos__reverse(sc, region, plane_marker->corners[i], screen_co); if (BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) && - BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], V2D_IS_CLIPPED)) { + BLI_lasso_is_point_inside( + mcoords, mcoords_len, screen_co[0], screen_co[1], V2D_IS_CLIPPED)) { if (select) { plane_track->flag |= SELECT; } @@ -685,10 +687,10 @@ static int do_lasso_select_marker(bContext *C, static int clip_lasso_select_exec(bContext *C, wmOperator *op) { - int mcords_tot; - const int(*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot); + int mcoords_len; + const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len); - if (mcords) { + if (mcoords) { const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); const bool select = (sel_op != SEL_OP_SUB); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { @@ -696,9 +698,9 @@ static int clip_lasso_select_exec(bContext *C, wmOperator *op) ED_clip_select_all(sc, SEL_DESELECT, NULL); } - do_lasso_select_marker(C, mcords, mcords_tot, select); + do_lasso_select_marker(C, mcoords, mcoords_len, select); - MEM_freeN((void *)mcords); + MEM_freeN((void *)mcoords); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index c04d08d7b78..7f6d0658ec8 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1264,9 +1264,7 @@ static void filelist_intern_free(FileListIntern *filelist_intern) MEM_SAFE_FREE(filelist_intern->filtered); } -static void filelist_cache_preview_runf(TaskPool *__restrict pool, - void *taskdata, - int UNUSED(threadid)) +static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdata) { FileListEntryCache *cache = BLI_task_pool_user_data(pool); FileListEntryPreviewTaskData *preview_taskdata = taskdata; @@ -1325,9 +1323,7 @@ static void filelist_cache_preview_freef(TaskPool *__restrict UNUSED(pool), void static void filelist_cache_preview_ensure_running(FileListEntryCache *cache) { if (!cache->previews_pool) { - TaskScheduler *scheduler = BLI_task_scheduler_get(); - - cache->previews_pool = BLI_task_pool_create_background(scheduler, cache, TASK_PRIORITY_LOW); + cache->previews_pool = BLI_task_pool_create_background(cache, TASK_PRIORITY_LOW); cache->previews_done = BLI_thread_queue_init(); IMB_thumb_locks_acquire(); diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index 8c931a0c4a3..6c984860efc 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -42,6 +42,7 @@ #include "BKE_context.h" #include "BKE_curve.h" #include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_screen.h" diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index a2e9ba86dec..ae435b5624a 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -760,8 +760,8 @@ static int graphkeys_lassoselect_exec(bContext *C, wmOperator *op) } data_lasso.rectf_view = &rect_fl; - data_lasso.mcords = WM_gesture_lasso_path_to_array(C, op, &data_lasso.mcords_tot); - if (data_lasso.mcords == NULL) { + data_lasso.mcoords = WM_gesture_lasso_path_to_array(C, op, &data_lasso.mcoords_len); + if (data_lasso.mcoords == NULL) { return OPERATOR_CANCELLED; } @@ -782,13 +782,13 @@ static int graphkeys_lassoselect_exec(bContext *C, wmOperator *op) } /* get settings from operator */ - BLI_lasso_boundbox(&rect, data_lasso.mcords, data_lasso.mcords_tot); + BLI_lasso_boundbox(&rect, data_lasso.mcoords, data_lasso.mcoords_len); BLI_rctf_rcti_copy(&rect_fl, &rect); /* apply box_select action */ box_select_graphkeys(&ac, &rect_fl, BEZT_OK_REGION_LASSO, selectmode, incl_handles, &data_lasso); - MEM_freeN((void *)data_lasso.mcords); + MEM_freeN((void *)data_lasso.mcoords); /* send notifier that keyframe selection has changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 3658ebae3a2..6f3ef44fe94 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -656,8 +656,8 @@ static void uiblock_layer_pass_buttons( /* pass */ rpass = (rl ? BLI_findlink(&rl->passes, iuser->pass) : NULL); - if (rpass && RE_passes_have_name(rl)) { - display_name = rpass->name; + if (rl && RE_passes_have_name(rl)) { + display_name = rpass ? rpass->name : ""; rnd_pt = ui_imageuser_data_copy(&rnd_pt_local); but = uiDefMenuBut(block, ui_imageuser_pass_menu, diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index 7f911113b7c..c9f2ec38354 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -264,7 +264,10 @@ void ED_space_image_get_aspect(SpaceImage *sima, float *r_aspx, float *r_aspy) } } -void ED_space_image_get_zoom(SpaceImage *sima, ARegion *region, float *r_zoomx, float *r_zoomy) +void ED_space_image_get_zoom(SpaceImage *sima, + const ARegion *region, + float *r_zoomx, + float *r_zoomy) { int width, height; @@ -314,7 +317,7 @@ void ED_image_get_uv_aspect(Image *ima, ImageUser *iuser, float *r_aspx, float * } /* takes event->mval */ -void ED_image_mouse_pos(SpaceImage *sima, ARegion *region, const int mval[2], float co[2]) +void ED_image_mouse_pos(SpaceImage *sima, const ARegion *region, const int mval[2], float co[2]) { int sx, sy, width, height; float zoomx, zoomy; @@ -341,7 +344,7 @@ void ED_image_view_center_to_point(SpaceImage *sima, float x, float y) } void ED_image_point_pos( - SpaceImage *sima, ARegion *region, float x, float y, float *r_x, float *r_y) + SpaceImage *sima, const ARegion *region, float x, float y, float *r_x, float *r_y) { int sx, sy, width, height; float zoomx, zoomy; @@ -356,7 +359,7 @@ void ED_image_point_pos( } void ED_image_point_pos__reverse(SpaceImage *sima, - ARegion *region, + const ARegion *region, const float co[2], float r_co[2]) { diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index 5bcb21b152f..f8c0e66873f 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -369,10 +369,16 @@ static void stats_update(Depsgraph *depsgraph, ViewLayer *view_layer) if (obedit) { /* Edit Mode */ - FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, ((View3D *)NULL), ob->type, ob->mode, ob_iter) { - stats_object_edit(ob_iter, &stats); + FOREACH_OBJECT_BEGIN (view_layer, ob_iter) { + if (ob_iter->base_flag & BASE_VISIBLE_VIEWLAYER) { + if (ob_iter->mode == OB_MODE_EDIT) { + stats_object_edit(ob_iter, &stats); + stats.totobjsel++; + } + stats.totobj++; + } } - FOREACH_OBJECT_IN_MODE_END; + FOREACH_OBJECT_END; } else if (ob && (ob->mode & OB_MODE_POSE)) { /* Pose Mode */ @@ -469,23 +475,21 @@ static void stats_row(int col1, *y -= height; BLF_draw_default(col1, *y, 0.0f, key, 128); char values[128]; - BLI_snprintf(values, sizeof(values), (value2) ? "%s/%s" : "%s", value1, value2); + BLI_snprintf(values, sizeof(values), (value2) ? "%s / %s" : "%s", value1, value2); BLF_draw_default(col2, *y, 0.0f, values, sizeof(values)); } void ED_info_draw_stats( Main *bmain, Scene *scene, ViewLayer *view_layer, int x, int *y, int height) { - /* Looping through dependency graph when interface is locked is not safe. - * The interface is marked as locked when jobs wants to modify the - * dependency graph. */ - wmWindowManager *wm = bmain->wm.first; - if (wm->is_interface_locked) { - return; - } - - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + /* Create stats if they don't already exist. */ if (!view_layer->stats) { + /* Do not not access dependency graph if interface is marked as locked. */ + wmWindowManager *wm = bmain->wm.first; + if (wm->is_interface_locked) { + return; + } + Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); stats_update(depsgraph, view_layer); } @@ -569,7 +573,7 @@ void ED_info_draw_stats( int col2 = x + longest_label + (0.5f * U.widget_unit); /* Add some extra margin above this section. */ - *y -= (0.5f * height); + *y -= (0.6f * height); if (object_mode == OB_MODE_OBJECT) { stats_row(col1, labels[OBJ], col2, stats_fmt.totobjsel, stats_fmt.totobj, y, height); @@ -577,6 +581,7 @@ void ED_info_draw_stats( if (obedit) { if (obedit->type == OB_MESH) { + stats_row(col1, labels[OBJ], col2, stats_fmt.totobjsel, stats_fmt.totobj, y, height); stats_row(col1, labels[VERTS], col2, stats_fmt.totvertsel, stats_fmt.totvert, y, height); stats_row(col1, labels[EDGES], col2, stats_fmt.totedgesel, stats_fmt.totedge, y, height); stats_row(col1, labels[FACES], col2, stats_fmt.totfacesel, stats_fmt.totface, y, height); diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 70f3db1e2ab..44003a5b9bc 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -73,6 +73,9 @@ #include "NOD_texture.h" #include "node_intern.h" /* own include */ +/* Default flags for uiItemR(). Name is kept short since this is used a lot in this file. */ +#define DEFAULT_FLAGS UI_ITEM_R_SPLIT_EMPTY_NAME + /* ****************** SOCKET BUTTON DRAW FUNCTIONS ***************** */ static void node_socket_button_label(bContext *UNUSED(C), @@ -94,7 +97,7 @@ static void node_buts_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *p PointerRNA sockptr; RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr); - uiItemR(layout, &sockptr, "default_value", 0, "", ICON_NONE); + uiItemR(layout, &sockptr, "default_value", DEFAULT_FLAGS, "", ICON_NONE); } static void node_buts_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -108,7 +111,7 @@ static void node_buts_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr col = uiLayoutColumn(layout, false); uiTemplateColorPicker(col, &sockptr, "default_value", 1, 0, 0, 0); - uiItemR(col, &sockptr, "default_value", UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(col, &sockptr, "default_value", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); } static void node_buts_mix_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -119,12 +122,12 @@ static void node_buts_mix_rgb(uiLayout *layout, bContext *UNUSED(C), PointerRNA col = uiLayoutColumn(layout, false); row = uiLayoutRow(col, true); - uiItemR(row, ptr, "blend_type", 0, "", ICON_NONE); + uiItemR(row, ptr, "blend_type", DEFAULT_FLAGS, "", ICON_NONE); if (ELEM(ntree->type, NTREE_COMPOSIT, NTREE_TEXTURE)) { - uiItemR(row, ptr, "use_alpha", 0, "", ICON_IMAGE_RGB_ALPHA); + uiItemR(row, ptr, "use_alpha", DEFAULT_FLAGS, "", ICON_IMAGE_RGB_ALPHA); } - uiItemR(col, ptr, "use_clamp", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_buts_time(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -146,8 +149,8 @@ static void node_buts_time(uiLayout *layout, bContext *UNUSED(C), PointerRNA *pt uiTemplateCurveMapping(layout, ptr, "curve", 's', false, false, false, false); row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "frame_start", 0, IFACE_("Sta"), ICON_NONE); - uiItemR(row, ptr, "frame_end", 0, IFACE_("End"), ICON_NONE); + uiItemR(row, ptr, "frame_start", DEFAULT_FLAGS, IFACE_("Sta"), ICON_NONE); + uiItemR(row, ptr, "frame_end", DEFAULT_FLAGS, IFACE_("End"), ICON_NONE); } static void node_buts_colorramp(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -197,7 +200,7 @@ static void node_buts_normal(uiLayout *layout, bContext *UNUSED(C), PointerRNA * PointerRNA sockptr; RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, output, &sockptr); - uiItemR(layout, &sockptr, "default_value", 0, "", ICON_NONE); + uiItemR(layout, &sockptr, "default_value", DEFAULT_FLAGS, "", ICON_NONE); } #if 0 /* not used in 2.5x yet */ @@ -243,33 +246,33 @@ static void node_buts_texture(uiLayout *layout, bContext *UNUSED(C), PointerRNA short multi = (node->id && ((Tex *)node->id)->use_nodes && (node->type != CMP_NODE_TEXTURE) && (node->type != TEX_NODE_TEXTURE)); - uiItemR(layout, ptr, "texture", 0, "", ICON_NONE); + uiItemR(layout, ptr, "texture", DEFAULT_FLAGS, "", ICON_NONE); if (multi) { /* Number Drawing not optimal here, better have a list*/ - uiItemR(layout, ptr, "node_output", 0, "", ICON_NONE); + uiItemR(layout, ptr, "node_output", DEFAULT_FLAGS, "", ICON_NONE); } } static void node_shader_buts_clamp(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "clamp_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "clamp_type", DEFAULT_FLAGS, "", ICON_NONE); } static void node_shader_buts_map_range(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "interpolation_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "interpolation_type", DEFAULT_FLAGS, "", ICON_NONE); if (!ELEM(RNA_enum_get(ptr, "interpolation_type"), NODE_MAP_RANGE_SMOOTHSTEP, NODE_MAP_RANGE_SMOOTHERSTEP)) { - uiItemR(layout, ptr, "clamp", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "clamp", DEFAULT_FLAGS, NULL, ICON_NONE); } } static void node_buts_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); - uiItemR(layout, ptr, "use_clamp", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE); } static int node_resize_area_default(bNode *node, int x, int y) @@ -535,9 +538,9 @@ static int node_resize_area_frame(bNode *node, int x, int y) static void node_buts_frame_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "label_size", 0, IFACE_("Label Size"), ICON_NONE); - uiItemR(layout, ptr, "shrink", 0, IFACE_("Shrink"), ICON_NONE); - uiItemR(layout, ptr, "text", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "label_size", DEFAULT_FLAGS, IFACE_("Label Size"), ICON_NONE); + uiItemR(layout, ptr, "shrink", DEFAULT_FLAGS, IFACE_("Shrink"), ICON_NONE); + uiItemR(layout, ptr, "text", DEFAULT_FLAGS, NULL, ICON_NONE); } #define NODE_REROUTE_SIZE 8.0f @@ -698,7 +701,7 @@ static void node_buts_image_user(uiLayout *layout, col = uiLayoutColumn(layout, false); - uiItemR(col, imaptr, "source", 0, "", ICON_NONE); + uiItemR(col, imaptr, "source", DEFAULT_FLAGS, "", ICON_NONE); source = RNA_enum_get(imaptr, "source"); @@ -717,23 +720,23 @@ static void node_buts_image_user(uiLayout *layout, if (ELEM(source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "frame_duration", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "frame_start", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "frame_offset", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "use_cyclic", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "use_auto_refresh", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "frame_duration", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "frame_start", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "frame_offset", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_cyclic", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_auto_refresh", DEFAULT_FLAGS, NULL, ICON_NONE); } if (compositor && RNA_enum_get(imaptr, "type") == IMA_TYPE_MULTILAYER && RNA_boolean_get(ptr, "has_layers")) { col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "layer", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "layer", DEFAULT_FLAGS, NULL, ICON_NONE); } uiLayout *split = uiLayoutSplit(layout, 0.5f, true); PointerRNA colorspace_settings_ptr = RNA_pointer_get(imaptr, "colorspace_settings"); uiItemL(split, IFACE_("Color Space"), ICON_NONE); - uiItemR(split, &colorspace_settings_ptr, "name", 0, "", ICON_NONE); + uiItemR(split, &colorspace_settings_ptr, "name", DEFAULT_FLAGS, "", ICON_NONE); /* Avoid losing changes image is painted. */ if (BKE_image_is_dirty(imaptr->data)) { @@ -743,35 +746,35 @@ static void node_buts_image_user(uiLayout *layout, static void node_shader_buts_mapping(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "vector_type", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_shader_buts_vector_rotate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "rotation_type", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "invert", 0, NULL, 0); + uiItemR(layout, ptr, "rotation_type", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, NULL, 0); } static void node_shader_buts_vect_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); + uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE); } static void node_shader_buts_vect_transform(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "vector_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); - uiItemR(layout, ptr, "convert_from", 0, "", ICON_NONE); - uiItemR(layout, ptr, "convert_to", 0, "", ICON_NONE); + uiItemR(layout, ptr, "vector_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(layout, ptr, "convert_from", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "convert_to", DEFAULT_FLAGS, "", ICON_NONE); } static void node_shader_buts_attribute(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "attribute_name", 0, IFACE_("Name"), ICON_NONE); + uiItemR(layout, ptr, "attribute_name", DEFAULT_FLAGS, IFACE_("Name"), ICON_NONE); } static void node_shader_buts_wireframe(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "use_pixel_size", 0, NULL, 0); + uiItemR(layout, ptr, "use_pixel_size", DEFAULT_FLAGS, NULL, 0); } static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -790,14 +793,14 @@ static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA UI_TEMPLATE_ID_FILTER_ALL, false, NULL); - uiItemR(layout, ptr, "interpolation", 0, "", ICON_NONE); - uiItemR(layout, ptr, "projection", 0, "", ICON_NONE); + uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, "", ICON_NONE); if (RNA_enum_get(ptr, "projection") == SHD_PROJ_BOX) { - uiItemR(layout, ptr, "projection_blend", 0, "Blend", ICON_NONE); + uiItemR(layout, ptr, "projection_blend", DEFAULT_FLAGS, "Blend", ICON_NONE); } - uiItemR(layout, ptr, "extension", 0, "", ICON_NONE); + uiItemR(layout, ptr, "extension", DEFAULT_FLAGS, "", ICON_NONE); /* note: image user properties used directly here, unlike compositor image node, * which redefines them in the node struct RNA to get proper updates. @@ -828,8 +831,8 @@ static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, Poin false, NULL); - uiItemR(layout, ptr, "interpolation", 0, "", ICON_NONE); - uiItemR(layout, ptr, "projection", 0, "", ICON_NONE); + uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, "", ICON_NONE); node_buts_image_user(layout, C, &iuserptr, &imaptr, &iuserptr, false); } @@ -839,29 +842,29 @@ static void node_shader_buts_tex_environment_ex(uiLayout *layout, bContext *C, P PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user"); uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0); - uiItemR(layout, ptr, "interpolation", 0, IFACE_("Interpolation"), ICON_NONE); - uiItemR(layout, ptr, "projection", 0, IFACE_("Projection"), ICON_NONE); + uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, IFACE_("Interpolation"), ICON_NONE); + uiItemR(layout, ptr, "projection", DEFAULT_FLAGS, IFACE_("Projection"), ICON_NONE); } static void node_shader_buts_tex_sky(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "sky_type", 0, "", ICON_NONE); - uiItemR(layout, ptr, "sun_direction", 0, "", ICON_NONE); - uiItemR(layout, ptr, "turbidity", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "sky_type", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "sun_direction", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "turbidity", DEFAULT_FLAGS, NULL, ICON_NONE); if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_NEW) { - uiItemR(layout, ptr, "ground_albedo", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "ground_albedo", DEFAULT_FLAGS, NULL, ICON_NONE); } } static void node_shader_buts_tex_gradient(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "gradient_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "gradient_type", DEFAULT_FLAGS, "", ICON_NONE); } static void node_shader_buts_tex_magic(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "turbulence_depth", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "turbulence_depth", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_shader_buts_tex_brick(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -869,48 +872,48 @@ static void node_shader_buts_tex_brick(uiLayout *layout, bContext *UNUSED(C), Po uiLayout *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "offset", UI_ITEM_R_SLIDER, IFACE_("Offset"), ICON_NONE); - uiItemR(col, ptr, "offset_frequency", 0, IFACE_("Frequency"), ICON_NONE); + uiItemR(col, ptr, "offset", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, IFACE_("Offset"), ICON_NONE); + uiItemR(col, ptr, "offset_frequency", DEFAULT_FLAGS, IFACE_("Frequency"), ICON_NONE); col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "squash", 0, IFACE_("Squash"), ICON_NONE); - uiItemR(col, ptr, "squash_frequency", 0, IFACE_("Frequency"), ICON_NONE); + uiItemR(col, ptr, "squash", DEFAULT_FLAGS, IFACE_("Squash"), ICON_NONE); + uiItemR(col, ptr, "squash_frequency", DEFAULT_FLAGS, IFACE_("Frequency"), ICON_NONE); } static void node_shader_buts_tex_wave(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "wave_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "wave_type", DEFAULT_FLAGS, "", ICON_NONE); int type = RNA_enum_get(ptr, "wave_type"); if (type == SHD_WAVE_BANDS) { - uiItemR(layout, ptr, "bands_direction", 0, "", ICON_NONE); + uiItemR(layout, ptr, "bands_direction", DEFAULT_FLAGS, "", ICON_NONE); } else { /* SHD_WAVE_RINGS */ - uiItemR(layout, ptr, "rings_direction", 0, "", ICON_NONE); + uiItemR(layout, ptr, "rings_direction", DEFAULT_FLAGS, "", ICON_NONE); } - uiItemR(layout, ptr, "wave_profile", 0, "", ICON_NONE); + uiItemR(layout, ptr, "wave_profile", DEFAULT_FLAGS, "", ICON_NONE); } static void node_shader_buts_tex_musgrave(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "musgrave_dimensions", 0, "", ICON_NONE); - uiItemR(layout, ptr, "musgrave_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "musgrave_dimensions", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "musgrave_type", DEFAULT_FLAGS, "", ICON_NONE); } static void node_shader_buts_tex_voronoi(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "voronoi_dimensions", 0, "", ICON_NONE); - uiItemR(layout, ptr, "feature", 0, "", ICON_NONE); + uiItemR(layout, ptr, "voronoi_dimensions", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "feature", DEFAULT_FLAGS, "", ICON_NONE); int feature = RNA_enum_get(ptr, "feature"); if (!ELEM(feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS) && RNA_enum_get(ptr, "voronoi_dimensions") != 1) { - uiItemR(layout, ptr, "distance", 0, "", ICON_NONE); + uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, "", ICON_NONE); } } static void node_shader_buts_tex_noise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "noise_dimensions", 0, "", ICON_NONE); + uiItemR(layout, ptr, "noise_dimensions", DEFAULT_FLAGS, "", ICON_NONE); } static void node_shader_buts_tex_pointdensity(uiLayout *layout, @@ -926,7 +929,7 @@ static void node_shader_buts_tex_pointdensity(uiLayout *layout, RNA_id_pointer_create(ob ? (ID *)ob->data : NULL, &obdata_ptr); uiItemR(layout, ptr, "point_source", UI_ITEM_R_EXPAND, NULL, ICON_NONE); - uiItemR(layout, ptr, "object", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "object", DEFAULT_FLAGS, NULL, ICON_NONE); if (node->id && shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) { PointerRNA dataptr; @@ -934,15 +937,15 @@ static void node_shader_buts_tex_pointdensity(uiLayout *layout, uiItemPointerR(layout, ptr, "particle_system", &dataptr, "particle_systems", NULL, ICON_NONE); } - uiItemR(layout, ptr, "space", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "radius", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "interpolation", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "resolution", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "space", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "radius", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "interpolation", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "resolution", DEFAULT_FLAGS, NULL, ICON_NONE); if (shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) { - uiItemR(layout, ptr, "particle_color_source", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "particle_color_source", DEFAULT_FLAGS, NULL, ICON_NONE); } else { - uiItemR(layout, ptr, "vertex_color_source", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "vertex_color_source", DEFAULT_FLAGS, NULL, ICON_NONE); if (shader_point_density->ob_color_source == SHD_POINTDENSITY_COLOR_VERTWEIGHT) { if (ob_ptr.data) { uiItemPointerR( @@ -960,18 +963,18 @@ static void node_shader_buts_tex_pointdensity(uiLayout *layout, static void node_shader_buts_tex_coord(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "object", 0, NULL, 0); - uiItemR(layout, ptr, "from_instancer", 0, NULL, 0); + uiItemR(layout, ptr, "object", DEFAULT_FLAGS, NULL, 0); + uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, NULL, 0); } static void node_shader_buts_bump(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "invert", 0, NULL, 0); + uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, NULL, 0); } static void node_shader_buts_uvmap(uiLayout *layout, bContext *C, PointerRNA *ptr) { - uiItemR(layout, ptr, "from_instancer", 0, NULL, 0); + uiItemR(layout, ptr, "from_instancer", DEFAULT_FLAGS, NULL, 0); if (!RNA_boolean_get(ptr, "from_instancer")) { PointerRNA obptr = CTX_data_pointer_get(C, "active_object"); @@ -997,12 +1000,12 @@ static void node_shader_buts_vertex_color(uiLayout *layout, bContext *C, Pointer static void node_shader_buts_uvalongstroke(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "use_tips", 0, NULL, 0); + uiItemR(layout, ptr, "use_tips", DEFAULT_FLAGS, NULL, 0); } static void node_shader_buts_normal_map(uiLayout *layout, bContext *C, PointerRNA *ptr) { - uiItemR(layout, ptr, "space", 0, "", 0); + uiItemR(layout, ptr, "space", DEFAULT_FLAGS, "", 0); if (RNA_enum_get(ptr, "space") == SHD_SPACE_TANGENT) { PointerRNA obptr = CTX_data_pointer_get(C, "active_object"); @@ -1012,14 +1015,14 @@ static void node_shader_buts_normal_map(uiLayout *layout, bContext *C, PointerRN uiItemPointerR(layout, ptr, "uv_map", &dataptr, "uv_layers", "", ICON_NONE); } else { - uiItemR(layout, ptr, "uv_map", 0, "", 0); + uiItemR(layout, ptr, "uv_map", DEFAULT_FLAGS, "", 0); } } } static void node_shader_buts_displacement(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "space", 0, "", 0); + uiItemR(layout, ptr, "space", DEFAULT_FLAGS, "", 0); } static void node_shader_buts_tangent(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -1028,7 +1031,7 @@ static void node_shader_buts_tangent(uiLayout *layout, bContext *C, PointerRNA * split = uiLayoutSplit(layout, 0.0f, false); - uiItemR(split, ptr, "direction_type", 0, "", 0); + uiItemR(split, ptr, "direction_type", DEFAULT_FLAGS, "", 0); row = uiLayoutRow(split, false); @@ -1040,50 +1043,50 @@ static void node_shader_buts_tangent(uiLayout *layout, bContext *C, PointerRNA * uiItemPointerR(row, ptr, "uv_map", &dataptr, "uv_layers", "", ICON_NONE); } else { - uiItemR(row, ptr, "uv_map", 0, "", 0); + uiItemR(row, ptr, "uv_map", DEFAULT_FLAGS, "", 0); } } else { - uiItemR(row, ptr, "axis", UI_ITEM_R_EXPAND, NULL, 0); + uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, 0); } } static void node_shader_buts_glossy(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "distribution", 0, "", ICON_NONE); + uiItemR(layout, ptr, "distribution", DEFAULT_FLAGS, "", ICON_NONE); } static void node_shader_buts_principled(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "distribution", 0, "", ICON_NONE); - uiItemR(layout, ptr, "subsurface_method", 0, "", ICON_NONE); + uiItemR(layout, ptr, "distribution", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "subsurface_method", DEFAULT_FLAGS, "", ICON_NONE); } static void node_shader_buts_anisotropic(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "distribution", 0, "", ICON_NONE); + uiItemR(layout, ptr, "distribution", DEFAULT_FLAGS, "", ICON_NONE); } static void node_shader_buts_subsurface(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "falloff", 0, "", ICON_NONE); + uiItemR(layout, ptr, "falloff", DEFAULT_FLAGS, "", ICON_NONE); } static void node_shader_buts_toon(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "component", 0, "", ICON_NONE); + uiItemR(layout, ptr, "component", DEFAULT_FLAGS, "", ICON_NONE); } static void node_shader_buts_hair(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "component", 0, "", ICON_NONE); + uiItemR(layout, ptr, "component", DEFAULT_FLAGS, "", ICON_NONE); } static void node_shader_buts_principled_hair(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "parametrization", 0, "", ICON_NONE); + uiItemR(layout, ptr, "parametrization", DEFAULT_FLAGS, "", ICON_NONE); } static void node_shader_buts_ies(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1091,15 +1094,15 @@ static void node_shader_buts_ies(uiLayout *layout, bContext *UNUSED(C), PointerR uiLayout *row; row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); row = uiLayoutRow(layout, true); if (RNA_enum_get(ptr, "mode") == NODE_IES_INTERNAL) { - uiItemR(row, ptr, "ies", 0, "", ICON_NONE); + uiItemR(row, ptr, "ies", DEFAULT_FLAGS, "", ICON_NONE); } else { - uiItemR(row, ptr, "filepath", 0, "", ICON_NONE); + uiItemR(row, ptr, "filepath", DEFAULT_FLAGS, "", ICON_NONE); } } @@ -1108,15 +1111,15 @@ static void node_shader_buts_script(uiLayout *layout, bContext *UNUSED(C), Point uiLayout *row; row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "mode", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); row = uiLayoutRow(layout, true); if (RNA_enum_get(ptr, "mode") == NODE_SCRIPT_INTERNAL) { - uiItemR(row, ptr, "script", 0, "", ICON_NONE); + uiItemR(row, ptr, "script", DEFAULT_FLAGS, "", ICON_NONE); } else { - uiItemR(row, ptr, "filepath", 0, "", ICON_NONE); + uiItemR(row, ptr, "filepath", DEFAULT_FLAGS, "", ICON_NONE); } uiItemO(row, "", ICON_FILE_REFRESH, "node.shader_script_update"); @@ -1130,14 +1133,14 @@ static void node_shader_buts_script_ex(uiLayout *layout, bContext *C, PointerRNA #if 0 /* not implemented yet */ if (RNA_enum_get(ptr, "mode") == NODE_SCRIPT_EXTERNAL) { - uiItemR(layout, ptr, "use_auto_update", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_auto_update", DEFAULT_FLAGS, NULL, ICON_NONE); } #endif } static void node_buts_output_shader(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "target", 0, "", ICON_NONE); + uiItemR(layout, ptr, "target", DEFAULT_FLAGS, "", ICON_NONE); } static void node_buts_output_linestyle(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1146,32 +1149,32 @@ static void node_buts_output_linestyle(uiLayout *layout, bContext *UNUSED(C), Po col = uiLayoutColumn(layout, false); row = uiLayoutRow(col, true); - uiItemR(row, ptr, "blend_type", 0, "", ICON_NONE); - uiItemR(col, ptr, "use_clamp", 0, NULL, ICON_NONE); + uiItemR(row, ptr, "blend_type", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_shader_buts_bevel(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "samples", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_shader_buts_ambient_occlusion(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "samples", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "inside", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "only_local", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "samples", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "inside", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "only_local", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_shader_buts_white_noise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "noise_dimensions", 0, "", ICON_NONE); + uiItemR(layout, ptr, "noise_dimensions", DEFAULT_FLAGS, "", ICON_NONE); } static void node_shader_buts_output_aov(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "name", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "name", DEFAULT_FLAGS, NULL, ICON_NONE); } /* only once called */ @@ -1356,10 +1359,10 @@ static void node_buts_image_views(uiLayout *layout, if (RNA_boolean_get(ptr, "has_views")) { if (RNA_enum_get(ptr, "view") == 0) { - uiItemR(col, ptr, "view", 0, NULL, ICON_CAMERA_STEREO); + uiItemR(col, ptr, "view", DEFAULT_FLAGS, NULL, ICON_CAMERA_STEREO); } else { - uiItemR(col, ptr, "view", 0, NULL, ICON_SCENE); + uiItemR(col, ptr, "view", DEFAULT_FLAGS, NULL, ICON_SCENE); } } } @@ -1420,7 +1423,7 @@ static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, Pointer col = uiLayoutColumn(layout, false); row = uiLayoutRow(col, true); - uiItemR(row, ptr, "layer", 0, "", ICON_NONE); + uiItemR(row, ptr, "layer", DEFAULT_FLAGS, "", ICON_NONE); prop = RNA_struct_find_property(ptr, "layer"); if (!(RNA_property_enum_identifier( @@ -1447,56 +1450,56 @@ static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), Point filter = RNA_enum_get(ptr, "filter_type"); reference = RNA_boolean_get(ptr, "use_variable_size"); - uiItemR(col, ptr, "filter_type", 0, "", ICON_NONE); + uiItemR(col, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE); if (filter != R_FILTER_FAST_GAUSS) { - uiItemR(col, ptr, "use_variable_size", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_variable_size", DEFAULT_FLAGS, NULL, ICON_NONE); if (!reference) { - uiItemR(col, ptr, "use_bokeh", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_bokeh", DEFAULT_FLAGS, NULL, ICON_NONE); } - uiItemR(col, ptr, "use_gamma_correction", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_gamma_correction", DEFAULT_FLAGS, NULL, ICON_NONE); } - uiItemR(col, ptr, "use_relative", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_relative", DEFAULT_FLAGS, NULL, ICON_NONE); if (RNA_boolean_get(ptr, "use_relative")) { uiItemL(col, IFACE_("Aspect Correction"), ICON_NONE); row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "aspect_correction", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "aspect_correction", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "factor_x", 0, IFACE_("X"), ICON_NONE); - uiItemR(col, ptr, "factor_y", 0, IFACE_("Y"), ICON_NONE); + uiItemR(col, ptr, "factor_x", DEFAULT_FLAGS, IFACE_("X"), ICON_NONE); + uiItemR(col, ptr, "factor_y", DEFAULT_FLAGS, IFACE_("Y"), ICON_NONE); } else { col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "size_x", 0, IFACE_("X"), ICON_NONE); - uiItemR(col, ptr, "size_y", 0, IFACE_("Y"), ICON_NONE); + uiItemR(col, ptr, "size_x", DEFAULT_FLAGS, IFACE_("X"), ICON_NONE); + uiItemR(col, ptr, "size_y", DEFAULT_FLAGS, IFACE_("Y"), ICON_NONE); } - uiItemR(col, ptr, "use_extended_bounds", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_extended_bounds", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_dblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayout *col; - uiItemR(layout, ptr, "iterations", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_wrap", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_wrap", DEFAULT_FLAGS, NULL, ICON_NONE); col = uiLayoutColumn(layout, true); uiItemL(col, IFACE_("Center:"), ICON_NONE); - uiItemR(col, ptr, "center_x", 0, IFACE_("X"), ICON_NONE); - uiItemR(col, ptr, "center_y", 0, IFACE_("Y"), ICON_NONE); + uiItemR(col, ptr, "center_x", DEFAULT_FLAGS, IFACE_("X"), ICON_NONE); + uiItemR(col, ptr, "center_y", DEFAULT_FLAGS, IFACE_("Y"), ICON_NONE); uiItemS(layout); col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "distance", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "angle", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "distance", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "angle", DEFAULT_FLAGS, NULL, ICON_NONE); uiItemS(layout); - uiItemR(layout, ptr, "spin", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "zoom", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "spin", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "zoom", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_bilateralblur(uiLayout *layout, @@ -1506,9 +1509,9 @@ static void node_composit_buts_bilateralblur(uiLayout *layout, uiLayout *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "iterations", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "sigma_color", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "sigma_space", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "iterations", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "sigma_color", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "sigma_space", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -1517,60 +1520,60 @@ static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA col = uiLayoutColumn(layout, false); uiItemL(col, IFACE_("Bokeh Type:"), ICON_NONE); - uiItemR(col, ptr, "bokeh", 0, "", ICON_NONE); - uiItemR(col, ptr, "angle", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "bokeh", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(col, ptr, "angle", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_gamma_correction", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_gamma_correction", DEFAULT_FLAGS, NULL, ICON_NONE); col = uiLayoutColumn(layout, false); uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_zbuffer") == true); - uiItemR(col, ptr, "f_stop", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "f_stop", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "blur_max", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "threshold", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE); col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "use_preview", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_preview", DEFAULT_FLAGS, NULL, ICON_NONE); uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "use_zbuffer", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_zbuffer", DEFAULT_FLAGS, NULL, ICON_NONE); sub = uiLayoutColumn(col, false); uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_zbuffer") == false); - uiItemR(sub, ptr, "z_scale", 0, NULL, ICON_NONE); + uiItemR(sub, ptr, "z_scale", DEFAULT_FLAGS, NULL, ICON_NONE); } /* qdn: glare node */ static void node_composit_buts_glare(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "glare_type", 0, "", ICON_NONE); - uiItemR(layout, ptr, "quality", 0, "", ICON_NONE); + uiItemR(layout, ptr, "glare_type", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "quality", DEFAULT_FLAGS, "", ICON_NONE); if (RNA_enum_get(ptr, "glare_type") != 1) { - uiItemR(layout, ptr, "iterations", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "iterations", DEFAULT_FLAGS, NULL, ICON_NONE); if (RNA_enum_get(ptr, "glare_type") != 0) { - uiItemR(layout, ptr, "color_modulation", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(layout, ptr, "color_modulation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); } } - uiItemR(layout, ptr, "mix", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "threshold", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "mix", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE); if (RNA_enum_get(ptr, "glare_type") == 2) { - uiItemR(layout, ptr, "streaks", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "angle_offset", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "streaks", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "angle_offset", DEFAULT_FLAGS, NULL, ICON_NONE); } if (RNA_enum_get(ptr, "glare_type") == 0 || RNA_enum_get(ptr, "glare_type") == 2) { - uiItemR(layout, ptr, "fade", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(layout, ptr, "fade", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); if (RNA_enum_get(ptr, "glare_type") == 0) { - uiItemR(layout, ptr, "use_rotate_45", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_rotate_45", DEFAULT_FLAGS, NULL, ICON_NONE); } } if (RNA_enum_get(ptr, "glare_type") == 1) { - uiItemR(layout, ptr, "size", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "size", DEFAULT_FLAGS, NULL, ICON_NONE); } } @@ -1579,17 +1582,17 @@ static void node_composit_buts_tonemap(uiLayout *layout, bContext *UNUSED(C), Po uiLayout *col; col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "tonemap_type", 0, "", ICON_NONE); + uiItemR(col, ptr, "tonemap_type", DEFAULT_FLAGS, "", ICON_NONE); if (RNA_enum_get(ptr, "tonemap_type") == 0) { - uiItemR(col, ptr, "key", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "offset", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "gamma", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "key", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "gamma", DEFAULT_FLAGS, NULL, ICON_NONE); } else { - uiItemR(col, ptr, "intensity", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "contrast", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "adaptation", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "correction", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "intensity", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "adaptation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "correction", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); } } @@ -1598,12 +1601,12 @@ static void node_composit_buts_lensdist(uiLayout *layout, bContext *UNUSED(C), P uiLayout *col; col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "use_projector", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_projector", DEFAULT_FLAGS, NULL, ICON_NONE); col = uiLayoutColumn(col, false); uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_projector") == false); - uiItemR(col, ptr, "use_jitter", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "use_fit", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_jitter", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_fit", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1611,46 +1614,46 @@ static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), Po uiLayout *col; col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "samples", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "factor", 0, IFACE_("Blur"), ICON_NONE); + uiItemR(col, ptr, "samples", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "factor", DEFAULT_FLAGS, IFACE_("Blur"), ICON_NONE); col = uiLayoutColumn(layout, true); uiItemL(col, IFACE_("Speed:"), ICON_NONE); - uiItemR(col, ptr, "speed_min", 0, IFACE_("Min"), ICON_NONE); - uiItemR(col, ptr, "speed_max", 0, IFACE_("Max"), ICON_NONE); + uiItemR(col, ptr, "speed_min", DEFAULT_FLAGS, IFACE_("Min"), ICON_NONE); + uiItemR(col, ptr, "speed_max", DEFAULT_FLAGS, IFACE_("Max"), ICON_NONE); - uiItemR(layout, ptr, "use_curved", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_curved", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_filter(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "filter_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE); } static void node_composit_buts_flip(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "axis", 0, "", ICON_NONE); + uiItemR(layout, ptr, "axis", DEFAULT_FLAGS, "", ICON_NONE); } static void node_composit_buts_crop(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayout *col; - uiItemR(layout, ptr, "use_crop_size", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "relative", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_crop_size", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "relative", DEFAULT_FLAGS, NULL, ICON_NONE); col = uiLayoutColumn(layout, true); if (RNA_boolean_get(ptr, "relative")) { - uiItemR(col, ptr, "rel_min_x", 0, IFACE_("Left"), ICON_NONE); - uiItemR(col, ptr, "rel_max_x", 0, IFACE_("Right"), ICON_NONE); - uiItemR(col, ptr, "rel_min_y", 0, IFACE_("Up"), ICON_NONE); - uiItemR(col, ptr, "rel_max_y", 0, IFACE_("Down"), ICON_NONE); + uiItemR(col, ptr, "rel_min_x", DEFAULT_FLAGS, IFACE_("Left"), ICON_NONE); + uiItemR(col, ptr, "rel_max_x", DEFAULT_FLAGS, IFACE_("Right"), ICON_NONE); + uiItemR(col, ptr, "rel_min_y", DEFAULT_FLAGS, IFACE_("Up"), ICON_NONE); + uiItemR(col, ptr, "rel_max_y", DEFAULT_FLAGS, IFACE_("Down"), ICON_NONE); } else { - uiItemR(col, ptr, "min_x", 0, IFACE_("Left"), ICON_NONE); - uiItemR(col, ptr, "max_x", 0, IFACE_("Right"), ICON_NONE); - uiItemR(col, ptr, "min_y", 0, IFACE_("Up"), ICON_NONE); - uiItemR(col, ptr, "max_y", 0, IFACE_("Down"), ICON_NONE); + uiItemR(col, ptr, "min_x", DEFAULT_FLAGS, IFACE_("Left"), ICON_NONE); + uiItemR(col, ptr, "max_x", DEFAULT_FLAGS, IFACE_("Right"), ICON_NONE); + uiItemR(col, ptr, "min_y", DEFAULT_FLAGS, IFACE_("Up"), ICON_NONE); + uiItemR(col, ptr, "max_y", DEFAULT_FLAGS, IFACE_("Down"), ICON_NONE); } } @@ -1660,8 +1663,8 @@ static void node_composit_buts_splitviewer(uiLayout *layout, bContext *UNUSED(C) col = uiLayoutColumn(layout, false); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "axis", UI_ITEM_R_EXPAND, NULL, ICON_NONE); - uiItemR(col, ptr, "factor", 0, NULL, ICON_NONE); + uiItemR(row, ptr, "axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(col, ptr, "factor", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_double_edge_mask(uiLayout *layout, @@ -1673,9 +1676,9 @@ static void node_composit_buts_double_edge_mask(uiLayout *layout, col = uiLayoutColumn(layout, false); uiItemL(col, IFACE_("Inner Edge:"), ICON_NONE); - uiItemR(col, ptr, "inner_mode", 0, "", ICON_NONE); + uiItemR(col, ptr, "inner_mode", DEFAULT_FLAGS, "", ICON_NONE); uiItemL(col, IFACE_("Buffer Edge:"), ICON_NONE); - uiItemR(col, ptr, "edge_mode", 0, "", ICON_NONE); + uiItemR(col, ptr, "edge_mode", DEFAULT_FLAGS, "", ICON_NONE); } static void node_composit_buts_map_range(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1683,7 +1686,7 @@ static void node_composit_buts_map_range(uiLayout *layout, bContext *UNUSED(C), uiLayout *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_clamp", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_clamp", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1691,20 +1694,20 @@ static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C), uiLayout *sub, *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "offset", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "size", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "size", DEFAULT_FLAGS, NULL, ICON_NONE); col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_min", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_min", DEFAULT_FLAGS, NULL, ICON_NONE); sub = uiLayoutColumn(col, false); uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_min")); - uiItemR(sub, ptr, "min", 0, "", ICON_NONE); + uiItemR(sub, ptr, "min", DEFAULT_FLAGS, "", ICON_NONE); col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_max", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_max", DEFAULT_FLAGS, NULL, ICON_NONE); sub = uiLayoutColumn(col, false); uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_max")); - uiItemR(sub, ptr, "max", 0, "", ICON_NONE); + uiItemR(sub, ptr, "max", DEFAULT_FLAGS, "", ICON_NONE); } static void node_composit_buts_alphaover(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1712,8 +1715,8 @@ static void node_composit_buts_alphaover(uiLayout *layout, bContext *UNUSED(C), uiLayout *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_premultiply", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "premul", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_premultiply", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "premul", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1721,27 +1724,27 @@ static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), P uiLayout *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "use_alpha", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "use_antialias_z", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "use_antialias_z", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "distance", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, NULL, ICON_NONE); switch (RNA_enum_get(ptr, "mode")) { case CMP_NODE_DILATEERODE_DISTANCE_THRESH: - uiItemR(layout, ptr, "edge", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "edge", DEFAULT_FLAGS, NULL, ICON_NONE); break; case CMP_NODE_DILATEERODE_DISTANCE_FEATHER: - uiItemR(layout, ptr, "falloff", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "falloff", DEFAULT_FLAGS, NULL, ICON_NONE); break; } } static void node_composit_buts_inpaint(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "distance", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "distance", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_despeckle(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1749,8 +1752,8 @@ static void node_composit_buts_despeckle(uiLayout *layout, bContext *UNUSED(C), uiLayout *col; col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "threshold", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "threshold_neighbor", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "threshold_neighbor", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_diff_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1758,8 +1761,8 @@ static void node_composit_buts_diff_matte(uiLayout *layout, bContext *UNUSED(C), uiLayout *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "tolerance", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "falloff", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); } static void node_composit_buts_distance_matte(uiLayout *layout, @@ -1772,10 +1775,10 @@ static void node_composit_buts_distance_matte(uiLayout *layout, uiItemL(layout, IFACE_("Color Space:"), ICON_NONE); row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "channel", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); - uiItemR(col, ptr, "tolerance", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "falloff", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "falloff", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); } static void node_composit_buts_color_spill(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1784,23 +1787,23 @@ static void node_composit_buts_color_spill(uiLayout *layout, bContext *UNUSED(C) uiItemL(layout, IFACE_("Despill Channel:"), ICON_NONE); row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "channel", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "limit_method", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, NULL, ICON_NONE); if (RNA_enum_get(ptr, "limit_method") == 0) { uiItemL(col, IFACE_("Limiting Channel:"), ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "limit_channel", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); } - uiItemR(col, ptr, "ratio", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "use_unspill", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "ratio", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "use_unspill", DEFAULT_FLAGS, NULL, ICON_NONE); if (RNA_boolean_get(ptr, "use_unspill") == true) { - uiItemR(col, ptr, "unspill_red", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "unspill_green", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "unspill_blue", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "unspill_red", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "unspill_green", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "unspill_blue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); } } @@ -1809,12 +1812,12 @@ static void node_composit_buts_chroma_matte(uiLayout *layout, bContext *UNUSED(C uiLayout *col; col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "tolerance", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "threshold", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "tolerance", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "threshold", DEFAULT_FLAGS, NULL, ICON_NONE); col = uiLayoutColumn(layout, true); /*uiItemR(col, ptr, "lift", UI_ITEM_R_SLIDER, NULL, ICON_NONE); Removed for now */ - uiItemR(col, ptr, "gain", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); /*uiItemR(col, ptr, "shadow_adjust", UI_ITEM_R_SLIDER, NULL, ICON_NONE); Removed for now*/ } @@ -1823,9 +1826,9 @@ static void node_composit_buts_color_matte(uiLayout *layout, bContext *UNUSED(C) uiLayout *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "color_hue", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "color_saturation", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "color_value", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "color_hue", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "color_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "color_value", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); } static void node_composit_buts_channel_matte(uiLayout *layout, @@ -1836,24 +1839,24 @@ static void node_composit_buts_channel_matte(uiLayout *layout, uiItemL(layout, IFACE_("Color Space:"), ICON_NONE); row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "color_space", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "color_space", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); col = uiLayoutColumn(layout, false); uiItemL(col, IFACE_("Key Channel:"), ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "matte_channel", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "matte_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "limit_method", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "limit_method", DEFAULT_FLAGS, NULL, ICON_NONE); if (RNA_enum_get(ptr, "limit_method") == 0) { uiItemL(col, IFACE_("Limiting Channel:"), ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "limit_channel", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, ptr, "limit_channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); } - uiItemR(col, ptr, "limit_max", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "limit_min", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); } static void node_composit_buts_luma_matte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1861,19 +1864,19 @@ static void node_composit_buts_luma_matte(uiLayout *layout, bContext *UNUSED(C), uiLayout *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "limit_max", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "limit_min", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "limit_max", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(col, ptr, "limit_min", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); } static void node_composit_buts_map_uv(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "alpha", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "alpha", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_id_mask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "index", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_antialiasing", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "index", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_antialiasing", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_file_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -1887,7 +1890,7 @@ static void node_composit_buts_file_output(uiLayout *layout, bContext *UNUSED(C) else { uiItemL(layout, IFACE_("Base Path:"), ICON_NONE); } - uiItemR(layout, ptr, "base_path", 0, "", ICON_NONE); + uiItemR(layout, ptr, "base_path", DEFAULT_FLAGS, "", ICON_NONE); } static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) { @@ -1973,7 +1976,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi uiItemL(col, IFACE_("Layer:"), ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, &active_input_ptr, "name", 0, "", ICON_NONE); + uiItemR(row, &active_input_ptr, "name", DEFAULT_FLAGS, "", ICON_NONE); uiItemFullO(row, "NODE_OT_output_file_remove_active_socket", "", @@ -1988,7 +1991,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi uiItemL(col, IFACE_("File Subpath:"), ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, &active_input_ptr, "path", 0, "", ICON_NONE); + uiItemR(row, &active_input_ptr, "path", DEFAULT_FLAGS, "", ICON_NONE); uiItemFullO(row, "NODE_OT_output_file_remove_active_socket", "", @@ -2003,7 +2006,7 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi col = uiLayoutColumn(layout, true); uiItemL(col, IFACE_("Format:"), ICON_NONE); - uiItemR(col, &active_input_ptr, "use_node_format", 0, NULL, ICON_NONE); + uiItemR(col, &active_input_ptr, "use_node_format", DEFAULT_FLAGS, NULL, ICON_NONE); col = uiLayoutColumn(layout, false); uiLayoutSetActive(col, RNA_boolean_get(&active_input_ptr, "use_node_format") == false); @@ -2018,20 +2021,20 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "space", 0, "", ICON_NONE); + uiItemR(layout, ptr, "space", DEFAULT_FLAGS, "", ICON_NONE); if (RNA_enum_get(ptr, "space") == CMP_SCALE_RENDERPERCENT) { uiLayout *row; - uiItemR(layout, ptr, "frame_method", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(layout, ptr, "frame_method", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "offset_x", 0, "X", ICON_NONE); - uiItemR(row, ptr, "offset_y", 0, "Y", ICON_NONE); + uiItemR(row, ptr, "offset_x", DEFAULT_FLAGS, "X", ICON_NONE); + uiItemR(row, ptr, "offset_y", DEFAULT_FLAGS, "Y", ICON_NONE); } } static void node_composit_buts_rotate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "filter_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE); } static void node_composit_buts_invert(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -2039,25 +2042,25 @@ static void node_composit_buts_invert(uiLayout *layout, bContext *UNUSED(C), Poi uiLayout *col; col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "invert_rgb", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "invert_alpha", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "invert_rgb", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "invert_alpha", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "mapping", 0, "", ICON_NONE); + uiItemR(layout, ptr, "mapping", DEFAULT_FLAGS, "", ICON_NONE); } static void node_composit_buts_view_levels(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "channel", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(layout, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); } static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayout *split, *col, *row; - uiItemR(layout, ptr, "correction_method", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, NULL, ICON_NONE); if (RNA_enum_get(ptr, "correction_method") == 0) { @@ -2065,17 +2068,17 @@ static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C col = uiLayoutColumn(split, false); uiTemplateColorPicker(col, ptr, "lift", 1, 1, 0, 1); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "lift", 0, NULL, ICON_NONE); + uiItemR(row, ptr, "lift", DEFAULT_FLAGS, NULL, ICON_NONE); col = uiLayoutColumn(split, false); uiTemplateColorPicker(col, ptr, "gamma", 1, 1, 1, 1); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "gamma", 0, NULL, ICON_NONE); + uiItemR(row, ptr, "gamma", DEFAULT_FLAGS, NULL, ICON_NONE); col = uiLayoutColumn(split, false); uiTemplateColorPicker(col, ptr, "gain", 1, 1, 1, 1); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "gain", 0, NULL, ICON_NONE); + uiItemR(row, ptr, "gain", DEFAULT_FLAGS, NULL, ICON_NONE); } else { @@ -2083,46 +2086,46 @@ static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C col = uiLayoutColumn(split, false); uiTemplateColorPicker(col, ptr, "offset", 1, 1, 0, 1); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "offset", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "offset_basis", 0, NULL, ICON_NONE); + uiItemR(row, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "offset_basis", DEFAULT_FLAGS, NULL, ICON_NONE); col = uiLayoutColumn(split, false); uiTemplateColorPicker(col, ptr, "power", 1, 1, 0, 1); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "power", 0, NULL, ICON_NONE); + uiItemR(row, ptr, "power", DEFAULT_FLAGS, NULL, ICON_NONE); col = uiLayoutColumn(split, false); uiTemplateColorPicker(col, ptr, "slope", 1, 1, 0, 1); row = uiLayoutRow(col, false); - uiItemR(row, ptr, "slope", 0, NULL, ICON_NONE); + uiItemR(row, ptr, "slope", DEFAULT_FLAGS, NULL, ICON_NONE); } } static void node_composit_buts_colorbalance_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "correction_method", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "correction_method", DEFAULT_FLAGS, NULL, ICON_NONE); if (RNA_enum_get(ptr, "correction_method") == 0) { uiTemplateColorPicker(layout, ptr, "lift", 1, 1, 0, 1); - uiItemR(layout, ptr, "lift", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "lift", DEFAULT_FLAGS, NULL, ICON_NONE); uiTemplateColorPicker(layout, ptr, "gamma", 1, 1, 1, 1); - uiItemR(layout, ptr, "gamma", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "gamma", DEFAULT_FLAGS, NULL, ICON_NONE); uiTemplateColorPicker(layout, ptr, "gain", 1, 1, 1, 1); - uiItemR(layout, ptr, "gain", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "gain", DEFAULT_FLAGS, NULL, ICON_NONE); } else { uiTemplateColorPicker(layout, ptr, "offset", 1, 1, 0, 1); - uiItemR(layout, ptr, "offset", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "offset", DEFAULT_FLAGS, NULL, ICON_NONE); uiTemplateColorPicker(layout, ptr, "power", 1, 1, 0, 1); - uiItemR(layout, ptr, "power", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "power", DEFAULT_FLAGS, NULL, ICON_NONE); uiTemplateColorPicker(layout, ptr, "slope", 1, 1, 0, 1); - uiItemR(layout, ptr, "slope", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "slope", DEFAULT_FLAGS, NULL, ICON_NONE); } } @@ -2144,7 +2147,7 @@ static void node_composit_buts_huecorrect(uiLayout *layout, bContext *UNUSED(C), static void node_composit_buts_ycc(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE); } static void node_composit_buts_movieclip(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -2181,19 +2184,19 @@ static void node_composit_buts_stabilize2d(uiLayout *layout, bContext *C, Pointe return; } - uiItemR(layout, ptr, "filter_type", 0, "", ICON_NONE); - uiItemR(layout, ptr, "invert", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(layout, ptr, "invert", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_translate(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "use_relative", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "wrap_axis", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_relative", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "wrap_axis", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_transform(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "filter_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "filter_type", DEFAULT_FLAGS, "", ICON_NONE); } static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -2207,7 +2210,7 @@ static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, Po return; } - uiItemR(layout, ptr, "distortion_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "distortion_type", DEFAULT_FLAGS, "", ICON_NONE); } static void node_composit_buts_colorcorrection(uiLayout *layout, @@ -2217,9 +2220,9 @@ static void node_composit_buts_colorcorrection(uiLayout *layout, uiLayout *row; row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "red", 0, NULL, ICON_NONE); - uiItemR(row, ptr, "green", 0, NULL, ICON_NONE); - uiItemR(row, ptr, "blue", 0, NULL, ICON_NONE); + uiItemR(row, ptr, "red", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "green", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "blue", DEFAULT_FLAGS, NULL, ICON_NONE); row = uiLayoutRow(layout, false); uiItemL(row, "", ICON_NONE); @@ -2231,39 +2234,39 @@ static void node_composit_buts_colorcorrection(uiLayout *layout, row = uiLayoutRow(layout, false); uiItemL(row, IFACE_("Master"), ICON_NONE); - uiItemR(row, ptr, "master_saturation", UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "master_contrast", UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "master_gamma", UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "master_gain", UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "master_lift", UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "master_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "master_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "master_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "master_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "master_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); row = uiLayoutRow(layout, false); uiItemL(row, IFACE_("Highlights"), ICON_NONE); - uiItemR(row, ptr, "highlights_saturation", UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "highlights_contrast", UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "highlights_gamma", UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "highlights_gain", UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "highlights_lift", UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "highlights_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "highlights_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "highlights_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "highlights_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "highlights_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); row = uiLayoutRow(layout, false); uiItemL(row, IFACE_("Midtones"), ICON_NONE); - uiItemR(row, ptr, "midtones_saturation", UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "midtones_contrast", UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "midtones_gamma", UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "midtones_gain", UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "midtones_lift", UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "midtones_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "midtones_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "midtones_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "midtones_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "midtones_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); row = uiLayoutRow(layout, false); uiItemL(row, IFACE_("Shadows"), ICON_NONE); - uiItemR(row, ptr, "shadows_saturation", UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "shadows_contrast", UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "shadows_gamma", UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "shadows_gain", UI_ITEM_R_SLIDER, "", ICON_NONE); - uiItemR(row, ptr, "shadows_lift", UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "shadows_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "shadows_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "shadows_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "shadows_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); + uiItemR(row, ptr, "shadows_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, "", ICON_NONE); row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "midtones_start", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_end", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); } static void node_composit_buts_colorcorrection_ex(uiLayout *layout, @@ -2273,48 +2276,48 @@ static void node_composit_buts_colorcorrection_ex(uiLayout *layout, uiLayout *row; row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "red", 0, NULL, ICON_NONE); - uiItemR(row, ptr, "green", 0, NULL, ICON_NONE); - uiItemR(row, ptr, "blue", 0, NULL, ICON_NONE); + uiItemR(row, ptr, "red", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "green", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "blue", DEFAULT_FLAGS, NULL, ICON_NONE); row = layout; uiItemL(row, IFACE_("Saturation"), ICON_NONE); - uiItemR(row, ptr, "master_saturation", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "highlights_saturation", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_saturation", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "shadows_saturation", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "master_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "highlights_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "midtones_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "shadows_saturation", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); uiItemL(row, IFACE_("Contrast"), ICON_NONE); - uiItemR(row, ptr, "master_contrast", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "highlights_contrast", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_contrast", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "shadows_contrast", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "master_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "highlights_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "midtones_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "shadows_contrast", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); uiItemL(row, IFACE_("Gamma"), ICON_NONE); - uiItemR(row, ptr, "master_gamma", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "highlights_gamma", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_gamma", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "shadows_gamma", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "master_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "highlights_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "midtones_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "shadows_gamma", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); uiItemL(row, IFACE_("Gain"), ICON_NONE); - uiItemR(row, ptr, "master_gain", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "highlights_gain", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_gain", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "shadows_gain", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "master_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "highlights_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "midtones_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "shadows_gain", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); uiItemL(row, IFACE_("Lift"), ICON_NONE); - uiItemR(row, ptr, "master_lift", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "highlights_lift", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_lift", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "shadows_lift", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "master_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "highlights_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "midtones_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "shadows_lift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); row = uiLayoutRow(layout, false); - uiItemR(row, ptr, "midtones_start", 0, NULL, ICON_NONE); - uiItemR(row, ptr, "midtones_end", 0, NULL, ICON_NONE); + uiItemR(row, ptr, "midtones_start", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "midtones_end", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "check", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "check", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_switch_view_ex(uiLayout *layout, @@ -2336,32 +2339,32 @@ static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), Po uiLayout *row; row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "x", 0, NULL, ICON_NONE); - uiItemR(row, ptr, "y", 0, NULL, ICON_NONE); + uiItemR(row, ptr, "x", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "y", DEFAULT_FLAGS, NULL, ICON_NONE); row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "width", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "height", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(layout, ptr, "rotation", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "mask_type", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_bokehimage(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "flaps", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "angle", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "rounding", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(layout, ptr, "catadioptric", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(layout, ptr, "shift", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(layout, ptr, "flaps", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "angle", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "rounding", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(layout, ptr, "catadioptric", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(layout, ptr, "shift", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); } static void node_composit_buts_bokehblur(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "use_variable_size", 0, NULL, ICON_NONE); - // uiItemR(layout, ptr, "f_stop", 0, NULL, ICON_NONE); // UNUSED - uiItemR(layout, ptr, "blur_max", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_extended_bounds", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_variable_size", DEFAULT_FLAGS, NULL, ICON_NONE); + // uiItemR(layout, ptr, "f_stop", DEFAULT_FLAGS, NULL, ICON_NONE); // UNUSED + uiItemR(layout, ptr, "blur_max", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_extended_bounds", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_backdrop_viewer( @@ -2487,36 +2490,36 @@ static void node_composit_buts_ellipsemask(uiLayout *layout, bContext *UNUSED(C) { uiLayout *row; row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "x", 0, NULL, ICON_NONE); - uiItemR(row, ptr, "y", 0, NULL, ICON_NONE); + uiItemR(row, ptr, "x", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(row, ptr, "y", DEFAULT_FLAGS, NULL, ICON_NONE); row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "width", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(row, ptr, "height", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "width", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(row, ptr, "height", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(layout, ptr, "rotation", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "mask_type", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "rotation", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "mask_type", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_composite(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "use_alpha", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_viewer(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "use_alpha", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_viewer_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayout *col; - uiItemR(layout, ptr, "use_alpha", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "tile_order", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_alpha", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "tile_order", DEFAULT_FLAGS, NULL, ICON_NONE); if (RNA_enum_get(ptr, "tile_order") == 0) { col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "center_x", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "center_y", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "center_x", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(col, ptr, "center_y", DEFAULT_FLAGS, NULL, ICON_NONE); } } @@ -2525,19 +2528,19 @@ static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *p bNode *node = ptr->data; uiTemplateID(layout, C, ptr, "mask", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false, NULL); - uiItemR(layout, ptr, "use_feather", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_feather", DEFAULT_FLAGS, NULL, ICON_NONE); - uiItemR(layout, ptr, "size_source", 0, "", ICON_NONE); + uiItemR(layout, ptr, "size_source", DEFAULT_FLAGS, "", ICON_NONE); if (node->custom1 & (CMP_NODEFLAG_MASK_FIXED | CMP_NODEFLAG_MASK_FIXED_SCENE)) { - uiItemR(layout, ptr, "size_x", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "size_y", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "size_x", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "size_y", DEFAULT_FLAGS, NULL, ICON_NONE); } - uiItemR(layout, ptr, "use_motion_blur", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, NULL, ICON_NONE); if (node->custom1 & CMP_NODEFLAG_MASK_MOTION_BLUR) { - uiItemR(layout, ptr, "motion_blur_samples", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "motion_blur_shutter", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, NULL, ICON_NONE); } } @@ -2563,18 +2566,18 @@ static void node_composit_buts_keying(uiLayout *layout, bContext *UNUSED(C), Poi { /* bNode *node = ptr->data; */ /* UNUSED */ - uiItemR(layout, ptr, "blur_pre", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "screen_balance", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "despill_factor", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "despill_balance", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "edge_kernel_radius", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "edge_kernel_tolerance", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "clip_black", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "clip_white", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "dilate_distance", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "feather_falloff", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "feather_distance", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "blur_post", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "blur_pre", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "screen_balance", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "despill_factor", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "despill_balance", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "edge_kernel_radius", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "edge_kernel_tolerance", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "clip_black", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "clip_white", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "dilate_distance", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "feather_falloff", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "feather_distance", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "blur_post", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -2606,13 +2609,13 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN uiItemPointerR(col, ptr, "track_name", &object_ptr, "tracks", "", ICON_ANIM_DATA); } else { - uiItemR(layout, ptr, "track_name", 0, "", ICON_ANIM_DATA); + uiItemR(layout, ptr, "track_name", DEFAULT_FLAGS, "", ICON_ANIM_DATA); } - uiItemR(layout, ptr, "position", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "position", DEFAULT_FLAGS, NULL, ICON_NONE); if (ELEM(node->custom1, CMP_TRACKPOS_RELATIVE_FRAME, CMP_TRACKPOS_ABSOLUTE_FRAME)) { - uiItemR(layout, ptr, "frame_relative", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "frame_relative", DEFAULT_FLAGS, NULL, ICON_NONE); } } } @@ -2651,10 +2654,10 @@ static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, P } } - uiItemR(layout, ptr, "use_motion_blur", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_motion_blur", DEFAULT_FLAGS, NULL, ICON_NONE); if (data->flag & CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR) { - uiItemR(layout, ptr, "motion_blur_samples", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "motion_blur_shutter", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "motion_blur_samples", DEFAULT_FLAGS, NULL, ICON_NONE); + uiItemR(layout, ptr, "motion_blur_shutter", DEFAULT_FLAGS, NULL, ICON_NONE); } } @@ -2666,8 +2669,8 @@ static void node_composit_buts_cornerpin(uiLayout *UNUSED(layout), static void node_composit_buts_sunbeams(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "source", UI_ITEM_R_EXPAND, "", ICON_NONE); - uiItemR(layout, ptr, "ray_length", UI_ITEM_R_SLIDER, NULL, ICON_NONE); + uiItemR(layout, ptr, "source", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, "", ICON_NONE); + uiItemR(layout, ptr, "ray_length", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, NULL, ICON_NONE); } static void node_composit_buts_cryptomatte(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -2680,7 +2683,7 @@ static void node_composit_buts_cryptomatte(uiLayout *layout, bContext *UNUSED(C) uiTemplateCryptoPicker(row, ptr, "add"); uiTemplateCryptoPicker(row, ptr, "remove"); - uiItemR(col, ptr, "matte_id", 0, "", ICON_NONE); + uiItemR(col, ptr, "matte_id", DEFAULT_FLAGS, "", ICON_NONE); } static void node_composit_buts_cryptomatte_ex(uiLayout *layout, @@ -2695,7 +2698,7 @@ static void node_composit_buts_brightcontrast(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "use_premultiply", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_premultiply", DEFAULT_FLAGS, NULL, ICON_NONE); } static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -2708,7 +2711,7 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po } #endif - uiItemR(layout, ptr, "use_hdr", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_hdr", DEFAULT_FLAGS, NULL, ICON_NONE); } /* only once called */ @@ -2958,12 +2961,12 @@ static void node_texture_buts_bricks(uiLayout *layout, bContext *UNUSED(C), Poin uiLayout *col; col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "offset", UI_ITEM_R_SLIDER, IFACE_("Offset"), ICON_NONE); - uiItemR(col, ptr, "offset_frequency", 0, IFACE_("Frequency"), ICON_NONE); + uiItemR(col, ptr, "offset", DEFAULT_FLAGS | UI_ITEM_R_SLIDER, IFACE_("Offset"), ICON_NONE); + uiItemR(col, ptr, "offset_frequency", DEFAULT_FLAGS, IFACE_("Frequency"), ICON_NONE); col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "squash", 0, IFACE_("Squash"), ICON_NONE); - uiItemR(col, ptr, "squash_frequency", 0, IFACE_("Frequency"), ICON_NONE); + uiItemR(col, ptr, "squash", DEFAULT_FLAGS, IFACE_("Squash"), ICON_NONE); + uiItemR(col, ptr, "squash_frequency", DEFAULT_FLAGS, IFACE_("Frequency"), ICON_NONE); } static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -2980,68 +2983,73 @@ static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), Pointe switch (tex->type) { case TEX_BLEND: - uiItemR(col, &tex_ptr, "progression", 0, "", ICON_NONE); + uiItemR(col, &tex_ptr, "progression", DEFAULT_FLAGS, "", ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "use_flip_axis", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, &tex_ptr, "use_flip_axis", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); break; case TEX_MARBLE: row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "marble_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, &tex_ptr, "marble_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "noise_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "noise_basis", 0, "", ICON_NONE); + uiItemR(row, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "noise_basis_2", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); break; case TEX_MAGIC: - uiItemR(col, &tex_ptr, "noise_depth", 0, NULL, ICON_NONE); + uiItemR(col, &tex_ptr, "noise_depth", DEFAULT_FLAGS, NULL, ICON_NONE); break; case TEX_STUCCI: row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "stucci_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, &tex_ptr, "stucci_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "noise_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); - uiItemR(col, &tex_ptr, "noise_basis", 0, "", ICON_NONE); + uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); break; case TEX_WOOD: - uiItemR(col, &tex_ptr, "noise_basis", 0, "", ICON_NONE); - uiItemR(col, &tex_ptr, "wood_type", 0, "", ICON_NONE); + uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(col, &tex_ptr, "wood_type", DEFAULT_FLAGS, "", ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "noise_basis_2", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, &tex_ptr, "noise_basis_2", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); row = uiLayoutRow(col, false); uiLayoutSetActive(row, !(ELEM(tex->stype, TEX_BAND, TEX_RING))); - uiItemR(row, &tex_ptr, "noise_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); break; case TEX_CLOUDS: - uiItemR(col, &tex_ptr, "noise_basis", 0, "", ICON_NONE); + uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "cloud_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(row, &tex_ptr, "cloud_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); row = uiLayoutRow(col, false); - uiItemR(row, &tex_ptr, "noise_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); - uiItemR(col, &tex_ptr, "noise_depth", UI_ITEM_R_EXPAND, IFACE_("Depth"), ICON_NONE); + uiItemR(row, &tex_ptr, "noise_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(col, + &tex_ptr, + "noise_depth", + DEFAULT_FLAGS | UI_ITEM_R_EXPAND, + IFACE_("Depth"), + ICON_NONE); break; case TEX_DISTNOISE: - uiItemR(col, &tex_ptr, "noise_basis", 0, "", ICON_NONE); - uiItemR(col, &tex_ptr, "noise_distortion", 0, "", ICON_NONE); + uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(col, &tex_ptr, "noise_distortion", DEFAULT_FLAGS, "", ICON_NONE); break; case TEX_MUSGRAVE: - uiItemR(col, &tex_ptr, "musgrave_type", 0, "", ICON_NONE); - uiItemR(col, &tex_ptr, "noise_basis", 0, "", ICON_NONE); + uiItemR(col, &tex_ptr, "musgrave_type", DEFAULT_FLAGS, "", ICON_NONE); + uiItemR(col, &tex_ptr, "noise_basis", DEFAULT_FLAGS, "", ICON_NONE); break; case TEX_VORONOI: - uiItemR(col, &tex_ptr, "distance_metric", 0, "", ICON_NONE); + uiItemR(col, &tex_ptr, "distance_metric", DEFAULT_FLAGS, "", ICON_NONE); if (tex->vn_distm == TEX_MINKOVSKY) { - uiItemR(col, &tex_ptr, "minkovsky_exponent", 0, NULL, ICON_NONE); + uiItemR(col, &tex_ptr, "minkovsky_exponent", DEFAULT_FLAGS, NULL, ICON_NONE); } - uiItemR(col, &tex_ptr, "color_mode", 0, "", ICON_NONE); + uiItemR(col, &tex_ptr, "color_mode", DEFAULT_FLAGS, "", ICON_NONE); break; } } @@ -3071,7 +3079,7 @@ static void node_texture_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA static void node_texture_buts_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "filepath", 0, "", ICON_NONE); + uiItemR(layout, ptr, "filepath", DEFAULT_FLAGS, "", ICON_NONE); } /* only once called */ @@ -3129,33 +3137,33 @@ static void node_simulation_buts_particle_simulation(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "name", 0, "", ICON_NONE); + uiItemR(layout, ptr, "name", DEFAULT_FLAGS, "", ICON_NONE); } static void node_simulation_buts_particle_time_step_event(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE); } static void node_simulation_buts_particle_attribute(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "data_type", DEFAULT_FLAGS, "", ICON_NONE); } static void node_simulation_buts_set_particle_attribute(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "data_type", DEFAULT_FLAGS, "", ICON_NONE); } static void node_simulation_buts_time(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); + uiItemR(layout, ptr, "mode", DEFAULT_FLAGS, "", ICON_NONE); } static void node_simulation_set_butfunc(bNodeType *ntype) @@ -3183,19 +3191,19 @@ static void node_simulation_set_butfunc(bNodeType *ntype) static void node_function_buts_boolean_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); + uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE); } static void node_function_buts_float_compare(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); + uiItemR(layout, ptr, "operation", DEFAULT_FLAGS, "", ICON_NONE); } static void node_function_buts_switch(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "data_type", DEFAULT_FLAGS, "", ICON_NONE); } static void node_function_set_butfunc(bNodeType *ntype) @@ -3462,7 +3470,7 @@ static void std_node_socket_draw( case SOCK_FLOAT: case SOCK_INT: case SOCK_BOOLEAN: - uiItemR(layout, ptr, "default_value", 0, text, 0); + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); break; case SOCK_VECTOR: if (sock->flag & SOCK_COMPACT) { @@ -3470,11 +3478,11 @@ static void std_node_socket_draw( } else { if (sock->typeinfo->subtype == PROP_DIRECTION) { - uiItemR(layout, ptr, "default_value", 0, "", ICON_NONE); + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, "", ICON_NONE); } else { uiLayout *column = uiLayoutColumn(layout, true); - uiItemR(column, ptr, "default_value", 0, text, ICON_NONE); + uiItemR(column, ptr, "default_value", DEFAULT_FLAGS, text, ICON_NONE); } } break; @@ -3482,15 +3490,15 @@ static void std_node_socket_draw( case SOCK_STRING: { uiLayout *row = uiLayoutSplit(layout, 0.5f, false); uiItemL(row, text, 0); - uiItemR(row, ptr, "default_value", 0, "", 0); + uiItemR(row, ptr, "default_value", DEFAULT_FLAGS, "", 0); break; } case SOCK_OBJECT: { - uiItemR(layout, ptr, "default_value", 0, text, 0); + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); break; } case SOCK_IMAGE: { - uiItemR(layout, ptr, "default_value", 0, text, 0); + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); break; } default: @@ -3508,32 +3516,32 @@ static void std_node_socket_interface_draw(bContext *UNUSED(C), uiLayout *layout switch (type) { case SOCK_FLOAT: { uiLayout *row; - uiItemR(layout, ptr, "default_value", 0, NULL, 0); + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, NULL, 0); row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "min_value", 0, IFACE_("Min"), 0); - uiItemR(row, ptr, "max_value", 0, IFACE_("Max"), 0); + uiItemR(row, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), 0); + uiItemR(row, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), 0); break; } case SOCK_INT: { uiLayout *row; - uiItemR(layout, ptr, "default_value", 0, NULL, 0); + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, NULL, 0); row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "min_value", 0, IFACE_("Min"), 0); - uiItemR(row, ptr, "max_value", 0, IFACE_("Max"), 0); + uiItemR(row, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), 0); + uiItemR(row, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), 0); break; } case SOCK_VECTOR: { uiLayout *row; - uiItemR(layout, ptr, "default_value", UI_ITEM_R_EXPAND, NULL, 0); + uiItemR(layout, ptr, "default_value", UI_ITEM_R_EXPAND, NULL, DEFAULT_FLAGS); row = uiLayoutRow(layout, true); - uiItemR(row, ptr, "min_value", 0, IFACE_("Min"), 0); - uiItemR(row, ptr, "max_value", 0, IFACE_("Max"), 0); + uiItemR(row, ptr, "min_value", DEFAULT_FLAGS, IFACE_("Min"), 0); + uiItemR(row, ptr, "max_value", DEFAULT_FLAGS, IFACE_("Max"), 0); break; } case SOCK_BOOLEAN: case SOCK_RGBA: case SOCK_STRING: { - uiItemR(layout, ptr, "default_value", 0, NULL, 0); + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, NULL, 0); break; } } diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.c index 50c32da4b5a..547abaf8809 100644 --- a/source/blender/editors/space_node/node_select.c +++ b/source/blender/editors/space_node/node_select.c @@ -766,7 +766,10 @@ static int node_lasso_select_invoke(bContext *C, wmOperator *op, const wmEvent * return WM_gesture_lasso_invoke(C, op, event); } -static bool do_lasso_select_node(bContext *C, const int mcords[][2], short moves, eSelectOp sel_op) +static bool do_lasso_select_node(bContext *C, + const int mcoords[][2], + short mcoords_len, + eSelectOp sel_op) { SpaceNode *snode = CTX_wm_space_node(C); bNode *node; @@ -783,7 +786,7 @@ static bool do_lasso_select_node(bContext *C, const int mcords[][2], short moves } /* get rectangle from operator */ - BLI_lasso_boundbox(&rect, mcords, moves); + BLI_lasso_boundbox(&rect, mcoords, mcoords_len); /* do actual selection */ for (node = snode->edittree->nodes.first; node; node = node->next) { @@ -799,7 +802,7 @@ static bool do_lasso_select_node(bContext *C, const int mcords[][2], short moves if (UI_view2d_view_to_region_clip( ®ion->v2d, cent[0], cent[1], &screen_co[0], &screen_co[1]) && BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) && - BLI_lasso_is_point_inside(mcords, moves, screen_co[0], screen_co[1], INT_MAX)) { + BLI_lasso_is_point_inside(mcoords, mcoords_len, screen_co[0], screen_co[1], INT_MAX)) { nodeSetSelected(node, select); changed = true; } @@ -814,15 +817,15 @@ static bool do_lasso_select_node(bContext *C, const int mcords[][2], short moves static int node_lasso_select_exec(bContext *C, wmOperator *op) { - int mcords_tot; - const int(*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot); + int mcoords_len; + const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len); - if (mcords) { + if (mcoords) { const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); - do_lasso_select_node(C, mcords, mcords_tot, sel_op); + do_lasso_select_node(C, mcoords, mcoords_len, sel_op); - MEM_freeN((void *)mcords); + MEM_freeN((void *)mcoords); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.c index 91fe6a97432..87b1f662b59 100644 --- a/source/blender/editors/space_node/node_templates.c +++ b/source/blender/editors/space_node/node_templates.c @@ -716,18 +716,14 @@ static void ui_node_draw_node( uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, int depth) { bNodeSocket *input; - uiLayout *col, *split; PointerRNA nodeptr; RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr); if (node->typeinfo->draw_buttons) { if (node->type != NODE_GROUP) { - split = uiLayoutSplit(layout, 0.5f, false); - col = uiLayoutColumn(split, false); - col = uiLayoutColumn(split, false); - - node->typeinfo->draw_buttons(col, C, &nodeptr); + uiLayoutSetPropSep(layout, true); + node->typeinfo->draw_buttons(layout, C, &nodeptr); } } @@ -741,12 +737,9 @@ static void ui_node_draw_input( { PointerRNA inputptr, nodeptr; uiBlock *block = uiLayoutGetBlock(layout); - uiBut *bt; - uiLayout *split, *row, *col; + uiLayout *row = NULL; bNode *lnode; - char label[UI_MAX_NAME_STR]; - int i, indent = (depth > 1) ? 2 * (depth - 1) : 0; - int dependency_loop; + bool dependency_loop; if (input->flag & SOCK_UNAVAIL) { return; @@ -765,48 +758,43 @@ static void ui_node_draw_input( RNA_pointer_create(&ntree->id, &RNA_NodeSocket, input, &inputptr); RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr); - /* indented label */ - for (i = 0; i < indent; i++) { - label[i] = ' '; - } - label[indent] = '\0'; - BLI_snprintf(label + indent, UI_MAX_NAME_STR - indent, "%s", IFACE_(input->name)); + row = uiLayoutRow(layout, true); + /* Decorations are added manually here. */ + uiLayoutSetPropDecorate(row, false); - /* split in label and value */ - split = uiLayoutSplit(layout, 0.5f, false); + uiPropertySplitWrapper split_wrapper = uiItemPropertySplitWrapperCreate(row); + /* Empty decorator item for alignment. */ + bool add_dummy_decorator = false; - row = uiLayoutRow(split, true); + { + uiLayout *sub = uiLayoutRow(split_wrapper.label_column, true); - if (depth > 0) { - UI_block_emboss_set(block, UI_EMBOSS_NONE); + if (depth > 0) { + UI_block_emboss_set(block, UI_EMBOSS_NONE); - if (lnode && - (lnode->inputs.first || (lnode->typeinfo->draw_buttons && lnode->type != NODE_GROUP))) { - int icon = (input->flag & SOCK_COLLAPSED) ? ICON_DISCLOSURE_TRI_RIGHT : - ICON_DISCLOSURE_TRI_DOWN; - uiItemR(row, &inputptr, "show_expanded", UI_ITEM_R_ICON_ONLY, "", icon); - } - else { - uiItemL(row, "", ICON_BLANK1); - } + if (lnode && + (lnode->inputs.first || (lnode->typeinfo->draw_buttons && lnode->type != NODE_GROUP))) { + int icon = (input->flag & SOCK_COLLAPSED) ? ICON_DISCLOSURE_TRI_RIGHT : + ICON_DISCLOSURE_TRI_DOWN; + uiItemR(sub, &inputptr, "show_expanded", UI_ITEM_R_ICON_ONLY, "", icon); + } - bt = block->buttons.last; - bt->rect.xmax = UI_UNIT_X / 2; + UI_block_emboss_set(block, UI_EMBOSS); + } - UI_block_emboss_set(block, UI_EMBOSS); + sub = uiLayoutRow(sub, true); + uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_RIGHT); + uiItemL(sub, IFACE_(input->name), ICON_NONE); } - uiItemL(row, label, ICON_NONE); - bt = block->buttons.last; - bt->drawflag = UI_BUT_TEXT_RIGHT; - if (dependency_loop) { - row = uiLayoutRow(split, false); uiItemL(row, IFACE_("Dependency Loop"), ICON_ERROR); + add_dummy_decorator = true; } else if (lnode) { /* input linked to a node */ - uiTemplateNodeLink(split, C, ntree, node, input); + uiTemplateNodeLink(row, C, ntree, node, input); + add_dummy_decorator = true; if (depth == 0 || !(input->flag & SOCK_COLLAPSED)) { if (depth == 0) { @@ -817,29 +805,43 @@ static void ui_node_draw_input( } } else { - row = uiLayoutRow(split, true); + row = uiLayoutRow(row, true); uiTemplateNodeLink(row, C, ntree, node, input); + if (input->flag & SOCK_HIDE_VALUE) { + add_dummy_decorator = true; + } /* input not linked, show value */ - if (!(input->flag & SOCK_HIDE_VALUE)) { + else { + uiLayout *sub = row; + switch (input->type) { + case SOCK_VECTOR: + if (input->type == SOCK_VECTOR) { + uiItemS(row); + sub = uiLayoutColumn(row, true); + } + ATTR_FALLTHROUGH; case SOCK_FLOAT: case SOCK_INT: case SOCK_BOOLEAN: case SOCK_RGBA: case SOCK_STRING: - uiItemR(row, &inputptr, "default_value", 0, "", ICON_NONE); - break; - case SOCK_VECTOR: - uiItemS(row); - col = uiLayoutColumn(row, false); - uiItemR(col, &inputptr, "default_value", 0, "", ICON_NONE); + uiItemR(sub, &inputptr, "default_value", 0, "", ICON_NONE); + uiItemDecoratorR( + split_wrapper.decorate_column, &inputptr, "default_value", RNA_NO_INDEX); break; + default: + add_dummy_decorator = true; } } } + if (add_dummy_decorator) { + uiItemDecoratorR(split_wrapper.decorate_column, NULL, NULL, 0); + } + /* clear */ node->flag &= ~NODE_TEST; } diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c index c77ee67b859..82ff9e06194 100644 --- a/source/blender/editors/space_outliner/outliner_collections.c +++ b/source/blender/editors/space_outliner/outliner_collections.c @@ -300,19 +300,15 @@ static TreeTraversalAction collection_find_data_to_edit(TreeElement *te, void *c return TRAVERSE_CONTINUE; } -static int collection_delete_exec(bContext *C, wmOperator *op) +void outliner_collection_delete( + bContext *C, Main *bmain, Scene *scene, ReportList *reports, bool hierarchy) { - struct wmMsgBus *mbus = CTX_wm_message_bus(C); - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - const Base *basact_prev = BASACT(view_layer); SpaceOutliner *soops = CTX_wm_space_outliner(C); + struct CollectionEditData data = { .scene = scene, .soops = soops, }; - bool hierarchy = RNA_boolean_get(op->ptr, "hierarchy"); data.collections_to_edit = BLI_gset_ptr_new(__func__); @@ -358,7 +354,7 @@ static int collection_delete_exec(bContext *C, wmOperator *op) } else { BKE_reportf( - op->reports, + reports, RPT_WARNING, "Cannot delete linked collection '%s', it is used by other linked scenes/collections", collection->id.name + 2); @@ -367,6 +363,17 @@ static int collection_delete_exec(bContext *C, wmOperator *op) } BLI_gset_free(data.collections_to_edit, NULL); +} + +static int collection_hierarchy_delete_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + const Base *basact_prev = BASACT(view_layer); + + outliner_collection_delete(C, bmain, scene, op->reports, true); DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); DEG_relations_tag_update(bmain); @@ -382,24 +389,19 @@ static int collection_delete_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void OUTLINER_OT_collection_delete(wmOperatorType *ot) +void OUTLINER_OT_collection_hierarchy_delete(wmOperatorType *ot) { /* identifiers */ - ot->name = "Delete Collection"; - ot->idname = "OUTLINER_OT_collection_delete"; - ot->description = "Delete selected collections"; + ot->name = "Delete Hierarchy"; + ot->idname = "OUTLINER_OT_collection_hierarchy_delete"; + ot->description = "Delete selected collection hierarchies"; /* api callbacks */ - ot->exec = collection_delete_exec; + ot->exec = collection_hierarchy_delete_exec; ot->poll = ED_outliner_collections_editor_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - PropertyRNA *prop = RNA_def_boolean( - ot->srna, "hierarchy", false, "Hierarchy", "Delete child objects and collections"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /** \} */ diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 469a0065e3a..8b4e38a77fd 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -83,9 +83,11 @@ #include "outliner_intern.h" -/* ************************************************************** */ +/** \} */ -/* Highlight --------------------------------------------------- */ +/* -------------------------------------------------------------------- */ +/** \name Highlight on Cursor Motion Operator + * \{ */ static int outliner_highlight_update(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { @@ -140,7 +142,11 @@ void OUTLINER_OT_highlight_update(wmOperatorType *ot) ot->poll = ED_operator_outliner_active; } -/* Toggle Open/Closed ------------------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Open/Closed Operator + * \{ */ /* Open or close a tree element, optionally toggling all children recursively */ void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all) @@ -262,8 +268,10 @@ void OUTLINER_OT_item_openclose(wmOperatorType *ot) RNA_def_boolean(ot->srna, "all", false, "All", "Close or open all items"); } +/** \} */ + /* -------------------------------------------------------------------- */ -/** \name Object Mode Enter/Exit +/** \name Object Mode Enter/Exit Utilities * \{ */ static void item_object_mode_enter_exit(bContext *C, ReportList *reports, Object *ob, bool enter) @@ -317,7 +325,9 @@ void item_object_mode_exit_cb(bContext *C, /** \} */ -/* Rename --------------------------------------------------- */ +/* -------------------------------------------------------------------- */ +/** \name Rename Operator + * \{ */ static void do_item_rename(ARegion *region, TreeElement *te, @@ -449,7 +459,11 @@ void OUTLINER_OT_item_rename(wmOperatorType *ot) ot->poll = ED_operator_outliner_active; } -/* ID delete --------------------------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name ID Delete Operator + * \{ */ static void id_delete(bContext *C, ReportList *reports, TreeElement *te, TreeStoreElem *tselem) { @@ -563,7 +577,11 @@ void OUTLINER_OT_id_delete(wmOperatorType *ot) ot->poll = ED_operator_outliner_active; } -/* ID remap --------------------------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name ID Remap Operator + * \{ */ static int outliner_id_remap_exec(bContext *C, wmOperator *op) { @@ -736,7 +754,11 @@ void id_remap_cb(bContext *C, WM_operator_properties_free(&op_props); } -/* ID copy/Paste ------------------------------------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name ID Copy Operator + * \{ */ static int outliner_id_copy_tag(SpaceOutliner *soops, ListBase *tree) { @@ -801,6 +823,12 @@ void OUTLINER_OT_id_copy(wmOperatorType *ot) ot->flag = 0; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name ID Paste Operator + * \{ */ + static int outliner_id_paste_exec(bContext *C, wmOperator *op) { char str[FILE_MAX]; @@ -835,7 +863,11 @@ void OUTLINER_OT_id_paste(wmOperatorType *ot) ot->flag = 0; } -/* Library relocate/reload --------------------------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Library Relocate Operator + * \{ */ static int lib_relocate( bContext *C, TreeElement *te, TreeStoreElem *tselem, wmOperatorType *ot, const bool reload) @@ -979,6 +1011,12 @@ static int outliner_lib_reload_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Library Reload Operator + * \{ */ + void OUTLINER_OT_lib_reload(wmOperatorType *ot) { ot->name = "Reload Library"; @@ -1002,13 +1040,11 @@ void lib_reload_cb(bContext *C, lib_relocate(C, te, tselem, ot, true); } -/* ************************************************************** */ -/* Setting Toggling Operators */ - -/* =============================================== */ -/* Toggling Utilities (Exported) */ +/** \} */ -/* Apply Settings ------------------------------- */ +/* -------------------------------------------------------------------- */ +/** \name Apply Settings Utilities + * \{ */ static int outliner_count_levels(ListBase *lb, const int curlevel) { @@ -1090,7 +1126,11 @@ bool outliner_flag_flip(ListBase *lb, short flag) return changed; } -/* Restriction Columns ------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Restriction Column Utility + * \{ */ /* same check needed for both object operation and restrict column button func * return 0 when in edit mode (cannot restrict view or select) @@ -1116,10 +1156,11 @@ int common_restrict_check(bContext *C, Object *ob) return 1; } -/* =============================================== */ -/* Outliner setting toggles */ +/** \} */ -/* Toggle Expanded (Outliner) ---------------------------------------- */ +/* -------------------------------------------------------------------- */ +/** \name Toggle Expanded (Outliner) Operator + * \{ */ static int outliner_toggle_expanded_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -1152,7 +1193,11 @@ void OUTLINER_OT_expanded_toggle(wmOperatorType *ot) /* no undo or registry, UI option */ } -/* Toggle Selected (Outliner) ---------------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Selected (Outliner) Operator + * \{ */ static int outliner_select_all_exec(bContext *C, wmOperator *op) { @@ -1202,10 +1247,11 @@ void OUTLINER_OT_select_all(wmOperatorType *ot) WM_operator_properties_select_all(ot); } -/* ************************************************************** */ -/* Hotkey Only Operators */ +/** \} */ -/* Show Active --------------------------------------------------- */ +/* -------------------------------------------------------------------- */ +/** \name View Show Active (Outliner) Operator + * \{ */ static void outliner_set_coordinates_element_recursive(SpaceOutliner *soops, TreeElement *te, @@ -1355,7 +1401,11 @@ void OUTLINER_OT_show_active(wmOperatorType *ot) ot->poll = ED_operator_outliner_active; } -/* View Panning --------------------------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Panning (Outliner) Operator + * \{ */ static int outliner_scroll_page_exec(bContext *C, wmOperator *op) { @@ -1393,10 +1443,14 @@ void OUTLINER_OT_scroll_page(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/* Search ------------------------------------------------------- */ -// TODO: probably obsolete now with filtering? +/** \} */ + +#if 0 // TODO: probably obsolete now with filtering? + +/* -------------------------------------------------------------------- */ +/** \name Search + * \{ */ -#if 0 /* find next element that has this name */ static TreeElement *outliner_find_name( @@ -1509,9 +1563,14 @@ static void outliner_find_panel( BKE_reportf(reports, RPT_WARNING, "Not found: %s", name); } } -#endif -/* Show One Level ----------------------------------------------- */ +/** \} */ + +#endif /* if 0 */ + +/* -------------------------------------------------------------------- */ +/** \name Show One Level Operator + * \{ */ /* helper function for Show/Hide one level operator */ static void outliner_openclose_level(ListBase *lb, int curlevel, int level, int open) @@ -1584,7 +1643,11 @@ void OUTLINER_OT_show_one_level(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/* Show Hierarchy ----------------------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Show Hierarchy Operator + * \{ */ /* Helper function for tree_element_shwo_hierarchy() - * recursively checks whether subtrees have any objects. */ @@ -1676,11 +1739,15 @@ void OUTLINER_OT_show_hierarchy(wmOperatorType *ot) /* no undo or registry, UI option */ } -/* ************************************************************** */ -/* ANIMATO OPERATIONS */ -/* KeyingSet and Driver Creation - Helper functions */ +/** \} */ -/* specialized poll callback for these operators to work in Datablocks view only */ +/* -------------------------------------------------------------------- */ +/** \name Animation Internal Utilities + * \{ */ + +/** + * Specialized poll callback for these operators to work in data-blocks view only. + */ static bool ed_operator_outliner_datablocks_active(bContext *C) { ScrArea *area = CTX_wm_area(C); @@ -1842,19 +1909,23 @@ static void tree_element_to_path(TreeElement *te, BLI_freelistN(&hierarchy); } -/* =============================================== */ -/* Driver Operations */ +/** \} */ -/* These operators are only available in databrowser mode for now, as - * they depend on having RNA paths and/or hierarchies available. +/* -------------------------------------------------------------------- */ +/** \name Driver Internal Utilities + * \{ */ + +/** + * Driver Operations + * + * These operators are only available in data-browser mode for now, + * as they depend on having RNA paths and/or hierarchies available. */ enum { DRIVERS_EDITMODE_ADD = 0, DRIVERS_EDITMODE_REMOVE, } /*eDrivers_EditModes*/; -/* Utilities ---------------------------------- */ - /* Recursively iterate over tree, finding and working on selected items */ static void do_outliner_drivers_editop(SpaceOutliner *soops, ListBase *tree, @@ -1930,7 +2001,11 @@ static void do_outliner_drivers_editop(SpaceOutliner *soops, } } -/* Add Operator ---------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver Add Operator + * \{ */ static int outliner_drivers_addsel_exec(bContext *C, wmOperator *op) { @@ -1965,7 +2040,11 @@ void OUTLINER_OT_drivers_add_selected(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* Remove Operator ---------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver Remove Operator + * \{ */ static int outliner_drivers_deletesel_exec(bContext *C, wmOperator *op) { @@ -2000,10 +2079,16 @@ void OUTLINER_OT_drivers_delete_selected(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* =============================================== */ -/* Keying Set Operations */ +/** \} */ -/* These operators are only available in databrowser mode for now, as +/* -------------------------------------------------------------------- */ +/** \name Keying-Set Internal Utilities + * \{ */ + +/** + * Keying-Set Operations + * + * These operators are only available in data-browser mode for now, as * they depend on having RNA paths and/or hierarchies available. */ enum { @@ -2011,8 +2096,6 @@ enum { KEYINGSET_EDITMODE_REMOVE, } /*eKeyingSet_EditModes*/; -/* Utilities ---------------------------------- */ - /* find the 'active' KeyingSet, and add if not found (if adding is allowed) */ // TODO: should this be an API func? static KeyingSet *verify_active_keyingset(Scene *scene, short add) @@ -2104,7 +2187,11 @@ static void do_outliner_keyingset_editop(SpaceOutliner *soops, } } -/* Add Operator ---------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Keying-Set Add Operator + * \{ */ static int outliner_keyingset_additems_exec(bContext *C, wmOperator *op) { @@ -2145,7 +2232,11 @@ void OUTLINER_OT_keyingset_add_selected(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* Remove Operator ---------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Keying-Set Remove Operator + * \{ */ static int outliner_keyingset_removeitems_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -2182,8 +2273,11 @@ void OUTLINER_OT_keyingset_remove_selected(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ************************************************************** */ -/* ORPHANED DATABLOCKS */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Purge Orphan Data-Blocks Operator + * \{ */ static bool ed_operator_outliner_id_orphans_active(bContext *C) { @@ -2195,7 +2289,7 @@ static bool ed_operator_outliner_id_orphans_active(bContext *C) return true; } -/* Purge Orphans Operator --------------------------------------- */ +/** \} */ static void outliner_orphans_purge_tag(ID *id, int *num_tagged) { @@ -2313,3 +2407,5 @@ void OUTLINER_OT_orphans_purge(wmOperatorType *ot) PropertyRNA *prop = RNA_def_int(ot->srna, "num_deleted", 0, 0, INT_MAX, "", "", 0, INT_MAX); RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); } + +/** \} */ diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 3032d38b7ac..f2b64bc2a4b 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -431,6 +431,7 @@ void OUTLINER_OT_animdata_operation(struct wmOperatorType *ot); void OUTLINER_OT_action_set(struct wmOperatorType *ot); void OUTLINER_OT_constraint_operation(struct wmOperatorType *ot); void OUTLINER_OT_modifier_operation(struct wmOperatorType *ot); +void OUTLINER_OT_delete(struct wmOperatorType *ot); /* ---------------------------------------------------------------- */ @@ -442,11 +443,16 @@ void outliner_keymap(struct wmKeyConfig *keyconf); bool outliner_is_collection_tree_element(const TreeElement *te); struct Collection *outliner_collection_from_tree_element(const TreeElement *te); +void outliner_collection_delete(struct bContext *C, + struct Main *bmain, + struct Scene *scene, + struct ReportList *reports, + bool hierarchy); void OUTLINER_OT_collection_new(struct wmOperatorType *ot); void OUTLINER_OT_collection_duplicate_linked(struct wmOperatorType *ot); void OUTLINER_OT_collection_duplicate(struct wmOperatorType *ot); -void OUTLINER_OT_collection_delete(struct wmOperatorType *ot); +void OUTLINER_OT_collection_hierarchy_delete(struct wmOperatorType *ot); void OUTLINER_OT_collection_objects_select(struct wmOperatorType *ot); void OUTLINER_OT_collection_objects_deselect(struct wmOperatorType *ot); void OUTLINER_OT_collection_link(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c index 9d7efc7fe46..af7d97b6950 100644 --- a/source/blender/editors/space_outliner/outliner_ops.c +++ b/source/blender/editors/space_outliner/outliner_ops.c @@ -66,6 +66,7 @@ void outliner_operatortypes(void) WM_operatortype_append(OUTLINER_OT_action_set); WM_operatortype_append(OUTLINER_OT_constraint_operation); WM_operatortype_append(OUTLINER_OT_modifier_operation); + WM_operatortype_append(OUTLINER_OT_delete); WM_operatortype_append(OUTLINER_OT_show_one_level); WM_operatortype_append(OUTLINER_OT_show_active); @@ -93,7 +94,7 @@ void outliner_operatortypes(void) WM_operatortype_append(OUTLINER_OT_collection_new); WM_operatortype_append(OUTLINER_OT_collection_duplicate_linked); WM_operatortype_append(OUTLINER_OT_collection_duplicate); - WM_operatortype_append(OUTLINER_OT_collection_delete); + WM_operatortype_append(OUTLINER_OT_collection_hierarchy_delete); WM_operatortype_append(OUTLINER_OT_collection_objects_select); WM_operatortype_append(OUTLINER_OT_collection_objects_deselect); WM_operatortype_append(OUTLINER_OT_collection_link); diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index e87e71f0689..fa8422573ab 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -357,20 +357,24 @@ static eOLDrawState tree_element_set_active_object(bContext *C, } te_ob = outliner_find_id(soops, &soops->tree, (ID *)ob); - if (te_ob != NULL) { + if (te_ob != NULL && te_ob != te) { parent_tselem = TREESTORE(te_ob); } - if (!ELEM(NULL, parent_tselem, base)) { + if (base) { if (set == OL_SETSEL_EXTEND) { /* swap select */ if (base->flag & BASE_SELECTED) { ED_object_base_select(base, BA_DESELECT); - parent_tselem->flag &= ~TSE_SELECTED; + if (parent_tselem) { + parent_tselem->flag &= ~TSE_SELECTED; + } } else { ED_object_base_select(base, BA_SELECT); - parent_tselem->flag |= TSE_SELECTED; + if (parent_tselem) { + parent_tselem->flag |= TSE_SELECTED; + } } } else { @@ -386,7 +390,9 @@ static eOLDrawState tree_element_set_active_object(bContext *C, BKE_view_layer_base_deselect_all(view_layer); } ED_object_base_select(base, BA_SELECT); - parent_tselem->flag |= TSE_SELECTED; + if (parent_tselem) { + parent_tselem->flag |= TSE_SELECTED; + } } if (recursive) { diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 5db5fe6f565..172d64c9c67 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -89,9 +89,9 @@ #include "outliner_intern.h" -/* ****************************************************** */ - -/* ************ SELECTION OPERATIONS ********* */ +/* -------------------------------------------------------------------- */ +/** \name ID/Library/Data Set/Un-link Utilities + * \{ */ static void set_operation_types(SpaceOutliner *soops, ListBase *lb, @@ -414,7 +414,12 @@ static void outliner_do_libdata_operation(bContext *C, } } -/* ******************************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Scene Menu Operator + * \{ */ + typedef enum eOutliner_PropSceneOps { OL_SCENE_OP_DELETE = 1, } eOutliner_PropSceneOps; @@ -502,7 +507,12 @@ void OUTLINER_OT_scene_operation(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", prop_scene_op_types, 0, "Scene Operation", ""); } -/* ******************************************** */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Search Utilities + * \{ */ /** * Stores the parent and a child element of a merged icon-row icon for @@ -643,6 +653,12 @@ static void object_select_cb(bContext *C, } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks (Selection, Users & Library) Utilities + * \{ */ + static void object_select_hierarchy_cb(bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), @@ -673,13 +689,10 @@ static void object_deselect_cb(bContext *C, } } -static void object_delete_cb(bContext *C, - ReportList *reports, - Scene *scene, - TreeElement *UNUSED(te), - TreeStoreElem *UNUSED(tsep), - TreeStoreElem *tselem, - void *UNUSED(user_data)) +static void outliner_object_delete(bContext *C, + ReportList *reports, + Scene *scene, + TreeStoreElem *tselem) { Object *ob = (Object *)tselem->id; if (ob) { @@ -885,7 +898,11 @@ void outliner_do_object_operation(bContext *C, outliner_do_object_operation_ex(C, reports, scene_act, soops, lb, operation_cb, NULL, true); } -/* ******************************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Internal Tagging Utilities + * \{ */ static void clear_animdata_cb(int UNUSED(event), TreeElement *UNUSED(te), @@ -937,7 +954,11 @@ static void refreshdrivers_animdata_cb(int UNUSED(event), } } -/* --------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Object Operation Utilities + * \{ */ typedef enum eOutliner_PropDataOps { OL_DOP_SELECT = 1, @@ -1310,13 +1331,16 @@ static void object_batch_delete_hierarchy_cb(bContext *C, } } -/* **************************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Object Menu Operator + * \{ */ enum { OL_OP_SELECT = 1, OL_OP_DESELECT, OL_OP_SELECT_HIERARCHY, - OL_OP_DELETE, OL_OP_DELETE_HIERARCHY, OL_OP_REMAP, OL_OP_LOCALIZED, /* disabled, see below */ @@ -1332,7 +1356,6 @@ static const EnumPropertyItem prop_object_op_types[] = { {OL_OP_SELECT, "SELECT", ICON_RESTRICT_SELECT_OFF, "Select", ""}, {OL_OP_DESELECT, "DESELECT", 0, "Deselect", ""}, {OL_OP_SELECT_HIERARCHY, "SELECT_HIERARCHY", 0, "Select Hierarchy", ""}, - {OL_OP_DELETE, "DELETE", ICON_X, "Delete", ""}, {OL_OP_DELETE_HIERARCHY, "DELETE_HIERARCHY", 0, "Delete Hierarchy", ""}, {OL_OP_REMAP, "REMAP", @@ -1388,29 +1411,6 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) str = "Deselect Objects"; selection_changed = true; } - else if (event == OL_OP_DELETE) { - ViewLayer *view_layer = CTX_data_view_layer(C); - const Base *basact_prev = BASACT(view_layer); - - outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_delete_cb); - - /* XXX: tree management normally happens from draw_outliner(), but when - * you're clicking to fast on Delete object from context menu in - * outliner several mouse events can be handled in one cycle without - * handling notifiers/redraw which leads to deleting the same object twice. - * cleanup tree here to prevent such cases. */ - outliner_cleanup_tree(soops); - - DEG_relations_tag_update(bmain); - str = "Delete Objects"; - DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); - if (basact_prev != BASACT(view_layer)) { - WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); - WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, LayerObjects, active); - } - selection_changed = true; - } else if (event == OL_OP_DELETE_HIERARCHY) { ViewLayer *view_layer = CTX_data_view_layer(C); const Base *basact_prev = BASACT(view_layer); @@ -1435,7 +1435,7 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) BKE_id_multi_tagged_delete(bmain); } - /* XXX: See OL_OP_DELETE comment above. */ + /* XXX: See outliner_delete_exec comment below. */ outliner_cleanup_tree(soops); DEG_relations_tag_update(bmain); @@ -1502,7 +1502,85 @@ void OUTLINER_OT_object_operation(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", prop_object_op_types, 0, "Object Operation", ""); } -/* **************************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Delete Object/Collection Operator + * \{ */ + +static void outliner_objects_delete( + bContext *C, Scene *scene, SpaceOutliner *soops, ReportList *reports, ListBase *lb) +{ + LISTBASE_FOREACH (TreeElement *, te, lb) { + TreeStoreElem *tselem = TREESTORE(te); + + if (tselem->flag & TSE_SELECTED) { + if (tselem->type == 0 && te->idcode == ID_OB) { + outliner_object_delete(C, reports, scene, tselem); + } + } + + if (TSELEM_OPEN(tselem, soops)) { + outliner_objects_delete(C, scene, soops, reports, &te->subtree); + } + } +} + +static int outliner_delete_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + SpaceOutliner *soops = CTX_wm_space_outliner(C); + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + + ViewLayer *view_layer = CTX_data_view_layer(C); + const Base *basact_prev = BASACT(view_layer); + + outliner_collection_delete(C, bmain, scene, op->reports, false); + outliner_objects_delete(C, scene, soops, op->reports, &soops->tree); + + /* Tree management normally happens from draw_outliner(), but when + * you're clicking too fast on Delete object from context menu in + * outliner several mouse events can be handled in one cycle without + * handling notifiers/redraw which leads to deleting the same object twice. + * cleanup tree here to prevent such cases. */ + outliner_cleanup_tree(soops); + + DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + + if (basact_prev != BASACT(view_layer)) { + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, LayerObjects, active); + } + + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + ED_outliner_select_sync_from_object_tag(C); + + return OPERATOR_FINISHED; +} + +void OUTLINER_OT_delete(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Delete"; + ot->idname = "OUTLINER_OT_delete"; + ot->description = "Delete selected objects and collections"; + + /* callbacks */ + ot->exec = outliner_delete_exec; + ot->poll = ED_operator_outliner_active; + + /* flags */ + ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name ID-Data Menu Operator + * \{ */ typedef enum eOutlinerIdOpTypes { OUTLINER_IDOP_INVALID = 0, @@ -1802,7 +1880,11 @@ void OUTLINER_OT_id_operation(wmOperatorType *ot) RNA_def_enum_funcs(ot->prop, outliner_id_operation_itemf); } -/* **************************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Library Menu Operator + * \{ */ typedef enum eOutlinerLibOpTypes { OL_LIB_INVALID = 0, @@ -1903,7 +1985,11 @@ void OUTLINER_OT_lib_operation(wmOperatorType *ot) ot->srna, "type", outliner_lib_op_type_items, 0, "Library Operation", ""); } -/* **************************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Outliner Set Active Action Operator + * \{ */ static void outliner_do_id_set_operation( SpaceOutliner *soops, @@ -1929,8 +2015,6 @@ static void outliner_do_id_set_operation( } } -/* ------------------------------------------ */ - static void actionset_id_cb(TreeElement *UNUSED(te), TreeStoreElem *tselem, TreeStoreElem *tsep, @@ -2028,7 +2112,11 @@ void OUTLINER_OT_action_set(wmOperatorType *ot) ot->prop = prop; } -/* **************************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Animation Menu Operator + * \{ */ typedef enum eOutliner_AnimDataOps { OUTLINER_ANIMOP_INVALID = 0, @@ -2144,7 +2232,11 @@ void OUTLINER_OT_animdata_operation(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", prop_animdata_op_types, 0, "Animation Operation", ""); } -/* **************************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Constraint Menu Operator + * \{ */ static const EnumPropertyItem prop_constraint_op_types[] = { {OL_CONSTRAINTOP_ENABLE, "ENABLE", ICON_HIDE_OFF, "Enable", ""}, @@ -2190,7 +2282,11 @@ void OUTLINER_OT_constraint_operation(wmOperatorType *ot) ot->srna, "type", prop_constraint_op_types, 0, "Constraint Operation", ""); } -/* ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Modifier Menu Operator + * \{ */ static const EnumPropertyItem prop_modifier_op_types[] = { {OL_MODIFIER_OP_TOGVIS, "TOGVIS", ICON_RESTRICT_VIEW_OFF, "Toggle viewport use", ""}, @@ -2235,7 +2331,11 @@ void OUTLINER_OT_modifier_operation(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", prop_modifier_op_types, 0, "Modifier Operation", ""); } -/* ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Data Menu Operator + * \{ */ // XXX: select linked is for RNA structs only static const EnumPropertyItem prop_data_op_types[] = { @@ -2328,7 +2428,11 @@ void OUTLINER_OT_data_operation(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", prop_data_op_types, 0, "Data Operation", ""); } -/* ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Context Menu Operator + * \{ */ static int outliner_operator_menu(bContext *C, const char *opname) { @@ -2493,4 +2597,4 @@ void OUTLINER_OT_operation(wmOperatorType *ot) ot->poll = ED_operator_outliner_active; } -/* ****************************************************** */ +/** \} */ diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 9b06de67825..7bb62b0d1e2 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -59,7 +59,7 @@ #include "BLT_translation.h" -#include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_idtype.h" #include "BKE_layer.h" #include "BKE_lib_id.h" diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 83671a0d600..e615e7be92f 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -56,6 +56,7 @@ #include "ED_anim_api.h" #include "ED_numinput.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_sequencer.h" #include "ED_space_api.h" @@ -3561,6 +3562,7 @@ static int sequencer_paste_exec(bContext *C, wmOperator *UNUSED(op)) DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + ED_outliner_select_sync_from_sequence_tag(C); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index b6bf390b2f6..fac378ae104 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -102,7 +102,7 @@ #define M_GOLDEN_RATIO_CONJUGATE 0.618033988749895f -#define VIEW3D_OVERLAY_LINEHEIGHT (0.8f * U.widget_unit) +#define VIEW3D_OVERLAY_LINEHEIGHT (0.9f * U.widget_unit) /* -------------------------------------------------------------------- */ /** \name General Functions @@ -1576,7 +1576,7 @@ void view3d_draw_region_info(const bContext *C, ARegion *region) draw_grid_unit_name(scene, rv3d, v3d, xoffset, &yoffset); } - DRW_draw_region_engine_info(xoffset, yoffset); + DRW_draw_region_engine_info(xoffset, &yoffset, VIEW3D_OVERLAY_LINEHEIGHT); } if ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0 && (v3d->overlay.flag & V3D_OVERLAY_STATS)) { diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index a53262df267..1ad3f8bb1f5 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -47,6 +47,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_select_utils.h" #include "ED_transform.h" @@ -117,6 +118,7 @@ static int view3d_pastebuffer_exec(bContext *C, wmOperator *op) } WM_event_add_notifier(C, NC_WINDOW, NULL); + ED_outliner_select_sync_from_object_tag(C); BKE_reportf(op->reports, RPT_INFO, "%d object(s) pasted", num_pasted); diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 0a272db1344..97e7cf32a41 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -408,8 +408,8 @@ typedef struct LassoSelectUserData { const rcti *rect; const rctf *rect_fl; rctf _rect_fl; - const int (*mcords)[2]; - int moves; + const int (*mcoords)[2]; + int mcoords_len; eSelectOp sel_op; /* runtime */ @@ -421,8 +421,8 @@ typedef struct LassoSelectUserData { static void view3d_userdata_lassoselect_init(LassoSelectUserData *r_data, ViewContext *vc, const rcti *rect, - const int (*mcords)[2], - const int moves, + const int (*mcoords)[2], + const int mcoords_len, const eSelectOp sel_op) { r_data->vc = vc; @@ -431,8 +431,8 @@ static void view3d_userdata_lassoselect_init(LassoSelectUserData *r_data, r_data->rect_fl = &r_data->_rect_fl; BLI_rctf_rcti_copy(&r_data->_rect_fl, rect); - r_data->mcords = mcords; - r_data->moves = moves; + r_data->mcoords = mcoords; + r_data->mcoords_len = mcoords_len; r_data->sel_op = sel_op; /* runtime */ @@ -527,7 +527,8 @@ static void do_lasso_select_pose__do_tag(void *userData, if (screen_co_a[0] != IS_CLIPPED) { points_proj_tot++; if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) && - BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_a), INT_MAX)) { + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), INT_MAX)) { is_point_done = true; } } @@ -536,22 +537,28 @@ static void do_lasso_select_pose__do_tag(void *userData, if (screen_co_b[0] != IS_CLIPPED) { points_proj_tot++; if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) && - BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_b), INT_MAX)) { + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, UNPACK2(screen_co_b), INT_MAX)) { is_point_done = true; } } /* if one of points selected, we skip the bone itself */ - if ((is_point_done == true) || - ((is_point_done == false) && (points_proj_tot == 2) && - BLI_lasso_is_edge_inside( - data->mcords, data->moves, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX))) { + if ((is_point_done == true) || ((is_point_done == false) && (points_proj_tot == 2) && + BLI_lasso_is_edge_inside(data->mcoords, + data->mcoords_len, + UNPACK2(screen_co_a), + UNPACK2(screen_co_b), + INT_MAX))) { pchan->bone->flag |= BONE_DONE; } data->is_changed |= is_point_done; } } -static void do_lasso_tag_pose(ViewContext *vc, Object *ob, const int mcords[][2], short moves) +static void do_lasso_tag_pose(ViewContext *vc, + Object *ob, + const int mcoords[][2], + short mcoords_len) { ViewContext vc_tmp; LassoSelectUserData data; @@ -564,9 +571,9 @@ static void do_lasso_tag_pose(ViewContext *vc, Object *ob, const int mcords[][2] vc_tmp = *vc; vc_tmp.obact = ob; - BLI_lasso_boundbox(&rect, mcords, moves); + BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, 0); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, 0); ED_view3d_init_mats_rv3d(vc_tmp.obact, vc->rv3d); @@ -574,8 +581,8 @@ static void do_lasso_tag_pose(ViewContext *vc, Object *ob, const int mcords[][2] } static bool do_lasso_select_objects(ViewContext *vc, - const int mcords[][2], - const short moves, + const int mcoords[][2], + const short mcoords_len, const eSelectOp sel_op) { View3D *v3d = vc->v3d; @@ -591,7 +598,7 @@ static bool do_lasso_select_objects(ViewContext *vc, const bool is_select = base->flag & BASE_SELECTED; const bool is_inside = ((ED_view3d_project_base(vc->region, base) == V3D_PROJ_RET_OK) && BLI_lasso_is_point_inside( - mcords, moves, base->sx, base->sy, IS_CLIPPED)); + mcoords, mcoords_len, base->sx, base->sy, IS_CLIPPED)); const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); if (sel_op_result != -1) { ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT); @@ -685,8 +692,8 @@ static bool do_pose_tag_select_op_exec(Base **bases, const uint bases_len, const } static bool do_lasso_select_pose(ViewContext *vc, - const int mcords[][2], - const short moves, + const int mcoords[][2], + const short mcoords_len, const eSelectOp sel_op) { uint bases_len; @@ -695,7 +702,7 @@ static bool do_lasso_select_pose(ViewContext *vc, for (int i = 0; i < bases_len; i++) { Base *base_iter = bases[i]; Object *ob_iter = base_iter->object; - do_lasso_tag_pose(vc, ob_iter, mcords, moves); + do_lasso_tag_pose(vc, ob_iter, mcoords, mcoords_len); } const bool changed_multi = do_pose_tag_select_op_exec(bases, bases_len, sel_op); @@ -715,9 +722,10 @@ static void do_lasso_select_mesh__doSelectVert(void *userData, { LassoSelectUserData *data = userData; const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT); - const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && - BLI_lasso_is_point_inside( - data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED)); + const bool is_inside = + (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED)); const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); if (sel_op_result != -1) { BM_vert_select_set(data->vc->em->bm, eve, sel_op_result); @@ -746,8 +754,10 @@ static void do_lasso_select_mesh__doSelectEdge_pass0(void *user_data, const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); const bool is_inside = (is_visible && edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b) && - BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_a), IS_CLIPPED) && - BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_b), IS_CLIPPED)); + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), IS_CLIPPED) && + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, UNPACK2(screen_co_b), IS_CLIPPED)); const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); if (sel_op_result != -1) { BM_edge_select_set(data->vc->em->bm, eed, sel_op_result); @@ -770,8 +780,8 @@ static void do_lasso_select_mesh__doSelectEdge_pass1(void *user_data, } const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT); - const bool is_inside = (is_visible && BLI_lasso_is_edge_inside(data->mcords, - data->moves, + const bool is_inside = (is_visible && BLI_lasso_is_edge_inside(data->mcoords, + data->mcoords_len, UNPACK2(screen_co_a), UNPACK2(screen_co_b), IS_CLIPPED)); @@ -789,9 +799,10 @@ static void do_lasso_select_mesh__doSelectFace(void *userData, { LassoSelectUserData *data = userData; const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT); - const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && - BLI_lasso_is_point_inside( - data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED)); + const bool is_inside = + (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED)); const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); if (sel_op_result != -1) { BM_face_select_set(data->vc->em->bm, efa, sel_op_result); @@ -801,8 +812,8 @@ static void do_lasso_select_mesh__doSelectFace(void *userData, static bool do_lasso_select_mesh(ViewContext *vc, wmGenericUserData *wm_userdata, - const int mcords[][2], - short moves, + const int mcoords[][2], + short mcoords_len, const eSelectOp sel_op) { LassoSelectUserData data; @@ -812,9 +823,9 @@ static bool do_lasso_select_mesh(ViewContext *vc, /* set editmesh */ vc->em = BKE_editmesh_from_object(vc->obedit); - BLI_lasso_boundbox(&rect, mcords, moves); + BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { if (vc->em->bm->totvertsel) { @@ -836,7 +847,7 @@ static bool do_lasso_select_mesh(ViewContext *vc, editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode); esel = wm_userdata->data; esel->select_bitmap = DRW_select_buffer_bitmap_from_poly( - vc->depsgraph, vc->region, vc->v3d, mcords, moves, &rect, NULL); + vc->depsgraph, vc->region, vc->v3d, mcoords, mcoords_len, &rect, NULL); } } @@ -897,7 +908,7 @@ static void do_lasso_select_curve__doSelect(void *userData, LassoSelectUserData *data = userData; const bool is_inside = BLI_lasso_is_point_inside( - data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED); + data->mcoords, data->mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED); if (bp) { const bool is_select = bp->f1 & SELECT; const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); @@ -930,16 +941,16 @@ static void do_lasso_select_curve__doSelect(void *userData, } static bool do_lasso_select_curve(ViewContext *vc, - const int mcords[][2], - short moves, + const int mcoords[][2], + short mcoords_len, const eSelectOp sel_op) { LassoSelectUserData data; rcti rect; - BLI_lasso_boundbox(&rect, mcords, moves); + BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { Curve *curve = (Curve *)vc->obedit->data; @@ -958,9 +969,10 @@ static void do_lasso_select_lattice__doSelect(void *userData, BPoint *bp, const { LassoSelectUserData *data = userData; const bool is_select = bp->f1 & SELECT; - const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && - BLI_lasso_is_point_inside( - data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED)); + const bool is_inside = + (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED)); const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); if (sel_op_result != -1) { SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT); @@ -968,16 +980,16 @@ static void do_lasso_select_lattice__doSelect(void *userData, BPoint *bp, const } } static bool do_lasso_select_lattice(ViewContext *vc, - const int mcords[][2], - short moves, + const int mcoords[][2], + short mcoords_len, const eSelectOp sel_op) { LassoSelectUserData data; rcti rect; - BLI_lasso_boundbox(&rect, mcords, moves); + BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_lattice_flags_set(vc->obedit, 0); @@ -1002,7 +1014,8 @@ static void do_lasso_select_armature__doSelectBone(void *userData, if (screen_co_a[0] != IS_CLIPPED) { if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) && - BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_a), INT_MAX)) { + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, UNPACK2(screen_co_a), INT_MAX)) { is_inside_flag |= BONESEL_ROOT; } } @@ -1012,7 +1025,8 @@ static void do_lasso_select_armature__doSelectBone(void *userData, if (screen_co_b[0] != IS_CLIPPED) { if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) && - BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_b), INT_MAX)) { + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, UNPACK2(screen_co_b), INT_MAX)) { is_inside_flag |= BONESEL_TIP; } } @@ -1022,8 +1036,11 @@ static void do_lasso_select_armature__doSelectBone(void *userData, if (is_ignore_flag == 0) { if (is_inside_flag == (BONE_ROOTSEL | BONE_TIPSEL) || - BLI_lasso_is_edge_inside( - data->mcords, data->moves, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX)) { + BLI_lasso_is_edge_inside(data->mcoords, + data->mcoords_len, + UNPACK2(screen_co_a), + UNPACK2(screen_co_b), + INT_MAX)) { is_inside_flag |= BONESEL_BONE; } } @@ -1033,16 +1050,16 @@ static void do_lasso_select_armature__doSelectBone(void *userData, } static bool do_lasso_select_armature(ViewContext *vc, - const int mcords[][2], - short moves, + const int mcoords[][2], + short mcoords_len, const eSelectOp sel_op) { LassoSelectUserData data; rcti rect; - BLI_lasso_boundbox(&rect, mcords, moves); + BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= ED_armature_edit_deselect_all_visible(vc->obedit); @@ -1071,9 +1088,10 @@ static void do_lasso_select_mball__doSelectElem(void *userData, { LassoSelectUserData *data = userData; const bool is_select = ml->flag & SELECT; - const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && - BLI_lasso_is_point_inside( - data->mcords, data->moves, screen_co[0], screen_co[1], INT_MAX)); + const bool is_inside = + (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, screen_co[0], screen_co[1], INT_MAX)); const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); if (sel_op_result != -1) { SET_FLAG_FROM_TEST(ml->flag, sel_op_result, SELECT); @@ -1081,8 +1099,8 @@ static void do_lasso_select_mball__doSelectElem(void *userData, } } static bool do_lasso_select_meta(ViewContext *vc, - const int mcords[][2], - short moves, + const int mcoords[][2], + short mcoords_len, const eSelectOp sel_op) { LassoSelectUserData data; @@ -1090,9 +1108,9 @@ static bool do_lasso_select_meta(ViewContext *vc, MetaBall *mb = (MetaBall *)vc->obedit->data; - BLI_lasso_boundbox(&rect, mcords, moves); + BLI_lasso_boundbox(&rect, mcoords, mcoords_len); - view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); if (SEL_OP_USE_PRE_DESELECT(sel_op)) { data.is_changed |= BKE_mball_deselect_all(mb); @@ -1113,9 +1131,10 @@ static void do_lasso_select_meshobject__doSelectVert(void *userData, { LassoSelectUserData *data = userData; const bool is_select = mv->flag & SELECT; - const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && - BLI_lasso_is_point_inside( - data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED)); + const bool is_inside = + (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) && + BLI_lasso_is_point_inside( + data->mcoords, data->mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED)); const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside); if (sel_op_result != -1) { SET_FLAG_FROM_TEST(mv->flag, sel_op_result, SELECT); @@ -1124,8 +1143,8 @@ static void do_lasso_select_meshobject__doSelectVert(void *userData, } static bool do_lasso_select_paintvert(ViewContext *vc, wmGenericUserData *wm_userdata, - const int mcords[][2], - short moves, + const int mcoords[][2], + short mcoords_len, const eSelectOp sel_op) { const bool use_zbuf = !XRAY_ENABLED(vc->v3d); @@ -1143,7 +1162,7 @@ static bool do_lasso_select_paintvert(ViewContext *vc, changed |= paintvert_deselect_all_visible(ob, SEL_DESELECT, false); } - BLI_lasso_boundbox(&rect, mcords, moves); + BLI_lasso_boundbox(&rect, mcoords, mcoords_len); struct EditSelectBuf_Cache *esel = wm_userdata->data; if (use_zbuf) { @@ -1151,7 +1170,7 @@ static bool do_lasso_select_paintvert(ViewContext *vc, editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_VERTEX); esel = wm_userdata->data; esel->select_bitmap = DRW_select_buffer_bitmap_from_poly( - vc->depsgraph, vc->region, vc->v3d, mcords, moves, &rect, NULL); + vc->depsgraph, vc->region, vc->v3d, mcoords, mcoords_len, &rect, NULL); } } @@ -1163,7 +1182,7 @@ static bool do_lasso_select_paintvert(ViewContext *vc, else { LassoSelectUserData data; - view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op); + view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, sel_op); ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); @@ -1185,8 +1204,8 @@ static bool do_lasso_select_paintvert(ViewContext *vc, } static bool do_lasso_select_paintface(ViewContext *vc, wmGenericUserData *wm_userdata, - const int mcords[][2], - short moves, + const int mcoords[][2], + short mcoords_len, const eSelectOp sel_op) { Object *ob = vc->obact; @@ -1203,14 +1222,14 @@ static bool do_lasso_select_paintface(ViewContext *vc, changed |= paintface_deselect_all_visible(vc->C, ob, SEL_DESELECT, false); } - BLI_lasso_boundbox(&rect, mcords, moves); + BLI_lasso_boundbox(&rect, mcoords, mcoords_len); struct EditSelectBuf_Cache *esel = wm_userdata->data; if (esel == NULL) { editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_FACE); esel = wm_userdata->data; esel->select_bitmap = DRW_select_buffer_bitmap_from_poly( - vc->depsgraph, vc->region, vc->v3d, mcords, moves, &rect, NULL); + vc->depsgraph, vc->region, vc->v3d, mcoords, mcoords_len, &rect, NULL); } if (esel->select_bitmap) { @@ -1224,7 +1243,7 @@ static bool do_lasso_select_paintface(ViewContext *vc, } #if 0 -static void do_lasso_select_node(int mcords[][2], short moves, const eSelectOp sel_op) +static void do_lasso_select_node(int mcoords[][2], short mcoords_len, const eSelectOp sel_op) { SpaceNode *snode = area->spacedata.first; @@ -1234,7 +1253,7 @@ static void do_lasso_select_node(int mcords[][2], short moves, const eSelectOp s float node_centf[2]; bool changed = false; - BLI_lasso_boundbox(&rect, mcords, moves); + BLI_lasso_boundbox(&rect, mcoords, mcoords_len); /* store selection in temp test flag */ for (node = snode->edittree->nodes.first; node; node = node->next) { @@ -1244,7 +1263,7 @@ static void do_lasso_select_node(int mcords[][2], short moves, const eSelectOp s ipoco_to_areaco_noclip(G.v2d, node_centf, node_cent); const bool is_select = node->flag & SELECT; const bool is_inside = (BLI_rcti_isect_pt_v(&rect, node_cent) && - BLI_lasso_is_point_inside(mcords, moves, node_cent[0], node_cent[1])); + BLI_lasso_is_point_inside(mcoords, mcoords_len, node_cent[0], node_cent[1])); const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); if (sel_op_result != -1) { SET_FLAG_FROM_TEST(node->flag, sel_op_result, SELECT); @@ -1257,8 +1276,11 @@ static void do_lasso_select_node(int mcords[][2], short moves, const eSelectOp s } #endif -static bool view3d_lasso_select( - bContext *C, ViewContext *vc, const int mcords[][2], short moves, const eSelectOp sel_op) +static bool view3d_lasso_select(bContext *C, + ViewContext *vc, + const int mcoords[][2], + short mcoords_len, + const eSelectOp sel_op) { Object *ob = CTX_data_active_object(C); bool changed_multi = false; @@ -1268,26 +1290,26 @@ static bool view3d_lasso_select( if (vc->obedit == NULL) { /* Object Mode */ if (BKE_paint_select_face_test(ob)) { - changed_multi |= do_lasso_select_paintface(vc, wm_userdata, mcords, moves, sel_op); + changed_multi |= do_lasso_select_paintface(vc, wm_userdata, mcoords, mcoords_len, sel_op); } else if (BKE_paint_select_vert_test(ob)) { - changed_multi |= do_lasso_select_paintvert(vc, wm_userdata, mcords, moves, sel_op); + changed_multi |= do_lasso_select_paintvert(vc, wm_userdata, mcoords, mcoords_len, sel_op); } else if (ob && (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))) { /* pass */ } else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) { - changed_multi |= PE_lasso_select(C, mcords, moves, sel_op); + changed_multi |= PE_lasso_select(C, mcoords, mcoords_len, sel_op); } else if (ob && (ob->mode & OB_MODE_POSE)) { - changed_multi |= do_lasso_select_pose(vc, mcords, moves, sel_op); + changed_multi |= do_lasso_select_pose(vc, mcoords, mcoords_len, sel_op); if (changed_multi) { ED_outliner_select_sync_from_pose_bone_tag(C); } } else { - changed_multi |= do_lasso_select_objects(vc, mcords, moves, sel_op); + changed_multi |= do_lasso_select_objects(vc, mcoords, mcoords_len, sel_op); if (changed_multi) { ED_outliner_select_sync_from_object_tag(C); } @@ -1300,23 +1322,23 @@ static bool view3d_lasso_select( switch (vc->obedit->type) { case OB_MESH: - changed = do_lasso_select_mesh(vc, wm_userdata, mcords, moves, sel_op); + changed = do_lasso_select_mesh(vc, wm_userdata, mcoords, mcoords_len, sel_op); break; case OB_CURVE: case OB_SURF: - changed = do_lasso_select_curve(vc, mcords, moves, sel_op); + changed = do_lasso_select_curve(vc, mcoords, mcoords_len, sel_op); break; case OB_LATTICE: - changed = do_lasso_select_lattice(vc, mcords, moves, sel_op); + changed = do_lasso_select_lattice(vc, mcoords, mcoords_len, sel_op); break; case OB_ARMATURE: - changed = do_lasso_select_armature(vc, mcords, moves, sel_op); + changed = do_lasso_select_armature(vc, mcoords, mcoords_len, sel_op); if (changed) { ED_outliner_select_sync_from_edit_bone_tag(C); } break; case OB_MBALL: - changed = do_lasso_select_meta(vc, mcords, moves, sel_op); + changed = do_lasso_select_meta(vc, mcoords, mcoords_len, sel_op); break; default: BLI_assert(!"lasso select on incorrect object type"); @@ -1342,10 +1364,10 @@ static bool view3d_lasso_select( static int view3d_lasso_select_exec(bContext *C, wmOperator *op) { ViewContext vc; - int mcords_tot; - const int(*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot); + int mcoords_len; + const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len); - if (mcords) { + if (mcoords) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); view3d_operator_needs_opengl(C); BKE_object_update_select_id(CTX_data_main(C)); @@ -1354,9 +1376,9 @@ static int view3d_lasso_select_exec(bContext *C, wmOperator *op) ED_view3d_viewcontext_init(C, &vc, depsgraph); eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); - bool changed_multi = view3d_lasso_select(C, &vc, mcords, mcords_tot, sel_op); + bool changed_multi = view3d_lasso_select(C, &vc, mcoords, mcoords_len, sel_op); - MEM_freeN((void *)mcords); + MEM_freeN((void *)mcoords); if (changed_multi) { return OPERATOR_FINISHED; @@ -1971,7 +1993,7 @@ static bool ed_object_select_pick(bContext *C, /* setup view context for argument to callbacks */ ED_view3d_viewcontext_init(C, &vc, depsgraph); - ARegion *region = CTX_wm_region(C); + const ARegion *region = CTX_wm_region(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index d376c71d867..f3306caa931 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -778,9 +778,10 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf) return keymap; } -static void transform_event_xyz_constraint(TransInfo *t, short key_type, char cmode, bool is_plane) +static void transform_event_xyz_constraint(TransInfo *t, short key_type, bool is_plane) { if (!(t->flag & T_NO_CONSTRAINT)) { + char cmode = constraintModeToChar(t); int constraint_axis, constraint_plane; const bool edit_2d = (t->flag & T_2D_EDIT) != 0; const char *msg1 = "", *msg2 = "", *msg3 = ""; @@ -824,34 +825,22 @@ static void transform_event_xyz_constraint(TransInfo *t, short key_type, char cm } } else if (!edit_2d) { - if (cmode != axis) { - /* First press, constraint to an axis. */ - t->orientation.index = 0; - const short *orientation_ptr = t->orientation.types[t->orientation.index]; - const short orientation = orientation_ptr ? *orientation_ptr : V3D_ORIENT_GLOBAL; - if (is_plane == false) { - setUserConstraint(t, orientation, constraint_axis, msg2); - } - else { - setUserConstraint(t, orientation, constraint_plane, msg3); - } - } - else { + if (ELEM(cmode, '\0', axis)) { /* Successive presses on existing axis, cycle orientation modes. */ t->orientation.index = (t->orientation.index + 1) % ARRAY_SIZE(t->orientation.types); + initTransformOrientation(t->context, t, t->orientation.types[t->orientation.index]); + } - if (t->orientation.index == 0) { - stopConstraint(t); + if (t->orientation.index == 0) { + stopConstraint(t); + } + else { + const short orientation = t->orientation.types[t->orientation.index]; + if (is_plane == false) { + setUserConstraint(t, orientation, constraint_axis, msg2); } else { - const short *orientation_ptr = t->orientation.types[t->orientation.index]; - const short orientation = orientation_ptr ? *orientation_ptr : V3D_ORIENT_GLOBAL; - if (is_plane == false) { - setUserConstraint(t, orientation, constraint_axis, msg2); - } - else { - setUserConstraint(t, orientation, constraint_plane, msg3); - } + setUserConstraint(t, orientation, constraint_plane, msg3); } } } @@ -861,7 +850,6 @@ static void transform_event_xyz_constraint(TransInfo *t, short key_type, char cm int transformEvent(TransInfo *t, const wmEvent *event) { - char cmode = constraintModeToChar(t); bool handled = false; const int modifiers_prev = t->modifiers; const int mode_prev = t->mode; @@ -1047,42 +1035,42 @@ int transformEvent(TransInfo *t, const wmEvent *event) break; case TFM_MODAL_AXIS_X: if (!(t->flag & T_NO_CONSTRAINT)) { - transform_event_xyz_constraint(t, EVT_XKEY, cmode, false); + transform_event_xyz_constraint(t, EVT_XKEY, false); t->redraw |= TREDRAW_HARD; handled = true; } break; case TFM_MODAL_AXIS_Y: if ((t->flag & T_NO_CONSTRAINT) == 0) { - transform_event_xyz_constraint(t, EVT_YKEY, cmode, false); + transform_event_xyz_constraint(t, EVT_YKEY, false); t->redraw |= TREDRAW_HARD; handled = true; } break; case TFM_MODAL_AXIS_Z: if ((t->flag & (T_NO_CONSTRAINT)) == 0) { - transform_event_xyz_constraint(t, EVT_ZKEY, cmode, false); + transform_event_xyz_constraint(t, EVT_ZKEY, false); t->redraw |= TREDRAW_HARD; handled = true; } break; case TFM_MODAL_PLANE_X: if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) { - transform_event_xyz_constraint(t, EVT_XKEY, cmode, true); + transform_event_xyz_constraint(t, EVT_XKEY, true); t->redraw |= TREDRAW_HARD; handled = true; } break; case TFM_MODAL_PLANE_Y: if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) { - transform_event_xyz_constraint(t, EVT_YKEY, cmode, true); + transform_event_xyz_constraint(t, EVT_YKEY, true); t->redraw |= TREDRAW_HARD; handled = true; } break; case TFM_MODAL_PLANE_Z: if ((t->flag & (T_NO_CONSTRAINT | T_2D_EDIT)) == 0) { - transform_event_xyz_constraint(t, EVT_ZKEY, cmode, true); + transform_event_xyz_constraint(t, EVT_ZKEY, true); t->redraw |= TREDRAW_HARD; handled = true; } @@ -1228,17 +1216,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) stopConstraint(t); } else { - if (event->shift) { - /* bit hackish... but it prevents mmb select to print the - * orientation from menu */ - float mati[3][3]; - strcpy(t->spacename, "global"); - unit_m3(mati); - initSelectConstraint(t, mati); - } - else { - initSelectConstraint(t, t->spacemtx); - } + initSelectConstraint(t, event->shift); postSelectConstraint(t); } } @@ -1699,18 +1677,6 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) ts->prop_mode = t->prop_mode; } } - - if (t->spacetype == SPACE_VIEW3D) { - if ((prop = RNA_struct_find_property(op->ptr, "orient_type")) && - !RNA_property_is_set(op->ptr, prop) && - (t->orientation.user != V3D_ORIENT_CUSTOM_MATRIX)) { - TransformOrientationSlot *orient_slot = &t->scene->orientation_slots[SCE_ORIENT_DEFAULT]; - orient_slot->type = t->orientation.user; - BLI_assert(((orient_slot->index_custom == -1) && (t->orientation.custom == NULL)) || - (BKE_scene_transform_orientation_get_index(t->scene, t->orientation.custom) == - orient_slot->index_custom)); - } - } } if (t->flag & T_MODAL) { @@ -1737,34 +1703,6 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) RNA_property_boolean_set(op->ptr, prop, (t->flag & T_NO_MIRROR) == 0); } - /* Orientation used for redo. */ - const bool use_orient_axis = (t->orient_matrix_is_set && - (RNA_struct_find_property(op->ptr, "orient_axis") != NULL)); - short orientation; - if (t->con.mode & CON_APPLY) { - orientation = t->con.orientation; - if (orientation == V3D_ORIENT_CUSTOM) { - const int orientation_index_custom = BKE_scene_transform_orientation_get_index( - t->scene, t->orientation.custom); - /* Maybe we need a t->con.custom_orientation? - * Seems like it would always match t->orientation.custom. */ - orientation = V3D_ORIENT_CUSTOM + orientation_index_custom; - BLI_assert(orientation >= V3D_ORIENT_CUSTOM); - } - } - else if ((t->orientation.user == V3D_ORIENT_CUSTOM_MATRIX) && - (prop = RNA_struct_find_property(op->ptr, "orient_matrix_type"))) { - orientation = RNA_property_enum_get(op->ptr, prop); - } - else if (use_orient_axis) { - /* We're not using an orientation, use the fallback. */ - orientation = t->orientation.unset; - } - else { - orientation = V3D_ORIENT_GLOBAL; - unit_m3(t->spacemtx); - } - if ((prop = RNA_struct_find_property(op->ptr, "orient_axis"))) { if (t->flag & T_MODAL) { if (t->con.mode & CON_APPLY) { @@ -1784,42 +1722,31 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) } } - if ((prop = RNA_struct_find_property(op->ptr, "orient_matrix"))) { - if (t->flag & T_MODAL) { - if (orientation != V3D_ORIENT_CUSTOM_MATRIX) { - if (t->flag & T_MODAL) { - RNA_enum_set(op->ptr, "orient_matrix_type", orientation); - } - } - if (t->con.mode & CON_APPLY) { - RNA_float_set_array(op->ptr, "orient_matrix", &t->con.mtx[0][0]); - } - else if (use_orient_axis) { - RNA_float_set_array(op->ptr, "orient_matrix", &t->orient_matrix[0][0]); - } - else { - RNA_float_set_array(op->ptr, "orient_matrix", &t->spacemtx[0][0]); - } + if ((prop = RNA_struct_find_property(op->ptr, "orient_type"))) { + short orient_set, orient_cur; + orient_set = RNA_property_is_set(op->ptr, prop) ? RNA_property_enum_get(op->ptr, prop) : -1; + orient_cur = t->orientation.types[t->orientation.index]; + + if (!ELEM(orient_cur, orient_set, V3D_ORIENT_CUSTOM_MATRIX)) { + RNA_property_enum_set(op->ptr, prop, orient_cur); + orient_set = orient_cur; } - } - if ((prop = RNA_struct_find_property(op->ptr, "orient_type"))) { - /* constraint orientation can be global, even if user selects something else - * so use the orientation in the constraint if set */ + if (((prop = RNA_struct_find_property(op->ptr, "orient_matrix_type")) && + !RNA_property_is_set(op->ptr, prop))) { + /* Set the first time to register on redo. */ + RNA_property_enum_set(op->ptr, prop, orient_set); - /* Use 'orient_matrix' instead. */ - if (t->flag & T_MODAL) { - if (orientation != V3D_ORIENT_CUSTOM_MATRIX) { - RNA_property_enum_set(op->ptr, prop, orientation); + if (((prop = RNA_struct_find_property(op->ptr, "orient_matrix")) && + !RNA_property_is_set(op->ptr, prop))) { + RNA_float_set_array(op->ptr, "orient_matrix", &t->spacemtx[0][0]); } } } if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis"))) { bool constraint_axis[3] = {false, false, false}; - if (t->flag & T_MODAL) { - /* Only set if needed, so we can hide in the UI when nothing is set. - * See 'transform_poll_property'. */ + if (t->idx_max == 0) { if (t->con.mode & CON_APPLY) { if (t->con.mode & CON_AXIS0) { constraint_axis[0] = true; @@ -1830,10 +1757,17 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) if (t->con.mode & CON_AXIS2) { constraint_axis[2] = true; } - } - if (ELEM(true, UNPACK3(constraint_axis))) { RNA_property_boolean_set_array(op->ptr, prop, constraint_axis); } + else { + RNA_property_unset(op->ptr, prop); + } + } + else { + constraint_axis[0] = true; + constraint_axis[1] = true; + constraint_axis[2] = true; + RNA_property_boolean_set_array(op->ptr, prop, constraint_axis); } } @@ -1959,7 +1893,13 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve unit_m3(t->spacemtx); initTransInfo(C, t, op, event); - initTransformOrientation(C, t); + + /* Use the custom orientation when it is set. */ + short orientation = t->orientation.types[0] == V3D_ORIENT_CUSTOM_MATRIX ? + V3D_ORIENT_CUSTOM_MATRIX : + t->orientation.types[t->orientation.index]; + + initTransformOrientation(C, t, orientation); if (t->spacetype == SPACE_VIEW3D) { t->draw_handle_apply = ED_region_draw_cb_activate( @@ -2141,38 +2081,8 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } /* Constraint init from operator */ - if ((t->flag & T_MODAL) || - /* For mirror operator the constraint axes are effectively the values. */ - (RNA_struct_find_property(op->ptr, "value") == NULL)) { - if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis")) && - RNA_property_is_set(op->ptr, prop)) { - bool constraint_axis[3]; - - RNA_property_boolean_get_array(op->ptr, prop, constraint_axis); - - if (constraint_axis[0] || constraint_axis[1] || constraint_axis[2]) { - t->con.mode |= CON_APPLY; - - if (constraint_axis[0]) { - t->con.mode |= CON_AXIS0; - } - if (constraint_axis[1]) { - t->con.mode |= CON_AXIS1; - } - if (constraint_axis[2]) { - t->con.mode |= CON_AXIS2; - } - - setUserConstraint(t, t->orientation.user, t->con.mode, "%s"); - } - } - } - else { - /* So we can adjust in non global orientation. */ - if (t->orientation.user != V3D_ORIENT_GLOBAL) { - t->con.mode |= CON_APPLY | CON_AXIS0 | CON_AXIS1 | CON_AXIS2; - setUserConstraint(t, t->orientation.user, t->con.mode, "%s"); - } + if (t->con.mode & CON_APPLY) { + setUserConstraint(t, t->orientation.types[t->orientation.index], t->con.mode, "%s"); } /* Don't write into the values when non-modal because they are already set from operator redo diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index f4ea0beadb6..503e7bd4691 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -531,17 +531,12 @@ typedef struct TransInfo { bool is_launch_event_tweak; struct { - /** Orientation type when when we're not constrained. - * nearly always global except for rotate which defaults to screen-space orientation. */ - short unset; - /** Orientation to use when a key is pressed. */ - short user; - /* Used when user is global. */ - short user_alt; short index; - short *types[2]; - /* this gets used when custom_orientation is V3D_ORIENT_CUSTOM */ + short types[3]; + /* this gets used when orientation.type[x] is V3D_ORIENT_CUSTOM */ struct TransformOrientation *custom; + /* this gets used when orientation.type[0] is V3D_ORIENT_CUSTOM_MATRIX */ + float custom_matrix[3][3]; } orientation; /** backup from view3d, to restore on end. */ short gizmo_flag; @@ -566,15 +561,6 @@ typedef struct TransInfo { /** Secondary axis, shear uses this. */ int orient_axis_ortho; - /** Often this matrix has similar usage to #TransInfo.spacemtx however this - * is used to define extra axes to operate on, not necessarily a space. - * - * For example, by default rotation operates on the view (`orient_matrix[2]`), - * even when the current space isn't set to the view. */ - float orient_matrix[3][3]; - /** Don't overwrite when set by operator redo defines the orientation axis. */ - bool orient_matrix_is_set; - /** remove elements if operator is canceled. */ bool remove_on_cancel; @@ -926,7 +912,7 @@ void transform_data_ext_rotate(TransData *td, float mat[3][3], bool use_drot); /*********************** Transform Orientations ******************************/ -void initTransformOrientation(struct bContext *C, TransInfo *t); +void initTransformOrientation(struct bContext *C, TransInfo *t, short orientation); /* Those two fill in mat and return non-zero on success */ bool createSpaceNormal(float mat[3][3], const float normal[3]); diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index cdff9fdf750..94cfed32841 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -713,7 +713,10 @@ void setUserConstraint(TransInfo *t, short orientation, int mode, const char fte break; case V3D_ORIENT_VIEW: BLI_snprintf(text, sizeof(text), ftext, TIP_("view")); - setConstraint(t, t->spacemtx, mode, text); + float mtx[3][3]; + copy_m3_m3(mtx, t->spacemtx); + negate_v3(mtx[2]); + setConstraint(t, mtx, mode, text); break; case V3D_ORIENT_CURSOR: BLI_snprintf(text, sizeof(text), ftext, TIP_("cursor")); @@ -984,17 +987,22 @@ void getConstraintMatrix(TransInfo *t) /*------------------------- MMB Select -------------------------------*/ -void initSelectConstraint(TransInfo *t, float mtx[3][3]) +void initSelectConstraint(TransInfo *t, bool force_global) { - copy_m3_m3(t->con.mtx, mtx); - t->con.mode |= CON_APPLY; - t->con.mode |= CON_SELECT; + short orientation; + if (force_global) { + orientation = V3D_ORIENT_GLOBAL; + } + else { + if (t->orientation.index == 0) { + t->orientation.index = 1; + initTransformOrientation(t->context, t, t->orientation.types[t->orientation.index]); + } + orientation = t->orientation.types[t->orientation.index]; + } + setUserConstraint(t, orientation, CON_APPLY | CON_SELECT, ""); setNearestAxis(t); - t->con.drawExtra = NULL; - t->con.applyVec = applyAxisConstraintVec; - t->con.applySize = applyAxisConstraintSize; - t->con.applyRot = applyAxisConstraintRot; } void selectConstraint(TransInfo *t) diff --git a/source/blender/editors/transform/transform_constraints.h b/source/blender/editors/transform/transform_constraints.h index 8938ca93ad8..c98234c83da 100644 --- a/source/blender/editors/transform/transform_constraints.h +++ b/source/blender/editors/transform/transform_constraints.h @@ -36,7 +36,7 @@ void drawPropCircle(const struct bContext *C, TransInfo *t); void startConstraint(TransInfo *t); void stopConstraint(TransInfo *t); void getConstraintMatrix(TransInfo *t); -void initSelectConstraint(TransInfo *t, float mtx[3][3]); +void initSelectConstraint(TransInfo *t, bool force_global); void selectConstraint(TransInfo *t); void postSelectConstraint(TransInfo *t); void setNearestAxis(TransInfo *t); diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index eeae4f83470..62fdaa4908e 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -447,12 +447,12 @@ static void bone_children_clear_transflag(int mode, short around, ListBase *lb) } } -/* sets transform flags in the bones - * returns total number of bones with BONE_TRANSFORM */ -int count_set_pose_transflags(Object *ob, - const int mode, - const short around, - bool has_translate_rotate[2]) +/* Sets transform flags in the bones. + * Returns total number of bones with `BONE_TRANSFORM`. */ +int transform_convert_pose_transflags_update(Object *ob, + const int mode, + const short around, + bool has_translate_rotate[2]) { bArmature *arm = ob->data; bPoseChannel *pchan; @@ -2286,7 +2286,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t) /* set BONE_TRANSFORM flags for autokey, gizmo draw might have changed them */ if (!canceled && (t->mode != TFM_DUMMY)) { - count_set_pose_transflags(ob, t->mode, t->around, NULL); + transform_convert_pose_transflags_update(ob, t->mode, t->around, NULL); } /* if target-less IK grabbing, we calculate the pchan transforms and clear flag */ diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index f975d320e62..fccaeb994e9 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -37,10 +37,10 @@ struct bKinematicConstraint; struct bPoseChannel; /* transform_convert.c */ -int count_set_pose_transflags(Object *ob, - const int mode, - const short around, - bool has_translate_rotate[2]); +int transform_convert_pose_transflags_update(Object *ob, + const int mode, + const short around, + bool has_translate_rotate[2]); void transform_autoik_update(TransInfo *t, short mode); void autokeyframe_object(struct bContext *C, struct Scene *scene, diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index 17d210f57b9..35bb7e3f0d8 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -669,7 +669,9 @@ void createTransPose(TransInfo *t) const bool mirror = ((pose->flag & POSE_MIRROR_EDIT) != 0); /* set flags and count total */ - tc->data_len = count_set_pose_transflags(ob, t->mode, t->around, has_translate_rotate); + tc->data_len = transform_convert_pose_transflags_update( + ob, t->mode, t->around, has_translate_rotate); + if (tc->data_len == 0) { continue; } @@ -685,6 +687,9 @@ void createTransPose(TransInfo *t) if (mirror) { int total_mirrored = 0; LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + /* Clear the MIRROR flag from previous runs. */ + pchan->bone->flag &= ~BONE_TRANSFORM_MIRROR; + if ((pchan->bone->flag & BONE_TRANSFORM) && BKE_pose_channel_get_mirrored(ob->pose, pchan->name)) { total_mirrored++; diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 485ceb2c209..a21df9f66f5 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -1431,11 +1431,6 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve unit_m3(t->mat); - unit_m3(t->orient_matrix); - negate_m3(t->orient_matrix); - /* Leave 't->orient_matrix_is_set' to false, - * so we overwrite it when we have a useful value. */ - /* Default to rotate on the Z axis. */ t->orient_axis = 2; t->orient_axis_ortho = 1; @@ -1513,22 +1508,6 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->around = V3D_AROUND_CURSOR; } - TransformOrientationSlot *orient_slot = &t->scene->orientation_slots[SCE_ORIENT_DEFAULT]; - t->orientation.unset = V3D_ORIENT_GLOBAL; - t->orientation.user = orient_slot->type; - t->orientation.custom = BKE_scene_transform_orientation_find(t->scene, - orient_slot->index_custom); - - t->orientation.index = 0; - ARRAY_SET_ITEMS(t->orientation.types, &t->orientation.user, NULL); - - /* Make second orientation local if both are global. */ - if (t->orientation.user == V3D_ORIENT_GLOBAL) { - t->orientation.user_alt = V3D_ORIENT_LOCAL; - t->orientation.types[0] = &t->orientation.user_alt; - SWAP(short *, t->orientation.types[0], t->orientation.types[1]); - } - /* exceptional case */ if (t->around == V3D_AROUND_LOCAL_ORIGINS) { if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL)) { @@ -1617,48 +1596,100 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->around = V3D_AROUND_CENTER_BOUNDS; } - if (op && (prop = RNA_struct_find_property(op->ptr, "orient_axis"))) { - t->orient_axis = RNA_property_enum_get(op->ptr, prop); - } - if (op && (prop = RNA_struct_find_property(op->ptr, "orient_axis_ortho"))) { - t->orient_axis_ortho = RNA_property_enum_get(op->ptr, prop); - } + if (op && (prop = RNA_struct_find_property(op->ptr, "constraint_axis")) && + RNA_property_is_set(op->ptr, prop)) { + bool constraint_axis[3]; - if (op && - ((prop = RNA_struct_find_property(op->ptr, "orient_matrix")) && - RNA_property_is_set(op->ptr, prop)) && - ((t->flag & T_MODAL) || - /* When using redo, don't use the custom constraint matrix - * if the user selects a different orientation. */ - (RNA_enum_get(op->ptr, "orient_type") == RNA_enum_get(op->ptr, "orient_matrix_type")))) { - RNA_property_float_get_array(op->ptr, prop, &t->orient_matrix[0][0]); - copy_m3_m3(t->spacemtx, t->orient_matrix); - /* Some transform modes use this to operate on an axis. */ - t->orient_matrix_is_set = true; - t->orientation.user = V3D_ORIENT_CUSTOM_MATRIX; - t->orientation.custom = 0; - if (t->flag & T_MODAL) { - RNA_enum_set(op->ptr, "orient_matrix_type", RNA_enum_get(op->ptr, "orient_type")); + RNA_property_boolean_get_array(op->ptr, prop, constraint_axis); + + if (constraint_axis[0] || constraint_axis[1] || constraint_axis[2]) { + t->con.mode |= CON_APPLY; + + if (constraint_axis[0]) { + t->con.mode |= CON_AXIS0; + } + if (constraint_axis[1]) { + t->con.mode |= CON_AXIS1; + } + if (constraint_axis[2]) { + t->con.mode |= CON_AXIS2; + } } } - else if (op && ((prop = RNA_struct_find_property(op->ptr, "orient_type")) && - RNA_property_is_set(op->ptr, prop))) { - short orientation = RNA_property_enum_get(op->ptr, prop); + + { TransformOrientation *custom_orientation = NULL; + short orient_type_default = V3D_ORIENT_GLOBAL; + short orient_type_set = -1; + short orient_type_matrix_set = -1; + short orient_type_constraint = -1; + + if (op && (prop = RNA_struct_find_property(op->ptr, "orient_axis"))) { + t->orient_axis = RNA_property_enum_get(op->ptr, prop); + orient_type_default = V3D_ORIENT_VIEW; + } + if (op && (prop = RNA_struct_find_property(op->ptr, "orient_axis_ortho"))) { + t->orient_axis_ortho = RNA_property_enum_get(op->ptr, prop); + } - if (orientation >= V3D_ORIENT_CUSTOM) { - if (orientation >= V3D_ORIENT_CUSTOM + BIF_countTransformOrientation(C)) { - orientation = V3D_ORIENT_GLOBAL; + if (op && ((prop = RNA_struct_find_property(op->ptr, "orient_type")) && + RNA_property_is_set(op->ptr, prop))) { + orient_type_set = RNA_property_enum_get(op->ptr, prop); + } + + if (op && ((prop = RNA_struct_find_property(op->ptr, "orient_matrix")) && + RNA_property_is_set(op->ptr, prop))) { + RNA_property_float_get_array(op->ptr, prop, &t->orientation.custom_matrix[0][0]); + + if ((prop = RNA_struct_find_property(op->ptr, "orient_matrix_type")) && + RNA_property_is_set(op->ptr, prop)) { + orient_type_matrix_set = RNA_property_enum_get(op->ptr, prop); } else { + orient_type_matrix_set = orient_type_set; + } + + if (orient_type_matrix_set == orient_type_set) { + /* When using redo, don't use the custom constraint matrix + * if the user selects a different orientation. */ + orient_type_default = V3D_ORIENT_CUSTOM_MATRIX; + orient_type_constraint = orient_type_set; + } + } + + if (orient_type_constraint == -1) { + if (orient_type_set != -1) { + orient_type_default = orient_type_set; + + if (orient_type_default >= V3D_ORIENT_CUSTOM) { + if (orient_type_default >= V3D_ORIENT_CUSTOM + BIF_countTransformOrientation(C)) { + orient_type_default = V3D_ORIENT_GLOBAL; + } + else { + custom_orientation = BKE_scene_transform_orientation_find( + t->scene, orient_type_default - V3D_ORIENT_CUSTOM); + orient_type_default = V3D_ORIENT_CUSTOM; + } + } + orient_type_constraint = orient_type_default; + } + else { + TransformOrientationSlot *orient_slot = &t->scene->orientation_slots[SCE_ORIENT_DEFAULT]; + orient_type_constraint = orient_slot->type; custom_orientation = BKE_scene_transform_orientation_find(t->scene, - orientation - V3D_ORIENT_CUSTOM); - orientation = V3D_ORIENT_CUSTOM; + orient_slot->index_custom); } } - t->orientation.user = orientation; + t->orientation.types[0] = orient_type_default; + t->orientation.types[1] = orient_type_constraint; + t->orientation.types[2] = orient_type_constraint != V3D_ORIENT_GLOBAL ? V3D_ORIENT_GLOBAL : + V3D_ORIENT_LOCAL; t->orientation.custom = custom_orientation; + + if (t->con.mode & CON_APPLY) { + t->orientation.index = 1; + } } if (op && ((prop = RNA_struct_find_property(op->ptr, "release_confirm")) && diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index aa298b04d1a..362cbc1b800 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -1033,7 +1033,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C, /* mislead counting bones... bah. We don't know the gizmo mode, could be mixed */ const int mode = TFM_ROTATION; - const int totsel_iter = count_set_pose_transflags( + const int totsel_iter = transform_convert_pose_transflags_update( ob_iter, mode, V3D_AROUND_CENTER_BOUNDS, NULL); if (totsel_iter) { @@ -1402,7 +1402,7 @@ void drawDial3d(const TransInfo *t) } else { axis_idx = MAN_AXIS_ROT_C; - negate_v3_v3(mat_basis[2], t->orient_matrix[t->orient_axis]); + negate_v3_v3(mat_basis[2], t->spacemtx[t->orient_axis]); scale *= 1.2f; line_with -= 1.0f; } diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index a2b3a891031..4c14c3aebd7 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -529,7 +529,7 @@ void headerRotation(TransInfo *t, char str[UI_MAX_DRAW_STR], float final) void postInputRotation(TransInfo *t, float values[3]) { float axis_final[3]; - copy_v3_v3(axis_final, t->orient_matrix[t->orient_axis]); + copy_v3_v3(axis_final, t->spacemtx[t->orient_axis]); if ((t->con.mode & CON_APPLY) && t->con.applyRot) { t->con.applyRot(t, NULL, NULL, axis_final, values); } diff --git a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c index 18149a09f20..fde0d5b187e 100644 --- a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c +++ b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c @@ -81,7 +81,7 @@ static void applyNormalRotation(TransInfo *t, const int UNUSED(mval[2])) char str[UI_MAX_DRAW_STR]; float axis_final[3]; - copy_v3_v3(axis_final, t->orient_matrix[t->orient_axis]); + copy_v3_v3(axis_final, t->spacemtx[t->orient_axis]); if ((t->con.mode & CON_APPLY) && t->con.applyRot) { t->con.applyRot(t, NULL, NULL, axis_final, NULL); diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c index 6c2b3dc77d2..f52bfda0d14 100644 --- a/source/blender/editors/transform/transform_mode_rotate.c +++ b/source/blender/editors/transform/transform_mode_rotate.c @@ -146,7 +146,7 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2])) snapGridIncrement(t, &final); float axis_final[3]; - copy_v3_v3(axis_final, t->orient_matrix[t->orient_axis]); + copy_v3_v3(axis_final, t->spacemtx[t->orient_axis]); if ((t->con.mode & CON_APPLY) && t->con.applyRot) { t->con.applyRot(t, NULL, NULL, axis_final, NULL); diff --git a/source/blender/editors/transform/transform_mode_shear.c b/source/blender/editors/transform/transform_mode_shear.c index ba79f5f3c7b..da34bf50ba3 100644 --- a/source/blender/editors/transform/transform_mode_shear.c +++ b/source/blender/editors/transform/transform_mode_shear.c @@ -53,17 +53,17 @@ static void initShear_mouseInputMode(TransInfo *t) { float dir[3]; bool dir_flip = false; - copy_v3_v3(dir, t->orient_matrix[t->orient_axis_ortho]); + copy_v3_v3(dir, t->spacemtx[t->orient_axis_ortho]); /* Needed for axis aligned view gizmo. */ - if (t->orientation.user == V3D_ORIENT_VIEW) { + if (t->orientation.types[t->orientation.index] == V3D_ORIENT_VIEW) { if (t->orient_axis_ortho == 0) { - if (t->center2d[1] > t->mouse.imval[1]) { + if (t->center2d[1] < t->mouse.imval[1]) { dir_flip = !dir_flip; } } else if (t->orient_axis_ortho == 1) { - if (t->center2d[0] > t->mouse.imval[0]) { + if (t->center2d[0] < t->mouse.imval[0]) { dir_flip = !dir_flip; } } @@ -154,8 +154,8 @@ static void applyShear(TransInfo *t, const int UNUSED(mval[2])) unit_m3(smat); smat[1][0] = value; - copy_v3_v3(axismat_inv[0], t->orient_matrix[t->orient_axis_ortho]); - copy_v3_v3(axismat_inv[2], t->orient_matrix[t->orient_axis]); + copy_v3_v3(axismat_inv[0], t->spacemtx[t->orient_axis_ortho]); + copy_v3_v3(axismat_inv[2], t->spacemtx[t->orient_axis]); cross_v3_v3v3(axismat_inv[1], axismat_inv[0], axismat_inv[2]); invert_m3_m3(axismat, axismat_inv); diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index 76823adfd20..3b1f3559daa 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -407,14 +407,19 @@ bool applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3] return true; } -static int count_bone_select(bArmature *arm, ListBase *lb, const bool do_it) +/* Updates all `BONE_TRANSFORM` flags. + * Returns total number of bones with `BONE_TRANSFORM`. + * Note: `transform_convert_pose_transflags_update` has a similar logic. */ +static int armature_bone_transflags_update_recursive(bArmature *arm, + ListBase *lb, + const bool do_it) { Bone *bone; bool do_next; int total = 0; for (bone = lb->first; bone; bone = bone->next) { - bone->flag &= ~(BONE_TRANSFORM | BONE_TRANSFORM_MIRROR); + bone->flag &= ~BONE_TRANSFORM; do_next = do_it; if (do_it) { if (bone->layer & arm->layer) { @@ -427,18 +432,18 @@ static int count_bone_select(bArmature *arm, ListBase *lb, const bool do_it) } } } - total += count_bone_select(arm, &bone->childbase, do_next); + total += armature_bone_transflags_update_recursive(arm, &bone->childbase, do_next); } return total; } -void initTransformOrientation(bContext *C, TransInfo *t) +void initTransformOrientation(bContext *C, TransInfo *t, short orientation) { Object *ob = CTX_data_active_object(C); Object *obedit = CTX_data_active_object(C); - switch (t->orientation.user) { + switch (orientation) { case V3D_ORIENT_GLOBAL: unit_m3(t->spacemtx); BLI_strncpy(t->spacename, TIP_("global"), sizeof(t->spacename)); @@ -471,28 +476,28 @@ void initTransformOrientation(bContext *C, TransInfo *t) break; - case V3D_ORIENT_VIEW: + case V3D_ORIENT_VIEW: { + float mat[3][3]; if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) { - RegionView3D *rv3d = t->region->regiondata; - float mat[3][3]; - BLI_strncpy(t->spacename, TIP_("view"), sizeof(t->spacename)); - copy_m3_m4(mat, rv3d->viewinv); + copy_m3_m4(mat, t->viewinv); normalize_m3(mat); - copy_m3_m3(t->spacemtx, mat); } else { - unit_m3(t->spacemtx); + unit_m3(mat); } + negate_v3(mat[2]); + copy_m3_m3(t->spacemtx, mat); break; + } case V3D_ORIENT_CURSOR: { BLI_strncpy(t->spacename, TIP_("cursor"), sizeof(t->spacename)); BKE_scene_cursor_rot_to_mat3(&t->scene->cursor, t->spacemtx); break; } case V3D_ORIENT_CUSTOM_MATRIX: - /* Already set. */ BLI_strncpy(t->spacename, TIP_("custom"), sizeof(t->spacename)); + copy_m3_m3(t->spacemtx, t->orientation.custom_matrix); break; case V3D_ORIENT_CUSTOM: BLI_strncpy(t->spacename, t->orientation.custom->name, sizeof(t->spacename)); @@ -505,20 +510,6 @@ void initTransformOrientation(bContext *C, TransInfo *t) } break; } - - if (t->orient_matrix_is_set == false) { - t->orient_matrix_is_set = true; - if (t->flag & T_MODAL) { - /* Rotate for example defaults to operating on the view plane. */ - t->orientation.unset = V3D_ORIENT_VIEW; - copy_m3_m4(t->orient_matrix, t->viewinv); - normalize_m3(t->orient_matrix); - negate_m3(t->orient_matrix); - } - else { - copy_m3_m3(t->orient_matrix, t->spacemtx); - } - } } /** @@ -1072,10 +1063,9 @@ int getTransformOrientation_ex(const bContext *C, ok = true; } else { - int totsel; - - totsel = count_bone_select(arm, &arm->bonebase, true); - if (totsel) { + int transformed_len; + transformed_len = armature_bone_transflags_update_recursive(arm, &arm->bonebase, true); + if (transformed_len) { /* use channels to get stats */ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { if (pchan->bone && pchan->bone->flag & BONE_TRANSFORM) { diff --git a/source/blender/editors/uvedit/CMakeLists.txt b/source/blender/editors/uvedit/CMakeLists.txt index d2ba9ab9591..b40b82c50fb 100644 --- a/source/blender/editors/uvedit/CMakeLists.txt +++ b/source/blender/editors/uvedit/CMakeLists.txt @@ -40,6 +40,7 @@ set(SRC uvedit_draw.c uvedit_ops.c uvedit_parametrizer.c + uvedit_select.c uvedit_smart_stitch.c uvedit_unwrap_ops.c diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index 7bc6b048585..ffab5bd094f 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -99,6 +99,7 @@ bool uv_find_nearest_face_multi(struct Scene *scene, void uvedit_live_unwrap_update(struct SpaceImage *sima, struct Scene *scene, struct Object *obedit); +void uvedit_pixel_to_float(struct SpaceImage *sima, float pixeldist, float r_dist[2]); /* operators */ @@ -113,4 +114,31 @@ void UV_OT_sphere_project(struct wmOperatorType *ot); void UV_OT_unwrap(struct wmOperatorType *ot); void UV_OT_stitch(struct wmOperatorType *ot); +/* uvedit_select.c */ + +bool uvedit_select_is_any_selected(struct Scene *scene, struct Image *ima, struct Object *obedit); +bool uvedit_select_is_any_selected_multi(struct Scene *scene, + struct Image *ima, + struct Object **objects, + const uint objects_len); +const float *uvedit_first_selected_uv_from_vertex(struct Scene *scene, + struct Object *obedit, + struct Image *ima, + struct BMVert *eve, + const int cd_loop_uv_offset); + +void UV_OT_select_all(struct wmOperatorType *ot); +void UV_OT_select(struct wmOperatorType *ot); +void UV_OT_select_loop(struct wmOperatorType *ot); +void UV_OT_select_linked(struct wmOperatorType *ot); +void UV_OT_select_linked_pick(struct wmOperatorType *ot); +void UV_OT_select_split(struct wmOperatorType *ot); +void UV_OT_select_pinned(struct wmOperatorType *ot); +void UV_OT_select_box(struct wmOperatorType *ot); +void UV_OT_select_lasso(struct wmOperatorType *ot); +void UV_OT_select_circle(struct wmOperatorType *ot); +void UV_OT_select_more(struct wmOperatorType *ot); +void UV_OT_select_less(struct wmOperatorType *ot); +void UV_OT_select_overlap(struct wmOperatorType *ot); + #endif /* __UVEDIT_INTERN_H__ */ diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index e169222380e..a99e05cb52b 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -36,15 +36,9 @@ #include "DNA_scene_types.h" #include "DNA_space_types.h" -#include "BLI_alloca.h" #include "BLI_array.h" -#include "BLI_blenlib.h" -#include "BLI_hash.h" -#include "BLI_kdopbvh.h" #include "BLI_kdtree.h" -#include "BLI_lasso_2d.h" #include "BLI_math.h" -#include "BLI_polyfill_2d.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -52,31 +46,22 @@ #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_editmesh.h" -#include "BKE_image.h" #include "BKE_layer.h" #include "BKE_main.h" #include "BKE_material.h" -#include "BKE_mesh.h" #include "BKE_mesh_mapping.h" #include "BKE_node.h" -#include "BKE_report.h" -#include "BKE_scene.h" #include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" #include "ED_image.h" #include "ED_mesh.h" #include "ED_node.h" -#include "ED_object.h" #include "ED_screen.h" -#include "ED_select_utils.h" -#include "ED_transform.h" #include "ED_uvedit.h" #include "RNA_access.h" #include "RNA_define.h" -#include "RNA_enum_types.h" #include "WM_api.h" #include "WM_message.h" @@ -88,26 +73,6 @@ #include "uvedit_intern.h" -static bool uv_select_is_any_selected(Scene *scene, Image *ima, Object *obedit); -static bool uv_select_is_any_selected_multi(Scene *scene, - Image *ima, - Object **objects, - const uint objects_len); -static void uv_select_all_perform(Scene *scene, Image *ima, Object *obedit, int action); -static void uv_select_all_perform_multi( - Scene *scene, Image *ima, Object **objects, const uint objects_len, int action); -static void uv_select_flush_from_tag_face(SpaceImage *sima, - Scene *scene, - Object *obedit, - const bool select); -static void uv_select_flush_from_tag_loop(SpaceImage *sima, - Scene *scene, - Object *obedit, - const bool select); -static void uv_select_tag_update_for_object(Depsgraph *depsgraph, - const ToolSettings *ts, - Object *obedit); - /* -------------------------------------------------------------------- */ /** \name State Testing * \{ */ @@ -225,7 +190,7 @@ void ED_object_assign_active_image(Main *bmain, Object *ob, int mat_nr, Image *i /** \name Space Conversion * \{ */ -static void uvedit_pixel_to_float(SpaceImage *sima, float *dist, float pixeldist) +void uvedit_pixel_to_float(SpaceImage *sima, float pixeldist, float r_dist[2]) { int width, height; @@ -237,351 +202,8 @@ static void uvedit_pixel_to_float(SpaceImage *sima, float *dist, float pixeldist height = IMG_SIZE_FALLBACK; } - dist[0] = pixeldist / width; - dist[1] = pixeldist / height; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Visibility and Selection Utilities - * \{ */ - -static void uvedit_vertex_select_tagged(BMEditMesh *em, - Scene *scene, - bool select, - int cd_loop_uv_offset) -{ - BMFace *efa; - BMLoop *l; - BMIter iter, liter; - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) { - uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); - } - } - } -} - -bool uvedit_face_visible_nolocal_ex(const ToolSettings *ts, BMFace *efa) -{ - if (ts->uv_flag & UV_SYNC_SELECTION) { - return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0); - } - else { - return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0 && BM_elem_flag_test(efa, BM_ELEM_SELECT)); - } -} -bool uvedit_face_visible_nolocal(const Scene *scene, BMFace *efa) -{ - return uvedit_face_visible_nolocal_ex(scene->toolsettings, efa); -} - -bool uvedit_face_visible_test_ex(const ToolSettings *ts, Object *obedit, Image *ima, BMFace *efa) -{ - if (ts->uv_flag & UV_SHOW_SAME_IMAGE) { - Image *face_image; - ED_object_get_active_image(obedit, efa->mat_nr + 1, &face_image, NULL, NULL, NULL); - return (face_image == ima) ? uvedit_face_visible_nolocal_ex(ts, efa) : false; - } - else { - return uvedit_face_visible_nolocal_ex(ts, efa); - } -} -bool uvedit_face_visible_test(const Scene *scene, Object *obedit, Image *ima, BMFace *efa) -{ - return uvedit_face_visible_test_ex(scene->toolsettings, obedit, ima, efa); -} - -bool uvedit_face_select_test_ex(const ToolSettings *ts, BMFace *efa, const int cd_loop_uv_offset) -{ - if (ts->uv_flag & UV_SYNC_SELECTION) { - return (BM_elem_flag_test(efa, BM_ELEM_SELECT)); - } - else { - BMLoop *l; - MLoopUV *luv; - BMIter liter; - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - if (!(luv->flag & MLOOPUV_VERTSEL)) { - return false; - } - } - - return true; - } -} -bool uvedit_face_select_test(const Scene *scene, BMFace *efa, const int cd_loop_uv_offset) -{ - return uvedit_face_select_test_ex(scene->toolsettings, efa, cd_loop_uv_offset); -} - -bool uvedit_face_select_set(const struct Scene *scene, - struct BMEditMesh *em, - struct BMFace *efa, - const bool select, - const bool do_history, - const int cd_loop_uv_offset) -{ - if (select) { - return uvedit_face_select_enable(scene, em, efa, do_history, cd_loop_uv_offset); - } - else { - return uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset); - } -} - -bool uvedit_face_select_enable(const Scene *scene, - BMEditMesh *em, - BMFace *efa, - const bool do_history, - const int cd_loop_uv_offset) -{ - const ToolSettings *ts = scene->toolsettings; - - if (ts->uv_flag & UV_SYNC_SELECTION) { - BM_face_select_set(em->bm, efa, true); - if (do_history) { - BM_select_history_store(em->bm, (BMElem *)efa); - } - } - else { - BMLoop *l; - MLoopUV *luv; - BMIter liter; - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv->flag |= MLOOPUV_VERTSEL; - } - - return true; - } - - return false; -} - -bool uvedit_face_select_disable(const Scene *scene, - BMEditMesh *em, - BMFace *efa, - const int cd_loop_uv_offset) -{ - const ToolSettings *ts = scene->toolsettings; - - if (ts->uv_flag & UV_SYNC_SELECTION) { - BM_face_select_set(em->bm, efa, false); - } - else { - BMLoop *l; - MLoopUV *luv; - BMIter liter; - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv->flag &= ~MLOOPUV_VERTSEL; - } - - return true; - } - - return false; -} - -bool uvedit_edge_select_test_ex(const ToolSettings *ts, BMLoop *l, const int cd_loop_uv_offset) -{ - if (ts->uv_flag & UV_SYNC_SELECTION) { - if (ts->selectmode & SCE_SELECT_FACE) { - return BM_elem_flag_test(l->f, BM_ELEM_SELECT); - } - else if (ts->selectmode == SCE_SELECT_EDGE) { - return BM_elem_flag_test(l->e, BM_ELEM_SELECT); - } - else { - return BM_elem_flag_test(l->v, BM_ELEM_SELECT) && - BM_elem_flag_test(l->next->v, BM_ELEM_SELECT); - } - } - else { - MLoopUV *luv1, *luv2; - - luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); - - return (luv1->flag & MLOOPUV_VERTSEL) && (luv2->flag & MLOOPUV_VERTSEL); - } -} -bool uvedit_edge_select_test(const Scene *scene, BMLoop *l, const int cd_loop_uv_offset) -{ - return uvedit_edge_select_test_ex(scene->toolsettings, l, cd_loop_uv_offset); -} - -void uvedit_edge_select_set(BMEditMesh *em, - const Scene *scene, - BMLoop *l, - const bool select, - const bool do_history, - const int cd_loop_uv_offset) - -{ - if (select) { - uvedit_edge_select_enable(em, scene, l, do_history, cd_loop_uv_offset); - } - else { - uvedit_edge_select_disable(em, scene, l, cd_loop_uv_offset); - } -} - -void uvedit_edge_select_enable(BMEditMesh *em, - const Scene *scene, - BMLoop *l, - const bool do_history, - const int cd_loop_uv_offset) - -{ - const ToolSettings *ts = scene->toolsettings; - - if (ts->uv_flag & UV_SYNC_SELECTION) { - if (ts->selectmode & SCE_SELECT_FACE) { - BM_face_select_set(em->bm, l->f, true); - } - else if (ts->selectmode & SCE_SELECT_EDGE) { - BM_edge_select_set(em->bm, l->e, true); - } - else { - BM_vert_select_set(em->bm, l->e->v1, true); - BM_vert_select_set(em->bm, l->e->v2, true); - } - - if (do_history) { - BM_select_history_store(em->bm, (BMElem *)l->e); - } - } - else { - MLoopUV *luv1, *luv2; - - luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); - - luv1->flag |= MLOOPUV_VERTSEL; - luv2->flag |= MLOOPUV_VERTSEL; - } -} - -void uvedit_edge_select_disable(BMEditMesh *em, - const Scene *scene, - BMLoop *l, - const int cd_loop_uv_offset) - -{ - const ToolSettings *ts = scene->toolsettings; - - if (ts->uv_flag & UV_SYNC_SELECTION) { - if (ts->selectmode & SCE_SELECT_FACE) { - BM_face_select_set(em->bm, l->f, false); - } - else if (ts->selectmode & SCE_SELECT_EDGE) { - BM_edge_select_set(em->bm, l->e, false); - } - else { - BM_vert_select_set(em->bm, l->e->v1, false); - BM_vert_select_set(em->bm, l->e->v2, false); - } - } - else { - MLoopUV *luv1, *luv2; - - luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); - - luv1->flag &= ~MLOOPUV_VERTSEL; - luv2->flag &= ~MLOOPUV_VERTSEL; - } -} - -bool uvedit_uv_select_test_ex(const ToolSettings *ts, BMLoop *l, const int cd_loop_uv_offset) -{ - if (ts->uv_flag & UV_SYNC_SELECTION) { - if (ts->selectmode & SCE_SELECT_FACE) { - return BM_elem_flag_test_bool(l->f, BM_ELEM_SELECT); - } - else { - return BM_elem_flag_test_bool(l->v, BM_ELEM_SELECT); - } - } - else { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - return (luv->flag & MLOOPUV_VERTSEL) != 0; - } -} -bool uvedit_uv_select_test(const Scene *scene, BMLoop *l, const int cd_loop_uv_offset) -{ - return uvedit_uv_select_test_ex(scene->toolsettings, l, cd_loop_uv_offset); -} - -void uvedit_uv_select_set(BMEditMesh *em, - const Scene *scene, - BMLoop *l, - const bool select, - const bool do_history, - const int cd_loop_uv_offset) -{ - if (select) { - uvedit_uv_select_enable(em, scene, l, do_history, cd_loop_uv_offset); - } - else { - uvedit_uv_select_disable(em, scene, l, cd_loop_uv_offset); - } -} - -void uvedit_uv_select_enable(BMEditMesh *em, - const Scene *scene, - BMLoop *l, - const bool do_history, - const int cd_loop_uv_offset) -{ - const ToolSettings *ts = scene->toolsettings; - - if (ts->uv_flag & UV_SYNC_SELECTION) { - if (ts->selectmode & SCE_SELECT_FACE) { - BM_face_select_set(em->bm, l->f, true); - } - else { - BM_vert_select_set(em->bm, l->v, true); - } - - if (do_history) { - BM_select_history_remove(em->bm, (BMElem *)l->v); - } - } - else { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv->flag |= MLOOPUV_VERTSEL; - } -} - -void uvedit_uv_select_disable(BMEditMesh *em, - const Scene *scene, - BMLoop *l, - const int cd_loop_uv_offset) -{ - const ToolSettings *ts = scene->toolsettings; - - if (ts->uv_flag & UV_SYNC_SELECTION) { - if (ts->selectmode & SCE_SELECT_FACE) { - BM_face_select_set(em->bm, l->f, false); - } - else { - BM_vert_select_set(em->bm, l->v, false); - } - } - else { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv->flag &= ~MLOOPUV_VERTSEL; - } + r_dist[0] = pixeldist / width; + r_dist[1] = pixeldist / height; } /** \} */ @@ -770,7 +392,8 @@ bool ED_uvedit_center_from_pivot_ex(SpaceImage *sima, uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( view_layer, ((View3D *)NULL), &objects_len); - *r_has_select = uv_select_is_any_selected_multi(scene, sima->image, objects, objects_len); + *r_has_select = uvedit_select_is_any_selected_multi( + scene, sima->image, objects, objects_len); MEM_freeN(objects); } break; @@ -799,877 +422,6 @@ bool ED_uvedit_center_from_pivot( /** \} */ /* -------------------------------------------------------------------- */ -/** \name Find Nearest Elements - * \{ */ - -bool uv_find_nearest_edge( - Scene *scene, Image *ima, Object *obedit, const float co[2], UvNearestHit *hit) -{ - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMFace *efa; - BMLoop *l; - BMIter iter, liter; - MLoopUV *luv, *luv_next; - int i; - bool found = false; - - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - BM_mesh_elem_index_ensure(em->bm, BM_VERT); - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { - continue; - } - BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); - - const float dist_test_sq = dist_squared_to_line_segment_v2(co, luv->uv, luv_next->uv); - - if (dist_test_sq < hit->dist_sq) { - hit->efa = efa; - - hit->l = l; - hit->luv = luv; - hit->luv_next = luv_next; - hit->lindex = i; - - hit->dist_sq = dist_test_sq; - found = true; - } - } - } - return found; -} - -bool uv_find_nearest_edge_multi(Scene *scene, - Image *ima, - Object **objects, - const uint objects_len, - const float co[2], - UvNearestHit *hit_final) -{ - bool found = false; - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - if (uv_find_nearest_edge(scene, ima, obedit, co, hit_final)) { - hit_final->ob = obedit; - found = true; - } - } - return found; -} - -bool uv_find_nearest_face( - Scene *scene, Image *ima, Object *obedit, const float co[2], UvNearestHit *hit_final) -{ - BMEditMesh *em = BKE_editmesh_from_object(obedit); - bool found = false; - - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - /* this will fill in hit.vert1 and hit.vert2 */ - float dist_sq_init = hit_final->dist_sq; - UvNearestHit hit = *hit_final; - if (uv_find_nearest_edge(scene, ima, obedit, co, &hit)) { - hit.dist_sq = dist_sq_init; - hit.l = NULL; - hit.luv = hit.luv_next = NULL; - - BMIter iter; - BMFace *efa; - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { - continue; - } - - float cent[2]; - uv_poly_center(efa, cent, cd_loop_uv_offset); - - const float dist_test_sq = len_squared_v2v2(co, cent); - - if (dist_test_sq < hit.dist_sq) { - hit.efa = efa; - hit.dist_sq = dist_test_sq; - found = true; - } - } - } - if (found) { - *hit_final = hit; - } - return found; -} - -bool uv_find_nearest_face_multi(Scene *scene, - Image *ima, - Object **objects, - const uint objects_len, - const float co[2], - UvNearestHit *hit_final) -{ - bool found = false; - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - if (uv_find_nearest_face(scene, ima, obedit, co, hit_final)) { - hit_final->ob = obedit; - found = true; - } - } - return found; -} - -static bool uv_nearest_between(const BMLoop *l, const float co[2], const int cd_loop_uv_offset) -{ - const float *uv_prev = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset))->uv; - const float *uv_curr = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset))->uv; - const float *uv_next = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset))->uv; - - return ((line_point_side_v2(uv_prev, uv_curr, co) > 0.0f) && - (line_point_side_v2(uv_next, uv_curr, co) <= 0.0f)); -} - -bool uv_find_nearest_vert(Scene *scene, - Image *ima, - Object *obedit, - float const co[2], - const float penalty_dist, - UvNearestHit *hit_final) -{ - bool found = false; - - /* this will fill in hit.vert1 and hit.vert2 */ - float dist_sq_init = hit_final->dist_sq; - UvNearestHit hit = *hit_final; - if (uv_find_nearest_edge(scene, ima, obedit, co, &hit)) { - hit.dist_sq = dist_sq_init; - - hit.l = NULL; - hit.luv = hit.luv_next = NULL; - - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMFace *efa; - BMIter iter; - - BM_mesh_elem_index_ensure(em->bm, BM_VERT); - - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { - continue; - } - - BMIter liter; - BMLoop *l; - int i; - BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { - float dist_test_sq; - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - if (penalty_dist != 0.0f && uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - dist_test_sq = len_v2v2(co, luv->uv) + penalty_dist; - dist_test_sq = square_f(dist_test_sq); - } - else { - dist_test_sq = len_squared_v2v2(co, luv->uv); - } - - if (dist_test_sq <= hit.dist_sq) { - if (dist_test_sq == hit.dist_sq) { - if (!uv_nearest_between(l, co, cd_loop_uv_offset)) { - continue; - } - } - - hit.dist_sq = dist_test_sq; - - hit.l = l; - hit.luv = luv; - hit.luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); - hit.efa = efa; - hit.lindex = i; - found = true; - } - } - } - } - - if (found) { - *hit_final = hit; - } - - return found; -} - -bool uv_find_nearest_vert_multi(Scene *scene, - Image *ima, - Object **objects, - const uint objects_len, - float const co[2], - const float penalty_dist, - UvNearestHit *hit_final) -{ - bool found = false; - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - if (uv_find_nearest_vert(scene, ima, obedit, co, penalty_dist, hit_final)) { - hit_final->ob = obedit; - found = true; - } - } - return found; -} - -bool ED_uvedit_nearest_uv(const Scene *scene, - Object *obedit, - Image *ima, - const float co[2], - float *dist_sq, - float r_uv[2]) -{ - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMIter iter; - BMFace *efa; - const float *uv_best = NULL; - float dist_best = *dist_sq; - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { - continue; - } - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - const float *uv = ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset))->uv; - const float dist_test = len_squared_v2v2(co, uv); - if (dist_best > dist_test) { - dist_best = dist_test; - uv_best = uv; - } - } while ((l_iter = l_iter->next) != l_first); - } - - if (uv_best != NULL) { - copy_v2_v2(r_uv, uv_best); - *dist_sq = dist_best; - return true; - } - else { - return false; - } -} - -bool ED_uvedit_nearest_uv_multi(const Scene *scene, - Image *ima, - Object **objects, - const uint objects_len, - const float co[2], - float *dist_sq, - float r_uv[2]) -{ - bool found = false; - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - if (ED_uvedit_nearest_uv(scene, obedit, ima, co, dist_sq, r_uv)) { - found = true; - } - } - return found; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Loop Select - * \{ */ - -static void uv_select_edgeloop_vertex_loop_flag(UvMapVert *first) -{ - UvMapVert *iterv; - int count = 0; - - for (iterv = first; iterv; iterv = iterv->next) { - if (iterv->separate && iterv != first) { - break; - } - - count++; - } - - if (count < 5) { - first->flag = 1; - } -} - -static UvMapVert *uv_select_edgeloop_vertex_map_get(UvVertMap *vmap, BMFace *efa, BMLoop *l) -{ - UvMapVert *iterv, *first; - first = BM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v)); - - for (iterv = first; iterv; iterv = iterv->next) { - if (iterv->separate) { - first = iterv; - } - if (iterv->poly_index == BM_elem_index_get(efa)) { - return first; - } - } - - return NULL; -} - -static bool uv_select_edgeloop_edge_tag_faces(BMEditMesh *em, - UvMapVert *first1, - UvMapVert *first2, - int *totface) -{ - UvMapVert *iterv1, *iterv2; - BMFace *efa; - int tot = 0; - - /* count number of faces this edge has */ - for (iterv1 = first1; iterv1; iterv1 = iterv1->next) { - if (iterv1->separate && iterv1 != first1) { - break; - } - - for (iterv2 = first2; iterv2; iterv2 = iterv2->next) { - if (iterv2->separate && iterv2 != first2) { - break; - } - - if (iterv1->poly_index == iterv2->poly_index) { - /* if face already tagged, don't do this edge */ - efa = BM_face_at_index(em->bm, iterv1->poly_index); - if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { - return false; - } - - tot++; - break; - } - } - } - - if (*totface == 0) { /* start edge */ - *totface = tot; - } - else if (tot != *totface) { /* check for same number of faces as start edge */ - return false; - } - - /* tag the faces */ - for (iterv1 = first1; iterv1; iterv1 = iterv1->next) { - if (iterv1->separate && iterv1 != first1) { - break; - } - - for (iterv2 = first2; iterv2; iterv2 = iterv2->next) { - if (iterv2->separate && iterv2 != first2) { - break; - } - - if (iterv1->poly_index == iterv2->poly_index) { - efa = BM_face_at_index(em->bm, iterv1->poly_index); - BM_elem_flag_enable(efa, BM_ELEM_TAG); - break; - } - } - } - - return true; -} - -static int uv_select_edgeloop(Scene *scene, - Image *ima, - Object *obedit, - UvNearestHit *hit, - const float limit[2], - const bool extend) -{ - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMFace *efa; - BMIter iter, liter; - BMLoop *l; - UvVertMap *vmap; - UvMapVert *iterv_curr; - UvMapVert *iterv_next; - int starttotf; - bool looking, select; - - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - /* setup */ - BM_mesh_elem_table_ensure(em->bm, BM_FACE); - vmap = BM_uv_vert_map_create(em->bm, limit, false, false); - - BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE); - - if (!extend) { - uv_select_all_perform(scene, ima, obedit, SEL_DESELECT); - } - - BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); - - /* set flags for first face and verts */ - iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l); - iterv_next = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l->next); - uv_select_edgeloop_vertex_loop_flag(iterv_curr); - uv_select_edgeloop_vertex_loop_flag(iterv_next); - - starttotf = 0; - uv_select_edgeloop_edge_tag_faces(em, iterv_curr, iterv_next, &starttotf); - - /* sorry, first edge isn't even ok */ - looking = !(iterv_curr->flag == 0 && iterv_next->flag == 0); - - /* iterate */ - while (looking) { - looking = false; - - /* find correct valence edges which are not tagged yet, but connect to tagged one */ - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!BM_elem_flag_test(efa, BM_ELEM_TAG) && - uvedit_face_visible_test(scene, obedit, ima, efa)) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - /* check face not hidden and not tagged */ - if (!(iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, efa, l))) { - continue; - } - if (!(iterv_next = uv_select_edgeloop_vertex_map_get(vmap, efa, l->next))) { - continue; - } - - /* check if vertex is tagged and has right valence */ - if (iterv_curr->flag || iterv_next->flag) { - if (uv_select_edgeloop_edge_tag_faces(em, iterv_curr, iterv_next, &starttotf)) { - looking = true; - BM_elem_flag_enable(efa, BM_ELEM_TAG); - - uv_select_edgeloop_vertex_loop_flag(iterv_curr); - uv_select_edgeloop_vertex_loop_flag(iterv_next); - break; - } - } - } - } - } - } - - /* do the actual select/deselect */ - iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l); - iterv_next = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l->next); - iterv_curr->flag = 1; - iterv_next->flag = 1; - - if (extend) { - select = !(uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset)); - } - else { - select = true; - } - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, efa, l); - - if (iterv_curr->flag) { - uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); - } - } - } - - /* cleanup */ - BM_uv_vert_map_free(vmap); - - return (select) ? 1 : -1; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Linked - * \{ */ - -static void uv_select_linked_multi(Scene *scene, - Image *ima, - Object **objects, - const uint objects_len, - const float limit[2], - UvNearestHit *hit_final, - bool extend, - bool deselect, - bool toggle, - bool select_faces) -{ - /* loop over objects, or just use hit_final->ob */ - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - if (hit_final && ob_index != 0) { - break; - } - Object *obedit = hit_final ? hit_final->ob : objects[ob_index]; - - BMFace *efa; - BMLoop *l; - BMIter iter, liter; - MLoopUV *luv; - UvVertMap *vmap; - UvMapVert *vlist, *iterv, *startv; - int i, stacksize = 0, *stack; - uint a; - char *flag; - - BMEditMesh *em = BKE_editmesh_from_object(obedit); - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - BM_mesh_elem_table_ensure(em->bm, BM_FACE); /* we can use this too */ - - /* Note, we had 'use winding' so we don't consider overlapping islands as connected, see T44320 - * this made *every* projection split the island into front/back islands. - * Keep 'use_winding' to false, see: T50970. - * - * Better solve this by having a delimit option for select-linked operator, - * keeping island-select working as is. */ - vmap = BM_uv_vert_map_create(em->bm, limit, !select_faces, false); - - if (vmap == NULL) { - continue; - } - - stack = MEM_mallocN(sizeof(*stack) * (em->bm->totface + 1), "UvLinkStack"); - flag = MEM_callocN(sizeof(*flag) * em->bm->totface, "UvLinkFlag"); - - if (hit_final == NULL) { - /* Use existing selection */ - BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) { - if (uvedit_face_visible_test(scene, obedit, ima, efa)) { - if (select_faces) { - if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - stack[stacksize] = a; - stacksize++; - flag[a] = 1; - } - } - else { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - - if (luv->flag & MLOOPUV_VERTSEL) { - stack[stacksize] = a; - stacksize++; - flag[a] = 1; - - break; - } - } - } - } - } - } - else { - BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) { - if (efa == hit_final->efa) { - stack[stacksize] = a; - stacksize++; - flag[a] = 1; - break; - } - } - } - - while (stacksize > 0) { - - stacksize--; - a = stack[stacksize]; - - efa = BM_face_at_index(em->bm, a); - - BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { - - /* make_uv_vert_map_EM sets verts tmp.l to the indices */ - vlist = BM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v)); - - startv = vlist; - - for (iterv = vlist; iterv; iterv = iterv->next) { - if (iterv->separate) { - startv = iterv; - } - if (iterv->poly_index == a) { - break; - } - } - - for (iterv = startv; iterv; iterv = iterv->next) { - if ((startv != iterv) && (iterv->separate)) { - break; - } - else if (!flag[iterv->poly_index]) { - flag[iterv->poly_index] = 1; - stack[stacksize] = iterv->poly_index; - stacksize++; - } - } - } - } - - /* Toggling - if any of the linked vertices is selected (and visible), we deselect. */ - if ((toggle == true) && (extend == false) && (deselect == false)) { - BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) { - bool found_selected = false; - if (!flag[a]) { - continue; - } - - if (select_faces) { - if (BM_elem_flag_test(efa, BM_ELEM_SELECT) && !BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - found_selected = true; - } - } - else { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - - if (luv->flag & MLOOPUV_VERTSEL) { - found_selected = true; - } - } - - if (found_selected) { - deselect = true; - break; - } - } - } - } - -#define SET_SELECTION(value) \ - if (select_faces) { \ - BM_face_select_set(em->bm, efa, value); \ - } \ - else { \ - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { \ - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); \ - luv->flag = (value) ? (luv->flag | MLOOPUV_VERTSEL) : (luv->flag & ~MLOOPUV_VERTSEL); \ - } \ - } \ - (void)0 - - BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) { - if (!flag[a]) { - if (!extend && !deselect && !toggle) { - SET_SELECTION(false); - } - continue; - } - - if (!deselect) { - SET_SELECTION(true); - } - else { - SET_SELECTION(false); - } - } - -#undef SET_SELECTION - - MEM_freeN(stack); - MEM_freeN(flag); - BM_uv_vert_map_free(vmap); - } -} - -/* WATCH IT: this returns first selected UV, - * not ideal in many cases since there could be multiple */ -static float *uv_sel_co_from_eve( - Scene *scene, Object *obedit, Image *ima, BMEditMesh *em, BMVert *eve) -{ - BMIter liter; - BMLoop *l; - - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - BM_ITER_ELEM (l, &liter, eve, BM_LOOPS_OF_VERT) { - if (!uvedit_face_visible_test(scene, obedit, ima, l->f)) { - continue; - } - - if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - return luv->uv; - } - } - - return NULL; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select More/Less Operator - * \{ */ - -static int uv_select_more_less(bContext *C, const bool select) -{ - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - Image *ima = CTX_data_edit_image(C); - SpaceImage *sima = CTX_wm_space_image(C); - - BMFace *efa; - BMLoop *l; - BMIter iter, liter; - const ToolSettings *ts = scene->toolsettings; - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( - view_layer, ((View3D *)NULL), &objects_len); - - const bool is_uv_face_selectmode = (ts->uv_selectmode == UV_SELECT_FACE); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - bool changed = false; - - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - if (ts->uv_flag & UV_SYNC_SELECTION) { - if (select) { - EDBM_select_more(em, true); - } - else { - EDBM_select_less(em, true); - } - - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - continue; - } - - if (is_uv_face_selectmode) { - - /* clear tags */ - BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); - - /* mark loops to be selected */ - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (uvedit_face_visible_test(scene, obedit, ima, efa)) { - -#define IS_SEL 1 -#define IS_UNSEL 2 - - int sel_state = 0; - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - if (luv->flag & MLOOPUV_VERTSEL) { - sel_state |= IS_SEL; - } - else { - sel_state |= IS_UNSEL; - } - - /* if we have a mixed selection, tag to grow it */ - if (sel_state == (IS_SEL | IS_UNSEL)) { - BM_elem_flag_enable(efa, BM_ELEM_TAG); - changed = true; - break; - } - } - -#undef IS_SEL -#undef IS_UNSEL - } - } - } - else { - - /* clear tags */ - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - BM_elem_flag_disable(l, BM_ELEM_TAG); - } - } - - /* mark loops to be selected */ - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (uvedit_face_visible_test(scene, obedit, ima, efa)) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - - if (((luv->flag & MLOOPUV_VERTSEL) != 0) == select) { - BM_elem_flag_enable(l->next, BM_ELEM_TAG); - BM_elem_flag_enable(l->prev, BM_ELEM_TAG); - changed = true; - } - } - } - } - } - - if (changed) { - if (is_uv_face_selectmode) { - /* Select tagged faces. */ - uv_select_flush_from_tag_face(sima, scene, obedit, select); - } - else { - /* Select tagged loops. */ - uv_select_flush_from_tag_loop(sima, scene, obedit, select); - } - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; -} - -static int uv_select_more_exec(bContext *C, wmOperator *UNUSED(op)) -{ - return uv_select_more_less(C, true); -} - -static void UV_OT_select_more(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select More"; - ot->description = "Select more UV vertices connected to initial selection"; - ot->idname = "UV_OT_select_more"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* api callbacks */ - ot->exec = uv_select_more_exec; - ot->poll = ED_operator_uvedit_space_image; -} - -static int uv_select_less_exec(bContext *C, wmOperator *UNUSED(op)) -{ - return uv_select_more_less(C, false); -} - -static void UV_OT_select_less(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Less"; - ot->description = "Deselect UV vertices at the boundary of each selection region"; - ot->idname = "UV_OT_select_less"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* api callbacks */ - ot->exec = uv_select_less_exec; - ot->poll = ED_operator_uvedit_space_image; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Weld Align Operator * \{ */ @@ -1866,9 +618,10 @@ static void uv_weld_align(bContext *C, eUVWeldAlign tool) if (BLI_array_len(eve_line) > 2) { /* we know the returns from these must be valid */ - const float *uv_start = uv_sel_co_from_eve(scene, obedit, ima, em, eve_line[0]); - const float *uv_end = uv_sel_co_from_eve( - scene, obedit, ima, em, eve_line[BLI_array_len(eve_line) - 1]); + const float *uv_start = uvedit_first_selected_uv_from_vertex( + scene, obedit, ima, eve_line[0], cd_loop_uv_offset); + const float *uv_end = uvedit_first_selected_uv_from_vertex( + scene, obedit, ima, eve_line[BLI_array_len(eve_line) - 1], cd_loop_uv_offset); /* For UV_STRAIGHTEN_X & UV_STRAIGHTEN_Y modes */ float a = 0.0f; eUVWeldAlign tool_local = tool; @@ -2316,1665 +1069,6 @@ static void UV_OT_weld(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name (De)Select All Operator - * \{ */ - -static bool uv_select_is_any_selected(Scene *scene, Image *ima, Object *obedit) -{ - const ToolSettings *ts = scene->toolsettings; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMFace *efa; - BMLoop *l; - BMIter iter, liter; - MLoopUV *luv; - - if (ts->uv_flag & UV_SYNC_SELECTION) { - return (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel); - } - else { - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { - continue; - } - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - if (luv->flag & MLOOPUV_VERTSEL) { - return true; - } - } - } - } - return false; -} - -static bool uv_select_is_any_selected_multi(Scene *scene, - Image *ima, - Object **objects, - const uint objects_len) -{ - bool found = false; - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - if (uv_select_is_any_selected(scene, ima, obedit)) { - found = true; - break; - } - } - return found; -} - -static void uv_select_all_perform(Scene *scene, Image *ima, Object *obedit, int action) -{ - const ToolSettings *ts = scene->toolsettings; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMFace *efa; - BMLoop *l; - BMIter iter, liter; - MLoopUV *luv; - - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - if (action == SEL_TOGGLE) { - action = uv_select_is_any_selected(scene, ima, obedit) ? SEL_DESELECT : SEL_SELECT; - } - - if (ts->uv_flag & UV_SYNC_SELECTION) { - switch (action) { - case SEL_TOGGLE: - EDBM_select_toggle_all(em); - break; - case SEL_SELECT: - EDBM_flag_enable_all(em, BM_ELEM_SELECT); - break; - case SEL_DESELECT: - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - break; - case SEL_INVERT: - EDBM_select_swap(em); - EDBM_selectmode_flush(em); - break; - } - } - else { - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { - continue; - } - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - - switch (action) { - case SEL_SELECT: - luv->flag |= MLOOPUV_VERTSEL; - break; - case SEL_DESELECT: - luv->flag &= ~MLOOPUV_VERTSEL; - break; - case SEL_INVERT: - luv->flag ^= MLOOPUV_VERTSEL; - break; - } - } - } - } -} - -static void uv_select_all_perform_multi( - Scene *scene, Image *ima, Object **objects, const uint objects_len, int action) -{ - if (action == SEL_TOGGLE) { - action = uv_select_is_any_selected_multi(scene, ima, objects, objects_len) ? SEL_DESELECT : - SEL_SELECT; - } - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - uv_select_all_perform(scene, ima, obedit, action); - } -} - -static int uv_select_all_exec(bContext *C, wmOperator *op) -{ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene = CTX_data_scene(C); - const ToolSettings *ts = scene->toolsettings; - Image *ima = CTX_data_edit_image(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - - int action = RNA_enum_get(op->ptr, "action"); - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( - view_layer, ((View3D *)NULL), &objects_len); - - uv_select_all_perform_multi(scene, ima, objects, objects_len, action); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - uv_select_tag_update_for_object(depsgraph, ts, obedit); - } - - MEM_freeN(objects); - - return OPERATOR_FINISHED; -} - -static void UV_OT_select_all(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "(De)select All"; - ot->description = "Change selection of all UV vertices"; - ot->idname = "UV_OT_select_all"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* api callbacks */ - ot->exec = uv_select_all_exec; - ot->poll = ED_operator_uvedit; - - WM_operator_properties_select_all(ot); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Mouse Select Operator - * \{ */ - -static bool uv_sticky_select( - float *limit, int hitv[], int v, float *hituv[], float *uv, int sticky, int hitlen) -{ - int i; - - /* this function test if some vertex needs to selected - * in addition to the existing ones due to sticky select */ - if (sticky == SI_STICKY_DISABLE) { - return false; - } - - for (i = 0; i < hitlen; i++) { - if (hitv[i] == v) { - if (sticky == SI_STICKY_LOC) { - if (fabsf(hituv[i][0] - uv[0]) < limit[0] && fabsf(hituv[i][1] - uv[1]) < limit[1]) { - return true; - } - } - else if (sticky == SI_STICKY_VERTEX) { - return true; - } - } - } - - return false; -} - -static int uv_mouse_select_multi(bContext *C, - Object **objects, - uint objects_len, - const float co[2], - const bool extend, - const bool deselect_all, - const bool loop) -{ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - SpaceImage *sima = CTX_wm_space_image(C); - Scene *scene = CTX_data_scene(C); - const ToolSettings *ts = scene->toolsettings; - Image *ima = CTX_data_edit_image(C); - BMFace *efa; - BMLoop *l; - BMIter iter, liter; - MLoopUV *luv; - UvNearestHit hit = UV_NEAREST_HIT_INIT; - int i, selectmode, sticky, sync, *hitv = NULL; - bool select = true; - bool found_item = false; - /* 0 == don't flush, 1 == sel, -1 == desel; only use when selection sync is enabled */ - int flush = 0; - int hitlen = 0; - float limit[2], **hituv = NULL; - - /* notice 'limit' is the same no matter the zoom level, since this is like - * remove doubles and could annoying if it joined points when zoomed out. - * 'penalty' is in screen pixel space otherwise zooming in on a uv-vert and - * shift-selecting can consider an adjacent point close enough to add to - * the selection rather than de-selecting the closest. */ - - float penalty_dist; - { - float penalty[2]; - uvedit_pixel_to_float(sima, limit, 0.05f); - uvedit_pixel_to_float(sima, penalty, 5.0f / (sima ? sima->zoom : 1.0f)); - penalty_dist = len_v2(penalty); - } - - /* retrieve operation mode */ - if (ts->uv_flag & UV_SYNC_SELECTION) { - sync = 1; - - if (ts->selectmode & SCE_SELECT_FACE) { - selectmode = UV_SELECT_FACE; - } - else if (ts->selectmode & SCE_SELECT_EDGE) { - selectmode = UV_SELECT_EDGE; - } - else { - selectmode = UV_SELECT_VERTEX; - } - - sticky = SI_STICKY_DISABLE; - } - else { - sync = 0; - selectmode = ts->uv_selectmode; - sticky = (sima) ? sima->sticky : 1; - } - - /* find nearest element */ - if (loop) { - /* find edge */ - found_item = uv_find_nearest_edge_multi(scene, ima, objects, objects_len, co, &hit); - } - else if (selectmode == UV_SELECT_VERTEX) { - /* find vertex */ - found_item = uv_find_nearest_vert_multi( - scene, ima, objects, objects_len, co, penalty_dist, &hit); - found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist); - - if (found_item) { - /* mark 1 vertex as being hit */ - hitv = BLI_array_alloca(hitv, hit.efa->len); - hituv = BLI_array_alloca(hituv, hit.efa->len); - copy_vn_i(hitv, hit.efa->len, 0xFFFFFFFF); - - hitv[hit.lindex] = BM_elem_index_get(hit.l->v); - hituv[hit.lindex] = hit.luv->uv; - - hitlen = hit.efa->len; - } - } - else if (selectmode == UV_SELECT_EDGE) { - /* find edge */ - found_item = uv_find_nearest_edge_multi(scene, ima, objects, objects_len, co, &hit); - found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist); - - if (found_item) { - /* mark 2 edge vertices as being hit */ - hitv = BLI_array_alloca(hitv, hit.efa->len); - hituv = BLI_array_alloca(hituv, hit.efa->len); - copy_vn_i(hitv, hit.efa->len, 0xFFFFFFFF); - - hitv[hit.lindex] = BM_elem_index_get(hit.l->v); - hitv[(hit.lindex + 1) % hit.efa->len] = BM_elem_index_get(hit.l->next->v); - hituv[hit.lindex] = hit.luv->uv; - hituv[(hit.lindex + 1) % hit.efa->len] = hit.luv_next->uv; - - hitlen = hit.efa->len; - } - } - else if (selectmode == UV_SELECT_FACE) { - /* find face */ - found_item = uv_find_nearest_face_multi(scene, ima, objects, objects_len, co, &hit); - found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist); - - if (found_item) { - BMEditMesh *em = BKE_editmesh_from_object(hit.ob); - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - /* make active */ - BM_mesh_active_face_set(em->bm, hit.efa); - - /* mark all face vertices as being hit */ - - hitv = BLI_array_alloca(hitv, hit.efa->len); - hituv = BLI_array_alloca(hituv, hit.efa->len); - BM_ITER_ELEM_INDEX (l, &liter, hit.efa, BM_LOOPS_OF_FACE, i) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - hituv[i] = luv->uv; - hitv[i] = BM_elem_index_get(l->v); - } - - hitlen = hit.efa->len; - } - } - else if (selectmode == UV_SELECT_ISLAND) { - found_item = uv_find_nearest_edge_multi(scene, ima, objects, objects_len, co, &hit); - found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist); - } - - if (!found_item) { - if (deselect_all) { - uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - uv_select_tag_update_for_object(depsgraph, ts, obedit); - } - - return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; - } - return OPERATOR_CANCELLED; - } - - Object *obedit = hit.ob; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - /* do selection */ - if (loop) { - if (!extend) { - /* TODO(MULTI_EDIT): We only need to de-select non-active */ - uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); - } - flush = uv_select_edgeloop(scene, ima, obedit, &hit, limit, extend); - } - else if (selectmode == UV_SELECT_ISLAND) { - if (!extend) { - /* TODO(MULTI_EDIT): We only need to de-select non-active */ - uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); - } - /* Current behavior of 'extend' - * is actually toggling, so pass extend flag as 'toggle' here */ - uv_select_linked_multi( - scene, ima, objects, objects_len, limit, &hit, false, false, extend, false); - } - else if (extend) { - if (selectmode == UV_SELECT_VERTEX) { - /* (de)select uv vertex */ - select = !uvedit_uv_select_test(scene, hit.l, cd_loop_uv_offset); - uvedit_uv_select_set(em, scene, hit.l, select, true, cd_loop_uv_offset); - flush = 1; - } - else if (selectmode == UV_SELECT_EDGE) { - /* (de)select edge */ - select = !(uvedit_edge_select_test(scene, hit.l, cd_loop_uv_offset)); - uvedit_edge_select_set(em, scene, hit.l, select, true, cd_loop_uv_offset); - flush = 1; - } - else if (selectmode == UV_SELECT_FACE) { - /* (de)select face */ - select = !(uvedit_face_select_test(scene, hit.efa, cd_loop_uv_offset)); - uvedit_face_select_set(scene, em, hit.efa, select, true, cd_loop_uv_offset); - flush = -1; - } - - /* de-selecting an edge may deselect a face too - validate */ - if (sync) { - if (select == false) { - BM_select_history_validate(em->bm); - } - } - - /* (de)select sticky uv nodes */ - if (sticky != SI_STICKY_DISABLE) { - - BM_mesh_elem_index_ensure(em->bm, BM_VERT); - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { - continue; - } - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - if (uv_sticky_select( - limit, hitv, BM_elem_index_get(l->v), hituv, luv->uv, sticky, hitlen)) { - uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); - } - } - } - - flush = select ? 1 : -1; - } - } - else { - /* deselect all */ - uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); - - if (selectmode == UV_SELECT_VERTEX) { - /* select vertex */ - uvedit_uv_select_enable(em, scene, hit.l, true, cd_loop_uv_offset); - flush = 1; - } - else if (selectmode == UV_SELECT_EDGE) { - /* select edge */ - uvedit_edge_select_enable(em, scene, hit.l, true, cd_loop_uv_offset); - flush = 1; - } - else if (selectmode == UV_SELECT_FACE) { - /* select face */ - uvedit_face_select_enable(scene, em, hit.efa, true, cd_loop_uv_offset); - } - - /* select sticky uvs */ - if (sticky != SI_STICKY_DISABLE) { - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { - continue; - } - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (sticky == SI_STICKY_DISABLE) { - continue; - } - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - - if (uv_sticky_select( - limit, hitv, BM_elem_index_get(l->v), hituv, luv->uv, sticky, hitlen)) { - uvedit_uv_select_enable(em, scene, l, false, cd_loop_uv_offset); - } - - flush = 1; - } - } - } - } - - if (sync) { - /* flush for mesh selection */ - - /* before bmesh */ -#if 0 - if (ts->selectmode != SCE_SELECT_FACE) { - if (flush == 1) { - EDBM_select_flush(em); - } - else if (flush == -1) { - EDBM_deselect_flush(em); - } - } -#else - if (flush != 0) { - if (loop) { - /* push vertex -> edge selection */ - if (select) { - EDBM_select_flush(em); - } - else { - EDBM_deselect_flush(em); - } - } - else { - EDBM_selectmode_flush(em); - } - } -#endif - } - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obiter = objects[ob_index]; - uv_select_tag_update_for_object(depsgraph, ts, obiter); - } - - return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; -} -static int uv_mouse_select( - bContext *C, const float co[2], const bool extend, const bool deselect_all, const bool loop) -{ - ViewLayer *view_layer = CTX_data_view_layer(C); - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( - view_layer, ((View3D *)NULL), &objects_len); - int ret = uv_mouse_select_multi(C, objects, objects_len, co, extend, deselect_all, loop); - MEM_freeN(objects); - return ret; -} - -static int uv_select_exec(bContext *C, wmOperator *op) -{ - float co[2]; - - RNA_float_get_array(op->ptr, "location", co); - const bool extend = RNA_boolean_get(op->ptr, "extend"); - const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); - const bool loop = false; - - return uv_mouse_select(C, co, extend, deselect_all, loop); -} - -static int uv_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - ARegion *region = CTX_wm_region(C); - float co[2]; - - UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); - RNA_float_set_array(op->ptr, "location", co); - - return uv_select_exec(C, op); -} - -static void UV_OT_select(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select"; - ot->description = "Select UV vertices"; - ot->idname = "UV_OT_select"; - ot->flag = OPTYPE_UNDO; - - /* api callbacks */ - ot->exec = uv_select_exec; - ot->invoke = uv_select_invoke; - ot->poll = ED_operator_uvedit; /* requires space image */ - - /* properties */ - PropertyRNA *prop; - RNA_def_boolean(ot->srna, - "extend", - 0, - "Extend", - "Extend selection rather than clearing the existing selection"); - prop = RNA_def_boolean(ot->srna, - "deselect_all", - false, - "Deselect On Nothing", - "Deselect all when nothing under the cursor"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - - RNA_def_float_vector( - ot->srna, - "location", - 2, - NULL, - -FLT_MAX, - FLT_MAX, - "Location", - "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", - -100.0f, - 100.0f); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Loop Select Operator - * \{ */ - -static int uv_select_loop_exec(bContext *C, wmOperator *op) -{ - float co[2]; - - RNA_float_get_array(op->ptr, "location", co); - const bool extend = RNA_boolean_get(op->ptr, "extend"); - const bool deselect_all = false; - const bool loop = true; - - return uv_mouse_select(C, co, extend, deselect_all, loop); -} - -static int uv_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - ARegion *region = CTX_wm_region(C); - float co[2]; - - UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); - RNA_float_set_array(op->ptr, "location", co); - - return uv_select_loop_exec(C, op); -} - -static void UV_OT_select_loop(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Loop Select"; - ot->description = "Select a loop of connected UV vertices"; - ot->idname = "UV_OT_select_loop"; - ot->flag = OPTYPE_UNDO; - - /* api callbacks */ - ot->exec = uv_select_loop_exec; - ot->invoke = uv_select_loop_invoke; - ot->poll = ED_operator_uvedit; /* requires space image */ - - /* properties */ - RNA_def_boolean(ot->srna, - "extend", - 0, - "Extend", - "Extend selection rather than clearing the existing selection"); - RNA_def_float_vector( - ot->srna, - "location", - 2, - NULL, - -FLT_MAX, - FLT_MAX, - "Location", - "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", - -100.0f, - 100.0f); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Linked Operator - * \{ */ - -static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent *event, bool pick) -{ - SpaceImage *sima = CTX_wm_space_image(C); - Scene *scene = CTX_data_scene(C); - const ToolSettings *ts = scene->toolsettings; - ViewLayer *view_layer = CTX_data_view_layer(C); - Image *ima = CTX_data_edit_image(C); - float limit[2]; - bool extend = true; - bool deselect = false; - bool select_faces = (ts->uv_flag & UV_SYNC_SELECTION) && (ts->selectmode & SCE_SELECT_FACE); - - UvNearestHit hit = UV_NEAREST_HIT_INIT; - - if ((ts->uv_flag & UV_SYNC_SELECTION) && !(ts->selectmode & SCE_SELECT_FACE)) { - BKE_report(op->reports, - RPT_ERROR, - "Select linked only works in face select mode when sync selection is enabled"); - return OPERATOR_CANCELLED; - } - - if (pick) { - extend = RNA_boolean_get(op->ptr, "extend"); - deselect = RNA_boolean_get(op->ptr, "deselect"); - } - uvedit_pixel_to_float(sima, limit, 0.05f); - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( - view_layer, ((View3D *)NULL), &objects_len); - - if (pick) { - float co[2]; - - if (event) { - /* invoke */ - ARegion *region = CTX_wm_region(C); - - UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); - RNA_float_set_array(op->ptr, "location", co); - } - else { - /* exec */ - RNA_float_get_array(op->ptr, "location", co); - } - - if (!uv_find_nearest_edge_multi(scene, ima, objects, objects_len, co, &hit)) { - MEM_freeN(objects); - return OPERATOR_CANCELLED; - } - } - - if (!extend) { - uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); - } - - uv_select_linked_multi(scene, - ima, - objects, - objects_len, - limit, - pick ? &hit : NULL, - extend, - deselect, - false, - select_faces); - - /* weak!, but works */ - Object **objects_free = objects; - if (pick) { - objects = &hit.ob; - objects_len = 1; - } - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - DEG_id_tag_update(obedit->data, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); - } - - MEM_SAFE_FREE(objects_free); - - return OPERATOR_FINISHED; -} - -static int uv_select_linked_exec(bContext *C, wmOperator *op) -{ - return uv_select_linked_internal(C, op, NULL, false); -} - -static void UV_OT_select_linked(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Linked"; - ot->description = "Select all UV vertices linked to the active UV map"; - ot->idname = "UV_OT_select_linked"; - - /* api callbacks */ - ot->exec = uv_select_linked_exec; - ot->poll = ED_operator_uvedit; /* requires space image */ - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Linked (Cursor Pick) Operator - * \{ */ - -static int uv_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - return uv_select_linked_internal(C, op, event, true); -} - -static int uv_select_linked_pick_exec(bContext *C, wmOperator *op) -{ - return uv_select_linked_internal(C, op, NULL, true); -} - -static void UV_OT_select_linked_pick(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Linked Pick"; - ot->description = "Select all UV vertices linked under the mouse"; - ot->idname = "UV_OT_select_linked_pick"; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* api callbacks */ - ot->invoke = uv_select_linked_pick_invoke; - ot->exec = uv_select_linked_pick_exec; - ot->poll = ED_operator_uvedit; /* requires space image */ - - /* properties */ - RNA_def_boolean(ot->srna, - "extend", - 0, - "Extend", - "Extend selection rather than clearing the existing selection"); - RNA_def_boolean(ot->srna, - "deselect", - 0, - "Deselect", - "Deselect linked UV vertices rather than selecting them"); - RNA_def_float_vector( - ot->srna, - "location", - 2, - NULL, - -FLT_MAX, - FLT_MAX, - "Location", - "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", - -100.0f, - 100.0f); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Split Operator - * \{ */ - -/** - * \note This is based on similar use case to #MESH_OT_split(), which has a similar effect - * but in this case they are not joined to begin with (only having the behavior of being joined) - * so its best to call this #uv_select_split() instead of just split(), but assigned to the same - * key as #MESH_OT_split - Campbell. - */ -static int uv_select_split_exec(bContext *C, wmOperator *op) -{ - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - const ToolSettings *ts = scene->toolsettings; - Image *ima = CTX_data_edit_image(C); - - BMFace *efa; - BMLoop *l; - BMIter iter, liter; - MLoopUV *luv; - - if (ts->uv_flag & UV_SYNC_SELECTION) { - BKE_report(op->reports, RPT_ERROR, "Cannot split selection when sync selection is enabled"); - return OPERATOR_CANCELLED; - } - - bool changed_multi = false; - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( - view_layer, ((View3D *)NULL), &objects_len); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMesh *bm = BKE_editmesh_from_object(obedit)->bm; - - bool changed = false; - - const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); - - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - bool is_sel = false; - bool is_unsel = false; - - if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { - continue; - } - - /* are we all selected? */ - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - - if (luv->flag & MLOOPUV_VERTSEL) { - is_sel = true; - } - else { - is_unsel = true; - } - - /* we have mixed selection, bail out */ - if (is_sel && is_unsel) { - break; - } - } - - if (is_sel && is_unsel) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv->flag &= ~MLOOPUV_VERTSEL; - } - - changed = true; - } - } - - if (changed) { - changed_multi = true; - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL); - } - } - MEM_freeN(objects); - - return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; -} - -static void UV_OT_select_split(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Split"; - ot->description = "Select only entirely selected faces"; - ot->idname = "UV_OT_select_split"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* api callbacks */ - ot->exec = uv_select_split_exec; - ot->poll = ED_operator_uvedit; /* requires space image */ -} - -static void uv_select_sync_flush(const ToolSettings *ts, BMEditMesh *em, const short select) -{ - /* bmesh API handles flushing but not on de-select */ - if (ts->uv_flag & UV_SYNC_SELECTION) { - if (ts->selectmode != SCE_SELECT_FACE) { - if (select == false) { - EDBM_deselect_flush(em); - } - else { - EDBM_select_flush(em); - } - } - - if (select == false) { - BM_select_history_validate(em->bm); - } - } -} - -static void uv_select_tag_update_for_object(Depsgraph *depsgraph, - const ToolSettings *ts, - Object *obedit) -{ - if (ts->uv_flag & UV_SYNC_SELECTION) { - DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); - WM_main_add_notifier(NC_GEOM | ND_SELECT, obedit->data); - } - else { - Object *obedit_eval = DEG_get_evaluated_object(depsgraph, obedit); - BKE_mesh_batch_cache_dirty_tag(obedit_eval->data, BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT); - /* Only for region redraw. */ - WM_main_add_notifier(NC_GEOM | ND_SELECT, obedit->data); - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select/Tag Flushing Utils - * - * Utility functions to flush the uv-selection from tags. - * \{ */ - -/** - * helper function for #uv_select_flush_from_tag_loop and uv_select_flush_from_tag_face - */ -static void uv_select_flush_from_tag_sticky_loc_internal(Scene *scene, - BMEditMesh *em, - UvVertMap *vmap, - const uint efa_index, - BMLoop *l, - const bool select, - const int cd_loop_uv_offset) -{ - UvMapVert *start_vlist = NULL, *vlist_iter; - BMFace *efa_vlist; - - uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); - - vlist_iter = BM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v)); - - while (vlist_iter) { - if (vlist_iter->separate) { - start_vlist = vlist_iter; - } - - if (efa_index == vlist_iter->poly_index) { - break; - } - - vlist_iter = vlist_iter->next; - } - - vlist_iter = start_vlist; - while (vlist_iter) { - - if (vlist_iter != start_vlist && vlist_iter->separate) { - break; - } - - if (efa_index != vlist_iter->poly_index) { - BMLoop *l_other; - efa_vlist = BM_face_at_index(em->bm, vlist_iter->poly_index); - /* tf_vlist = BM_ELEM_CD_GET_VOID_P(efa_vlist, cd_poly_tex_offset); */ /* UNUSED */ - - l_other = BM_iter_at_index( - em->bm, BM_LOOPS_OF_FACE, efa_vlist, vlist_iter->loop_of_poly_index); - - uvedit_uv_select_set(em, scene, l_other, select, false, cd_loop_uv_offset); - } - vlist_iter = vlist_iter->next; - } -} - -/** - * Flush the selection from face tags based on sticky and selection modes. - * - * needed because settings the selection a face is done in a number of places but it also - * needs to respect the sticky modes for the UV verts, so dealing with the sticky modes - * is best done in a separate function. - * - * \note This function is very similar to #uv_select_flush_from_tag_loop, - * be sure to update both upon changing. - */ -static void uv_select_flush_from_tag_face(SpaceImage *sima, - Scene *scene, - Object *obedit, - const bool select) -{ - /* Selecting UV Faces with some modes requires us to change - * the selection in other faces (depending on the sticky mode). - * - * This only needs to be done when the Mesh is not used for - * selection (so for sticky modes, vertex or location based). */ - - const ToolSettings *ts = scene->toolsettings; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMFace *efa; - BMLoop *l; - BMIter iter, liter; - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_VERTEX) { - /* Tag all verts as untouched, then touch the ones that have a face center - * in the loop and select all MLoopUV's that use a touched vert. */ - BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - BM_elem_flag_enable(l->v, BM_ELEM_TAG); - } - } - } - - /* now select tagged verts */ - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */ - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) { - uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); - } - } - } - } - else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) { - struct UvVertMap *vmap; - float limit[2]; - uint efa_index; - - uvedit_pixel_to_float(sima, limit, 0.05); - - BM_mesh_elem_table_ensure(em->bm, BM_FACE); - vmap = BM_uv_vert_map_create(em->bm, limit, false, false); - if (vmap == NULL) { - return; - } - - BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) { - if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { - /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */ - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - uv_select_flush_from_tag_sticky_loc_internal( - scene, em, vmap, efa_index, l, select, cd_loop_uv_offset); - } - } - } - BM_uv_vert_map_free(vmap); - } - else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */ - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { - uvedit_face_select_set(scene, em, efa, select, false, cd_loop_uv_offset); - } - } - } -} - -/** - * Flush the selection from loop tags based on sticky and selection modes. - * - * needed because settings the selection a face is done in a number of places but it also needs - * to respect the sticky modes for the UV verts, so dealing with the sticky modes is best done - * in a separate function. - * - * \note This function is very similar to #uv_select_flush_from_tag_loop, - * be sure to update both upon changing. - */ -static void uv_select_flush_from_tag_loop(SpaceImage *sima, - Scene *scene, - Object *obedit, - const bool select) -{ - /* Selecting UV Loops with some modes requires us to change - * the selection in other faces (depending on the sticky mode). - * - * This only needs to be done when the Mesh is not used for - * selection (so for sticky modes, vertex or location based). */ - - const ToolSettings *ts = scene->toolsettings; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMFace *efa; - BMLoop *l; - BMIter iter, liter; - - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_VERTEX) { - /* Tag all verts as untouched, then touch the ones that have a face center - * in the loop and select all MLoopUV's that use a touched vert. */ - BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (BM_elem_flag_test(l, BM_ELEM_TAG)) { - BM_elem_flag_enable(l->v, BM_ELEM_TAG); - } - } - } - - /* now select tagged verts */ - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */ - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) { - uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); - } - } - } - } - else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) { - struct UvVertMap *vmap; - float limit[2]; - uint efa_index; - - uvedit_pixel_to_float(sima, limit, 0.05); - - BM_mesh_elem_table_ensure(em->bm, BM_FACE); - vmap = BM_uv_vert_map_create(em->bm, limit, false, false); - if (vmap == NULL) { - return; - } - - BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) { - /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */ - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (BM_elem_flag_test(l, BM_ELEM_TAG)) { - uv_select_flush_from_tag_sticky_loc_internal( - scene, em, vmap, efa_index, l, select, cd_loop_uv_offset); - } - } - } - BM_uv_vert_map_free(vmap); - } - else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */ - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (BM_elem_flag_test(l, BM_ELEM_TAG)) { - uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); - } - } - } - } -} - -/** \} */ - -#define UV_SELECT_ISLAND_LIMIT \ - float limit[2]; \ - uvedit_pixel_to_float(sima, limit, 0.05f) - -/* -------------------------------------------------------------------- */ -/** \name Box Select Operator - * \{ */ - -static int uv_box_select_exec(bContext *C, wmOperator *op) -{ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - SpaceImage *sima = CTX_wm_space_image(C); - Scene *scene = CTX_data_scene(C); - const ToolSettings *ts = scene->toolsettings; - ViewLayer *view_layer = CTX_data_view_layer(C); - Image *ima = CTX_data_edit_image(C); - ARegion *region = CTX_wm_region(C); - BMFace *efa; - BMLoop *l; - BMIter iter, liter; - MLoopUV *luv; - rctf rectf; - bool pinned; - const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ? - (ts->selectmode == SCE_SELECT_FACE) : - (ts->uv_selectmode == UV_SELECT_FACE)); - - /* get rectangle from operator */ - WM_operator_properties_border_to_rctf(op, &rectf); - UI_view2d_region_to_view_rctf(®ion->v2d, &rectf, &rectf); - - const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); - const bool select = (sel_op != SEL_OP_SUB); - const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op); - - pinned = RNA_boolean_get(op->ptr, "pinned"); - - UV_SELECT_ISLAND_LIMIT; - - bool changed_multi = false; - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( - view_layer, ((View3D *)NULL), &objects_len); - - if (use_pre_deselect) { - uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); - } - - /* don't indent to avoid diff noise! */ - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - bool changed = false; - - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - /* do actual selection */ - if (use_face_center && !pinned) { - /* handle face selection mode */ - float cent[2]; - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - /* assume not touched */ - BM_elem_flag_disable(efa, BM_ELEM_TAG); - - if (uvedit_face_visible_test(scene, obedit, ima, efa)) { - uv_poly_center(efa, cent, cd_loop_uv_offset); - if (BLI_rctf_isect_pt_v(&rectf, cent)) { - BM_elem_flag_enable(efa, BM_ELEM_TAG); - changed = true; - } - } - } - - /* (de)selects all tagged faces and deals with sticky modes */ - if (changed) { - uv_select_flush_from_tag_face(sima, scene, obedit, select); - } - } - else { - /* other selection modes */ - changed = true; - BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { - continue; - } - bool has_selected = false; - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - if ((select) != (uvedit_uv_select_test(scene, l, cd_loop_uv_offset))) { - if (!pinned || (ts->uv_flag & UV_SYNC_SELECTION)) { - /* UV_SYNC_SELECTION - can't do pinned selection */ - if (BLI_rctf_isect_pt_v(&rectf, luv->uv)) { - uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); - BM_elem_flag_enable(l->v, BM_ELEM_TAG); - has_selected = true; - } - } - else if (pinned) { - if ((luv->flag & MLOOPUV_PINNED) && BLI_rctf_isect_pt_v(&rectf, luv->uv)) { - uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); - BM_elem_flag_enable(l->v, BM_ELEM_TAG); - } - } - } - } - if (has_selected && ts->uv_selectmode == UV_SELECT_ISLAND) { - UvNearestHit hit = { - .ob = obedit, - .efa = efa, - }; - uv_select_linked_multi( - scene, ima, objects, objects_len, limit, &hit, true, !select, false, false); - } - } - - if (sima->sticky == SI_STICKY_VERTEX) { - uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset); - } - } - - if (changed || use_pre_deselect) { - changed_multi = true; - - uv_select_sync_flush(ts, em, select); - uv_select_tag_update_for_object(depsgraph, ts, obedit); - } - } - - MEM_freeN(objects); - - return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; -} - -static void UV_OT_select_box(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Box Select"; - ot->description = "Select UV vertices using box selection"; - ot->idname = "UV_OT_select_box"; - - /* api callbacks */ - ot->invoke = WM_gesture_box_invoke; - ot->exec = uv_box_select_exec; - ot->modal = WM_gesture_box_modal; - ot->poll = ED_operator_uvedit_space_image; /* requires space image */ - ot->cancel = WM_gesture_box_cancel; - - /* flags */ - ot->flag = OPTYPE_UNDO; - - /* properties */ - RNA_def_boolean(ot->srna, "pinned", 0, "Pinned", "Border select pinned UVs only"); - - WM_operator_properties_gesture_box(ot); - WM_operator_properties_select_operation_simple(ot); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Circle Select Operator - * \{ */ - -static int uv_inside_circle(const float uv[2], const float offset[2], const float ellipse[2]) -{ - /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */ - float x, y; - x = (uv[0] - offset[0]) * ellipse[0]; - y = (uv[1] - offset[1]) * ellipse[1]; - return ((x * x + y * y) < 1.0f); -} - -static int uv_circle_select_exec(bContext *C, wmOperator *op) -{ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - SpaceImage *sima = CTX_wm_space_image(C); - Image *ima = CTX_data_edit_image(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - const ToolSettings *ts = scene->toolsettings; - ARegion *region = CTX_wm_region(C); - BMFace *efa; - BMLoop *l; - BMIter iter, liter; - MLoopUV *luv; - int x, y, radius, width, height; - float zoomx, zoomy, offset[2], ellipse[2]; - - const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ? - (ts->selectmode == SCE_SELECT_FACE) : - (ts->uv_selectmode == UV_SELECT_FACE)); - - /* get operator properties */ - x = RNA_int_get(op->ptr, "x"); - y = RNA_int_get(op->ptr, "y"); - radius = RNA_int_get(op->ptr, "radius"); - - /* compute ellipse size and location, not a circle since we deal - * with non square image. ellipse is normalized, r = 1.0. */ - ED_space_image_get_size(sima, &width, &height); - ED_space_image_get_zoom(sima, region, &zoomx, &zoomy); - - ellipse[0] = width * zoomx / radius; - ellipse[1] = height * zoomy / radius; - - UI_view2d_region_to_view(®ion->v2d, x, y, &offset[0], &offset[1]); - - UV_SELECT_ISLAND_LIMIT; - - bool changed_multi = false; - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( - view_layer, ((View3D *)NULL), &objects_len); - - const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"), - WM_gesture_is_modal_first(op->customdata)); - const bool select = (sel_op != SEL_OP_SUB); - const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op); - - if (use_pre_deselect) { - uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); - } - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - bool changed = false; - - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - /* do selection */ - if (use_face_center) { - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - BM_elem_flag_disable(efa, BM_ELEM_TAG); - /* assume not touched */ - if (select != uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) { - float cent[2]; - uv_poly_center(efa, cent, cd_loop_uv_offset); - if (uv_inside_circle(cent, offset, ellipse)) { - BM_elem_flag_enable(efa, BM_ELEM_TAG); - changed = true; - } - } - } - - /* (de)selects all tagged faces and deals with sticky modes */ - if (changed) { - uv_select_flush_from_tag_face(sima, scene, obedit, select); - } - } - else { - BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { - continue; - } - bool has_selected = false; - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if ((select) != (uvedit_uv_select_test(scene, l, cd_loop_uv_offset))) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - if (uv_inside_circle(luv->uv, offset, ellipse)) { - changed = true; - uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); - BM_elem_flag_enable(l->v, BM_ELEM_TAG); - has_selected = true; - } - } - } - if (has_selected && ts->uv_selectmode == UV_SELECT_ISLAND) { - UvNearestHit hit = { - .ob = obedit, - .efa = efa, - }; - uv_select_linked_multi( - scene, ima, objects, objects_len, limit, &hit, true, !select, false, false); - } - } - - if (sima->sticky == SI_STICKY_VERTEX) { - uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset); - } - } - - if (changed || use_pre_deselect) { - changed_multi = true; - - uv_select_sync_flush(ts, em, select); - uv_select_tag_update_for_object(depsgraph, ts, obedit); - } - } - MEM_freeN(objects); - - return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; -} - -static void UV_OT_select_circle(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Circle Select"; - ot->description = "Select UV vertices using circle selection"; - ot->idname = "UV_OT_select_circle"; - - /* api callbacks */ - ot->invoke = WM_gesture_circle_invoke; - ot->modal = WM_gesture_circle_modal; - ot->exec = uv_circle_select_exec; - ot->poll = ED_operator_uvedit_space_image; /* requires space image */ - ot->cancel = WM_gesture_circle_cancel; - - /* flags */ - ot->flag = OPTYPE_UNDO; - - /* properties */ - WM_operator_properties_gesture_circle(ot); - WM_operator_properties_select_operation_simple(ot); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Lasso Select Operator - * \{ */ - -static bool do_lasso_select_mesh_uv(bContext *C, - const int mcords[][2], - short moves, - const eSelectOp sel_op) -{ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - SpaceImage *sima = CTX_wm_space_image(C); - Image *ima = CTX_data_edit_image(C); - ARegion *region = CTX_wm_region(C); - Scene *scene = CTX_data_scene(C); - const ToolSettings *ts = scene->toolsettings; - ViewLayer *view_layer = CTX_data_view_layer(C); - const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ? - (ts->selectmode == SCE_SELECT_FACE) : - (ts->uv_selectmode == UV_SELECT_FACE)); - const bool select = (sel_op != SEL_OP_SUB); - const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op); - - BMIter iter, liter; - - BMFace *efa; - BMLoop *l; - int screen_uv[2]; - bool changed_multi = false; - rcti rect; - - UV_SELECT_ISLAND_LIMIT; - - BLI_lasso_boundbox(&rect, mcords, moves); - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( - view_layer, ((View3D *)NULL), &objects_len); - - if (use_pre_deselect) { - uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); - } - - /* don't indent to avoid diff noise! */ - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - - bool changed = false; - - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - if (use_face_center) { /* Face Center Sel */ - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - BM_elem_flag_disable(efa, BM_ELEM_TAG); - /* assume not touched */ - if (select != uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) { - float cent[2]; - uv_poly_center(efa, cent, cd_loop_uv_offset); - - if (UI_view2d_view_to_region_clip( - ®ion->v2d, cent[0], cent[1], &screen_uv[0], &screen_uv[1]) && - BLI_rcti_isect_pt_v(&rect, screen_uv) && - BLI_lasso_is_point_inside( - mcords, moves, screen_uv[0], screen_uv[1], V2D_IS_CLIPPED)) { - BM_elem_flag_enable(efa, BM_ELEM_TAG); - changed = true; - } - } - } - - /* (de)selects all tagged faces and deals with sticky modes */ - if (changed) { - uv_select_flush_from_tag_face(sima, scene, obedit, select); - } - } - else { /* Vert Sel */ - BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { - continue; - } - bool has_selected = false; - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if ((select) != (uvedit_uv_select_test(scene, l, cd_loop_uv_offset))) { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - if (UI_view2d_view_to_region_clip( - ®ion->v2d, luv->uv[0], luv->uv[1], &screen_uv[0], &screen_uv[1]) && - BLI_rcti_isect_pt_v(&rect, screen_uv) && - BLI_lasso_is_point_inside( - mcords, moves, screen_uv[0], screen_uv[1], V2D_IS_CLIPPED)) { - uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); - changed = true; - BM_elem_flag_enable(l->v, BM_ELEM_TAG); - has_selected = true; - } - } - } - if (has_selected && ts->uv_selectmode == UV_SELECT_ISLAND) { - UvNearestHit hit = { - .ob = obedit, - .efa = efa, - }; - uv_select_linked_multi( - scene, ima, objects, objects_len, limit, &hit, true, !select, false, false); - } - } - - if (sima->sticky == SI_STICKY_VERTEX) { - uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset); - } - } - - if (changed || use_pre_deselect) { - changed_multi = true; - - uv_select_sync_flush(ts, em, select); - uv_select_tag_update_for_object(depsgraph, ts, obedit); - } - } - MEM_freeN(objects); - - return changed_multi; -} - -static int uv_lasso_select_exec(bContext *C, wmOperator *op) -{ - int mcords_tot; - const int(*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot); - - if (mcords) { - const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); - bool changed = do_lasso_select_mesh_uv(C, mcords, mcords_tot, sel_op); - MEM_freeN((void *)mcords); - - return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED; - } - - return OPERATOR_PASS_THROUGH; -} - -static void UV_OT_select_lasso(wmOperatorType *ot) -{ - ot->name = "Lasso Select UV"; - ot->description = "Select UVs using lasso selection"; - ot->idname = "UV_OT_select_lasso"; - - ot->invoke = WM_gesture_lasso_invoke; - ot->modal = WM_gesture_lasso_modal; - ot->exec = uv_lasso_select_exec; - ot->poll = ED_operator_uvedit_space_image; - ot->cancel = WM_gesture_lasso_cancel; - - /* flags */ - ot->flag = OPTYPE_UNDO; - - /* properties */ - WM_operator_properties_gesture_lasso(ot); - WM_operator_properties_select_operation_simple(ot); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Snap Cursor Operator * \{ */ @@ -4379,314 +1473,6 @@ static void UV_OT_pin(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Select Pinned UV's Operator - * \{ */ - -static int uv_select_pinned_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene = CTX_data_scene(C); - const ToolSettings *ts = scene->toolsettings; - ViewLayer *view_layer = CTX_data_view_layer(C); - Image *ima = CTX_data_edit_image(C); - BMFace *efa; - BMLoop *l; - BMIter iter, liter; - MLoopUV *luv; - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( - view_layer, ((View3D *)NULL), &objects_len); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - bool changed = false; - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { - continue; - } - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - - if (luv->flag & MLOOPUV_PINNED) { - uvedit_uv_select_enable(em, scene, l, false, cd_loop_uv_offset); - changed = true; - } - } - } - - if (changed) { - uv_select_tag_update_for_object(depsgraph, ts, obedit); - } - } - MEM_freeN(objects); - - return OPERATOR_FINISHED; -} - -static void UV_OT_select_pinned(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Selected Pinned"; - ot->description = "Select all pinned UV vertices"; - ot->idname = "UV_OT_select_pinned"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* api callbacks */ - ot->exec = uv_select_pinned_exec; - ot->poll = ED_operator_uvedit; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Select Overlap Operator - * \{ */ - -BLI_INLINE uint overlap_hash(const void *overlap_v) -{ - const BVHTreeOverlap *overlap = overlap_v; - - /* Designed to treat (A,B) and (B,A) as the same. */ - int x = overlap->indexA; - int y = overlap->indexB; - if (x > y) { - SWAP(int, x, y); - } - return BLI_hash_int_2d(x, y); -} - -BLI_INLINE bool overlap_cmp(const void *a_v, const void *b_v) -{ - const BVHTreeOverlap *a = a_v; - const BVHTreeOverlap *b = b_v; - return !((a->indexA == b->indexA && a->indexB == b->indexB) || - (a->indexA == b->indexB && a->indexB == b->indexA)); -} - -struct UVOverlapData { - int ob_index; - int face_index; - float tri[3][2]; -}; - -static int uv_select_overlap(bContext *C, const bool extend) -{ - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - Image *ima = CTX_data_edit_image(C); - - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( - view_layer, ((View3D *)NULL), &objects_len); - - /* Calculate maximum number of tree nodes and prepare initial selection. */ - uint uv_tri_len = 0; - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - BM_mesh_elem_table_ensure(em->bm, BM_FACE); - BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE); - BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); - if (!extend) { - uv_select_all_perform(scene, ima, obedit, SEL_DESELECT); - } - - BMIter iter; - BMFace *efa; - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!uvedit_face_visible_test_ex(scene->toolsettings, obedit, ima, efa)) { - continue; - } - uv_tri_len += efa->len - 2; - } - } - - struct UVOverlapData *overlap_data = MEM_mallocN(sizeof(struct UVOverlapData) * uv_tri_len, - "UvOverlapData"); - BVHTree *uv_tree = BLI_bvhtree_new(uv_tri_len, 0.0f, 4, 6); - - /* Use a global data index when inserting into the BVH. */ - int data_index = 0; - - int face_len_alloc = 3; - float(*uv_verts)[2] = MEM_mallocN(sizeof(*uv_verts) * face_len_alloc, "UvOverlapCoords"); - uint(*indices)[3] = MEM_mallocN(sizeof(*indices) * (face_len_alloc - 2), "UvOverlapTris"); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMIter iter, liter; - BMFace *efa; - BMLoop *l; - - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - - /* Triangulate each UV face and store it inside the BVH. */ - int face_index; - BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, face_index) { - - if (!uvedit_face_visible_test_ex(scene->toolsettings, obedit, ima, efa)) { - continue; - } - - const uint face_len = efa->len; - const uint tri_len = face_len - 2; - - if (face_len_alloc < face_len) { - MEM_freeN(uv_verts); - MEM_freeN(indices); - uv_verts = MEM_mallocN(sizeof(*uv_verts) * face_len, "UvOverlapCoords"); - indices = MEM_mallocN(sizeof(*indices) * tri_len, "UvOverlapTris"); - face_len_alloc = face_len; - } - - int vert_index; - BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, vert_index) { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - copy_v2_v2(uv_verts[vert_index], luv->uv); - } - - BLI_polyfill_calc(uv_verts, face_len, 0, indices); - - for (int t = 0; t < tri_len; t++) { - overlap_data[data_index].ob_index = ob_index; - overlap_data[data_index].face_index = face_index; - - /* BVH needs 3D, overlap data uses 2D. */ - const float tri[3][3] = { - {UNPACK2(uv_verts[indices[t][0]]), 0.0f}, - {UNPACK2(uv_verts[indices[t][1]]), 0.0f}, - {UNPACK2(uv_verts[indices[t][2]]), 0.0f}, - }; - - copy_v2_v2(overlap_data[data_index].tri[0], tri[0]); - copy_v2_v2(overlap_data[data_index].tri[1], tri[1]); - copy_v2_v2(overlap_data[data_index].tri[2], tri[2]); - - BLI_bvhtree_insert(uv_tree, data_index, &tri[0][0], 3); - data_index++; - } - } - } - BLI_assert(data_index == uv_tri_len); - - MEM_freeN(uv_verts); - MEM_freeN(indices); - - BLI_bvhtree_balance(uv_tree); - - uint tree_overlap_len; - BVHTreeOverlap *overlap = BLI_bvhtree_overlap(uv_tree, uv_tree, &tree_overlap_len, NULL, NULL); - - if (overlap != NULL) { - GSet *overlap_set = BLI_gset_new_ex(overlap_hash, overlap_cmp, __func__, tree_overlap_len); - - for (int i = 0; i < tree_overlap_len; i++) { - /* Skip overlaps against yourself. */ - if (overlap[i].indexA == overlap[i].indexB) { - continue; - } - - /* Skip overlaps that have already been tested. */ - if (!BLI_gset_add(overlap_set, &overlap[i])) { - continue; - } - - const struct UVOverlapData *o_a = &overlap_data[overlap[i].indexA]; - const struct UVOverlapData *o_b = &overlap_data[overlap[i].indexB]; - Object *obedit_a = objects[o_a->ob_index]; - Object *obedit_b = objects[o_b->ob_index]; - BMEditMesh *em_a = BKE_editmesh_from_object(obedit_a); - BMEditMesh *em_b = BKE_editmesh_from_object(obedit_b); - BMFace *face_a = em_a->bm->ftable[o_a->face_index]; - BMFace *face_b = em_b->bm->ftable[o_b->face_index]; - const int cd_loop_uv_offset_a = CustomData_get_offset(&em_a->bm->ldata, CD_MLOOPUV); - const int cd_loop_uv_offset_b = CustomData_get_offset(&em_b->bm->ldata, CD_MLOOPUV); - - /* Skip if both faces are already selected. */ - if (uvedit_face_select_test(scene, face_a, cd_loop_uv_offset_a) && - uvedit_face_select_test(scene, face_b, cd_loop_uv_offset_b)) { - continue; - } - - /* Main tri-tri overlap test. */ - const float endpoint_bias = -1e-4f; - const float(*t1)[2] = o_a->tri; - const float(*t2)[2] = o_b->tri; - float vi[2]; - bool result = ( - /* Don't use 'isect_tri_tri_v2' here - * because it's important to ignore overlap at end-points. */ - isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[0], t2[1], endpoint_bias, vi) == 1 || - isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[1], t2[2], endpoint_bias, vi) == 1 || - isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[2], t2[0], endpoint_bias, vi) == 1 || - isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[0], t2[1], endpoint_bias, vi) == 1 || - isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[1], t2[2], endpoint_bias, vi) == 1 || - isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[2], t2[0], endpoint_bias, vi) == 1 || - isect_seg_seg_v2_point_ex(t1[2], t1[0], t2[0], t2[1], endpoint_bias, vi) == 1 || - isect_seg_seg_v2_point_ex(t1[2], t1[0], t2[1], t2[2], endpoint_bias, vi) == 1 || - isect_point_tri_v2(t1[0], t2[0], t2[1], t2[2]) != 0 || - isect_point_tri_v2(t2[0], t1[0], t1[1], t1[2]) != 0); - - if (result) { - uvedit_face_select_enable(scene, em_a, face_a, false, cd_loop_uv_offset_a); - uvedit_face_select_enable(scene, em_b, face_b, false, cd_loop_uv_offset_b); - } - } - - BLI_gset_free(overlap_set, NULL); - MEM_freeN(overlap); - } - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - uv_select_tag_update_for_object(depsgraph, scene->toolsettings, objects[ob_index]); - } - - BLI_bvhtree_free(uv_tree); - - MEM_freeN(overlap_data); - MEM_freeN(objects); - - return OPERATOR_FINISHED; -} - -static int uv_select_overlap_exec(bContext *C, wmOperator *op) -{ - bool extend = RNA_boolean_get(op->ptr, "extend"); - return uv_select_overlap(C, extend); -} - -static void UV_OT_select_overlap(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Overlap"; - ot->description = "Select all UV faces which overlap each other"; - ot->idname = "UV_OT_select_overlap"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* api callbacks */ - ot->exec = uv_select_overlap_exec; - ot->poll = ED_operator_uvedit; - - /* properties */ - RNA_def_boolean(ot->srna, - "extend", - 0, - "Extend", - "Extend selection rather than clearing the existing selection"); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Hide Operator * \{ */ @@ -5334,6 +2120,7 @@ static void UV_OT_mark_seam(wmOperatorType *ot) void ED_operatortypes_uvedit(void) { + /* uvedit_select.c */ WM_operatortype_append(UV_OT_select_all); WM_operatortype_append(UV_OT_select); WM_operatortype_append(UV_OT_select_loop); diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c new file mode 100644 index 00000000000..06866195c1c --- /dev/null +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -0,0 +1,3415 @@ +/* + * 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 eduv + */ + +#include <math.h> +#include <stdlib.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_image_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" + +#include "BLI_alloca.h" +#include "BLI_blenlib.h" +#include "BLI_hash.h" +#include "BLI_kdopbvh.h" +#include "BLI_lasso_2d.h" +#include "BLI_math.h" +#include "BLI_polyfill_2d.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_editmesh.h" +#include "BKE_layer.h" +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" +#include "BKE_report.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "ED_image.h" +#include "ED_mesh.h" +#include "ED_screen.h" +#include "ED_select_utils.h" +#include "ED_uvedit.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_view2d.h" + +#include "uvedit_intern.h" + +static void uv_select_all_perform(Scene *scene, Image *ima, Object *obedit, int action); +static void uv_select_all_perform_multi( + Scene *scene, Image *ima, Object **objects, const uint objects_len, int action); +static void uv_select_flush_from_tag_face(SpaceImage *sima, + Scene *scene, + Object *obedit, + const bool select); +static void uv_select_flush_from_tag_loop(SpaceImage *sima, + Scene *scene, + Object *obedit, + const bool select); +static void uv_select_tag_update_for_object(Depsgraph *depsgraph, + const ToolSettings *ts, + Object *obedit); + +/* -------------------------------------------------------------------- */ +/** \name Visibility and Selection Utilities + * \{ */ + +static void uv_select_island_limit_default(SpaceImage *sima, float r_limit[2]) +{ + uvedit_pixel_to_float(sima, 0.05f, r_limit); +} + +static void uvedit_vertex_select_tagged(BMEditMesh *em, + Scene *scene, + bool select, + int cd_loop_uv_offset) +{ + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) { + uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); + } + } + } +} + +bool uvedit_face_visible_nolocal_ex(const ToolSettings *ts, BMFace *efa) +{ + if (ts->uv_flag & UV_SYNC_SELECTION) { + return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0); + } + else { + return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0 && BM_elem_flag_test(efa, BM_ELEM_SELECT)); + } +} +bool uvedit_face_visible_nolocal(const Scene *scene, BMFace *efa) +{ + return uvedit_face_visible_nolocal_ex(scene->toolsettings, efa); +} + +bool uvedit_face_visible_test_ex(const ToolSettings *ts, Object *obedit, Image *ima, BMFace *efa) +{ + if (ts->uv_flag & UV_SHOW_SAME_IMAGE) { + Image *face_image; + ED_object_get_active_image(obedit, efa->mat_nr + 1, &face_image, NULL, NULL, NULL); + return (face_image == ima) ? uvedit_face_visible_nolocal_ex(ts, efa) : false; + } + else { + return uvedit_face_visible_nolocal_ex(ts, efa); + } +} +bool uvedit_face_visible_test(const Scene *scene, Object *obedit, Image *ima, BMFace *efa) +{ + return uvedit_face_visible_test_ex(scene->toolsettings, obedit, ima, efa); +} + +bool uvedit_face_select_test_ex(const ToolSettings *ts, BMFace *efa, const int cd_loop_uv_offset) +{ + if (ts->uv_flag & UV_SYNC_SELECTION) { + return (BM_elem_flag_test(efa, BM_ELEM_SELECT)); + } + else { + BMLoop *l; + MLoopUV *luv; + BMIter liter; + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (!(luv->flag & MLOOPUV_VERTSEL)) { + return false; + } + } + + return true; + } +} +bool uvedit_face_select_test(const Scene *scene, BMFace *efa, const int cd_loop_uv_offset) +{ + return uvedit_face_select_test_ex(scene->toolsettings, efa, cd_loop_uv_offset); +} + +bool uvedit_face_select_set(const struct Scene *scene, + struct BMEditMesh *em, + struct BMFace *efa, + const bool select, + const bool do_history, + const int cd_loop_uv_offset) +{ + if (select) { + return uvedit_face_select_enable(scene, em, efa, do_history, cd_loop_uv_offset); + } + else { + return uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset); + } +} + +bool uvedit_face_select_enable(const Scene *scene, + BMEditMesh *em, + BMFace *efa, + const bool do_history, + const int cd_loop_uv_offset) +{ + const ToolSettings *ts = scene->toolsettings; + + if (ts->uv_flag & UV_SYNC_SELECTION) { + BM_face_select_set(em->bm, efa, true); + if (do_history) { + BM_select_history_store(em->bm, (BMElem *)efa); + } + } + else { + BMLoop *l; + MLoopUV *luv; + BMIter liter; + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + luv->flag |= MLOOPUV_VERTSEL; + } + + return true; + } + + return false; +} + +bool uvedit_face_select_disable(const Scene *scene, + BMEditMesh *em, + BMFace *efa, + const int cd_loop_uv_offset) +{ + const ToolSettings *ts = scene->toolsettings; + + if (ts->uv_flag & UV_SYNC_SELECTION) { + BM_face_select_set(em->bm, efa, false); + } + else { + BMLoop *l; + MLoopUV *luv; + BMIter liter; + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + luv->flag &= ~MLOOPUV_VERTSEL; + } + + return true; + } + + return false; +} + +bool uvedit_edge_select_test_ex(const ToolSettings *ts, BMLoop *l, const int cd_loop_uv_offset) +{ + if (ts->uv_flag & UV_SYNC_SELECTION) { + if (ts->selectmode & SCE_SELECT_FACE) { + return BM_elem_flag_test(l->f, BM_ELEM_SELECT); + } + else if (ts->selectmode == SCE_SELECT_EDGE) { + return BM_elem_flag_test(l->e, BM_ELEM_SELECT); + } + else { + return BM_elem_flag_test(l->v, BM_ELEM_SELECT) && + BM_elem_flag_test(l->next->v, BM_ELEM_SELECT); + } + } + else { + MLoopUV *luv1, *luv2; + + luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); + + return (luv1->flag & MLOOPUV_VERTSEL) && (luv2->flag & MLOOPUV_VERTSEL); + } +} +bool uvedit_edge_select_test(const Scene *scene, BMLoop *l, const int cd_loop_uv_offset) +{ + return uvedit_edge_select_test_ex(scene->toolsettings, l, cd_loop_uv_offset); +} + +void uvedit_edge_select_set(BMEditMesh *em, + const Scene *scene, + BMLoop *l, + const bool select, + const bool do_history, + const int cd_loop_uv_offset) + +{ + if (select) { + uvedit_edge_select_enable(em, scene, l, do_history, cd_loop_uv_offset); + } + else { + uvedit_edge_select_disable(em, scene, l, cd_loop_uv_offset); + } +} + +void uvedit_edge_select_enable(BMEditMesh *em, + const Scene *scene, + BMLoop *l, + const bool do_history, + const int cd_loop_uv_offset) + +{ + const ToolSettings *ts = scene->toolsettings; + + if (ts->uv_flag & UV_SYNC_SELECTION) { + if (ts->selectmode & SCE_SELECT_FACE) { + BM_face_select_set(em->bm, l->f, true); + } + else if (ts->selectmode & SCE_SELECT_EDGE) { + BM_edge_select_set(em->bm, l->e, true); + } + else { + BM_vert_select_set(em->bm, l->e->v1, true); + BM_vert_select_set(em->bm, l->e->v2, true); + } + + if (do_history) { + BM_select_history_store(em->bm, (BMElem *)l->e); + } + } + else { + MLoopUV *luv1, *luv2; + + luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); + + luv1->flag |= MLOOPUV_VERTSEL; + luv2->flag |= MLOOPUV_VERTSEL; + } +} + +void uvedit_edge_select_disable(BMEditMesh *em, + const Scene *scene, + BMLoop *l, + const int cd_loop_uv_offset) + +{ + const ToolSettings *ts = scene->toolsettings; + + if (ts->uv_flag & UV_SYNC_SELECTION) { + if (ts->selectmode & SCE_SELECT_FACE) { + BM_face_select_set(em->bm, l->f, false); + } + else if (ts->selectmode & SCE_SELECT_EDGE) { + BM_edge_select_set(em->bm, l->e, false); + } + else { + BM_vert_select_set(em->bm, l->e->v1, false); + BM_vert_select_set(em->bm, l->e->v2, false); + } + } + else { + MLoopUV *luv1, *luv2; + + luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); + + luv1->flag &= ~MLOOPUV_VERTSEL; + luv2->flag &= ~MLOOPUV_VERTSEL; + } +} + +bool uvedit_uv_select_test_ex(const ToolSettings *ts, BMLoop *l, const int cd_loop_uv_offset) +{ + if (ts->uv_flag & UV_SYNC_SELECTION) { + if (ts->selectmode & SCE_SELECT_FACE) { + return BM_elem_flag_test_bool(l->f, BM_ELEM_SELECT); + } + else { + return BM_elem_flag_test_bool(l->v, BM_ELEM_SELECT); + } + } + else { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + return (luv->flag & MLOOPUV_VERTSEL) != 0; + } +} +bool uvedit_uv_select_test(const Scene *scene, BMLoop *l, const int cd_loop_uv_offset) +{ + return uvedit_uv_select_test_ex(scene->toolsettings, l, cd_loop_uv_offset); +} + +void uvedit_uv_select_set(BMEditMesh *em, + const Scene *scene, + BMLoop *l, + const bool select, + const bool do_history, + const int cd_loop_uv_offset) +{ + if (select) { + uvedit_uv_select_enable(em, scene, l, do_history, cd_loop_uv_offset); + } + else { + uvedit_uv_select_disable(em, scene, l, cd_loop_uv_offset); + } +} + +void uvedit_uv_select_enable(BMEditMesh *em, + const Scene *scene, + BMLoop *l, + const bool do_history, + const int cd_loop_uv_offset) +{ + const ToolSettings *ts = scene->toolsettings; + + if (ts->uv_flag & UV_SYNC_SELECTION) { + if (ts->selectmode & SCE_SELECT_FACE) { + BM_face_select_set(em->bm, l->f, true); + } + else { + BM_vert_select_set(em->bm, l->v, true); + } + + if (do_history) { + BM_select_history_remove(em->bm, (BMElem *)l->v); + } + } + else { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + luv->flag |= MLOOPUV_VERTSEL; + } +} + +void uvedit_uv_select_disable(BMEditMesh *em, + const Scene *scene, + BMLoop *l, + const int cd_loop_uv_offset) +{ + const ToolSettings *ts = scene->toolsettings; + + if (ts->uv_flag & UV_SYNC_SELECTION) { + if (ts->selectmode & SCE_SELECT_FACE) { + BM_face_select_set(em->bm, l->f, false); + } + else { + BM_vert_select_set(em->bm, l->v, false); + } + } + else { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + luv->flag &= ~MLOOPUV_VERTSEL; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Find Nearest Elements + * \{ */ + +bool uv_find_nearest_edge( + Scene *scene, Image *ima, Object *obedit, const float co[2], UvNearestHit *hit) +{ + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + MLoopUV *luv, *luv_next; + int i; + bool found = false; + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + BM_mesh_elem_index_ensure(em->bm, BM_VERT); + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { + continue; + } + BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); + + const float dist_test_sq = dist_squared_to_line_segment_v2(co, luv->uv, luv_next->uv); + + if (dist_test_sq < hit->dist_sq) { + hit->efa = efa; + + hit->l = l; + hit->luv = luv; + hit->luv_next = luv_next; + hit->lindex = i; + + hit->dist_sq = dist_test_sq; + found = true; + } + } + } + return found; +} + +bool uv_find_nearest_edge_multi(Scene *scene, + Image *ima, + Object **objects, + const uint objects_len, + const float co[2], + UvNearestHit *hit_final) +{ + bool found = false; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + if (uv_find_nearest_edge(scene, ima, obedit, co, hit_final)) { + hit_final->ob = obedit; + found = true; + } + } + return found; +} + +bool uv_find_nearest_face( + Scene *scene, Image *ima, Object *obedit, const float co[2], UvNearestHit *hit_final) +{ + BMEditMesh *em = BKE_editmesh_from_object(obedit); + bool found = false; + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + /* this will fill in hit.vert1 and hit.vert2 */ + float dist_sq_init = hit_final->dist_sq; + UvNearestHit hit = *hit_final; + if (uv_find_nearest_edge(scene, ima, obedit, co, &hit)) { + hit.dist_sq = dist_sq_init; + hit.l = NULL; + hit.luv = hit.luv_next = NULL; + + BMIter iter; + BMFace *efa; + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { + continue; + } + + float cent[2]; + uv_poly_center(efa, cent, cd_loop_uv_offset); + + const float dist_test_sq = len_squared_v2v2(co, cent); + + if (dist_test_sq < hit.dist_sq) { + hit.efa = efa; + hit.dist_sq = dist_test_sq; + found = true; + } + } + } + if (found) { + *hit_final = hit; + } + return found; +} + +bool uv_find_nearest_face_multi(Scene *scene, + Image *ima, + Object **objects, + const uint objects_len, + const float co[2], + UvNearestHit *hit_final) +{ + bool found = false; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + if (uv_find_nearest_face(scene, ima, obedit, co, hit_final)) { + hit_final->ob = obedit; + found = true; + } + } + return found; +} + +static bool uv_nearest_between(const BMLoop *l, const float co[2], const int cd_loop_uv_offset) +{ + const float *uv_prev = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset))->uv; + const float *uv_curr = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset))->uv; + const float *uv_next = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset))->uv; + + return ((line_point_side_v2(uv_prev, uv_curr, co) > 0.0f) && + (line_point_side_v2(uv_next, uv_curr, co) <= 0.0f)); +} + +bool uv_find_nearest_vert(Scene *scene, + Image *ima, + Object *obedit, + float const co[2], + const float penalty_dist, + UvNearestHit *hit_final) +{ + bool found = false; + + /* this will fill in hit.vert1 and hit.vert2 */ + float dist_sq_init = hit_final->dist_sq; + UvNearestHit hit = *hit_final; + if (uv_find_nearest_edge(scene, ima, obedit, co, &hit)) { + hit.dist_sq = dist_sq_init; + + hit.l = NULL; + hit.luv = hit.luv_next = NULL; + + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMFace *efa; + BMIter iter; + + BM_mesh_elem_index_ensure(em->bm, BM_VERT); + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { + continue; + } + + BMIter liter; + BMLoop *l; + int i; + BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { + float dist_test_sq; + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (penalty_dist != 0.0f && uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + dist_test_sq = len_v2v2(co, luv->uv) + penalty_dist; + dist_test_sq = square_f(dist_test_sq); + } + else { + dist_test_sq = len_squared_v2v2(co, luv->uv); + } + + if (dist_test_sq <= hit.dist_sq) { + if (dist_test_sq == hit.dist_sq) { + if (!uv_nearest_between(l, co, cd_loop_uv_offset)) { + continue; + } + } + + hit.dist_sq = dist_test_sq; + + hit.l = l; + hit.luv = luv; + hit.luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); + hit.efa = efa; + hit.lindex = i; + found = true; + } + } + } + } + + if (found) { + *hit_final = hit; + } + + return found; +} + +bool uv_find_nearest_vert_multi(Scene *scene, + Image *ima, + Object **objects, + const uint objects_len, + float const co[2], + const float penalty_dist, + UvNearestHit *hit_final) +{ + bool found = false; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + if (uv_find_nearest_vert(scene, ima, obedit, co, penalty_dist, hit_final)) { + hit_final->ob = obedit; + found = true; + } + } + return found; +} + +bool ED_uvedit_nearest_uv(const Scene *scene, + Object *obedit, + Image *ima, + const float co[2], + float *dist_sq, + float r_uv[2]) +{ + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMIter iter; + BMFace *efa; + const float *uv_best = NULL; + float dist_best = *dist_sq; + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { + continue; + } + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + const float *uv = ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset))->uv; + const float dist_test = len_squared_v2v2(co, uv); + if (dist_best > dist_test) { + dist_best = dist_test; + uv_best = uv; + } + } while ((l_iter = l_iter->next) != l_first); + } + + if (uv_best != NULL) { + copy_v2_v2(r_uv, uv_best); + *dist_sq = dist_best; + return true; + } + else { + return false; + } +} + +bool ED_uvedit_nearest_uv_multi(const Scene *scene, + Image *ima, + Object **objects, + const uint objects_len, + const float co[2], + float *dist_sq, + float r_uv[2]) +{ + bool found = false; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + if (ED_uvedit_nearest_uv(scene, obedit, ima, co, dist_sq, r_uv)) { + found = true; + } + } + return found; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Loop Select + * \{ */ + +static void uv_select_edgeloop_vertex_loop_flag(UvMapVert *first) +{ + UvMapVert *iterv; + int count = 0; + + for (iterv = first; iterv; iterv = iterv->next) { + if (iterv->separate && iterv != first) { + break; + } + + count++; + } + + if (count < 5) { + first->flag = 1; + } +} + +static UvMapVert *uv_select_edgeloop_vertex_map_get(UvVertMap *vmap, BMFace *efa, BMLoop *l) +{ + UvMapVert *iterv, *first; + first = BM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v)); + + for (iterv = first; iterv; iterv = iterv->next) { + if (iterv->separate) { + first = iterv; + } + if (iterv->poly_index == BM_elem_index_get(efa)) { + return first; + } + } + + return NULL; +} + +static bool uv_select_edgeloop_edge_tag_faces(BMEditMesh *em, + UvMapVert *first1, + UvMapVert *first2, + int *totface) +{ + UvMapVert *iterv1, *iterv2; + BMFace *efa; + int tot = 0; + + /* count number of faces this edge has */ + for (iterv1 = first1; iterv1; iterv1 = iterv1->next) { + if (iterv1->separate && iterv1 != first1) { + break; + } + + for (iterv2 = first2; iterv2; iterv2 = iterv2->next) { + if (iterv2->separate && iterv2 != first2) { + break; + } + + if (iterv1->poly_index == iterv2->poly_index) { + /* if face already tagged, don't do this edge */ + efa = BM_face_at_index(em->bm, iterv1->poly_index); + if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { + return false; + } + + tot++; + break; + } + } + } + + if (*totface == 0) { /* start edge */ + *totface = tot; + } + else if (tot != *totface) { /* check for same number of faces as start edge */ + return false; + } + + /* tag the faces */ + for (iterv1 = first1; iterv1; iterv1 = iterv1->next) { + if (iterv1->separate && iterv1 != first1) { + break; + } + + for (iterv2 = first2; iterv2; iterv2 = iterv2->next) { + if (iterv2->separate && iterv2 != first2) { + break; + } + + if (iterv1->poly_index == iterv2->poly_index) { + efa = BM_face_at_index(em->bm, iterv1->poly_index); + BM_elem_flag_enable(efa, BM_ELEM_TAG); + break; + } + } + } + + return true; +} + +static int uv_select_edgeloop(Scene *scene, + Image *ima, + Object *obedit, + UvNearestHit *hit, + const float limit[2], + const bool extend) +{ + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMFace *efa; + BMIter iter, liter; + BMLoop *l; + UvVertMap *vmap; + UvMapVert *iterv_curr; + UvMapVert *iterv_next; + int starttotf; + bool looking, select; + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + /* setup */ + BM_mesh_elem_table_ensure(em->bm, BM_FACE); + vmap = BM_uv_vert_map_create(em->bm, limit, false, false); + + BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE); + + if (!extend) { + uv_select_all_perform(scene, ima, obedit, SEL_DESELECT); + } + + BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); + + /* set flags for first face and verts */ + iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l); + iterv_next = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l->next); + uv_select_edgeloop_vertex_loop_flag(iterv_curr); + uv_select_edgeloop_vertex_loop_flag(iterv_next); + + starttotf = 0; + uv_select_edgeloop_edge_tag_faces(em, iterv_curr, iterv_next, &starttotf); + + /* sorry, first edge isn't even ok */ + looking = !(iterv_curr->flag == 0 && iterv_next->flag == 0); + + /* iterate */ + while (looking) { + looking = false; + + /* find correct valence edges which are not tagged yet, but connect to tagged one */ + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!BM_elem_flag_test(efa, BM_ELEM_TAG) && + uvedit_face_visible_test(scene, obedit, ima, efa)) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + /* check face not hidden and not tagged */ + if (!(iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, efa, l))) { + continue; + } + if (!(iterv_next = uv_select_edgeloop_vertex_map_get(vmap, efa, l->next))) { + continue; + } + + /* check if vertex is tagged and has right valence */ + if (iterv_curr->flag || iterv_next->flag) { + if (uv_select_edgeloop_edge_tag_faces(em, iterv_curr, iterv_next, &starttotf)) { + looking = true; + BM_elem_flag_enable(efa, BM_ELEM_TAG); + + uv_select_edgeloop_vertex_loop_flag(iterv_curr); + uv_select_edgeloop_vertex_loop_flag(iterv_next); + break; + } + } + } + } + } + } + + /* do the actual select/deselect */ + iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l); + iterv_next = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l->next); + iterv_curr->flag = 1; + iterv_next->flag = 1; + + if (extend) { + select = !(uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset)); + } + else { + select = true; + } + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, efa, l); + + if (iterv_curr->flag) { + uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); + } + } + } + + /* cleanup */ + BM_uv_vert_map_free(vmap); + + return (select) ? 1 : -1; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Linked + * \{ */ + +static void uv_select_linked_multi(Scene *scene, + Image *ima, + Object **objects, + const uint objects_len, + const float limit[2], + UvNearestHit *hit_final, + bool extend, + bool deselect, + bool toggle, + bool select_faces) +{ + /* loop over objects, or just use hit_final->ob */ + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + if (hit_final && ob_index != 0) { + break; + } + Object *obedit = hit_final ? hit_final->ob : objects[ob_index]; + + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + MLoopUV *luv; + UvVertMap *vmap; + UvMapVert *vlist, *iterv, *startv; + int i, stacksize = 0, *stack; + uint a; + char *flag; + + BMEditMesh *em = BKE_editmesh_from_object(obedit); + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + BM_mesh_elem_table_ensure(em->bm, BM_FACE); /* we can use this too */ + + /* Note, we had 'use winding' so we don't consider overlapping islands as connected, see T44320 + * this made *every* projection split the island into front/back islands. + * Keep 'use_winding' to false, see: T50970. + * + * Better solve this by having a delimit option for select-linked operator, + * keeping island-select working as is. */ + vmap = BM_uv_vert_map_create(em->bm, limit, !select_faces, false); + + if (vmap == NULL) { + continue; + } + + stack = MEM_mallocN(sizeof(*stack) * (em->bm->totface + 1), "UvLinkStack"); + flag = MEM_callocN(sizeof(*flag) * em->bm->totface, "UvLinkFlag"); + + if (hit_final == NULL) { + /* Use existing selection */ + BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) { + if (uvedit_face_visible_test(scene, obedit, ima, efa)) { + if (select_faces) { + if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + stack[stacksize] = a; + stacksize++; + flag[a] = 1; + } + } + else { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + + if (luv->flag & MLOOPUV_VERTSEL) { + stack[stacksize] = a; + stacksize++; + flag[a] = 1; + + break; + } + } + } + } + } + } + else { + BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) { + if (efa == hit_final->efa) { + stack[stacksize] = a; + stacksize++; + flag[a] = 1; + break; + } + } + } + + while (stacksize > 0) { + + stacksize--; + a = stack[stacksize]; + + efa = BM_face_at_index(em->bm, a); + + BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { + + /* make_uv_vert_map_EM sets verts tmp.l to the indices */ + vlist = BM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v)); + + startv = vlist; + + for (iterv = vlist; iterv; iterv = iterv->next) { + if (iterv->separate) { + startv = iterv; + } + if (iterv->poly_index == a) { + break; + } + } + + for (iterv = startv; iterv; iterv = iterv->next) { + if ((startv != iterv) && (iterv->separate)) { + break; + } + else if (!flag[iterv->poly_index]) { + flag[iterv->poly_index] = 1; + stack[stacksize] = iterv->poly_index; + stacksize++; + } + } + } + } + + /* Toggling - if any of the linked vertices is selected (and visible), we deselect. */ + if ((toggle == true) && (extend == false) && (deselect == false)) { + BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) { + bool found_selected = false; + if (!flag[a]) { + continue; + } + + if (select_faces) { + if (BM_elem_flag_test(efa, BM_ELEM_SELECT) && !BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + found_selected = true; + } + } + else { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + + if (luv->flag & MLOOPUV_VERTSEL) { + found_selected = true; + } + } + + if (found_selected) { + deselect = true; + break; + } + } + } + } + +#define SET_SELECTION(value) \ + if (select_faces) { \ + BM_face_select_set(em->bm, efa, value); \ + } \ + else { \ + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { \ + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); \ + luv->flag = (value) ? (luv->flag | MLOOPUV_VERTSEL) : (luv->flag & ~MLOOPUV_VERTSEL); \ + } \ + } \ + (void)0 + + BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) { + if (!flag[a]) { + if (!extend && !deselect && !toggle) { + SET_SELECTION(false); + } + continue; + } + + if (!deselect) { + SET_SELECTION(true); + } + else { + SET_SELECTION(false); + } + } + +#undef SET_SELECTION + + MEM_freeN(stack); + MEM_freeN(flag); + BM_uv_vert_map_free(vmap); + } +} + +/** + * \warning This returns first selected UV, + * not ideal in many cases since there could be multiple. + */ +const float *uvedit_first_selected_uv_from_vertex( + Scene *scene, Object *obedit, Image *ima, BMVert *eve, const int cd_loop_uv_offset) +{ + BMIter liter; + BMLoop *l; + + BM_ITER_ELEM (l, &liter, eve, BM_LOOPS_OF_VERT) { + if (!uvedit_face_visible_test(scene, obedit, ima, l->f)) { + continue; + } + + if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + return luv->uv; + } + } + + return NULL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select More/Less Operator + * \{ */ + +static int uv_select_more_less(bContext *C, const bool select) +{ + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Image *ima = CTX_data_edit_image(C); + SpaceImage *sima = CTX_wm_space_image(C); + + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + const ToolSettings *ts = scene->toolsettings; + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, ((View3D *)NULL), &objects_len); + + const bool is_uv_face_selectmode = (ts->uv_selectmode == UV_SELECT_FACE); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + bool changed = false; + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + if (ts->uv_flag & UV_SYNC_SELECTION) { + if (select) { + EDBM_select_more(em, true); + } + else { + EDBM_select_less(em, true); + } + + DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + continue; + } + + if (is_uv_face_selectmode) { + + /* clear tags */ + BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); + + /* mark loops to be selected */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (uvedit_face_visible_test(scene, obedit, ima, efa)) { + +#define IS_SEL 1 +#define IS_UNSEL 2 + + int sel_state = 0; + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (luv->flag & MLOOPUV_VERTSEL) { + sel_state |= IS_SEL; + } + else { + sel_state |= IS_UNSEL; + } + + /* if we have a mixed selection, tag to grow it */ + if (sel_state == (IS_SEL | IS_UNSEL)) { + BM_elem_flag_enable(efa, BM_ELEM_TAG); + changed = true; + break; + } + } + +#undef IS_SEL +#undef IS_UNSEL + } + } + } + else { + + /* clear tags */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + BM_elem_flag_disable(l, BM_ELEM_TAG); + } + } + + /* mark loops to be selected */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (uvedit_face_visible_test(scene, obedit, ima, efa)) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + + if (((luv->flag & MLOOPUV_VERTSEL) != 0) == select) { + BM_elem_flag_enable(l->next, BM_ELEM_TAG); + BM_elem_flag_enable(l->prev, BM_ELEM_TAG); + changed = true; + } + } + } + } + } + + if (changed) { + if (is_uv_face_selectmode) { + /* Select tagged faces. */ + uv_select_flush_from_tag_face(sima, scene, obedit, select); + } + else { + /* Select tagged loops. */ + uv_select_flush_from_tag_loop(sima, scene, obedit, select); + } + DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + } + } + MEM_freeN(objects); + + return OPERATOR_FINISHED; +} + +static int uv_select_more_exec(bContext *C, wmOperator *UNUSED(op)) +{ + return uv_select_more_less(C, true); +} + +void UV_OT_select_more(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select More"; + ot->description = "Select more UV vertices connected to initial selection"; + ot->idname = "UV_OT_select_more"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = uv_select_more_exec; + ot->poll = ED_operator_uvedit_space_image; +} + +static int uv_select_less_exec(bContext *C, wmOperator *UNUSED(op)) +{ + return uv_select_more_less(C, false); +} + +void UV_OT_select_less(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Less"; + ot->description = "Deselect UV vertices at the boundary of each selection region"; + ot->idname = "UV_OT_select_less"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = uv_select_less_exec; + ot->poll = ED_operator_uvedit_space_image; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name (De)Select All Operator + * \{ */ + +bool uvedit_select_is_any_selected(Scene *scene, Image *ima, Object *obedit) +{ + const ToolSettings *ts = scene->toolsettings; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + MLoopUV *luv; + + if (ts->uv_flag & UV_SYNC_SELECTION) { + return (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel); + } + else { + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { + continue; + } + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (luv->flag & MLOOPUV_VERTSEL) { + return true; + } + } + } + } + return false; +} + +bool uvedit_select_is_any_selected_multi(Scene *scene, + Image *ima, + Object **objects, + const uint objects_len) +{ + bool found = false; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + if (uvedit_select_is_any_selected(scene, ima, obedit)) { + found = true; + break; + } + } + return found; +} + +static void uv_select_all_perform(Scene *scene, Image *ima, Object *obedit, int action) +{ + const ToolSettings *ts = scene->toolsettings; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + MLoopUV *luv; + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + if (action == SEL_TOGGLE) { + action = uvedit_select_is_any_selected(scene, ima, obedit) ? SEL_DESELECT : SEL_SELECT; + } + + if (ts->uv_flag & UV_SYNC_SELECTION) { + switch (action) { + case SEL_TOGGLE: + EDBM_select_toggle_all(em); + break; + case SEL_SELECT: + EDBM_flag_enable_all(em, BM_ELEM_SELECT); + break; + case SEL_DESELECT: + EDBM_flag_disable_all(em, BM_ELEM_SELECT); + break; + case SEL_INVERT: + EDBM_select_swap(em); + EDBM_selectmode_flush(em); + break; + } + } + else { + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { + continue; + } + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + + switch (action) { + case SEL_SELECT: + luv->flag |= MLOOPUV_VERTSEL; + break; + case SEL_DESELECT: + luv->flag &= ~MLOOPUV_VERTSEL; + break; + case SEL_INVERT: + luv->flag ^= MLOOPUV_VERTSEL; + break; + } + } + } + } +} + +static void uv_select_all_perform_multi( + Scene *scene, Image *ima, Object **objects, const uint objects_len, int action) +{ + if (action == SEL_TOGGLE) { + action = uvedit_select_is_any_selected_multi(scene, ima, objects, objects_len) ? SEL_DESELECT : + SEL_SELECT; + } + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + uv_select_all_perform(scene, ima, obedit, action); + } +} + +static int uv_select_all_exec(bContext *C, wmOperator *op) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + const ToolSettings *ts = scene->toolsettings; + Image *ima = CTX_data_edit_image(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + int action = RNA_enum_get(op->ptr, "action"); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, ((View3D *)NULL), &objects_len); + + uv_select_all_perform_multi(scene, ima, objects, objects_len, action); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + uv_select_tag_update_for_object(depsgraph, ts, obedit); + } + + MEM_freeN(objects); + + return OPERATOR_FINISHED; +} + +void UV_OT_select_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "(De)select All"; + ot->description = "Change selection of all UV vertices"; + ot->idname = "UV_OT_select_all"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = uv_select_all_exec; + ot->poll = ED_operator_uvedit; + + WM_operator_properties_select_all(ot); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mouse Select Operator + * \{ */ + +static bool uv_sticky_select( + float *limit, int hitv[], int v, float *hituv[], float *uv, int sticky, int hitlen) +{ + int i; + + /* this function test if some vertex needs to selected + * in addition to the existing ones due to sticky select */ + if (sticky == SI_STICKY_DISABLE) { + return false; + } + + for (i = 0; i < hitlen; i++) { + if (hitv[i] == v) { + if (sticky == SI_STICKY_LOC) { + if (fabsf(hituv[i][0] - uv[0]) < limit[0] && fabsf(hituv[i][1] - uv[1]) < limit[1]) { + return true; + } + } + else if (sticky == SI_STICKY_VERTEX) { + return true; + } + } + } + + return false; +} + +static int uv_mouse_select_multi(bContext *C, + Object **objects, + uint objects_len, + const float co[2], + const bool extend, + const bool deselect_all, + const bool loop) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + SpaceImage *sima = CTX_wm_space_image(C); + Scene *scene = CTX_data_scene(C); + const ToolSettings *ts = scene->toolsettings; + Image *ima = CTX_data_edit_image(C); + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + MLoopUV *luv; + UvNearestHit hit = UV_NEAREST_HIT_INIT; + int i, selectmode, sticky, sync, *hitv = NULL; + bool select = true; + bool found_item = false; + /* 0 == don't flush, 1 == sel, -1 == desel; only use when selection sync is enabled */ + int flush = 0; + int hitlen = 0; + float limit[2], **hituv = NULL; + + /* notice 'limit' is the same no matter the zoom level, since this is like + * remove doubles and could annoying if it joined points when zoomed out. + * 'penalty' is in screen pixel space otherwise zooming in on a uv-vert and + * shift-selecting can consider an adjacent point close enough to add to + * the selection rather than de-selecting the closest. */ + + float penalty_dist; + { + float penalty[2]; + uvedit_pixel_to_float(sima, 0.05f, limit); + uvedit_pixel_to_float(sima, 5.0f / (sima ? sima->zoom : 1.0f), penalty); + penalty_dist = len_v2(penalty); + } + + /* retrieve operation mode */ + if (ts->uv_flag & UV_SYNC_SELECTION) { + sync = 1; + + if (ts->selectmode & SCE_SELECT_FACE) { + selectmode = UV_SELECT_FACE; + } + else if (ts->selectmode & SCE_SELECT_EDGE) { + selectmode = UV_SELECT_EDGE; + } + else { + selectmode = UV_SELECT_VERTEX; + } + + sticky = SI_STICKY_DISABLE; + } + else { + sync = 0; + selectmode = ts->uv_selectmode; + sticky = (sima) ? sima->sticky : 1; + } + + /* find nearest element */ + if (loop) { + /* find edge */ + found_item = uv_find_nearest_edge_multi(scene, ima, objects, objects_len, co, &hit); + } + else if (selectmode == UV_SELECT_VERTEX) { + /* find vertex */ + found_item = uv_find_nearest_vert_multi( + scene, ima, objects, objects_len, co, penalty_dist, &hit); + found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist); + + if (found_item) { + /* mark 1 vertex as being hit */ + hitv = BLI_array_alloca(hitv, hit.efa->len); + hituv = BLI_array_alloca(hituv, hit.efa->len); + copy_vn_i(hitv, hit.efa->len, 0xFFFFFFFF); + + hitv[hit.lindex] = BM_elem_index_get(hit.l->v); + hituv[hit.lindex] = hit.luv->uv; + + hitlen = hit.efa->len; + } + } + else if (selectmode == UV_SELECT_EDGE) { + /* find edge */ + found_item = uv_find_nearest_edge_multi(scene, ima, objects, objects_len, co, &hit); + found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist); + + if (found_item) { + /* mark 2 edge vertices as being hit */ + hitv = BLI_array_alloca(hitv, hit.efa->len); + hituv = BLI_array_alloca(hituv, hit.efa->len); + copy_vn_i(hitv, hit.efa->len, 0xFFFFFFFF); + + hitv[hit.lindex] = BM_elem_index_get(hit.l->v); + hitv[(hit.lindex + 1) % hit.efa->len] = BM_elem_index_get(hit.l->next->v); + hituv[hit.lindex] = hit.luv->uv; + hituv[(hit.lindex + 1) % hit.efa->len] = hit.luv_next->uv; + + hitlen = hit.efa->len; + } + } + else if (selectmode == UV_SELECT_FACE) { + /* find face */ + found_item = uv_find_nearest_face_multi(scene, ima, objects, objects_len, co, &hit); + found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist); + + if (found_item) { + BMEditMesh *em = BKE_editmesh_from_object(hit.ob); + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + /* make active */ + BM_mesh_active_face_set(em->bm, hit.efa); + + /* mark all face vertices as being hit */ + + hitv = BLI_array_alloca(hitv, hit.efa->len); + hituv = BLI_array_alloca(hituv, hit.efa->len); + BM_ITER_ELEM_INDEX (l, &liter, hit.efa, BM_LOOPS_OF_FACE, i) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + hituv[i] = luv->uv; + hitv[i] = BM_elem_index_get(l->v); + } + + hitlen = hit.efa->len; + } + } + else if (selectmode == UV_SELECT_ISLAND) { + found_item = uv_find_nearest_edge_multi(scene, ima, objects, objects_len, co, &hit); + found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist); + } + + if (!found_item) { + if (deselect_all) { + uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + uv_select_tag_update_for_object(depsgraph, ts, obedit); + } + + return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; + } + + Object *obedit = hit.ob; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + /* do selection */ + if (loop) { + if (!extend) { + /* TODO(MULTI_EDIT): We only need to de-select non-active */ + uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); + } + flush = uv_select_edgeloop(scene, ima, obedit, &hit, limit, extend); + } + else if (selectmode == UV_SELECT_ISLAND) { + if (!extend) { + /* TODO(MULTI_EDIT): We only need to de-select non-active */ + uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); + } + /* Current behavior of 'extend' + * is actually toggling, so pass extend flag as 'toggle' here */ + uv_select_linked_multi( + scene, ima, objects, objects_len, limit, &hit, false, false, extend, false); + } + else if (extend) { + if (selectmode == UV_SELECT_VERTEX) { + /* (de)select uv vertex */ + select = !uvedit_uv_select_test(scene, hit.l, cd_loop_uv_offset); + uvedit_uv_select_set(em, scene, hit.l, select, true, cd_loop_uv_offset); + flush = 1; + } + else if (selectmode == UV_SELECT_EDGE) { + /* (de)select edge */ + select = !(uvedit_edge_select_test(scene, hit.l, cd_loop_uv_offset)); + uvedit_edge_select_set(em, scene, hit.l, select, true, cd_loop_uv_offset); + flush = 1; + } + else if (selectmode == UV_SELECT_FACE) { + /* (de)select face */ + select = !(uvedit_face_select_test(scene, hit.efa, cd_loop_uv_offset)); + uvedit_face_select_set(scene, em, hit.efa, select, true, cd_loop_uv_offset); + flush = -1; + } + + /* de-selecting an edge may deselect a face too - validate */ + if (sync) { + if (select == false) { + BM_select_history_validate(em->bm); + } + } + + /* (de)select sticky uv nodes */ + if (sticky != SI_STICKY_DISABLE) { + + BM_mesh_elem_index_ensure(em->bm, BM_VERT); + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { + continue; + } + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (uv_sticky_select( + limit, hitv, BM_elem_index_get(l->v), hituv, luv->uv, sticky, hitlen)) { + uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); + } + } + } + + flush = select ? 1 : -1; + } + } + else { + /* deselect all */ + uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); + + if (selectmode == UV_SELECT_VERTEX) { + /* select vertex */ + uvedit_uv_select_enable(em, scene, hit.l, true, cd_loop_uv_offset); + flush = 1; + } + else if (selectmode == UV_SELECT_EDGE) { + /* select edge */ + uvedit_edge_select_enable(em, scene, hit.l, true, cd_loop_uv_offset); + flush = 1; + } + else if (selectmode == UV_SELECT_FACE) { + /* select face */ + uvedit_face_select_enable(scene, em, hit.efa, true, cd_loop_uv_offset); + } + + /* select sticky uvs */ + if (sticky != SI_STICKY_DISABLE) { + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { + continue; + } + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (sticky == SI_STICKY_DISABLE) { + continue; + } + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + + if (uv_sticky_select( + limit, hitv, BM_elem_index_get(l->v), hituv, luv->uv, sticky, hitlen)) { + uvedit_uv_select_enable(em, scene, l, false, cd_loop_uv_offset); + } + + flush = 1; + } + } + } + } + + if (sync) { + /* flush for mesh selection */ + + /* before bmesh */ +#if 0 + if (ts->selectmode != SCE_SELECT_FACE) { + if (flush == 1) { + EDBM_select_flush(em); + } + else if (flush == -1) { + EDBM_deselect_flush(em); + } + } +#else + if (flush != 0) { + if (loop) { + /* push vertex -> edge selection */ + if (select) { + EDBM_select_flush(em); + } + else { + EDBM_deselect_flush(em); + } + } + else { + EDBM_selectmode_flush(em); + } + } +#endif + } + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obiter = objects[ob_index]; + uv_select_tag_update_for_object(depsgraph, ts, obiter); + } + + return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; +} +static int uv_mouse_select( + bContext *C, const float co[2], const bool extend, const bool deselect_all, const bool loop) +{ + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, ((View3D *)NULL), &objects_len); + int ret = uv_mouse_select_multi(C, objects, objects_len, co, extend, deselect_all, loop); + MEM_freeN(objects); + return ret; +} + +static int uv_select_exec(bContext *C, wmOperator *op) +{ + float co[2]; + + RNA_float_get_array(op->ptr, "location", co); + const bool extend = RNA_boolean_get(op->ptr, "extend"); + const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); + const bool loop = false; + + return uv_mouse_select(C, co, extend, deselect_all, loop); +} + +static int uv_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + const ARegion *region = CTX_wm_region(C); + float co[2]; + + UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); + RNA_float_set_array(op->ptr, "location", co); + + return uv_select_exec(C, op); +} + +void UV_OT_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select"; + ot->description = "Select UV vertices"; + ot->idname = "UV_OT_select"; + ot->flag = OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = uv_select_exec; + ot->invoke = uv_select_invoke; + ot->poll = ED_operator_uvedit; /* requires space image */ + + /* properties */ + PropertyRNA *prop; + RNA_def_boolean(ot->srna, + "extend", + 0, + "Extend", + "Extend selection rather than clearing the existing selection"); + prop = RNA_def_boolean(ot->srna, + "deselect_all", + false, + "Deselect On Nothing", + "Deselect all when nothing under the cursor"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + RNA_def_float_vector( + ot->srna, + "location", + 2, + NULL, + -FLT_MAX, + FLT_MAX, + "Location", + "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", + -100.0f, + 100.0f); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Loop Select Operator + * \{ */ + +static int uv_select_loop_exec(bContext *C, wmOperator *op) +{ + float co[2]; + + RNA_float_get_array(op->ptr, "location", co); + const bool extend = RNA_boolean_get(op->ptr, "extend"); + const bool deselect_all = false; + const bool loop = true; + + return uv_mouse_select(C, co, extend, deselect_all, loop); +} + +static int uv_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + const ARegion *region = CTX_wm_region(C); + float co[2]; + + UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); + RNA_float_set_array(op->ptr, "location", co); + + return uv_select_loop_exec(C, op); +} + +void UV_OT_select_loop(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Loop Select"; + ot->description = "Select a loop of connected UV vertices"; + ot->idname = "UV_OT_select_loop"; + ot->flag = OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = uv_select_loop_exec; + ot->invoke = uv_select_loop_invoke; + ot->poll = ED_operator_uvedit; /* requires space image */ + + /* properties */ + RNA_def_boolean(ot->srna, + "extend", + 0, + "Extend", + "Extend selection rather than clearing the existing selection"); + RNA_def_float_vector( + ot->srna, + "location", + 2, + NULL, + -FLT_MAX, + FLT_MAX, + "Location", + "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", + -100.0f, + 100.0f); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Linked Operator + * \{ */ + +static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent *event, bool pick) +{ + SpaceImage *sima = CTX_wm_space_image(C); + Scene *scene = CTX_data_scene(C); + const ToolSettings *ts = scene->toolsettings; + ViewLayer *view_layer = CTX_data_view_layer(C); + Image *ima = CTX_data_edit_image(C); + float limit[2]; + bool extend = true; + bool deselect = false; + bool select_faces = (ts->uv_flag & UV_SYNC_SELECTION) && (ts->selectmode & SCE_SELECT_FACE); + + UvNearestHit hit = UV_NEAREST_HIT_INIT; + + if ((ts->uv_flag & UV_SYNC_SELECTION) && !(ts->selectmode & SCE_SELECT_FACE)) { + BKE_report(op->reports, + RPT_ERROR, + "Select linked only works in face select mode when sync selection is enabled"); + return OPERATOR_CANCELLED; + } + + if (pick) { + extend = RNA_boolean_get(op->ptr, "extend"); + deselect = RNA_boolean_get(op->ptr, "deselect"); + } + uv_select_island_limit_default(sima, limit); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, ((View3D *)NULL), &objects_len); + + if (pick) { + float co[2]; + + if (event) { + /* invoke */ + const ARegion *region = CTX_wm_region(C); + + UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); + RNA_float_set_array(op->ptr, "location", co); + } + else { + /* exec */ + RNA_float_get_array(op->ptr, "location", co); + } + + if (!uv_find_nearest_edge_multi(scene, ima, objects, objects_len, co, &hit)) { + MEM_freeN(objects); + return OPERATOR_CANCELLED; + } + } + + if (!extend) { + uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); + } + + uv_select_linked_multi(scene, + ima, + objects, + objects_len, + limit, + pick ? &hit : NULL, + extend, + deselect, + false, + select_faces); + + /* weak!, but works */ + Object **objects_free = objects; + if (pick) { + objects = &hit.ob; + objects_len = 1; + } + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + DEG_id_tag_update(obedit->data, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + } + + MEM_SAFE_FREE(objects_free); + + return OPERATOR_FINISHED; +} + +static int uv_select_linked_exec(bContext *C, wmOperator *op) +{ + return uv_select_linked_internal(C, op, NULL, false); +} + +void UV_OT_select_linked(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Linked"; + ot->description = "Select all UV vertices linked to the active UV map"; + ot->idname = "UV_OT_select_linked"; + + /* api callbacks */ + ot->exec = uv_select_linked_exec; + ot->poll = ED_operator_uvedit; /* requires space image */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Linked (Cursor Pick) Operator + * \{ */ + +static int uv_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + return uv_select_linked_internal(C, op, event, true); +} + +static int uv_select_linked_pick_exec(bContext *C, wmOperator *op) +{ + return uv_select_linked_internal(C, op, NULL, true); +} + +void UV_OT_select_linked_pick(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Linked Pick"; + ot->description = "Select all UV vertices linked under the mouse"; + ot->idname = "UV_OT_select_linked_pick"; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->invoke = uv_select_linked_pick_invoke; + ot->exec = uv_select_linked_pick_exec; + ot->poll = ED_operator_uvedit; /* requires space image */ + + /* properties */ + RNA_def_boolean(ot->srna, + "extend", + 0, + "Extend", + "Extend selection rather than clearing the existing selection"); + RNA_def_boolean(ot->srna, + "deselect", + 0, + "Deselect", + "Deselect linked UV vertices rather than selecting them"); + RNA_def_float_vector( + ot->srna, + "location", + 2, + NULL, + -FLT_MAX, + FLT_MAX, + "Location", + "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds", + -100.0f, + 100.0f); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Split Operator + * \{ */ + +/** + * \note This is based on similar use case to #MESH_OT_split(), which has a similar effect + * but in this case they are not joined to begin with (only having the behavior of being joined) + * so its best to call this #uv_select_split() instead of just split(), but assigned to the same + * key as #MESH_OT_split - Campbell. + */ +static int uv_select_split_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + const ToolSettings *ts = scene->toolsettings; + Image *ima = CTX_data_edit_image(C); + + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + MLoopUV *luv; + + if (ts->uv_flag & UV_SYNC_SELECTION) { + BKE_report(op->reports, RPT_ERROR, "Cannot split selection when sync selection is enabled"); + return OPERATOR_CANCELLED; + } + + bool changed_multi = false; + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, ((View3D *)NULL), &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMesh *bm = BKE_editmesh_from_object(obedit)->bm; + + bool changed = false; + + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + bool is_sel = false; + bool is_unsel = false; + + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { + continue; + } + + /* are we all selected? */ + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + + if (luv->flag & MLOOPUV_VERTSEL) { + is_sel = true; + } + else { + is_unsel = true; + } + + /* we have mixed selection, bail out */ + if (is_sel && is_unsel) { + break; + } + } + + if (is_sel && is_unsel) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + luv->flag &= ~MLOOPUV_VERTSEL; + } + + changed = true; + } + } + + if (changed) { + changed_multi = true; + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL); + } + } + MEM_freeN(objects); + + return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +} + +void UV_OT_select_split(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Split"; + ot->description = "Select only entirely selected faces"; + ot->idname = "UV_OT_select_split"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = uv_select_split_exec; + ot->poll = ED_operator_uvedit; /* requires space image */ +} + +static void uv_select_sync_flush(const ToolSettings *ts, BMEditMesh *em, const short select) +{ + /* bmesh API handles flushing but not on de-select */ + if (ts->uv_flag & UV_SYNC_SELECTION) { + if (ts->selectmode != SCE_SELECT_FACE) { + if (select == false) { + EDBM_deselect_flush(em); + } + else { + EDBM_select_flush(em); + } + } + + if (select == false) { + BM_select_history_validate(em->bm); + } + } +} + +static void uv_select_tag_update_for_object(Depsgraph *depsgraph, + const ToolSettings *ts, + Object *obedit) +{ + if (ts->uv_flag & UV_SYNC_SELECTION) { + DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); + WM_main_add_notifier(NC_GEOM | ND_SELECT, obedit->data); + } + else { + Object *obedit_eval = DEG_get_evaluated_object(depsgraph, obedit); + BKE_mesh_batch_cache_dirty_tag(obedit_eval->data, BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT); + /* Only for region redraw. */ + WM_main_add_notifier(NC_GEOM | ND_SELECT, obedit->data); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select/Tag Flushing Utils + * + * Utility functions to flush the uv-selection from tags. + * \{ */ + +/** + * helper function for #uv_select_flush_from_tag_loop and uv_select_flush_from_tag_face + */ +static void uv_select_flush_from_tag_sticky_loc_internal(Scene *scene, + BMEditMesh *em, + UvVertMap *vmap, + const uint efa_index, + BMLoop *l, + const bool select, + const int cd_loop_uv_offset) +{ + UvMapVert *start_vlist = NULL, *vlist_iter; + BMFace *efa_vlist; + + uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); + + vlist_iter = BM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v)); + + while (vlist_iter) { + if (vlist_iter->separate) { + start_vlist = vlist_iter; + } + + if (efa_index == vlist_iter->poly_index) { + break; + } + + vlist_iter = vlist_iter->next; + } + + vlist_iter = start_vlist; + while (vlist_iter) { + + if (vlist_iter != start_vlist && vlist_iter->separate) { + break; + } + + if (efa_index != vlist_iter->poly_index) { + BMLoop *l_other; + efa_vlist = BM_face_at_index(em->bm, vlist_iter->poly_index); + /* tf_vlist = BM_ELEM_CD_GET_VOID_P(efa_vlist, cd_poly_tex_offset); */ /* UNUSED */ + + l_other = BM_iter_at_index( + em->bm, BM_LOOPS_OF_FACE, efa_vlist, vlist_iter->loop_of_poly_index); + + uvedit_uv_select_set(em, scene, l_other, select, false, cd_loop_uv_offset); + } + vlist_iter = vlist_iter->next; + } +} + +/** + * Flush the selection from face tags based on sticky and selection modes. + * + * needed because settings the selection a face is done in a number of places but it also + * needs to respect the sticky modes for the UV verts, so dealing with the sticky modes + * is best done in a separate function. + * + * \note This function is very similar to #uv_select_flush_from_tag_loop, + * be sure to update both upon changing. + */ +static void uv_select_flush_from_tag_face(SpaceImage *sima, + Scene *scene, + Object *obedit, + const bool select) +{ + /* Selecting UV Faces with some modes requires us to change + * the selection in other faces (depending on the sticky mode). + * + * This only needs to be done when the Mesh is not used for + * selection (so for sticky modes, vertex or location based). */ + + const ToolSettings *ts = scene->toolsettings; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_VERTEX) { + /* Tag all verts as untouched, then touch the ones that have a face center + * in the loop and select all MLoopUV's that use a touched vert. */ + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + BM_elem_flag_enable(l->v, BM_ELEM_TAG); + } + } + } + + /* now select tagged verts */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */ + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) { + uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); + } + } + } + } + else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) { + struct UvVertMap *vmap; + float limit[2]; + uint efa_index; + + uv_select_island_limit_default(sima, limit); + + BM_mesh_elem_table_ensure(em->bm, BM_FACE); + vmap = BM_uv_vert_map_create(em->bm, limit, false, false); + if (vmap == NULL) { + return; + } + + BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) { + if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { + /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */ + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + uv_select_flush_from_tag_sticky_loc_internal( + scene, em, vmap, efa_index, l, select, cd_loop_uv_offset); + } + } + } + BM_uv_vert_map_free(vmap); + } + else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { + uvedit_face_select_set(scene, em, efa, select, false, cd_loop_uv_offset); + } + } + } +} + +/** + * Flush the selection from loop tags based on sticky and selection modes. + * + * needed because settings the selection a face is done in a number of places but it also needs + * to respect the sticky modes for the UV verts, so dealing with the sticky modes is best done + * in a separate function. + * + * \note This function is very similar to #uv_select_flush_from_tag_loop, + * be sure to update both upon changing. + */ +static void uv_select_flush_from_tag_loop(SpaceImage *sima, + Scene *scene, + Object *obedit, + const bool select) +{ + /* Selecting UV Loops with some modes requires us to change + * the selection in other faces (depending on the sticky mode). + * + * This only needs to be done when the Mesh is not used for + * selection (so for sticky modes, vertex or location based). */ + + const ToolSettings *ts = scene->toolsettings; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_VERTEX) { + /* Tag all verts as untouched, then touch the ones that have a face center + * in the loop and select all MLoopUV's that use a touched vert. */ + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (BM_elem_flag_test(l, BM_ELEM_TAG)) { + BM_elem_flag_enable(l->v, BM_ELEM_TAG); + } + } + } + + /* now select tagged verts */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */ + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) { + uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); + } + } + } + } + else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) { + struct UvVertMap *vmap; + float limit[2]; + uint efa_index; + + uv_select_island_limit_default(sima, limit); + + BM_mesh_elem_table_ensure(em->bm, BM_FACE); + vmap = BM_uv_vert_map_create(em->bm, limit, false, false); + if (vmap == NULL) { + return; + } + + BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) { + /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */ + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (BM_elem_flag_test(l, BM_ELEM_TAG)) { + uv_select_flush_from_tag_sticky_loc_internal( + scene, em, vmap, efa_index, l, select, cd_loop_uv_offset); + } + } + } + BM_uv_vert_map_free(vmap); + } + else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (BM_elem_flag_test(l, BM_ELEM_TAG)) { + uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); + } + } + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Box Select Operator + * \{ */ + +static int uv_box_select_exec(bContext *C, wmOperator *op) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + SpaceImage *sima = CTX_wm_space_image(C); + Scene *scene = CTX_data_scene(C); + const ToolSettings *ts = scene->toolsettings; + ViewLayer *view_layer = CTX_data_view_layer(C); + Image *ima = CTX_data_edit_image(C); + const ARegion *region = CTX_wm_region(C); + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + MLoopUV *luv; + rctf rectf; + bool pinned; + float limit[2]; + const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ? + (ts->selectmode == SCE_SELECT_FACE) : + (ts->uv_selectmode == UV_SELECT_FACE)); + const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ? + (ts->selectmode == SCE_SELECT_EDGE) : + (ts->uv_selectmode == UV_SELECT_EDGE)); + + /* get rectangle from operator */ + WM_operator_properties_border_to_rctf(op, &rectf); + UI_view2d_region_to_view_rctf(®ion->v2d, &rectf, &rectf); + + const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); + const bool select = (sel_op != SEL_OP_SUB); + const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op); + + pinned = RNA_boolean_get(op->ptr, "pinned"); + + uv_select_island_limit_default(sima, limit); + + bool changed_multi = false; + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, ((View3D *)NULL), &objects_len); + + if (use_pre_deselect) { + uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); + } + + /* don't indent to avoid diff noise! */ + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + bool changed = false; + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + /* do actual selection */ + if (use_face_center && !pinned) { + /* handle face selection mode */ + float cent[2]; + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + /* assume not touched */ + BM_elem_flag_disable(efa, BM_ELEM_TAG); + + if (uvedit_face_visible_test(scene, obedit, ima, efa)) { + uv_poly_center(efa, cent, cd_loop_uv_offset); + if (BLI_rctf_isect_pt_v(&rectf, cent)) { + BM_elem_flag_enable(efa, BM_ELEM_TAG); + changed = true; + } + } + } + + /* (de)selects all tagged faces and deals with sticky modes */ + if (changed) { + uv_select_flush_from_tag_face(sima, scene, obedit, select); + } + } + else if (use_edge && !pinned) { + changed = true; + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { + continue; + } + + BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev; + MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset); + bool luv_select_prev = uvedit_uv_select_test(scene, l_prev, cd_loop_uv_offset); + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + const bool luv_select = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); + if ((select != luv_select) || (select != luv_select_prev)) { + if (BLI_rctf_isect_pt_v(&rectf, luv->uv) && + BLI_rctf_isect_pt_v(&rectf, luv_prev->uv)) { + uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); + uvedit_uv_select_set(em, scene, l_prev, select, false, cd_loop_uv_offset); + BM_elem_flag_enable(l->v, BM_ELEM_TAG); + BM_elem_flag_enable(l_prev->v, BM_ELEM_TAG); + } + } + l_prev = l; + luv_prev = luv; + luv_select_prev = luv_select; + } + } + + if (sima->sticky == SI_STICKY_VERTEX) { + uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset); + } + } + else { + /* other selection modes */ + changed = true; + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { + continue; + } + bool has_selected = false; + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if ((select) != (uvedit_uv_select_test(scene, l, cd_loop_uv_offset))) { + if (!pinned || (ts->uv_flag & UV_SYNC_SELECTION)) { + /* UV_SYNC_SELECTION - can't do pinned selection */ + if (BLI_rctf_isect_pt_v(&rectf, luv->uv)) { + uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); + BM_elem_flag_enable(l->v, BM_ELEM_TAG); + has_selected = true; + } + } + else if (pinned) { + if ((luv->flag & MLOOPUV_PINNED) && BLI_rctf_isect_pt_v(&rectf, luv->uv)) { + uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); + BM_elem_flag_enable(l->v, BM_ELEM_TAG); + } + } + } + } + if (has_selected && ts->uv_selectmode == UV_SELECT_ISLAND) { + UvNearestHit hit = { + .ob = obedit, + .efa = efa, + }; + uv_select_linked_multi( + scene, ima, objects, objects_len, limit, &hit, true, !select, false, false); + } + } + + if (sima->sticky == SI_STICKY_VERTEX) { + uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset); + } + } + + if (changed || use_pre_deselect) { + changed_multi = true; + + uv_select_sync_flush(ts, em, select); + uv_select_tag_update_for_object(depsgraph, ts, obedit); + } + } + + MEM_freeN(objects); + + return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +} + +void UV_OT_select_box(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Box Select"; + ot->description = "Select UV vertices using box selection"; + ot->idname = "UV_OT_select_box"; + + /* api callbacks */ + ot->invoke = WM_gesture_box_invoke; + ot->exec = uv_box_select_exec; + ot->modal = WM_gesture_box_modal; + ot->poll = ED_operator_uvedit_space_image; /* requires space image */ + ot->cancel = WM_gesture_box_cancel; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "pinned", 0, "Pinned", "Border select pinned UVs only"); + + WM_operator_properties_gesture_box(ot); + WM_operator_properties_select_operation_simple(ot); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Circle Select Operator + * \{ */ + +static int uv_circle_select_is_point_inside(const float uv[2], + const float offset[2], + const float ellipse[2]) +{ + /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */ + const float co[2] = { + (uv[0] - offset[0]) * ellipse[0], + (uv[1] - offset[1]) * ellipse[1], + }; + return len_squared_v2(co) < 1.0f; +} + +static int uv_circle_select_is_edge_inside(const float uv_a[2], + const float uv_b[2], + const float offset[2], + const float ellipse[2]) +{ + /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */ + const float co_a[2] = { + (uv_a[0] - offset[0]) * ellipse[0], + (uv_a[1] - offset[1]) * ellipse[1], + }; + const float co_b[2] = { + (uv_b[0] - offset[0]) * ellipse[0], + (uv_b[1] - offset[1]) * ellipse[1], + }; + return dist_squared_to_line_segment_v2((const float[2]){0.0f, 0.0f}, co_a, co_b) < 1.0f; +} + +static int uv_circle_select_exec(bContext *C, wmOperator *op) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = CTX_data_edit_image(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + const ToolSettings *ts = scene->toolsettings; + const ARegion *region = CTX_wm_region(C); + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + MLoopUV *luv; + int x, y, radius, width, height; + float zoomx, zoomy; + float limit[2], offset[2], ellipse[2]; + + const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ? + (ts->selectmode == SCE_SELECT_FACE) : + (ts->uv_selectmode == UV_SELECT_FACE)); + const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ? + (ts->selectmode == SCE_SELECT_EDGE) : + (ts->uv_selectmode == UV_SELECT_EDGE)); + + /* get operator properties */ + x = RNA_int_get(op->ptr, "x"); + y = RNA_int_get(op->ptr, "y"); + radius = RNA_int_get(op->ptr, "radius"); + + /* compute ellipse size and location, not a circle since we deal + * with non square image. ellipse is normalized, r = 1.0. */ + ED_space_image_get_size(sima, &width, &height); + ED_space_image_get_zoom(sima, region, &zoomx, &zoomy); + + ellipse[0] = width * zoomx / radius; + ellipse[1] = height * zoomy / radius; + + UI_view2d_region_to_view(®ion->v2d, x, y, &offset[0], &offset[1]); + + uv_select_island_limit_default(sima, limit); + + bool changed_multi = false; + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, ((View3D *)NULL), &objects_len); + + const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"), + WM_gesture_is_modal_first(op->customdata)); + const bool select = (sel_op != SEL_OP_SUB); + const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op); + + if (use_pre_deselect) { + uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); + } + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + bool changed = false; + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + /* do selection */ + if (use_face_center) { + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BM_elem_flag_disable(efa, BM_ELEM_TAG); + /* assume not touched */ + if (select != uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) { + float cent[2]; + uv_poly_center(efa, cent, cd_loop_uv_offset); + if (uv_circle_select_is_point_inside(cent, offset, ellipse)) { + BM_elem_flag_enable(efa, BM_ELEM_TAG); + changed = true; + } + } + } + + /* (de)selects all tagged faces and deals with sticky modes */ + if (changed) { + uv_select_flush_from_tag_face(sima, scene, obedit, select); + } + } + else if (use_edge) { + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { + continue; + } + + BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev; + MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset); + bool luv_select_prev = uvedit_uv_select_test(scene, l_prev, cd_loop_uv_offset); + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + const bool luv_select = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); + if ((select != luv_select) || (select != luv_select_prev)) { + if (uv_circle_select_is_edge_inside(luv->uv, luv_prev->uv, offset, ellipse)) { + changed = true; + uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); + uvedit_uv_select_set(em, scene, l_prev, select, false, cd_loop_uv_offset); + BM_elem_flag_enable(l->v, BM_ELEM_TAG); + BM_elem_flag_enable(l_prev->v, BM_ELEM_TAG); + } + } + l_prev = l; + luv_prev = luv; + luv_select_prev = luv_select; + } + } + + if (sima->sticky == SI_STICKY_VERTEX) { + uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset); + } + } + else { + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { + continue; + } + bool has_selected = false; + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if ((select) != (uvedit_uv_select_test(scene, l, cd_loop_uv_offset))) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (uv_circle_select_is_point_inside(luv->uv, offset, ellipse)) { + changed = true; + uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); + BM_elem_flag_enable(l->v, BM_ELEM_TAG); + has_selected = true; + } + } + } + if (has_selected && ts->uv_selectmode == UV_SELECT_ISLAND) { + UvNearestHit hit = { + .ob = obedit, + .efa = efa, + }; + uv_select_linked_multi( + scene, ima, objects, objects_len, limit, &hit, true, !select, false, false); + } + } + + if (sima->sticky == SI_STICKY_VERTEX) { + uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset); + } + } + + if (changed || use_pre_deselect) { + changed_multi = true; + + uv_select_sync_flush(ts, em, select); + uv_select_tag_update_for_object(depsgraph, ts, obedit); + } + } + MEM_freeN(objects); + + return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED; +} + +void UV_OT_select_circle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Circle Select"; + ot->description = "Select UV vertices using circle selection"; + ot->idname = "UV_OT_select_circle"; + + /* api callbacks */ + ot->invoke = WM_gesture_circle_invoke; + ot->modal = WM_gesture_circle_modal; + ot->exec = uv_circle_select_exec; + ot->poll = ED_operator_uvedit_space_image; /* requires space image */ + ot->cancel = WM_gesture_circle_cancel; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + WM_operator_properties_gesture_circle(ot); + WM_operator_properties_select_operation_simple(ot); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Lasso Select Operator + * \{ */ + +static bool do_lasso_select_mesh_uv_is_point_inside(const ARegion *region, + const rcti *clip_rect, + const int mcoords[][2], + const int mcoords_len, + const float co_test[2]) +{ + int co_screen[2]; + if (UI_view2d_view_to_region_clip( + ®ion->v2d, co_test[0], co_test[1], &co_screen[0], &co_screen[1]) && + BLI_rcti_isect_pt_v(clip_rect, co_screen) && + BLI_lasso_is_point_inside( + mcoords, mcoords_len, co_screen[0], co_screen[1], V2D_IS_CLIPPED)) { + return true; + } + return false; +} + +static bool do_lasso_select_mesh_uv(bContext *C, + const int mcoords[][2], + short mcoords_len, + const eSelectOp sel_op) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = CTX_data_edit_image(C); + const ARegion *region = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + const ToolSettings *ts = scene->toolsettings; + ViewLayer *view_layer = CTX_data_view_layer(C); + const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ? + (ts->selectmode == SCE_SELECT_FACE) : + (ts->uv_selectmode == UV_SELECT_FACE)); + const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ? + (ts->selectmode == SCE_SELECT_EDGE) : + (ts->uv_selectmode == UV_SELECT_EDGE)); + + const bool select = (sel_op != SEL_OP_SUB); + const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op); + + BMIter iter, liter; + + BMFace *efa; + BMLoop *l; + float limit[2]; + bool changed_multi = false; + rcti rect; + + uv_select_island_limit_default(sima, limit); + + BLI_lasso_boundbox(&rect, mcoords, mcoords_len); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, ((View3D *)NULL), &objects_len); + + if (use_pre_deselect) { + uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); + } + + /* don't indent to avoid diff noise! */ + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + + bool changed = false; + + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + if (use_face_center) { /* Face Center Sel */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BM_elem_flag_disable(efa, BM_ELEM_TAG); + /* assume not touched */ + if (select != uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) { + float cent[2]; + uv_poly_center(efa, cent, cd_loop_uv_offset); + if (do_lasso_select_mesh_uv_is_point_inside(region, &rect, mcoords, mcoords_len, cent)) { + BM_elem_flag_enable(efa, BM_ELEM_TAG); + changed = true; + } + } + } + + /* (de)selects all tagged faces and deals with sticky modes */ + if (changed) { + uv_select_flush_from_tag_face(sima, scene, obedit, select); + } + } + else if (use_edge) { + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { + continue; + } + + BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev; + MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset); + bool luv_select_prev = uvedit_uv_select_test(scene, l_prev, cd_loop_uv_offset); + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + const bool luv_select = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); + if ((select != luv_select) || (select != luv_select_prev)) { + if (do_lasso_select_mesh_uv_is_point_inside( + region, &rect, mcoords, mcoords_len, luv->uv) && + do_lasso_select_mesh_uv_is_point_inside( + region, &rect, mcoords, mcoords_len, luv_prev->uv)) { + uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); + uvedit_uv_select_set(em, scene, l_prev, select, false, cd_loop_uv_offset); + changed = true; + BM_elem_flag_enable(l->v, BM_ELEM_TAG); + BM_elem_flag_enable(l_prev->v, BM_ELEM_TAG); + } + } + l_prev = l; + luv_prev = luv; + luv_select_prev = luv_select; + } + } + + if (sima->sticky == SI_STICKY_VERTEX) { + uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset); + } + } + else { /* Vert Sel */ + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { + continue; + } + bool has_selected = false; + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if ((select) != (uvedit_uv_select_test(scene, l, cd_loop_uv_offset))) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (do_lasso_select_mesh_uv_is_point_inside( + region, &rect, mcoords, mcoords_len, luv->uv)) { + uvedit_uv_select_set(em, scene, l, select, false, cd_loop_uv_offset); + changed = true; + BM_elem_flag_enable(l->v, BM_ELEM_TAG); + has_selected = true; + } + } + } + if (has_selected && ts->uv_selectmode == UV_SELECT_ISLAND) { + UvNearestHit hit = { + .ob = obedit, + .efa = efa, + }; + uv_select_linked_multi( + scene, ima, objects, objects_len, limit, &hit, true, !select, false, false); + } + } + + if (sima->sticky == SI_STICKY_VERTEX) { + uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset); + } + } + + if (changed || use_pre_deselect) { + changed_multi = true; + + uv_select_sync_flush(ts, em, select); + uv_select_tag_update_for_object(depsgraph, ts, obedit); + } + } + MEM_freeN(objects); + + return changed_multi; +} + +static int uv_lasso_select_exec(bContext *C, wmOperator *op) +{ + int mcoords_len; + const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len); + + if (mcoords) { + const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode"); + bool changed = do_lasso_select_mesh_uv(C, mcoords, mcoords_len, sel_op); + MEM_freeN((void *)mcoords); + + return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + } + + return OPERATOR_PASS_THROUGH; +} + +void UV_OT_select_lasso(wmOperatorType *ot) +{ + ot->name = "Lasso Select UV"; + ot->description = "Select UVs using lasso selection"; + ot->idname = "UV_OT_select_lasso"; + + ot->invoke = WM_gesture_lasso_invoke; + ot->modal = WM_gesture_lasso_modal; + ot->exec = uv_lasso_select_exec; + ot->poll = ED_operator_uvedit_space_image; + ot->cancel = WM_gesture_lasso_cancel; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + WM_operator_properties_gesture_lasso(ot); + WM_operator_properties_select_operation_simple(ot); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Pinned UV's Operator + * \{ */ + +static int uv_select_pinned_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + const ToolSettings *ts = scene->toolsettings; + ViewLayer *view_layer = CTX_data_view_layer(C); + Image *ima = CTX_data_edit_image(C); + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + MLoopUV *luv; + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, ((View3D *)NULL), &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + bool changed = false; + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { + continue; + } + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + + if (luv->flag & MLOOPUV_PINNED) { + uvedit_uv_select_enable(em, scene, l, false, cd_loop_uv_offset); + changed = true; + } + } + } + + if (changed) { + uv_select_tag_update_for_object(depsgraph, ts, obedit); + } + } + MEM_freeN(objects); + + return OPERATOR_FINISHED; +} + +void UV_OT_select_pinned(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Selected Pinned"; + ot->description = "Select all pinned UV vertices"; + ot->idname = "UV_OT_select_pinned"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = uv_select_pinned_exec; + ot->poll = ED_operator_uvedit; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Overlap Operator + * \{ */ + +BLI_INLINE uint overlap_hash(const void *overlap_v) +{ + const BVHTreeOverlap *overlap = overlap_v; + + /* Designed to treat (A,B) and (B,A) as the same. */ + int x = overlap->indexA; + int y = overlap->indexB; + if (x > y) { + SWAP(int, x, y); + } + return BLI_hash_int_2d(x, y); +} + +BLI_INLINE bool overlap_cmp(const void *a_v, const void *b_v) +{ + const BVHTreeOverlap *a = a_v; + const BVHTreeOverlap *b = b_v; + return !((a->indexA == b->indexA && a->indexB == b->indexB) || + (a->indexA == b->indexB && a->indexB == b->indexA)); +} + +struct UVOverlapData { + int ob_index; + int face_index; + float tri[3][2]; +}; + +static int uv_select_overlap(bContext *C, const bool extend) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Image *ima = CTX_data_edit_image(C); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, ((View3D *)NULL), &objects_len); + + /* Calculate maximum number of tree nodes and prepare initial selection. */ + uint uv_tri_len = 0; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + BM_mesh_elem_table_ensure(em->bm, BM_FACE); + BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE); + BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); + if (!extend) { + uv_select_all_perform(scene, ima, obedit, SEL_DESELECT); + } + + BMIter iter; + BMFace *efa; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test_ex(scene->toolsettings, obedit, ima, efa)) { + continue; + } + uv_tri_len += efa->len - 2; + } + } + + struct UVOverlapData *overlap_data = MEM_mallocN(sizeof(struct UVOverlapData) * uv_tri_len, + "UvOverlapData"); + BVHTree *uv_tree = BLI_bvhtree_new(uv_tri_len, 0.0f, 4, 6); + + /* Use a global data index when inserting into the BVH. */ + int data_index = 0; + + int face_len_alloc = 3; + float(*uv_verts)[2] = MEM_mallocN(sizeof(*uv_verts) * face_len_alloc, "UvOverlapCoords"); + uint(*indices)[3] = MEM_mallocN(sizeof(*indices) * (face_len_alloc - 2), "UvOverlapTris"); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMIter iter, liter; + BMFace *efa; + BMLoop *l; + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + /* Triangulate each UV face and store it inside the BVH. */ + int face_index; + BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, face_index) { + + if (!uvedit_face_visible_test_ex(scene->toolsettings, obedit, ima, efa)) { + continue; + } + + const uint face_len = efa->len; + const uint tri_len = face_len - 2; + + if (face_len_alloc < face_len) { + MEM_freeN(uv_verts); + MEM_freeN(indices); + uv_verts = MEM_mallocN(sizeof(*uv_verts) * face_len, "UvOverlapCoords"); + indices = MEM_mallocN(sizeof(*indices) * tri_len, "UvOverlapTris"); + face_len_alloc = face_len; + } + + int vert_index; + BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, vert_index) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + copy_v2_v2(uv_verts[vert_index], luv->uv); + } + + BLI_polyfill_calc(uv_verts, face_len, 0, indices); + + for (int t = 0; t < tri_len; t++) { + overlap_data[data_index].ob_index = ob_index; + overlap_data[data_index].face_index = face_index; + + /* BVH needs 3D, overlap data uses 2D. */ + const float tri[3][3] = { + {UNPACK2(uv_verts[indices[t][0]]), 0.0f}, + {UNPACK2(uv_verts[indices[t][1]]), 0.0f}, + {UNPACK2(uv_verts[indices[t][2]]), 0.0f}, + }; + + copy_v2_v2(overlap_data[data_index].tri[0], tri[0]); + copy_v2_v2(overlap_data[data_index].tri[1], tri[1]); + copy_v2_v2(overlap_data[data_index].tri[2], tri[2]); + + BLI_bvhtree_insert(uv_tree, data_index, &tri[0][0], 3); + data_index++; + } + } + } + BLI_assert(data_index == uv_tri_len); + + MEM_freeN(uv_verts); + MEM_freeN(indices); + + BLI_bvhtree_balance(uv_tree); + + uint tree_overlap_len; + BVHTreeOverlap *overlap = BLI_bvhtree_overlap(uv_tree, uv_tree, &tree_overlap_len, NULL, NULL); + + if (overlap != NULL) { + GSet *overlap_set = BLI_gset_new_ex(overlap_hash, overlap_cmp, __func__, tree_overlap_len); + + for (int i = 0; i < tree_overlap_len; i++) { + /* Skip overlaps against yourself. */ + if (overlap[i].indexA == overlap[i].indexB) { + continue; + } + + /* Skip overlaps that have already been tested. */ + if (!BLI_gset_add(overlap_set, &overlap[i])) { + continue; + } + + const struct UVOverlapData *o_a = &overlap_data[overlap[i].indexA]; + const struct UVOverlapData *o_b = &overlap_data[overlap[i].indexB]; + Object *obedit_a = objects[o_a->ob_index]; + Object *obedit_b = objects[o_b->ob_index]; + BMEditMesh *em_a = BKE_editmesh_from_object(obedit_a); + BMEditMesh *em_b = BKE_editmesh_from_object(obedit_b); + BMFace *face_a = em_a->bm->ftable[o_a->face_index]; + BMFace *face_b = em_b->bm->ftable[o_b->face_index]; + const int cd_loop_uv_offset_a = CustomData_get_offset(&em_a->bm->ldata, CD_MLOOPUV); + const int cd_loop_uv_offset_b = CustomData_get_offset(&em_b->bm->ldata, CD_MLOOPUV); + + /* Skip if both faces are already selected. */ + if (uvedit_face_select_test(scene, face_a, cd_loop_uv_offset_a) && + uvedit_face_select_test(scene, face_b, cd_loop_uv_offset_b)) { + continue; + } + + /* Main tri-tri overlap test. */ + const float endpoint_bias = -1e-4f; + const float(*t1)[2] = o_a->tri; + const float(*t2)[2] = o_b->tri; + float vi[2]; + bool result = ( + /* Don't use 'isect_tri_tri_v2' here + * because it's important to ignore overlap at end-points. */ + isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[0], t2[1], endpoint_bias, vi) == 1 || + isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[1], t2[2], endpoint_bias, vi) == 1 || + isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[2], t2[0], endpoint_bias, vi) == 1 || + isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[0], t2[1], endpoint_bias, vi) == 1 || + isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[1], t2[2], endpoint_bias, vi) == 1 || + isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[2], t2[0], endpoint_bias, vi) == 1 || + isect_seg_seg_v2_point_ex(t1[2], t1[0], t2[0], t2[1], endpoint_bias, vi) == 1 || + isect_seg_seg_v2_point_ex(t1[2], t1[0], t2[1], t2[2], endpoint_bias, vi) == 1 || + isect_point_tri_v2(t1[0], t2[0], t2[1], t2[2]) != 0 || + isect_point_tri_v2(t2[0], t1[0], t1[1], t1[2]) != 0); + + if (result) { + uvedit_face_select_enable(scene, em_a, face_a, false, cd_loop_uv_offset_a); + uvedit_face_select_enable(scene, em_b, face_b, false, cd_loop_uv_offset_b); + } + } + + BLI_gset_free(overlap_set, NULL); + MEM_freeN(overlap); + } + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + uv_select_tag_update_for_object(depsgraph, scene->toolsettings, objects[ob_index]); + } + + BLI_bvhtree_free(uv_tree); + + MEM_freeN(overlap_data); + MEM_freeN(objects); + + return OPERATOR_FINISHED; +} + +static int uv_select_overlap_exec(bContext *C, wmOperator *op) +{ + bool extend = RNA_boolean_get(op->ptr, "extend"); + return uv_select_overlap(C, extend); +} + +void UV_OT_select_overlap(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Overlap"; + ot->description = "Select all UV faces which overlap each other"; + ot->idname = "UV_OT_select_overlap"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = uv_select_overlap_exec; + ot->poll = ED_operator_uvedit; + + /* properties */ + RNA_def_boolean(ot->srna, + "extend", + 0, + "Extend", + "Extend selection rather than clearing the existing selection"); +} + +/** \} */ diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt index 0e8aafad7ac..d3c85b891c6 100644 --- a/source/blender/gpencil_modifiers/CMakeLists.txt +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -56,10 +56,10 @@ set(SRC intern/MOD_gpencilsimplify.c intern/MOD_gpencilsmooth.c intern/MOD_gpencilsubdiv.c + intern/MOD_gpenciltexture.c intern/MOD_gpencilthick.c intern/MOD_gpenciltime.c intern/MOD_gpenciltint.c - intern/MOD_gpenciltexture.c MOD_gpencil_modifiertypes.h ) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c index d3d6fc470f7..15e069fc162 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c @@ -141,7 +141,7 @@ static void bakeModifier(struct Main *UNUSED(bmain), } GpencilModifierTypeInfo modifierType_Gpencil_Texture = { - /* name */ "Texture", + /* name */ "Texture Mapping", /* structName */ "TextureGpencilModifierData", /* structSize */ sizeof(TextureGpencilModifierData), /* type */ eGpencilModifierTypeType_Gpencil, diff --git a/source/blender/gpu/GPU_immediate_util.h b/source/blender/gpu/GPU_immediate_util.h index 1cf6475408f..47b44b59461 100644 --- a/source/blender/gpu/GPU_immediate_util.h +++ b/source/blender/gpu/GPU_immediate_util.h @@ -70,6 +70,13 @@ void imm_draw_disk_partial_fill_2d(uint pos, void imm_draw_box_wire_2d(uint pos, float x1, float y1, float x2, float y2); void imm_draw_box_wire_3d(uint pos, float x1, float y1, float x2, float y2); +void imm_draw_box_checker_2d_ex(float x1, + float y1, + float x2, + float y2, + const float color_primary[4], + const float color_secondary[4], + int checker_size); void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2); void imm_draw_cube_fill_3d(uint pos, const float co[3], const float aspect[3]); diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index dd64b858b35..0e382f2225f 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -29,9 +29,9 @@ extern "C" { #endif typedef struct GPUShader GPUShader; +struct GPUShaderInterface; struct GPUTexture; struct GPUUniformBuffer; -struct GPUShaderInterface; /* GPU Shader * - only for fragment shaders now diff --git a/source/blender/gpu/intern/gpu_immediate.c b/source/blender/gpu/intern/gpu_immediate.c index 5a5dfb3e1e8..f91a13a3b62 100644 --- a/source/blender/gpu/intern/gpu_immediate.c +++ b/source/blender/gpu/intern/gpu_immediate.c @@ -268,7 +268,8 @@ void immBegin(GPUPrimType prim_type, uint vertex_len) else { /* orphan this buffer & start with a fresh one */ /* this method works on all platforms, old & new */ - glBufferData(GL_ARRAY_BUFFER, active_buffer->buffer_size, NULL, GL_DYNAMIC_DRAW); + /* `GL_DYNAMIC_DRAW` was causing problems when closing temporary windows on old AMD GPUs. */ + glBufferData(GL_ARRAY_BUFFER, active_buffer->buffer_size, NULL, GL_STATIC_DRAW); active_buffer->buffer_offset = 0; } diff --git a/source/blender/gpu/intern/gpu_immediate_util.c b/source/blender/gpu/intern/gpu_immediate_util.c index 7266f595447..77b6f237f03 100644 --- a/source/blender/gpu/intern/gpu_immediate_util.c +++ b/source/blender/gpu/intern/gpu_immediate_util.c @@ -361,25 +361,35 @@ void imm_draw_box_wire_3d(uint pos, float x1, float y1, float x2, float y2) /** * Draw a standard checkerboard to indicate transparent backgrounds. */ -void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2) +void imm_draw_box_checker_2d_ex(float x1, + float y1, + float x2, + float y2, + const float color_primary[4], + const float color_secondary[4], + const int checker_size) { uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - float checker_primary[4]; - float checker_secondary[4]; - int checker_size = UI_GetThemeValue(TH_TRANSPARENT_CHECKER_SIZE); immBindBuiltinProgram(GPU_SHADER_2D_CHECKER); - UI_GetThemeColor4fv(TH_TRANSPARENT_CHECKER_PRIMARY, checker_primary); - UI_GetThemeColor4fv(TH_TRANSPARENT_CHECKER_SECONDARY, checker_secondary); - immUniform4fv("color1", checker_primary); - immUniform4fv("color2", checker_secondary); + immUniform4fv("color1", color_primary); + immUniform4fv("color2", color_secondary); immUniform1i("size", checker_size); immRectf(pos, x1, y1, x2, y2); immUnbindProgram(); } +void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2) +{ + float checker_primary[4]; + float checker_secondary[4]; + UI_GetThemeColor4fv(TH_TRANSPARENT_CHECKER_PRIMARY, checker_primary); + UI_GetThemeColor4fv(TH_TRANSPARENT_CHECKER_SECONDARY, checker_secondary); + int checker_size = UI_GetThemeValue(TH_TRANSPARENT_CHECKER_SIZE); + imm_draw_box_checker_2d_ex(x1, y1, x2, y2, checker_primary, checker_secondary, checker_size); +} void imm_draw_cube_fill_3d(uint pos, const float co[3], const float aspect[3]) { diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c index e6f0f44b2b3..7ebbd1a7409 100644 --- a/source/blender/imbuf/intern/imageprocess.c +++ b/source/blender/imbuf/intern/imageprocess.c @@ -338,7 +338,7 @@ void nearest_interpolation(ImBuf *in, ImBuf *out, float x, float y, int xout, in /*********************** Threaded image processing *************************/ -static void processor_apply_func(TaskPool *__restrict pool, void *taskdata, int UNUSED(threadid)) +static void processor_apply_func(TaskPool *__restrict pool, void *taskdata) { void (*do_thread)(void *) = (void (*)(void *))BLI_task_pool_user_data(pool); do_thread(taskdata); @@ -353,14 +353,13 @@ void IMB_processor_apply_threaded( { const int lines_per_task = 64; - TaskScheduler *task_scheduler = BLI_task_scheduler_get(); TaskPool *task_pool; void *handles; int total_tasks = (buffer_lines + lines_per_task - 1) / lines_per_task; int i, start_line; - task_pool = BLI_task_pool_create(task_scheduler, do_thread, TASK_PRIORITY_LOW); + task_pool = BLI_task_pool_create(do_thread, TASK_PRIORITY_LOW); handles = MEM_callocN(handle_size * total_tasks, "processor apply threaded handles"); @@ -399,9 +398,7 @@ typedef struct ScanlineGlobalData { int total_scanlines; } ScanlineGlobalData; -static void processor_apply_scanline_func(TaskPool *__restrict pool, - void *taskdata, - int UNUSED(threadid)) +static void processor_apply_scanline_func(TaskPool *__restrict pool, void *taskdata) { ScanlineGlobalData *data = BLI_task_pool_user_data(pool); int start_scanline = POINTER_AS_INT(taskdata); @@ -420,8 +417,7 @@ void IMB_processor_apply_threaded_scanlines(int total_scanlines, data.scanlines_per_task = scanlines_per_task; data.total_scanlines = total_scanlines; const int total_tasks = (total_scanlines + scanlines_per_task - 1) / scanlines_per_task; - TaskScheduler *task_scheduler = BLI_task_scheduler_get(); - TaskPool *task_pool = BLI_task_pool_create(task_scheduler, &data, TASK_PRIORITY_LOW); + TaskPool *task_pool = BLI_task_pool_create(&data, TASK_PRIORITY_LOW); for (int i = 0, start_line = 0; i < total_tasks; i++) { BLI_task_pool_push( task_pool, processor_apply_scanline_func, POINTER_FROM_INT(start_line), false, NULL); diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index ba1485004ae..289f8c7a7b9 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -106,6 +106,18 @@ enum { IDP_NUMTYPES = 10, }; +/** Used by some IDP utils, keep values in sync with type enum above. */ +enum { + IDP_TYPE_FILTER_STRING = 1 << 0, + IDP_TYPE_FILTER_INT = 1 << 1, + IDP_TYPE_FILTER_FLOAT = 1 << 2, + IDP_TYPE_FILTER_ARRAY = 1 << 5, + IDP_TYPE_FILTER_GROUP = 1 << 6, + IDP_TYPE_FILTER_ID = 1 << 7, + IDP_TYPE_FILTER_DOUBLE = 1 << 8, + IDP_TYPE_FILTER_IDPARRAY = 1 << 9, +}; + /*->subtype */ /* IDP_STRING */ diff --git a/source/blender/makesdna/DNA_fluid_types.h b/source/blender/makesdna/DNA_fluid_types.h index 166b3c22932..6583aa2eeee 100644 --- a/source/blender/makesdna/DNA_fluid_types.h +++ b/source/blender/makesdna/DNA_fluid_types.h @@ -393,7 +393,6 @@ typedef struct FluidDomainSettings { float surface_tension; float viscosity_base; int viscosity_exponent; - float domain_size; /* Mesh options. */ float mesh_concave_upper; @@ -404,7 +403,7 @@ typedef struct FluidDomainSettings { int mesh_scale; int totvert; short mesh_generator; - char _pad5[2]; /* Unused. */ + char _pad5[6]; /* Unused. */ /* Secondary particle options. */ int particle_type; diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 1714ec8b498..9fee839f979 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -468,6 +468,7 @@ typedef struct SequencerScopes { #define SEQ_SPEED_INTEGRATE (1 << 0) #define SEQ_SPEED_UNUSED_1 (1 << 1) /* cleared */ #define SEQ_SPEED_COMPRESS_IPO_Y (1 << 2) +#define SEQ_SPEED_USE_INTERPOLATION (1 << 3) /* ***************** SEQUENCE ****************** */ #define SEQ_NAME_MAXSTR 64 diff --git a/source/blender/makesdna/DNA_tracking_types.h b/source/blender/makesdna/DNA_tracking_types.h index ab9f3d07849..32a00cc25d1 100644 --- a/source/blender/makesdna/DNA_tracking_types.h +++ b/source/blender/makesdna/DNA_tracking_types.h @@ -70,6 +70,9 @@ typedef struct MovieTrackingCamera { /* Division distortion model coefficients */ float division_k1, division_k2; + + /* Nuke distortion model coefficients */ + float nuke_k1, nuke_k2; } MovieTrackingCamera; typedef struct MovieTrackingMarker { @@ -455,6 +458,7 @@ typedef struct MovieTracking { enum { TRACKING_DISTORTION_MODEL_POLYNOMIAL = 0, TRACKING_DISTORTION_MODEL_DIVISION = 1, + TRACKING_DISTORTION_MODEL_NUKE = 2, }; /* MovieTrackingCamera->units */ diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c index 33f19153e3a..ea8b0cd758b 100644 --- a/source/blender/makesrna/intern/rna_fcurve.c +++ b/source/blender/makesrna/intern/rna_fcurve.c @@ -213,6 +213,7 @@ static StructRNA *rna_FModifierType_refine(struct PointerRNA *ptr) # include "BKE_anim_data.h" # include "BKE_fcurve.h" +# include "BKE_fcurve_driver.h" # include "DEG_depsgraph.h" # include "DEG_depsgraph_build.h" diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c index d89bdcd074f..86860e5e14b 100644 --- a/source/blender/makesrna/intern/rna_fluid.c +++ b/source/blender/makesrna/intern/rna_fluid.c @@ -1687,11 +1687,6 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) "e.g. 5*10^-6)"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_datacache_reset"); - prop = RNA_def_property(srna, "domain_size", PROP_FLOAT, PROP_NONE); - RNA_def_property_range(prop, 0.001, 10000.0); - RNA_def_property_ui_text(prop, "Meters", "Domain size in meters (longest domain side)"); - RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_domain_reset"); - /* mesh options options */ prop = RNA_def_property(srna, "mesh_concave_upper", PROP_FLOAT, PROP_NONE); @@ -2472,7 +2467,7 @@ static void rna_def_fluid_flow_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Random", "Amount of random velocity"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_flow_reset"); - prop = RNA_def_property(srna, "velocity_coord", PROP_FLOAT, PROP_XYZ); + prop = RNA_def_property(srna, "velocity_coord", PROP_FLOAT, PROP_VELOCITY); RNA_def_property_float_sdna(prop, NULL, "vel_coord"); RNA_def_property_array(prop, 3); RNA_def_property_range(prop, -1000.1, 1000.1); diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 63fe1cf05ca..b36cc493447 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -1471,7 +1471,7 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Info", "Layer name"); RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilLayer_info_set"); RNA_def_struct_name_property(srna, prop); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | NA_RENAME, NULL); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA | NA_RENAME, "rna_GPencil_update"); /* Frames */ prop = RNA_def_property(srna, "frames", PROP_COLLECTION, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index cb16dae7afa..1906c663c54 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -132,7 +132,7 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { {eGpencilModifierType_Texture, "GP_TEXTURE", ICON_TEXTURE, - "Texture", + "Texture Mapping", "Change stroke uv texture values"}, {0, NULL, 0, NULL, NULL}, }; @@ -2166,18 +2166,19 @@ static void rna_def_modifier_gpenciltexture(BlenderRNA *brna) }; static const EnumPropertyItem mode_items[] = { - {STROKE, "STROKE", 0, "Stroke UVs", "Manipulate only stroke UVs"}, - {FILL, "FILL", 0, "Fill UVs", "Manipulate only fill UVs"}, + {STROKE, "STROKE", 0, "Stroke", "Manipulate only stroke texture coordinates"}, + {FILL, "FILL", 0, "Fill", "Manipulate only fill texture coordinates"}, {STROKE_AND_FILL, "STROKE_AND_FILL", 0, - "Stroke and Fill UVs", - "Manipulate both stroke and fill UVs"}, + "Stroke and Fill", + "Manipulate both stroke and fill texture coordinates"}, {0, NULL, 0, NULL, NULL}, }; srna = RNA_def_struct(brna, "TextureGpencilModifier", "GpencilModifier"); - RNA_def_struct_ui_text(srna, "Texture Modifier", "Transform stroke texture UVs Modifier"); + RNA_def_struct_ui_text( + srna, "Texture Modifier", "Transform stroke texture coordinates Modifier"); RNA_def_struct_sdna(srna, "TextureGpencilModifierData"); RNA_def_struct_ui_icon(srna, ICON_TEXTURE); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index c32a88b9918..7727f68c4c0 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -266,7 +266,7 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = { ICON_MOD_WAVE, "Wave", "Adds a ripple-like motion to an object’s geometry"}, - {0, "", 0, N_("Simulate"), ""}, + {0, "", 0, N_("Physics"), ""}, {eModifierType_Cloth, "CLOTH", ICON_MOD_CLOTH, "Cloth", ""}, {eModifierType_Collision, "COLLISION", ICON_MOD_PHYSICS, "Collision", ""}, {eModifierType_DynamicPaint, "DYNAMIC_PAINT", ICON_MOD_DYNAMICPAINT, "Dynamic Paint", ""}, diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 9242ad7237f..8089aa389c9 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -203,7 +203,7 @@ const EnumPropertyItem rna_enum_node_math_items[] = { "Round A to the nearest integer. Round upward if the fraction part is 0.5"}, {NODE_MATH_FLOOR, "FLOOR", 0, "Floor", "The largest integer smaller than or equal A"}, {NODE_MATH_CEIL, "CEIL", 0, "Ceil", "The smallest integer greater than or equal A"}, - {NODE_MATH_TRUNC, "TRUNC", 0, "Truncate", "trunc(A)"}, + {NODE_MATH_TRUNC, "TRUNC", 0, "Truncate", "The integer part of A, removing fractional digits"}, {0, "", ICON_NONE, NULL, NULL}, {NODE_MATH_FRACTION, "FRACT", 0, "Fraction", "The fraction part of A"}, {NODE_MATH_MODULO, "MODULO", 0, "Modulo", "Modulo using fmod(A,B)"}, diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index d218084fc66..fd8ddfff14f 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -2020,9 +2020,10 @@ static void rna_def_editor(BlenderRNA *brna) prop = RNA_def_property(srna, "use_prefetch", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "cache_flag", SEQ_CACHE_PREFETCH_ENABLE); - RNA_def_property_ui_text(prop, - "Prefetch frames", - "Render frames ahead of playhead in background for faster playback"); + RNA_def_property_ui_text( + prop, + "Prefetch Frames", + "Render frames ahead of playhead in the background for faster playback"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL); prop = RNA_def_property(srna, "recycle_max_cost", PROP_FLOAT, PROP_NONE); @@ -2760,6 +2761,13 @@ static void rna_def_speed_control(StructRNA *srna) prop, "Scale to Length", "Scale values from 0.0 to 1.0 to target sequence length"); RNA_def_property_update( prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update"); + + prop = RNA_def_property(srna, "frame_interpolation_mode", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", SEQ_SPEED_USE_INTERPOLATION); + RNA_def_property_ui_text( + prop, "Frame interpolation", "Do crossfade blending between current and next frame"); + RNA_def_property_update( + prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update"); } static void rna_def_gaussian_blur(StructRNA *srna) diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c index 131c13c4d90..507d06482df 100644 --- a/source/blender/makesrna/intern/rna_tracking.c +++ b/source/blender/makesrna/intern/rna_tracking.c @@ -1149,6 +1149,7 @@ static void rna_def_trackingCamera(BlenderRNA *brna) "Divisions", "Division distortion model which " "better represents wide-angle cameras"}, + {TRACKING_DISTORTION_MODEL_NUKE, "NUKE", 0, "Nuke", "Nuke distortion model"}, {0, NULL, 0, NULL, NULL}, }; @@ -1252,6 +1253,19 @@ static void rna_def_trackingCamera(BlenderRNA *brna) RNA_def_property_ui_text(prop, "K2", "First coefficient of second order division distortion"); RNA_def_property_update(prop, NC_MOVIECLIP | NA_EDITED, "rna_tracking_flushUpdate"); + /* Nuke distortion parameters */ + prop = RNA_def_property(srna, "nuke_k1", PROP_FLOAT, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_range(prop, -10, 10, 0.1, 3); + RNA_def_property_ui_text(prop, "K1", "First coefficient of second order Nuke distortion"); + RNA_def_property_update(prop, NC_MOVIECLIP | NA_EDITED, "rna_tracking_flushUpdate"); + + prop = RNA_def_property(srna, "nuke_k2", PROP_FLOAT, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_range(prop, -10, 10, 0.1, 3); + RNA_def_property_ui_text(prop, "K2", "Second coefficient of second order Nuke distortion"); + RNA_def_property_update(prop, NC_MOVIECLIP | NA_EDITED, "rna_tracking_flushUpdate"); + /* pixel aspect */ prop = RNA_def_property(srna, "pixel_aspect", PROP_FLOAT, PROP_XYZ); RNA_def_property_float_sdna(prop, NULL, "pixel_aspect"); diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index b08e48a4b17..04a8500d136 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -45,6 +45,16 @@ static bool rna_XrSessionState_is_running(bContext *C) # endif } +static void rna_XrSessionState_reset_to_base_pose(bContext *C) +{ +# ifdef WITH_XR_OPENXR + wmWindowManager *wm = CTX_wm_manager(C); + WM_xr_session_base_pose_reset(&wm->xr); +# else + UNUSED_VARS(C); +# endif +} + # ifdef WITH_XR_OPENXR static wmXrData *rna_XrSessionState_wm_xr_data_get(PointerRNA *ptr) { @@ -197,6 +207,12 @@ static void rna_def_xr_session_state(BlenderRNA *brna) parm = RNA_def_boolean(func, "result", 0, "Result", ""); RNA_def_function_return(func, parm); + func = RNA_def_function(srna, "reset_to_base_pose", "rna_XrSessionState_reset_to_base_pose"); + RNA_def_function_ui_description(func, "Force resetting of position and rotation deltas"); + RNA_def_function_flag(func, FUNC_NO_SELF); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); + prop = RNA_def_property(srna, "viewer_pose_location", PROP_FLOAT, PROP_TRANSLATION); RNA_def_property_array(prop, 3); RNA_def_property_float_funcs(prop, "rna_XrSessionState_viewer_pose_location_get", NULL, NULL); diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c index 49492200021..b54d5fed390 100644 --- a/source/blender/modifiers/intern/MOD_datatransfer.c +++ b/source/blender/modifiers/intern/MOD_datatransfer.c @@ -225,8 +225,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * else if (result->totvert > HIGH_POLY_WARNING || ((Mesh *)(ob_source->data))->totvert > HIGH_POLY_WARNING) { modifier_setError( - md, - "You are using a rather high poly as source or destination, computation might be slow"); + md, "Source or destination object has a high polygon count, computation might be slow"); } return result; diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c index 4c4c25ec449..e842e81f97f 100644 --- a/source/blender/modifiers/intern/MOD_solidify_extrude.c +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -1231,10 +1231,6 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex MEM_freeN(poly_nors); } - if (numPolys == 0 && numVerts != 0) { - modifier_setError(md, "Faces needed for useful output"); - } - return result; } diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index b3c5313999f..d237b55e900 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -149,7 +149,6 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, const uint numLoops = (uint)mesh->totloop; if (numPolys == 0 && numVerts != 0) { - modifier_setError(md, "Faces needed for useful output"); return mesh; } diff --git a/source/blender/nodes/composite/nodes/node_composite_image.c b/source/blender/nodes/composite/nodes/node_composite_image.c index 10de192277b..382459993b7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.c +++ b/source/blender/nodes/composite/nodes/node_composite_image.c @@ -86,6 +86,12 @@ static void cmp_node_image_add_pass_output(bNodeTree *ntree, { bNodeSocket *sock = BLI_findstring(&node->outputs, name, offsetof(bNodeSocket, name)); + /* Replace if types don't match. */ + if (sock && sock->type != type) { + nodeRemoveSocket(ntree, node, sock); + sock = NULL; + } + /* Create socket if it doesn't exist yet. */ if (sock == NULL) { if (rres_index >= 0) { diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c index 31f67cb1ffa..b3d595cc69b 100644 --- a/source/blender/nodes/texture/node_texture_tree.c +++ b/source/blender/nodes/texture/node_texture_tree.c @@ -282,7 +282,7 @@ int ntreeTexExecTree(bNodeTree *nodes, float dyt[3], int osatex, const short thread, - Tex *UNUSED(tex), + const Tex *UNUSED(tex), short which_output, int cfra, int preview, diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c index 726599ff06e..50ae05694eb 100644 --- a/source/blender/python/intern/bpy_driver.c +++ b/source/blender/python/intern/bpy_driver.c @@ -33,7 +33,7 @@ #include "BLI_math_base.h" #include "BLI_string.h" -#include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "BKE_global.h" #include "bpy_rna_driver.h" /* for pyrna_driver_get_variable_value */ diff --git a/source/blender/python/intern/bpy_rna_driver.c b/source/blender/python/intern/bpy_rna_driver.c index a8d8252b231..9240e34bbab 100644 --- a/source/blender/python/intern/bpy_rna_driver.c +++ b/source/blender/python/intern/bpy_rna_driver.c @@ -26,7 +26,7 @@ #include "BLI_utildefines.h" -#include "BKE_fcurve.h" +#include "BKE_fcurve_driver.h" #include "RNA_access.h" diff --git a/source/blender/render/extern/include/RE_render_ext.h b/source/blender/render/extern/include/RE_render_ext.h index bca3b749192..bdf81354b8d 100644 --- a/source/blender/render/extern/include/RE_render_ext.h +++ b/source/blender/render/extern/include/RE_render_ext.h @@ -33,18 +33,16 @@ struct ImagePool; struct MTex; /* render_texture.c */ -/* used by particle.c, effect.c, editmesh_modes.c and brush.c, returns 1 if rgb, 0 otherwise */ -int externtex(const struct MTex *mtex, - const float vec[3], - float *tin, - float *tr, - float *tg, - float *tb, - float *ta, - const int thread, - struct ImagePool *pool, - const bool skip_load_image, - const bool texnode_preview); +bool RE_texture_evaluate(const struct MTex *mtex, + const float vec[3], + const int thread, + struct ImagePool *pool, + const bool skip_load_image, + const bool texnode_preview, + /* Return arguments. */ + float *r_intensity, + float r_rgba[4]) ATTR_NONNULL(1, 2, 7, 8); + void texture_rgb_blend( float in[3], const float tex[3], const float out[3], float fact, float facg, int blendtype); float texture_value_blend(float tex, float out, float fact, float facg, int blendtype); diff --git a/source/blender/render/intern/source/render_texture.c b/source/blender/render/intern/source/render_texture.c index eabba94ef29..ee484924bf9 100644 --- a/source/blender/render/intern/source/render_texture.c +++ b/source/blender/render/intern/source/render_texture.c @@ -79,7 +79,7 @@ void RE_texture_rng_exit(void) /* ------------------------------------------------------------------------- */ /* this allows colorbanded textures to control normals as well */ -static void tex_normal_derivate(Tex *tex, TexResult *texres) +static void tex_normal_derivate(const Tex *tex, TexResult *texres) { if (tex->flag & TEX_COLORBAND) { float col[4]; @@ -106,7 +106,7 @@ static void tex_normal_derivate(Tex *tex, TexResult *texres) texres->nor[2] = texres->tin - texres->nor[2]; } -static int blend(Tex *tex, const float texvec[3], TexResult *texres) +static int blend(const Tex *tex, const float texvec[3], TexResult *texres) { float x, y, t; @@ -170,7 +170,7 @@ static int blend(Tex *tex, const float texvec[3], TexResult *texres) /* newnoise: all noisebased types now have different noisebases to choose from */ -static int clouds(Tex *tex, const float texvec[3], TexResult *texres) +static int clouds(const Tex *tex, const float texvec[3], TexResult *texres) { int rv = TEX_INT; @@ -271,7 +271,7 @@ static float tex_tri(float a) } /* computes basic wood intensity value at x,y,z */ -static float wood_int(Tex *tex, float x, float y, float z) +static float wood_int(const Tex *tex, float x, float y, float z) { float wi = 0; /* wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2 */ @@ -308,7 +308,7 @@ static float wood_int(Tex *tex, float x, float y, float z) return wi; } -static int wood(Tex *tex, const float texvec[3], TexResult *texres) +static int wood(const Tex *tex, const float texvec[3], TexResult *texres) { int rv = TEX_INT; @@ -329,7 +329,7 @@ static int wood(Tex *tex, const float texvec[3], TexResult *texres) } /* computes basic marble intensity at x,y,z */ -static float marble_int(Tex *tex, float x, float y, float z) +static float marble_int(const Tex *tex, float x, float y, float z) { float n, mi; short wf = tex->noisebasis2; /* wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2 */ @@ -367,7 +367,7 @@ static float marble_int(Tex *tex, float x, float y, float z) return mi; } -static int marble(Tex *tex, const float texvec[3], TexResult *texres) +static int marble(const Tex *tex, const float texvec[3], TexResult *texres) { int rv = TEX_INT; @@ -391,7 +391,7 @@ static int marble(Tex *tex, const float texvec[3], TexResult *texres) /* ------------------------------------------------------------------------- */ -static int magic(Tex *tex, const float texvec[3], TexResult *texres) +static int magic(const Tex *tex, const float texvec[3], TexResult *texres) { float x, y, z, turb; int n; @@ -467,7 +467,7 @@ static int magic(Tex *tex, const float texvec[3], TexResult *texres) /* ------------------------------------------------------------------------- */ /* newnoise: stucci also modified to use different noisebasis */ -static int stucci(Tex *tex, const float texvec[3], TexResult *texres) +static int stucci(const Tex *tex, const float texvec[3], TexResult *texres) { float nor[3], b2, ofs; int retval = TEX_INT; @@ -533,7 +533,7 @@ static int stucci(Tex *tex, const float texvec[3], TexResult *texres) /* ------------------------------------------------------------------------- */ /* newnoise: musgrave terrain noise types */ -static float mg_mFractalOrfBmTex(Tex *tex, const float texvec[3], TexResult *texres) +static int mg_mFractalOrfBmTex(const Tex *tex, const float texvec[3], TexResult *texres) { int rv = TEX_INT; float (*mgravefunc)(float, float, float, float, float, float, int); @@ -588,7 +588,7 @@ static float mg_mFractalOrfBmTex(Tex *tex, const float texvec[3], TexResult *tex return rv; } -static float mg_ridgedOrHybridMFTex(Tex *tex, const float texvec[3], TexResult *texres) +static int mg_ridgedOrHybridMFTex(const Tex *tex, const float texvec[3], TexResult *texres) { int rv = TEX_INT; float (*mgravefunc)(float, float, float, float, float, float, float, float, int); @@ -651,7 +651,7 @@ static float mg_ridgedOrHybridMFTex(Tex *tex, const float texvec[3], TexResult * return rv; } -static float mg_HTerrainTex(Tex *tex, const float texvec[3], TexResult *texres) +static int mg_HTerrainTex(const Tex *tex, const float texvec[3], TexResult *texres) { int rv = TEX_INT; @@ -702,7 +702,7 @@ static float mg_HTerrainTex(Tex *tex, const float texvec[3], TexResult *texres) return rv; } -static float mg_distNoiseTex(Tex *tex, const float texvec[3], TexResult *texres) +static int mg_distNoiseTex(const Tex *tex, const float texvec[3], TexResult *texres) { int rv = TEX_INT; @@ -747,7 +747,7 @@ static float mg_distNoiseTex(Tex *tex, const float texvec[3], TexResult *texres) * probably the slowest, especially with minkovsky, bumpmapping, could be done another way. */ -static float voronoiTex(Tex *tex, const float texvec[3], TexResult *texres) +static int voronoiTex(const Tex *tex, const float texvec[3], TexResult *texres) { int rv = TEX_INT; float da[4], pa[12]; /* distance and point coordinate arrays of 4 nearest neighbors */ @@ -761,8 +761,7 @@ static float voronoiTex(Tex *tex, const float texvec[3], TexResult *texres) } voronoi(texvec[0], texvec[1], texvec[2], da, pa, tex->vn_mexp, tex->vn_distm); - texres->tin = sc * fabsf(tex->vn_w1 * da[0] + tex->vn_w2 * da[1] + tex->vn_w3 * da[2] + - tex->vn_w4 * da[3]); + texres->tin = sc * fabsf(dot_v4v4(&tex->vn_w1, da)); if (tex->vn_coltype) { float ca[3]; /* cell color */ @@ -809,14 +808,11 @@ static float voronoiTex(Tex *tex, const float texvec[3], TexResult *texres) /* calculate bumpnormal */ voronoi(texvec[0] + offs, texvec[1], texvec[2], da, pa, tex->vn_mexp, tex->vn_distm); - texres->nor[0] = sc * fabsf(tex->vn_w1 * da[0] + tex->vn_w2 * da[1] + tex->vn_w3 * da[2] + - tex->vn_w4 * da[3]); + texres->nor[0] = sc * fabsf(dot_v4v4(&tex->vn_w1, da)); voronoi(texvec[0], texvec[1] + offs, texvec[2], da, pa, tex->vn_mexp, tex->vn_distm); - texres->nor[1] = sc * fabsf(tex->vn_w1 * da[0] + tex->vn_w2 * da[1] + tex->vn_w3 * da[2] + - tex->vn_w4 * da[3]); + texres->nor[1] = sc * fabsf(dot_v4v4(&tex->vn_w1, da)); voronoi(texvec[0], texvec[1], texvec[2] + offs, da, pa, tex->vn_mexp, tex->vn_distm); - texres->nor[2] = sc * fabsf(tex->vn_w1 * da[0] + tex->vn_w2 * da[1] + tex->vn_w3 * da[2] + - tex->vn_w4 * da[3]); + texres->nor[2] = sc * fabsf(dot_v4v4(&tex->vn_w1, da)); tex_normal_derivate(tex, texres); rv |= TEX_NOR; @@ -835,7 +831,7 @@ static float voronoiTex(Tex *tex, const float texvec[3], TexResult *texres) /* ------------------------------------------------------------------------- */ -static int texnoise(Tex *tex, TexResult *texres, int thread) +static int texnoise(const Tex *tex, TexResult *texres, int thread) { float div = 3.0; int val, ran, loop, shift = 29; @@ -1724,17 +1720,20 @@ float texture_value_blend(float tex, float out, float fact, float facg, int blen /* ------------------------------------------------------------------------- */ -int externtex(const MTex *mtex, - const float vec[3], - float *tin, - float *tr, - float *tg, - float *tb, - float *ta, - const int thread, - struct ImagePool *pool, - const bool skip_load_image, - const bool texnode_preview) +/** + * \param pool: Thread pool, may be NULL. + * + * \return True if the texture has color, otherwise false. + */ +bool RE_texture_evaluate(const MTex *mtex, + const float vec[3], + const int thread, + struct ImagePool *pool, + const bool skip_load_image, + const bool texnode_preview, + /* Return arguments. */ + float *r_intensity, + float r_rgba[4]) { Tex *tex; TexResult texr; @@ -1796,11 +1795,11 @@ int externtex(const MTex *mtex, texr.tb = mtex->b; } - *tin = texr.tin; - *tr = texr.tr; - *tg = texr.tg; - *tb = texr.tb; - *ta = texr.ta; + *r_intensity = texr.tin; + r_rgba[0] = texr.tr; + r_rgba[1] = texr.tg; + r_rgba[2] = texr.tb; + r_rgba[3] = texr.ta; return (rgb != 0); } diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 90ff7bb8f85..22fd55cd49a 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -153,8 +153,8 @@ endif() if(WITH_HEADLESS) add_definitions(-DWITH_HEADLESS) -elseif(WITH_X11) - add_definitions(-DWITH_X11) +elseif(WITH_GHOST_X11) + add_definitions(-DWITH_GHOST_X11) endif() if(WITH_PYTHON) diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 2f540262a66..21a6088bfe9 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -617,7 +617,7 @@ int WM_gesture_lasso_modal(struct bContext *C, struct wmOperator *op, const stru void WM_gesture_lasso_cancel(struct bContext *C, struct wmOperator *op); const int (*WM_gesture_lasso_path_to_array(struct bContext *C, struct wmOperator *op, - int *mcords_tot))[2]; + int *mcoords_len))[2]; int WM_gesture_straightline_invoke(struct bContext *C, struct wmOperator *op, const struct wmEvent *event); @@ -872,6 +872,7 @@ void WM_generic_user_data_free(struct wmGenericUserData *user_data); bool WM_xr_session_exists(const wmXrData *xr); bool WM_xr_session_is_ready(const wmXrData *xr); struct wmXrSessionState *WM_xr_session_state_handle_get(const wmXrData *xr); +void WM_xr_session_base_pose_reset(wmXrData *xr); bool WM_xr_session_state_viewer_pose_location_get(const wmXrData *xr, float r_location[3]); bool WM_xr_session_state_viewer_pose_rotation_get(const wmXrData *xr, float r_rotation[4]); bool WM_xr_session_state_viewer_pose_matrix_info_get(const wmXrData *xr, diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 8b8c1b90dc9..560581a56e2 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -3170,9 +3170,9 @@ void wm_event_do_handlers(bContext *C) } if (is_playing_sound == 0) { - const float time = BKE_sound_sync_scene(scene_eval); + const double time = BKE_sound_sync_scene(scene_eval); if (isfinite(time)) { - int ncfra = time * (float)FPS + 0.5f; + int ncfra = time * FPS + 0.5; if (ncfra != scene->r.cfra) { scene->r.cfra = ncfra; ED_update_for_newframe(CTX_data_main(C), depsgraph); diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c index 71ae44297e7..9aa401722b7 100644 --- a/source/blender/windowmanager/intern/wm_gesture.c +++ b/source/blender/windowmanager/intern/wm_gesture.c @@ -320,24 +320,24 @@ static void draw_filled_lasso_px_cb(int x, int x_end, int y, void *user_data) static void draw_filled_lasso(wmGesture *gt) { const short *lasso = (short *)gt->customdata; - const int tot = gt->points; - int(*moves)[2] = MEM_mallocN(sizeof(*moves) * (tot + 1), __func__); + const int mcoords_len = gt->points; + int(*mcoords)[2] = MEM_mallocN(sizeof(*mcoords) * (mcoords_len + 1), __func__); int i; rcti rect; float red[4] = {1.0f, 0.0f, 0.0f, 0.0f}; - for (i = 0; i < tot; i++, lasso += 2) { - moves[i][0] = lasso[0]; - moves[i][1] = lasso[1]; + for (i = 0; i < mcoords_len; i++, lasso += 2) { + mcoords[i][0] = lasso[0]; + mcoords[i][1] = lasso[1]; } - BLI_lasso_boundbox(&rect, (const int(*)[2])moves, tot); + BLI_lasso_boundbox(&rect, (const int(*)[2])mcoords, mcoords_len); BLI_rcti_translate(&rect, gt->winrct.xmin, gt->winrct.ymin); BLI_rcti_isect(>->winrct, &rect, &rect); BLI_rcti_translate(&rect, -gt->winrct.xmin, -gt->winrct.ymin); - /* highly unlikely this will fail, but could crash if (tot == 0) */ + /* Highly unlikely this will fail, but could crash if (mcoords_len == 0). */ if (BLI_rcti_is_empty(&rect) == false) { const int w = BLI_rcti_size_x(&rect); const int h = BLI_rcti_size_y(&rect); @@ -348,8 +348,8 @@ static void draw_filled_lasso(wmGesture *gt) rect.ymin, rect.xmax, rect.ymax, - (const int(*)[2])moves, - tot, + (const int(*)[2])mcoords, + mcoords_len, draw_filled_lasso_px_cb, &lasso_fill_data); @@ -390,7 +390,7 @@ static void draw_filled_lasso(wmGesture *gt) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } - MEM_freeN(moves); + MEM_freeN(mcoords); } static void wm_gesture_draw_lasso(wmGesture *gt, bool filled) diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index 11beb7d2fd5..9fb368a02b4 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -718,10 +718,10 @@ void WM_gesture_lines_cancel(bContext *C, wmOperator *op) */ const int (*WM_gesture_lasso_path_to_array(bContext *UNUSED(C), wmOperator *op, - int *mcords_tot))[2] + int *r_mcoords_len))[2] { PropertyRNA *prop = RNA_struct_find_property(op->ptr, "path"); - int(*mcords)[2] = NULL; + int(*mcoords)[2] = NULL; BLI_assert(prop != NULL); if (prop) { @@ -729,26 +729,26 @@ const int (*WM_gesture_lasso_path_to_array(bContext *UNUSED(C), if (len) { int i = 0; - mcords = MEM_mallocN(sizeof(int) * 2 * len, __func__); + mcoords = MEM_mallocN(sizeof(int[2]) * len, __func__); RNA_PROP_BEGIN (op->ptr, itemptr, prop) { float loc[2]; RNA_float_get_array(&itemptr, "loc", loc); - mcords[i][0] = (int)loc[0]; - mcords[i][1] = (int)loc[1]; + mcoords[i][0] = (int)loc[0]; + mcoords[i][1] = (int)loc[1]; i++; } RNA_PROP_END; } - *mcords_tot = len; + *r_mcoords_len = len; } else { - *mcords_tot = 0; + *r_mcoords_len = 0; } /* cast for 'const' */ - return (const int(*)[2])mcords; + return mcoords; } #if 0 diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 17d697840a0..a93d4c7bf37 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -44,6 +44,7 @@ #include "BLI_listbase.h" #include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_task.h" #include "BLI_threads.h" #include "BLI_timer.h" #include "BLI_utildefines.h" @@ -198,7 +199,7 @@ void WM_init_opengl(Main *bmain) opengl_is_init = true; } -static void sound_jack_sync_callback(Main *bmain, int mode, float time) +static void sound_jack_sync_callback(Main *bmain, int mode, double time) { /* Ugly: Blender doesn't like it when the animation is played back during rendering. */ if (G.is_rendering) { @@ -648,6 +649,7 @@ void WM_exit_ex(bContext *C, const bool do_python) DNA_sdna_current_free(); BLI_threadapi_exit(); + BLI_task_scheduler_exit(); /* No need to call this early, rather do it late so that other * pieces of Blender using sound may exit cleanly, see also T50676. */ diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index 84f099b0dbc..948e8d9fb74 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -317,7 +317,13 @@ static void playanim_toscreen( GPU_blend(true); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - imm_draw_box_checker_2d(offs_x, offs_y, offs_x + span_x, offs_y + span_y); + imm_draw_box_checker_2d_ex(offs_x, + offs_y, + offs_x + span_x, + offs_y + span_y, + (const float[4]){0.15, 0.15, 0.15, 1.0}, + (const float[4]){0.20, 0.20, 0.20, 1.0}, + 8); } IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 1e25d73a86d..080c2ee26c2 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -769,19 +769,6 @@ void wm_window_ghostwindows_ensure(wmWindowManager *wm) * in practice the window manager will likely move to the correct monitor */ wm_init_state.start_x = 0; wm_init_state.start_y = 0; - -#ifdef WITH_X11 /* X11 */ - /* X11, don't start maximized because we can't figure out the dimensions - * of a single display yet if there are multiple, due to lack of Xinerama - * handling in GHOST. */ - wm_init_state.size_x = min_ii(wm_init_state.size_x, WM_WIN_INIT_SIZE_X); - wm_init_state.size_y = min_ii(wm_init_state.size_y, WM_WIN_INIT_SIZE_Y); - /* pad */ - wm_init_state.start_x = WM_WIN_INIT_PAD; - wm_init_state.start_y = WM_WIN_INIT_PAD; - wm_init_state.size_x -= WM_WIN_INIT_PAD * 2; - wm_init_state.size_y -= WM_WIN_INIT_PAD * 2; -#endif } LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { @@ -807,6 +794,36 @@ void wm_window_ghostwindows_remove_invalid(bContext *C, wmWindowManager *wm) } } +/* Update window size and position based on data from GHOST window. */ +static bool wm_window_update_size_position(wmWindow *win) +{ + GHOST_RectangleHandle client_rect; + int l, t, r, b, scr_w, scr_h; + int sizex, sizey, posx, posy; + + client_rect = GHOST_GetClientBounds(win->ghostwin); + GHOST_GetRectangle(client_rect, &l, &t, &r, &b); + + GHOST_DisposeRectangle(client_rect); + + wm_get_desktopsize(&scr_w, &scr_h); + sizex = r - l; + sizey = b - t; + posx = l; + posy = scr_h - t - win->sizey; + + if (win->sizex != sizex || win->sizey != sizey || win->posx != posx || win->posy != posy) { + win->sizex = sizex; + win->sizey = sizey; + win->posx = posx; + win->posy = posy; + return true; + } + else { + return false; + } +} + /** * new window, no screen yet, but we open ghostwindow for it, * also gets the window level handlers @@ -941,12 +958,19 @@ wmWindow *WM_window_open_temp(bContext *C, ED_area_newspace(C, area, space_type, false); ED_screen_change(C, screen); - ED_screen_refresh(wm, win); /* test scale */ if (win->ghostwin) { + /* Set size in GHOST window and then update size and position from GHOST, + * in case they where changed by GHOST to fit the monitor/screen. */ wm_window_set_size(win, win->sizex, win->sizey); - wm_window_raise(win); + wm_window_update_size_position(win); + } + /* Refresh screen dimensions, after the effective window size is known. */ + ED_screen_refresh(wm, win); + + if (win->ghostwin) { + wm_window_raise(win); GHOST_SetTitle(win->ghostwin, title); return win; } @@ -1354,21 +1378,6 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr /* win32: gives undefined window size when minimized */ if (state != GHOST_kWindowStateMinimized) { - GHOST_RectangleHandle client_rect; - int l, t, r, b, scr_w, scr_h; - int sizex, sizey, posx, posy; - - client_rect = GHOST_GetClientBounds(win->ghostwin); - GHOST_GetRectangle(client_rect, &l, &t, &r, &b); - - GHOST_DisposeRectangle(client_rect); - - wm_get_desktopsize(&scr_w, &scr_h); - sizex = r - l; - sizey = b - t; - posx = l; - posy = scr_h - t - win->sizey; - /* * Ghost sometimes send size or move events when the window hasn't changed. * One case of this is using compiz on linux. To alleviate the problem @@ -1377,15 +1386,9 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr * It might be good to eventually do that at Ghost level, but that is for * another time. */ - if (win->sizex != sizex || win->sizey != sizey || win->posx != posx || - win->posy != posy) { + if (wm_window_update_size_position(win)) { const bScreen *screen = WM_window_get_active_screen(win); - win->sizex = sizex; - win->sizey = sizey; - win->posx = posx; - win->posy = posy; - /* debug prints */ if (G.debug & G_DEBUG_EVENTS) { const char *state_str; diff --git a/source/blender/windowmanager/wm_window.h b/source/blender/windowmanager/wm_window.h index ce9d79b8e59..45cfe1431d7 100644 --- a/source/blender/windowmanager/wm_window.h +++ b/source/blender/windowmanager/wm_window.h @@ -82,11 +82,4 @@ int wm_window_new_main_exec(bContext *C, struct wmOperator *op); void wm_test_autorun_warning(bContext *C); -/* Initial (unmaximized) size to start with for - * systems that can't find it for themselves (X11). - * Clamped by real desktop limits */ -#define WM_WIN_INIT_SIZE_X 1800 -#define WM_WIN_INIT_SIZE_Y 1000 -#define WM_WIN_INIT_PAD 40 - #endif /* __WM_WINDOW_H__ */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index b53ae45a29f..9b7e9a15948 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -34,11 +34,16 @@ typedef struct wmXrSessionState { float viewer_viewmat[4][4]; float focal_len; + /** Copy of XrSessionSettings.base_pose_ data to detect changes that need + * resetting to base pose. */ + char prev_base_pose_type; /* eXRSessionBasePoseType */ + Object *prev_base_pose_object; /** Copy of XrSessionSettings.flag created on the last draw call, stored to detect changes. */ int prev_settings_flag; /** Copy of wmXrDrawData.eye_position_ofs. */ float prev_eye_position_ofs[3]; + bool force_reset_to_base_pose; bool is_view_data_set; } wmXrSessionState; diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index dc228d1b18b..e9ff38c5a92 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -95,6 +95,11 @@ bool WM_xr_session_exists(const wmXrData *xr) return xr->runtime && xr->runtime->context && xr->runtime->session_state.is_started; } +void WM_xr_session_base_pose_reset(wmXrData *xr) +{ + xr->runtime->session_state.force_reset_to_base_pose = true; +} + /** * Check if the session is running, according to the OpenXR definition. */ @@ -154,6 +159,17 @@ static void wm_xr_session_draw_data_populate(wmXrData *xr_data, wm_xr_session_base_pose_calc(r_draw_data->scene, settings, &r_draw_data->base_pose); } +static bool wm_xr_session_draw_data_needs_reset_to_base_pose(const wmXrSessionState *state, + const XrSessionSettings *settings) +{ + if (state->force_reset_to_base_pose) { + return true; + } + return ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) && + ((state->prev_base_pose_type != settings->base_pose_type) || + (state->prev_base_pose_object != settings->base_pose_object)); +} + void wm_xr_session_draw_data_update(const wmXrSessionState *state, const XrSessionSettings *settings, const GHOST_XrDrawViewInfo *draw_view, @@ -166,7 +182,8 @@ void wm_xr_session_draw_data_update(const wmXrSessionState *state, /* Set the eye position offset, it's used to offset the base pose when changing positional * tracking. */ - if (!state->is_view_data_set) { + if (!state->is_view_data_set || + wm_xr_session_draw_data_needs_reset_to_base_pose(state, settings)) { /* Always use the exact base pose with no offset when starting the session. */ copy_v3_fl(draw_data->eye_position_ofs, 0.0f); } @@ -223,6 +240,8 @@ void wm_xr_session_state_update(const XrSessionSettings *settings, copy_v3_v3(state->prev_eye_position_ofs, draw_data->eye_position_ofs); state->prev_settings_flag = settings->flag; + state->prev_base_pose_type = settings->base_pose_type; + state->prev_base_pose_object = settings->base_pose_object; state->is_view_data_set = true; } diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 4b51f9738b3..b3e83b1412f 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -687,6 +687,17 @@ elseif(WIN32) ) endif() + if(WITH_WINDOWS_PDB) + if(WITH_WINDOWS_STRIPPED_PDB) + # Icky hack for older cmake from https://stackoverflow.com/a/21198501 + # $<CONFIG> will work in newer cmake but the version currently (3.12) + # on the buildbot does not support this endavour. + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_INSTALL_CONFIG_NAME}/blender_public.pdb DESTINATION . RENAME blender.pdb) + else() + install(FILES $<TARGET_PDB_FILE:blender> DESTINATION . RENAME blender.pdb) + endif() + endif() + if(WITH_PYTHON) string(REPLACE "." "" _PYTHON_VERSION_NO_DOTS ${PYTHON_VERSION}) @@ -803,7 +814,20 @@ elseif(WIN32) DESTINATION "." ) endif() - + if(WITH_TBB) + install( + FILES + ${LIBDIR}/tbb/lib/tbb.dll + DESTINATION "." + CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel + ) + install( + FILES + ${LIBDIR}/tbb/lib/debug/tbb_debug.dll + DESTINATION "." + CONFIGURATIONS Debug + ) + endif() if(WITH_TBB_MALLOC_PROXY) install( FILES @@ -1085,6 +1109,20 @@ endif() # the use of vcpkg if(WIN32) set_target_properties(blender PROPERTIES VS_GLOBAL_VcpkgEnabled "false") + set_target_properties(blender PROPERTIES + PDB_NAME "blender_private" + PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>") + if (WITH_WINDOWS_PDB AND WITH_WINDOWS_STRIPPED_PDB) + # This is slightly messy, but single target generators like ninja will not have the + # CMAKE_CFG_INTDIR variable and multitarget generators like msbuild will not have + # CMAKE_BUILD_TYPE. This can be simplified by target_link_options and the $<CONFIG> + # generator expression in newer cmake (2.13+) but until that time this fill have suffice. + if(CMAKE_BUILD_TYPE) + set_property(TARGET blender APPEND PROPERTY LINK_FLAGS " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/blender_public.pdb") + else() + set_property(TARGET blender APPEND PROPERTY LINK_FLAGS " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/blender_public.pdb") + endif() + endif() endif() # ----------------------------------------------------------------------------- diff --git a/source/creator/blender.map b/source/creator/blender.map index fda6c37b10d..7720ad2b56c 100644 --- a/source/creator/blender.map +++ b/source/creator/blender.map @@ -18,14 +18,12 @@ local: *cineon*; *COLLADA*; cu*; - decodeInstruction; *default_error_condition*; *dpx*; *embree*; ff_*; fftw*; FLAC*; - ForceStackAlign; FT_*; *GeneratedSaxParser*; *google*; @@ -40,14 +38,11 @@ local: jack_*; jpeg_*; jsimd**; - _Jv_RegisterClasses; lame_*; *llvm*; *LLVM*; *MathML*; *mkldnn*; - Name; - NumNamedVarArgParams; oc_*; ogg*; *oidn*; @@ -72,8 +67,46 @@ local: vp9*; vpx*; x264_*; - X86CompilationCallback*; xml*; xvid*; *YAML*; + + /* LLVM symbols not in the LLVM namespace that can conflict with LLVM usage + * in OpenGL and OpenCL drivers. */ + decodeInstruction; + EnableHotColdSplit; + EnableIPRA; + EnableOrderFileInstrumentation; + EnableVPlanNativePath; + EnableVPlanPredication; + FlattenedProfileUsed; + ForceStackAlign; + ForceSummaryEdgesCold; + FSEC; + __jit_debug_descriptor; + __jit_debug_register_code; + _Jv_RegisterClasses; + MachineRegionInfoPassID; + MemOPSizeLarge; + MemOPSizeRange; + MISchedPostRA; + ModuleSummaryDotFile; + __morestack; + Name; + NumNamedVarArgParams; + PGOViewCounts; + PrintBlockFreqFuncName; + PrintBranchProbFuncName; + ProfileLikelyProb; + StartAfterOptName; + StartBeforeOptName; + StaticLikelyProb; + StopAfterOptName; + StopBeforeOptName; + UseDbgAddr; + ViewBlockFreqFuncName; + ViewBlockLayoutWithBFI; + ViewHotFreqPercent; + WriteRelBFToSummary; + X86CompilationCallback*; }; diff --git a/source/creator/creator.c b/source/creator/creator.c index 6c4af7be95e..ea64184c826 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -43,6 +43,7 @@ #include "BLI_args.h" #include "BLI_string.h" #include "BLI_system.h" +#include "BLI_task.h" #include "BLI_threads.h" #include "BLI_utildefines.h" @@ -401,6 +402,9 @@ int main(int argc, G.factory_startup = true; #endif + /* After parsing number of threads argument. */ + BLI_task_scheduler_init(); + #ifdef WITH_FFMPEG IMB_ffmpeg_init(); #endif diff --git a/source/creator/creator_signals.c b/source/creator/creator_signals.c index 7f7a03f0465..dbf947a86fd 100644 --- a/source/creator/creator_signals.c +++ b/source/creator/creator_signals.c @@ -190,97 +190,25 @@ static void sig_handle_crash(int signum) } # ifdef WIN32 -LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS *ExceptionInfo) +extern LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS *ExceptionInfo) { - switch (ExceptionInfo->ExceptionRecord->ExceptionCode) { - case EXCEPTION_ACCESS_VIOLATION: - fputs("Error : EXCEPTION_ACCESS_VIOLATION\n", stderr); - break; - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - fputs("Error : EXCEPTION_ARRAY_BOUNDS_EXCEEDED\n", stderr); - break; - case EXCEPTION_BREAKPOINT: - fputs("Error : EXCEPTION_BREAKPOINT\n", stderr); - break; - case EXCEPTION_DATATYPE_MISALIGNMENT: - fputs("Error : EXCEPTION_DATATYPE_MISALIGNMENT\n", stderr); - break; - case EXCEPTION_FLT_DENORMAL_OPERAND: - fputs("Error : EXCEPTION_FLT_DENORMAL_OPERAND\n", stderr); - break; - case EXCEPTION_FLT_DIVIDE_BY_ZERO: - fputs("Error : EXCEPTION_FLT_DIVIDE_BY_ZERO\n", stderr); - break; - case EXCEPTION_FLT_INEXACT_RESULT: - fputs("Error : EXCEPTION_FLT_INEXACT_RESULT\n", stderr); - break; - case EXCEPTION_FLT_INVALID_OPERATION: - fputs("Error : EXCEPTION_FLT_INVALID_OPERATION\n", stderr); - break; - case EXCEPTION_FLT_OVERFLOW: - fputs("Error : EXCEPTION_FLT_OVERFLOW\n", stderr); - break; - case EXCEPTION_FLT_STACK_CHECK: - fputs("Error : EXCEPTION_FLT_STACK_CHECK\n", stderr); - break; - case EXCEPTION_FLT_UNDERFLOW: - fputs("Error : EXCEPTION_FLT_UNDERFLOW\n", stderr); - break; - case EXCEPTION_ILLEGAL_INSTRUCTION: - fputs("Error : EXCEPTION_ILLEGAL_INSTRUCTION\n", stderr); - break; - case EXCEPTION_IN_PAGE_ERROR: - fputs("Error : EXCEPTION_IN_PAGE_ERROR\n", stderr); - break; - case EXCEPTION_INT_DIVIDE_BY_ZERO: - fputs("Error : EXCEPTION_INT_DIVIDE_BY_ZERO\n", stderr); - break; - case EXCEPTION_INT_OVERFLOW: - fputs("Error : EXCEPTION_INT_OVERFLOW\n", stderr); - break; - case EXCEPTION_INVALID_DISPOSITION: - fputs("Error : EXCEPTION_INVALID_DISPOSITION\n", stderr); - break; - case EXCEPTION_NONCONTINUABLE_EXCEPTION: - fputs("Error : EXCEPTION_NONCONTINUABLE_EXCEPTION\n", stderr); - break; - case EXCEPTION_PRIV_INSTRUCTION: - fputs("Error : EXCEPTION_PRIV_INSTRUCTION\n", stderr); - break; - case EXCEPTION_SINGLE_STEP: - fputs("Error : EXCEPTION_SINGLE_STEP\n", stderr); - break; - case EXCEPTION_STACK_OVERFLOW: - fputs("Error : EXCEPTION_STACK_OVERFLOW\n", stderr); - break; - default: - fputs("Error : Unrecognized Exception\n", stderr); - break; - } - - fflush(stderr); - - /* If this is a stack overflow then we can't walk the stack, so just show + /* If this is a stack overflow then we can't walk the stack, so just try to show * where the error happened */ - if (EXCEPTION_STACK_OVERFLOW != ExceptionInfo->ExceptionRecord->ExceptionCode) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) { HMODULE mod; CHAR modulename[MAX_PATH]; LPVOID address = ExceptionInfo->ExceptionRecord->ExceptionAddress; - + fprintf(stderr, "Error : EXCEPTION_STACK_OVERFLOW\n"); fprintf(stderr, "Address : 0x%p\n", address); if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, address, &mod)) { if (GetModuleFileName(mod, modulename, MAX_PATH)) { fprintf(stderr, "Module : %s\n", modulename); } } - - fflush(stderr); - -# ifdef NDEBUG - TerminateProcess(GetCurrentProcess(), SIGSEGV); -# else + } + else { + BLI_windows_handle_exception(ExceptionInfo); sig_handle_crash(SIGSEGV); -# endif } return EXCEPTION_EXECUTE_HANDLER; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a3d4b2a501e..80d749c7040 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -39,7 +39,8 @@ unset(_default_test_python_exe) # set(TEST_BLENDER_EXE valgrind --track-origins=yes --error-limit=no ${TEST_BLENDER_EXE}) # Standard Blender arguments for running tests. -set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup) +# Specify exit code so that if a Python script error happens, the test fails. +set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --python-exit-code 1) # Python CTests if(WITH_BLENDER AND WITH_PYTHON) diff --git a/tests/gtests/blenkernel/BKE_armature_test.cc b/tests/gtests/blenkernel/BKE_armature_test.cc index 0f390a471d7..ed6045081d4 100644 --- a/tests/gtests/blenkernel/BKE_armature_test.cc +++ b/tests/gtests/blenkernel/BKE_armature_test.cc @@ -23,7 +23,7 @@ #include "testing/testing.h" -static const float FLOAT_EPSILON = 1e-7; +static const float FLOAT_EPSILON = 1.2e-7; TEST(mat3_vec_to_roll, UnitMatrix) { diff --git a/tests/gtests/blenkernel/BKE_fcurve_test.cc b/tests/gtests/blenkernel/BKE_fcurve_test.cc new file mode 100644 index 00000000000..d28c5075bd8 --- /dev/null +++ b/tests/gtests/blenkernel/BKE_fcurve_test.cc @@ -0,0 +1,211 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 by Blender Foundation. + */ +#include "testing/testing.h" + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "BKE_fcurve.h" + +#include "ED_keyframing.h" + +#include "DNA_anim_types.h" +} + +// Epsilon for floating point comparisons. +static const float EPSILON = 1e-7f; + +TEST(evaluate_fcurve, EmptyFCurve) +{ + FCurve *fcu = static_cast<FCurve *>(MEM_callocN(sizeof(FCurve), "FCurve")); + EXPECT_EQ(evaluate_fcurve(fcu, 47.0f), 0.0f); + free_fcurve(fcu); +} + +TEST(evaluate_fcurve, OnKeys) +{ + FCurve *fcu = static_cast<FCurve *>(MEM_callocN(sizeof(FCurve), "FCurve")); + + insert_vert_fcurve(fcu, 1.0f, 7.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF); + insert_vert_fcurve(fcu, 2.0f, 13.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF); + insert_vert_fcurve(fcu, 3.0f, 19.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF); + + EXPECT_NEAR(evaluate_fcurve(fcu, 1.0f), 7.0f, EPSILON); // hits 'on or before first' function + EXPECT_NEAR(evaluate_fcurve(fcu, 2.0f), 13.0f, EPSILON); // hits 'between' function + EXPECT_NEAR(evaluate_fcurve(fcu, 3.0f), 19.0f, EPSILON); // hits 'on or after last' function + + /* Also test within a specific time epsilon of the keys, as this was an issue in T39207. + * This epsilon is just slightly smaller than the epsilon given to binarysearch_bezt_index_ex() + * in fcurve_eval_between_keyframes(), so it should hit the "exact" code path. */ + float time_epsilon = 0.00008f; + EXPECT_NEAR(evaluate_fcurve(fcu, 2.0f - time_epsilon), 13.0f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, 2.0f + time_epsilon), 13.0f, EPSILON); + + free_fcurve(fcu); +} + +TEST(evaluate_fcurve, InterpolationConstant) +{ + FCurve *fcu = static_cast<FCurve *>(MEM_callocN(sizeof(FCurve), "FCurve")); + + EXPECT_EQ(insert_vert_fcurve(fcu, 1.0f, 7.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF), 0); + EXPECT_EQ(insert_vert_fcurve(fcu, 2.0f, 13.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF), 1); + + fcu->bezt[0].ipo = BEZT_IPO_CONST; + fcu->bezt[1].ipo = BEZT_IPO_CONST; + + EXPECT_NEAR(evaluate_fcurve(fcu, 1.25f), 7.0f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, 1.50f), 7.0f, EPSILON); + + free_fcurve(fcu); +} + +TEST(evaluate_fcurve, InterpolationLinear) +{ + FCurve *fcu = static_cast<FCurve *>(MEM_callocN(sizeof(FCurve), "FCurve")); + + EXPECT_EQ(insert_vert_fcurve(fcu, 1.0f, 7.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF), 0); + EXPECT_EQ(insert_vert_fcurve(fcu, 2.0f, 13.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF), 1); + + fcu->bezt[0].ipo = BEZT_IPO_LIN; + fcu->bezt[1].ipo = BEZT_IPO_LIN; + + EXPECT_NEAR(evaluate_fcurve(fcu, 1.25f), 8.5f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, 1.50f), 10.0f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, 1.75f), 11.5f, EPSILON); + + free_fcurve(fcu); +} + +TEST(evaluate_fcurve, InterpolationBezier) +{ + FCurve *fcu = static_cast<FCurve *>(MEM_callocN(sizeof(FCurve), "FCurve")); + + EXPECT_EQ(insert_vert_fcurve(fcu, 1.0f, 7.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF), 0); + EXPECT_EQ(insert_vert_fcurve(fcu, 2.0f, 13.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF), 1); + + EXPECT_EQ(fcu->bezt[0].ipo, BEZT_IPO_BEZ); + EXPECT_EQ(fcu->bezt[1].ipo, BEZT_IPO_BEZ); + + // Test with default handles. + EXPECT_NEAR(evaluate_fcurve(fcu, 1.25f), 7.8297067f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, 1.50f), 10.0f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, 1.75f), 12.170294f, EPSILON); + + // Test with modified handles. + fcu->bezt[0].vec[0][0] = 0.71855f; // left handle X + fcu->bezt[0].vec[0][1] = 6.22482f; // left handle Y + fcu->bezt[0].vec[2][0] = 1.35148f; // right handle X + fcu->bezt[0].vec[2][1] = 7.96806f; // right handle Y + + fcu->bezt[1].vec[0][0] = 1.66667f; // left handle X + fcu->bezt[1].vec[0][1] = 10.4136f; // left handle Y + fcu->bezt[1].vec[2][0] = 2.33333f; // right handle X + fcu->bezt[1].vec[2][1] = 15.5864f; // right handle Y + + EXPECT_NEAR(evaluate_fcurve(fcu, 1.25f), 7.945497f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, 1.50f), 9.3495407f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, 1.75f), 11.088551f, EPSILON); + + free_fcurve(fcu); +} + +TEST(evaluate_fcurve, InterpolationBounce) +{ + FCurve *fcu = static_cast<FCurve *>(MEM_callocN(sizeof(FCurve), "FCurve")); + + EXPECT_EQ(insert_vert_fcurve(fcu, 1.0f, 7.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF), 0); + EXPECT_EQ(insert_vert_fcurve(fcu, 2.0f, 13.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF), 1); + + fcu->bezt[0].ipo = BEZT_IPO_BOUNCE; + fcu->bezt[1].ipo = BEZT_IPO_BOUNCE; + + fcu->bezt[0].easing = BEZT_IPO_EASE_IN; + fcu->bezt[1].easing = BEZT_IPO_EASE_AUTO; + + EXPECT_NEAR(evaluate_fcurve(fcu, 1.4f), 8.3649998f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, 1.5f), 8.4062500f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, 1.8f), 11.184999f, EPSILON); + + free_fcurve(fcu); +} + +TEST(evaluate_fcurve, ExtrapolationLinearKeys) +{ + FCurve *fcu = static_cast<FCurve *>(MEM_callocN(sizeof(FCurve), "FCurve")); + + EXPECT_EQ(insert_vert_fcurve(fcu, 1.0f, 7.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF), 0); + EXPECT_EQ(insert_vert_fcurve(fcu, 2.0f, 13.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF), 1); + fcu->bezt[0].ipo = BEZT_IPO_LIN; + fcu->bezt[1].ipo = BEZT_IPO_LIN; + + fcu->extend = FCURVE_EXTRAPOLATE_LINEAR; + // Before first keyframe. + EXPECT_NEAR(evaluate_fcurve(fcu, 0.75f), 5.5f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, 0.50f), 4.0f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, -1.50f), -8.0f, EPSILON); + // After last keyframe. + EXPECT_NEAR(evaluate_fcurve(fcu, 2.75f), 17.5f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, 3.50f), 22.0f, EPSILON); + + fcu->extend = FCURVE_EXTRAPOLATE_CONSTANT; + // Before first keyframe. + EXPECT_NEAR(evaluate_fcurve(fcu, 0.75f), 7.0f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, -1.50f), 7.0f, EPSILON); + // After last keyframe. + EXPECT_NEAR(evaluate_fcurve(fcu, 2.75f), 13.0f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, 3.50f), 13.0f, EPSILON); + + free_fcurve(fcu); +} + +TEST(evaluate_fcurve, ExtrapolationBezierKeys) +{ + FCurve *fcu = static_cast<FCurve *>(MEM_callocN(sizeof(FCurve), "FCurve")); + + EXPECT_EQ(insert_vert_fcurve(fcu, 1.0f, 7.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF), 0); + EXPECT_EQ(insert_vert_fcurve(fcu, 2.0f, 13.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NO_USERPREF), 1); + + fcu->bezt[0].vec[0][0] = 0.71855f; // left handle X + fcu->bezt[0].vec[0][1] = 6.22482f; // left handle Y + fcu->bezt[0].vec[2][0] = 1.35148f; // right handle X + fcu->bezt[0].vec[2][1] = 7.96806f; // right handle Y + + fcu->bezt[1].vec[0][0] = 1.66667f; // left handle X + fcu->bezt[1].vec[0][1] = 10.4136f; // left handle Y + fcu->bezt[1].vec[2][0] = 2.33333f; // right handle X + fcu->bezt[1].vec[2][1] = 15.5864f; // right handle Y + + fcu->extend = FCURVE_EXTRAPOLATE_LINEAR; + // Before first keyframe. + EXPECT_NEAR(evaluate_fcurve(fcu, 0.75f), 6.3114409f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, -0.50f), 2.8686447f, EPSILON); + // After last keyframe. + EXPECT_NEAR(evaluate_fcurve(fcu, 2.75f), 18.81946f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, 3.50f), 24.63892f, EPSILON); + + fcu->extend = FCURVE_EXTRAPOLATE_CONSTANT; + // Before first keyframe. + EXPECT_NEAR(evaluate_fcurve(fcu, 0.75f), 7.0f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, -1.50f), 7.0f, EPSILON); + // After last keyframe. + EXPECT_NEAR(evaluate_fcurve(fcu, 2.75f), 13.0f, EPSILON); + EXPECT_NEAR(evaluate_fcurve(fcu, 3.50f), 13.0f, EPSILON); + + free_fcurve(fcu); +} diff --git a/tests/gtests/blenkernel/CMakeLists.txt b/tests/gtests/blenkernel/CMakeLists.txt index bbc274c5ddb..5cf4c7a27af 100644 --- a/tests/gtests/blenkernel/CMakeLists.txt +++ b/tests/gtests/blenkernel/CMakeLists.txt @@ -23,7 +23,9 @@ set(INC .. ../../../source/blender/blenkernel ../../../source/blender/blenlib + ../../../source/blender/editors/include ../../../source/blender/makesdna + ../../../source/blender/makesrna ../../../intern/guardedalloc ../../../intern/atomic ) @@ -39,3 +41,4 @@ if(WITH_BUILDINFO) endif() BLENDER_TEST(BKE_armature "bf_blenloader;bf_blenkernel;bf_blenlib;${BUILDINFO}") +BLENDER_TEST(BKE_fcurve "bf_blenloader;bf_blenkernel;bf_editor_animation;${BUILDINFO}") diff --git a/tests/gtests/blenlib/BLI_linklist_lockfree_test.cc b/tests/gtests/blenlib/BLI_linklist_lockfree_test.cc index d1176527cb5..023d02e5075 100644 --- a/tests/gtests/blenlib/BLI_linklist_lockfree_test.cc +++ b/tests/gtests/blenlib/BLI_linklist_lockfree_test.cc @@ -63,7 +63,7 @@ struct IndexedNode { int index; }; -void concurrent_insert(TaskPool *__restrict pool, void *taskdata, int /*threadid*/) +void concurrent_insert(TaskPool *__restrict pool, void *taskdata) { LockfreeLinkList *list = (LockfreeLinkList *)BLI_task_pool_user_data(pool); CHECK_NOTNULL(list); @@ -76,14 +76,12 @@ void concurrent_insert(TaskPool *__restrict pool, void *taskdata, int /*threadid TEST(LockfreeLinkList, InsertMultipleConcurrent) { - static const int num_threads = 512; static const int num_nodes = 655360; /* Initialize list. */ LockfreeLinkList list; BLI_linklist_lockfree_init(&list); /* Initialize task scheduler and pool. */ - TaskScheduler *scheduler = BLI_task_scheduler_create(num_threads); - TaskPool *pool = BLI_task_pool_create_suspended(scheduler, &list, TASK_PRIORITY_HIGH); + TaskPool *pool = BLI_task_pool_create_suspended(&list, TASK_PRIORITY_HIGH); /* Push tasks to the pool. */ for (int i = 0; i < num_nodes; ++i) { BLI_task_pool_push(pool, concurrent_insert, POINTER_FROM_INT(i), false, NULL); @@ -112,5 +110,4 @@ TEST(LockfreeLinkList, InsertMultipleConcurrent) /* Cleanup data. */ BLI_linklist_lockfree_free(&list, MEM_freeN); BLI_task_pool_free(pool); - BLI_task_scheduler_free(scheduler); } diff --git a/tests/gtests/blenlib/BLI_map_test.cc b/tests/gtests/blenlib/BLI_map_test.cc index 2d052635c88..5a19216fa7c 100644 --- a/tests/gtests/blenlib/BLI_map_test.cc +++ b/tests/gtests/blenlib/BLI_map_test.cc @@ -9,16 +9,20 @@ TEST(map, DefaultConstructor) { IntFloatMap map; EXPECT_EQ(map.size(), 0); + EXPECT_TRUE(map.is_empty()); } TEST(map, AddIncreasesSize) { IntFloatMap map; EXPECT_EQ(map.size(), 0); + EXPECT_TRUE(map.is_empty()); map.add(2, 5.0f); EXPECT_EQ(map.size(), 1); + EXPECT_FALSE(map.is_empty()); map.add(6, 2.0f); EXPECT_EQ(map.size(), 2); + EXPECT_FALSE(map.is_empty()); } TEST(map, Contains) @@ -209,6 +213,17 @@ TEST(map, AddOverride) EXPECT_EQ(map.lookup(3), 7.0f); } +TEST(map, LookupOrAddDefault) +{ + IntFloatMap map; + map.lookup_or_add_default(3) = 6; + EXPECT_EQ(map.lookup(3), 6); + map.lookup_or_add_default(5) = 2; + EXPECT_EQ(map.lookup(5), 2); + map.lookup_or_add_default(3) += 4; + EXPECT_EQ(map.lookup(3), 10); +} + TEST(map, MoveConstructorSmall) { IntFloatMap map1; diff --git a/tests/gtests/blenlib/BLI_set_test.cc b/tests/gtests/blenlib/BLI_set_test.cc index dc163e752d8..90c052d7d2b 100644 --- a/tests/gtests/blenlib/BLI_set_test.cc +++ b/tests/gtests/blenlib/BLI_set_test.cc @@ -10,6 +10,7 @@ TEST(set, Defaultconstructor) { IntSet set; EXPECT_EQ(set.size(), 0); + EXPECT_TRUE(set.is_empty()); } TEST(set, ContainsNotExistant) @@ -22,8 +23,10 @@ TEST(set, ContainsExistant) { IntSet set; EXPECT_FALSE(set.contains(5)); + EXPECT_TRUE(set.is_empty()); set.add(5); EXPECT_TRUE(set.contains(5)); + EXPECT_FALSE(set.is_empty()); } TEST(set, AddMany) @@ -191,3 +194,11 @@ TEST(set, UniquePtrValues) EXPECT_EQ(set.size(), 3); } + +TEST(set, Clear) +{ + Set<int> set = {3, 4, 6, 7}; + EXPECT_EQ(set.size(), 4); + set.clear(); + EXPECT_EQ(set.size(), 0); +} diff --git a/tests/gtests/blenlib/BLI_task_test.cc b/tests/gtests/blenlib/BLI_task_test.cc index fe0f481d469..ed300b3f238 100644 --- a/tests/gtests/blenlib/BLI_task_test.cc +++ b/tests/gtests/blenlib/BLI_task_test.cc @@ -54,7 +54,7 @@ TEST(task, RangeIter) BLI_task_parallel_range(0, NUM_ITEMS, data, task_range_iter_func, &settings); - /* Those checks should ensure us all items of the listbase were processed once, and only once - + /* Those checks should ensure us all items of the listbase were processed once, and only once * as expected. */ int expected_sum = 0; diff --git a/tests/gtests/blenlib/BLI_vector_set_test.cc b/tests/gtests/blenlib/BLI_vector_set_test.cc index 816d5d653a5..bfdd47ccb13 100644 --- a/tests/gtests/blenlib/BLI_vector_set_test.cc +++ b/tests/gtests/blenlib/BLI_vector_set_test.cc @@ -8,6 +8,7 @@ TEST(vector_set, DefaultConstructor) { IntVectorSet set; EXPECT_EQ(set.size(), 0); + EXPECT_TRUE(set.is_empty()); } TEST(vector_set, InitializerListConstructor_WithoutDuplicates) @@ -70,8 +71,10 @@ TEST(vector_set, MoveAssignment) TEST(vector_set, AddNewIncreasesSize) { IntVectorSet set; + EXPECT_TRUE(set.is_empty()); EXPECT_EQ(set.size(), 0); set.add(5); + EXPECT_FALSE(set.is_empty()); EXPECT_EQ(set.size(), 1); } diff --git a/tests/gtests/blenlib/BLI_vector_test.cc b/tests/gtests/blenlib/BLI_vector_test.cc index 1585b77675b..e5a1ca2b200 100644 --- a/tests/gtests/blenlib/BLI_vector_test.cc +++ b/tests/gtests/blenlib/BLI_vector_test.cc @@ -345,14 +345,14 @@ TEST(vector, RemoveLast) EXPECT_EQ(vec.size(), 0); } -TEST(vector, Empty) +TEST(vector, IsEmpty) { IntVector vec; - EXPECT_TRUE(vec.empty()); + EXPECT_TRUE(vec.is_empty()); vec.append(1); - EXPECT_FALSE(vec.empty()); + EXPECT_FALSE(vec.is_empty()); vec.remove_last(); - EXPECT_TRUE(vec.empty()); + EXPECT_TRUE(vec.is_empty()); } TEST(vector, RemoveReorder) @@ -368,7 +368,7 @@ TEST(vector, RemoveReorder) vec.remove_and_reorder(0); EXPECT_EQ(vec[0], 7); vec.remove_and_reorder(0); - EXPECT_TRUE(vec.empty()); + EXPECT_TRUE(vec.is_empty()); } TEST(vector, RemoveFirstOccurrenceAndReorder) diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 753bc7c5b91..a3df01fdbe2 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -185,6 +185,22 @@ add_blender_test( ) add_blender_test( + physics_cloth + ${TEST_SRC_DIR}/physics/cloth_test.blend + --python ${TEST_PYTHON_DIR}/physics_cloth.py + -- + --run-all-tests +) + +add_blender_test( + physics_softbody + ${TEST_SRC_DIR}/physics/softbody_test.blend + --python ${TEST_PYTHON_DIR}/physics_softbody.py + -- + --run-all-tests +) + +add_blender_test( constraints --python ${CMAKE_CURRENT_LIST_DIR}/bl_constraints.py -- diff --git a/tests/python/bevel_operator.py b/tests/python/bevel_operator.py index 3cdbeb9300b..884bd356b96 100644 --- a/tests/python/bevel_operator.py +++ b/tests/python/bevel_operator.py @@ -176,9 +176,4 @@ def main(): if __name__ == "__main__": - try: - main() - except: - import traceback - traceback.print_exc() - sys.exit(1) + main() diff --git a/tests/python/bl_alembic_io_test.py b/tests/python/bl_alembic_io_test.py index 2786a2db4d7..b9eca3057e7 100644 --- a/tests/python/bl_alembic_io_test.py +++ b/tests/python/bl_alembic_io_test.py @@ -375,12 +375,4 @@ def main(): if __name__ == "__main__": - import traceback - # So a python error exits Blender itself too - try: - main() - except SystemExit: - raise - except: - traceback.print_exc() - sys.exit(1) + main() diff --git a/tests/python/bl_blendfile_io.py b/tests/python/bl_blendfile_io.py index 0b055b9d46a..ab06e313566 100644 --- a/tests/python/bl_blendfile_io.py +++ b/tests/python/bl_blendfile_io.py @@ -77,9 +77,4 @@ def main(): if __name__ == '__main__': import sys sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []) - try: - main() - except: - import traceback - traceback.print_exc() - sys.exit(1) + main() diff --git a/tests/python/bl_blendfile_liblink.py b/tests/python/bl_blendfile_liblink.py index 7d93d7c8455..d1cc7efc7fd 100644 --- a/tests/python/bl_blendfile_liblink.py +++ b/tests/python/bl_blendfile_liblink.py @@ -75,9 +75,4 @@ def main(): if __name__ == '__main__': import sys sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []) - try: - main() - except: - import traceback - traceback.print_exc() - sys.exit(1) + main() diff --git a/tests/python/bl_bundled_modules.py b/tests/python/bl_bundled_modules.py index 3ef5040af01..d3fe2861d9e 100644 --- a/tests/python/bl_bundled_modules.py +++ b/tests/python/bl_bundled_modules.py @@ -21,7 +21,7 @@ # Test that modules we ship with our Python installation are available import bz2 -import cffi +import ctypes import lzma import numpy import sqlite3 diff --git a/tests/python/bl_constraints.py b/tests/python/bl_constraints.py index 13a431541bc..9fce8acc84e 100644 --- a/tests/python/bl_constraints.py +++ b/tests/python/bl_constraints.py @@ -255,12 +255,4 @@ def main(): if __name__ == "__main__": - import traceback - # So a python error exits Blender itself too - try: - main() - except SystemExit: - raise - except: - traceback.print_exc() - sys.exit(1) + main() diff --git a/tests/python/bl_load_addons.py b/tests/python/bl_load_addons.py index f0c2f3f7fdf..01f0b4d72d8 100644 --- a/tests/python/bl_load_addons.py +++ b/tests/python/bl_load_addons.py @@ -144,11 +144,4 @@ def main(): if __name__ == "__main__": - - # So a python error exits(1) - try: - main() - except: - import traceback - traceback.print_exc() - sys.exit(1) + main() diff --git a/tests/python/bl_load_py_modules.py b/tests/python/bl_load_py_modules.py index 5d1a5dd8ee0..c6ad53b1f74 100644 --- a/tests/python/bl_load_py_modules.py +++ b/tests/python/bl_load_py_modules.py @@ -234,10 +234,4 @@ def main(): if __name__ == "__main__": - # So a python error exits(1) - try: - main() - except: - import traceback - traceback.print_exc() - sys.exit(1) + main() diff --git a/tests/python/bl_mesh_modifiers.py b/tests/python/bl_mesh_modifiers.py index 80f810ffad8..746e7a183a3 100644 --- a/tests/python/bl_mesh_modifiers.py +++ b/tests/python/bl_mesh_modifiers.py @@ -842,17 +842,7 @@ if __name__ == "__main__": print("Load Handler:", bpy.data.filepath) if load_handler.first is False: bpy.app.handlers.scene_update_post.remove(load_handler) - try: - main() - import sys - sys.exit(0) - except: - import traceback - traceback.print_exc() - - # import sys - # sys.exit(1) # comment to debug - + main() else: load_handler.first = False diff --git a/tests/python/bl_mesh_validate.py b/tests/python/bl_mesh_validate.py index 47a5e5efe47..8c5d914f92a 100644 --- a/tests/python/bl_mesh_validate.py +++ b/tests/python/bl_mesh_validate.py @@ -152,10 +152,4 @@ def main(): if __name__ == "__main__": - # So a python error exits(1) - try: - main() - except: - import traceback - traceback.print_exc() - sys.exit(1) + main() diff --git a/tests/python/bl_pyapi_idprop_datablock.py b/tests/python/bl_pyapi_idprop_datablock.py index 648b63d1637..44fec6a9043 100644 --- a/tests/python/bl_pyapi_idprop_datablock.py +++ b/tests/python/bl_pyapi_idprop_datablock.py @@ -20,7 +20,6 @@ import bpy import sys import os import tempfile -import traceback import inspect from bpy.types import UIList @@ -331,11 +330,4 @@ def main(): if __name__ == "__main__": - try: - main() - except: - import traceback - - traceback.print_exc() - sys.stderr.flush() - os._exit(1) + main() diff --git a/tests/python/bl_test.py b/tests/python/bl_test.py index 173d575a912..6315ffbfa9d 100644 --- a/tests/python/bl_test.py +++ b/tests/python/bl_test.py @@ -136,12 +136,7 @@ def main(): print(" Running: '%s'" % run) print(" MD5: '%s'!" % md5) - try: - result = eval(run) - except: - import traceback - traceback.print_exc() - sys.exit(1) + result = eval(run) if write_blend is not None: print(" Writing Blend: %s" % write_blend) @@ -188,10 +183,4 @@ def main(): if __name__ == "__main__": - # So a python error exits(1) - try: - main() - except: - import traceback - traceback.print_exc() - sys.exit(1) + main() diff --git a/tests/python/boolean_operator.py b/tests/python/boolean_operator.py index b05e60eea6c..5a674c35a47 100644 --- a/tests/python/boolean_operator.py +++ b/tests/python/boolean_operator.py @@ -60,9 +60,4 @@ def main(): if __name__ == "__main__": - try: - main() - except: - import traceback - traceback.print_exc() - sys.exit(1) + main() diff --git a/tests/python/modifiers.py b/tests/python/modifiers.py index 697cddc9ba2..5e032f658af 100644 --- a/tests/python/modifiers.py +++ b/tests/python/modifiers.py @@ -255,9 +255,4 @@ def main(): if __name__ == "__main__": - try: - main() - except: - import traceback - traceback.print_exc() - sys.exit(1) + main() diff --git a/tests/python/modules/mesh_test.py b/tests/python/modules/mesh_test.py index f188d998884..af0e78257d5 100644 --- a/tests/python/modules/mesh_test.py +++ b/tests/python/modules/mesh_test.py @@ -73,6 +73,28 @@ class ModifierSpec: " with parameters: " + str(self.modifier_parameters) +class PhysicsSpec: + """ + Holds one Physics modifier and its parameters. + """ + + def __init__(self, modifier_name: str, modifier_type: str, modifier_parameters: dict, frame_end: int): + """ + Constructs a physics spec. + :param modifier_name: str - name of object modifier, e.g. "Cloth" + :param modifier_type: str - type of object modifier, e.g. "CLOTH" + :param modifier_parameters: dict - {name : val} dictionary giving modifier parameters, e.g. {"quality" : 4} + :param frame_end:int - the last frame of the simulation at which it is baked + """ + self.modifier_name = modifier_name + self.modifier_type = modifier_type + self.modifier_parameters = modifier_parameters + self.frame_end = frame_end + + def __str__(self): + return "Physics Modifier: " + self.modifier_name + " of type " + self.modifier_type + \ + " with parameters: " + str(self.modifier_parameters) + " with frame end: " + str(self.frame_end) + class OperatorSpec: """ Holds one operator and its parameters. @@ -105,7 +127,7 @@ class MeshTest: the public method run_test(). """ - def __init__(self, test_object_name: str, expected_object_name: str, operations_stack=None, apply_modifiers=False): + def __init__(self, test_object_name: str, expected_object_name: str, operations_stack=None, apply_modifiers=False, threshold=None): """ Constructs a MeshTest object. Raises a KeyError if objects with names expected_object_name or test_object_name don't exist. @@ -125,6 +147,7 @@ class MeshTest: type(operation))) self.operations_stack = operations_stack self.apply_modifier = apply_modifiers + self.threshold = threshold self.verbose = os.environ.get("BLENDER_VERBOSE") is not None self.update = os.getenv('BLENDER_TEST_UPDATE') is not None @@ -235,6 +258,49 @@ class MeshTest: if self.apply_modifier: bpy.ops.object.modifier_apply(modifier=modifier_spec.modifier_name) + + def _bake_current_simulation(self, obj, test_mod_type, test_mod_name, frame_end): + for scene in bpy.data.scenes: + for modifier in obj.modifiers: + if modifier.type == test_mod_type: + obj.modifiers[test_mod_name].point_cache.frame_end = frame_end + override = {'scene': scene, 'active_object': obj, 'point_cache': modifier.point_cache} + bpy.ops.ptcache.bake(override, bake=True) + break + + def _apply_physics_settings(self, test_object, physics_spec: PhysicsSpec): + """ + Apply Physics settings to test objects. + """ + scene = bpy.context.scene + scene.frame_set(1) + modifier = test_object.modifiers.new(physics_spec.modifier_name, + physics_spec.modifier_type) + physics_setting = modifier.settings + if self.verbose: + print("Created modifier '{}' of type '{}'.". + format(physics_spec.modifier_name, physics_spec.modifier_type)) + + + for param_name in physics_spec.modifier_parameters: + try: + setattr(physics_setting, param_name, physics_spec.modifier_parameters[param_name]) + if self.verbose: + print("\t set parameter '{}' with value '{}'". + format(param_name, physics_spec.modifier_parameters[param_name])) + except AttributeError: + # Clean up first + bpy.ops.object.delete() + raise AttributeError("Modifier '{}' has no parameter named '{}'". + format(physics_spec.modifier_type, param_name)) + + scene.frame_set(physics_spec.frame_end + 1) + + self._bake_current_simulation(test_object, physics_spec.modifier_type, physics_spec.modifier_name, physics_spec.frame_end) + if self.apply_modifier: + bpy.ops.object.modifier_apply(modifier=physics_spec.modifier_name) + + def _apply_operator(self, test_object, operator: OperatorSpec): """ Apply operator on test object. @@ -302,9 +368,12 @@ class MeshTest: elif isinstance(operation, OperatorSpec): self._apply_operator(evaluated_test_object, operation) + + elif isinstance(operation, PhysicsSpec): + self._apply_physics_settings(evaluated_test_object, operation) else: - raise ValueError("Expected operation of type {} or {}. Got {}". - format(type(ModifierSpec), type(OperatorSpec), + raise ValueError("Expected operation of type {} or {} or {}. Got {}". + format(type(ModifierSpec), type(OperatorSpec), type(PhysicsSpec), type(operation))) # Compare resulting mesh with expected one. @@ -312,7 +381,10 @@ class MeshTest: print("Comparing expected mesh with resulting mesh...") evaluated_test_mesh = evaluated_test_object.data expected_mesh = self.expected_object.data - compare_result = evaluated_test_mesh.unit_test_compare(mesh=expected_mesh) + 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') # Also check if invalid geometry (which is never expected) had to be corrected... @@ -434,7 +506,7 @@ class ModifierTest: >>> modifiers_test.run_all_tests() """ - def __init__(self, modifier_tests: list, apply_modifiers=False): + def __init__(self, modifier_tests: list, apply_modifiers=False, threshold=None): """ Construct a modifier test. :param modifier_tests: list - list of modifier test cases. Each element in the list must contain the following @@ -445,6 +517,7 @@ class ModifierTest: """ self.modifier_tests = modifier_tests self.apply_modifiers = apply_modifiers + self.threshold = threshold self.verbose = os.environ.get("BLENDER_VERBOSE") is not None self._failed_tests_list = [] @@ -461,7 +534,7 @@ class ModifierTest: expected_object_name = case[1] spec_list = case[2] - test = MeshTest(test_object_name, expected_object_name) + test = MeshTest(test_object_name, expected_object_name, threshold=self.threshold) if self.apply_modifiers: test.apply_modifier = True diff --git a/tests/python/operators.py b/tests/python/operators.py index c5b3ac745c6..626aaedc724 100644 --- a/tests/python/operators.py +++ b/tests/python/operators.py @@ -163,10 +163,4 @@ def main(): if __name__ == "__main__": - try: - main() - except: - import traceback - - traceback.print_exc() - sys.exit(1) + main() diff --git a/tests/python/physics_cloth.py b/tests/python/physics_cloth.py new file mode 100644 index 00000000000..5b9151ea089 --- /dev/null +++ b/tests/python/physics_cloth.py @@ -0,0 +1,51 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import os +import sys + +import bpy + +sys.path.append(os.path.dirname(os.path.realpath(__file__))) +from modules.mesh_test import ModifierTest, PhysicsSpec + + +def main(): + test = [ + ["testCloth", "expectedCloth", + [PhysicsSpec('Cloth', 'CLOTH', {'quality': 5}, 35)]], + ] + cloth_test = ModifierTest(test, threshold=1e-3) + + command = list(sys.argv) + for i, cmd in enumerate(command): + if cmd == "--run-all-tests": + cloth_test.apply_modifiers = True + cloth_test.run_all_tests() + break + elif cmd == "--run-test": + cloth_test.apply_modifiers = False + index = int(command[i + 1]) + cloth_test.run_test(index) + break + + +if __name__ == "__main__": + main() diff --git a/tests/python/physics_softbody.py b/tests/python/physics_softbody.py new file mode 100644 index 00000000000..8d431be742c --- /dev/null +++ b/tests/python/physics_softbody.py @@ -0,0 +1,51 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import os +import sys + +import bpy + +sys.path.append(os.path.dirname(os.path.realpath(__file__))) +from modules.mesh_test import ModifierTest, PhysicsSpec + + +def main(): + test = [ + ["testSoftBody", "expectedSoftBody", + [PhysicsSpec('Softbody', 'SOFT_BODY', {'use_goal': False, 'bend': 8, 'pull': 0.8, 'push': 0.8}, 45)]], + ] + softBody_test = ModifierTest(test) + + command = list(sys.argv) + for i, cmd in enumerate(command): + if cmd == "--run-all-tests": + softBody_test.apply_modifiers = True + softBody_test.run_all_tests() + break + elif cmd == "--run-test": + softBody_test.apply_modifiers = False + index = int(command[i + 1]) + softBody_test.run_test(index) + break + + +if __name__ == "__main__": + main() |